Redesign: replace hugo-sustain with self-contained editorial theme
Drop the hugo-sustain submodule and build a custom theme forked from the shrug.host design system, recolored to a potato identity (russet skin, sprout-green leitmotif, purple-potato accent; potato-flesh light + soil dark, follows prefers-color-scheme with a persisted toggle). - Self-hosted Commit Mono + Source Serif 4 (no external requests) - Token-based CSS: tokens / base / components / layouts - Layouts: baseof, index (hero + latest posts + "now"), single, list, projects (card grid), partials for nav/footer/head + OG/Twitter/schema - SEO scaffolding hugo-sustain lacked: OpenGraph, Person/BlogPosting schema, RSS, canonical - Pipeline unchanged (Hugo 0.148.2, GitHub Pages); post URLs preserved Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
[submodule "themes/hugo-sustain"]
|
|
||||||
path = themes/hugo-sustain
|
|
||||||
url = https://git.shrug.pw/neil/hugo-sustain.git
|
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
thepotato.tech — Base: reset, elements, typography primitives
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
||||||
|
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;}
|
||||||
|
|
||||||
|
body{
|
||||||
|
font-family:var(--font-serif);
|
||||||
|
font-size:var(--text-lg);
|
||||||
|
line-height:1.65;
|
||||||
|
color:var(--text);
|
||||||
|
background:var(--bg);
|
||||||
|
-webkit-font-smoothing:antialiased;
|
||||||
|
-moz-osx-font-smoothing:grayscale;
|
||||||
|
position:relative;
|
||||||
|
min-height:100vh;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Film grain overlay */
|
||||||
|
body::before{
|
||||||
|
content:"";position:fixed;inset:0;z-index:9999;pointer-events:none;
|
||||||
|
background-image:var(--noise);mix-blend-mode:multiply;opacity:.6;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) body::before{mix-blend-mode:screen;}}
|
||||||
|
:root[data-theme="dark"] body::before{mix-blend-mode:screen;}
|
||||||
|
|
||||||
|
.site-main{flex:1 0 auto;}
|
||||||
|
|
||||||
|
h1,h2,h3,h4,h5,h6{
|
||||||
|
font-family:var(--font-mono);
|
||||||
|
line-height:1.15;
|
||||||
|
font-weight:700;
|
||||||
|
color:var(--text);
|
||||||
|
letter-spacing:-.01em;
|
||||||
|
text-wrap:balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
a{color:var(--link);text-decoration:none;transition:color var(--tr);}
|
||||||
|
a:hover{color:var(--link-hover);}
|
||||||
|
|
||||||
|
strong{font-weight:700;color:var(--text);}
|
||||||
|
em{font-style:italic;}
|
||||||
|
|
||||||
|
img{max-width:100%;height:auto;}
|
||||||
|
|
||||||
|
::selection{background:var(--sprout);color:#fff;}
|
||||||
|
|
||||||
|
:focus-visible{outline:2px solid var(--russet);outline-offset:3px;border-radius:3px;}
|
||||||
|
|
||||||
|
.sr-only{
|
||||||
|
position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
|
||||||
|
clip:rect(0,0,0,0);white-space:nowrap;border:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip link */
|
||||||
|
.skip-link{
|
||||||
|
position:absolute;left:-999px;top:0;z-index:10000;
|
||||||
|
background:var(--russet);color:#fff;padding:var(--s3) var(--s5);
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);border-radius:0 0 var(--radius) 0;
|
||||||
|
}
|
||||||
|
.skip-link:focus{left:0;color:#fff;}
|
||||||
|
|
||||||
|
/* --- Layout primitives --- */
|
||||||
|
.container{max-width:var(--max);margin:0 auto;padding:0 var(--s6);}
|
||||||
|
.container--text{max-width:var(--max-text);}
|
||||||
|
|
||||||
|
.section{padding:var(--s20) 0;}
|
||||||
|
.section--surface{
|
||||||
|
background:var(--bg-surface);
|
||||||
|
border-top:1px solid var(--border);
|
||||||
|
border-bottom:1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Shared mono devices --- */
|
||||||
|
.eyebrow{
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);color:var(--sprout);
|
||||||
|
display:inline-flex;align-items:center;gap:var(--s2);letter-spacing:.02em;
|
||||||
|
}
|
||||||
|
.section-label{
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);color:var(--russet);
|
||||||
|
letter-spacing:.04em;display:block;margin-bottom:var(--s3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The potato "eye" / sprout — leitmotif dot */
|
||||||
|
.dot{
|
||||||
|
display:inline-block;width:8px;height:8px;border-radius:50%;
|
||||||
|
background:var(--dot);flex:0 0 auto;
|
||||||
|
box-shadow:0 0 0 3px color-mix(in srgb,var(--sprout) 22%,transparent);
|
||||||
|
}
|
||||||
|
.dot--pulse{animation:pulse 2.4s ease-in-out infinite;}
|
||||||
|
@keyframes pulse{0%,100%{opacity:1;}50%{opacity:.4;}}
|
||||||
|
@media (prefers-reduced-motion:reduce){
|
||||||
|
.dot--pulse{animation:none;}
|
||||||
|
html{scroll-behavior:auto;}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
thepotato.tech — Components: nav, buttons, chips, cards, post rows,
|
||||||
|
breadcrumb, prose, footer
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* --- Navbar --- */
|
||||||
|
.navbar{height:var(--nav-h);display:flex;align-items:center;border-bottom:1px solid var(--border);}
|
||||||
|
.navbar__inner{
|
||||||
|
max-width:var(--max);margin:0 auto;width:100%;padding:0 var(--s6);
|
||||||
|
display:flex;align-items:center;justify-content:space-between;gap:var(--s4);
|
||||||
|
}
|
||||||
|
.navbar__logo{
|
||||||
|
display:flex;align-items:center;gap:var(--s3);
|
||||||
|
font-family:var(--font-mono);font-weight:700;color:var(--text);font-size:var(--text-lg);
|
||||||
|
}
|
||||||
|
.navbar__logo .spud{font-size:1.15rem;line-height:1;}
|
||||||
|
.navbar__logo .shrug{color:var(--text-faint);font-weight:400;font-size:var(--text-sm);}
|
||||||
|
.navbar__logo:hover{color:var(--russet);}
|
||||||
|
.navbar__links{
|
||||||
|
display:flex;align-items:center;gap:var(--s6);list-style:none;
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);
|
||||||
|
}
|
||||||
|
.navbar__links a{color:var(--text-muted);}
|
||||||
|
.navbar__links a:hover,.navbar__links a[aria-current="page"]{color:var(--russet);}
|
||||||
|
.navbar__hamburger{display:none;background:none;border:0;color:var(--text);cursor:pointer;padding:var(--s2);}
|
||||||
|
|
||||||
|
.theme-toggle{
|
||||||
|
background:var(--bg-elevated);border:1px solid var(--border-strong);
|
||||||
|
color:var(--text);width:34px;height:34px;border-radius:999px;cursor:pointer;
|
||||||
|
display:inline-flex;align-items:center;justify-content:center;transition:border-color var(--tr);
|
||||||
|
}
|
||||||
|
.theme-toggle:hover{border-color:var(--russet);}
|
||||||
|
.theme-toggle .icon-moon{display:none;}
|
||||||
|
:root[data-theme="dark"] .theme-toggle .icon-sun{display:none;}
|
||||||
|
:root[data-theme="dark"] .theme-toggle .icon-moon{display:inline;}
|
||||||
|
@media (prefers-color-scheme:dark){
|
||||||
|
:root:not([data-theme="light"]) .theme-toggle .icon-sun{display:none;}
|
||||||
|
:root:not([data-theme="light"]) .theme-toggle .icon-moon{display:inline;}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Buttons --- */
|
||||||
|
.btn{
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);font-weight:700;
|
||||||
|
padding:.7rem 1.3rem;border-radius:999px;transition:all var(--tr);
|
||||||
|
border:1px solid transparent;display:inline-flex;align-items:center;gap:var(--s2);cursor:pointer;
|
||||||
|
}
|
||||||
|
.btn--primary{background:var(--russet);color:#fff;}
|
||||||
|
.btn--primary:hover{background:var(--russet-deep);color:#fff;transform:translateY(-1px);}
|
||||||
|
.btn--ghost{border-color:var(--border-strong);color:var(--text);}
|
||||||
|
.btn--ghost:hover{border-color:var(--russet);color:var(--russet);}
|
||||||
|
|
||||||
|
/* --- Chips --- */
|
||||||
|
.chip{
|
||||||
|
display:inline-block;font-family:var(--font-mono);font-size:var(--text-xs);
|
||||||
|
letter-spacing:.06em;text-transform:uppercase;padding:.15rem .5rem;border-radius:999px;
|
||||||
|
}
|
||||||
|
.chip--draft{color:var(--violet);border:1px solid color-mix(in srgb,var(--violet) 45%,transparent);}
|
||||||
|
.chip--tag{color:var(--text-muted);border:1px solid var(--border-strong);}
|
||||||
|
|
||||||
|
/* --- Section header --- */
|
||||||
|
.section-head{margin-bottom:var(--s10);}
|
||||||
|
.section-head h2{font-size:var(--text-3xl);}
|
||||||
|
.section-head p{color:var(--text-muted);max-width:var(--max-text);margin-top:var(--s3);}
|
||||||
|
|
||||||
|
/* --- Post list rows --- */
|
||||||
|
.posts{display:flex;flex-direction:column;}
|
||||||
|
.post-row{
|
||||||
|
display:grid;grid-template-columns:130px 1fr;gap:var(--s6);
|
||||||
|
padding:var(--s6) 0;border-top:1px solid var(--border);align-items:baseline;
|
||||||
|
}
|
||||||
|
.posts .post-row:last-child{border-bottom:1px solid var(--border);}
|
||||||
|
.post-row__meta{
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);color:var(--text-faint);
|
||||||
|
display:flex;flex-direction:column;gap:var(--s2);
|
||||||
|
}
|
||||||
|
.post-row__title{
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-xl);font-weight:700;color:var(--text);
|
||||||
|
margin-bottom:var(--s2);transition:color var(--tr);text-wrap:balance;
|
||||||
|
}
|
||||||
|
.post-row:hover .post-row__title{color:var(--russet);}
|
||||||
|
.post-row__dek{color:var(--text-muted);font-size:var(--text-base);max-width:60ch;}
|
||||||
|
|
||||||
|
/* --- Cards (projects / now) --- */
|
||||||
|
.card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:var(--s6);}
|
||||||
|
.card{
|
||||||
|
border:1px solid var(--border);border-radius:var(--radius-lg);
|
||||||
|
padding:var(--s6);background:var(--bg-elevated);transition:border-color var(--tr),transform var(--tr);
|
||||||
|
}
|
||||||
|
.card:hover{border-color:var(--border-strong);transform:translateY(-2px);}
|
||||||
|
.card__title{font-size:var(--text-lg);margin-bottom:var(--s3);display:flex;align-items:center;gap:var(--s3);}
|
||||||
|
.card__title .icon{color:var(--sprout);width:1.1em;text-align:center;}
|
||||||
|
.card__body{color:var(--text-muted);font-size:var(--text-base);}
|
||||||
|
.card__body a{text-decoration:underline;text-underline-offset:2px;}
|
||||||
|
|
||||||
|
/* --- Breadcrumb --- */
|
||||||
|
.breadcrumb{
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);color:var(--text-faint);
|
||||||
|
margin-bottom:var(--s6);display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;
|
||||||
|
}
|
||||||
|
.breadcrumb a{color:var(--text-muted);}
|
||||||
|
.breadcrumb a:hover{color:var(--russet);}
|
||||||
|
|
||||||
|
/* --- Prose (post body) --- */
|
||||||
|
.prose{max-width:var(--max-text);margin:0 auto;}
|
||||||
|
.prose>*+*{margin-top:var(--s6);}
|
||||||
|
.prose p{color:var(--text);}
|
||||||
|
.prose h2{font-size:var(--text-2xl);margin-top:var(--s12);}
|
||||||
|
.prose h2::before{content:"// ";color:var(--russet);}
|
||||||
|
.prose h3{font-size:var(--text-xl);margin-top:var(--s10);}
|
||||||
|
.prose a{text-decoration:underline;text-decoration-color:color-mix(in srgb,var(--russet) 45%,transparent);text-underline-offset:3px;}
|
||||||
|
.prose a:hover{text-decoration-color:var(--russet);}
|
||||||
|
.prose ul,.prose ol{padding-left:1.4rem;}
|
||||||
|
.prose li{margin-bottom:var(--s2);}
|
||||||
|
.prose li::marker{color:var(--sprout);}
|
||||||
|
.prose blockquote{
|
||||||
|
border-left:3px solid var(--sprout);padding:var(--s2) 0 var(--s2) var(--s5);
|
||||||
|
color:var(--text-muted);font-style:italic;
|
||||||
|
}
|
||||||
|
.prose img{border-radius:var(--radius-lg);border:1px solid var(--border);margin:var(--s4) 0;}
|
||||||
|
.prose figure{margin:var(--s6) 0;}
|
||||||
|
.prose figcaption{font-family:var(--font-mono);font-size:var(--text-sm);color:var(--text-faint);margin-top:var(--s2);text-align:center;}
|
||||||
|
.prose code{
|
||||||
|
font-family:var(--font-mono);font-size:.9em;background:var(--bg-surface);
|
||||||
|
border:1px solid var(--border);border-radius:5px;padding:.1em .4em;
|
||||||
|
}
|
||||||
|
.prose pre{
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);background:var(--bg-soil);
|
||||||
|
color:var(--text-on-soil);padding:var(--s5);border-radius:var(--radius-lg);
|
||||||
|
overflow-x:auto;line-height:1.55;
|
||||||
|
}
|
||||||
|
.prose pre code{background:none;border:0;padding:0;color:inherit;font-size:inherit;}
|
||||||
|
.prose hr{border:0;border-top:1px solid var(--border);margin:var(--s10) 0;}
|
||||||
|
.prose table{width:100%;border-collapse:collapse;font-size:var(--text-base);}
|
||||||
|
.prose th,.prose td{border:1px solid var(--border);padding:var(--s2) var(--s3);text-align:left;}
|
||||||
|
.prose th{font-family:var(--font-mono);background:var(--bg-surface);}
|
||||||
|
|
||||||
|
/* --- Post footer --- */
|
||||||
|
.post-foot{
|
||||||
|
max-width:var(--max-text);margin:var(--s16) auto 0;padding-top:var(--s6);
|
||||||
|
border-top:1px solid var(--border);font-family:var(--font-mono);font-size:var(--text-sm);
|
||||||
|
color:var(--text-muted);display:flex;justify-content:space-between;gap:var(--s4);flex-wrap:wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Footer --- */
|
||||||
|
.footer{border-top:1px solid var(--border);padding:var(--s12) 0;background:var(--bg-surface);}
|
||||||
|
.footer__inner{
|
||||||
|
max-width:var(--max);margin:0 auto;padding:0 var(--s6);
|
||||||
|
display:flex;justify-content:space-between;gap:var(--s6);flex-wrap:wrap;align-items:flex-start;
|
||||||
|
}
|
||||||
|
.footer__note{font-family:var(--font-mono);font-size:var(--text-sm);color:var(--text-faint);max-width:46ch;line-height:1.7;}
|
||||||
|
.footer__links{display:flex;gap:var(--s5);font-family:var(--font-mono);font-size:var(--text-sm);flex-wrap:wrap;}
|
||||||
|
.footer__links a{color:var(--text-muted);}
|
||||||
|
.footer__links a:hover{color:var(--russet);}
|
||||||
|
|
||||||
|
@media (max-width:720px){
|
||||||
|
.post-row{grid-template-columns:1fr;gap:var(--s2);}
|
||||||
|
.navbar__hamburger{display:inline-flex;}
|
||||||
|
.navbar__logo .shrug{display:none;}
|
||||||
|
.navbar__links{
|
||||||
|
position:absolute;top:var(--nav-h);left:0;right:0;flex-direction:column;align-items:flex-start;
|
||||||
|
gap:var(--s4);padding:var(--s5) var(--s6);background:var(--bg-elevated);
|
||||||
|
border-bottom:1px solid var(--border);box-shadow:var(--shadow-md);
|
||||||
|
display:none;z-index:150;
|
||||||
|
}
|
||||||
|
.navbar__links.is-open{display:flex;}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
thepotato.tech — Layouts: hero, home sections, page hero, soil strip
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* --- Home hero --- */
|
||||||
|
.hero{padding:var(--s24) 0 var(--s16);}
|
||||||
|
.hero__eyebrow{margin-bottom:var(--s5);}
|
||||||
|
.hero__title{font-size:clamp(2.6rem,7vw,var(--text-6xl));line-height:1.05;margin-bottom:var(--s6);}
|
||||||
|
.hero .grad{
|
||||||
|
background:linear-gradient(102deg,var(--russet),var(--sprout-bright));
|
||||||
|
-webkit-background-clip:text;background-clip:text;color:transparent;
|
||||||
|
}
|
||||||
|
.hero__lede{font-size:var(--text-xl);max-width:var(--max-text);color:var(--text-muted);}
|
||||||
|
.hero__lede p{margin-bottom:var(--s4);}
|
||||||
|
.hero__lede strong{color:var(--text);}
|
||||||
|
.hero__lede a{text-decoration:underline;text-underline-offset:3px;text-decoration-color:color-mix(in srgb,var(--russet) 45%,transparent);}
|
||||||
|
|
||||||
|
.actions{display:flex;gap:var(--s4);flex-wrap:wrap;align-items:center;margin-top:var(--s8);}
|
||||||
|
|
||||||
|
.social-inline{
|
||||||
|
display:flex;gap:var(--s5);margin-top:var(--s10);
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);flex-wrap:wrap;
|
||||||
|
}
|
||||||
|
.social-inline a{color:var(--text-muted);display:inline-flex;gap:var(--s2);align-items:center;}
|
||||||
|
.social-inline a:hover{color:var(--sprout);}
|
||||||
|
|
||||||
|
/* --- "Now" / soil strip --- */
|
||||||
|
.soil{background:var(--bg-soil);color:var(--text-on-soil);}
|
||||||
|
.soil h1,.soil h2,.soil h3{color:var(--text-on-soil);}
|
||||||
|
.soil .section-label{color:var(--sprout-bright);}
|
||||||
|
.soil p{color:var(--text-on-soil-muted);}
|
||||||
|
.now-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:var(--s6);}
|
||||||
|
.now-card{border:1px solid rgba(255,255,255,.09);border-radius:var(--radius-lg);padding:var(--s6);background:rgba(255,255,255,.02);}
|
||||||
|
.now-card h3{font-size:var(--text-base);margin-bottom:var(--s2);display:flex;align-items:center;gap:var(--s2);}
|
||||||
|
.now-card p{font-size:var(--text-sm);}
|
||||||
|
.now-card .k{font-family:var(--font-mono);color:var(--sprout-bright);}
|
||||||
|
|
||||||
|
/* --- Page hero (single / list) --- */
|
||||||
|
.page-hero{padding:var(--s16) 0 var(--s10);border-bottom:1px solid var(--border);}
|
||||||
|
.page-hero__eyebrow{margin-bottom:var(--s5);}
|
||||||
|
.page-hero__title{font-size:clamp(2rem,5vw,var(--text-5xl));line-height:1.08;margin:var(--s4) 0 var(--s5);}
|
||||||
|
.page-hero__subtitle{font-size:var(--text-xl);color:var(--text-muted);max-width:var(--max-text);}
|
||||||
|
.page-hero__meta{
|
||||||
|
font-family:var(--font-mono);font-size:var(--text-sm);color:var(--text-muted);
|
||||||
|
display:flex;gap:var(--s4);flex-wrap:wrap;align-items:center;margin-top:var(--s4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:640px){
|
||||||
|
.hero{padding:var(--s16) 0 var(--s12);}
|
||||||
|
.section{padding:var(--s16) 0;}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
thepotato.tech — Design Tokens
|
||||||
|
Forked from the shrug.host editorial system, recolored to a potato palette:
|
||||||
|
russet skin, sprout-green, a rare purple-potato pop. Potato-flesh light,
|
||||||
|
soil dark. Light mode is the default; dark overrides via [data-theme="dark"]
|
||||||
|
and prefers-color-scheme.
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* --- Self-hosted fonts (no external requests) --- */
|
||||||
|
@font-face{
|
||||||
|
font-family:'Source Serif 4';
|
||||||
|
src:url('/fonts/SourceSerif4.woff2') format('woff2');
|
||||||
|
font-weight:200 900; font-style:normal; font-display:swap;
|
||||||
|
}
|
||||||
|
@font-face{
|
||||||
|
font-family:'Source Serif 4';
|
||||||
|
src:url('/fonts/SourceSerif4-Italic.woff2') format('woff2');
|
||||||
|
font-weight:200 900; font-style:italic; font-display:swap;
|
||||||
|
}
|
||||||
|
@font-face{
|
||||||
|
font-family:'Commit Mono';
|
||||||
|
src:url('/fonts/CommitMono-400-Regular.woff2') format('woff2');
|
||||||
|
font-weight:400; font-style:normal; font-display:swap;
|
||||||
|
}
|
||||||
|
@font-face{
|
||||||
|
font-family:'Commit Mono';
|
||||||
|
src:url('/fonts/CommitMono-400-Italic.woff2') format('woff2');
|
||||||
|
font-weight:400; font-style:italic; font-display:swap;
|
||||||
|
}
|
||||||
|
@font-face{
|
||||||
|
font-family:'Commit Mono';
|
||||||
|
src:url('/fonts/CommitMono-700-Regular.woff2') format('woff2');
|
||||||
|
font-weight:700; font-style:normal; font-display:swap;
|
||||||
|
}
|
||||||
|
@font-face{
|
||||||
|
font-family:'Commit Mono';
|
||||||
|
src:url('/fonts/CommitMono-700-Italic.woff2') format('woff2');
|
||||||
|
font-weight:700; font-style:italic; font-display:swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Light mode tokens (default) --- */
|
||||||
|
:root{
|
||||||
|
/* Potato palette */
|
||||||
|
--russet:#8a5622; /* potato skin — links, CTA */
|
||||||
|
--russet-deep:#6f4318;
|
||||||
|
--sprout:#697a2b; /* light-struck / sprouting green — leitmotif dot */
|
||||||
|
--sprout-bright:#7d9134;
|
||||||
|
--violet:#6a4a86; /* purple-potato — rare pop (draft chips) */
|
||||||
|
|
||||||
|
/* Backgrounds */
|
||||||
|
--bg:#efe7d6; /* potato-flesh paper */
|
||||||
|
--bg-surface:#e7dcc5;
|
||||||
|
--bg-elevated:#f6efe1;
|
||||||
|
--bg-soil:#241d12; /* dark inset sections */
|
||||||
|
|
||||||
|
/* Text */
|
||||||
|
--text:#241d12; /* soil */
|
||||||
|
--text-muted:#6d5f45;
|
||||||
|
--text-faint:#93856a;
|
||||||
|
--text-on-soil:#f2ead8;
|
||||||
|
--text-on-soil-muted:#c3b591;
|
||||||
|
|
||||||
|
/* Borders */
|
||||||
|
--border:#d8cbac;
|
||||||
|
--border-strong:#c4b492;
|
||||||
|
|
||||||
|
/* Semantic */
|
||||||
|
--link:var(--russet);
|
||||||
|
--link-hover:var(--russet-deep);
|
||||||
|
--dot:var(--sprout);
|
||||||
|
|
||||||
|
/* Shadows */
|
||||||
|
--shadow-sm:0 1px 2px rgba(60,42,16,.10);
|
||||||
|
--shadow-md:0 6px 20px rgba(60,42,16,.10),0 2px 6px rgba(60,42,16,.06);
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
--font-mono:'Commit Mono','JetBrains Mono',ui-monospace,monospace;
|
||||||
|
--font-serif:'Source Serif 4',Georgia,ui-serif,serif;
|
||||||
|
|
||||||
|
--text-xs:.75rem; --text-sm:.875rem; --text-base:1rem; --text-lg:1.125rem;
|
||||||
|
--text-xl:1.25rem; --text-2xl:1.5rem; --text-3xl:1.875rem; --text-4xl:2.25rem;
|
||||||
|
--text-5xl:3rem; --text-6xl:3.9rem;
|
||||||
|
|
||||||
|
/* Spacing */
|
||||||
|
--s1:.25rem; --s2:.5rem; --s3:.75rem; --s4:1rem; --s5:1.25rem; --s6:1.5rem;
|
||||||
|
--s8:2rem; --s10:2.5rem; --s12:3rem; --s16:4rem; --s20:5rem; --s24:6rem;
|
||||||
|
|
||||||
|
/* Layout */
|
||||||
|
--max:1080px; --max-text:680px;
|
||||||
|
--radius:8px; --radius-lg:14px;
|
||||||
|
--nav-h:60px;
|
||||||
|
--tr:.18s ease;
|
||||||
|
|
||||||
|
/* Film-grain overlay */
|
||||||
|
--noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Dark mode: soil at night --- */
|
||||||
|
@media (prefers-color-scheme:dark){
|
||||||
|
:root:not([data-theme="light"]){
|
||||||
|
--russet:#c8823f; --russet-deep:#dd9a57;
|
||||||
|
--sprout:#9fb653; --sprout-bright:#b6cf63; --violet:#b192d6;
|
||||||
|
--bg:#17130d; --bg-surface:#201a11; --bg-elevated:#2a2216; --bg-soil:#0e0b07;
|
||||||
|
--text:#efe6d4; --text-muted:#b6a884; --text-faint:#8a7c60;
|
||||||
|
--text-on-soil:#efe6d4; --text-on-soil-muted:#b6a884;
|
||||||
|
--border:#33291b; --border-strong:#463a26;
|
||||||
|
--shadow-sm:0 1px 2px rgba(0,0,0,.4);
|
||||||
|
--shadow-md:0 6px 20px rgba(0,0,0,.5),0 2px 6px rgba(0,0,0,.3);
|
||||||
|
--noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"]{
|
||||||
|
--russet:#c8823f; --russet-deep:#dd9a57;
|
||||||
|
--sprout:#9fb653; --sprout-bright:#b6cf63; --violet:#b192d6;
|
||||||
|
--bg:#17130d; --bg-surface:#201a11; --bg-elevated:#2a2216; --bg-soil:#0e0b07;
|
||||||
|
--text:#efe6d4; --text-muted:#b6a884; --text-faint:#8a7c60;
|
||||||
|
--text-on-soil:#efe6d4; --text-on-soil-muted:#b6a884;
|
||||||
|
--border:#33291b; --border-strong:#463a26;
|
||||||
|
--shadow-sm:0 1px 2px rgba(0,0,0,.4);
|
||||||
|
--shadow-md:0 6px 20px rgba(0,0,0,.5),0 2px 6px rgba(0,0,0,.3);
|
||||||
|
--noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
$primary-color: #115c9e;
|
|
||||||
$hover-color: #5E7191;
|
|
||||||
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
border-top-color: $primary-color;
|
|
||||||
}
|
|
||||||
div.footer {
|
|
||||||
border-bottom-color: $primary-color;
|
|
||||||
}
|
|
||||||
.container a {
|
|
||||||
color: $primary-color;
|
|
||||||
}
|
|
||||||
.container a:hover {
|
|
||||||
color: $hover-color;
|
|
||||||
}
|
|
||||||
.panel-default h4 a, h5 a {
|
|
||||||
color: $primary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure.inline {
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: auto;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure.inline-40 {
|
|
||||||
display: inline-flex;
|
|
||||||
height: auto;
|
|
||||||
width: 40%;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure.inline-60 {
|
|
||||||
display: inline-flex;
|
|
||||||
height: auto;
|
|
||||||
width: 60%;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure.inline-80 {
|
|
||||||
display: inline-flex;
|
|
||||||
height: auto;
|
|
||||||
width: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure.right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure.left {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
figure > img {
|
|
||||||
display: inline-flex;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2, h3 {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-weight: bold
|
|
||||||
}
|
|
||||||
|
|
||||||
.clearfix {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.clearfix::after {
|
|
||||||
content: "";
|
|
||||||
clear: both;
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Projects page styling */
|
|
||||||
.project-card {
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #e8e8e8;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
height: 100%;
|
|
||||||
transition: box-shadow 0.3s ease, transform 0.2s ease;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card:hover {
|
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-icon {
|
|
||||||
font-size: 1.8rem;
|
|
||||||
color: $primary-color;
|
|
||||||
margin-right: 1rem;
|
|
||||||
min-width: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-title {
|
|
||||||
margin: 0;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-title a {
|
|
||||||
color: $primary-color;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-title a:hover {
|
|
||||||
color: $hover-color;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-description {
|
|
||||||
color: #666;
|
|
||||||
line-height: 1.6;
|
|
||||||
margin: 0;
|
|
||||||
flex: 1;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,26 @@
|
|||||||
#baseURL = "https://drop1.neilhanlon.me:1313/"
|
|
||||||
baseURL = "/"
|
baseURL = "/"
|
||||||
languageCode = "en-US"
|
languageCode = "en-US"
|
||||||
title = "Neil Hanlon"
|
title = "Neil Hanlon"
|
||||||
# Enable comments by entering your Disqus shortname
|
enableRobotsTXT = true
|
||||||
disqusShortname = ""
|
|
||||||
# Enable Google Analytics by entering your tracking code
|
|
||||||
# googleAnalytics = ""
|
|
||||||
theme = "hugo-sustain"
|
|
||||||
|
|
||||||
[permalinks]
|
[permalinks]
|
||||||
post = "/:year/:month/:day/:slug"
|
post = "/:year/:month/:day/:slug"
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
avatar = "img/profile.png"
|
|
||||||
author = "Neil Hanlon"
|
author = "Neil Hanlon"
|
||||||
description = "Neil Hanlon - Senior Network/DevOps/Infrastructure Engineer"
|
description = "Neil Hanlon — Senior Network/DevOps/Infrastructure Engineer"
|
||||||
|
hero_eyebrow = "neil@potato:~$ whoami"
|
||||||
|
|
||||||
# Custom assets can be linked with their paths relative to static/
|
# "What's running" cards on the homepage
|
||||||
custom_css = []
|
[[params.now]]
|
||||||
custom_scss = ['sass/overrides.scss']
|
title = "Consulting"
|
||||||
custom_js = []
|
body = "Infrastructure for growing tech companies via <span class=\"k\">shrugpw.com</span> — compliance, migrations, networks done right."
|
||||||
|
[[params.now]]
|
||||||
|
title = "Rocky Linux"
|
||||||
|
body = "Still building and maintaining the infrastructure. Open source is the good part of this whole thing."
|
||||||
|
[[params.now]]
|
||||||
|
title = "The homelab"
|
||||||
|
body = "FreeIPA, FreeRADIUS, an SSID with a potato in the name, and far too many little agents writing to a database."
|
||||||
|
|
||||||
[params.social]
|
[params.social]
|
||||||
Github = "NeilHanlon"
|
Github = "NeilHanlon"
|
||||||
@@ -28,13 +29,24 @@ theme = "hugo-sustain"
|
|||||||
LinkedIn = "hanlonneil"
|
LinkedIn = "hanlonneil"
|
||||||
Signal = "hScowCEn-H1GKkCrare0m_xOEPqTWxt51pW9SI2bU5TlXrMW9QFD6qHBLqG1jDq9"
|
Signal = "hScowCEn-H1GKkCrare0m_xOEPqTWxt51pW9SI2bU5TlXrMW9QFD6qHBLqG1jDq9"
|
||||||
Keybase = "nhanlon"
|
Keybase = "nhanlon"
|
||||||
#Stackoverflow = "username"
|
|
||||||
#Medium = "hanlon.neil"
|
|
||||||
Instagram = "noellathekitty"
|
Instagram = "noellathekitty"
|
||||||
|
|
||||||
[params.volunteer]
|
[params.volunteer]
|
||||||
Rocky = "https://rockylinux.org"
|
Rocky = "https://rockylinux.org"
|
||||||
|
|
||||||
|
[markup]
|
||||||
|
[markup.goldmark.renderer]
|
||||||
|
unsafe = true
|
||||||
|
[markup.highlight]
|
||||||
|
noClasses = false
|
||||||
|
guessSyntax = true
|
||||||
|
|
||||||
|
[outputs]
|
||||||
|
home = ["HTML", "RSS"]
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tag = "tags"
|
||||||
|
category = "categories"
|
||||||
|
|
||||||
## Main Menu
|
## Main Menu
|
||||||
[[menu.main]]
|
[[menu.main]]
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="{{ .Site.LanguageCode | default "en" }}">
|
||||||
|
<head>
|
||||||
|
{{- partial "head.html" . }}
|
||||||
|
</head>
|
||||||
|
<body class="{{ block "body-class" . }}{{ end }}">
|
||||||
|
<a href="#main-content" class="skip-link">Skip to content</a>
|
||||||
|
|
||||||
|
{{- partial "navbar.html" . }}
|
||||||
|
|
||||||
|
<main class="site-main" id="main-content">
|
||||||
|
{{- block "main" . }}{{- end }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{{- partial "footer.html" . }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<div class="page-hero">
|
||||||
|
<div class="container">
|
||||||
|
<p class="page-hero__eyebrow eyebrow">
|
||||||
|
<span class="dot dot--pulse" aria-hidden="true"></span>
|
||||||
|
{{ with .Params.eyebrow }}{{ . }}{{ else }}~/{{ .Section | default "posts" }}{{ end }}
|
||||||
|
</p>
|
||||||
|
<h1 class="page-hero__title">{{ .Title }}</h1>
|
||||||
|
{{- with .Description }}<p class="page-hero__subtitle">{{ . }}</p>{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
{{- with .Content }}<div class="prose" style="margin-bottom:var(--s10);">{{ . }}</div>{{ end }}
|
||||||
|
<div class="posts">
|
||||||
|
{{- range .Pages.ByDate.Reverse }}
|
||||||
|
<a href="{{ .RelPermalink }}" class="post-row">
|
||||||
|
<div class="post-row__meta">
|
||||||
|
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "Jan 2, 2006" }}</time>
|
||||||
|
{{- if .Draft }}<span class="chip chip--draft">draft</span>
|
||||||
|
{{- else }}{{ with .Params.tags }}<span class="chip chip--tag">{{ index . 0 }}</span>{{ end }}{{ end }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="post-row__title">{{ .Title }}</div>
|
||||||
|
<p class="post-row__dek">{{ .Description | default .Summary | truncate 180 }}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{- end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
@@ -1,25 +1,28 @@
|
|||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<!-- Header -->
|
<div class="page-hero">
|
||||||
{{ partial "header" . }}
|
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<section id="projects">
|
<p class="page-hero__eyebrow eyebrow">
|
||||||
<h4 class="my-5">{{ .Site.Data.projects.name }}</h4>
|
<span class="dot dot--pulse" aria-hidden="true"></span> ~/projects
|
||||||
<div class="row">
|
</p>
|
||||||
{{ range $el := .Site.Data.projects.source }}
|
<h1 class="page-hero__title">{{ .Title }}</h1>
|
||||||
<div class="col-md-6 mb-4">
|
<p class="page-hero__subtitle">Open source, governance, and infrastructure I help keep running.</p>
|
||||||
<div class="project-card">
|
|
||||||
<div class="project-header">
|
|
||||||
<i class="{{ .icon }} project-icon"></i>
|
|
||||||
<h5 class="project-title">
|
|
||||||
<a href="{{ .url }}" target="_blank">{{ $el.name }}</a>
|
|
||||||
</h5>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="project-description">{{ $el.description }}</p>
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
{{- with .Content }}<div class="prose" style="margin-bottom:var(--s10);">{{ . }}</div>{{ end }}
|
||||||
|
<div class="card-grid">
|
||||||
|
{{- range .Site.Data.projects.source }}
|
||||||
|
<div class="card">
|
||||||
|
<h3 class="card__title"><span class="dot" aria-hidden="true"></span> {{ .name }}</h3>
|
||||||
|
<div class="card__body">
|
||||||
|
<p>{{ .description }}</p>
|
||||||
|
{{- with .url }}<p style="margin-top:var(--s3);"><a href="{{ . }}" rel="noopener">{{ . | replaceRE "^https?://" "" | replaceRE "/$" "" }} →</a></p>{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<article>
|
||||||
|
<div class="page-hero">
|
||||||
|
<div class="container">
|
||||||
|
<nav class="breadcrumb" aria-label="Breadcrumb">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<span aria-hidden="true">›</span>
|
||||||
|
{{- if .CurrentSection.Title }}
|
||||||
|
<a href="{{ .CurrentSection.RelPermalink }}">{{ .CurrentSection.Title }}</a>
|
||||||
|
<span aria-hidden="true">›</span>
|
||||||
|
{{- end }}
|
||||||
|
<span aria-current="page">{{ .Title }}</span>
|
||||||
|
</nav>
|
||||||
|
<p class="page-hero__eyebrow eyebrow">
|
||||||
|
<span class="dot dot--pulse" aria-hidden="true"></span>
|
||||||
|
{{ with .Params.eyebrow }}{{ . }}{{ else }}~/{{ .Section }}{{ end }}
|
||||||
|
</p>
|
||||||
|
<h1 class="page-hero__title">{{ .Title }}</h1>
|
||||||
|
{{- with .Description }}<p class="page-hero__subtitle">{{ . }}</p>{{ end }}
|
||||||
|
<div class="page-hero__meta">
|
||||||
|
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
|
||||||
|
<span aria-hidden="true">·</span>
|
||||||
|
<span>{{ .ReadingTime }} min read</span>
|
||||||
|
{{- if .Draft }}<span aria-hidden="true">·</span><span class="chip chip--draft">draft</span>{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="prose">
|
||||||
|
{{ .Content }}
|
||||||
|
</div>
|
||||||
|
<div class="post-foot">
|
||||||
|
<span>
|
||||||
|
{{- with .Params.tags }}Filed under {{ delimit . " · " }}{{ else }}{{ .Site.Params.author }}{{ end -}}
|
||||||
|
</span>
|
||||||
|
<a href="{{ .CurrentSection.RelPermalink }}">← All posts</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
{{ define "body-class" }}page-home{{ end }}
|
||||||
|
|
||||||
|
{{ define "main" }}
|
||||||
|
|
||||||
|
<header class="hero">
|
||||||
|
<div class="container">
|
||||||
|
<p class="hero__eyebrow eyebrow">
|
||||||
|
<span class="dot dot--pulse" aria-hidden="true"></span>
|
||||||
|
{{ .Site.Params.hero_eyebrow | default "neil@potato:~$ whoami" }}
|
||||||
|
</p>
|
||||||
|
<h1 class="hero__title">
|
||||||
|
{{ .Site.Params.author }} —<br>
|
||||||
|
infrastructure, networks, systems<br>
|
||||||
|
& the occasional <span class="grad">potato</span>.
|
||||||
|
</h1>
|
||||||
|
<div class="hero__lede">
|
||||||
|
{{ .Content }}
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<a class="btn btn--primary" href="/posts/">Read the blog →</a>
|
||||||
|
<a class="btn btn--ghost" href="/projects/">Projects</a>
|
||||||
|
<a class="btn btn--ghost" href="https://shrugpw.com" rel="noopener">Hire me →</a>
|
||||||
|
</div>
|
||||||
|
<div class="social-inline">
|
||||||
|
{{- with .Site.Params.social.github }}<a href="https://github.com/{{ . }}" rel="me noopener"><span class="dot" aria-hidden="true"></span> github/{{ . }}</a>{{ end }}
|
||||||
|
{{- with .Site.Params.social.bluesky }}<a href="https://bsky.app/profile/{{ replace . "@" "" }}" rel="me noopener">bsky/{{ . }}</a>{{ end }}
|
||||||
|
{{- with .Site.Params.social.instagram }}<a href="https://instagram.com/{{ . }}" rel="noopener">the cat's instagram</a>{{ end }}
|
||||||
|
{{- with .Site.Params.social.email }}<a href="mailto:{{ . }}">{{ . }}</a>{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="section section--surface" aria-labelledby="writing-heading">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-head">
|
||||||
|
<span class="section-label">// writing</span>
|
||||||
|
<h2 id="writing-heading">Latest posts</h2>
|
||||||
|
<p>Homelab, infrastructure, and the occasional life event that turns into a systems-design problem.</p>
|
||||||
|
</div>
|
||||||
|
<div class="posts">
|
||||||
|
{{- range first 8 (where .Site.RegularPages "Section" "posts").ByDate.Reverse }}
|
||||||
|
<a href="{{ .RelPermalink }}" class="post-row">
|
||||||
|
<div class="post-row__meta">
|
||||||
|
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "Jan 2, 2006" }}</time>
|
||||||
|
{{- if .Draft }}<span class="chip chip--draft">draft</span>
|
||||||
|
{{- else }}{{ with .Params.tags }}<span class="chip chip--tag">{{ index . 0 }}</span>{{ end }}{{ end }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="post-row__title">{{ .Title }}</div>
|
||||||
|
<p class="post-row__dek">{{ .Description | default .Summary | truncate 180 }}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{- end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{- with .Site.Params.now }}
|
||||||
|
<section class="section soil" aria-labelledby="now-heading">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-head">
|
||||||
|
<span class="section-label">// now</span>
|
||||||
|
<h2 id="now-heading">What's running</h2>
|
||||||
|
</div>
|
||||||
|
<div class="now-grid">
|
||||||
|
{{- range . }}
|
||||||
|
<div class="now-card">
|
||||||
|
<h3><span class="dot" aria-hidden="true"></span> {{ .title }}</h3>
|
||||||
|
<p>{{ .body | safeHTML }}</p>
|
||||||
|
</div>
|
||||||
|
{{- end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<footer class="footer" aria-label="Site footer">
|
||||||
|
<div class="footer__inner">
|
||||||
|
<p class="footer__note">
|
||||||
|
Built with Hugo. No trackers, no popups, occasionally a typo left in on purpose.
|
||||||
|
<span aria-hidden="true">¯\_(ツ)_/¯</span><br>
|
||||||
|
© {{ now.Year }} {{ .Site.Params.author }}.
|
||||||
|
</p>
|
||||||
|
<div class="footer__links">
|
||||||
|
{{- with .Site.Params.social.github }}<a href="https://github.com/{{ . }}" rel="me noopener">github</a>{{ end }}
|
||||||
|
{{- with .Site.Params.social.bluesky }}<a href="https://bsky.app/profile/{{ replace . "@" "" }}" rel="me noopener">bluesky</a>{{ end }}
|
||||||
|
{{- with .OutputFormats.Get "rss" }}<a href="{{ .RelPermalink }}">rss</a>{{ end }}
|
||||||
|
<a href="https://shrugpw.com" rel="noopener">consulting</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
|
||||||
|
<title>{{ if .IsHome }}{{ .Site.Title }} — {{ .Site.Params.description }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}</title>
|
||||||
|
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
|
||||||
|
<meta name="author" content="{{ .Site.Params.author }}">
|
||||||
|
|
||||||
|
{{- partial "opengraph.html" . }}
|
||||||
|
{{- partial "twitter_cards.html" . }}
|
||||||
|
{{- partial "schema.html" . }}
|
||||||
|
|
||||||
|
<link rel="canonical" href="{{ .Permalink }}">
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
|
|
||||||
|
{{- with .OutputFormats.Get "rss" }}
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="{{ $.Site.Title }}" href="{{ .Permalink }}">
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
<!-- Preload critical fonts -->
|
||||||
|
<link rel="preload" href="/fonts/CommitMono-700-Regular.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
|
<link rel="preload" href="/fonts/SourceSerif4.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
|
|
||||||
|
<!-- Styles -->
|
||||||
|
{{- $tokens := resources.Get "css/tokens.css" }}
|
||||||
|
{{- $base := resources.Get "css/base.css" }}
|
||||||
|
{{- $components := resources.Get "css/components.css" }}
|
||||||
|
{{- $layouts := resources.Get "css/layouts.css" }}
|
||||||
|
{{- $css := slice $tokens $base $components $layouts | resources.Concat "css/main.css" }}
|
||||||
|
{{- if hugo.IsProduction }}
|
||||||
|
{{- $css = $css | resources.Minify | resources.Fingerprint }}
|
||||||
|
<link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Integrity }}">
|
||||||
|
{{- else }}
|
||||||
|
<link rel="stylesheet" href="{{ $css.RelPermalink }}">
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
<!-- Theme: read preference before render to avoid flash -->
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var t = localStorage.getItem('potato-theme');
|
||||||
|
if (t) { document.documentElement.setAttribute('data-theme', t); }
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<nav class="navbar" aria-label="Main navigation">
|
||||||
|
<div class="navbar__inner">
|
||||||
|
|
||||||
|
<a href="/" class="navbar__logo" aria-label="{{ .Site.Title }} home">
|
||||||
|
<span class="spud" aria-hidden="true">🥔</span>
|
||||||
|
<span>thepotato.tech</span>
|
||||||
|
<span class="shrug" aria-hidden="true">¯\_(ツ)_/¯</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button class="navbar__hamburger" aria-label="Toggle navigation" aria-expanded="false" id="nav-hamburger">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true">
|
||||||
|
<line x1="2" y1="5" x2="18" y2="5"/><line x1="2" y1="10" x2="18" y2="10"/><line x1="2" y1="15" x2="18" y2="15"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ul class="navbar__links" id="nav-links" role="list">
|
||||||
|
{{- $cur := . }}
|
||||||
|
{{- range .Site.Menus.main }}
|
||||||
|
<li><a href="{{ .URL }}"{{ if $cur.IsMenuCurrent "main" . }} aria-current="page"{{ end }}>{{ .Name }}</a></li>
|
||||||
|
{{- end }}
|
||||||
|
<li>
|
||||||
|
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle dark mode">
|
||||||
|
<svg class="icon-sun" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/>
|
||||||
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
||||||
|
<line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/>
|
||||||
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||||||
|
</svg>
|
||||||
|
<svg class="icon-moon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||||
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Toggle theme</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var btn = document.getElementById('theme-toggle');
|
||||||
|
if (btn) btn.addEventListener('click', function(){
|
||||||
|
var root = document.documentElement;
|
||||||
|
var cur = root.getAttribute('data-theme');
|
||||||
|
if (!cur) cur = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
|
var next = cur === 'dark' ? 'light' : 'dark';
|
||||||
|
root.setAttribute('data-theme', next);
|
||||||
|
localStorage.setItem('potato-theme', next);
|
||||||
|
});
|
||||||
|
var burger = document.getElementById('nav-hamburger');
|
||||||
|
var links = document.getElementById('nav-links');
|
||||||
|
if (burger && links) burger.addEventListener('click', function(){
|
||||||
|
var open = links.classList.toggle('is-open');
|
||||||
|
burger.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{{/* Open Graph meta tags */}}
|
||||||
|
{{- $title := cond .IsHome .Site.Title .Title -}}
|
||||||
|
{{- $desc := .Description | default .Site.Params.description -}}
|
||||||
|
{{- $type := cond .IsHome "website" "article" -}}
|
||||||
|
<meta property="og:url" content="{{ .Permalink }}">
|
||||||
|
<meta property="og:site_name" content="{{ .Site.Title }}">
|
||||||
|
<meta property="og:title" content="{{ $title }}">
|
||||||
|
<meta property="og:description" content="{{ $desc }}">
|
||||||
|
<meta property="og:locale" content="{{ .Site.Language.Lang }}">
|
||||||
|
<meta property="og:type" content="{{ $type }}">
|
||||||
|
{{- with .Params.image }}<meta property="og:image" content="{{ . | absURL }}">{{- end }}
|
||||||
|
{{- if not .IsHome }}
|
||||||
|
{{- with .Date }}<meta property="article:published_time" content="{{ .Format "2006-01-02T15:04:05Z07:00" }}">{{- end }}
|
||||||
|
{{- with .Lastmod }}<meta property="article:modified_time" content="{{ .Format "2006-01-02T15:04:05Z07:00" }}">{{- end }}
|
||||||
|
{{- range .Params.tags }}<meta property="article:tag" content="{{ . }}">{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{{/* Schema.org — Person on home, BlogPosting on articles */}}
|
||||||
|
{{- if .IsHome }}
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "{{ .Site.Params.author }}",
|
||||||
|
"url": "{{ .Site.BaseURL }}",
|
||||||
|
"jobTitle": "Infrastructure, Network & Systems Engineer",
|
||||||
|
"description": "{{ .Site.Params.description }}",
|
||||||
|
"sameAs": [
|
||||||
|
{{- $links := slice -}}
|
||||||
|
{{- with .Site.Params.social.github }}{{ $links = $links | append (printf "https://github.com/%s" .) }}{{ end -}}
|
||||||
|
{{- with .Site.Params.social.bluesky }}{{ $links = $links | append (printf "https://bsky.app/profile/%s" (replace . "@" "")) }}{{ end -}}
|
||||||
|
{{- with .Site.Params.social.linkedin }}{{ $links = $links | append (printf "https://linkedin.com/in/%s" .) }}{{ end -}}
|
||||||
|
{{ delimit (apply $links "printf" "%q" ".") ", " }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{- else if .IsPage }}
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "BlogPosting",
|
||||||
|
"headline": {{ .Title | jsonify }},
|
||||||
|
"description": {{ (.Description | default .Summary) | jsonify }},
|
||||||
|
"url": "{{ .Permalink }}",
|
||||||
|
"datePublished": "{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}",
|
||||||
|
"dateModified": "{{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" }}",
|
||||||
|
"author": { "@type": "Person", "name": "{{ .Site.Params.author }}", "url": "{{ .Site.BaseURL }}" }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{{/* Twitter/X card meta */}}
|
||||||
|
{{- $title := cond .IsHome .Site.Title .Title -}}
|
||||||
|
{{- $desc := .Description | default .Site.Params.description -}}
|
||||||
|
<meta name="twitter:card" content="{{ cond (isset .Params "image") "summary_large_image" "summary" }}">
|
||||||
|
<meta name="twitter:title" content="{{ $title }}">
|
||||||
|
<meta name="twitter:description" content="{{ $desc }}">
|
||||||
|
{{- with .Params.image }}<meta name="twitter:image" content="{{ . | absURL }}">{{- end }}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
||||||
|
<rect width="64" height="64" rx="12" fill="#efe7d6"/>
|
||||||
|
<path d="M20 22c-5 4-8 12-4 22 3 7 12 11 22 8 10-3 16-13 12-24-3-8-11-12-19-11-4 .5-8 2-11 5z" fill="#8a5622"/>
|
||||||
|
<path d="M22 25c-3 3-5 9-2 16 2 5 8 8 15 6 7-2 11-9 8-16-2-6-8-9-14-8-3 .3-5 1-7 2z" fill="#a06a30" opacity="0.55"/>
|
||||||
|
<circle cx="26" cy="30" r="2.1" fill="#241d12" opacity="0.7"/>
|
||||||
|
<circle cx="38" cy="26" r="1.7" fill="#241d12" opacity="0.7"/>
|
||||||
|
<circle cx="42" cy="40" r="2" fill="#241d12" opacity="0.7"/>
|
||||||
|
<circle cx="30" cy="44" r="1.6" fill="#241d12" opacity="0.7"/>
|
||||||
|
<path d="M38 25c1-5 4-8 8-8" fill="none" stroke="#697a2b" stroke-width="3" stroke-linecap="round"/>
|
||||||
|
<circle cx="46" cy="16" r="3" fill="#7d9134"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 789 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Submodule themes/hugo-sustain deleted from 1d8c53242f
Reference in New Issue
Block a user