@fluid-theme/core 0.4.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/AGENTS.md +6 -29
  2. package/CHANGELOG.md +564 -2
  3. package/CLAUDE.md +201 -14
  4. package/GEMINI.md +6 -45
  5. package/README.md +17 -8
  6. package/assets/js/compare.js +77 -0
  7. package/assets/js/fit-text.js +103 -0
  8. package/assets/js/lightbox.js +153 -0
  9. package/assets/js/scroll-reveal.js +72 -0
  10. package/assets/js/toc-build.mjs +86 -0
  11. package/assets/js/toc.js +384 -0
  12. package/dist/fluid-1x.min.css +4 -0
  13. package/dist/fluid-fonts-archivo.min.css +2 -0
  14. package/dist/fluid-fonts-bodoni.min.css +2 -0
  15. package/dist/fluid-fonts.min.css +2 -0
  16. package/dist/fluid-legacy-1x.css +3 -0
  17. package/dist/fluid-nav.min.css +3 -0
  18. package/dist/fluid-toc.min.css +3 -0
  19. package/dist/fluid-zero.min.css +3 -0
  20. package/dist/fluid.min.css +3 -2
  21. package/dist/fonts/OFL-archivo.txt +93 -0
  22. package/dist/fonts/OFL-bodoni-moda.txt +93 -0
  23. package/dist/fonts/OFL-hanken-grotesk.txt +93 -0
  24. package/dist/fonts/OFL-newsreader.txt +93 -0
  25. package/dist/fonts/OFL-schibsted-grotesk.txt +93 -0
  26. package/dist/fonts/archivo-latin-wdth-italic.woff2 +0 -0
  27. package/dist/fonts/archivo-latin-wdth-normal.woff2 +0 -0
  28. package/dist/fonts/bodoni-moda-latin-opsz-italic.woff2 +0 -0
  29. package/dist/fonts/bodoni-moda-latin-opsz-normal.woff2 +0 -0
  30. package/dist/fonts/hanken-grotesk-latin-wght-italic.woff2 +0 -0
  31. package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  32. package/dist/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  33. package/dist/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  34. package/dist/fonts/schibsted-grotesk-latin-wght-italic.woff2 +0 -0
  35. package/dist/fonts/schibsted-grotesk-latin-wght-normal.woff2 +0 -0
  36. package/fluid-agent-kit/README.md +1 -0
  37. package/fluid-agent-kit/skills/accessibility.md +3 -1
  38. package/fluid-agent-kit/skills/css-conventions.md +22 -5
  39. package/fluid-agent-kit/skills/image-optimization.md +2 -0
  40. package/fluid-agent-kit/skills/preset-composition.md +108 -0
  41. package/package.json +36 -11
  42. package/src/animations/scroll-reveal.css +75 -193
  43. package/src/components/accordion.css +6 -47
  44. package/src/components/alert.css +47 -88
  45. package/src/components/avatar.css +4 -20
  46. package/src/components/badge.css +25 -48
  47. package/src/components/button.css +69 -175
  48. package/src/components/card.css +146 -481
  49. package/src/components/chip.css +46 -12
  50. package/src/components/compare.css +234 -0
  51. package/src/components/dropdown.css +10 -67
  52. package/src/components/empty-state.css +4 -0
  53. package/src/components/form.css +113 -81
  54. package/src/components/gallery.css +252 -0
  55. package/src/components/hero.css +203 -88
  56. package/src/components/indicator.css +23 -117
  57. package/src/components/loading.css +7 -29
  58. package/src/components/modal.css +12 -65
  59. package/src/components/nav-modes.css +261 -0
  60. package/src/components/nav.css +324 -392
  61. package/src/components/search-input.css +4 -2
  62. package/src/components/step-indicator.css +26 -27
  63. package/src/components/table.css +64 -171
  64. package/src/components/toc.css +254 -0
  65. package/src/components/tooltip.css +5 -46
  66. package/src/core/base.css +393 -547
  67. package/src/core/reset.css +17 -5
  68. package/src/core/tokens.css +426 -2276
  69. package/src/fluid.css +258 -17
  70. package/src/fonts/OFL-newsreader.txt +93 -0
  71. package/src/fonts/OFL-schibsted-grotesk.txt +93 -0
  72. package/src/fonts/fluid-fonts.css +55 -0
  73. package/src/fonts/newsreader-latin-opsz-italic.woff2 +0 -0
  74. package/src/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  75. package/src/fonts/schibsted-grotesk-latin-wght-italic.woff2 +0 -0
  76. package/src/fonts/schibsted-grotesk-latin-wght-normal.woff2 +0 -0
  77. package/src/layouts/positioning.css +6 -3
  78. package/src/legacy-1x.css +61 -0
  79. package/src/sections/cta.css +33 -101
  80. package/src/sections/features.css +42 -219
  81. package/src/sections/footer.css +53 -134
  82. package/src/sections/stats.css +40 -153
  83. package/src/sections/team.css +24 -113
  84. package/src/sections/testimonials.css +34 -148
  85. package/src/zero/base.css +166 -0
  86. package/src/zero/tokens.css +64 -0
  87. package/src/zero.css +20 -0
  88. package/starters/workflows/README.md +40 -0
  89. package/starters/workflows/dependabot.yml +21 -0
  90. package/starters/workflows/update-fluid-theme.yml +296 -0
  91. package/themes/fonts/OFL-archivo.txt +93 -0
  92. package/themes/fonts/OFL-bodoni-moda.txt +93 -0
  93. package/themes/fonts/OFL-hanken-grotesk.txt +93 -0
  94. package/themes/fonts/archivo-latin-wdth-italic.woff2 +0 -0
  95. package/themes/fonts/archivo-latin-wdth-normal.woff2 +0 -0
  96. package/themes/fonts/archivo.css +20 -0
  97. package/themes/fonts/bodoni-moda-latin-opsz-italic.woff2 +0 -0
  98. package/themes/fonts/bodoni-moda-latin-opsz-normal.woff2 +0 -0
  99. package/themes/fonts/bodoni.css +35 -0
  100. package/themes/fonts/hanken-grotesk-latin-wght-italic.woff2 +0 -0
  101. package/themes/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  102. package/themes/gallery.css +105 -0
  103. package/themes/swiss.css +53 -0
  104. package/src/components/scroll-progress.css +0 -104
  105. package/src/components/settings-panel.css +0 -603
  106. package/src/components/themed-images.css +0 -229
  107. package/src/components/version-badge.css +0 -70
