feat: initial shared design system extracted from newerror
Foundation + chrome for the bchen.dev static sites: design tokens, Spectral / Helvetica / IBM Plex Mono type, base + .prose typography, data-driven nav + footer, and prefers-color-scheme + [data-theme] dark mode with a pre-paint init. Colors are overridable CSS custom properties (defaults = newerror's palette); each site sets only its own colors.css. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C96hwEU9cM7mJpqATiv7R5
This commit is contained in:
316
base.css
Normal file
316
base.css
Normal file
@@ -0,0 +1,316 @@
|
||||
/* =====================================================================
|
||||
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; }
|
||||
|
||||
/* =====================================================================
|
||||
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; }
|
||||
}
|
||||
Reference in New Issue
Block a user