@axiapps/axi-design 1.7.1 → 1.9.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 +2 -0
- package/dist/axi.css +58 -0
- package/docs/RULES.md +15 -0
- package/docs/superpowers/plans/2026-09-23-axi-docs-site.md +2376 -0
- package/docs/superpowers/specs/2026-09-23-axi-docs-site-design.md +333 -0
- package/package.json +14 -5
- package/src/data.css +36 -0
- package/src/shells.css +22 -0
|
@@ -0,0 +1,333 @@
|
|
|
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.
|
package/package.json
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiapps/axi-design",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "The design language for the axi suite — flat and outlined, dark, drawn in saturated ink.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "darkharasho",
|
|
8
|
-
"repository": {
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/darkharasho/axi-design.git"
|
|
11
|
+
},
|
|
9
12
|
"homepage": "https://darkharasho.github.io/axi-design/",
|
|
10
|
-
"engines": {
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22"
|
|
15
|
+
},
|
|
11
16
|
"//publishConfig": "A scoped package defaults to a restricted publish, and a restricted design language is no use to the apps that consume it. Pinned here rather than passed as --access public on the command line, so it cannot be forgotten on a later release.",
|
|
12
|
-
"publishConfig": {
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
13
20
|
"//exports": "Two entry points, and both are stylesheets. Consumers import the path rather than the package root because there is no JavaScript here to be a default export - `import '@axiapps/axi-design/axi.css'` says what it does, and a bare `import '@axiapps/axi-design'` resolving to a stylesheet would not. ./tokens.css is the palette without the components, for an app that already draws its own components through its own variables and wants to point them at ours: it is the whole language for a consumer like that, and copying the token block by hand is the one way those values are guaranteed to drift.",
|
|
14
21
|
"exports": {
|
|
15
22
|
"./axi.css": "./dist/axi.css",
|
|
@@ -34,5 +41,7 @@
|
|
|
34
41
|
"build": "node scripts/build.mjs",
|
|
35
42
|
"test": "vitest run"
|
|
36
43
|
},
|
|
37
|
-
"devDependencies": {
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"vitest": "^2.1.0"
|
|
46
|
+
}
|
|
38
47
|
}
|
package/src/data.css
CHANGED
|
@@ -183,6 +183,42 @@
|
|
|
183
183
|
background: var(--axi-series, var(--axi-accent));
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/* ---------- ticks ---------- */
|
|
187
|
+
/* A run of yes/no along a timeline: attended or missed, passed or failed,
|
|
188
|
+
shipped or skipped. This is rule 9 read the other way round - a bar has a
|
|
189
|
+
length because a quantity has one, and "no" has no length at all. Drawn as
|
|
190
|
+
a bar it has to be faked with a stub, and a stub reads as "a little bit",
|
|
191
|
+
which is the same lie as a faded fill. So every event here is one mark of
|
|
192
|
+
the same size, and what changes between them is only which ink it is drawn
|
|
193
|
+
in: rule 10's two series, the accent for the fact and the neutral ramp for
|
|
194
|
+
its absence.
|
|
195
|
+
|
|
196
|
+
Neither mark is outlined, which is the one place this departs from the
|
|
197
|
+
filled/empty pair the language reaches for first. At the size a run of
|
|
198
|
+
twenty sits in a table row the outline IS the mark - three pixels of edge
|
|
199
|
+
around one pixel of middle - so the pair would differ in nothing the eye
|
|
200
|
+
can resolve. A strip drawn large enough to outline is a bar chart, and
|
|
201
|
+
.axi-bars already is one.
|
|
202
|
+
|
|
203
|
+
Marks are a fixed width and never flex: a run of fourteen and a run of
|
|
204
|
+
three share a column in a table, and the short one has to read as a short
|
|
205
|
+
run rather than as fourteen fatter events. */
|
|
206
|
+
.axi-ticks {
|
|
207
|
+
display: inline-flex;
|
|
208
|
+
align-items: stretch;
|
|
209
|
+
gap: var(--axi-ticks-gap, 3px);
|
|
210
|
+
height: var(--axi-tick-h, 15px);
|
|
211
|
+
}
|
|
212
|
+
.axi-ticks__tick {
|
|
213
|
+
flex: none;
|
|
214
|
+
width: var(--axi-tick-w, 5px);
|
|
215
|
+
background: var(--axi-rule);
|
|
216
|
+
border-radius: var(--axi-radius-sm);
|
|
217
|
+
}
|
|
218
|
+
.axi-ticks__tick--on {
|
|
219
|
+
background: var(--axi-series, var(--axi-accent));
|
|
220
|
+
}
|
|
221
|
+
|
|
186
222
|
/* ---------- plot ---------- */
|
|
187
223
|
/* A frame for a line or an area, with its horizontal rules drawn in. The
|
|
188
224
|
rules are hard stops in a repeating gradient, which is the select caret's
|
package/src/shells.css
CHANGED
|
@@ -120,6 +120,28 @@
|
|
|
120
120
|
.axi-menu__pop label:hover { background: var(--axi-ground); color: var(--axi-text); }
|
|
121
121
|
.axi-menu__pop input { margin: 3px 0 0; accent-color: var(--axi-accent); }
|
|
122
122
|
|
|
123
|
+
/* ---------- tooltip ---------- */
|
|
124
|
+
/* One line of ink on the darkest surface in the language, so it reads over
|
|
125
|
+
anything it lands on. The class draws the box only; coordinates come from
|
|
126
|
+
the consumer's script (measure the trigger, set left/top — gallery.js is
|
|
127
|
+
the reference wiring). Two contracts travel with it: the element is
|
|
128
|
+
appended to <body>, never inside the component it annotates, because
|
|
129
|
+
position: fixed re-anchors to any transformed ancestor and rule 4 makes
|
|
130
|
+
every hovered ancestor transformed; and it never carries information that
|
|
131
|
+
exists nowhere else, because a keyboard or touch user may never see it. */
|
|
132
|
+
.axi-tooltip {
|
|
133
|
+
position: fixed;
|
|
134
|
+
z-index: 99999;
|
|
135
|
+
padding: 5px 8px;
|
|
136
|
+
background: var(--axi-ink-line);
|
|
137
|
+
color: var(--axi-text);
|
|
138
|
+
border: var(--axi-border-hairline) solid var(--axi-rule);
|
|
139
|
+
border-radius: var(--axi-radius-sm);
|
|
140
|
+
font: var(--axi-t-micro);
|
|
141
|
+
white-space: nowrap;
|
|
142
|
+
pointer-events: none;
|
|
143
|
+
}
|
|
144
|
+
|
|
123
145
|
/* ---------- card ---------- */
|
|
124
146
|
.axi-card {
|
|
125
147
|
position: relative; overflow: hidden;
|