How drewpaine.net should look. Follow this when adding pages, posts or styles. The full brand guide with live component previews is at https://claude.ai/artifact/1XDByMx9r6S2Ce45dt72Pe.
The site should feel like a well-kept lab notebook: calm sage paper, forest-green ink, and a few precise marks of color where something matters.
Status:
src/css/style.cssimplements this system. Add new styles through the tokens it defines.
Five brand colors, from palette.pdf. Forest is the primary anchor; the background is Sage, lightened.
| Brand color | Hex | Role |
|---|---|---|
| Forest | #103900 |
Primary: headings, links, nav, buttons |
| Sage | #B8BDB5 |
The ground: page background (lightened), sidebars, rules |
| Navy | #052F5F |
Supporting information, focus ring |
| Oxblood | #6B0F1A |
Callouts only |
| Orange | #FB6107 |
Signal accent, in small amounts |
Copy these into the top of src/css/style.css and style everything through them. Never hard-code a hex value in a rule.
:root {
/* Color: light theme (default) */
--surface: #e3e5e1; /* page background: Sage tinted 60% toward white */
--surface-raised: #f1f2f0; /* author bio, raised blocks */
--sage: #b8bdb5; /* sidebar ground, strong dividers */
--line: var(--sage); /* hairline rules */
--ink: #16200f; /* body text */
--ink-muted: #434b3f; /* bylines, dates, captions */
--forest: #103900; /* primary anchor */
--forest-soft: #d8e2d0; /* highlighted row ground */
--on-forest: #f1f2f0; /* text on a forest fill */
--orange: #fb6107; /* accent marks */
--oxblood: #6b0f1a; /* callout border and title */
--oxblood-soft: #f2e4e5; /* callout ground */
--navy: #052f5f; /* sidebar titles */
--focus: #052f5f; /* keyboard focus ring */
/* Type */
--font-serif: "Newsreader", Georgia, "Times New Roman", serif;
--font-sans: "Inter", "Segoe UI", system-ui, sans-serif;
--font-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;
/* Spacing (4px base) */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--space-8: 32px;
--space-12: 48px;
--space-16: 64px;
/* Radius */
--radius-sm: 2px;
--radius-md: 6px;
--radius-round: 50%;
/* Layout */
--content-width: 70%;
--content-width-narrow: 90%;
--measure: 68ch;
}
/* Dark theme: same roles, lifted hues */
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
--surface: #121710;
--surface-raised: #1b2218;
--sage: #3b4337;
--ink: #e4e8e0;
--ink-muted: #a9b1a4;
--forest: #9fcb80;
--forest-soft: #1d2c14;
--on-forest: #121710;
--oxblood: #e8939c;
--oxblood-soft: #2e1417;
--navy: #93b8e6;
--focus: #fb6107;
}
}The site follows the visitor's system setting, and the Light/Dark toggle in the header overrides it. The toggle sets data-theme="light" or data-theme="dark" on <html> and saves the choice in the browser. In style.css the dark values appear twice, once for the system setting (:root:not([data-theme="light"])) and once for the toggle (:root[data-theme="dark"]); change both together. The snippet above shows the simpler system-only version.
- Paint the page with
--surface. Never use pure white or pure black. - Body text is
--ink; bylines and captions are--ink-muted. - Forest is the dominant hue: every title, heading and link is
--forest. A primary button is a--forestfill with--on-foresttext. - Use
--sageat full strength only for sidebar grounds and rules. It's too close to--surfaceto separate things on its own. - Use
--oxbloodonly in callouts and as a paragraph marker, and use at most one callout per screen. - Use
--navyfor sidebar titles and the focus ring. - Orange is never text on a light background. It's only 2.4:1 against
--surface. Use it for underlines, the active-nav bar and small rule marks. It can be large text (24px+) on a--forestfill, or any text in the dark theme. - Pair Forest with at most one other hue per component. The one exception is the paragraph markers, which cycle through Navy, Oxblood and Orange.
All text pairs meet WCAG AA (4.5:1) in both themes. Re-check any new pair before using it.
| Text | Background | Light | Dark |
|---|---|---|---|
--ink |
--surface |
13.3:1 | 14.6:1 |
--ink |
--sage |
8.8:1 | 8.3:1 |
--ink-muted |
--surface |
7.2:1 | 8.2:1 |
--ink-muted |
--sage |
4.7:1 | 4.65:1 |
--forest |
--surface |
10.3:1 | 9.8:1 |
--oxblood |
--oxblood-soft |
9.9:1 | 7.4:1 |
--navy |
--sage |
7.0:1 | 5.0:1 |
--on-forest |
--forest |
11.6:1 | 9.8:1 |
--orange |
--surface |
2.4:1 ✗ | 5.9:1 |
--orange |
--forest |
4.2:1 (large text only) | ✗ |
| Family | Token | Used for |
|---|---|---|
| Newsreader | --font-serif |
Name, titles, headings, lede |
| Inter | --font-sans |
All running text, nav, buttons |
| IBM Plex Mono | --font-mono |
Bylines, dates, uppercase labels |
Load them in the <head> of src/_includes/layouts/base.njk:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,400;0,14..32,700;1,14..32,400&family=IBM+Plex+Mono:wght@400;500&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,600;1,6..72,400&display=swap">| Style | Family | Size / line height | Weight | Use |
|---|---|---|---|---|
| display | serif | 48 / 52px (36px under 600px) | 600 | Reserved for a large name treatment; not currently used |
| h1 | serif | 40 / 46px (32px under 600px) | 600 | Page and post titles |
| h2 | serif | 28 / 34px | 600 | Sections, blog entry titles |
| h3 | serif | 22 / 28px | 600 | Sub-sections, box titles |
| lede | serif | 21 / 32px | 400 italic | Summary under a post title |
| body | sans | 18 / 29px | 400 | Running text |
| body-small | sans | 16 / 24px | 400 | Sidebars, author bio, blog summaries |
| byline | mono | 14 / 20px | 400 | Author and date lines, footer |
| label | mono | 12 / 16px, 0.08em tracking | 500 | UPPERCASE labels |
- Keep running text within
--measure(68ch). - Give headings
text-wrap: balance. - Use bold for emphasis in body text, not whole italic sentences.
- One centered column:
max-width: var(--content-width), widening tovar(--content-width-narrow)at 600px and below. This is the site's only breakpoint. - Never let the page scroll sideways.
- Separate sections with space and a 1px
--linerule, not boxes. Only callouts, sidebars and the author bio get a background. - Spacing:
--space-4inside boxes on phones,--space-6on desktop;--space-8around boxes and between blog entries;--space-12between page sections. - Corners:
--radius-mdon boxes,--radius-smon buttons,--radius-roundon author photos (the home page headshot is a portrait, so it uses--radius-md). No shadows.
Each maps to an existing class or template in the site.
| Component | Where | Spec |
|---|---|---|
| Site header | .site-header in base.njk |
Brand mark (36px tall) and "Drew Paine, PhD" in Newsreader 600 at 22px, --forest, linking home, on the left. Nav on the right when the blog is on. --line rule below, --space-12 before the page content. |
| Theme toggle | .theme-toggle in .site-header |
Right side of the header. A 36px square icon button: --forest icon, 1px --ink-muted border, --radius-sm, --forest-soft on hover. Shows a filled moon in light mode and a sun in dark mode (the mode it switches to), with a matching aria-label and tooltip. Hidden when JavaScript is off. |
| Site nav | .sitenav in .site-header |
Only shown when the blog is on. --space-6 gaps, bold sans --forest links, no underline. Current page: 3px --orange bar beneath and aria-current="page". |
| Post header | .post-header in post.njk |
h1 title, lede, then byline in --ink-muted ("By Drew Paine, PhD · September 28, 2026"), --line rule below. |
| Callout | .callout, {% callout %} |
Full width. --oxblood-soft ground, 1px --oxblood border, --radius-md, --space-6 padding, h3 title in --oxblood, text in --ink. |
| Sidebar | .sidebar, {% sidebar %} |
Floats right at 35%, full width under 600px. --sage ground, --radius-md, optional label in --ink-muted, h3 title in --navy, body-small text. |
| Paragraph markers | #bio p::before in style.css |
A 10×13px block with --radius-sm corners, hanging --space-6 left of each home page bio paragraph, level with the first line. Cycles --navy, --oxblood, --orange, echoing the brand guide cover. Under 600px the paragraphs indent by --space-6 and the marker sits inside. Decorative only; used on the home page bio, not in posts. |
| Author bio | .author-bio in post.njk |
--surface-raised ground, --radius-md, 96px round photo beside the text (stacked, centered under 600px), label "About the author", bold name, body-small bio, email in byline style. |
| Blog entry | .post-entry in blog/index.njk |
h2 title as an un-underlined --forest link, byline, body-small summary, bold "Read more →". --line rule between entries. |
a {
color: var(--forest);
text-decoration: underline;
text-decoration-color: var(--orange);
text-decoration-thickness: 2px;
text-underline-offset: 3px;
}
a:hover { text-decoration-color: var(--forest); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }Never remove the focus outline.
- Plain, direct and specific. Write for a researcher from another field.
- Bios are third person ("Drew Paine is…"). Posts may use first person.
- Title Case for page, role and post titles. UPPERCASE only in
labelstyle. - Spell out an organization on first use with its linked acronym in parentheses: "Better Scientific Software (BSSw)".
&in role and group names ("Product & User Experience"); "and" in sentences.- Write emails out (
pained - at - lbl - dot - gov); never usemailto:links. - The brand mark is the forest block with its dot grid from the brand guide cover: a
--forestrounded rectangle (24×30, 3px corners) with ten--surfacedots stepping up to the right. It appears in the site header, left of "Drew Paine, PhD" in Newsreader 600, and links home. Use it only with the name; don't recolor or redraw it. - No emoji. The only icons are the sun and moon on the theme toggle; everywhere else, use text labels.
- Every image needs alt text naming who or what it shows.