Browse Source

initial commit for my site

main
noah 2 years ago
parent
commit
f17b718221
52 changed files with 716 additions and 448 deletions
  1. +14
    -1
      .eleventy.js
  2. +1
    -0
      .eleventyignore
  3. +7
    -7
      _data/metadata.json
  4. +0
    -0
      _includes/foot.njk
  5. +0
    -0
      _includes/head.njk
  6. +10
    -0
      _includes/header.njk
  7. +0
    -10
      _includes/layouts/base.njk
  8. +4
    -2
      _includes/layouts/home.njk
  9. +41
    -0
      _includes/layouts/layout.njk
  10. +5
    -0
      _includes/layouts/page.njk
  11. +31
    -0
      _includes/layouts/photos.njk
  12. +4
    -2
      _includes/layouts/post.njk
  13. +12
    -0
      _includes/sidebar.njk
  14. +0
    -10
      about/index.md
  15. +0
    -226
      css/index.css
  16. +0
    -89
      css/prism-base16-monokai.dark.css
  17. +1
    -0
      css/style.css
  18. +42
    -0
      gulpfile.js
  19. BIN
      img/20200629_fledgling-tree-swallow.jpg
  20. BIN
      img/20200629_phoebe-with-food.jpg
  21. BIN
      img/20200629_song-sparrow.jpg
  22. BIN
      img/20200629_tree-swallow-feeding.jpg
  23. BIN
      img/rad_nebula.jpg
  24. BIN
      img/thumbs/20200629_fledgling-tree-swallow-thumb.jpg
  25. BIN
      img/thumbs/20200629_phoebe-with-food-thumb.jpg
  26. BIN
      img/thumbs/20200629_song-sparrow-thumb.jpg
  27. BIN
      img/thumbs/20200629_tree-swallow-feeding-thumb.jpg
  28. BIN
      img/thumbs/rad_nebula-thumb.jpg
  29. +14
    -0
      index.md
  30. +0
    -14
      index.njk
  31. +9
    -0
      package.json
  32. +19
    -0
      pages/my-photography-gear.md
  33. +27
    -0
      photos/_drafts/comet-neowise.md
  34. +5
    -0
      photos/photos.json
  35. +28
    -0
      photos/yard_birds_early_summer.md
  36. +36
    -0
      posts/django-migration-hiccup.md
  37. +0
    -26
      posts/firstpost.md
  38. +0
    -15
      posts/fourthpost.md
  39. +0
    -18
      posts/secondpost.md
  40. +0
    -28
      posts/thirdpost.md
  41. +18
    -0
      scss/_colors.scss
  42. +49
    -0
      scss/_photos.scss
  43. +48
    -0
      scss/_reset.scss
  44. +63
    -0
      scss/_solar.scss
  45. +199
    -0
      scss/style.scss
  46. +0
    -0
      templates/archive.njk
  47. +0
    -0
      templates/page-list.njk
  48. +18
    -0
      templates/photoblog.njk
  49. +11
    -0
      templates/post.njk
  50. +0
    -0
      templates/sitemap.xml.njk
  51. +0
    -0
      templates/tags-list.njk
  52. +0
    -0
      templates/tags.njk

+ 14
- 1
.eleventy.js View File

