Docs site: adopt the calm theme (landing + internal pages) - #135
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #133.
One visual system across the landing and the docs shell:
#2F5D8Ainstead 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:rootare both specificity (0,1,0) andbrand.cssloads after the theme's inline<style>, so a bare:rootblock would win on source order and silently break the theme toggle.styling.site_csstakes a single path, sodocs.cssreaches pages via@importfrombrand.css. CSP allows it (style-src 'self'). Staying onTINKERDOWN_REF=v0.3.9—styling.tokensis v0.4.0 and drags in the whole ephemeral-UI reframe for no gain here.Deviations from the design, and why
<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 on100vwarithmetic, which is the fastest way to fail the no-horizontal-overflow criterion. A real right-hand<aside>is an upstream change.h3:has(> code). The design's samples are symbol names; in the real content 804 of 807h3s are prose and the symbols live in tables, which get the definition grid instead.liis not a substitute — every inline<code>in a gloss would become its own flex item.reference/api.mdis sync-generated.::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.cssand would keep painting code blocks), and four example templates that still carried emerald are retinted — they render inside the landing's demo cards.greet-nojsrestates 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
1remis a flat 14px at every viewport, so three elements were already under the 11.5px floor —.page-source-meta codeat 10.12px (0.85rem×0.85em),.nav-group-titleat 10.92px,.search-button kbdat 11.0px.<pre>outward with margins sized for its own 800px measure; against a 720px column that overflows the page at 924px.<pre>re-parsed the Go sample as markdown, stripping its indentation.pre.installisinline-block, so on a 393px phone it sized to the fullgo getline and scrolled the page 12px sideways.make sweepcaught this; no test covers 393px on the landing.Verification
Against the pinned v0.3.9 image:
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 becauseresponsive_test.goowns 1280/768/393 and passes unchanged.responsive_test.go,docs_ia_test.go,breadcrumb_test.gopass unchanged; the light/dark toggle still round-trips. All eightTestSpine*pass — the five embed-lvt mounts and both greet-nojs iframes are unchanged in path and sandbox.make testgreen (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.TestThemeAccentInjectedupdated to the new accent (it is not in the issue's pass-unchanged list).🤖 Generated with Claude Code
https://claude.ai/code/session_0166MK1arBYbVZq6wfm8EsQZ