Skip to content

Default theme refresh: attached card, chip palette, heatmap timeline#699

Merged
srid merged 32 commits intomasterfrom
noble-cup
May 4, 2026
Merged

Default theme refresh: attached card, chip palette, heatmap timeline#699
srid merged 32 commits intomasterfrom
noble-cup

Conversation

@srid
Copy link
Copy Markdown
Owner

@srid srid commented May 3, 2026

The default Emanote theme reads as a single self-contained notebook card now — sidebar / prose / right-panel / title strip / backlinks-bottom all stack inside one rounded shadow-bound #container, so the page UI looks composed instead of assembled. The work started as a marginalia experiment (Tufte-style backlinks in the right margin) and grew through review into a full visual-language pass: every "linked note" surface in the codebase now uses the same chip language, headings tighten to a calmer scale, the timeline becomes a year-stacked heatmap with hover contexts, and the chrome typeface swaps to Mona Sans.

Layout — every edge attaches to the main card

┌──────────────────────────────────────────┐
│  ░░░░ note title strip (always) ░░░░     │
├────────────┬──────────────────┬──────────┤
│            │                  │          │
│  sidebar   │      prose       │  right   │
│  (md+)     │   (always)       │  panel   │
│            │                  │  (lg+)   │
│            │                  │          │
├────────────┴──────────────────┴──────────┤
│  ░░░░ backlinks + timeline strip (<lg) ░░│
├──────────────────────────────────────────┤
│  ░░░░ footer (always) ░░░░               │
└──────────────────────────────────────────┘
Width Sidebar Right panel Bottom strip
<md
mdlg
lg+

Right panel hides one breakpoint earlier than the sidebar so the prose keeps room as the viewport narrows — the bottom strip carries the same content (backlinks + timeline heatmap) while the sidebar stays visible.

A unified chip language

Every place that renders a "title of a note" now reads as one family — same color, weight, tracking, palette stop:

Surface Where
Page title strip top of card
Inline wikilinks in prose <Internal> via pandoc.tpl
Backlinks (margin / bottom / card / context flyout) three templates
Query results query-default.tpl, query-timeline.tpl
Timeline (daily backlinks) timeline.tpl
Tasks index special/tasks.tpl
Inline tags emanote:inline-tag in index.yaml
Metadata tag chips (page footer) metadata.tpl

bg-primary-50/70 text-primary-600 font-semibold tracking-tight px-1 py-0.5 rounded-sm — the same chip rule everywhere, with a small dial-down in saturation (the original bg-primary-50 + text-primary-700 read as too prominent against the prose).

External links inherit the color but skip the background and the bold weight — they get text-primary-600 + underline-on-hover + the existing external glyph, so prose still distinguishes "another note" from "another site".

Color and chrome

  • Theme: blueemerald (index.yaml:27). slate was tried first but reads as blue-gray; emerald gets Emanote off the "AI-app default" baseline at the same calm saturation level.
  • TOC drops the primary palette entirely (active item is bg-gray-100 text-gray-900) — TOC entries map to headings that render in plain prose, and reserving primary for "linked note" affordances tightens the visual semantics.
  • Container bumps past Tailwind's container 1536px cap to 100rem (1600px) at 2xl+, so ultra-wide viewports don't waste ~300px of horizontal gutter.

Typography

Before After
Chrome / headings Space Grotesk Mona Sans (variable, more characterful — GitHub uses it as their display face)
Prose Lora Lora (kept)
Code Space Mono Space Mono (kept)

Mona Sans's larger x-height makes the sidebar tree feel oversized at the inherited 16px, so #sidebar pins to text-sm (14px). Self-hosted via the existing fonts/update.sh flow — no Google Fonts runtime dependency.

Heading scale tightens

h1  text-5xl 48px → text-4xl 36px
h2  text-4xl 36px → text-3xl 30px
h3  text-3xl 30px → text-2xl 24px
h4  text-2xl 24px → text-xl  20px
h5  text-xl  20px → text-lg  18px
h6  text-lg  18px → text-base 16px

All headings now share font-semibold (the previous font-bold for h1+h2 only had no semantic basis). h2's section-divider rule drops from border-b-2 border-gray-200 to border-b border-gray-100 dark:border-gray-700 — a thin section break instead of a hard rule that competes with the heading. Margins tighten one notch at each level (mt-12 → mt-10, mt-10 → mt-8, etc.), so section breaks have ~4em of air rather than ~8em. letter-spacing: -0.02em now scopes to h1–h4 only — at 18px and below in Mona Sans the negative tracking made glyphs touch.

Timeline → year heatmap

Daily-note backlinks (<ema:note:backlinks:daily>) used to render as a chip list inside the prose <main>, below the article. With the right-panel established as the side-material hub, timeline belongs there too — and a heatmap reads better than a flat list for chronological texture.