@ -1,5 +1,6 @@
const { DateTime } = require("luxon");
const fs = require("fs");
const path = require('path');
const pluginRss = require("@11ty/eleventy-plugin-rss");
const pluginSyntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
const pluginNavigation = require("@11ty/eleventy-navigation");
@ -13,7 +14,12 @@ module.exports = function(eleventyConfig) {
eleventyConfig.setDataDeepMerge(true);
eleventyConfig.setFrontMatterParsingOptions({
excerpt: true
});
eleventyConfig.addLayoutAlias("post", "layouts/post.njk");
eleventyConfig.addLayoutAlias("photos", "layouts/photos.njk");
eleventyConfig.addFilter("readableDate", dateObj => {
return DateTime.fromJSDate(dateObj, {zone: 'utc'}).toFormat("dd LLL yyyy");
@ -33,10 +39,17 @@ module.exports = function(eleventyConfig) {
return array.slice(0, n);
});
// take a filename and return the generated thumbnail filename
eleventyConfig.addFilter("thumbnail", (path) => {
path.dirname = path.join(path.dirname, 'thumbs')
path.basename += '-thumb';
return path;
});
eleventyConfig.addCollection("tagList", require("./_11ty/getTagList"));
eleventyConfig.addPassthroughCopy("img");
eleventyConfig.addPassthroughCopy("css");
eleventyConfig.addPassthroughCopy("css/*.css");
/* Markdown Overrides */
let markdownLibrary = markdownIt({


+ 1
- 0
.eleventyignore View File

@ -1,2 +1,3 @@
README.md
_11ty/
*/_drafts/*

+ 7
- 7
_data/metadata.json View File

@ -1,15 +1,15 @@
{
"title": "Your Blog Name",
"url": "https://myurl.com/",
"description": "I am writing about my experiences as a naval navel-gazer.",
"title": "Noah Hall",
"url": "https://nthall.com/",
"description": "My personal site, featuring coding, photography, and whatever",
"feed": {
"subtitle": "I am writing about my experiences as a naval navel-gazer.",
"subtitle": "Blog posts about programming",
"filename": "feed.xml",
"path": "/feed/feed.xml",
"id": "https://myurl.com/"
"id": "https://nthall.com/"
},
"author": {
"name": "Your Name Here",
"email": "youremailaddress@example.com"
"name": "Noah Hall",
"email": "noah@nthall.com"
}
}

+ 0
- 0
_includes/foot.njk View File


+ 0
- 0
_includes/head.njk View File


+ 10
- 0
_includes/header.njk View File

@ -0,0 +1,10 @@
<header>
<div class="title-wrap">
<h1>Noah Hall</h1>
<ul class="menu">
{% for label, path in theme.menu %}
<li class="menu-item {% if is_current(path) %}current{% endif %}"><a href="{{ url_for(path) }}">{{ label }}</a></li>
{% endfor %}
</ul>
</div>
</header>

+ 0
- 10
_includes/layouts/base.njk View File

@ -13,7 +13,6 @@
<header>
<h1 class="home"><a href="{{ '/' | url }}">{{ metadata.title }}</a></h1>
{#- Read more about `eleventy-navigation` at https://www.11ty.dev/docs/plugins/navigation/ #}
<ul class="nav">
{%- for entry in collections.all | eleventyNavigation %}
<li class="nav-item{% if entry.url == page.url %} nav-item-active{% endif %}"><a href="{{ entry.url | url }}">{{ entry.title }}</a></li>
@ -22,15 +21,6 @@
</header>
<main{% if templateClass %} class="{{ templateClass }}"{% endif %}>
<div class="warning">
<ol>
<li>Edit the <code>_data/metadata.json</code> with your blog’s information.</li>
<li>(Optional) Edit <code>.eleventy.js</code> with your <a href="https://www.11ty.dev/docs/config/">configuration preferences</a>.</li>
<li>Delete this message from <code>_includes/layouts/base.njk</code>.</li>
</ol>
<p><em>This is an <a href="https://www.11ty.dev/">Eleventy project</a> created from the <a href="https://github.com/11ty/eleventy-base-blog"><code>eleventy-base-blog</code> repo</a>.</em></p>
</div>
{{ content | safe }}
</main>


+ 4
- 2
_includes/layouts/home.njk View File

@ -1,5 +1,7 @@
---
layout: layouts/base.njk
layout: layouts/layout.njk
templateClass: tmpl-home
---
{{ content | safe }}
<article>
{{ content | safe }}
</article>

+ 41
- 0
_includes/layouts/layout.njk View File

@ -0,0 +1,41 @@
<!doctype html>
<html lang="en">
<head>
<title>{% if page.title %}{{ page.title }} | {% endif %} Noah Hall</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="{{ page.categories }}, {{ page.tags }}">
<meta name="author" content="Noah Hall">
<meta name="generator" content="@11ty/eleventy">
<meta name="Description" content="{{ renderData.description or description or metadata.description }}">
<link rel="alternate" href="{{ metadata.feed.path | url }}" type="application/atom+xml" title="{{ metadata.title }}">
<meta name="color-scheme" content="dark light">
<link href="{{ '/css/style.css' | url }}" rel="stylesheet" type="text/css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/styles/solarized-dark.min.css" rel="stylesheet" type="text/css">
</head>
<body>
<header>
<div class="title-wrap">
<h1 class="home"><a href="{{ '/' | url }}">Noah Hall</a></h1>
<ul id="nav" class="menu">
{%- for entry in collections.all | eleventyNavigation %}
<li class="menu-item{% if entry.url == page.url %} current{% endif %}"><a href="{{ entry.url | url }}">{{ entry.title }}</a></li>
{%- endfor %}
</ul>
</div>
</header>
<div class="container">
<main {% if templateClass %} class="{{ templateClass }}"{% endif %}>
{{ content | safe }}
</main>
{% include "sidebar.njk" %}
</div>
<footer>
<div>
<i>Leave it better than you found it.</i>
</div>
</footer>
</body>
</html>

+ 5
- 0
_includes/layouts/page.njk View File

@ -0,0 +1,5 @@
<div class="content">
{% block body %}
{% endblock %}
</div>

+ 31
- 0
_includes/layouts/photos.njk View File

@ -0,0 +1,31 @@
---
layout: layouts/layout.njk
templateClass: tmpl-photos
---
<h1>{{ title }}</h1>
<article>
{{ content | safe }}
</article>
<div class='thumb-container'>
{%- for photo in photos %}
<div class="thumbnail" role="img" aria-label="{{ photo.alt }}">
<img src="{{ photo.file | url }}" href="#" title="{{ photo.title }}" alt="{{ photo.alt }}" />
</div>
{% endfor %}
</div>
<div class='carousel hidden'>
<div class='prev'>
<span><-</span>
</div>
{%- for photo in photos %}
<div class='photo'>
<img src="{{ photo.file | url }}" title="{{ photo.title }}" alt="{{ photo.alt }}" />
</div>
{% endfor %}
<div class='next'>
<span>-></span>
</div>
</div>

+ 4
- 2
_includes/layouts/post.njk View File

@ -1,9 +1,11 @@
---
layout: layouts/base.njk
layout: layouts/layout.njk
templateClass: tmpl-post
---
<h1>{{ title }}</h1>
{{ content | safe }}
<article>
{{ content | safe }}
</article>
<p><a href="{{ '/' | url }}">← Home</a></p>

+ 12
- 0
_includes/sidebar.njk View File

@ -0,0 +1,12 @@
<div id="sidebar">
<ul>
<h5>Contact me:</h5>
<li><a href="mailto:noah@nthall.com">Email (business)</a></li>
<li><a href="mailto:noah@flameazalea.com">Email (other)</a></li>
<h5>Find me online:</h5>
<li><a href="https://www.github.com/nthall">Github</a></li>
<li><a href="https://www.stackoverflow.com/users/354176/nthall">StackOverflow</a></li>
<li><a href="https://www.linkedin.com/in/nthall">LinkedIn</a></li>
<li><a href="https://social.coop/@redoak">Mastodon</a></li>
</ul>
</div>

+ 0
- 10
about/index.md View File

@ -1,10 +0,0 @@
---
layout: layouts/post.njk
title: About Me
templateClass: tmpl-post
eleventyNavigation:
key: About Me
order: 3
---
I am a person that writes stuff.

+ 0
- 226
css/index.css View File

@ -1,226 +0,0 @@
:root {
--red: #C5004A;
--darkred: #7F0036;
--lightgray: #e0e0e0;
--gray: #C0C0C0;
--darkgray: #333;
--navy: #17050F;
--blue: #082840;
--white: #fff;
}
* {
box-sizing: border-box;
}
html,
body {
padding: 0;
margin: 0;
font-family: system-ui, sans-serif;
color: var(--darkgray);
background-color: var(--white);
}
p:last-child {
margin-bottom: 0;
}
p,
.tmpl-post li,
img {
max-width: 37.5em; /* 600px /16 */
}
p,
.tmpl-post li {
line-height: 1.45;
}
a[href] {
color: var(--blue);
}
a[href]:visited {
color: var(--navy);
}
main {
padding: 1rem;
}
main :first-child {
margin-top: 0;
}
header {
border-bottom: 1px dashed var(--lightgray);
}
header:after {
content: "";
display: table;
clear: both;
}
table {
margin: 1em 0;
}
table td,
table th {
padding-right: 1em;
}
pre,
code {
font-family: Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", Courier, monospace;
line-height: 1.5;
}
pre {
font-size: 14px;
line-height: 1.375;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
padding: 1em;
margin: .5em 0;
background-color: #f6f6f6;
}
.highlight-line {
display: block;
padding: 0.125em 1em;
text-decoration: none; /* override del, ins, mark defaults */
color: inherit; /* override del, ins, mark defaults */
}
/* allow highlighting empty lines */
.highlight-line:empty:before {
content: " ";
}
/* avoid double line breaks when using display: block; */
.highlight-line + br {
display: none;
}
.highlight-line-isdir {
color: #b0b0b0;
background-color: #222;
}
.highlight-line-active {
background-color: #444;
background-color: hsla(0, 0%, 27%, .8);
}
.highlight-line-add {
background-color: #45844b;
}
.highlight-line-remove {
background-color: #902f2f;
}
/* Header */
.home {
padding: 0 1rem;
float: left;
margin: 1rem 0; /* 16px /16 */
font-size: 1em; /* 16px /16 */
}
.home :link:not(:hover) {
text-decoration: none;
}
/* Nav */
.nav {
padding: 0;
list-style: none;
float: left;
margin-left: 1em;
}
.nav-item {
display: inline-block;
margin-right: 1em;
}
.nav-item a[href]:not(:hover) {
text-decoration: none;
}
.nav-item-active {
font-weight: 700;
text-decoration: underline;
}
/* Posts list */
.postlist {
list-style: none;
padding: 0;
}
.postlist-item {
counter-increment: start-from -1;
}
.postlist-item:before {
display: inline-block;
pointer-events: none;
content: "" counter(start-from, decimal-leading-zero) ". ";
line-height: 100%;
text-align: right;
}
.postlist-date,
.postlist-item:before {
font-size: 0.8125em; /* 13px /16 */
color: var(--darkgray);
}
.postlist-date {
word-spacing: -0.5px;
}
.postlist-link {
display: inline-block;
padding: 0.25em 0.1875em; /* 4px 3px /16 */
}
.postlist-item-active .postlist-link {
font-weight: bold;
}
.tmpl-home .postlist-link {
font-size: 1.1875em; /* 19px /16 */
font-weight: 700;
}
/* Tags */
.tag {
display: inline-block;
vertical-align: text-top;
text-transform: uppercase;
font-size: 0.625em; /* 10px /16 */
padding: 2px 4px;
margin-left: 0.8em; /* 8px /10 */
background-color: var(--red);
color: var(--white);
border-radius: 0.25em; /* 3px /12 */
text-decoration: none;
}
a[href].tag,
a[href].tag:visited {
color: #fff;
}
/* Warning */
.warning {
background-color: #ffc;
padding: 1em 0.625em; /* 16px 10px /16 */
}
.warning ol:only-child {
margin: 0;
}
/* Direct Links / Markdown Headers */
.direct-link {
font-family: sans-serif;
text-decoration: none;
font-style: normal;
margin-left: .1em;
}
a[href].direct-link,
a[href].direct-link:visited {
color: transparent;
}
a[href].direct-link:focus,
a[href].direct-link:focus:visited,
:hover > a[href].direct-link,
:hover > a[href].direct-link:visited {
color: #aaa;
}

+ 0
- 89
css/prism-base16-monokai.dark.css View File

@ -1,89 +0,0 @@
code[class*="language-"], pre[class*="language-"] {
font-size: 14px;
line-height: 1.375;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
background: #272822;
color: #f8f8f2;
}
pre[class*="language-"] {
padding: 1.5em 0;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment, .token.prolog, .token.doctype, .token.cdata {
color: #75715e;
}
.token.punctuation {
color: #f8f8f2;
}
.token.namespace {
opacity: .7;
}
.token.operator, .token.boolean, .token.number {
color: #fd971f;
}
.token.property {
color: #f4bf75;
}
.token.tag {
color: #66d9ef;
}
.token.string {
color: #a1efe4;
}
.token.selector {
color: #ae81ff;
}
.token.attr-name {
color: #fd971f;
}
.token.entity, .token.url, .language-css .token.string, .style .token.string {
color: #a1efe4;
}
.token.attr-value, .token.keyword, .token.control, .token.directive, .token.unit {
color: #a6e22e;
}
.token.statement, .token.regex, .token.atrule {
color: #a1efe4;
}
.token.placeholder, .token.variable {
color: #66d9ef;
}
.token.deleted {
text-decoration: line-through;
}
.token.inserted {
border-bottom: 1px dotted #f9f8f5;
text-decoration: none;
}
.token.italic {
font-style: italic;
}
.token.important, .token.bold {
font-weight: bold;
}
.token.important {
color: #f92672;
}
.token.entity {
cursor: help;
}
pre > code.highlight {
outline: 0.4em solid #f92672;
outline-offset: .4em;
}

+ 1
- 0
css/style.css View File

@ -0,0 +1 @@
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}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}h1{font-size:2em}a,p,span,div{font-size:14px}a{color:#268bd2}a::visited{color:#6c71c4}a::hover{text-decoration:none}.space{color:rgba(0,0,0,0);-webkit-background-clip:text;background-clip:text;background-image:url("/static/rad_nebula.jpg");background-position:center}@media (prefers-color-scheme: light){body{background-color:#fdf6e3;color:#657b83}a::selection,p::selection,h1::selection,h2::selection,h3::selection,h4::selection,h5::selection,h6::selection,span::selection,em::selection,kbd::selection,li::selection,i::selection,b::selection{background-color:#eee8d5}code::selection{background-color:#657b83}}@media (prefers-color-scheme: dark){body{background-color:#002b36;color:#839496}a::selection,p::selection,h1::selection,h2::selection,h3::selection,h4::selection,h5::selection,h6::selection,span::selection,em::selection,kbd::selection,li::selection,i::selection,b::selection,code::selection{background-color:#073642}code::selection{background-color:#002b36}}div.thumb-container{display:flex;flex-direction:row;padding-top:1em;position:relative;flex-wrap:wrap}div.thumbnail{max-width:240px;flex:1 1 20px;padding:1em}div.thumbnail img{max-width:100%}div.carousel{border-radius:.5em}@media (prefers-color-scheme: light){div.carousel{background-color:rgba(253,246,227,0.75)}}@media (prefers-color-scheme: dark){div.carousel{background-color:rgba(0,43,54,0.75)}}div.carousel div.next,div.carousel div.prev{border-radius:2em}@media (prefers-color-scheme: light){div.carousel div.next:focus,div.carousel div.prev:focus{background-color:rgba(42,161,152,0.75)}div.carousel div.next:focus span,div.carousel div.prev:focus span{color:#fdf6e3}}@media (prefers-color-scheme: dark){div.carousel div.next:focus,div.carousel div.prev:focus{background-color:rgba(42,161,152,0.75)}div.carousel div.next:focus span,div.carousel div.prev:focus span{color:#002b36}}div.carousel div.next span,div.carousel div.prev span{color:#2aa198;size:4em}body{min-height:480px;font-size:20px;line-height:24px;padding-bottom:1.5em;position:relative}a,p,span,div{font-size:16px;line-height:24px}code,kbd{border:1px solid;padding:2px;border-radius:5px;font-family:monospace}@media (prefers-color-scheme: dark){code,kbd{background-color:#073642;border-color:#657b83;color:#93a1a1}}@media (prefers-color-scheme: light){code,kbd{border-color:#839496;background-color:#eee8d5;color:#586e75}}h1{font-size:2em}h2{font-size:1.8em}h3{font-size:1.6em}h4{font-size:1.4em}h5{font-size:1.3em}h6{font-size:1.2em}h1,h2,h3,h4,h5,h6{line-height:3rem}i,em{font-style:italic}.container{display:flex;flex-direction:row;padding-top:2em;position:relative}main{flex:3 1 70%;margin-left:6em;padding-right:1.5em;width:30px;order:1;height:100%}main article p{margin-bottom:.5em;text-indent:1.5em}#sidebar{flex:1 6 25%;width:30px;order:2;padding-left:1em;height:100% p;height-margin-bottom:.5em}@media (prefers-color-scheme: light){#sidebar{border-left:1px dashed #839496}}@media (prefers-color-scheme: dark){#sidebar{border-left:1px dashed #657b83}}#sidebar ul{margin-bottom:.5em}#sidebar ul li{list-style-type:none;margin-left:1rem}header{height:12em;background-image:url("/img/rad_nebula.jpg");background-size:cover;background-position:center;padding-top:3em;text-align:center}header .title-wrap{padding-top:4em}header h1>a{font-size:2em;text-decoration:none}header h2{font-size:2em}header #nav{margin:0}header #nav ul li{float:left;display:inline;list-style-type:none}header #nav ul li:after{clear:both}footer{width:100%;margin:0px 0px .5em 0px;position:absolute;bottom:0px;clear:both}footer *{display:block;margin:auto;width:max-content}.title-wrap{border-radius:.5em;width:40%;margin-left:30%;padding:3em 1em}@media (prefers-color-scheme: light){.title-wrap{background-color:rgba(253,246,227,0.75)}}@media (prefers-color-scheme: dark){.title-wrap{background-color:rgba(0,43,54,0.75)}}ol{list-style-type:decimal;list-style-position:inside;line-height:2rem;margin-left:1.5em}ul.menu li{display:inline-block;margin:1em .5em}ul.menu li a{color:#d33682;text-decoration:none}.post-date{font-style:italic;font-size:16px}.hidden{display:none}

+ 42
- 0
gulpfile.js View File

@ -0,0 +1,42 @@
const { src, dest, series, gulpParallel, watch } = require('gulp');
const cp = require('child_process');
const sass = require('gulp-sass');
const gm = require('gulp-image-resize');
const parallel = require('concurrent-transform');
const changed = require('gulp-changed');
const rename = require('gulp-rename');
const renderSass = () => {
return src(['./scss/*.scss'])
.pipe(sass({
outputStyle: 'compressed'
})
.on('error', sass.logError))
.pipe(dest('./css'));
}
const thumbnails = () => {
const photo_dest = './img/thumbs';
return src(['./img/**/*.{jpg,png}', '!./img/thumbs/**'])
.pipe(changed(photo_dest)) // check for changed photos!
.pipe(parallel( // run processing in parallel
gm({ // the actual processing func
width: 180,
height: 180,
crop: true,
quality: .7
})
))
.pipe(rename(function (path) { // add -thumb to end of filename
path.basename += '-thumb';
}))
.pipe(dest(photo_dest)); // put them where they belong!
}
const eleventy = () => {
return cp.spawn('npx', ['eleventy'], {'stdio': 'inherit'});
}
exports.sass = renderSass
exports.eleventy = eleventy
exports.default = series(renderSass, thumbnails, eleventy)

BIN
img/20200629_fledgling-tree-swallow.jpg View File

Before After
Width: 2551  |  Height: 3572  |  Size: 5.1 MiB

BIN
img/20200629_phoebe-with-food.jpg View File

Before After
Width: 1572  |  Height: 2544  |  Size: 2.7 MiB

BIN
img/20200629_song-sparrow.jpg View File

Before After
Width: 1760  |  Height: 2848  |  Size: 2.1 MiB

BIN
img/20200629_tree-swallow-feeding.jpg View File

Before After
Width: 3053  |  Height: 4273  |  Size: 6.3 MiB

BIN
img/rad_nebula.jpg View File

Before After
Width: 1000  |  Height: 956  |  Size: 199 KiB

BIN
img/thumbs/20200629_fledgling-tree-swallow-thumb.jpg View File

Before After
Width: 180  |  Height: 180  |  Size: 46 KiB

BIN
img/thumbs/20200629_phoebe-with-food-thumb.jpg View File

Before After
Width: 180  |  Height: 180  |  Size: 43 KiB

BIN
img/thumbs/20200629_song-sparrow-thumb.jpg View File

Before After
Width: 180  |  Height: 180  |  Size: 46 KiB

BIN
img/thumbs/20200629_tree-swallow-feeding-thumb.jpg View File

Before After
Width: 180  |  Height: 180  |  Size: 44 KiB

BIN
img/thumbs/rad_nebula-thumb.jpg View File

Before After
Width: 180  |  Height: 180  |  Size: 9.5 KiB

+ 14
- 0
index.md View File

@ -0,0 +1,14 @@
---
layout: layouts/home.njk
eleventyNavigation:
key: Home
order: 1
---
Hi, I'm Noah Hall, a parent, organic gardener, birdwatcher, [political organizer](https://avldsa.org), [Unitarian Universalist](https://uuasheville.org), [web developer](https://github.com/nthall), and many other things. I'm interested in music, community, ecology, interconnectedness, the absurd and the ineffable.
This is my website. It's got some [writing](/blog/) (mostly about software), some [pictures](/photos/) (mostly of birds), some links to other things I've worked on, written, spent time doing, etc., and... I'm not sure what else! Honestly I've never been great at maintaining a personal *or* professional website, and I'm giving it an honest try for the first time now. So, this is very much an evolving and developing place, more than a static artifact.
I am currently working as a web consultant, leveraging over a decade of experience in software development to plan, build, and maintain web applications for a wide variety of clients. I aim to practice a humane, ethical software development that gives consideration to maintainability, accessibility, and privacy. I'm available for planning, project management, software development, system administration, and other services. Please [email me](mailto:noah@nthall.com) to discuss your needs.
I am also interested in talking to like-minded developers, technologists, writers, community managers, and any others who may be interested in forming a cooperative for either software consulting/agency work or product development, or both!

+ 0
- 14
index.njk View File

@ -1,14 +0,0 @@
---
layout: layouts/home.njk
eleventyNavigation:
key: Home
order: 1
---
<h1>Latest 3 Posts</h1>
{% set postslist = collections.posts | head(-3) %}
{% set postslistCounter = collections.posts | length %}
{% include "postslist.njk" %}
<p>More posts can be found in <a href="{{ '/posts/' | url }}">the archive</a>.</p>

+ 9
- 0
package.json View File

@ -30,5 +30,14 @@
"luxon": "^1.21.3",
"markdown-it": "^8.4.2",
"markdown-it-anchor": "^5.2.5"
},
"dependencies": {
"child_process": "^1.0.2",
"concurrent-transform": "^1.0.0",
"gulp-changed": "^4.0.2",
"gulp-image-resize": "^0.13.1",
"gulp-rename": "^2.0.0",
"gulp-sass": "^4.1.0",
"node-sass": "^4.14.1"
}
}

+ 19
- 0
pages/my-photography-gear.md View File

@ -0,0 +1,19 @@
---
layout: post
title: My Photography Gear
---
This is the gear I'm currently using for photography.
Cameras:
- Canon 7d Mark II (primary)
- Canon Rebel T3i (backup)
Lenses:
- Sigma Contemporary 150-600mm
- Canon EF-S 75-300mm
- Canon EF-S 24mm
Other:
- Topo Designs Camera Cube

+ 27
- 0
photos/_drafts/comet-neowise.md View File

@ -0,0 +1,27 @@
---
title: Photographing Comet Neowise
date: 2020-07-18
tags: space, photos
layout: photos
---
I took a drive up the Blue Ridge Parkway to try a new photography challenge: a comet! I've read a bit about astrophotography, so I had some ideas about what settings to use and what to expect, but I came back down the mountain with some lessons learned. I also came down with some pictures of a real comet! It was very fun and exciting; I always love a chance to be humbled by the enormity of the cosmos.
Some things I'm glad I brought:
- a jacket (it's chilly up there at night, even in July)
- binoculars to help locate the comet as it first became visible
- water
- extra camera battery
Some things I wish I'd brought:
- a towel or hanky to wipe off condensation while packing up
- a headlamp for fiddling with settings, plus packing up and getting back to my car in the dark
- a folding chair
- a snack
- someone to share the experience with
Some gear that would've helped if I'd had it:
- a remote shutter release
- a better tripod maybe?
I'm happy with the images I got, given it was a new setting and subject. Surely they could have been better, or at least I could have gotten the good ones more efficiently and had less deletion to do. I learned quickly that my setup had some unavoidable and ruinous shakiness after hitting the shutter, but I figured out that I could use the self-timer to let the camera settle before taking the picture. Some kind of remote trigger would be a more convenient way to eliminate that issue and get pictures faster - not that I regret spending any minute of my evening the way I did. I'm withholding final judgment until I've played with the images, but I think I have a little more reading to do to make the most of my next chance at space photography.

+ 5
- 0
photos/photos.json View File

@ -0,0 +1,5 @@
{
"tags": [
"photos"
]
}

+ 28
- 0
photos/yard_birds_early_summer.md View File

@ -0,0 +1,28 @@
---
layout: "photos"
title: "Yard Birds of Early Summer 2020"
photos:
-
file: "/img/20200629_phoebe-with-food.jpg"
title: "Phoebe perched with food"
alt: "an eastern phoebe perches on a piece of cattle panel, holding a bug in its beak."
-
file: "/img/20200629_song-sparrow.jpg"
title: "Song Sparrow"
alt: "a song sparrow perches in a bush, partially obscured by the leaves and looking at the camera."
-
file: "/img/20200629_fledgling-tree-swallow.jpg"
title: "Fledgling Tree Swallow"
alt: "a young tree swallow peers out from its nest box towards the camera."
-
file: "/img/20200629_tree-swallow-feeding.jpg"
title: "Tree Swallow Feeding"
alt: "one of the fledgling's parents have returned to share some food with the young bird!"
---
Isolation has me deeply missing my regular birding haunts. On the other hand, we're lucky to have a phenomenal, rich birding area all around us in our neighborhood! I've been trying to make the most of it, deepening my knowledge of and connection to this little stretch of valley.
---
Here are a few fun captures from my yard, and my next-door neighbor's birdhouse which has gifted us with the presence of a complement of tree swallows this year. There's always something going on if we remember to look.

+ 36
- 0
posts/django-migration-hiccup.md View File

@ -0,0 +1,36 @@
---
title: Django DB Migration Hiccup
date: 2017-03-14 01:28:43
tags:
- django
- django-rest-framework
- postgresql
- sqlite
- watershed
category:
- development
layout: post
---
I just ran into a minor issue that should have been very simple but took me a few moments to understand, so I wanted to quickly share the solution.
I've been running [Watershed](https://github.com/nthall/watershed) (my side-project, fun app) in very lazy debug mode for a small handful of users so far. That means `DEBUG = True`, and it also means the database was still sqlite3. I'll be fixing the `DEBUG` mode soon when I set up proper separation between prod and dev. This issue is something I ran into while migrating from sqlite to postgres.
The simplest way to change over to a new database engine, if you don't have anyone depending on uptime yet (I didn't!), is:
1. Set up postgresql (user & database created, permissions & socket settings, etc)
2. `$ python manage.py dumpdata > data.json`
3. Change settings.py to point to your new db
4. `$ python manage.py migrate`
5. `$ python manage.py loaddata data.json`
6. Redeploy
I ran into trouble on step 5. Watershed's backend relies heavily on [Django Rest Framework](https://www.django-rest-framework.org), including using their Authtoken scheme for authentication. The hiccup happened in `loaddata` hitting a duplicate key error when it tried to import those tokens.
First I just tried running `loaddata` a couple more times, because why would there be any keys in any table? That didn't work. So I opened up data.json in vim just to poke around. It *looked* like perfectly reasonable data (which I knew had been running fine anyway!) but it wasn't working.
Next I decided to load in all the data *except* the authtoken rows. If it worked I could just focus on the authtoken data without the entire DB hanging in the balance. I used `python -m json.tool data.json > pretty.json` to format the dump and opened it with vim. I cut out the authtoken data and put it in a separate json file that could be loaded on its own, just in case I needed to be able to do that. `pretty.json`, naturally, loaded flawlessly. So at least everything else looked fine.
At this point it occurred to me to open the dbshell and poke around. The problem table, `authtoken_token`, had been created with rows for all existing users, with a `created` date of the time of migration. Now, this is probably the better default for most cases, and not a real issue you'd run into in a production setup. But this isn't that! My app relies on saving the authtoken to local storage in a Chrome extension for authenticated requests, and the extension isn't yet smart enough to ask the user to log in again if their token is rejected. So, it was easiest for me to drop those rows, load in my old tokens, and make a few notes about filling out the authentication management in the extension.
If I had planned better and foreseen this class of problems, I could have fixed the extension to account for that possibility before migrating the database. In fact, it's almost certainly bad to assume that the token will never change; in more critical apps than mine, you might even regularly invalidate those tokens and generate replacements. But for now, things are mostly working again, and we're several steps closer to a real (beta) release. The rest, as always, can wait.

+ 0
- 26
posts/firstpost.md View File

@ -1,26 +0,0 @@
---
title: This is my first post.
description: This is a post on My Blog about agile frameworks.
date: 2018-05-01
tags:
- another-tag
layout: layouts/post.njk
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
## Section Header
Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.
``` text/2-3
// this is a command
function myCommand() {
let counter = 0;
counter++;
}
// Test with a line break above this line.
console.log('Test');
```

+ 0
- 15
posts/fourthpost.md View File

@ -1,15 +0,0 @@
---
title: This is my fourth post.
description: This is a post on My Blog about touchpoints and circling wagons.
date: 2018-09-30
tags: second-tag
layout: layouts/post.njk
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
## Section Header
Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.

+ 0
- 18
posts/secondpost.md View File

@ -1,18 +0,0 @@
---
title: This is my second post.
description: This is a post on My Blog about leveraging agile frameworks.
date: 2018-07-04
tags:
- number-2
layout: layouts/post.njk
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.
## Section Header
<a href="{{ '/posts/firstpost/' | url }}">First post</a>
<a href="{{ '/posts/thirdpost/' | url }}">Third post</a>
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.

+ 0
- 28
posts/thirdpost.md View File

@ -1,28 +0,0 @@
---
title: This is my third post.
description: This is a post on My Blog about win-win survival strategies.
date: 2018-08-24
tags:
- second-tag
layout: layouts/post.njk
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.
``` js/2/4
// this is a command
function myCommand() {
let counter = 0;
counter++;
}
// Test with a line break above this line.
console.log('Test');
```
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
## Section Header
Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.

+ 18
- 0
scss/_colors.scss View File

@ -0,0 +1,18 @@
// solarized
$base03: #002b36;
$base02: #073642;
$base01: #586e75;
$base00: #657b83;
$base0: #839496;
$base1: #93a1a1;
$base2: #eee8d5;
$base3: #fdf6e3;
$yellow: #b58900;
$orange: #cb4b16;
$red: #dc322f;
$magenta: #d33682;
$violet: #6c71c4;
$blue: #268bd2;
$cyan: #2aa198;
$green: #859900;

+ 49
- 0
scss/_photos.scss View File

@ -0,0 +1,49 @@
div.thumb-container {
display: flex;
flex-direction: row;
padding-top: 1em;
position: relative;
flex-wrap: wrap;
}
div.thumbnail {
max-width: 240px;
flex: 1 1 20px;
padding: 1em;
img {
max-width: 100%;
}
}
div.carousel {
@media (prefers-color-scheme: light) {
background-color: rgba($base3, .75);
}
@media (prefers-color-scheme: dark) {
background-color: rgba($base03, .75);
}
border-radius: .5em;
div.next, div.prev {
border-radius: 2em;
&:focus {
@media (prefers-color-scheme: light) {
background-color: rgba($cyan, .75);
span {
color: $base3;
}
}
@media (prefers-color-scheme: dark) {
background-color: rgba($cyan, .75);
span {
color: $base03;
}
}
}
span {
color: $cyan;
size: 4em;
}
}
}

+ 48
- 0
scss/_reset.scss View 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;
}

