Files
static-design-system/base.css
Brendan Chen 2ed585029b feat: add standard nav->content top rhythm (56px)
Content region gets a shared top padding on .page > main so every consuming
site has the same nav-to-content gap (matches newerror's .about spacing).
When newerror adopts the submodule, its per-page-type top paddings become
deltas over this baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C96hwEU9cM7mJpqATiv7R5
2026-06-30 21:36:40 -04:00

317 lines
9.3 KiB
CSS

/* =====================================================================
static-design-system — base.css
Shared visual foundation for the bchen.dev static sites.
Canonical design: "A New Error" (newerror.bchen.dev) — the "Ledger"
direction: Spectral serif body, Helvetica sans UI, IBM Plex Mono.
HOW COLOR WORKS
Every color is a CSS custom property. The values below are DEFAULTS
(newerror's palette). Each consuming site overrides ONLY these color
tokens in its own colors.css, which loads AFTER this file. Fonts,
spacing, typography, nav/footer chrome, and dark-mode behavior are
shared and should NOT be re-declared per site.
IMPORTANT: keep this file Liquid-safe. Its contents are passed through the
Jekyll include tag by each site's served wrapper (assets/ds.css), so do NOT
introduce Liquid delimiters (double-brace or brace-percent sequences).
Plain CSS braces are fine.
===================================================================== */
:root {
/* Type */
--serif: "Spectral", Georgia, "Times New Roman", serif;
--sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
--mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
/* Layout */
--measure: 760px; /* standard content column */
--read: 680px; /* narrower reading column (articles) */
--pad: 34px;
--gutter: 104px; /* date column width (ledger layouts) */
}
/* ---- Color tokens — DEFAULTS (newerror). Override per site in colors.css ---- */
:root, :root[data-theme="light"] {
--bg: #FAFBFC;
--ink: #1B2027;
--muted: #69717C;
--faint: #99A1AC;
--rule: #E6E9EF;
--rule-2: #b6b9be;
--accent: #1463C7;
--accent-hover: #0F4FA3;
--field: #F1F3F6;
}
:root[data-theme="dark"] {
--bg: #14171C;
--ink: #E2E6EC;
--muted: #9AA2AD;
--faint: #69717C;
--rule: #262B32;
--rule-2: #5e6670;
--accent: #6FA9F2;
--accent-hover: #97C1F8;
--field: #1D2128;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #14171C;
--ink: #E2E6EC;
--muted: #9AA2AD;
--faint: #69717C;
--rule: #262B32;
--rule-2: #5e6670;
--accent: #6FA9F2;
--accent-hover: #97C1F8;
--field: #1D2128;
}
}
/* =====================================================================
Base
===================================================================== */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: var(--serif);
font-size: 18px;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
transition: background-color 0.25s ease, color 0.25s ease;
}
::selection { background: color-mix(in oklab, var(--accent) 22%, transparent); }
a { color: inherit; }
/* ---- shell / responsive width ---- */
.shell {
width: 100%;
max-width: var(--measure);
margin-inline: auto;
padding-inline: var(--pad);
}
.page { min-height: 100vh; display: flex; flex-direction: column; }
.page > main { flex: 1 0 auto; padding-top: 56px; } /* standard nav->content rhythm */
/* =====================================================================
Top navigation
===================================================================== */
.nav {
font-family: var(--sans);
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 16px;
padding: 38px 0 18px;
border-bottom: 1px solid var(--rule);
}
.nav.minimal { border-bottom: none; padding-bottom: 0; }
.mast {
font-family: var(--serif);
font-size: 20px;
font-weight: 600;
letter-spacing: 0.005em;
color: var(--ink);
text-decoration: none;
white-space: nowrap;
}
.mast:hover { color: var(--accent); }
.nav-links {
display: flex;
align-items: baseline;
gap: 26px;
}
.nav-links a, .iconbtn {
font-family: var(--sans);
font-size: 13.5px;
letter-spacing: 0.01em;
color: var(--muted);
text-decoration: none;
cursor: pointer;
background: none;
border: none;
padding: 0;
}
.nav-links a:hover, .iconbtn:hover { color: var(--accent); }
.nav-links a.active { color: var(--ink); }
.nav-tools { display: flex; align-items: center; gap: 18px; }
.iconbtn {
display: inline-flex; align-items: center; justify-content: center;
width: 26px; height: 26px; border-radius: 6px;
color: var(--muted);
}
.iconbtn:hover { color: var(--accent); background: var(--field); }
.iconbtn svg { width: 15px; height: 15px; display: block; }
/* hamburger — hidden on desktop */
.hamburger { display: none; }
.mobile-menu { display: none; }
/* =====================================================================
Footer
===================================================================== */
.foot {
font-family: var(--sans);
border-top: 1px solid var(--rule);
margin-top: 20px;
padding: 22px 0 40px;
display: flex; align-items: center; justify-content: space-between;
gap: 16px;
font-size: 12.5px;
color: var(--faint);
flex: 0 0 auto;
}
.foot a { color: var(--muted); text-decoration: none; }
.foot a:hover { color: var(--accent); }
.foot .rss-link { display: inline-flex; align-items: center; gap: 7px; }
/* =====================================================================
Prose — reusable content typography (markdown pages, articles)
Wrap rendered content in <div class="prose"> to opt in.
===================================================================== */
.prose { font-size: 19.5px; line-height: 1.68; color: var(--ink); }
.prose > :first-child { margin-top: 0; }
.prose p { margin: 0 0 1.3em; text-wrap: pretty; }
.prose h1 {
font-family: var(--serif);
font-size: 32px; line-height: 1.16; font-weight: 600;
letter-spacing: -0.018em; color: var(--ink);
margin: 0 0 0.5em; text-wrap: pretty;
}
.prose h2 {
font-family: var(--serif);
font-size: 25px; font-weight: 600; line-height: 1.25;
letter-spacing: -0.01em;
margin: 2em 0 0.6em;
}
.prose h3 {
font-family: var(--serif);
font-size: 21px; font-weight: 600;
margin: 1.7em 0 0.5em;
}
.prose a {
color: var(--ink);
text-decoration: underline;
text-decoration-color: var(--rule-2);
text-underline-offset: 2px;
text-decoration-thickness: 1px;
}
.prose a:hover { color: var(--accent); text-decoration-color: var(--accent); }
.prose strong { font-weight: 600; }
.prose em { font-style: italic; }
.prose blockquote {
margin: 1.6em 0;
padding-left: 22px;
border-left: 2px solid var(--rule-2);
color: var(--muted);
font-style: italic;
}
.prose blockquote p:last-child { margin-bottom: 0; }
.prose ul, .prose ol { margin: 0 0 1.3em; padding-left: 1.3em; }
.prose li { margin: 0.3em 0; padding-left: 0.2em; }
.prose li::marker { color: var(--faint); }
.prose hr {
border: none; height: 0;
border-top: 1px solid var(--rule);
margin: 2.4em auto;
width: 40%;
}
.prose code {
font-family: var(--mono);
font-size: 0.82em;
background: var(--field);
padding: 0.12em 0.4em;
border-radius: 4px;
}
.prose pre {
font-family: var(--mono);
font-size: 14px; line-height: 1.55;
background: var(--field);
padding: 18px 20px;
border-radius: 8px;
overflow-x: auto;
margin: 1.5em 0;
}
.prose pre code { background: none; padding: 0; font-size: inherit; }
.prose figure { margin: 2em 0; }
.prose figure img { display: block; width: 100%; height: auto; border-radius: 3px; }
.prose figcaption {
font-family: var(--sans);
font-size: 12.5px; color: var(--faint);
margin-top: 10px; text-align: left;
}
.prose img { max-width: 100%; height: auto; }
/* =====================================================================
Mobile
===================================================================== */
@media (max-width: 640px) {
:root { --pad: 22px; --gutter: 74px; }
body { font-size: 17px; }
.nav-links { display: none; }
.hamburger { display: inline-flex; }
.mobile-menu {
display: none;
font-family: var(--sans);
}
.mobile-menu.open {
display: flex; flex-direction: column;
gap: 2px;
padding: 8px 0 14px;
border-bottom: 1px solid var(--rule);
}
.mobile-menu a, .mobile-menu button {
font-family: var(--sans);
text-align: left;
background: none; border: none; cursor: pointer;
color: var(--ink);
font-size: 16px;
padding: 12px 4px;
text-decoration: none;
border-radius: 6px;
}
.mobile-menu a:hover, .mobile-menu button:hover { color: var(--accent); }
.mobile-menu .row { display: flex; align-items: center; gap: 10px; }
.prose { font-size: 18.5px; }
.prose h1 { font-size: 30px; }
}
/* =====================================================================
Icon swaps (theme toggle + hamburger), driven by [data-theme] / .is-open
===================================================================== */
.theme-toggle .ico-sun { display: none; }
.theme-toggle .ico-moon { display: block; }
:root[data-theme="dark"] .theme-toggle .ico-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .ico-sun { display: block; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) .theme-toggle .ico-moon { display: none; }
:root:not([data-theme="light"]) .theme-toggle .ico-sun { display: block; }
}
.hamburger .ico-close { display: none; }
.hamburger.is-open .ico-bars { display: none; }
.hamburger.is-open .ico-close { display: block; }
/* reduce motion */
@media (prefers-reduced-motion: reduce) {
* { transition: none !important; }
}