@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.
- package/README.md +48 -10
- package/dist/axi.css +559 -30
- package/dist/themes/glass.css +11 -0
- package/docs/RULES.md +157 -17
- package/package.json +8 -4
- package/src/base.css +2 -0
- package/src/feedback.css +67 -0
- package/src/forms.css +89 -0
- package/src/primitives.css +162 -16
- package/src/prose.css +1 -1
- package/src/shells.css +209 -13
- package/src/tokens.css +25 -0
- package/docs/superpowers/plans/2026-09-23-axi-docs-site.md +0 -2376
- package/docs/superpowers/specs/2026-09-23-axi-docs-site-design.md +0 -333
|
@@ -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.
|