2025
JAN ▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢
FEB ▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢
MAR ▢▢▢▢▢▢▢▢▢▢▢▢▢▤▤▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢
APR ▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢▢
…

Per-year layout: 12 month rows × 31 cells (4×4px each, 5px stride ≈ 155px so it fits the right-panel's narrow content width). Filled cells are anchors — clicking navigates to the daily note, hovering opens a context flyout (header YYYY-MM-DD — title in the primary palette, then the rendered backlink context paragraphs, exact same visual language as backlinks-margin's flyout). Empty cells are gray placeholders. Years stack newest-first.

Renders in two homes — the right-panel (lg+) and the bottom strip (<lg) — so narrow viewports see the timeline too. Re-renders on @emanote/morph so live-server in-app navigation refreshes without a hard reload.

JS lives in _emanote-static/js/timeline-heatmap.js, wired through emanoteJsModuleNames in JsBundle.hs (one-line addition). Dates are parsed from each backlink's title via a YYYY-MM-DD regex; entries without a parseable date are silently skipped and the section hides itself if zero entries parse.

Sidebar polish

  • Site title in the sidebar header (was hardcoded "Home"), reading from page.siteTitle.
  • text-sm leading-snug so Mona Sans's larger x-height doesn't blow the tree out vertically.
  • Active-item palette aligned with the chip language — bg-primary-50/70 text-primary-600 font-semibold tracking-tight.
  • Toggle icons swap from filled circles to gray-400 chevrons; leaf icon becomes a small gray dot in the icon slot (was a file SVG).
  • Depth rails dropped — the indent gap is enough rhythm; the rail competed with the active-item chip.

Special pages adopt the card chrome

/-/all, /-/tags, /-/tasks (and any future special page) used to render against a bare bg-gray-200/300 wrapper that didn't match note pages at all. They now share the no-sidebar branch's card chrome — same title strip, same footer, same rounded shadow.

A "← Home" affordance sits absolute-positioned at the left of the title strip (hidden md:inline-flex) so there's always a one-click way back to / from any special page.

/-/tasks got its own restructure: each task-group is rendered as a chip-style note title with /-separated breadcrumbs above its task list, and individual tasks render as hover-tinted rows (rounded-sm hover:bg-gray-50 px-2 py-1).