+ 63
- 0
scss/_solar.scss View File

@ -0,0 +1,63 @@
@import "_colors";
h1 {
font-size: 2em;
}
a, p, span, div {
font-size: 14px;
}
a {
color: $blue;
&::visited {
color: $violet;
}
&::hover {
text-decoration: none;
}
}
.space {
color: rgba(0,0,0,0);
-webkit-background-clip: text;
background-clip: text;
background-image: url('/static/rad_nebula.jpg');
background-position: center;
}
@media (prefers-color-scheme: light) {
body {
background-color: $base3;
color: $base00;
}
a, p, h1, h2, h3, h4, h5, h6, span, em, kbd, li, i, b {
&::selection {
background-color: $base2;
}
}
code::selection {
background-color: $base00;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: $base03;
color: $base0;
}
a, p, h1, h2, h3, h4, h5, h6, span, em, kbd, li, i, b, code {
&::selection {
background-color: $base02;
}
}
code::selection {
background-color: $base03;
}
}

+ 199
- 0
scss/style.scss View File

@ -0,0 +1,199 @@
@import "_reset";
@import "_colors";
@import "_solar";
@import "_photos";
body {
min-height: 480px;
font-size: 20px;
line-height: 24px;
padding-bottom: 1.5em;
position: relative;
}
a, p, span, div {
font-size: 16px;
line-height: 24px;
}
code, kbd {
border: 1px solid;
padding: 2px;
border-radius: 5px;
font-family: monospace;
@media(prefers-color-scheme: dark) {
background-color: $base02;
border-color: $base00;
color: $base1;
}
@media(prefers-color-scheme: light) {
border-color: $base0;
background-color: $base2;
color: $base01;
}
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.8em;
}
h3 {
font-size: 1.6em;
}
h4 {
font-size: 1.4em;
}
h5 {
font-size: 1.3em;
}
h6 {
font-size: 1.2em;
}
h1, h2, h3, h4, h5, h6 {
line-height: 3rem;
}
i, em {
font-style: italic;
}
.container {
display: flex;
flex-direction: row;
padding-top: 2em;
position: relative;
}
main {
flex: 3 1 70%;
margin-left: 6em;
padding-right: 1.5em;
width: 30px;
order: 1;
height: 100%;
article {
p {
margin-bottom: .5em;
text-indent: 1.5em;
}
}
}
#sidebar {
flex: 1 6 25%;
width: 30px;
order: 2;
@media (prefers-color-scheme: light) {
border-left: 1px dashed $base0;
}
@media (prefers-color-scheme: dark) {
border-left: 1px dashed $base00;
}
padding-left: 1em;
height: 100%
p {
margin-bottom: .5em;
}
ul {
margin-bottom: .5em;
li {
list-style-type: none;
margin-left: 1rem;
}
}
}
header {
height: 12em;
background-image: url('/img/rad_nebula.jpg');
background-size: cover;
background-position: center;
padding-top: 3em;
text-align: center;
.title-wrap {
padding-top: 4em;
}
h1 > a {
font-size: 2em;
text-decoration: none;
}
h2 {
font-size: 2em;
}
#nav {
margin: 0;
ul li {
float: left;
display: inline;
list-style-type: none;
&:after {
clear: both;
}
}
}
}
footer {
width: 100%;
margin: 0px 0px .5em 0px;
position: absolute;
bottom: 0px;
clear: both;
* {
display: block;
margin: auto;
width: max-content;
}
}
.title-wrap {
border-radius: .5em;
@media (prefers-color-scheme: light) {
background-color: rgba($base3, .75);
}
@media (prefers-color-scheme: dark) {
background-color: rgba($base03, .75);
}
width: 40%;
margin-left: 30%;
padding: 3em 1em;
}
ol {
list-style-type: decimal;
list-style-position: inside;
line-height: 2rem;
margin-left: 1.5em;
}
ul.menu {
li {
display: inline-block;
margin: 1em .5em;
a {
color: $magenta;
text-decoration: none;
}
}
}
.post-date {
font-style: italic;
font-size: 16px;
}
.hidden {
display: none;
}

