Skip to content

Docs site: adopt the calm theme (landing + internal pages) - #135

Merged
adnaan merged 5 commits into
mainfrom
calm-theme
Aug 8, 2026
Merged

Docs site: adopt the calm theme (landing + internal pages)#135
adnaan merged 5 commits into
mainfrom
calm-theme

Conversation

@adnaan

@adnaan adnaan commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Closes #133.

One visual system across the landing and the docs shell: #2F5D8A instead of emerald, IBM Plex Sans instead of Inter, no gradients, no shadows, no transforms.

How the colors are applied

By overriding tinkerdown's own semantic custom properties in brand.css, not by hardcoding hexes into rules — so the shell's dark theme keeps working off the same variables and no dark palette had to be invented.

The override is scoped :root:not([data-theme="dark"]). That is not decoration: [data-theme="dark"] and :root are both specificity (0,1,0) and brand.css loads after the theme's inline <style>, so a bare :root block would win on source order and silently break the theme toggle.

styling.site_css takes a single path, so docs.css reaches pages via @import from brand.css. CSP allows it (style-src 'self'). Staying on TINKERDOWN_REF=v0.3.9styling.tokens is v0.4.0 and drags in the whole ephemeral-UI reframe for no gain here.

Deviations from the design, and why

  • Archetypes A and C ship two-column, not three. tinkerdown's client injects the page TOC inside the active page's sidebar <li>. It is restyled where it lands; pulling it out to a fixed right gutter leaves a hole behind it and makes page width depend on 100vw arithmetic, which is the fastest way to fail the no-horizontal-overflow criterion. A real right-hand <aside> is an upstream change.
  • Archetype C's mono entry names apply only to h3:has(> code). The design's samples are symbol names; in the real content 804 of 807 h3s are prose and the symbols live in tables, which get the definition grid instead.
  • Archetype B's rows are inline, not left/right justified. That split needs a wrapper markdown never emits, and flex-on-li is not a substitute — every inline <code> in a gloss would become its own flex item.
  • Category chips are dropped. No markdown source, and reference/api.md is sync-generated.
  • Callout labels come from the markdown's own bold lead-in rather than a fabricated ::before, so a "Security note" does not render as "NOTE / Security note".

Beyond the issue's file list

Prism's syntax palette is neutralised to three hues (it loads before brand.css and would keep painting code blocks), and four example templates that still carried emerald are retinted — they render inside the landing's demo cards. greet-nojs restates the whole palette because it is shown in an iframe and CSS does not cross that boundary. The greet-wall heartbeat dot keeps its pulse: the rule is that nothing moves unless the app is updating, and that dot marks a server push.

Found by measuring, not by reading

  • 1rem is a flat 14px at every viewport, so three elements were already under the 11.5px floor — .page-source-meta code at 10.12px (0.85rem × 0.85em), .nav-group-title at 10.92px, .search-button kbd at 11.0px.
  • The theme bleeds <pre> outward with margins sized for its own 800px measure; against a 720px column that overflows the page at 924px.
  • Goldmark ends an HTML block at the first blank line — a blank line inside a <pre> re-parsed the Go sample as markdown, stripping its indentation.
  • pre.install is inline-block, so on a 393px phone it sized to the full go get line and scrolled the page 12px sideways. make sweep caught this; no test covers 393px on the landing.

Verification