Tables, code blocks, syntax highlighting, images

  • Tables: width:100%, border-collapse: collapse, thead bottom-rule + tbody row separator + row hover. heist-extra hardcodes Tailwind cell-border classes that conflict with the new style — overridden via !important in styles.tpl for now (issue filed: srid/heist-extra#18).
  • Code blocks: dropped the heavy framing (py-4 pr-4 rounded-lg overflow-x-auto) for a cleaner mb-6 text-sm — the surrounding card already gives them visual containment.
  • Syntax highlighting: skylighting.css rewritten with a coordinated light/dark token palette. Primary tokens follow the theme via var(--color-primary-*); secondary tokens (slate / orange / rose / violet / amber) use hardcoded hex because Tailwind v4 only emits CSS variables for palettes referenced in scanned classes.
  • Prose images: main p > img:only-child now renders framed (rounded-md border) so screenshots and diagrams sit inside a thin chrome instead of bleeding into the prose flow.
  • Footnote popup: FOOTNOTE N header in the primary palette + border-l-4 border-l-primary-500 so the popup self-identifies and visually anchors back to its <sup> ref.

Footer rewrite

Folded into the bottom of the card (was a separate sibling of #container). Renders as a text-link strip — Home / Index / Tags / Tasks on the left, Emanote logo + version on the right — sharing the card's bg-gray-50 chrome and md:rounded-b-lg so it visually closes the page.

Task list polish

In-prose task lists (- [ ] ... / - [x] ...) get a dedicated --ema-checkbox SVG that's em-sized (h-[0.9em] w-[0.9em]) with align-[-0.1em] so it sits on the text baseline. Strokes lightened (rect stroke-width=2, check stroke-width=3). The marker-slot rule that hides the bullet when the first child is a checkbox is now scoped to main ul.list-disc li:has(> svg.--ema-checkbox) — previously it fired on /-/tasks where there's no list-disc context, mis-rendering tasks there.

Backlinks-bottom row layout

The narrow-viewport backlinks card was rendering as one flat block of chips + contexts. Now each row is separated (divide-y divide-gray-200) with the context indented under its chip via a border-l-2 rule, so multi-backlink pages parse at a glance instead of reading as a single ball of mud. The same row-separated pattern is applied to the no-sidebar full-card backlinks.tpl for consistency.

Drive-bys

  • Nix chrome-devtools shell evaluates on Darwin. pkgs.chromium has Linux-only meta.platforms; on macOS the wrapper now omits --executable-path and lets Puppeteer locate the system Chrome. Linux keeps the pinned nixpkgs chromium so the shell stays self-contained.
  • /code-police project rules. .agency/code-police.md captures four patterns from Stork: drop inline IIFE for an ES module under _emanote-static/js/ #672's follow-ups (one-field options bags, mirror flags shadowing DOM state, vendor globals from separate <script src>, prefer Tailwind utilities over styles.tpl CSS) plus calibration examples for built-in rules.
  • /neuron-layout backlinks bug fix. No-sidebar branch was rendering backlinks-bottom (which is lg:hidden), so backlinks vanished entirely at desktop widths. Now uses the full-card backlinks template since there's no right-panel to host the margin variant.
  • "Linked from" consistent across all three backlinks templates (was "Links to this page" in the card variant).
  • Docs cleanup: docs/architecture.md (out of date) and docs/tips/js.md removed; docs/tips/js/{math,mermaid}.md moved up to docs/tips/{math,mermaid}.md with _redirects entries.

Try it locally

nix run github:srid/emanote/noble-cup -- -L $PWD/docs run --port 9010

Generated by Claude Code (model claude-opus-4-7).

srid added 11 commits May 3, 2026 19:09
nixpkgs ships no Chromium derivation for Darwin (its meta.platforms is
Linux-only), so referencing pkgs.chromium fails eval on macOS. On
Darwin, fall through to chrome-devtools-mcp's built-in browser
discovery — Puppeteer locates a user-installed Google Chrome at the
standard /Applications path, otherwise downloads chrome-headless-shell
into its cache. Linux keeps the pinned nixpkgs chromium so the shell
stays fully self-contained.
The default layout now reads as one rounded card with chrome attached
on every edge: title strip on top, sidebar on the left, right panel
(TOC + backlinks) on the right at lg+, and a backlinks strip on the
bottom at smaller widths. Right panel hides one breakpoint earlier
than the sidebar so the prose keeps room as the viewport narrows.

Backlinks in the right margin show titles only; hovering reveals the
context as a flyout to the left of the card. The flyout uses a
transparent hover-bridge — outer wrapper extends right up to the
link, inner card sits 0.75rem inside — so cursor traversal between
link and card never drops the hover.

Also lands a one-shot page-load reveal (sidebar fade + main rise),
gated on prefers-reduced-motion.
The Stork ES-module PR (#672) accumulated seven follow-up commits
across /code-police and /hickey+/lowy. Three patterns recur often
enough to warrant a project rule:

- one-field options bags (replace with a positional param)
- mirror flags shadowing DOM state (query the DOM)
- vendor globals from separate <script src> (guard at module load)

Plus two calibration examples for built-in rules: silent URL/parse
fallbacks must log, and string literals across hot-path methods hit
the rule-of-three at exactly three sites.
…alette

Color was bleeding everywhere — every internal wikilink rendered as a
filled blue pill, the title strip was a saturated banner, and the TOC
fought with backlinks for visual weight. Take the page-title strip's
treatment as the anchor (bg-primary-50 + text-primary-700 + tracking-tight)
and unify everything that links to a note around it: in-prose wikilinks,
backlinks (margin / bottom / card), query results, timeline entries,
the tasks index. Reading any one of them, you learn the rest at once.

Title strip itself goes from text-primary-900 to a brighter
text-primary-700 so it sits in the same shade family as the chips it
anchors. External links inherit the color + weight but skip the
background — underline-on-hover plus the external glyph still keep
them distinct from internal note links.

TOC drops primary entirely (active item is bg-gray-100 / text-gray-900)
since TOC entries map to headings that render in plain text — primary
is reserved for "this links to another note".

backlink-context flyouts now divide adjacent contexts with a thin
top-rule and bump intra-context paragraph spacing from 0.35em → 0.5rem,
so multiple referencing snippets no longer read as one continuous block.

Drive-bys: standardize on "Linked from" across all three backlinks
templates (was "Links to this page" in the card), and fix the
no-sidebar branch (e.g. /neuron-layout) so backlinks show again — it
was rendering backlinks-bottom which is lg:hidden, so backlinks
disappeared entirely at desktop widths. Now uses the full-card
backlinks template since there's no right-panel to host the margin
variant.
External links no longer share the tracking-tight + font-semibold
"tiny title" weight that internal wikilinks use — they're just colored
prose with the existing underline-on-hover and external glyph. Reading
prose with several externals + internals in the same paragraph, the
internal ones now stand out as the destination affordance and external
ones recede appropriately.

Footnote popup picks up two grounding cues:

- A "Footnote N" header in the primary palette (`text-primary-600`,
  uppercase, tracking-wider) so the popup self-identifies — readers
  landing in a popup mid-paragraph see what kind of side material
  they're looking at.
- A `border-l-4 border-l-primary-500` rule along the left edge anchors
  the popup back to the in-prose `<sup>` ref's primary palette without
  needing a tail/arrow.

Typography also tightens to match the backlink-margin flyout
(`text-[0.85rem] leading-[1.55]`, `[&_p]:m-0 [&_p+p]:mt-2`) so footnote
bodies and backlink contexts feel like the same kind of "side
material" rather than two unrelated tooltip flavors.
Mona Sans replaces Space Grotesk for headings, sidebar, TOC, breadcrumbs,
and the new chip surfaces (right-panel + backlinks-margin/bottom). It's a
variable display+text sans with more character than Inter and less of the
"Tailwind starter" feel of Space Grotesk — GitHub uses it as their display
face. Self-hosted via the existing update.sh flow (kills the Google Fonts
runtime dependency).

The sans-font selector in styles.tpl was missing the new chip surfaces
(#right-panel, #backlinks-margin, #backlinks-bottom), so wikilink/backlink
chips inherited body serif while the title strip + TOC were sans — visible
inconsistency in the chip language. Added them so every "title of a note"
surface speaks the same UI font.

Mona Sans's x-height is taller than Space Grotesk's, so the sidebar
tree at the inherited 16px started feeling oversized. Pinning the
sidebar to text-sm (14px) brings it back in line with the new typeface
without affecting other surfaces.
…scale

Theme palette swaps blue → emerald — slate read as blue, and a
non-blue primary lets Emanote's UI step out of "AI-app default"
territory without straying from a calm, single-hue palette.

Note-link chips (wikilinks, backlinks-margin / -bottom / -card,
queries, timeline, tasks index, inline tags) drop one fg stop
(700 → 600) and gain ~30% transparency on the bg fill (50 → 50/70,
hover 100 → 100/80). With the emerald palette already lower
saturation than blue, the combination keeps chips as a visible
"linked note" affordance without dominating prose.

Tag pills (`emanote:inline-tag`) join the same chip language —
previously gray-100 + rounded-lg + shadow, now matching the
wikilink palette so all "linked note / tag" affordances read as
one family.

Heading scale tightens to a ~1.20 ratio:
  h1 4xl 36px (was 5xl 48px)
  h2 3xl 30px (was 4xl 36px)
  h3 2xl 24px (was 3xl 30px)
  h4 xl 20px (was 2xl 24px)
  h5 lg 18px (was xl 20px)
  h6 base 16px (was lg 18px)

All headings now share font-semibold (was font-bold for h1/h2 only —
the split had no semantic basis). h2's section-divider rule drops
from `border-b-2 border-gray-200` to `border-b border-gray-100
dark:border-gray-700` so it reads as a thin section break instead of
a hard rule competing with the heading itself. Margins tighten one
notch at each level (mt-12 → mt-10, mt-10 → mt-8, etc.) so section
breaks have ~4em of air rather than ~8em.

Letter-spacing -0.02em now scopes to h1-h4 only — at 18px and below
in Mona Sans the negative tracking made glyphs touch.

Container max-width bumps past Tailwind `container`'s 1536px cap to
100rem (1600px) at 2xl+ so ultra-wide viewports (1920+) don't waste
~200px of horizontal gutter on each side of the card.
Timeline (daily-note backlinks via `<ema:note:backlinks:daily>`)
previously rendered as a chip list inside the prose `<main>`, below
the article. With the right-panel established as the "side material"
hub (TOC + backlinks-margin), timeline belongs there too — and a
heatmap conveys the chronological texture better than a flat list.

`timeline.tpl` is now a thin section with two children: a hidden
`<ul id="timeline-data">` carrying one `<li data-url data-title>`
per daily backlink (the screen-reader / no-JS fallback), and a
`<div id="timeline-heatmap">` that the new
`_emanote-static/js/timeline-heatmap.js` paints into.

Per-year layout: 12 month rows × 31 cells. Cells are 4×4px (~5px
stride × 31 ≈ 155px) so the heatmap fits the right-panel's narrow
~150px content width at lg, with breathing room at xl. Filled cells
are anchors carrying the date + linked-note title via the native
`title` attribute; clicking navigates. Empty cells (and trailing
non-existent days for short months) are gray placeholders for grid
alignment. Years stack newest-first.

Dates are parsed from the linked-note title via a YYYY-MM-DD regex —
notes without a parseable date are silently skipped; if no entries
parse, the section hides itself rather than showing a bare
"Timeline" header. Re-renders on `@emanote/morph` so live-server
in-app navigation refreshes the cells without a hard reload.

Wires through `emanoteJsModuleNames` in JsBundle.hs (one-line
addition); cabal's `_emanote-static/js/*.js` glob picks up the new
file automatically.
Phase 2's heatmap was right-panel-only (lg+). Below that breakpoint
the right-panel is hidden, and pages with daily-note backlinks
showed nothing. Fix: render the heatmap a second time inside the
bottom-strip aside so narrow viewports see it too — same content,
two homes, one shows at lg+ and the other at <lg.

Implementation: timeline.tpl now uses classes (`.emanote-timeline`,
`.timeline-data`, `.timeline-heatmap`) instead of IDs so two
instances are valid HTML. timeline-heatmap.js paints each instance
independently via querySelectorAll. Each section's hidden-fallback
ul lives next to its own heatmap target — cleanly self-contained.

backlinks-bottom.tpl restructures to host both daily and non-daily
backlinks: the <aside> now renders unconditionally with a
[&:not(:has(*))]:hidden Tailwind guard, and the Heist splices for
:daily and :nodaily emit their content (or nothing) inside it.
On pages with neither kind of backlink, the aside has zero element
children and the guard hides the chrome — so no empty bar appears.
Cells in the heatmap previously only carried a native title attribute
("date — note title"), which is enough to identify the day but not
enough to read why this note was referenced from that daily note.

Each filled cell now hosts a CSS-only group-hover flyout — same
visual language as backlinks-margin's flyout — built from the
rendered backlink context already present inside `.timeline-data`'s
hidden <li>. Header line "YYYY-MM-DD — title" in the primary
palette, followed by the cloned context paragraphs below.

The outer wrapper carries pt-1.5 of transparent padding above the
visible card (positioned `top-full`), forming the same hover-bridge
trick used elsewhere — cursor traversing from the 4px cell down to
the card stays inside the flyout's bounding box, so group-hover
doesn't drop mid-traverse.

Native `title=` is kept as the keyboard / screen-reader fallback and
as a graceful degradation when the visual flyout clips off-screen.
Setting both `title` and the rich hover flyout meant the browser's
default gray tooltip layered on top of the styled card. Replace title
with aria-label so screen readers still announce the cell while the
visual UI is owned by the flyout.
@srid srid changed the title Attach panels to a single card; right-margin TOC + backlinks Default theme refresh: attached card, chip palette, heatmap timeline May 4, 2026
srid added 13 commits May 3, 2026 20:30
Footer fold-in
  Footer now renders as the last child inside #container instead of
  floating below it as a centered icon row. New shape: a thin text-link
  strip (Home / Index / Tags / Tasks left, Emanote attribution right)
  on bg-gray-50 with a top border, matching the sidebar / right-panel
  chrome. md:rounded-b-lg so the footer's tinted bg curves with the
  container's bottom corners. The previous icon row read as orphaned
  mobile bottom-nav and broke the "self-contained card" identity.

E2E selector updates
  The chip-language / panel-attachment refactor broke three e2e
  scenarios that were locating panels by their old IDs:
    - #timeline    → .emanote-timeline (class; renders in two homes)
    - #backlinks   → #backlinks-margin / #backlinks-bottom (#backlinks
                     kept as a fallback for the legacy card / error
                     layouts)
  panelLinksTo now searches across all matching panel selectors via
  querySelectorAll, so the test is structure-independent and won't
  break again the next time we add another panel home.

  The "every backlink context wrapper has overflow-y visible"
  regression guard (#542 follow-up) was anchored to '#backlinks li > div'
  — fragile, since context.tpl's wrapper now lives nested inside a
  flyout. context.tpl gets a stable class hook (.emanote-backlink-context),
  and the test queries by class — robust across whichever panel the
  context renders in.
The heatmap was rendering at the same compact 4×4px cell size in both
homes, leaving 70% empty space in the wide bottom strip while the
right-panel column was tight. Cell sizing is now context-aware: the
right-panel home (narrow column) keeps the fixed 4×4 squares;
the bottom-strip home gets flex-1 / min-w-[6px] / h-2.5 so cells
stretch as horizontal bars to fill the available row width.

Detection lives in render(): a section inside #backlinks-bottom is
"wide", anything else is "narrow". The cells container also picks up
flex-1 in the wide context so its children's flex-1 has a real width
to distribute.
The previous structure put #backlinks-bottom on the outer aside that
contained both the timeline and the regular backlinks. The e2e test
'the Backlinks panel does not link to "dailyhost/2025-01-01"' then
saw the timeline's daily-link anchors as if they were in the
backlinks panel, and failed.

Move the ID to the inner regular-backlinks <section>; the outer
aside carries .emanote-bottom-strip (chrome only). #backlinks-bottom
now refers specifically to the non-daily list, matching the
semantics of #backlinks-margin (right-panel) and #backlinks (legacy
card / error layout).

timeline-heatmap.js's wide-context detection updates accordingly:
.closest('.emanote-bottom-strip') instead of .closest('#backlinks-bottom').
…t; tables

Special pages (`/-/all`, `/-/tags`, `/-/tasks`) now render inside the
same rounded card as note pages — title strip on top with a
back-to-Home link in the corner (their only nav since there's no
sidebar), content in the white middle, footer attached at the
bottom. Drop the legacy `bg-gray-200/300` wrappers inside index.tpl,
tagindex.tpl, tasks.tpl that were leaving visible gray rectangles
inside the now-white card.

Tasks index (`/-/tasks`) restructures: heavy `border-2 shadow rounded`
boxes per task become a clean hover-tinted row list, breadcrumbs
move to a thin gray bar with `/` separator instead of the awkward
trailing `\` per crumb, and the note-title link adopts the wikilink
chip language.

In-prose task list alignment: `<li>:has(> svg.--ema-checkbox)` swaps
flex layout for absolute positioning of the checkbox in the marker
slot, so task text shares the same left edge as adjacent regular
bullets — mixed task/non-task lists now read as one column.

Tables: Pandoc emits `<table>` with no class, so style by element
under `main` (header bottom-rule, hover-tinted rows, comfy padding).

Drive-bys
  - `.agency/code-police.md`: new `emanote-tailwind-first` rule —
    prefer inline Tailwind utility classes over CSS in styles.tpl
    unless the rule needs `:has()` / `@keyframes` / vendor-class
    hooks Tailwind can't express.
  - List markers in `context.tpl` get `marker:text-gray-400` for
    consistency with the prose-side BulletList rule.
  - `docs/tips/js/{math,mermaid}.md` move out of `js/` (not specific
    to JS); `js/` folder removed; `_redirects` updated; orgmode.org
    link updated; `docs/architecture.md` deleted (out of date).
…e blocks

Cabal version bumps to 2.0.0.0 (still Unreleased). The default-theme
overhaul (#699) is a meaningful enough chrome rewrite that 2.0 is
warranted: every panel attaches inside one card, the chip language
unifies across wikilinks/backlinks/queries/timeline/tags, and the
timeline heatmap is a new visualization surface.

docs/guide/html-template/
  - `toc.md` rewritten to describe the right-panel home, the
    neutral-gray treatment (TOC entries map to plain-text headings),
    and the scroll-spy highlight.
  - New `backlinks.md` covers the daily/non-daily split, the
    chip+flyout language, and where each variant renders by viewport.
  - New `right-panel.md` documents the column itself: chrome that
    mirrors the sidebar, the three surfaces (TOC + timeline + linked
    from), and the <lg fallback to a bottom-attached strip.

Code blocks tighten:
  - `skylighting.css` `pre` rule moves from hardcoded #f8f8f8 / #1e1e1e
    to Tailwind design tokens (`var(--color-gray-50)` etc.) so the
    chrome tracks the rest of the palette and the dark-mode toggle.
  - `pandoc.tpl` `<CodeBlock>` wrapper drops `py-4 pr-4 rounded-lg
    overflow-x-auto` — the `<pre>` inside already owns its bg /
    border / rounded / padding / overflow. The previous double layer
    of chrome had asymmetric right-only padding that left a visible
    gap on the right but not the left.

Tables:
  - drop the `tbody tr:last-child { border-bottom: none }` override.
    Without it the table just trailed off; the consistent 1px
    border-bottom on every row gives the table a clear bottom edge.
…polish

Syntax highlighting (`skylighting.css`) moves from a hardcoded
GitHub-light circa 2018 palette to design-token colors that
coordinate with the rest of the theme:

  primary (theme accent — defaults to emerald, follows index.yaml's
           theme: setting):  Keyword, ControlFlow, Function, Import, BuiltIn
  slate    (cool, recedes):  String, VerbatimString
  orange   (warm distinct):  Number/Decimal/Float, Char, SpecialChar
  amber                   :  Preprocessor, Warning
  rose                    :  Constant, Error, Alert
  violet   (cool-purple)  :  DataType, Variable, Attribute (distinct
                             from values without competing with kw)
  gray italic             :  Comment, Documentation, Annotation,
                             Information

Dark-mode tokens use the lighter -300/-400 stops of each hue family
for legible contrast against the dark code-block background. The
emerald-themed default reads as one design surface; sites that
override `theme:` in index.yaml automatically get a matching
keyword color since `kw` keys off `--color-primary-700`.

Block-level prose images (an `<img>` that's the only content of its
parent paragraph) get a subtle frame in styles.tpl: 1px gray border,
rounded-md, centered with vertical breathing room. Inline images
(emoji, small icons) stay un-framed via the `:only-child` gate so
flowing text isn't disrupted.

Sidebar polish (4 wins):
  - Toggle icons swap from filled gray-700 circles to gray-400
    chevrons. The eye lands on titles, not toggles.
  - Active item picks up the wikilink chip palette (bg-primary-50/70
    + text-primary-600 + font-semibold + tracking-tight) so
    "you are here" reads as the same family as "this links to
    another note".
  - Header shows the site title (`page.siteTitle` from index.yaml)
    instead of the hardcoded "Home" string. The literal "Go to Home"
    moves into the link's title= tooltip.
  - leading-relaxed → leading-snug at text-sm packs ~15% more tree
    into the same viewport without crowding.

Backlinks-margin arrow flips: `→` (push) → `←` (pull). Backlinks are
incoming references — "this came from there" — so the arrow
points back at the listed source rather than away from it.

Query listings (default + timeline): drop the `flex-1` full-width
chip (each row was an oversized button). Result chips become
content-sized with a leading `→` arrow on the default variant or a
`tabular-nums` date column on the timeline variant; the whole row
gets a hover-tinted bg so the entire line reads as the click
target. Header collapses to small uppercase tracking-wider gray to
match the chrome label language.
…olved

Tailwind v4 only emits CSS variables for palettes whose classes
appear in scanned source files. The previous skylighting refresh
referenced `var(--color-slate-600)`, `--color-orange-700`, etc. —
palettes the templates don't otherwise use — so those tokens fell
through to inherit body text colour. The page looked far less
coloured than production.

Hardcode the non-primary palettes to Tailwind v4 default-palette
hex values; keywords / control flow / functions / imports keep
`var(--color-primary-*)` since the primary palette IS exposed
(it's referenced everywhere via the chip language). Site authors
who customize `theme:` still get a matching keyword colour;
strings / numbers / types / etc. stay on the curated coordinated
palette regardless of theme.
…chip

Each <li> in the bottom-strip Linked-from list previously stacked a
chip + a free-flowing context paragraph with only space-y-3
between rows — at small viewport widths the contexts ran together
and you couldn't tell which paragraph belonged to which chip.

Two changes:
  - divide-y between rows (1px gray, dark-mode-safe) so each entry
    is visually a separate block.
  - The context block gets ml-2 + pl-3 + border-l-2 — indented under
    its chip with a thin gray left rule, anchoring it to the chip
    above so the pair reads as one unit.
…ard too

components/backlinks.tpl is the variant used by the no-sidebar
layout (e.g. /neuron-layout) and error.tpl. It still wore the
pre-redesign card chrome (p-5 rounded-xl shadow-sm + bg-gray-50
inside the already-rounded parent card) and the same chip-then-
free-flowing-context pattern that produced the ball-of-mud on
narrow screens.

Brings it into line with backlinks-bottom.tpl: divide-y between
rows, context indented under its chip with a thin left rule,
outer chrome reduced to a top-rule + small heading so the
parent container's card chrome carries the visual weight.
Two visual-noise reductions in the sidebar:

  - Leaf nodes lost the file/document SVG icon (a 24px outlined
    document with content lines for active, plain outline for
    inactive). Replaced with a 4px gray dot centered in the same
    icon column, so leaves still indent past the toggle column
    but don't compete with the title for the eye. Folders keep
    their chevron — the chevron's presence/absence already
    disambiguates folder vs leaf without needing a leaf glyph.

  - Folgezettel depth rails (1px gray-200 left border on each
    nested .pl-2 .pl-2) removed. Indent alone is enough to convey
    hierarchy, and the user flagged the rails as visible noise.
    Comment in styles.tpl notes the previous treatment in case
    deep trees become hard to follow without orientation guides.

The two header icons (search, dark-mode-toggle) are still
right-aligned via justify-between on the parent flex; the visual
gap to the sidebar's right edge is just the inner div's px-4
padding (matches the rest of the layout).
heist-extra's Pandoc table renderer (Heist.Extra.Splices.Pandoc.Render)
hardcodes `border-r-2 border-l-2 border-gray-300` on every <th>/<td>
and `border-b-2 border-t-2 border-gray-300` on every <tr>, producing
a spreadsheet look with vertical cell rules. (Upstream TODO note in
that file: 'Move tailwind styles to pandoc.tpl'.)

Override in styles.tpl with element-selector + !important rules — left
and right cell borders zeroed, row top borders zeroed — so only the
thead bottom-rule and the per-row 1px bottom separator we set above
remain. Editorial GitHub-style table.
srid added 4 commits May 4, 2026 08:52
The page-footer tag list (rendered by ema:each-tag) was the last
tag surface still on the pre-redesign chrome: gray-100 pill +
gray-700 text + tiny px-1 + plain rounded. Other tag affordances
already moved to the chip language:

  - inline tags in prose (emanote:inline-tag rewriteClass) →
    bg-primary-50/70 + text-primary-600 + font-semibold tracking-tight
  - wikilinks / backlinks / queries / timeline → same palette

Bring metadata tags into the same family: bg-primary-50/70 +
text-primary-600 + px-1.5 py-0.5 rounded-sm + hover bg shift.
font-mono stays (the slashed-path identifier shape reads better in
mono than sans), so metadata tags read as "tag identifier" rather
than "tag link in prose" — same palette, distinct typography.

Spacing tightens: gap-x-2 / gap-y-1.5 instead of space-x-2 + space-y-2
(margin collapses are easier to predict), and mt-8 above the strip
gives breathing room between prose and tags.
Checkbox SVG (checked + unchecked) tightens visually:
  - h-4 w-4 fixed → h-[0.9em] w-[0.9em] (scales with surrounding
    text instead of being a fixed 16px square)
  - stroke-width 3 → 2 on the box outline (refined, not blocky)
  - stroke-width 3.5 → 3 on the check path
  - rx 3 → 4 (slightly softer corners, matches rounded-sm idiom)
  - mt-1 → align-[-0.1em] (sits on the text baseline rather than
    pushed-down a fixed amount)
  - text-gray-500 → text-gray-400 (less heavy outline)

The styles.tpl marker-slot absolute-positioning rule for in-prose
task lists now scopes to `main ul.list-disc li:has(...)` instead of
any `main li:has(...)`. Previously the rule fired on the special
tasks index page (`/-/tasks`) too, where the <ul> isn't a prose
list-disc — happened to work by accident because <main>'s px-6
padding equalled the rule's -1.5rem offset, but with the lighter
em-sized checkbox the alignment broke. Scoping fixes the special
page without affecting prose task lists.

Drive-by: docs/tips/js.md removed (the js/ folder children moved
out earlier; this stub is no longer needed).
srid added 4 commits May 4, 2026 10:25
fonts.md
  - Space Grotesk → Mona Sans in the "ships with" list (the actual
    chrome typeface in default/styles.tpl); link to github/mona-sans.
  - Drop "TOC accents" from the theme-colour callout — TOC dropped the
    primary palette entirely as part of #699; entries map to plain
    headings, not other notes, so they sit in neutral gray.
  - Reframe the theme-colour line as "drives the unified chip
    language" with cross-references to the chip surfaces (page title,
    wikilinks, backlinks, queries, timeline heatmap, tags).

daily-notes.md
  - "rendered as a 'timeline' in reverse chronological order" →
    year-stacked timeline heatmap, with a link to the backlinks doc's
    timeline section.
  - "Links to this page" panel → "Linked from" chip list (the panel
    label changed in the no-sidebar template; we want the docs to
    match what the user sees).
  - Demo paragraph names where each surface lives (right-panel at lg+,
    bottom strip at narrower widths) so the example is self-locating.
…nner

/-/all (Index): every entry now reads as a "linked note" chip — the
sidebar-tree template gets parameterized via a new tree-link-rest-class
bind that the caller supplies, so:
  - sidebar.tpl keeps its original hover-only rest state (chip palette
    is reserved for the active item)
  - special/index.tpl supplies the full chip palette so every leaf
    on /-/all looks like a wikilink chip
The <node:active> shadow inside sidebar-tree.tpl never fires on
/-/all (no current note), so the chip rest class becomes the universal
link style for that page.

/-/tags (Tag Index): rewritten to mirror the established chip
language end-to-end:
  - tagcrumbs render as #tag chips with / separators (matching the
    tasks.tpl breadcrumb pattern); the trailing crumb gets a slightly
    stronger fill so "you are here" stands out without breaking the
    family
  - subtags render as font-mono #tag chips + (N+M) count
  - notes-tagged-here render as the wikilink chip with a leading →
    arrow (same shape as query-default.tpl)
Header sections use the same text-xs uppercase tracking-wider micro-
heading as queries / right-panel sections.

text-l → text-lg in pandoc.tpl's <DefinitionList> <dt>: text-l is
not a valid Tailwind class, so DT terms have been rendering at the
default text size since v1. Definitively a typo for text-lg.

text-l → text-sm in index.yaml's emanote:error:yaml rewriteClass:
same typo. The error banner is font-mono, so text-sm reads better
than text-lg here.
Chip rest state on /-/all becomes text-color + bold only; the bg-tint
surfaces on hover. The earlier "full chip" rest state stretched a
solid primary-50 fill across every row (the link inherits flex-1
from sidebar-tree.tpl), painting the special-page card a wall of
blue.

The new rest class still reads as the wikilink chip family — same
text-primary-600, same font-semibold tracking-tight — just without
the resting bg fill. Keeps the "every entry is a linked note"
semantic intact while letting the white card breathe.
@srid srid marked this pull request as ready for review May 4, 2026 14:58
@srid srid merged commit 8a95c41 into master May 4, 2026
3 checks passed
@srid srid deleted the noble-cup branch May 4, 2026 15:01
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.

1 participant