package/CLAUDE.md CHANGED
@@ -2,16 +2,136 @@
2
2
 
3
3
  > **Fluid Framework** — A modern, semantic CSS design system built on physics principles, not utility classes.
4
4
 
5
+ ## How we build (read first)
6
+ The rules for every project are in `Agile-Media-Agency/the-fluid-stack/playbook/`
7
+ (start at `00-new-site-checklist.md`; `README.md` is the index; `design-and-ux.md`
8
+ is the page that points back here for design detail). This file holds only what is
9
+ specific to the theme and dated exceptions to the playbook. When you fix or add
10
+ something other sites could use, change the playbook in the same PR, add a row to
11
+ `playbook/FEATURES.md`, and open a `playbook-sync` issue on each sibling repo.
12
+ Project facts (domain, hosting, npm, status) live in
13
+ `neilswhite/neil-projects-registry/PROJECTS.md`, not here.
14
+
15
+ ## History was rewritten on 2026-10-27 (read before any push)
16
+
17
+ Every branch and tag was rewritten to purge client student reports (Neil's
18
+ call; audit FL-01, PR #76). Any clone, worktree or branch made before
19
+ 2026-10-27 08:50 UTC still carries them: **re-clone; never merge, rebase onto
20
+ or push anything from an old clone.** Old SHAs differ from the new ones (old
21
+ `main` 592f599 is new 893eb84). The repo stays private for the same reason.
22
+
23
+ **Exception, 2026-10-28: an old clone pushed the purged history back by
24
+ itself.** The MacBook Pro's `~/Projects/the-fluid-theme` predates the rewrite,
25
+ and its scheduled `gitsync.sh` (projects-tools, D-22) saved its 308 "unpushed"
26
+ old commits as branch `checkpoint/macbook-pro`, which put the reports commit
27
+ d3a678f back on a branch. The branch was deleted the same night (0 unique
28
+ files). So: a `checkpoint/<mac>` branch whose history differs from `main` is
29
+ an old clone, not work to rescue; delete it and re-clone on that Mac. gitsync
30
+ now refuses to checkpoint such a clone (projects-tools PR #4,
31
+ `purged-commits.txt`). Until every Mac has re-cloned, check
32
+ `gh api repos/Agile-Media-Agency/the-fluid-theme/branches --jq '.[].name'`
33
+ for a returning `checkpoint/*` branch.
34
+
35
+ ## Exceptions (dated; read before the next build)
36
+
37
+ - **2026-10-28: the MacBook Pro stays on gitsync's hold list until it
38
+ re-clones.** projects-tools `gitsync-hold.txt` (projects-tools #3) has
39
+ `the-fluid-theme macbook-pro`; the re-clone is registry
40
+ `todo/T102-macbook-reclone-fluid-theme.md`. Any branch whose merge-base with
41
+ `main` is `bd1cabc` ("Initial project structure", the second commit) is
42
+ pre-rewrite: do not merge or check it out.
43
+ - **2026-10-29: a clone that only fetched a re-uploaded branch still holds
44
+ the reports after the branch is gone.** The Mac Studio's
45
+ `~/Projects/the-fluid-theme` fetched `checkpoint/macbook-pro`; gitsync's
46
+ `fetch --prune` drops the ref, not the objects, so `git cat-file -t
47
+ d3a678f` still answers `commit` until a gc. Clean it with `git update-ref
48
+ -d refs/remotes/origin/checkpoint/<mac>`, then `git gc --prune=now`, and
49
+ check `cat-file` fails; never `git reflog expire --all`, which also wipes
50
+ every linked worktree's reflogs. Pending on the Mac Studio (registry
51
+ `todo/T102-macbook-reclone-fluid-theme.md`, Mac Studio section).
52
+ - **2026-10-28: npm `engines` values must be strings, or Dependabot aborts.**
53
+ `"engines": {"browsers": [...]}` (an array) made every Dependabot run fail with
54
+ "engines.browsers must be a string" from Oct 8 to Oct 24, so 16 alerts got no
55
+ fix PRs. Browser targets live in `browserslist` (informational) and, for the
56
+ build, in the `targets` in `scripts/build-css.mjs`. Never add `engines.browsers`.
57
+ - **2026-10-29: Playwright's Firefox does not launch on the Mac Studio.**
58
+ Firefox 157 (playwright firefox v1555, macOS 27) exits with "Could not find
59
+ profile folder" for any profile path, sandboxed or not; WebKit and Chromium
60
+ run. Cross-browser checks there are Chromium plus WebKit
61
+ (`BROWSER=webkit node scripts/nav-check.mjs`); run Firefox in a cloud session
62
+ (`/opt/pw-browsers`) or after a Playwright update, and say which ran.
63
+ - **2026-10-29: Safari picks a local font's cut by weight, not by name.**
64
+ WebKit treats `local("Arial")` as a family and returns Arial Bold for a
65
+ requested 530 (the inverse bar's 500 + 30), even inside a `font-weight: 100
66
+ 599` face. The fallback faces list the PostScript name first (`ArialMT`).
67
+ Georgia has no unique regular name, so the nav brand and links show in
68
+ Georgia Bold in Safari until Newsreader loads. Check fallback changes in
69
+ WebKit with the woff2 requests blocked.
70
+ - **2026-10-29: lightningcss writes `border-image: none` as `border-image:`.**
71
+ The minified shorthand is empty, so browsers drop it and the old border
72
+ image stays (found when the forced-colours reset of the full-bleed nav did
73
+ nothing). Reset a border image with `border-image-source: none`, and grep
74
+ `dist/` for `:}` or `:;` after adding a `none` to a shorthand.
75
+ - **2026-10-29: main deploys once per merge, not on both the push and Release.**
76
+ `site.yml` runs on `push: main` and again when Release finishes (so the footer
77
+ shows the released version); deploying on both uploaded main twice a minute
78
+ apart (14 pairs in October, e.g. runs 40812532042 and 40812587873), with Actions
79
+ minutes near the org's monthly cap. Its `release-check` job now skips the push
80
+ deploy whenever Release will act on the push (a changed file outside Release's
81
+ `paths-ignore`, and no `chore: release` in the subject). **Change Release's
82
+ `paths-ignore` or its `if:` and you must change `release-check` to match**, or
83
+ a merge either deploys twice or not at all. The Release-triggered run deploys
84
+ on any conclusion except `skipped`, so a failed Release still puts the merge
85
+ live with the old version in the footer. Known gap: if Release never starts
86
+ for a push the gate expected it to take, nothing deploys that merge; run
87
+ `site` by hand (workflow_dispatch) or push again.
88
+
89
+ - **2026-10-30: Firefox runs in a cloud session from the Playwright Docker
90
+ image, not the CDN.** `npx playwright install firefox` gets a 403 there
91
+ (cdn.playwright.dev is not allowed), but mcr.microsoft.com is: the
92
+ `playwright:v<version>-noble` amd64 image's `install firefox` layer is
93
+ `ms-playwright/firefox-<build>` with its own NSS. Pull that layer's blob
94
+ with curl from `/v2/playwright/blobs/<digest>` (manifest, then config
95
+ history to find the layer), untar it, and link the folder into
96
+ `/opt/pw-browsers`. The gates found one Firefox-only defect this way
97
+ (gallery, `_docs/plans/phone-nav-bottom.md` §10).
98
+ - **2026-10-30: gzip sizes differ by platform.** The same `fluid.min.css`
99
+ bytes measure 24,413 B gzip on Linux (Node 22 and 24, the CI runners) and
100
+ 24,395 B on the Mac Studio, because Node's bundled zlib differs. Leave
101
+ about 25 B under the core budget when you measure on the Mac, or
102
+ `prepublishOnly` (`npm run build` in Release) fails on CI.
103
+
104
+ - **2026-10-30: `[skip ci]` anywhere in a merge commit skips Release.**
105
+ GitHub skips every push workflow when the message holds it, title or
106
+ body, and a squash merge copies the branch commits' messages in. So #102
107
+ (Base v2) and #104 reached `main` unreleased, and Release has no manual
108
+ trigger. While Actions minutes are tight, put `[skip ci]` in branch
109
+ commits only if you will clear it from the merge box; the PR that must
110
+ release (`major` label, a file outside Release's `paths-ignore`) carries
111
+ none, in its title or its commits. Because #102 never ran CI on `main`,
112
+ its new `assets/js/toc-build.mjs` and font `OFL-*.txt` licences first met
113
+ `scripts/site-guard.sh` in #105's site build, which failed on the file
114
+ types; the guard now allows `*.mjs` and `OFL*.txt`.
115
+ - **2026-10-30: long-lived branches can reintroduce `engines.browsers`.**
116
+ `base-v2-strong` branched before #90 dropped `engines.browsers` from
117
+ `package.json`, so merging #102 brought the array back and silently broke
118
+ Dependabot again until removed in the v2 finish PR. Check `package.json` for
119
+ `engines.browsers` whenever merging a branch cut before 2026-10-29.
120
+
5
121
  ## Before Doing Anything
6
122
 
7
- 1. Read `fluid-agent-kit/skills/` — 4 skill files covering CSS conventions, UX rules, accessibility, and image optimization
8
- 2. Read `_docs/DEVELOPMENT.md` — master development guide
123
+ 0. Read `_docs/STATUS.md` — **what the theme is now: one lean base + per-site child themes, the knobs, the component list, what is superseded.** It wins over every older doc, including the rest of this file.
124
+ 1. Read `_docs/HANDOFF.md` — open blockers and working agreements (its history predates Pass 0).
125
+ 2. Read `fluid-agent-kit/skills/` — CSS conventions, UX rules, accessibility, image optimization (`preset-composition.md` is superseded)
9
126
  3. Read `_docs/UX-BEHAVIOR-SPEC.md` — 18-section behavioral specification (skim first, deep-read relevant sections)
10
127
 
11
128
  ## Quick Reference
12
129
 
13
130
  ```bash
14
- npm run serve # Open examples in browser at localhost:8080
131
+ npm run serve # Open examples in browser at localhost:8080
132
+ npm run build # Bundle + minify → dist/fluid.min.css
133
+ node scripts/visual-scan.mjs # Layout-defect scan: showcase sites, catalogue, consumers fixture (exit 1 = flags)
134
+ node scripts/axe-check.mjs # axe WCAG 2.1 A/AA, light/dark/OS-dark, 1280/390 (exit 1 = violations)
15
135
  ```
16
136
 
17
137
  ## What This Is
@@ -19,9 +139,9 @@ npm run serve # Open examples in browser at localhost:8080
19
139
  A CSS framework that proves modern CSS can replace the utility-class approach of Tailwind/Bootstrap:
20
140
 
21
141
  - **Semantic HTML** over `<div class="flex flex-col p-4">` soup
22
- - **CSS Custom Properties** for runtime theming (no rebuild needed)
23
- - **Complete presets** that change typography, colors, motion, AND layout
24
- - **Dark mode uses deep palette colors**, not just black
142
+ - **CSS Custom Properties** for runtime theming (no rebuild needed): about 20 knobs on `:root` (`_docs/STATUS.md`)
143
+ - **One base, a child theme per site**: presets and palettes were removed in Pass 0 (2026-10-23)
144
+ - **Dark mode is `light-dark()`**: one declaration per colour role, the OS decides, `<html data-theme>` overrides
25
145
  - **No build step** — link one CSS file and go
26
146
 
27
147
  ## Core Principles
@@ -31,8 +151,61 @@ A CSS framework that proves modern CSS can replace the utility-class approach of
31
151
  3. **Logical Properties** — `margin-block`, `padding-inline` (not `-top`, `-left`)
32
152
  4. **No Build Step** — Works directly in browsers
33
153
  5. **Progressive Enhancement** — Works without JavaScript
34
- 6. **Presets are complete experiences** — Not just color swaps
154
+ 6. **Knobs, not variations** — a site changes the knobs; components read them. No attribute-keyed skins
35
155
  7. **Accessibility is non-negotiable** — Connected to disabled and autism communities
156
+ 8. **Upstream-first** — see below; this one governs how every other fix gets made
157
+
158
+ ## Upstream-First (the rule that governs fixes)
159
+
160
+ The sites in `showcase/` are real sites **and** the framework's test beds. Building
161
+ them is how framework bugs surface.
162
+
163
+ > **Would any other Fluid site hit this? Then it is a theme fix, not a site fix.**
164
+ > Decision test: *delete the site tomorrow — does the lesson survive?* If yes, it
165
+ > belongs in `src/`.
166
+
167
+ Spacing bugs, missing defaults, broken interactions, a11y gaps, overflow cases, and
168
+ genuinely reusable utilities all go into `src/` (or `assets/js/`) — never patched
169
+ into a single site and left there. Content, brand voice, and page-specific
170
+ composition stay in the site. A site-level patch is acceptable only as a
171
+ documented stopgap with the framework work filed.
172
+
173
+ Full rule, decision table, and the running list of what these sites have already
174
+ sent upstream: **`_docs/SHOWCASE-SITES.md`**.
175
+
176
+ After any theme change, rebuild and re-vendor into the sites:
177
+
178
+ ```bash
179
+ npm run build && for s in urbictionary printsofthemasters shirtsandsquirrels; do
180
+ cp dist/fluid.min.css showcase/$s/assets/; done
181
+ ```
182
+ - **2026-10-29: never `curl … | grep -q` in `site.yml`.** Its steps run with
183
+ `shell: bash`, which is `-e -o pipefail`: `grep -q` exits on the first match,
184
+ curl gets "(23) Failure writing output", and the pipeline fails although the
185
+ page is right. The first `/status/` smoke test failed this way (run
186
+ 40817625356) while https://thefluidtheme.com/status/ showed the commit. Read
187
+ the page into a variable, then match it.
188
+
189
+ ## Verification
190
+
191
+ Screenshots are for judging design; **assertions are for catching defects.** A
192
+ purely visual review twice missed a button touching the section below it, in
193
+ frames where it was plainly visible. Before shipping site changes:
194
+
195
+ ```bash
196
+ npm run build
197
+ node scripts/visual-scan.mjs # 0 flags
198
+ node scripts/visual-scan.mjs scripts/fixtures/visual-scan-selftest.html # must exit 1
199
+ node scripts/reveal-check.mjs # 0 failures
200
+ node scripts/form-check.mjs # 0 failures
201
+ node scripts/axe-check.mjs # 0 violations
202
+ node scripts/focus-check.mjs # 0 failures (focus rings 3:1)
203
+ npm run test:zero # Fluid Zero fixture (3 gates)
204
+ node scripts/toc-check.mjs # TOC module: 0 failures
205
+ ```
206
+
207
+ The second command proves the detector still detects. A scanner that cannot fail
208
+ is worse than no scanner.
36
209
 
37
210
  ## Layer Order
38
211
 
@@ -45,11 +218,18 @@ A CSS framework that proves modern CSS can replace the utility-class approach of
45
218
  | File | Purpose |
46
219
  |------|---------|
47
220
  | `src/fluid.css` | Main entry point (imports everything) |
48
- | `src/core/tokens.css` | Design tokens, palettes, presets |
49
- | `src/components/` | 23 component CSS files |
221
+ | `_docs/STATUS.md` | **What the theme is now, the knobs, what is superseded** |
222
+ | `src/core/tokens.css` | The knobs and design tokens (one `:root` block) |
223
+ | `src/components/` | Component CSS files (plus opt-in `toc.css`, `nav-modes.css`) |
50
224
  | `src/sections/` | 6 section CSS files |
51
- | `templates/` | 18 HTML templates |
225
+ | `themes/` | Official child themes (`swiss.css`, `gallery.css`) and their self-hosted variable fonts (`themes/fonts/`) |
226
+ | `starters/workflows/` | Copyable automatic theme-update workflow (`update-fluid-theme.yml`) and `dependabot.yml` for sites |
227
+ | `templates/` | 19 HTML templates |
52
228
  | `examples/` | Live component demos |
229
+ | `showcase/` | Real example sites + framework test beds (see SHOWCASE-SITES.md) |
230
+ | `scripts/visual-scan.mjs` | Mechanical layout-defect scanner (ship gate) |
231
+ | `scripts/axe-check.mjs` | axe gate over `scripts/fixtures/catalog.html`, showcase, gallery, templates |
232
+ | `_docs/HANDOFF.md` | Current state, blockers, working agreements |
53
233
  | `_docs/UX-BEHAVIOR-SPEC.md` | UX behavioral rules (18 sections) |
54
234
  | `_docs/images-guide.md` | Image optimization best practices |
55
235
  | `fluid-agent-kit/` | AI agent skills and conventions |
@@ -63,6 +243,7 @@ This project includes the **Fluid Agent Kit** (`fluid-agent-kit/`) with skill fi
63
243
  - `fluid-agent-kit/skills/ux-rules.md` — 12 behavioral rules for interactive components
64
244
  - `fluid-agent-kit/skills/accessibility.md` — HTML, keyboard, motion, color standards
65
245
  - `fluid-agent-kit/skills/image-optimization.md` — Responsive images, lazy loading, focal points
246
+ - `fluid-agent-kit/skills/preset-composition.md` — superseded: presets were removed in Pass 0
66
247
 
67
248
  ## UX Behavior Spec
68
249
 
@@ -92,13 +273,19 @@ Full guide at `_docs/images-guide.md`. Quick reference at `fluid-agent-kit/skill
92
273
 
93
274
  | Document | What It Covers |
94
275
  |----------|----------------|
95
- | [DEVELOPMENT.md](_docs/DEVELOPMENT.md) | Master guide — read first |
276
+ | [STATUS.md](_docs/STATUS.md) | **What the theme is now — read first** |
277
+ | [defaults.md](_docs/defaults.md) | **Every default: the rule, why, how a site changes it** (nav colours, alignment, size limit) |
278
+ | [migrations/from-1x.md](_docs/migrations/from-1x.md) | **1.x → 2.0 migration paths** (keeping the 1.x look vs adopting Base v2, Stack `brand.ts` knobs) |
279
+ | [HANDOFF.md](_docs/HANDOFF.md) | Open blockers, working agreements, history |
280
+ | [SHOWCASE-SITES.md](_docs/SHOWCASE-SITES.md) | The showcase sites, upstream-first rule, ship gate, deploy runbook |
281
+ | [archive/](_docs/archive/README.md) | Superseded docs kept as history (the old master guide `DEVELOPMENT.md`, `PROJECT_STATUS.md`, the readiness roadmap) |
96
282
  | [UX-BEHAVIOR-SPEC.md](_docs/UX-BEHAVIOR-SPEC.md) | Behavioral rules — 18 sections |
97
283
  | [images-guide.md](_docs/images-guide.md) | Image optimization best practices |
98
- | [architecture.md](_docs/architecture.md) | Technical decisions |
284
+ | [architecture.md](_docs/architecture.md) | Technical decisions (rewritten in Pass 0) |
99
285
  | [reference/components.md](_docs/reference/components.md) | Component API |
100
- | [reference/tokens.md](_docs/reference/tokens.md) | Design token reference |
286
+ | [reference/tokens.md](_docs/reference/tokens.md) | Superseded token reference (the knobs: STATUS.md) |
287
+ | [theming-guide.md](_docs/theming-guide.md) | Superseded: presets and palettes, removed in Pass 0 |
101
288
 
102
289
  ## Version
103
290
 
104
- **Current: 0.4.1** — Update in package.json, PROJECT_STATUS.md, CHANGELOG.md, _config.yml, and version badges on every session that modifies files.
291
+ **Current: the latest git tag** (`git describe --tags --abbrev=0`; also `package.json`). The release workflow (`.github/workflows/release.yml`) tags and changelogs every merge to `main` as a patch unless the PR is labelled `minor` or `major` (root `CONTRIBUTING.md`, "PR Labels"), so a docs-only merge still releases a patch (0.9.25 on 2026-10-18 was one). The version is bumped **at release, not per session**; do not write the number here. The old "bump in five places every session" rule is retired (decision D6, `the-fluid-stack/playbook/versioning.md`, 2026-10-18). npm serves the released version (2.0.0 since 2026-10-30); the workflow publishes with npm trusted publishing, no token (D-8, 2026-10-29; `_docs/HANDOFF.md` blocker 1). If a publish fails after the tag is pushed, fix the npm setting and run Release by hand (workflow_dispatch); it publishes without bumping.
package/GEMINI.md CHANGED
@@ -1,48 +1,9 @@
1
- # Gemini Instructions — The Fluid Theme
1
+ # Gemini instructions: The Fluid Theme
2
2
 
3
- You are working with **The Fluid Theme**, a semantic CSS design system built on physics principles.
3
+ This file is a pointer. The instructions for every agent live in one place:
4
4
 
5
- ## Read These First
5
+ - **[`CLAUDE.md`](https://github.com/Agile-Media-Agency/the-fluid-theme/blob/main/CLAUDE.md)**: rules, gates, key files and the docs table. Agent-neutral despite the name.
6
+ - **[`_docs/STATUS.md`](https://github.com/Agile-Media-Agency/the-fluid-theme/blob/main/_docs/STATUS.md)**: what the theme is now (one lean base, a child theme per site, about 20 knobs on `:root`). It wins over every older doc.
7
+ - How every project is built: `Agile-Media-Agency/the-fluid-stack` `playbook/README.md`.
6
8
 
7
- 1. `fluid-agent-kit/skills/css-conventions.md` — CSS rules and token system
8
- 2. `fluid-agent-kit/skills/accessibility.md` — Accessibility standards
9
- 3. `fluid-agent-kit/skills/ux-rules.md` — UX behavioral rules
10
- 4. `fluid-agent-kit/skills/image-optimization.md` — Image best practices
11
-
12
- ## Core Rules
13
-
14
- ### CSS
15
- - Layer order: `@layer reset, tokens, base, layouts, components, themes, utilities;`
16
- - All values from design tokens: `var(--space-4)` not `16px`
17
- - Logical properties only: `margin-block-start` not `margin-top`
18
- - Variants via data attributes: `data-variant="danger"` not `.btn--danger`
19
- - Components in `@layer components { }`
20
-
21
- ### HTML
22
- - Semantic elements: `<article class="card">` not `<div class="flex flex-col">`
23
- - Native `<dialog>` for modals (automatic focus trapping via `.showModal()`)
24
- - Every `<img>` needs `alt`, `width`, `height`
25
- - `loading="lazy"` on all below-fold images
26
- - Use `data-focus` for image focal points, `data-aspect` for aspect ratios
27
-
28
- ### Accessibility (Non-Negotiable)
29
- - Never remove `:focus-visible` outlines
30
- - `prefers-reduced-motion: reduce` in every animated component
31
- - 44x44px minimum touch targets
32
- - Color alone never conveys information
33
-
34
- ### UX Behavior
35
- - Destructive actions require confirmation modals
36
- - Every action produces visible feedback
37
- - Specific button labels only ("Save Changes" not "Submit")
38
- - Validate forms on blur, then keystroke after first error
39
-
40
- ## Documentation
41
-
42
- - `_docs/DEVELOPMENT.md` — Master guide
43
- - `_docs/UX-BEHAVIOR-SPEC.md` — Full UX specification
44
- - `_docs/images-guide.md` — Image optimization guide
45
- - `_docs/reference/components.md` — Component API
46
- - `_docs/reference/tokens.md` — Token reference
47
-
48
- ## Version: 0.4.0
9
+ Presets, palettes, `data-preset`, `data-palette` and `_docs/reference/tokens.md` are superseded (Pass 0, 2026-10-23); do not write code for them. The version is the latest git tag; never write or bump it by hand. Pass 1 (draft PR #75, step 8) plans to replace this file and `CLAUDE.md` with one `AGENT.md`.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # The Fluid Theme Framework
2
2
 
3
+ > **Superseded (2026-10-23, Pass 0).** Most of this README describes the preset/palette system, which was removed. What the theme is now, and its knobs: [`_docs/STATUS.md`](_docs/STATUS.md); every default, why, and how to change it: [`_docs/defaults.md`](_docs/defaults.md). Kept as history.
4
+
3
5
  **[TheFluidTheme.com](https://thefluidtheme.com)** | [Live Components](https://thefluidtheme.com/examples/components.html) | [Live Foundation](https://thefluidtheme.com/examples/foundation.html) | [Templates](https://thefluidtheme.com/templates/)
4
6
 
5
7
  ## A Physics-Based Design System
@@ -22,6 +24,13 @@ Most design systems pick arbitrary animation timings. We use **Reynolds numbers*
22
24
  | **Rapids** | ~5000 | Turbulent, energetic with resistance |
23
25
  | **Tsunami** | ~50000 | Massive force, dramatic displacement |
24
26
 
27
+ ## Starter Kits
28
+
29
+ | Starter | What it is | Get started |
30
+ |---------|-----------|------------|
31
+ | [HTML](starters/html/) | Zero-setup, CDN-based. No npm, no build step. | Copy `starters/html/index.html` and open it. |
32
+ | [React](starters/react/) | Vite + React 18. Includes live theming demo. | `cd starters/react && npm install && npm run dev` |
33
+
25
34
  ## Installation
26
35
 
27
36
  ### CDN (recommended for quick start)
@@ -67,7 +76,7 @@ Grab the latest release from the [GitHub Releases](https://github.com/Agile-Medi
67
76
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fluid-theme/core/dist/fluid.min.css">
68
77
 
69
78
  <!-- Set your preferences -->
70
- <body data-theme="dark" data-motion="cascade" data-palette="fluid" data-accent="1">
79
+ <body data-theme="dark" data-preset="cascade" data-palette="fluid" data-accent="1">
71
80
 
72
81
  <!-- Use semantic HTML -->
73
82
  <article class="card">
@@ -173,7 +182,8 @@ the-fluid-theme/
173
182
  │ ├── README.md # Kit overview
174
183
  │ └── skills/ # CSS, UX, a11y, image skills
175
184
  ├── _docs/ # Documentation
176
- │ ├── _start-here.md # Full documentation
185
+ │ ├── STATUS.md # What the theme is now (start here)
186
+ │ ├── archive/ # Superseded docs, kept as history
177
187
  │ ├── images-guide.md # Image optimization guide
178
188
  │ └── reference/ # Component + layout API docs
179
189
  ├── _includes/ # Jekyll includes
@@ -200,7 +210,7 @@ Control everything via HTML attributes:
200
210
  ```html
201
211
  <body
202
212
  data-theme="dark" <!-- light | dark | auto -->
203
- data-motion="cascade" <!-- still | serene | stream | flowing | cascade | rapids | tsunami -->
213
+ data-preset="cascade" <!-- still | serene | stream | flowing | cascade | rapids | tsunami -->
204
214
  data-palette="fluid" <!-- fluid | morandi | bold | pastel | earth | mono | 80s | matrix | subtle | tsunami -->
205
215
  data-accent="1" <!-- 1-6, selects from current palette -->
206
216
  data-contrast="normal" <!-- normal | subdued -->
@@ -227,7 +237,7 @@ Control everything via HTML attributes:
227
237
  - [x] **Phase 4: Documentation** — Getting started guide, installation, tutorials, image optimization guide
228
238
  - [x] **Phase 5: Distribution** — npm publish, CDN, Fluid Agent Kit for AI assistants
229
239
 
230
- **Current**: v0.4.2 — See [PROJECT_STATUS.md](PROJECT_STATUS.md) for details.
240
+ **Current**: the latest git tag (`git describe --tags --abbrev=0`, also `package.json`); never written here. What the theme is now: [`_docs/STATUS.md`](_docs/STATUS.md).
231
241
 
232
242
  ## Browser Support
233
243
 
@@ -237,12 +247,11 @@ Modern browsers: Chrome 120+, Firefox 120+, Safari 16.4+, Edge 120+
237
247
 
238
248
  - **[TheFluidTheme.com](https://thefluidtheme.com)** — Live demo site
239
249
  - **[Getting Started](_docs/getting-started.md)** — Installation and first steps
240
- - **[Full Documentation](_docs/_start-here.md)** — Complete guide
250
+ - **[What the theme is now](_docs/STATUS.md)** — Start here
241
251
  - **[Component Reference](_docs/reference/components.md)** — All 23 components
242
252
  - **[Positioning Reference](_docs/reference/positioning.md)** — Layout system
243
253
  - **[Tokens Reference](_docs/reference/tokens.md)** — Design tokens
244
- - **[Public Readiness Roadmap](FRAMEWORK_READINESS.md)** — Launch plan
245
- - **[Executive Summary](SUMMARY.md)** — Quick overview
254
+ - **[Archive](_docs/archive/README.md)** — Superseded docs (readiness roadmap, summary, old status), kept as history
246
255
 
247
256
  ## License
248
257
 
@@ -250,4 +259,4 @@ MIT — Use it, modify it, make it yours.
250
259
 
251
260
  ---
252
261
 
253
- **Version 0.4.2** | **[TheFluidTheme.com](https://thefluidtheme.com)** | Built with physics and 32 years of design experience.
262
+ **Version:** the latest git tag | **[TheFluidTheme.com](https://thefluidtheme.com)** | Built with physics and 32 years of design experience.
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Before / After compare
3
+ *
4
+ * Turns each [data-compare] figure (markup in src/components/compare.css)
5
+ * into one frame with a divider: a native range input laid over the photos
6
+ * moves it, so mouse, touch, keyboard (arrows, Page Up / Page Down, Home,
7
+ * End) and screen readers all work without custom key handling. The slider
8
+ * announces "60% Before, 40% After", using the figure's own label text.
9
+ *
10
+ * Progressive enhancement: without this script the two photos sit side by
11
+ * side, each with its label, so nothing is lost.
12
+ *
13
+ * Usage:
14
+ * <figure class="compare" data-compare> … </figure>
15
+ * <script src="assets/js/compare.js" defer></script>
16
+ *
17
+ * Figures added after load: call window.fluidCompare() (idempotent).
18
+ */
19
+
20
+ (function () {
21
+ 'use strict';
22
+
23
+ function labelText(side, fallback) {
24
+ var label = side.querySelector('.compare__label');
25
+ var text = label && label.textContent.trim();
26
+ return text || fallback;
27
+ }
28
+
29
+ function setup(fig) {
30
+ if (fig.dataset.compareReady) return;
31
+ var stage = fig.querySelector('.compare__stage');
32
+ var before = fig.querySelector('.compare__before');
33
+ var after = fig.querySelector('.compare__after');
34
+ if (!stage || !before || !after) return;
35
+
36
+ var start = parseFloat(fig.dataset.start);
37
+ if (!(start >= 0 && start <= 100)) start = 50;
38
+ var beforeText = labelText(before, 'Before');
39
+ var afterText = labelText(after, 'After');
40
+
41
+ var range = document.createElement('input');
42
+ range.type = 'range';
43
+ range.className = 'compare__range';
44
+ range.min = '0';
45
+ range.max = '100';
46
+ range.step = '1';
47
+ range.value = String(start);
48
+ range.setAttribute('aria-label', fig.dataset.compareLabel || 'Compare before and after');
49
+
50
+ var handle = document.createElement('span');
51
+ handle.className = 'compare__handle';
52
+ handle.setAttribute('aria-hidden', 'true');
53
+ var knob = document.createElement('span');
54
+ knob.className = 'compare__knob';
55
+ knob.textContent = '‹ ›';
56
+ handle.appendChild(knob);
57
+
58
+ function update() {
59
+ var v = Number(range.value);
60
+ fig.style.setProperty('--compare-pos', v + '%');
61
+ range.setAttribute('aria-valuetext', v + '% ' + beforeText + ', ' + (100 - v) + '% ' + afterText);
62
+ }
63
+
64
+ range.addEventListener('input', update);
65
+ stage.append(handle, range);
66
+ update();
67
+ fig.dataset.compareReady = '1';
68
+ }
69
+
70
+ function init() {
71
+ document.querySelectorAll('[data-compare]').forEach(setup);
72
+ }
73
+
74
+ window.fluidCompare = init;
75
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
76
+ else init();
77
+ })();
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Fit Text
3
+ *
4
+ * Opt-in shrink-to-fit for display text. Add [data-fit-text] to any
5
+ * element whose longest word must never overflow its container —
6
+ * oversized single-word headlines, featured terms, big numbers.
7
+ *
8
+ * The element's font-size steps down (never up) until its content
9
+ * fits, to a floor of 55% of the authored size. Re-fits on resize.
10
+ *
11
+ * Progressive enhancement: pair with the CSS overflow safety in
12
+ * base.css / data-overflow so text stays contained without JS too.
13
+ *
14
+ * Usage:
15
+ * <h2 data-fit-text>ANTIDISESTABLISHMENTARIANISM</h2>
16
+ * <script src="assets/js/fit-text.js"></script>
17
+ *
18
+ * Measure-once mode, [data-fit] on a .display or .hero__headline: once the
19
+ * fonts are ready, the widest line (lines are the author's <br> breaks) is
20
+ * measured in em and written to --fit. From then on CSS alone sizes the
21
+ * headline to fill its column at every width (src/core/base.css), so there
22
+ * is no resize listener. Without JS the author's --fit, or the default 8,
23
+ * holds.
24
+ *
25
+ * <h1 class="display" data-fit>Make the<br>quiet thing<br>loud.</h1>
26
+ *
27
+ * Dynamic content: call window.fluidFitText() after injecting
28
+ * elements (idempotent; safe to call repeatedly).
29
+ */
30
+
31
+ (function () {
32
+ 'use strict';
33
+
34
+ var MIN_SCALE = 0.55;
35
+
36
+ function fit(el) {
37
+ // Reset to the authored size before measuring
38
+ el.style.fontSize = '';
39
+ var base = parseFloat(getComputedStyle(el).fontSize);
40
+ if (!base || el.clientWidth === 0) return;
41
+
42
+ var size = base;
43
+ var floor = base * MIN_SCALE;
44
+ var guard = 24;
45
+ while (el.scrollWidth > el.clientWidth && size > floor && guard--) {
46
+ size = Math.max(floor, size - Math.max(1, base * 0.04));
47
+ el.style.fontSize = size + 'px';
48
+ }
49
+ }
50
+
51
+ // The lines held on one line each: the widest line's width over the font
52
+ // size, plus 3% for side bearings and rounding. All headlines are set to
53
+ // one line first, then all are read, then all are restored and written:
54
+ // one forced layout for the page, not one per headline.
55
+ function measureAll() {
56
+ var els = Array.prototype.slice.call(document.querySelectorAll('[data-fit]'));
57
+ var saved = els.map(function (el) {
58
+ var s = el.style, kept = [s.whiteSpace, s.inlineSize];
59
+ s.whiteSpace = 'nowrap';
60
+ s.inlineSize = 'max-content';
61
+ return kept;
62
+ });
63
+ var reads = els.map(function (el) {
64
+ var range = document.createRange();
65
+ range.selectNodeContents(el);
66
+ return [range.getBoundingClientRect().width, parseFloat(getComputedStyle(el).fontSize)];
67
+ });
68
+ els.forEach(function (el, i) {
69
+ var s = el.style, w = reads[i][0], size = reads[i][1];
70
+ s.whiteSpace = saved[i][0];
71
+ s.inlineSize = saved[i][1];
72
+ if (w && size) s.setProperty('--fit', (w / size * 1.03).toFixed(3));
73
+ });
74
+ }
75
+
76
+ function fitAll() {
77
+ document.querySelectorAll('[data-fit-text]').forEach(fit);
78
+ }
79
+
80
+ var pending;
81
+ function onResize() {
82
+ clearTimeout(pending);
83
+ pending = setTimeout(fitAll, 100);
84
+ }
85
+
86
+ if (document.readyState === 'loading') {
87
+ document.addEventListener('DOMContentLoaded', fitAll);
88
+ } else {
89
+ fitAll();
90
+ }
91
+
92
+ window.addEventListener('resize', onResize);
93
+
94
+ // Re-fit when fonts finish loading (metrics change); measure [data-fit]
95
+ // only then, so the measurement uses the real face.
96
+ if (document.fonts && document.fonts.ready) {
97
+ document.fonts.ready.then(function () { measureAll(); fitAll(); });
98
+ } else {
99
+ measureAll();
100
+ }
101
+
102
+ window.fluidFitText = function () { measureAll(); fitAll(); };
103
+ })();