Compare commits
No commits in common. "b8f02d92dad7cb0def3ec417fc332ad0679daff5" and "9f0e1edc882b8020e12c2134e7ebd393b23b6ea6" have entirely different histories.
b8f02d92da
...
9f0e1edc88
122
.gitignore
vendored
|
@ -1,114 +1,16 @@
|
||||||
# Byte-compiled / optimized / DLL files
|
logs
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
.hypothesis/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
*.log
|
||||||
local_settings.py
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Flask stuff:
|
coverage
|
||||||
instance/
|
node_modules
|
||||||
.webassets-cache
|
build
|
||||||
|
.env.local
|
||||||
# Scrapy stuff:
|
.env.development.local
|
||||||
.scrapy
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
.python-version
|
|
||||||
|
|
||||||
# celery beat schedule file
|
|
||||||
celerybeat-schedule
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
|
|
||||||
# Editor
|
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
|
||||||
# DB
|
|
||||||
db.sqlite3
|
|
||||||
|
|
||||||
# Test mount
|
|
||||||
test/
|
|
||||||
|
|
||||||
# VS Code
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
output/
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
Title: Hand of Ozymandias
|
|
||||||
Date: 2012-03-23
|
|
||||||
Category: Projects
|
|
||||||
Summary: A withered hand I welded out of scrap metal.
|
|
||||||
|
|
||||||
I was visiting my cousins in Radium, BC and decided to learn stick welding at
|
|
||||||
their shop. I wanted to create a sculpture, so with pieces of scrap metal I
|
|
||||||
welded together this hand. The beads are far from perfect. Working with small
|
|
||||||
pieces of rusted metal made it difficult.
|
|
||||||
|
|
||||||
![a rusted metalic hand]({static}/images/hand-of-ozymandias/hand1.jpg)
|
|
||||||
|
|
||||||
## The Name
|
|
||||||
|
|
||||||
One of my favourite poems is [Ozymandias](https://en.wikipedia.org/wiki/Ozymandias)
|
|
||||||
by Percy Bysshe Shelley. It's about the inevitable complete decline of all
|
|
||||||
rulers and the empires they build, however mighty in their time. This is the
|
|
||||||
hand of Ozymandias sticking out from the sand, grasping for life after he has
|
|
||||||
been reduced to dust.
|
|
||||||
|
|
||||||
## Construction
|
|
||||||
|
|
||||||
I eyeballed the joint angles and my cousin cut them to spec with an angle
|
|
||||||
grinder. It was made in a machine shop with no real planning done ahead of time.
|
|
||||||
In between welds, I used my own hand as a reference. Below is a picture of me
|
|
||||||
adding a bead to it.
|
|
||||||
|
|
||||||
![me welding the hand causing a very bright light]({static}/images/hand-of-ozymandias/hand2.jpg)
|
|
Before Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 295 KiB |
Before Width: | Height: | Size: 350 KiB |
Before Width: | Height: | Size: 160 KiB |
|
@ -1,30 +0,0 @@
|
||||||
Title: LED Dress
|
|
||||||
Date: 2016-03-18
|
|
||||||
Category: Projects
|
|
||||||
Summary: A dress made out of LEDs that twinkle like stars.
|
|
||||||
|
|
||||||
A friend of mine was attending a stars and constellations themed ball. She
|
|
||||||
wanted to wear a dress that was lit up with LEDs acting as twinkling stars.
|
|
||||||
Seven of the 28 stars are aligned to resemble the Big Dipper constellation and
|
|
||||||
twinkle differently than the rest, which twinkle in a random pattern.
|
|
||||||
|
|
||||||
![a blue dress with a number of LEDs shining through the fabric]({static}/images/dress/dress1.jpg)
|
|
||||||
|
|
||||||
## Construction
|
|
||||||
|
|
||||||
The LEDs came from that strip that was cut up and soldered together with very
|
|
||||||
small wires. Each of the LEDs can be controlled individually.
|
|
||||||
|
|
||||||
![the controller circuit board on the left, and the soldered together LEDs on the right]({static}/images/dress/dress2.jpg)
|
|
||||||
|
|
||||||
21 of the stars are light magenta in color and twinkle by fading randomly. The
|
|
||||||
seven LEDs that form the Big Dipper continually scroll through a gradient of
|
|
||||||
three colors. Instead of calculating the values of each color in the gradient as
|
|
||||||
the program runs, a lookup table is used.
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<video autoplay muted loop>
|
|
||||||
<source src="{static}/videos/dress/dress3.mp4" type="video/mp4">
|
|
||||||
Your browser does not support the video tag.
|
|
||||||
</video>
|
|
||||||
</center>
|
|
|
@ -1,30 +0,0 @@
|
||||||
Title: Remote Control Light Switch
|
|
||||||
Date: 2014-10-09
|
|
||||||
Category: Projects
|
|
||||||
Summary: A device to toggle my lights remotely.
|
|
||||||
|
|
||||||
I wanted the ability to toggle my bedroom light remotely for convenience. I
|
|
||||||
designed a circuit that allows me to control my light with any
|
|
||||||
device that can load a webpage.
|
|
||||||
|
|
||||||
I still wanted to be able to control the light manually, so I bought a metallic
|
|
||||||
face-plate and turned it into a capacitive touch sensor. The slightest touch
|
|
||||||
anywhere on the plate is enough to toggle the light. I had to electrically
|
|
||||||
isolate the metal screws from it because they screw into a grounded switch box.
|
|
||||||
|
|
||||||
![a rusted metalic hand]({static}/images/light-switch/light1.jpg)
|
|
||||||
|
|
||||||
## Function
|
|
||||||
|
|
||||||
I have a Raspberry Pi ($35 computer) on my home network that runs a web server.
|
|
||||||
When you connect to it in your web browser, a page loads with buttons to turn
|
|
||||||
the light on or off. When you press a button, the server executes a command that
|
|
||||||
sends a message over Bluetooth to the light switch. The Bluetooth module in the
|
|
||||||
wall receives this message and forwards it to the microcontroller, which
|
|
||||||
processes it and toggles the relay. The whole circuit is also powered from mains
|
|
||||||
by an AC-DC converter.
|
|
||||||
|
|
||||||
This entire process happens quicker than half a second, so it feels instant.
|
|
||||||
|
|
||||||
![a rusted metalic hand]({static}/images/light-switch/light2.jpg)
|
|
||||||
![a rusted metalic hand]({static}/images/light-switch/light3.jpg)
|
|
|
@ -1,40 +0,0 @@
|
||||||
Title: Man’s Reach Exceeds His Grasp
|
|
||||||
Date: 2012-04-11
|
|
||||||
Category: Projects
|
|
||||||
Summary: My first attempt at painting with acrylic.
|
|
||||||
|
|
||||||
The painting is called “Man’s Reach Exceeds His Grasp”. I've always wanted to
|
|
||||||
try painting and thought I had a good idea, so after a couple of drawings I
|
|
||||||
attempted to paint it. I eventually got it framed at Michaels. Many thanks to my
|
|
||||||
friend Laura for the opportunity to do this, I couldn't have done it without her
|
|
||||||
help.
|
|
||||||
|
|
||||||
![a painting of water pouring out of a vase and into a hand]({static}/images/painting/painting1.jpg)
|
|
||||||
|
|
||||||
## The Meaning
|
|
||||||
|
|
||||||
It’s hard to see in the photo, but the moment the water touches his hand it
|
|
||||||
turns into sand and is taken by the slight breeze. The title is a quote from
|
|
||||||
Andrea del Sarto, a poem by Robert Browning. It is also said by Nikola Tesla’s
|
|
||||||
character in my favourite movie, [The Prestige](https://www.imdb.com/title/tt0482571/).
|
|
||||||
|
|
||||||
“I, painting from myself and to myself,
|
|
||||||
Know what I do, am unmoved by men’s blame
|
|
||||||
Or their praise either. Somebody remarks
|
|
||||||
Morello's outline there is wrongly traced,
|
|
||||||
His hue mistaken; what of that? or else,
|
|
||||||
Rightly traced and well ordered; what of that?
|
|
||||||
Speak as they please, what does the mountain care?
|
|
||||||
Ah, but a man’s reach should exceed his grasp,
|
|
||||||
Or what’s a heaven for?”
|
|
||||||
– Robert Browning from *Andrea del Sarto*
|
|
||||||
|
|
||||||
## Creation
|
|
||||||
|
|
||||||
I started with the background, trying to make it blurry and out of focus, then
|
|
||||||
slowly progressed to the foreground. The hands were drawn in pencil and painted
|
|
||||||
in. It was quite difficult to get the blending and shadows perfect, but I had
|
|
||||||
Laura to tell me when things didn't look right. Below I am trying to figure out
|
|
||||||
what a hand looks like in a mirror.
|
|
||||||
|
|
||||||
![me looking at my own hand in the mirror]({static}/images/painting/painting2.jpg)
|
|
|
@ -1,39 +0,0 @@
|
||||||
Title: Automatic Plant Waterer
|
|
||||||
Date: 2014-06-05
|
|
||||||
Category: Projects
|
|
||||||
Summary: A device that automatically waters plants.
|
|
||||||
|
|
||||||
One day I decided watering my one plant was too much work, so I automated it.
|
|
||||||
It's also great for when I'm on vacation. The plant is a year old now and
|
|
||||||
doesn't look as good as it used to (kinda like you). So this machine is like its
|
|
||||||
life support.
|
|
||||||
|
|
||||||
Update: this plant died long ago.
|
|
||||||
|
|
||||||
![the device and pump on a 2L pop bottle with a tube running to a flowerpot]({static}/images/plant-waterer/waterer1.jpg)
|
|
||||||
|
|
||||||
## First Attempt
|
|
||||||
|
|
||||||
The design was very simple and soldered together on perf board. A
|
|
||||||
microcontroller turns the pump on for 20 seconds, then waits 24 hours and
|
|
||||||
restarts. The pump ran way too fast so it was slowed down to 10% power.
|
|
||||||
|
|
||||||
This design suffered from a fatal problem. After running, there was a chance
|
|
||||||
that the tube would stay full of fluid. If the water level in the pop bottle was
|
|
||||||
too high, it could siphon out. I woke up with a flower pot overflowing with
|
|
||||||
water a couple of times.
|
|
||||||
|
|
||||||
![a new version feeding into a different plant]({static}/images/plant-waterer/waterer2.jpg)
|
|
||||||
|
|
||||||
## Second Attempt
|
|
||||||
|
|
||||||
I liked the idea so much that I made a second iteration. This one used a custom
|
|
||||||
printed circuit board with a lot more features. The pumping duration could be
|
|
||||||
adjusted with a screwdriver. This was useful as the plant (now a
|
|
||||||
[Ming aralia](https://en.wikipedia.org/wiki/Polyscias_fruticosa)) grew.
|
|
||||||
|
|
||||||
Another feature was the ability to run the pump backwards. This completely
|
|
||||||
eliminated the siphoning problem from before. After pumping for a set duration,
|
|
||||||
it would run backwards until the tube was cleared of water.
|
|
||||||
|
|
||||||
![the new version beside a big Min aralia plant]({static}/images/plant-waterer/waterer3.jpg)
|
|
|
@ -1,23 +0,0 @@
|
||||||
Title: Solar Car
|
|
||||||
Date: 2013-04-27
|
|
||||||
Category: Projects
|
|
||||||
Summary: About my time volunteering with the University of Calgary Solar Car Team, where I designed a maximum power point tracker.
|
|
||||||
|
|
||||||
I joined the University of Calgary Solar Car Team in my first semester for a
|
|
||||||
chance to learn things, gain practical experience, and meet people that share my
|
|
||||||
interests. The car was the top Canadian team in a 3000 km race from Darwin to
|
|
||||||
Adelaide, Australia in 2011. We met up at a shop on campus every Saturday
|
|
||||||
morning to work on the new Generation IV of the solar car.
|
|
||||||
|
|
||||||
![the MPPT device, a printed circuit board with components]({static}/images/solar-car/solar1.jpg)
|
|
||||||
|
|
||||||
## The Helianthus MPPT
|
|
||||||
|
|
||||||
I was in charge of designing and assembling the MPPTs (maximum power point
|
|
||||||
trackers) for the new generation solar car. An MPPT extracts as much power out
|
|
||||||
of the solar cells as possible. The solar array operates less efficiently
|
|
||||||
without them. The Generation IV car, Schulich Delta (pictured below) uses seven
|
|
||||||
of them: one per section of solar cells with similar lighting conditions. Andrei
|
|
||||||
and I designed the MPPT above.
|
|
||||||
|
|
||||||
![our team of 30 standing behind the solar car which is covered in sponsor logos]({static}/images/solar-car/solar2.jpg)
|
|
18
package.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "my-razzle-app",
|
||||||
|
"version": "2.0.0-alpha.8",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"start": "razzle start",
|
||||||
|
"build": "razzle build",
|
||||||
|
"test": "razzle test --env=jsdom",
|
||||||
|
"start:prod": "NODE_ENV=production node build/server.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.16.3",
|
||||||
|
"razzle": "^2.2.0",
|
||||||
|
"react": "^16.4.1",
|
||||||
|
"react-dom": "^16.4.1",
|
||||||
|
"react-router-dom": "^4.3.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*- #
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
AUTHOR = 'Tanner'
|
|
||||||
SITENAME = 'Tanner Collin'
|
|
||||||
SITEURL = ''
|
|
||||||
|
|
||||||
PATH = 'content'
|
|
||||||
|
|
||||||
TIMEZONE = 'Canada/Mountain'
|
|
||||||
|
|
||||||
DEFAULT_LANG = 'en'
|
|
||||||
|
|
||||||
# Feed generation is usually not desired when developing
|
|
||||||
FEED_ALL_ATOM = None
|
|
||||||
CATEGORY_FEED_ATOM = None
|
|
||||||
TRANSLATION_FEED_ATOM = None
|
|
||||||
AUTHOR_FEED_ATOM = None
|
|
||||||
AUTHOR_FEED_RSS = None
|
|
||||||
|
|
||||||
DEFAULT_PAGINATION = False
|
|
||||||
|
|
||||||
MARKDOWN = {
|
|
||||||
'extension_configs': {
|
|
||||||
'markdown.extensions.codehilite': {'css_class': 'highlight'},
|
|
||||||
'markdown.extensions.extra': {},
|
|
||||||
'markdown.extensions.meta': {},
|
|
||||||
'markdown.extensions.toc': {
|
|
||||||
'toc_depth': '2-3',
|
|
||||||
'anchorlink': True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'output_format': 'html5',
|
|
||||||
}
|
|
||||||
|
|
||||||
# Uncomment following line if you want document-relative URLs when developing
|
|
||||||
#RELATIVE_URLS = True
|
|
||||||
|
|
||||||
THEME = 'themes/theme'
|
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 32 KiB |
48
public/reset.css
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/* http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v2.0 | 20110126
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
ol, ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
blockquote, q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before, blockquote:after,
|
||||||
|
q:before, q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
2
public/robots.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
User-agent: *
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*- #
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
# This file is only used if you use `make publish` or
|
|
||||||
# explicitly specify it as your config file.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.append(os.curdir)
|
|
||||||
from pelicanconf import *
|
|
||||||
|
|
||||||
# If your site is available via HTTPS, make sure SITEURL begins with https://
|
|
||||||
SITEURL = ''
|
|
||||||
RELATIVE_URLS = False
|
|
||||||
|
|
||||||
FEED_ALL_ATOM = 'feeds/all.atom.xml'
|
|
||||||
CATEGORY_FEED_ATOM = 'feeds/{slug}.atom.xml'
|
|
||||||
|
|
||||||
DELETE_OUTPUT_DIRECTORY = True
|
|
||||||
|
|
||||||
# Following items are often useful when publishing
|
|
||||||
|
|
||||||
#DISQUS_SITENAME = ""
|
|
||||||
#GOOGLE_ANALYTICS = ""
|
|
10
src/App.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: #333;
|
||||||
|
background-color: #F9F9F9;
|
||||||
|
}
|
13
src/App.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Route from 'react-router-dom/Route';
|
||||||
|
import Switch from 'react-router-dom/Switch';
|
||||||
|
import Home from './Home';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<Switch>
|
||||||
|
<Route exact path='/' component={Home} />
|
||||||
|
</Switch>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
191
src/Grid.css
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
.grid-container {
|
||||||
|
background: #bad4ff;
|
||||||
|
max-width: 120rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item {
|
||||||
|
//background: linear-gradient(135deg, #87e0fd 0%,#53cbf1 40%,#05abe0 100%);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
width: 30rem;
|
||||||
|
min-width: 25rem;
|
||||||
|
min-height: 25rem;
|
||||||
|
padding-bottom: 30rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item-content {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu .logo {
|
||||||
|
object-fit: contain;
|
||||||
|
padding-top: 1em;
|
||||||
|
min-height: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu section {
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu section aside {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu section img {
|
||||||
|
height: 100%;
|
||||||
|
float: right;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project header {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project section {
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project.bottom section {
|
||||||
|
flex-flow: column;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project.left section p {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project.left section img {
|
||||||
|
height: 100%;
|
||||||
|
float: right;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project.bottom section p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project.bottom section img {
|
||||||
|
object-fit: contain;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 1em;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo a {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo header, .photo footer {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo header {
|
||||||
|
top: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo footer {
|
||||||
|
bottom: 0;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 1em;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo a:hover header {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo a:hover footer {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo img {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 120rem) {
|
||||||
|
.grid-item {
|
||||||
|
width: 25%;
|
||||||
|
padding-bottom: 25%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 102rem) {
|
||||||
|
.grid-item {
|
||||||
|
width: 33.33%;
|
||||||
|
padding-bottom: 33.33%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 76.5rem) {
|
||||||
|
.grid-container {
|
||||||
|
max-width: 68rem;
|
||||||
|
}
|
||||||
|
.grid-item {
|
||||||
|
width: 50%;
|
||||||
|
padding-bottom: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 51rem) {
|
||||||
|
.grid-container {
|
||||||
|
max-width: 34rem;
|
||||||
|
}
|
||||||
|
.grid-item {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 100%;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.grid-item-content.photo {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.photo a header {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.photo a footer {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
125
src/Grid.js
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import React from 'react';
|
||||||
|
import './Grid.css';
|
||||||
|
|
||||||
|
import logo from './logo.png';
|
||||||
|
import GridItem from './GridItem';
|
||||||
|
|
||||||
|
import tanner from './tanner.jpg';
|
||||||
|
import dress1 from './dress1.jpg';
|
||||||
|
import switch1 from './switch1.jpg';
|
||||||
|
import pump1 from './pump1.jpg';
|
||||||
|
import hand1 from './hand1.jpg';
|
||||||
|
|
||||||
|
import banff1 from './banff1.jpg';
|
||||||
|
import bee1 from './bee1.jpg';
|
||||||
|
import canmore1 from './canmore1.jpg';
|
||||||
|
import carshow1 from './carshow1.jpg';
|
||||||
|
|
||||||
|
//function importAll(r) {
|
||||||
|
// return r.keys().map(x => r(x));
|
||||||
|
//}
|
||||||
|
|
||||||
|
//const images = importAll(require.context('./images'));
|
||||||
|
|
||||||
|
|
||||||
|
class Grid extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='grid-container'>
|
||||||
|
<GridItem type='menu'>
|
||||||
|
<img src={logo} className='logo' alt='' />
|
||||||
|
<section>
|
||||||
|
<img src={tanner} alt='' />
|
||||||
|
<aside>
|
||||||
|
<p>Home</p>
|
||||||
|
<p>Projects</p>
|
||||||
|
<p>Photograhy</p>
|
||||||
|
<p>Art</p>
|
||||||
|
<p>About</p>
|
||||||
|
<p>Contact</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
<footer>More info or a .onion link.</footer>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem type='project left'>
|
||||||
|
<header>LED Dress</header>
|
||||||
|
<section>
|
||||||
|
<img src={dress1} alt='' />
|
||||||
|
<p>A dress lit up with LEDs that act as twinkling stars. Seven act as The Big Dipper, and the rest twinkle randomly.</p>
|
||||||
|
</section>
|
||||||
|
<footer>
|
||||||
|
<p>Project (Hardware)</p><p>2018-10-11</p>
|
||||||
|
</footer>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem type='photo'>
|
||||||
|
<a>
|
||||||
|
<header>Thorncliffe Car Show</header>
|
||||||
|
<img src={carshow1} alt='' />
|
||||||
|
<footer>
|
||||||
|
<p>Photography</p><p>2018-10-11</p>
|
||||||
|
</footer>
|
||||||
|
</a>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem type='project bottom'>
|
||||||
|
<header>Remote Control Lightswitch</header>
|
||||||
|
<section>
|
||||||
|
<img src={switch1} alt='' />
|
||||||
|
<p>Custom circuit that lets me control a ceiling light over Wifi.</p>
|
||||||
|
</section>
|
||||||
|
<footer>
|
||||||
|
<p>Project (Hardware)</p><p>2018-10-11</p>
|
||||||
|
</footer>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem type='project bottom'>
|
||||||
|
<header>Plant Waterer</header>
|
||||||
|
<section>
|
||||||
|
<img src={pump1} alt='' />
|
||||||
|
<p>Controller that waters my plant automatically every day.</p>
|
||||||
|
</section>
|
||||||
|
<footer>
|
||||||
|
<p>Project (Hardware)</p><p>2018-10-11</p>
|
||||||
|
</footer>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem type='photo'>
|
||||||
|
<a>
|
||||||
|
<header>Prince's Island Park</header>
|
||||||
|
<img src={bee1} alt='' />
|
||||||
|
<footer>
|
||||||
|
<p>Photography</p><p>2018-10-11</p>
|
||||||
|
</footer>
|
||||||
|
</a>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem type='photo'>
|
||||||
|
<a>
|
||||||
|
<header>Johnston Creek, Banff</header>
|
||||||
|
<img src={banff1} alt='' />
|
||||||
|
<footer>
|
||||||
|
<p>Photography</p><p>2018-10-11</p>
|
||||||
|
</footer>
|
||||||
|
</a>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem type='project left'>
|
||||||
|
<header>Hand of Ozymandias</header>
|
||||||
|
<section>
|
||||||
|
<img src={hand1} alt='' />
|
||||||
|
<p>Sculpture welded together out of scrap metal for practice.</p>
|
||||||
|
</section>
|
||||||
|
<footer>
|
||||||
|
<p>Project (Hardware)</p><p>2018-10-11</p>
|
||||||
|
</footer>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem type='photo'>
|
||||||
|
<a>
|
||||||
|
<header>VW Van in Canmore</header>
|
||||||
|
<img src={canmore1} alt='' />
|
||||||
|
<footer>
|
||||||
|
<p>Photography</p><p>2018-10-11</p>
|
||||||
|
</footer>
|
||||||
|
</a>
|
||||||
|
</GridItem>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Grid;
|
15
src/GridItem.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class GridItem extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='grid-item'>
|
||||||
|
<div className={'grid-item-content ' + this.props.type}>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GridItem;
|
3
src/Home.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.Home {
|
||||||
|
text-align: center;
|
||||||
|
}
|
16
src/Home.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import './Home.css';
|
||||||
|
|
||||||
|
import Grid from './Grid';
|
||||||
|
|
||||||
|
class Home extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='Home'>
|
||||||
|
<Grid />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Home;
|
BIN
src/banff1.jpg
Normal file
After Width: | Height: | Size: 565 KiB |
BIN
src/bee1.jpg
Normal file
After Width: | Height: | Size: 220 KiB |
BIN
src/canmore1.jpg
Normal file
After Width: | Height: | Size: 301 KiB |
BIN
src/carshow1.jpg
Normal file
After Width: | Height: | Size: 371 KiB |
15
src/client.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import App from './App';
|
||||||
|
import BrowserRouter from 'react-router-dom/BrowserRouter';
|
||||||
|
import React from 'react';
|
||||||
|
import { hydrate } from 'react-dom';
|
||||||
|
|
||||||
|
hydrate(
|
||||||
|
<BrowserRouter>
|
||||||
|
<App />
|
||||||
|
</BrowserRouter>,
|
||||||
|
document.getElementById('root')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
|
module.hot.accept();
|
||||||
|
}
|
BIN
src/dress1.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
src/hand1.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
26
src/index.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import app from './server';
|
||||||
|
import http from 'http';
|
||||||
|
|
||||||
|
const server = http.createServer(app);
|
||||||
|
|
||||||
|
let currentApp = app;
|
||||||
|
|
||||||
|
server.listen(process.env.PORT || 3000, error => {
|
||||||
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🚀 started');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
|
console.log('✅ Server-side HMR Enabled!');
|
||||||
|
|
||||||
|
module.hot.accept('./server', () => {
|
||||||
|
console.log('🔁 HMR Reloading `./server`...');
|
||||||
|
server.removeListener('request', currentApp);
|
||||||
|
const newApp = require('./server').default;
|
||||||
|
server.on('request', newApp);
|
||||||
|
currentApp = newApp;
|
||||||
|
});
|
||||||
|
}
|
BIN
src/logo.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/pump1.jpg
Normal file
After Width: | Height: | Size: 1.2 MiB |
52
src/server.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import App from './App';
|
||||||
|
import React from 'react';
|
||||||
|
import { StaticRouter } from 'react-router-dom';
|
||||||
|
import express from 'express';
|
||||||
|
import { renderToString } from 'react-dom/server';
|
||||||
|
|
||||||
|
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
|
||||||
|
|
||||||
|
const server = express();
|
||||||
|
server
|
||||||
|
.disable('x-powered-by')
|
||||||
|
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
|
||||||
|
.get('/*', (req, res) => {
|
||||||
|
const context = {};
|
||||||
|
const markup = renderToString(
|
||||||
|
<StaticRouter context={context} location={req.url}>
|
||||||
|
<App />
|
||||||
|
</StaticRouter>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (context.url) {
|
||||||
|
res.redirect(context.url);
|
||||||
|
} else {
|
||||||
|
res.status(200).send(
|
||||||
|
`<!doctype html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Tanner Collin</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="reset.css">
|
||||||
|
${
|
||||||
|
assets.client.css
|
||||||
|
? `<link rel="stylesheet" href="${assets.client.css}">`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
${
|
||||||
|
process.env.NODE_ENV === 'production'
|
||||||
|
? `<script src="${assets.client.js}" defer></script>`
|
||||||
|
: `<script src="${assets.client.js}" defer crossorigin></script>`
|
||||||
|
}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root">${markup}</div>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default server;
|
BIN
src/switch1.jpg
Normal file
After Width: | Height: | Size: 848 KiB |
BIN
src/tanner.jpg
Executable file
After Width: | Height: | Size: 324 KiB |
1
themes/theme/static/darkmode-js.min.js
vendored
|
@ -1,59 +0,0 @@
|
||||||
@font-face {
|
|
||||||
font-family: 'Apparatus SIL';
|
|
||||||
src: url('AppSILR.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Apparatus SIL';
|
|
||||||
font-style: italic;
|
|
||||||
src: url('AppSILI.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Apparatus SIL';
|
|
||||||
font-weight: bold;
|
|
||||||
src: url('AppSILB.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Apparatus SIL';
|
|
||||||
font-weight: bold;
|
|
||||||
font-style: italic;
|
|
||||||
src: url('AppSILBI.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Lato';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
src: local('Lato Italic'), local('Lato-Italic'), url('Lato-Italic.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Lato';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
src: local('Lato Bold Italic'), local('Lato-BoldItalic'), url('Lato-BoldItalic.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Lato';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
src: local('Lato Regular'), local('Lato-Regular'), url('Lato-Regular.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Lato';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
src: local('Lato Bold'), local('Lato-Bold'), url('Lato-Bold.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
Before Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 124 KiB |
|
@ -1,21 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<p><a href="/">← Return to Home</a></p>
|
|
||||||
<header>
|
|
||||||
<h1>{{ article.title }}</h1>
|
|
||||||
<div class="summary">
|
|
||||||
{{ article.summary }}
|
|
||||||
</div>
|
|
||||||
<p class="metadata">
|
|
||||||
{{ article.locale_date }}
|
|
||||||
{% if article.modified %}
|
|
||||||
— updated {{ article.locale_modified }}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
<hr />
|
|
||||||
<article>
|
|
||||||
{{ article.content }}
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
|
@ -1,48 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
{% include 'style.css' %}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="/theme/fonts/fonts.css" />
|
|
||||||
|
|
||||||
<title>Tanner Collin</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="sidebar">
|
|
||||||
<img src="/theme/me.jpg" alt="A picture of me smiling" />
|
|
||||||
<p>
|
|
||||||
Tanner Collin
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="topbar">
|
|
||||||
<p>
|
|
||||||
<img src="/theme/logo.png" alt="My name in handwriting" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{% block content %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/theme/darkmode-js.min.js"></script>
|
|
||||||
<script>
|
|
||||||
const options = {
|
|
||||||
bottom: '16px',
|
|
||||||
right: '16px',
|
|
||||||
buttonColorDark: '#666',
|
|
||||||
buttonColorLight: '#aaa',
|
|
||||||
label: '🌙',
|
|
||||||
};
|
|
||||||
new Darkmode(options).showWidget();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<p>
|
|
||||||
Hi, I'm Tanner! I do firmware design and web development.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Contact Info</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Email: <a href="mailto:site@tannercollin.com">site@tannercollin.com</a> <br />
|
|
||||||
Telegram: <a href="https://t.me/tannercollin" target="_blank">@tannercollin</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Resume</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Firmware Engineer at <a href="https://cabanablockchain.com" target="_blank">Cabana Blockchain</a>, 2018–</li>
|
|
||||||
<li>Lead Hardware Engineer at <a href="https://criticalcontrol.com/" target="_blank">Critical Control</a>, 2016–2018</li>
|
|
||||||
<li>Electrical Engineer at <a href="https://www.opener.aero/" target="_blank">Opener Aero</a>, 2016–2016</li>
|
|
||||||
<li>Electrical Engineer Intern at <a href="https://www.pason.com/" target="_blank">Pason Systems</a>, 2014–2015</li>
|
|
||||||
<li>BSc. Electrical Engineering from University of Calgary</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Projects</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
My main hobby is working on technical projects. I typically design websites or
|
|
||||||
build tools that make my life easier. Sometimes art.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
You can find my code on <a href="https://github.com/tannercollin" target="_blank">GitHub</a>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{% for article in articles_page.object_list %}
|
|
||||||
<h3><a href="{{ article.url }}">{{ article.title }}</a></h3>
|
|
||||||
<div class="summary">
|
|
||||||
{{ article.summary }}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
|
@ -1,141 +0,0 @@
|
||||||
html {
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font: 1.2rem/1.0 Lato,sans-serif;
|
|
||||||
background-color: rgb(245, 245, 245);
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #000000;
|
|
||||||
text-decoration: none;
|
|
||||||
outline: none;
|
|
||||||
border-bottom: 1px solid #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
font-size: 1rem;
|
|
||||||
background-color: #eee;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 56rem;
|
|
||||||
margin: 2rem auto 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
margin-top: 2px;
|
|
||||||
float: left;
|
|
||||||
width: 8rem;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar img {
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar p {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topbar {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content .topbar img {
|
|
||||||
filter: invert(100%);
|
|
||||||
width: 14rem;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc {
|
|
||||||
float: right;
|
|
||||||
background-color: #eee;
|
|
||||||
padding: 0.75rem;
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc ul {
|
|
||||||
padding-left: 1.2rem;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc li {
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content p.metadata {
|
|
||||||
color: #555;
|
|
||||||
font: 1rem/1.0 Apparatus SIL,serif;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content div.summary p {
|
|
||||||
margin-top: -0.5rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
max-width: 36rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content p {
|
|
||||||
font: 1.2rem/1.5 Apparatus SIL,serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content ul {
|
|
||||||
padding-left: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content li {
|
|
||||||
font: 1.2rem/1.5 Apparatus SIL,serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toclink:not(:hover)::after {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toclink::after {
|
|
||||||
color: #999;
|
|
||||||
content: "\00B6";
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width:36rem) {
|
|
||||||
.content {
|
|
||||||
margin-left: 10rem;
|
|
||||||
}
|
|
||||||
.topbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width:36rem) {
|
|
||||||
.sidebar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkmode-toggle {
|
|
||||||
z-index: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkmode--activated .content img {
|
|
||||||
filter: brightness(75%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkmode--activated video {
|
|
||||||
mix-blend-mode: difference;
|
|
||||||
}
|
|