@axiapps/axi-design 1.9.0 → 1.11.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.
@@ -1,333 +0,0 @@
1
- # axi-design documentation site — design
2
-
3
- **Date:** 2026-09-23
4
- **Status:** approved, ready for planning
5
- **Scope:** round one of two. This spec covers the documentation site only. Adding
6
- missing elements (modal, alert, breadcrumb, pagination, accordion, checkbox,
7
- radio, textarea, progress, toast, avatar, spinner, …) is round two and gets its
8
- own spec.
9
-
10
- ## Intent
11
-
12
- Today `axi-design` publishes a single `index.html` that scrolls through six
13
- sections of examples. It proves the components exist; it does not explain them,
14
- cannot be deep-linked, and has no mechanism preventing a component from going
15
- undocumented. This project turns that page into a documentation site in the
16
- mould of Bootstrap's — per-component pages, narrative guides, search — built
17
- entirely out of axi's own components.
18
-
19
- ### Audiences, all three weighted
20
-
21
- 1. **The author, across the suite.** Building an app, needs the exact class
22
- names and copyable markup in seconds. Optimises for density, search and
23
- deep links.
24
- 2. **An LLM agent writing axi-styled UI.** Needs the complete class inventory,
25
- the knob surface, and the rules it must not break. Optimises for
26
- completeness and machine-readable structure (`llms.txt`).
27
- 3. **An outside adopter arriving from npm.** Needs to see what it looks like
28
- and decide. Optimises for a landing page that shows rather than tells.
29
-
30
- ### Success criteria
31
-
32
- - Every `.axi-*` class in `src/` is reachable from a documentation page, and
33
- this is enforced by a test rather than by discipline.
34
- - The markup shown in a code block is, by construction, the markup that
35
- produced the demo beside it. Drift is structurally impossible.
36
- - The site's own chrome uses only axi components plus a quarantined
37
- `.docs-*` layer. The site is evidence the language can build a real site.
38
- - `docs/RULES.md` remains the single source of truth for the language; the
39
- site renders it rather than restating it.
40
- - Nothing a consumer downloads changes. `dist/axi.css`, the npm tarball and
41
- every published `/vN/axi.css` are untouched by this work.
42
-
43
- ### Non-goals
44
-
45
- - No new components, no changes to any file in `src/`. Gaps the work exposes
46
- are recorded for round two, not filled.
47
- - No light theme, no i18n, no versioned documentation (the site documents the
48
- current version; only the CSS artifacts are versioned, as today).
49
- - No runtime dependency, ever. The only new dependency is `marked`, build-time
50
- and `devDependencies` only.
51
-
52
- ## Approach
53
-
54
- Rejected: a static site generator (11ty/VitePress). It would theme the docs in
55
- its own design system, which for a design language forfeits the site's main
56
- argument, and it imports a toolchain into a repo with one devDependency.
57
-
58
- Rejected: hand-written HTML per page. No forcing function keeping components
59
- documented, and the shared chrome gets copy-pasted into thirty files — a cost
60
- that compounds exactly when round two doubles the page count.
61
-
62
- **Chosen:** a generator owned by this repo, driven by a component manifest, with
63
- Markdown for narrative prose. The manifest gives enforcement and machine-readable
64
- output; Markdown keeps the guides pleasant enough to actually write.
65
-
66
- ## Site map
67
-
68
- ```
69
- / Landing — the look, the pitch, install in three lines
70
- /start/ Getting started: the three consumption modes, set an accent
71
- /rules/ docs/RULES.md, rendered
72
- /theming/ Tokens, the accent list, the per-instance knob table
73
- /components/ Index: every component grouped by layer, with preview tiles
74
- /components/<id>/ One page per component family (~30 pages)
75
- /gallery/ The kitchen sink — today's index.html, everything on one scroll
76
- /llms.txt Exhaustive machine-readable reference
77
- /search.json Client-side search index
78
- /vN/axi.css Unchanged; staged from git tags exactly as today
79
- ```
80
-
81
- `/gallery/` is retained deliberately. It is the only view in which the system
82
- can be judged as a whole, which thirty separate pages actively obscure. It stops
83
- being the front door and becomes the smoke test.
84
-
85
- `/rules/` renders `docs/RULES.md` through `marked` into `.axi-prose`. There is no
86
- second copy. RULES.md continues to ship in the npm tarball and continues to be
87
- what `tests/tokens.test.mjs` is written against.
88
-
89
- ## The manifest
90
-
91
- ```
92
- docs/manifest/
93
- index.mjs combines the layer modules, validates entry shape
94
- primitives.mjs layout.mjs shells.mjs data.mjs prose.mjs utilities.mjs
95
- knobs.mjs the per-instance knob table, as data
96
- ```
97
-
98
- `.mjs` rather than JSON so example markup can be a readable template literal and
99
- entries can carry comments.
100
-
101
- ### Entry shape
102
-
103
- ```js
104
- {
105
- id: 'meter', // URL segment; /components/meter/
106
- name: 'Meter',
107
- layer: 'data', // sidebar group; see note below
108
- classes: ['.axi-meter', '.axi-meter__fill', '.axi-meter-list', …],
109
- summary: 'One or two sentences. Used on the page, the index tile and llms.txt.',
110
- rules: [1, 2], // clause numbers in docs/RULES.md
111
- knobs: ['--axi-meter-v', '--axi-meter-h', '--axi-series'],
112
- notes: `Optional Markdown for anything the summary cannot carry.`,
113
- examples: [
114
- { title: 'A single meter', note: 'Optional one-liner, right-aligned', html: `…` },
115
- ],
116
- }
117
- ```
118
-
119
- **`layer` is a documentation concern, not a source-file fact.** The `src/` split
120
- is by cascade order, not by category, and the two genuinely differ:
121
-
122
- | Class | Lives in | Documented under |
123
- |---|---|---|
124
- | `.axi-panel` | `primitives.css` | Layout |
125
- | `.axi-quote` | `shells.css` | Prose |
126
- | `.axi-notice`, `.axi-tooltip`, `.axi-eyebrow`, `.axi-sigil` | `shells.css` | Primitives |
127
- | `.axi-sr-only` | `base.css` | Utilities |
128
-
129
- `layer` is therefore declared per entry and never inferred from the file. A
130
- `utilities` group exists for classes that are real and documented but belong to
131
- no visual family.
132
-
133
- ### Single-sourcing of examples
134
-
135
- Each `examples[].html` string is rendered twice by the generator: raw into the
136
- live demo, and HTML-escaped-then-highlighted into the code block beneath it.
137
- This is the central mechanism of the design — the copyable code cannot drift
138
- from the rendered demo because there is only one string.
139
-
140
- ## The generator
141
-
142
- `scripts/site.mjs`, new, separate from `scripts/build.mjs`. `build.mjs` continues
143
- to build the shipped artifact and is not modified except where noted under
144
- "README knob table". Different lifecycles, and nothing in the site generator can
145
- affect what a consumer downloads.
146
-
147
- ```
148
- docs/site/
149
- shell.mjs page chrome: mast, sidebar, TOC, footer
150
- render.mjs component-page and index-page renderers
151
- markdown.mjs marked wrapper → .axi-prose
152
- highlight.mjs build-time HTML tokeniser
153
- docs.css docs-only chrome, .docs-* prefix
154
- search.js client-side search over search.json
155
- copy.js code-block copy buttons
156
- ```
157
-
158
- Markdown pages may embed generated content through a placeholder line — a
159
- lone `<!-- axi:knobs -->` or `<!-- axi:accents -->` in the source — which the
160
- generator replaces with the rendered table. This is how `/theming/` shows the
161
- knob and accent tables without a hand-maintained second copy, and it is the
162
- same mechanism as the README markers. An unrecognised `axi:` placeholder is a
163
- build error rather than a silent passthrough.
164
-
165
- Outputs to `_site/`: directory-style URLs (`_site/components/meter/index.html`),
166
- plus `search.json`, `llms.txt`, and copies of `dist/` and `docs/site/*.{css,js}`.
167
-
168
- ### Base-path handling
169
-
170
- The site is served from `darkharasho.github.io/axi-design/`, not from a domain
171
- root, while local preview is served from `/`. The generator takes a single
172
- `BASE` constant (`/axi-design/` for Pages, `/` for `npm run serve`) and every
173
- emitted href passes through one `url()` helper. A test asserts no emitted page
174
- contains a raw `href="/` or `src="/` outside that helper. This is the most
175
- likely thing to ship silently broken, so it is checked directly.
176
-
177
- ### Syntax highlighting
178
-
179
- Build-time, hand-rolled in `highlight.mjs`, roughly 30 lines. HTML only — the
180
- one language the samples are written in. Four token classes, coloured from
181
- existing tokens:
182
-
183
- | Token | Class | Ink |
184
- |---|---|---|
185
- | Tag name | `.t-tag` | `--axi-meta` |
186
- | Attribute name | `.t-attr` | `--axi-accent` |
187
- | Attribute value | `.t-str` | `--axi-ok` |
188
- | Punctuation | `.t-punc` | `--axi-text-faint` |
189
-
190
- A Prism/Shiki dependency is not justified for HTML, and would be a runtime cost
191
- paid by every visitor for a transform that can happen once at build time.
192
-
193
- ### Docs-only CSS is quarantined
194
-
195
- `docs/site/docs.css` holds the sidebar, TOC, code block and example-block
196
- styles under a `.docs-*` prefix. It is never read by `build.mjs`, never enters
197
- `dist/`, and is not in the npm `files` list.
198
-
199
- Deliberate side effect: **`docs.css` is the shopping list for round two.**
200
- Anything in it that proves generally useful becomes a candidate for promotion
201
- into `src/`, having earned its place by being needed rather than by appearing
202
- on another framework's feature list.
203
-
204
- ## Page anatomy
205
-
206
- ### Component page
207
-
208
- Three columns: sidebar (grouped by `layer`, current page filled solid in the
209
- accent — the language has no soft states, so "selected" is a block of ink),
210
- content, and an "On this page" TOC. Below the layout breakpoint the TOC is
211
- dropped and the sidebar collapses into a disclosure in the mast.
212
-
213
- Content order:
214
-
215
- 1. `.axi-eyebrow` with the layer name
216
- 2. Title
217
- 3. Lede — the `summary` field
218
- 4. Class list as `.axi-chip--meta` chips, so the names being looked for appear
219
- immediately below the title
220
- 5. `notes`, if present, rendered as `.axi-prose`
221
- 6. Examples — for each: title, optional right-aligned note, demo in a
222
- panel-weight block, code block tucked against the panel's offset so the two
223
- read as one object, copy button in the code block's corner
224
- 7. **Knobs** — table sourced from `knobs.mjs`, filtered to this entry's `knobs`
225
- 8. **Rules this answers** — each clause in `rules` rendered as `.axi-notice`
226
- with the clause number in the icon slot, deep-linking to `/rules/#rule-<n>`.
227
- `marked` derives heading ids from heading text, which would make the anchor
228
- depend on the wording of a rule's title and break the link whenever a rule
229
- is reworded. The Markdown renderer therefore overrides heading ids for
230
- `## <n>. …` headings in RULES.md to the stable form `rule-<n>`.
231
-
232
- Section 8 is what distinguishes this from a component list: every page closes by
233
- pointing at the clauses that dictate why the component looks the way it does.
234
-
235
- ### Chrome
236
-
237
- The mast is `.axi-mast` / `.axi-brand` / `.axi-sigil` / `.axi-tabs` /
238
- `.axi-search` / `.axi-select`, entirely existing components. The package version
239
- sits in `.axi-brand__name`'s `<small>`, read from `package.json` at build time.
240
- The accent switcher is the existing `gallery.js` behaviour, generalised to
241
- every page and driven by `accents.json`. `gallery.js` is split accordingly: the
242
- accent-switching portion moves to `docs/site/accent.js` and is loaded by every
243
- page, while the demo-specific behaviour (menu, drawer, tooltip positioning)
244
- stays in `gallery.js`, which is loaded only by `/gallery/`.
245
-
246
- ### Landing page
247
-
248
- One job: show, not tell. A hero composed of real components — panel, card with
249
- its strip, meter, chip row — with the live accent switcher, the three-line
250
- install directly beneath, and two buttons: "Get started" and "Components". The
251
- philosophy gets one paragraph and a link to `/rules/`, not a wall of text.
252
-
253
- ## The README knob table becomes generated
254
-
255
- `docs/manifest/knobs.mjs` becomes the source of truth for the per-instance knob
256
- table. `README.md` carries the table between HTML comment markers, regenerated
257
- by the build, with a test asserting the file on disk matches what the manifest
258
- produces. Today that table can drift from `src/` silently; after this it cannot.
259
- This is the one place the work touches `build.mjs`.
260
-
261
- ## Enforcement
262
-
263
- New `tests/manifest.test.mjs`:
264
-
265
- 1. Every `.axi-*` class defined in `src/` appears in some entry's `classes`.
266
- An undocumented component fails the build. In round two, a new element
267
- cannot land without its page.
268
- 2. Every class used in any example's `html` exists in `src/`. Catches typos in
269
- the docs and doc pages that outlive their CSS.
270
- 3. Every `--axi-*` custom property read with a fallback in `src/` appears in
271
- `knobs.mjs`.
272
- 4. `README.md`'s knob table matches the table generated from `knobs.mjs`.
273
- 5. Entry shape: unique `id`s, non-empty `summary`, `layer` from the known set,
274
- `rules` referencing clause numbers that exist in `RULES.md`.
275
-
276
- New `tests/site.test.mjs`:
277
-
278
- 6. The generator emits every expected page for a given manifest.
279
- 7. Example HTML is escaped in code blocks and never executed as markup there.
280
- 8. Highlighting round-trips: stripping the emitted `<span>`s and unescaping
281
- returns the original source string exactly.
282
- 9. No emitted page contains a raw absolute `href="/` or `src="/`.
283
-
284
- Existing `tests/tokens.test.mjs`, `tests/build.test.mjs` and
285
- `tests/accents.test.mjs` are unchanged.
286
-
287
- Per repository convention, vitest runs with `--maxWorkers=2`.
288
-
289
- ## Build and deploy
290
-
291
- ```
292
- npm run build scripts/build.mjs src/*.css → dist/axi.css, dist/accents.css
293
- npm run docs scripts/site.mjs manifest + md → _site/
294
- npm run serve scripts/serve.mjs ~25 lines, no dependency, local preview at /
295
- npm test vitest run
296
- ```
297
-
298
- `.github/workflows/pages.yml`: the current
299
- `cp -r index.html gallery.js dist _site/` is replaced by `npm run docs`, which
300
- builds `_site/` itself. The existing per-tag staging loop follows unchanged.
301
- The guarantee that every published `/vN/axi.css` keeps resolving is untouched,
302
- because those artifacts are read from git tags and not from the site build.
303
-
304
- `.gitignore` gains `_site/`.
305
-
306
- ## Implementation sequence
307
-
308
- Each step is independently verifiable, and the second is expected to fail
309
- loudly — that is its purpose.
310
-
311
- 1. Generator skeleton, shell, `docs.css`, one hardcoded component page.
312
- 2. `data.mjs` manifest + enforcement test 1. **Expect failures** naming
313
- undocumented classes (`.axi-axis`, `.axi-legend`, `.axi-badge-count`,
314
- `.axi-sigil`, `.axi-eyebrow` are the anticipated ones). Group them rather
315
- than forcing them into a layer they do not belong to.
316
- 3. Remaining manifest layers: primitives, layout, shells, prose, utilities.
317
- 4. `knobs.mjs`, the knobs section, and the generated README table.
318
- 5. Markdown pipeline: `/start/`, `/theming/`, `/rules/`.
319
- 6. Landing page; move `index.html` to `/gallery/`.
320
- 7. Search index and client search; `llms.txt`.
321
- 8. `pages.yml`, `.gitignore`, `npm run serve`.
322
-
323
- ## Risks
324
-
325
- - **Base-path breakage** — the site works locally and 404s on Pages. Mitigated
326
- by the single `url()` helper and test 9.
327
- - **Manifest tedium** — thirty entries with examples is the bulk of the work.
328
- Mitigated by porting the existing markup out of `index.html`, which is
329
- already written and already correct.
330
- - **Enforcement test 1 becoming an obstacle** — if a class legitimately should
331
- not be documented, the answer is an explicit `utilities` entry, never an
332
- exclusion list in the test. An exclusion list is the escape hatch this
333
- repository's existing tests are written specifically to avoid.