Against the pinned v0.3.9 image:

  • New e2e/calm_theme_test.go — the issue's acceptance section as assertions at 924px and 1440px across the landing and all three archetypes: no horizontal overflow, <pre> scrolls internally, no text under 11.5px, nothing at ≤13px lighter than #6B6862, gopher served locally with its CC BY 3.0 attribution. A new file because responsive_test.go owns 1280/768/393 and passes unchanged.
  • responsive_test.go, docs_ia_test.go, breadcrumb_test.go pass unchanged; the light/dark toggle still round-trips. All eight TestSpine* pass — the five embed-lvt mounts and both greet-nojs iframes are unchanged in path and sandbox.
  • make test green (20 packages). make sweep: 196 page-viewport visits, 0 overflow. Two flags remain on /recipes/ui-patterns/lists/large-table, a pre-existing 4.9 MB page that renders in 9.6s on production and 7.3s on this branch.
  • TestThemeAccentInjected updated to the new accent (it is not in the issue's pass-unchanged list).

🤖 Generated with Claude Code

https://claude.ai/code/session_0166MK1arBYbVZq6wfm8EsQZ

adnaan and others added 5 commits August 4, 2026 02:44
Issue #133, phases 1-2 of 4. Replaces the emerald accent and Inter with
the calm system's #2F5D8A and IBM Plex Sans, and adds content/assets/
docs.css for the three page archetypes. The landing is unchanged so far
and still ships its own emerald stylesheet; phase 3 rewrites it.

Colors are applied by overriding tinkerdown's semantic custom properties
rather than hardcoding hexes in rules, so the shell's dark theme keeps
working off the same variables. The override is scoped
:root:not([data-theme="dark"]) because [data-theme="dark"] and :root are
both specificity (0,1,0) and brand.css loads after the theme's inline
<style> — a bare :root block would win on source order and silently
break the theme toggle.

docs.css reaches pages via @import from brand.css: styling.site_css
takes a single path, so that is the only way to keep the file split the
issue asks for. CSP allows it (style-src 'self').

Three things measured against the shipped theme rather than assumed:

  - 1rem resolves to a flat 14px at every viewport (tinkerdown's
    --pico-font-size: 87.5% beats pico's responsive ladder), so three
    elements were already under the design's 11.5px floor and are now
    set in px: .page-source-meta code (10.12px, 0.85rem x 0.85em),
    .nav-group-title (10.92px) and .search-button kbd (11.0px).
  - The theme bleeds pre blocks outward with margins sized for its own
    800px measure. Against a 720px column that overflows the page at
    924px wide, so the bleed is neutralised.
  - Archetype C sets entry names in mono because the design's samples
    are symbol names. In the real content 804 of 807 h3s are prose and
    the symbols live in tables, so mono is applied only to headings
    that are themselves code, and the tables get the definition grid.

Section-index link rows key off :has(), the only signal available — no
page-type class is emitted and <body> carries no attributes. Both
authored shapes are matched (`[Title](/x) —` and `**[Title](/x)** —`),
covering 47 all-link lists and leaving all 434 prose lists alone.

Verified in the pinned v0.3.9 image at 924px and 1440px on the landing
and all three archetypes: no horizontal overflow, no text under 11.5px,
nothing at 13px or below lighter than #6B6862, pre blocks scrolling
internally. responsive_test.go, docs_ia_test.go and breadcrumb_test.go
pass unchanged, and the light/dark toggle still round-trips.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166MK1arBYbVZq6wfm8EsQZ
Issue #133, phases 3-4 of 4. The landing was the presenting problem: the
same greeting demo eight times, two stacked code blocks each, and no
wayfinding. It is now the design's six-step spine with a sticky rail,
and landing.css drops from 31KB of emerald gradients to the calm layout.
Copy is lifted verbatim from the design reference.

Every live demo is still a real app: the five embed-lvt mounts and both
greet-nojs iframes are unchanged in path and sandbox, so the whole
TestSpine* suite passes untouched. Their min-heights come down to fit
the calm cards — min-height, so a growing wall still pushes the card
open rather than clipping.

The four example templates that still carried emerald are retinted. Only
greet-nojs restates the whole palette: it is shown in an iframe and CSS
does not cross that boundary, so unlike the other demos (which the
landing styles directly) it has to carry the theme itself or the JS-off
card would not match the JS-on card beside it. The greet-wall heartbeat
dot keeps its pulse — the design's rule is that nothing moves unless the
app is updating, and that dot marks a server push, which is exactly the
app updating.

Four things found by measuring rather than by reading the stylesheet:

  - Goldmark ends an HTML block at the first blank line. A blank line
    inside a <pre> silently re-parsed the rest of the Go sample as
    markdown: indentation stripped, <p> tags inside the <code>. The
    snippets are compact now and index.md carries a note saying why.
  - `.doc h1` is a descendant selector, and the demos inline a whole
    running app into the page — so the greeting app's own <h1> was
    rendering at the hero's 44px. Headings are scoped to direct
    children of a section.
  - Pico sizes inputs with input:not([type=checkbox]){width:100%},
    which ties with a bare `input` on specificity and wins on order, so
    the iframe's input spanned the card until the rule was made
    specific enough.
  - pre.install is inline-block, so on a phone it sized to the full
    go-get line, dragged its flex parent with it and scrolled the page
    12px sideways. Caught by `make sweep`, not by any test.

New e2e/calm_theme_test.go turns the issue's acceptance section into
assertions at 924px and 1440px across the landing and all three
archetypes: no horizontal overflow, <pre> scrolls internally, no text
under 11.5px, nothing at 13px or below lighter than #6B6862, and the
gopher served locally with its CC BY 3.0 attribution. It is a new file
because responsive_test.go owns 1280/768/393 and must pass unchanged.
TestThemeAccentInjected moves to the new accent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166MK1arBYbVZq6wfm8EsQZ
/simplify pass over the calm-theme diff. Four findings, all in docs.css:

  - .page-toc-link.active could never match. The client's scroll tracking
    toggles .active on the .page-toc-item <li>, not on the <a> inside it,
    so the sibling selector was the only live one.
  - The lede rule restated the paragraph rule's font-size and line-height
    verbatim; only its extra margin was ever doing anything.
  - The base content-link rule reset background, padding and margin, but
    the theme sets none of those until :hover. The resets belong on the
    hover rule, which is where the 0.2rem twitch they undo comes from.
  - The bleed-neutralising selector list names block types content/ does
    not author. Kept, with a comment: it mirrors the theme's own list, and
    dropping them would let the overflow return the day a page adds one.

Checked and left alone: all three IBM Plex Sans and all three JetBrains
Mono weights are genuinely requested by the browser, and landing.css has
no selector without markup behind it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166MK1arBYbVZq6wfm8EsQZ
…samples

Two problems, one visible and one that read as "highlighting is off".

The landing's template samples were plain <pre> with no language class, so
Prism never touched them — the old landing hand-wrote <span class="tag">
colouring and the rewrite dropped it. They now carry language-html, which
also settles an inconsistency: every Go block on the page had a copy button
and the HTML blocks beside them did not. Verified that tokenising leaves
{{lvtClientScriptURL}}, {{.Name}} and {{if .lvt.Pending}} as literal text.

The palette was also flatter than the design intends. Reducing to three
hues left function names, types and HTML tag names in the same near-black
as punctuation, so ~19% of tokens carried any colour and a block read as
undifferentiated. Declared and invoked names — .token.tag, .token.function,
.token.class-name — get --lt-code-name #5A5560, a desaturated slate that is
darker than --lt-meta and so still clears the <=13px contrast floor that
calm_theme_test.go enforces. Coloured tokens go from ~19% to ~28%; the
background, borders and absence of shadows are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166MK1arBYbVZq6wfm8EsQZ
… timestamps as errors

Reported: "the server said hi at" is not horizontally aligned with the list
of greetings.

Two causes. .from-server carried justify-content:center while the wall rows
were left-aligned behind the list's 40px marker padding, so the heartbeat
floated in the middle of a column it belongs at the head of. Both now start
at the container's left edge and the wall's markers are gone — Pico sets
`ul li{list-style:square}` on the li, so a list-style on the ul never wins.

The second cause was mine: landing.css styled every <small> inside a demo
as a field error, block-level and full-width in --danger. That rule exists
for .lvt.ErrorTag output in greet-validate, but the wall emits <small> for
greeting timestamps — so every timestamp rendered rust-red on its own line,
which is also what wrapped each greeting onto two lines. Scoped to
`form small`.

The wall's own presentation now lives in wall.tmpl beside .from-server,
so it travels with the app to the recipe pages that embed it too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166MK1arBYbVZq6wfm8EsQZ
@adnaan adnaan closed this Aug 6, 2026
@adnaan adnaan reopened this Aug 6, 2026
@adnaan adnaan closed this Aug 8, 2026
@adnaan adnaan reopened this Aug 8, 2026
@adnaan
adnaan merged commit 2b10c5d into main Aug 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Docs site: adopt the calm theme (landing + internal pages)

1 participant