New theme, who dis?

This commit is contained in:
Garrett Mills 2025-02-22 20:10:24 -05:00
parent e443623e26
commit da1aa660ca
50 changed files with 1043 additions and 244 deletions

View File

@ -39,7 +39,11 @@ export class Home extends Controller {
@Inject()
protected readonly session!: Session
public async welcome(feedPosts: Collection<FeedPost>) {
public async welcome() {
return view('welcome')
}
public async welcome70s(feedPosts: Collection<FeedPost>) {
const workItems = await this.getWorkItems()
const workItemYears = workItems.map(item => item.startDate.getFullYear()).unique()
@ -47,7 +51,7 @@ export class Home extends Controller {
.map(item => item.startDate.getFullYear())
.unique()
return view('welcome', {
return view('welcome_70s', {
...this.getThemeCSS(),
feedPosts: feedPosts.toArray(),
workItemGroups: workItems.groupBy(item => item.startDate.getFullYear()),
@ -66,6 +70,13 @@ export class Home extends Controller {
})
}
public style() {
return view('style', {
title: 'Style Test',
...this.getThemeCSS(),
})
}
public optOutPrompt() {
return view('message', {
title: 'Analytics Opt-Out',

View File

@ -18,9 +18,15 @@ Route.endpoint('options', '**')
Route
.group('/', () => {
Route.get('/')
Route.get('/70s')
.pre(SiteTheme)
.parameterMiddleware(LoadFeedPosts)
.calls<Home>(Home, home => home.welcome70s)
.alias('home2')
Route.get('/')
// .pre(SiteTheme)
// .parameterMiddleware(LoadFeedPosts)
.calls<Home>(Home, home => home.welcome)
.alias('home')
@ -121,6 +127,10 @@ Route
.pre(SiteTheme)
.calls<Home>(Home, home => home.technical)
Route.get('/style')
.pre(SiteTheme)
.calls<Home>(Home, home => home.style)
Route.get('/analytics/opt-out')
.pre(SiteTheme)
.calls<Home>(Home, home => home.optOutPrompt)

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

View File

@ -147,6 +147,7 @@ b {
.section-border .section-border-inner-2 {
border-top: 15px solid var(--c-line-2);
}
body {
background: var(--c-background);
color: var(--c-font);

View File

@ -0,0 +1,314 @@
@font-face {
font-family: 'Obsidian';
src: url('font/obsidian/Obsidian-Roman.otf') format('opentype');
font-weight: normal;
font-style: normal;
/*font-display: swap;*/
}
@font-face {
font-family: "JetBrains Mono";
src: url("font/jetbrains-mono/JetBrainsMono-Regular.woff2") format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Reckless";
src: url("font/reckless/WEB/Reckless-Regular.woff2") format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Reckless";
src: url("font/reckless/WEB/Reckless-RegularItalic.woff2") format('woff2');
font-weight: normal;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Reckless";
src: url("font/reckless/WEB/Reckless-Bold.woff2") format('woff2');
font-weight: bold;
font-style: normal;
font-display: swap;
}
body {
--background: #111;
--background-2: #252525;
--background-3: #444;
--color: #fffbe3;
--color-2: #d0c895;
background: var(--background);
color: var(--color);
font-family: "Reckless", serif;
margin: 0;
padding: 0;
overflow-x: hidden;
display: flex;
flex-direction: column;
align-items: center;
}
.wrapper {
max-width: 800px;
width: calc(100% - 40px);
padding-left: 20px;
padding-right: 20px;
margin-bottom: 100px;
min-height: 100vh;
}
code {
font-family: "JetBrains Mono", monospace;
font-size: 0.8em;
color: var(--color-2);
}
p, h1, h2, h3, h4, h5, h6, li {
font-size: 1.3em;
}
h1:before, h2:before, h3:before, h4:before, h5:before, h6:before {
font-family: "Reckless", serif;
color: var(--color-2);
margin-right: 10px;
}
h1 {
font-size: 2em;
margin: 0;
margin-top: 50px;
}
h1:before {
content: '#';
/*margin-left: -27px;*/
}
h2 {
font-size: 1.7em;
margin: 0;
margin-top: 50px;
}
h2:before {
content: '##';
/*margin-left: -27px;*/
}
h3 {
font-size: 1.4em;
margin: 0;
margin-top: 50px;
}
h3:before {
content: '###';
/*margin-left: -27px;*/
}
h4 {
font-size: 1.3em;
color: var(--color-2);
margin: 0;
margin-top: 50px;
}
h4:before {
content: '####';
/*margin-left: -27px;*/
}
h5 {
font-size: 1.3em;
color: var(--color-2);
margin: 0;
margin-top: 50px;
}
h5:before {
content: '#####';
/*margin-left: -27px;*/
}
h6 {
font-size: 1.3em;
color: var(--color-2);
margin: 0;
margin-top: 50px;
}
h6:before {
content: '######';
/*margin-left: -27px;*/
}
a {
color: #dfdbc3;
}
p {
margin: 20px 0 0;
padding: 0;
}
.feed-item {
margin: 30px 0;
background: var(--background-2);
padding: 15px;
border: 1px solid var(--background-3);
}
.feed-item .tag, .secondary {
color: var(--color-2);
font-style: italic;
}
.secondary {
font-size: 0.8em;
}
.button-links a {
margin-right: 10px;
}
.obsidian {
font-family: "Obsidian", serif;
}
.hero {
font-family: "Obsidian", serif;
font-size: 11em;
margin-top: 0;
}
nav {
margin-top: 25px;
padding-bottom: 25px;
margin-bottom: 25px;
border-bottom: 1px solid #7f7b63;
/*margin-bottom: 150px;*/
}
nav ul {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 30px;
}
nav ul a {
color: #fffbe3;
text-decoration: none;
}
nav ul a:hover {
color: #cfcbb3;
}
blockquote {
border-left: 2px solid var(--color-2);
color: var(--color-2);
padding-left: 10px;
margin-left: 10px;
}
img {
max-width: 100%;
}
center {
margin: 0 20px;
color: var(--color-2);
}
pre {
overflow-x: scroll;
}
hr {
border-color: var(--color-2);
margin: 30px 0;
}
button {
border: 1px solid var(--background-3);
color: var(--color-2);
background: var(--background-2);
padding: 3px 15px;
margin: 15px 0;
transition: all 0.1s linear;
}
button:hover {
cursor: pointer;
color: var(--color);
background: var(--background);
border-color: var(--color-2);
}
ul.plain {
list-style-type: none;
margin: 0;
padding: 0;
}
ul.plain li {
margin: 20px 0;
}
.italic {
font-style: italic;
}
#about {
margin-top: 50px;
}
footer {
width: 100%;
min-height: 300px;
background-color: #ffffff08;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
footer .obsidian {
font-size: 4em;
margin-right: 30px;
padding-right: 30px;
border-right: 1px solid #fffbe1;
}
footer p {
margin: 5px 0 0;
}
.inline-markmark-logo {
height: 16pt;
margin-bottom: -3px;
opacity: 0.75;
}
@media screen and (max-width: 500px) {
nav ul {
flex-direction: column;
gap: 20px;
}
footer {
flex-direction: column;
align-items: start;
}
footer .obsidian, footer p {
margin: 0 50px;
}
footer .obsidian {
border-right: unset;
margin-bottom: 20px;
}
}

349
src/app/resources/assets/normalize.css vendored Normal file
View File

@ -0,0 +1,349 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

View File

@ -1,7 +1,7 @@
extends template
block blog_content
h2(style='font-size: 36pt') Uh, oh!
h2 Uh, oh!
p Looks like that post no longer exists, or the link is broken.
p
a.button(href=named('home') + '#contact') Let Me Know
a.button(href=named('home') + '#contact') Let me know.

View File

@ -1,12 +1,13 @@
extends template
block blog_content
h2(style="font-size: 36pt") Post Archive
h2 Post Archive
.recent-posts
each year in postYears
h3(style="background-color: var(--c-font)") #{year}
each post in postsByYear[year]
.post-tile
.date #{blogDate(post.date)}
a.title(href=blogUrl(post)) #{post.title}
h3 #{year}
ul.plain
each post in postsByYear[year]
li
.secondary #{blogDate(post.date)}
a.title(href=blogUrl(post)) #{post.title}

View File

@ -1,7 +1,7 @@
extends template
block blog_content
h2(style="font-size: 36pt") An RSS Manifesto
h2 An RSS Manifesto
blockquote More than a convenience, <em>RSS is good for the web</em>.

View File

@ -3,10 +3,11 @@ extends template
block blog_content
p Write-ups and musings, often technical, sometimes not.
h2(style="font-size: 36pt") Recent(ish) Posts
h2 Recent(ish) Posts
.recent-posts
each post in posts
.post-tile
.date #{blogDate(post.date)}
a.title(href=blogUrl(post)) #{post.title}
ul.plain
each post in posts
li
.secondary #{blogDate(post.date)}
a.title(href=blogUrl(post)) #{post.title}

View File

@ -23,7 +23,7 @@ block meta
// fixme: twitter:image
block append style
link(rel='stylesheet' href=asset('highlight/styles/' + themeRecord.highlightTheme + '.min.css'))
link(rel='stylesheet' href=asset('highlight/styles/dark.min.css'))
block append script
script(src=asset('highlight/highlight.min.js'))
@ -38,10 +38,9 @@ block blog_content
h2.post-title #{post.title}
p.post-byline by Garrett Mills on #{blogDate(post.date)}
.post-tags
ul
p.button-links
each tag in post.tags
li
a.button(href=named('blog')+'/tag/'+tag) ##{tag}
a.button-small.secondary(href=named('blog')+'/tag/'+tag) ##{tag}
.post-content !{renderedPost}
@ -51,6 +50,7 @@ block blog_content
.section-border-inner-2
.comments-container
hr
h1 Comments
p Thanks for reading! I'd love to hear your thoughts and questions.
p My blog uses an email-based comments system: <a href="mailto:#{chorusAddress}">submit a comment</a>

View File

@ -1,10 +1,11 @@
extends template
block blog_content
h2(style="font-size: 36pt; font-family: Lora, serif") ##{tag}
h2 Posts w/ Tag: #{tag}
.recent-posts
each post in posts
.post-tile
.date #{blogDate(post.date)}
a.title(href=blogUrl(post)) #{post.title}
ul.plain
each post in posts
li
.secondary #{blogDate(post.date)}
a.title(href=blogUrl(post)) #{post.title}

View File

@ -1,9 +1,9 @@
extends template
block blog_content
h2(style="font-size: 36pt") Tags
h2 Tags
.post-tags.listed
ul
each tag in tags
li <a class="button" href="#{named('blog')}/tag/#{tag}">##{tag}</a> (#{counts[tag]})
li <a class="button" href="#{named('blog')}/tag/#{tag}">##{tag}</a>&nbsp;&nbsp;&nbsp;<span style="color: var(--color-2); font-variant-ligatures: none;">(#{counts[tag]})</a>

View File

@ -1,4 +1,4 @@
extends ../template_70s
extends ../template_bam
block append style
link(rel='alternate' href=named('blog:atom') title="Garrett's Blog" type='application/atom+xml')
@ -8,27 +8,12 @@ block append style
block content
.container#top
.inner
if !post
.hero
// FIXME: make this the default?
.hero-box(style='margin-bottom: 0')
h1 Garrett Mills
section#blog-header
if !post
h2 Blog
else
h2(style='font-size: 26pt; padding-top: 20px') Garrett's Blog
ul.inline-nav
li
a.button(href=named('blog')) Home
li
a.button(href=named('home')) Main Site
li
a.button(href=named('blog:archive')) Archive
li
a.button(href=named('blog:tags')) Tags
li
a.button(href=named('blog:feeds')) Feeds
h1 Garrett's Blog
p.button-links
a.button(href=named('blog')) Home
a.button(href=named('blog:archive')) Archive
a.button(href=named('blog:tags')) Tags
a.button(href=named('blog:feeds')) Feeds
block blog_content

View File

@ -1,17 +1,14 @@
extends template_70s
extends template_bam
block content
.container#top
.inner
.hero
.hero-box
h1 Garrett Mills
section#recent
h2 posts & updates
.button-links
a.button-small(href='/feed/rss.xml') rss
a.button-small(href='/feed/atom.xml') atom
a.button-small(href='/feed/json.json') json
h1 Notes
p.button-links
a.button-small(href='/feed/rss.xml') RSS
a.button-small(href='/feed/atom.xml') Atom
a.button-small(href='/feed/json.json') JSON
each item in feedPosts
.feed-item
.feed-category(id=item.feedPostId)
@ -21,4 +18,4 @@ block content
p.text (draft)
p.text !{item.body}
.bottom
.stamp <a href="#{named('feed')}##{item.feedPostId}" class="feed-edit-button">permalink</a>&nbsp;&nbsp;|&nbsp;&nbsp;#{item.postedAt.toLocaleString()}
.stamp <a href="#{named('feed')}##{item.feedPostId}" class="feed-edit-button">permalink</a>&nbsp;&nbsp;|&nbsp;&nbsp;#{item.postedAt.toLocaleDateString()}

View File

@ -1,18 +1,15 @@
extends template_70s
extends template_bam
block content
.container#top
.inner
.hero
.hero-box
h1 Garrett Mills
section#header
h1 Bookmarks
p Below is a random collection of links to other sites that I found interesting.
p I think publishing personal bookmark lists is a great way to better connect and explore the smaller side of the Internet.
p This list is also available in the following formats:
ul
li <a href="#{route('/links.mark.md')}"><img class="inline-markmark-logo" src="#{asset(textIsLight ? 'markmark-light.svg' : 'markmark-dark.svg')}"/> MarkMark</a> (<a href="#{route('/markmark')}">learn more</a>).
li <a href="#{route('/links.mark.md')}"><img class="inline-markmark-logo" src="#{asset('markmark-light.svg')}"/> MarkMark</a> (<a href="#{route('/markmark')}">learn more</a>).
li <a href="#{named('links:rss')}">RSS</a>
li <a href="#{named('links:atom')}">Atom</a>
li <a href="#{named('links:json')}">JSON</a>
@ -22,15 +19,22 @@ block append style
style.
section { margin-top: 0; padding-bottom: 0; }
h1 { font-size: 40pt; }
h2 { font-size: 30pt; }
h3 { font-size: 24pt; }
h4 { font-size: 18pt; }
#links h1:before {
content: unset;
}
#links h1 { font-size: 25pt; margin-top: 60px; }
#links h1 p {
font-size: 0.8em;
}
*:not(li) > ul > li { margin-top: 20px; }
#links li p {
font-size: 1em;
}
#links .link-url a {
font-size: 0.7em;
}
.markmark.link-tags { margin-left: 30px; }
.markmark.link-tag { color: var(--c-font-muted); font-size: 0.8em; }
.markmark.link-date { margin-left: 30px; color: var(--c-font-muted); font-size: 0.8em; }
.markmark.link-tag { color: var(--color-2); font-size: 0.8em; }
.markmark.link-date { margin-left: 30px; color: var(--color-2); font-size: 0.8em; }

View File

@ -1,9 +1,9 @@
extends template_70s
extends template_bam
block content
.container#home
.inner(style='max-width: 40vw; margin-top: 50px')
h3 !{title}
h1 !{title}
p !{message}
if buttonAction
form(action=buttonAction method=(buttonMethod || 'get'))

View File

@ -1,4 +1,4 @@
extends template_70s
extends template_bam
block append style
link(rel='stylesheet' data-name='vs/editor/editor.main' href=asset('monaco/package/min/vs/editor/editor.main.css'))
@ -7,8 +7,8 @@ block content
.container#home
if needsAccessKey
.inner
h2 Snippet
h3 #{snippet.slug}
h1 Snippet
h2 #{snippet.slug}
p This snippet is protected by an access key. Please enter it to view:
form(method='get')
.form-group
@ -16,8 +16,8 @@ block content
button View
else if needsConfirm
.inner
h2 Snippet
h3 #{snippet.slug}
h1 Snippet
h2 #{snippet.slug}
p This snippet is single-access only. Once you view it, it will be permanently deleted. Continue?
form(method='get')
if accessKey
@ -26,9 +26,9 @@ block content
button Continue
else
.inner(style='width: 100%')
h2 Snippet
h3 #{snippet.slug}
#monaco-container(style="width: 100%; height: 100%")
h1 Snippet
h2 #{snippet.slug}
#monaco-container(style="width: 100%; height: 90vh")
block append script
script.

View File

@ -0,0 +1,25 @@
extends template_bam
block content
section
h1 Header 1
p Lorem ipsum dolor sit amet. Now is the time for all good men to come to the aid of their party before the quick brown fox jumps over the lazy little puppy dog.
h2 Header 2
p Lorem ipsum dolor sit amet. Now is the time for all good men to come to the aid of their party before the quick brown fox jumps over the lazy little puppy dog.
h3 Header 3
p Lorem ipsum dolor sit amet. Now is the time for all good men to come to the aid of their party before the quick brown fox jumps over the lazy little puppy dog.
h4 Header 4
p Lorem ipsum dolor sit amet. Now is the time for all good men to come to the aid of their party before the quick brown fox jumps over the lazy little puppy dog.
h5 Header 5
p Lorem ipsum dolor sit amet. Now is the time for all good men to come to the aid of their party before the quick brown fox jumps over the lazy little puppy dog.
h6 Header 6
p Lorem ipsum dolor sit amet. Now is the time for all good men to come to the aid of their party before the quick brown fox jumps over the lazy little puppy dog.
ul
li Item 1
li Item 2
li Item 3
ol
li Item 3
li Item 2
li Item 1
p Lorem <a href="#">ipsum dolor sit</a> amet. Now is the time for all good men to come to the aid of their party before the quick brown fox jumps over the lazy little puppy dog.

View File

@ -1,79 +1,55 @@
extends template_70s
extends template_bam
block content
.container#top
.inner
.hero
.hero-box
h1 Garrett Mills
section#technical
h2(style="font-size: 48pt") technical details for nerds
.fira-p
p This page contains a smattering of technical information that I think some people might find interesting, but not enough people for it to be included on the main page.
section#technical
h1 Technical Details for Nerds
p This page contains a smattering of technical information that I think some people might find interesting, but not enough people for it to be included on the main page.
h3 Website Theme
p I was going for a retro/70s aesthetic for this site. The CSS theme is fully-parameterized over a set of color variables.
p The first time you visit the homepage, the server randomly selects a theme for your browser to use across the site.
p I'm using a combination of <a href="https://creativemarket.com/storytypefont/6695985-The-Wobliy-Retro-Serif-Font" target="_blank">The Wobliy</a> as a display font and <a href="http://cyreal.org/fonts/lora" target="_blank">Lora</a> as a body font.
p For the curious, you can change the theme using the buttons below:
h2 Website Theme
p I had a lot of fun with the <a href="https://static.garrettmills.dev/assets/70s.png" target="_blank">previous 70s-inspired design</a>. I think visual design can do a lot to communicate a <i>vibe</i>.
p The current vibe feels like <i>less</i>. Less visual complexity, less scope, less techno-optimism, and perhaps a <i>bit</i> less pretentiousness.
p This design is my attempt to return to a <a href="https://motherfuckingwebsite.com/" target="_blank">simpler</a> <a href="http://bettermotherfuckingwebsite.com/" target="_blank">design</a> <a href="https://thebestmotherfucking.website/" target="_blank">language</a> that better conveys my current mood, with really (<i>really</i>) good fonts on a solid background.
ul.theme-buttons
each key in themeKeys
li
a.button(href=`../technical?theme=${key}`) #{config(`app.colors.${key}.displayName`)}#{key === themeName ? ' (current)' : ''}
br
br
h3 Fonts
p For the new logo, I'm using the <a href="https://www.typography.com/fonts/obsidian/overview" target="_blank">Obsidian Roman</a> display font by Hoefler & Co. I don't love to support Monotype, but Obsidian is just <i>so good</i>.
p The body font is comprised of a few variants of <a href="https://displaay.net/typeface/reckless-collection/reckless/" target="_blank">Reckless</a> by Displaay.
p For code/monospace text, I'm using my go-to font for code, <a href="https://www.jetbrains.com/lp/mono/" target="_blank">JetBrains Mono</a>. It's probably the single font I spend the most time looking at.
p You can also change the theme using the little-known <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Alternative_style_sheets" target="_blank">alternate stylesheets</a> feature by going to View > Page Style in your browser.
h2#license Source Code & Licensing
a(href='https://creativecommons.org/licenses/by-nc-sa/4.0/' target='_blank')
img(style='margin-top: 15px' src=asset('cc-by-nc-sa.png'))
p This website, its source code, and its contents are licensed under the terms of the Creative Commons BY-NC-SA 4.0 license. Learn more <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">here</a>.
p The source code for this site is available openly under the terms of the aforementioned license. You can view it <a href="https://code.garrettmills.dev/garrettmills/www" target="_blank">here</a>.
h4 The Stars
p The homepage of this site features a constellation of 6 translucent stars which appears at the top of the page.
p The positions of the stars are randomly generated based on the dimensions of the page. To discourage clustered/skewed constellations, the following criteria are used:
ol
li Standard deviation of the x-axis offset
li Standard deviation of the y-axis offset
li Standard deviation of the centroid distance
p Because it's interesting, you can view the # of generation attempts, as well as the aforementioned criteria in the footer of the homepage.
h2 Framework
p This site is built with Extollo, my free & libre application framework. You can learn more about Extollo <a href="https://extollo.garrettmills.dev" target="_blank">here</a>.
h3#license Source Code & Licensing
a(href='https://creativecommons.org/licenses/by-nc-sa/4.0/' target='_blank' style='margin-top: 15px')
img(src=asset('cc-by-nc-sa.png'))
p This website, its source code, and its contents are licensed under the terms of the Creative Commons BY-NC-SA 4.0 license. Learn more <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">here</a>.
p The source code for this site is available openly under the terms of the aforementioned license. You can view it <a href="https://code.garrettmills.dev/garrettmills/www" target="_blank">here</a>.
h2 Analytics
p This site, and some of the others in my <code>*.garrettmills.dev</code> platform record pseudo-anonymous page-view information. In particular, it stores:
ul
li Remote hostname
li Incoming IP address(es)
li Request method
li Original request URL
li Whether or not the request was XHR
li Timestamp of access
p Collecting the above information helps me gauge the rate-of-access of various pages on my platform over time.
p My analytics system is home-grown, intentionally, to record <em>just</em> the information I wanted to use. So, I avoid collecting any additional information than is necessary for my purposes.
h3 Framework
p This site is built with Extollo, my free & libre application framework. You can learn more about Extollo <a href="https://extollo.garrettmills.dev" target="_blank">here</a>.
h3 Opting-Out
p <small>Status: !{isOptOut ? 'You have opted-out of page view recording.' : 'Will record page views.'}</small>
p Even though my analytics-system is geared towards individual privacy and minimal obtrusiveness, you can still opt-out to avoid having your page views recorded for <code>*.garrettmills.dev</code> sites.
p To do so, click <a href="#{named('opt-out-prompt')}">here</a>.
if false
h3 The Background Animation
p The cascading code animation you see on some pages of this site is generated using real code pulled from my personal repositories.
p When you access a page, the backend for this site calls an API endpoint on my <a href="https://code.garrettmills.dev/garrettmills" target="_blank">Gitea server</a> to load a code snippet from a random file.
p Then, that snippet is converted to a double-layer SVG. The animation comes from automatically generating a series of CSS animation steps using <code>nth-child(...)</code> CSS calls on the various text-stroke lines.
p (Try "view page source" on the main page to see an example.)
h3 Collection Methods
p It's not super relevant to the privacy aspect, but for those who are curious, I record page-views two different ways:
ul
li On first-party <code>garrettmills.dev</code> pages, the page-views are recorded on the back-end when the page is served.
li For other <code>*.garrettmills.dev</code> pages (like my static host, CoreID, and Noded), a short script makes a request to a CORS-enabled endpoint to record the view.
p When you opt-out of page-view recording, this site sets a CORS-compatible cookie <code>analytics.optout</code> in your browser. If the analytics endpoints detect this cookie, the page view will not be recorded.
h3 Analytics
p This site, and some of the others in my <code>*.garrettmills.dev</code> platform record pseudo-anonymous page-view information. In particular, it stores:
ul
li Remote hostname
li Incoming IP address(es)
li Request method
li Original request URL
li Whether or not the request was XHR
li Timestamp of access
p Collecting the above information helps me gauge the rate-of-access of various pages on my platform over time.
p My analytics system is home-grown, intentionally, to record <em>just</em> the information I wanted to use. So, I avoid collecting any additional information than is necessary for my purposes.
h4 Opting-Out
p <small>Status: !{isOptOut ? 'You have opted-out of page view recording' : 'Will record page views'}</small>
p Even though my analytics-system is geared towards individual privacy and minimal obtrusiveness, you can still opt-out to avoid having your page views recorded for <code>*.garrettmills.dev</code> sites.
p To do so, click <a href="#{named('opt-out-prompt')}">here</a>.
h4 Collection Methods
p It's not super relevant to the privacy aspect, but for those who are curious, I record page-views two different ways:
ul
li On first-party <code>garrettmills.dev</code> pages, the page-views are recorded on the back-end when the page is served.
li For other <code>*.garrettmills.dev</code> pages (like my static host, CoreID, and Noded), a short script makes a request to a CORS-enabled endpoint to record the view.
p When you opt-out of page-view recording, this site sets a CORS-compatible cookie <code>analytics.optout</code> in your browser. If the analytics endpoints detect this cookie, the page view will not be recorded.
section#login
a(href=named('@auth:coreid:login')) Login
section#login
br
br
br
a(href=named('@auth:coreid:login')) Login

View File

@ -80,7 +80,7 @@ body
a(href='/#work') what I'm working on
li
a(href='/#contact') get in touch
li
//li
a(href='/#recent') latest updates
.col
ul.links

View File

@ -0,0 +1,106 @@
doctype html
head
style.
body {
--background: #111;
--background-2: #252525;
--background-3: #444;
--color: #fffbe3;
--color-2: #d0c895;
background: var(--background);
color: var(--color);
font-family: "Reckless", serif;
margin: 0;
padding: 0;
overflow-x: hidden;
display: flex;
flex-direction: column;
align-items: center;
}
.wrapper {
max-width: 800px;
width: calc(100% - 40px);
padding-left: 20px;
padding-right: 20px;
margin-bottom: 100px;
min-height: 100vh;
}
block meta
meta(charset='utf-8')
meta(name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no')
meta(http-equiv='x-ua-compatible' content='ie=edge')
meta(name='description' content='Hi, there! My name is Garrett. I am a computer scientist, software engineer, and speaker.')
meta(name='keywords' content='garrett mills glmdev developer speaker flitter extollo student')
meta(name='author' content=config('app.name', 'Garrett Mills'))
meta(name='robots' content='index, follow')
block title
if title
title #{title} | #{config('app.name', 'Garrett Mills')}
else
title #{config('app.name', 'Garrett Mills')}
block style
link(rel='stylesheet' href=asset('normalize.css'))
link(rel='stylesheet' href=asset('main-bam.css'))
//script.
// window.glmdev = window.glmdev || {}
// window.glmdev.themeStats = window.glmdev.themeStats || []
// window.glmdev.themeStats.push('Default Theme: !{themeDisplayName}')
//link(rel='stylesheet' href=asset('main-70s.css'))
//link(rel='stylesheet' href=`/theme/${themeName}.css` title=themeDisplayName)
//if themeStylesheets
// each sheet in themeStylesheets
// link(rel='alternate stylesheet' href=sheet.url title=sheet.displayName)
link(rel='author' href='/humans.txt')
//link(rel="alternate" href="/links.mark.md" title="Garrett Mills - My Bookmarks" type="text/markdown;variant=markmark")
//link(rel="alternate" href="/links" title="Garrett Mills - My Bookmarks" type="text/html")
//link(rel="alternate" href="/links/atom.xml" title="Garrett's Bookmarks (Atom)" type="application/atom+xml")
//link(rel="alternate" href="/links/rss2.xml" title="Garrett's Bookmarks (RSS)" type="application/rss+xml")
//link(rel="alternate" href="/links/json.json" title="Garrett's Bookmarks (JSON)" type="application/feed+json")
//link(rel="alternate" href="/feed/atom.xml" title="Garrett Mills - Posts & Updates (Atom)" type="application/atom+xml")
//link(rel="alternate" href="/feed/rss.xml" title="Garrett Mills - Posts & Updates (RSS)" type="application/rss+xml")
//link(rel="alternate" href="/feed/json.json" title="Garrett Mills - Posts & Updates (JSON)" type="application/feed+json")
//link(rel="alternate" href="/blog/atom.xml" title="Garrett's Blog (Atom)" type="application/atom+xml")
//link(rel="alternate" href="/blog/rss2.xml" title="Garrett's Blog (RSS)" type="application/rss+xml")
//link(rel="alternate" href="/blog/json.json" title="Garrett's Blog (JSON)" type="application/feed+json")
//link(rel='apple-touch-icon' sizes='180x180' href=asset('favicon/apple-touch-icon.png'))
//link(rel='manifest' href=asset('favicon/site.webmanifest'))
//link(rel='icon' type='image/png' sizes='32x32' href=asset('favicon/favicon-32x32.png'))
//link(rel='icon' type='image/png' sizes='16x16' href=asset('favicon/favicon-16x16.png'))
//link(rel='shortcut icon' href=asset('favicon/favicon.ico'))
body
.wrapper
header.hero glm.
nav
ul
li
a(href='/') Home
//li
a(href='/timeline') Timeline
li
a(href='/blog') Blog
li
a(href='/feed') Notes
li
a(href='/links') Bookmarks <img class="inline-markmark-logo" src="#{asset('markmark-light.svg')}"/>
block content
footer
.obsidian glm.
p &copy 2015-#{(new Date).getFullYear()} Garrett Mills — <a href="/technical">More Info</a>
if user()
p &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
a(href="/dash") Dashboard
block script

View File

@ -1,95 +1,18 @@
extends template_70s
extends template_bam
block content
.container#home
.inner
section.hero.full-height
.stars
each val in [1, 2, 3, 4, 5, 6]
svg(id=('star-' + val) width='47.581146mm' height='78.547104mm' viewbox='0 0 47.581146 78.547104' version='1.1' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg')
sodipodi:namedview#namedview7(pagecolor='#ffffff' bordercolor='currentColor' borderopacity='0.25' inkscape:showpageshadow='2' inkscape:pageopacity='0.0' inkscape:pagecheckerboard='0' inkscape:deskcolor='#d1d1d1' inkscape:document-units='mm' showgrid='false')
defs#defs2
g#layer1(inkscape:label='Layer 1' inkscape:groupmode='layer' transform='translate(-81.41733,-109.42827)')
path#path1153(style='fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:7;stroke-linecap:round;stroke-dasharray:none' d='m 105,112.92827 c 0,0 1.66434,35.57173 -20.082663,35.57173' inkscape:export-filename='star.svg' inkscape:export-xdpi='96' inkscape:export-ydpi='96')
path#path1153-3(style='fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:7;stroke-linecap:round;stroke-dasharray:none' d='m 105.41582,112.92827 c 0,0 -1.66434,35.57173 20.08267,35.57173')
path#path1153-6(style='fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:7;stroke-linecap:round;stroke-dasharray:none' d='m 105,184.47538 c 0,0 1.66434,-35.57173 -20.082665,-35.57173')
path#path1153-3-7(style='fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:7;stroke-linecap:round;stroke-dasharray:none' d='m 105.41582,184.47538 c 0,0 -1.66434,-35.57173 20.08267,-35.57173')
.hero-box
h1 Garrett Mills
p Software engineer, computer scientist, and nerd.
//p Hi, there. My name is Garrett, and I'm a computer scientist, software engineer, and speaker.
.section-border
.section-border-inner-1
.section-border-inner-2
section#about
h2 about me
.about-container
.img
img(src="/assets/img/profile.jpg" width=300 height=300)
.about
p Hi, I'm Garrett. Welcome to my corner of the internet.
p I'm a computer scientist and software engineer specializing in software/systems architectures and programming languages. Some of my more recent projects include <a href="https://github.com/swarmlang/swarm" target="_blank">the Swarm programming language</a>, <a href="https://extollo.garrettmills.dev" target="_blank">the Extollo framework</a>, and <a href="https://code.garrettmills.dev/Starship/CoreID" target="_blank">the CoreID SSO server</a>.
p I sometimes write <a href="#{named('blog')}">blog posts</a> and <a href="https://code.garrettmills.dev/garrettmills" target="_blank">publish code</a> from my projects.
.section-border.odd
.section-border-inner-1
.section-border-inner-2
if workItemYears && workItemYears.length
section#work
h2 what I'm working on
.timeline-group
each year in workItemYears
.work-container(class=(workItemHiddenYears.includes(year) ? 'timeline-item theme-hide' : 'timeline-item'))
.timeline-container
.timeline-year #{year}
each item in workItemGroups[year]
.work-container(class=(item.endDate ? 'timeline-item theme-hide' : 'timeline-item'))
.timeline-container
.timeline-content
.range-small #{item.rangeDisplay()}
h3 !{item.name}
p !{item.description}
.work-container
button#timeline-view-all show past work
.section-border
.section-border-inner-1
.section-border-inner-2
section#contact
h2 get in touch
.contact-container
.message
p I'd love to hear from you if you have questions or inquiries related to me or my projects. You can get in touch by text, e-mail, or using this form. I also occasionally share thoughts on my <a href="/blog">blog</a>.
p <b>E-mail:</b> <a href="mailto:shout@garrettmills.dev">shout@garrettmills.dev</a>
.form
form#contact-form(method='post' action=named('contact'))
.form-group
input#contactEmail.form-control(type='email' name='email' placeholder='E-Mail Address' required)
input#contactEEmail(type='email' name='e-mail')
.form-group
input#contactFirst.form-control(name='name' placeholder='Name' required)
.form-group
textarea.form-control#contactMessage(name='message' placeholder='Message' required rows=6)
.form-group
button Send
.section-border.odd
.section-border-inner-1
.section-border-inner-2
section#recent
h2 latest updates
each item in feedPosts
.feed-item
.feed-category(id='feedPostTag_' + item.feedPostId)
.tag #{item.tag}
span
if !item.visible
p.text (draft)
p.text !{item.body}
.bottom
.stamp <a href="#{named('feed')}##{item.feedPostId}" class="feed-edit-button">permalink</a>&nbsp;&nbsp;|&nbsp;&nbsp;#{item.postedAt.toLocaleString()}
.row.mt-4
.col-12.text-center
a.button(href="/feed") view all
section#about
img(style="height: 250px;" src=asset('img/01-self-portrait-bw.jpg'))
p.italic Hi, I'm Garrett.
p I'm a computer scientist and software engineer. Professionally, I design and build flexible, scalable software systems. Personally, I study programming languages, libre software, visual design, and the small web.
p Other things I like: self-hosting, RSS, photography, cooking, and really good fonts.
p#contact ✉️&nbsp;&nbsp;&nbsp;
block append script
script.
document.getElementById('contactEEmail').style.display = 'none'
// If you're reading this, you're not the problem ;)
const homeContactP = document.querySelector('#contact')
const emailAnchor = document.createElement('a')
emailAnchor.href = 'mailto:shout' + '@' + 'garrettmills.dev'
emailAnchor.innerText = 'shout' + '@' + 'garrettmills.dev'
homeContactP.appendChild(emailAnchor)

View File

@ -0,0 +1,95 @@
extends template_70s
block content
.container#home
.inner
section.hero.full-height
.stars
each val in [1, 2, 3, 4, 5, 6]
svg(id=('star-' + val) width='47.581146mm' height='78.547104mm' viewbox='0 0 47.581146 78.547104' version='1.1' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg')
sodipodi:namedview#namedview7(pagecolor='#ffffff' bordercolor='currentColor' borderopacity='0.25' inkscape:showpageshadow='2' inkscape:pageopacity='0.0' inkscape:pagecheckerboard='0' inkscape:deskcolor='#d1d1d1' inkscape:document-units='mm' showgrid='false')
defs#defs2
g#layer1(inkscape:label='Layer 1' inkscape:groupmode='layer' transform='translate(-81.41733,-109.42827)')
path#path1153(style='fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:7;stroke-linecap:round;stroke-dasharray:none' d='m 105,112.92827 c 0,0 1.66434,35.57173 -20.082663,35.57173' inkscape:export-filename='star.svg' inkscape:export-xdpi='96' inkscape:export-ydpi='96')
path#path1153-3(style='fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:7;stroke-linecap:round;stroke-dasharray:none' d='m 105.41582,112.92827 c 0,0 -1.66434,35.57173 20.08267,35.57173')
path#path1153-6(style='fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:7;stroke-linecap:round;stroke-dasharray:none' d='m 105,184.47538 c 0,0 1.66434,-35.57173 -20.082665,-35.57173')
path#path1153-3-7(style='fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:7;stroke-linecap:round;stroke-dasharray:none' d='m 105.41582,184.47538 c 0,0 -1.66434,-35.57173 20.08267,-35.57173')
.hero-box
h1 Garrett Mills
p Software engineer, computer scientist, and nerd.
//p Hi, there. My name is Garrett, and I'm a computer scientist, software engineer, and speaker.
.section-border
.section-border-inner-1
.section-border-inner-2
section#about
h2 about me
.about-container
.img
img(src="/assets/img/profile.jpg" width=300 height=300)
.about
p Hi, I'm Garrett. Welcome to my corner of the internet.
p I'm a computer scientist and software engineer specializing in software/systems architectures and programming languages. Some of my more recent projects include <a href="https://github.com/swarmlang/swarm" target="_blank">the Swarm programming language</a>, <a href="https://extollo.garrettmills.dev" target="_blank">the Extollo framework</a>, and <a href="https://code.garrettmills.dev/Starship/CoreID" target="_blank">the CoreID SSO server</a>.
p I sometimes write <a href="#{named('blog')}">blog posts</a> and <a href="https://code.garrettmills.dev/garrettmills" target="_blank">publish code</a> from my projects.
.section-border.odd
.section-border-inner-1
.section-border-inner-2
if workItemYears && workItemYears.length
section#work
h2 what I'm working on
.timeline-group
each year in workItemYears
.work-container(class=(workItemHiddenYears.includes(year) ? 'timeline-item theme-hide' : 'timeline-item'))
.timeline-container
.timeline-year #{year}
each item in workItemGroups[year]
.work-container(class=(item.endDate ? 'timeline-item theme-hide' : 'timeline-item'))
.timeline-container
.timeline-content
.range-small #{item.rangeDisplay()}
h3 !{item.name}
p !{item.description}
.work-container
button#timeline-view-all show past work
.section-border
.section-border-inner-1
.section-border-inner-2
section#contact
h2 get in touch
.contact-container
.message
p I'd love to hear from you if you have questions or inquiries related to me or my projects. You can get in touch by text, e-mail, or using this form. I also occasionally share thoughts on my <a href="/blog">blog</a>.
p <b>E-mail:</b> <a href="mailto:shout@garrettmills.dev">shout@garrettmills.dev</a>
.form
form#contact-form(method='post' action=named('contact'))
.form-group
input#contactEmail.form-control(type='email' name='email' placeholder='E-Mail Address' required)
input#contactEEmail(type='email' name='e-mail')
.form-group
input#contactFirst.form-control(name='name' placeholder='Name' required)
.form-group
textarea.form-control#contactMessage(name='message' placeholder='Message' required rows=6)
.form-group
button Send
.section-border.odd
.section-border-inner-1
.section-border-inner-2
section#recent
h2 latest updates
each item in feedPosts
.feed-item
.feed-category(id='feedPostTag_' + item.feedPostId)
.tag #{item.tag}
span
if !item.visible
p.text (draft)
p.text !{item.body}
.bottom
.stamp <a href="#{named('feed')}##{item.feedPostId}" class="feed-edit-button">permalink</a>&nbsp;&nbsp;|&nbsp;&nbsp;#{item.postedAt.toLocaleString()}
.row.mt-4
.col-12.text-center
a.button(href="/feed") view all
block append script
script.
document.getElementById('contactEEmail').style.display = 'none'