archive.njk → templates/archive.njk View File


page-list.njk → templates/page-list.njk View File


+ 18
- 0
templates/photoblog.njk View File

@ -0,0 +1,18 @@
---
layout: layouts/layout.njk
permalink: /photos/
eleventyNavigation:
key: Photos
order: 3
---
<h1>Photography</h1>
<p>My photography isn't particularly artful or unique, but it is part of my personal practice of mindfulness and deepening connection with nature. I maintain a list of <a href="/pages/{{ my-photo-gear | url }}">my gear</a> for anyone interested in that sort of thing.</p>
<br>
{% set postslist = collections.photos %}
{% include "postslist.njk" %}
<br>
<p>My photography, like everything else on this site, is licensed under the Cooperative Non-violent Public License. Please <a href="mailto:noah@nthall.com" target="_blank">email me</a> to inquire about alternative licensing.</p>

+ 11
- 0
templates/post.njk View File

@ -0,0 +1,11 @@
---
layout: layouts/layout.njk
---
{% block body %}
<h2>{{ page.title }}</h2>
<span class='post-date'>Posted {{ page.date }}{% if page.updated %}; Last updated {{ page.updated }}{% endif %}</span>
<br />
<article>
{{ page.content }}
</article>
{% endblock %}

sitemap.xml.njk → templates/sitemap.xml.njk View File


tags-list.njk → templates/tags-list.njk View File


tags.njk → templates/tags.njk View File


Loading…
Cancel
Save