Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
101 lines
6.2 KiB
Markdown
101 lines
6.2 KiB
Markdown
# Step 2 — Tailwind v4 design system, theme toggle, reader pages
|
|
|
|
Worktree: `/home/thallada/workspace/the-daily-epub-v2b` (branch `v2-frontend`).
|
|
Read `00-shared.md` first, especially the design language — it is the spec.
|
|
You own the design system and the **reader-facing** templates. Do not restyle
|
|
`src/web/templates/dashboard/*` (step 3 does that, on top of your system); they
|
|
may look plain until then, which is expected. Do not implement the sidebar
|
|
table of contents (step 4).
|
|
|
|
## 1. Tailwind toolchain (no Node at build or run time)
|
|
|
|
- `package.json` at the repo root with `devDependencies`
|
|
`tailwindcss` and `@tailwindcss/cli` pinned to `4.3.3`, scripts
|
|
`css` (`tailwindcss -i src/web/tailwind.css -o src/web/static/app.css --minify`),
|
|
`css:watch`, and `css:check` (build to a temp file and `diff` against the
|
|
committed `app.css`, non-zero on drift). `package-lock.json` committed;
|
|
`node_modules/` and `dev/` added to `.gitignore`.
|
|
- The compiled `src/web/static/app.css` is **committed** and embedded with
|
|
`include_str!` exactly as today, so `cargo build` and the server never need
|
|
Node. Document the workflow (edit templates/`tailwind.css` → `npm run css` →
|
|
commit both) in `README.md` and in `docs/runbooks/web-dashboard-rollout.md`.
|
|
- Fonts: `src/web/static/fonts/Newsreader.woff2`, `Newsreader-italic.woff2` and
|
|
`OFL.txt` are already in your worktree. Serve them from
|
|
`web::static_asset` (`include_bytes!`, `font/woff2`, same ETag/304 logic,
|
|
long cache) — extend the route so `/static/fonts/Newsreader.woff2` or
|
|
`/static/Newsreader.woff2` works (pick one, keep `safe` matching: a fixed
|
|
match arm per file, no path joins). `@font-face` in `tailwind.css` with
|
|
`font-weight: 200 800; font-stretch: normal; font-display: swap;` and the
|
|
`unicode-range` for latin from Google's CSS
|
|
(`U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD`).
|
|
Add a `<link rel="preload" as="font" type="font/woff2" crossorigin>` for the
|
|
regular face in the layout.
|
|
|
|
## 2. Theme toggle (item 2)
|
|
|
|
- `src/web/static/theme.js` (new, served like `app.js`, `include_str!`): a
|
|
handful of lines run **synchronously in `<head>`** before paint: read
|
|
`localStorage.theme` (`"light"`/`"dark"`, anything else = system), set
|
|
`document.documentElement.dataset.theme` accordingly (remove the attribute for
|
|
system). Wrap storage access in try/catch. No inline script (CSP).
|
|
- The toggle button in the layout's "ears" row: `<button type="button"
|
|
data-theme-toggle aria-label="Theme: system">` with an inline SVG icon (sun /
|
|
moon / auto) and a short visible label. `app.js` handles clicks: cycle
|
|
system → light → dark → system, persist (`removeItem` for system), update the
|
|
attribute, icon and label, and re-evaluate on `matchMedia("(prefers-color-scheme: dark)")`
|
|
changes while in system mode. The three icons can all be in the markup with
|
|
`hidden` toggled — remember `[hidden]` must win over any `display` utility.
|
|
- Everything themed goes through the semantic tokens; `color-scheme` follows
|
|
the theme so native controls and scrollbars match. Check both themes in
|
|
screenshots: contrast of muted text on paper, accent on dark paper, rating
|
|
buttons, flash, focus rings, images in dark mode.
|
|
|
|
## 3. Design system + layout
|
|
|
|
`src/web/tailwind.css` per the shared brief (tokens, dark variant, fonts,
|
|
`@layer base` for body/typography defaults, `@layer components` for `.btn`,
|
|
`.btn-primary`, `.badge` + states (`selected loved good down excluded
|
|
shortlisted admitted eligible cleared assessed triaged reason running requested
|
|
ok degraded failed dry_run`), `.kv`, `.scroll-x`, `.prose-body`, base tables
|
|
and form controls — step 3 will consume these). Rewrite `layout.html`: ears
|
|
row, masthead, primary nav with `aria-current="page"` derived from
|
|
`page.active_nav` (check what values handlers set), the admin nav row, flash,
|
|
`<main>`, footer, scripts. Add an accessible "skip to content" link.
|
|
|
|
## 4. Reader templates (restyle in place; keep every handler field)
|
|
|
|
`issue_public.html`, `issue_full.html`, `issue_list.html`, `article.html`,
|
|
`world.html`, `behind.html`, `login.html`, `account.html`, `error.html`,
|
|
`_rating_widget.html`. The public and full issue pages are the showcase — set
|
|
them like a front page: dateline, stats line, The Brief with its drop cap,
|
|
download buttons, sections as tracked labels on rules, entries per the spec,
|
|
lead story larger, the chapter links (World Briefing · Behind the paper) as a
|
|
centred rule-bounded row, the colophon as a two-column `.kv` in small sans.
|
|
Note: the backend step adds `summary`/`why` to the public entries and it will
|
|
merge after you; leave a clear place for them in `issue_public.html` (the same
|
|
summary/why treatment as the full issue) guarded with `{% if let Some(...) %}`
|
|
**only if the field exists in your tree** — otherwise just leave a comment
|
|
`{# step 1 adds summary/why here #}` so the merge is trivial.
|
|
|
|
Article page: h1 → source link, byline/meta, "Why it's here" callout, social
|
|
line, the summary as standfirst, the body in `.prose-body`, discussion with
|
|
nested comment styling (`blockquote.reply` indent, `.comment-meta` small
|
|
sans), rating control, "Read online ↗" button, prev/next cards, back link.
|
|
Login/account: a narrow centred card-free form, labelled inputs, primary button.
|
|
Archive: months as tracked labels, issues as a hairline list with date, number,
|
|
article count.
|
|
|
|
Phones (390px): masthead scales down, nav wraps as a centred row, everything
|
|
single-column, tap targets ≥ 40px, tables scroll inside `.scroll-x`.
|
|
|
|
## 5. Verify
|
|
|
|
Run the seeded dev server (shared brief), screenshot `/`, `/issues`,
|
|
`/issues/2026-09-02`, `/issues/2026-09-02/articles/1`, `/issues/2026-09-02/world`,
|
|
`/issues/2026-09-02/behind`, `/login`, `/account` as anonymous, `reader` and
|
|
`admin`, in light and dark, desktop and mobile, and **look at them**. Fix what
|
|
looks off. Make sure `npm run css:check` passes on the committed CSS, `cargo
|
|
fmt`, clippy `-D warnings` and `cargo test` are green (minus the sandbox-only
|
|
failures), then write `handoff-step2.md` and commit once on `v2-frontend`:
|
|
`Web dashboard v2 step 2: Tailwind design system, theme toggle, reader pages`.
|