@wildmason/aegis 1.14.1 → 1.15.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 (3) hide show
  1. package/CHANGELOG.md +236 -0
  2. package/inputs.css +33 -0
  3. package/package.json +64 -62
package/CHANGELOG.md ADDED
@@ -0,0 +1,236 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@wildmason/aegis` are recorded here, following
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ This file was started on 2026-09-01, later than it should have been. Entries
8
+ from 1.8.0 onward are backfilled from the release commits and the project health
9
+ page; both were written at the time, so the entries are sourced rather than
10
+ reconstructed from memory. Anything before 1.8.0 lives in `git log` only, and is
11
+ deliberately not summarised here rather than guessed at.
12
+
13
+ **Releases from 1.12.0 onward carry no git tag.** They were published through the
14
+ GitHub OIDC workflow, which does not tag, so `git tag` stops at `v1.11.0` and the
15
+ tag names before that are inconsistent (`1.4.4`, `v1.5.1`). Version links below
16
+ are omitted where no tag exists rather than pointed at a URL that 404s.
17
+
18
+ ## [Unreleased]
19
+
20
+ ## [1.15.0] - 2026-09-01
21
+
22
+ ### Added
23
+
24
+ - `.wm-input--error` — the invalid state for `.wm-input` and `.wm-input-group`.
25
+ It was documented in prose and in a live demo on the inputs guide page and had
26
+ never existed, so the demo captioned "error state" rendered a field identical
27
+ to a valid one. It fills with `--wm-color-danger-bg` and rims with
28
+ `--wm-color-danger-border`, the same tokens as the `.wm-error-msg` block below
29
+ the field, and yields to `:focus` so an accent border is never fighting a red
30
+ one while the user is fixing the field. Pair it with `.wm-error-msg` and
31
+ `aria-invalid="true"`: colour must not be the only signal.
32
+ - `scripts/check-design-language.mjs`, wired into `npm run build` as
33
+ `check:docs`. It holds all 32 agent-visible documents — `DESIGN_LANGUAGE.md`
34
+ plus the guide pages in `mcp/guide-index.json` — to the CSS this package
35
+ actually ships, in both directions: a name no CSS defines, and a shipped
36
+ `.wm-*` class no document mentions.
37
+
38
+ ### Changed
39
+
40
+ - `check:semantic-contrast` measures a fourth recipe and a fourth surface. The
41
+ new pair is `--wm-text-primary` on the danger tint, which is what
42
+ `.wm-input--error` paints and which no bar covered, because every other status
43
+ recipe reads `--wm-color-<tone>-readable` on the tint. `bg-sunken` joins the
44
+ surface sweep, since both `.wm-input:hover` and `.wm-input-group:hover` fill
45
+ with it. 144 combinations became 192; all pass.
46
+
47
+ ### Fixed
48
+
49
+ - `DESIGN_LANGUAGE.md` had never been migrated through the background-token
50
+ rename. It prescribed `--wm-bg-inset`, `--wm-bg-canvas` and `--wm-bg-surface`,
51
+ which became `sunken` / `base` / `raised`+`float` — so a consumer following the
52
+ spec wrote a `var()` naming an undefined property, which makes the whole
53
+ declaration invalid and apply to nothing, silently. Corrected against the
54
+ shipped CSS rather than by find-and-replace, since `surface` split in two.
55
+ - §13 taught eight lines of inline style for a keyboard badge. `.wm-kbd` and
56
+ `.wm-kbd-combo` had shipped all along; the spec now names them.
57
+ - §6 documented `.btn-primary`, `.btn-secondary`, `.btn-ghost` and `.btn-danger`
58
+ — classes this package has never shipped. It now documents `.wm-btn` with its
59
+ real variants, including `--flat` and `--xs`, which were undocumented.
60
+ - §15.1 listed `.wm-focus` and `.wm-focus-inset` utility classes that do not
61
+ exist. Every shipped control already carries its own `:focus-visible` rule; the
62
+ section now says which treatment goes with which fill.
63
+ - §17 presented twelve syntax, diff and code tokens as part of the token set.
64
+ Nine syntax tokens ship; the other twelve are app-local and were never
65
+ promoted. They are now listed as names to avoid, with the promotion path.
66
+ - §20 was still a roadmap for a package that had shipped, and was the source of
67
+ several of the names above.
68
+ - `docs/token-migration.md` stated that `--wm-bg-sunken` is the darkest colour in
69
+ every theme. Measured across all twelve theme blocks, it is the **lightest** in
70
+ `warm-light`, `neutral-light` and `solarized-precision`, and `sage-linen`
71
+ follows neither convention on purpose.
72
+ - The inputs guide's `.wm-class` placeholder and encapsulation example named
73
+ classes that do not exist.
74
+
75
+ ### Known limitation
76
+
77
+ - `.wm-input--error` measures APCA **Lc 60.3** on `solarized-precision` over
78
+ `--wm-bg-float`, against a bar of 60. It passes both bars on all twelve themes
79
+ (worst WCAG 6.20:1), but the margin is thin, and 1.13.0 set a precedent of
80
+ tuning to Lc 61 because Lc 60.0 can be flipped by a single hex rounding. The
81
+ tint reduces contrast on every one of the 192 combinations by construction, so
82
+ closing the margin would mean retuning a shipped token. Accepted deliberately
83
+ rather than overlooked; `check:semantic-contrast` will fail if it slips.
84
+
85
+ ## [1.14.1] - 2026-08-27
86
+
87
+ ### Fixed
88
+
89
+ - Five theme/tone pairs shipped at 1.08:1 to 1.38:1 because a guard asserted the
90
+ fallback it was checking. `themes.css` selects its default as
91
+ `:root, [data-theme='warm-light']`, so that block is both the warm-light theme
92
+ and a later bare-`:root` declaration — a theme silent on a token receives
93
+ warm-light's value, not its own tone. Every theme must now declare all four
94
+ `-readable` tokens; `var(--wm-color-<tone>)` is the supported way to say "my
95
+ tone already reads", and the guard resolves and measures it.
96
+
97
+ ## [1.14.0] - 2026-08-27
98
+
99
+ ### Added
100
+
101
+ - `WmTooltip` — an accessible tooltip directive on a CDK overlay, beside the
102
+ CSS-only `data-tooltip` chip. It shares `aria-describedby` rather than owning
103
+ it, satisfies WCAG 1.4.13 (Escape dismisses without moving focus and without
104
+ stopping the event; the pointer can enter the panel), and has no border by
105
+ measurement — at least 6.67:1 from every surface Aegis can put behind it.
106
+ `wmTooltipFocusable` is opt-in on purpose: never set it inside a
107
+ roving-tabindex container.
108
+ - `check:tooltip-contrast`, a third build gate.
109
+
110
+ ### Fixed
111
+
112
+ - Every anchored overlay now stays with its trigger when anything scrolls.
113
+ `createRepositionScrollStrategy` had never fired once: CDK's `ScrollDispatcher`
114
+ listens on the document in the **bubbling** phase and `scroll` does not bubble,
115
+ so in a fixed-height shell with an inner `overflow-y: auto` element both of its
116
+ sources are empty and the strategy is a silent no-op. `wm-popover`,
117
+ `wm-select` and `wm-combobox` had never set a strategy at all. Upgrading is the
118
+ whole fix.
119
+
120
+ ## [1.13.0] - 2026-08-27
121
+
122
+ ### Changed
123
+
124
+ - `--wm-color-accent-readable` is now declared by all twelve themes and held to
125
+ APCA. On WCAG the accent family looked healthy at 4.5:1 to 6.3:1; on APCA
126
+ eleven of twelve missed Lc 60 on a bare surface and all twelve missed it on the
127
+ accent's own 10% tint. Both pre-existing overrides failed, because both were
128
+ tuned to WCAG alone. Tuned to Lc 61 rather than 60, because tuning to the bar
129
+ exactly put one theme on Lc 60.0.
130
+
131
+ ## [1.12.0] - 2026-08-27
132
+
133
+ ### Added
134
+
135
+ - `--wm-color-<tone>-readable` for danger, warning and success — 31 overrides
136
+ across 12 themes. The tone is the saturated colour that *names* a status; it
137
+ was never a colour that could be *read*, failing on 31 of 36 theme/tone
138
+ combinations.
139
+ - `--wm-color-accent-border`, used by `.wm-info-msg`.
140
+ - `check:semantic-contrast`, a build gate over every theme/tone/surface
141
+ combination on both contrast models.
142
+
143
+ ### Fixed
144
+
145
+ - No status rim had ever met WCAG 1.4.11. `-border` was a 25% mix toward
146
+ `transparent`, measuring 1.18:1 to 1.88:1 on every theme and tone. Rims now use
147
+ the button-edge rule at 65/35 — 70/30 fails at 2.93:1, because a rim has a
148
+ neighbour the edge never had: the tint the block is filled with.
149
+ - Toasts stopped mixing their own 20% tint and took the shared tokens.
150
+
151
+ ## [1.11.0] - 2026-08-22
152
+
153
+ ### Added
154
+
155
+ - `--wm-color-danger-solid` and `--wm-color-danger-edge`.
156
+ - `check:destructive-contrast`, the first build gate to measure a token *pair*.
157
+
158
+ ### Fixed
159
+
160
+ - `.wm-btn--destructive` painted `#ffffff` on `--wm-color-danger`, a token tuned
161
+ for danger as *text*, so the label failed WCAG AA on 8 of 12 themes — worst
162
+ 2.74:1. The fill and the 3:1 component boundary now come from separate tokens,
163
+ because on `arctic-night` no single red satisfies both. Only the fill darkens
164
+ on hover; darkening the edge would eat the contrast it exists to guarantee.
165
+
166
+ ## [1.10.2] - 2026-08-18
167
+
168
+ ### Fixed
169
+
170
+ - The UI token now selects bundled Inter, and the mono token Cascadia Code,
171
+ before OS fallbacks. 1.10.1 packaged every face but still selected Segoe first,
172
+ so asset-presence proof did not prove runtime selection. The build guard now
173
+ checks both.
174
+
175
+ ## [1.10.1] - 2026-08-18
176
+
177
+ ### Added
178
+
179
+ - The complete typography stack is vendored: Inter and Cascadia Code variable
180
+ WOFF2 faces, normal and italic, with both SIL OFL 1.1 licenses and pinned
181
+ upstream checksums. The docs app no longer downloads a font.
182
+
183
+ ## [1.10.0] - 2026-08-09
184
+
185
+ ### Added
186
+
187
+ - `WmPopover` accepts a nullable `ariaLabel`, applied to the overlay shell that
188
+ already owns the role and the generated id the trigger's `aria-controls`
189
+ points at. Existing consumers emit no new attribute.
190
+
191
+ ## [1.9.2] - 2026-08-09
192
+
193
+ ### Fixed
194
+
195
+ - Inactive and unavailable tabs both resolved to `--wm-text-muted`, so
196
+ desaturating neutral text did nothing. Unavailable labels and icons now mix
197
+ muted text to 52% transparency, while focus outlines stay fully opaque.
198
+
199
+ ## [1.9.1] - 2026-08-09
200
+
201
+ ### Changed
202
+
203
+ - `wm-tab-separator` uses `--wm-border-subtle` and renders as an unfocusable
204
+ `role="separator"` with `aria-orientation="vertical"`, so grouping reaches
205
+ assistive technology without adding a keyboard stop.
206
+
207
+ ## [1.9.0] - 2026-08-09
208
+
209
+ ### Changed
210
+
211
+ - **BREAKING:** `wm-modal` traps focus itself. Consumers that applied their own
212
+ `cdkTrapFocus` must remove it or they nest a second trap. The removed traps
213
+ were inert, so nobody loses working behaviour: `CdkTrapFocus.ngAfterContentInit`
214
+ calls `attachAnchors()`, which requires a `parentNode`, and projected content
215
+ has none at that moment — measured at 0 anchors before, 2 after.
216
+ - Published through GitHub OIDC Trusted Publishing. No long-lived npm token and
217
+ no 2FA bypass.
218
+
219
+ ## [1.8.0] - 2026-08-04
220
+
221
+ ### Added
222
+
223
+ - `wm-combobox` gains `ariaLabel` and `ariaLabelledby`. The host is not a
224
+ labelable element and the input had no aria binding, so in multi-select — where
225
+ the placeholder is blanked once a chip renders — it could have no accessible
226
+ name at all. `ariaLabelledby` wins and suppresses `aria-label`, which makes
227
+ WCAG 2.5.3 structurally impossible to violate.
228
+
229
+ ---
230
+
231
+ Releases before 1.8.0 are in `git log`. See also `wiki/products/Aegis - Health.md`
232
+ for the decisions behind these changes, including the ones decided against.
233
+
234
+ [Unreleased]: https://github.com/wildmason/aegis/compare/master...HEAD
235
+ [1.11.0]: https://github.com/wildmason/aegis/releases/tag/v1.11.0
236
+ [1.9.0]: https://github.com/wildmason/aegis/releases/tag/v1.9.0
package/inputs.css CHANGED
@@ -38,6 +38,7 @@
38
38
  .wm-input-group Composite row: icon + .wm-input-bare (e.g. search)
39
39
  .wm-input-bare Borderless inner input used inside .wm-input-group
40
40
  .wm-input-icon Leading/trailing icon slot inside .wm-input-group
41
+ .wm-input--error Invalid state for .wm-input / .wm-input-group
41
42
  .wm-select-trigger Button-based custom select trigger (for app-select etc.)
42
43
  .wm-number-input Number input variant (spinner chrome removed)
43
44
  .wm-number-controls ± button strip appended to .wm-input-group
@@ -436,6 +437,38 @@
436
437
  cursor: not-allowed;
437
438
  }
438
439
 
440
+ /* Invalid: --wm-color-danger-border, the same edge the .wm-error-msg block
441
+ below the field draws, so the field and its message read as one thing.
442
+ The fill is --wm-color-danger-bg rather than a flat tint, because it is
443
+ the only danger background tuned per theme to stay behind text.
444
+
445
+ Kept above :focus in the cascade order deliberately: focus wins while the
446
+ user is fixing the field, so the accent border and its pulse are not
447
+ fighting a red one. The error border comes back the moment focus leaves,
448
+ and the message block never went anywhere.
449
+
450
+ Colour is never the only signal — pair this with .wm-error-msg and
451
+ aria-invalid="true", or a red border is the whole message to a user who
452
+ cannot see red.
453
+
454
+ Each selector is anchored to the element it is for. `.wm-input--error` alone
455
+ would match BOTH a control and a group, and a group is never `:focus` — the
456
+ input inside it is — so the control rule's `:not(:focus)` is vacuously true
457
+ on a group and paints the tint straight through the group rule's
458
+ `:not(:focus-within)`. Measured, not reasoned about: a focused group showed
459
+ the accent border with the error fill still behind it. */
460
+ .wm-input.wm-input--error:not(:focus),
461
+ .wm-input-group.wm-input--error:not(:focus-within) {
462
+ border-color: var(--wm-color-danger-border);
463
+ background-color: var(--wm-color-danger-bg);
464
+ }
465
+
466
+ .wm-input.wm-input--error:hover:not(:focus):not(:disabled),
467
+ .wm-input-group.wm-input--error:hover:not(:focus-within) {
468
+ border-color: var(--wm-color-danger);
469
+ background-color: var(--wm-color-danger-bg);
470
+ }
471
+
439
472
  /* Inherit color-scheme so date/time picker chrome (calendar icon, spinners)
440
473
  matches the active theme's light/dark declaration. */
441
474
  .wm-input[type='date'],
package/package.json CHANGED
@@ -1,62 +1,64 @@
1
- {
2
- "name": "@wildmason/aegis",
3
- "version": "1.14.1",
4
- "description": "Aegis design system \u00e2\u20ac\u201d CSS tokens and Angular component library",
5
- "scripts": {
6
- "check:offline-fonts": "node scripts/check-offline-fonts.mjs",
7
- "build": "npm run check:offline-fonts && npm run check:destructive-contrast && npm run check:semantic-contrast && npm run check:tooltip-contrast && ng-packagr -p ng-package.json",
8
- "prepublishOnly": "npm run build",
9
- "check:destructive-contrast": "node scripts/check-destructive-contrast.mjs",
10
- "check:semantic-contrast": "node scripts/check-semantic-contrast.mjs",
11
- "check:tooltip-contrast": "node scripts/check-tooltip-contrast.mjs"
12
- },
13
- "files": [
14
- "dist",
15
- "fonts",
16
- "themes.css",
17
- "inputs.css"
18
- ],
19
- "exports": {
20
- "./themes.css": "./themes.css",
21
- "./inputs.css": "./inputs.css",
22
- "./components": {
23
- "types": "./dist/types/wildmason-aegis.d.ts",
24
- "default": "./dist/fesm2022/wildmason-aegis.mjs"
25
- }
26
- },
27
- "peerDependencies": {
28
- "@angular/cdk": ">=21",
29
- "@angular/core": ">=21",
30
- "@tauri-apps/api": ">=2",
31
- "rxjs": ">=7",
32
- "tslib": "^2.3.0"
33
- },
34
- "devDependencies": {
35
- "@angular/cdk": "^21.0.0",
36
- "@angular/common": "21.2.1",
37
- "@angular/compiler": "21.2.1",
38
- "@angular/compiler-cli": "21.2.1",
39
- "@angular/core": "21.2.1",
40
- "@angular/forms": "21.2.1",
41
- "@angular/platform-browser": "21.2.1",
42
- "@tauri-apps/api": "^2.10.1",
43
- "ng-packagr": "^21.0.0",
44
- "tslib": "^2.8.1",
45
- "typescript": "^5.9.2"
46
- },
47
- "keywords": [
48
- "design-system",
49
- "css",
50
- "angular",
51
- "tokens"
52
- ],
53
- "license": "MIT",
54
- "repository": {
55
- "type": "git",
56
- "url": "git+https://github.com/wildmason/aegis.git"
57
- },
58
- "publishConfig": {
59
- "access": "public",
60
- "registry": "https://registry.npmjs.org"
61
- }
62
- }
1
+ {
2
+ "name": "@wildmason/aegis",
3
+ "version": "1.15.0",
4
+ "description": "Aegis design system \u00e2\u20ac\u201d CSS tokens and Angular component library",
5
+ "scripts": {
6
+ "check:offline-fonts": "node scripts/check-offline-fonts.mjs",
7
+ "build": "npm run check:offline-fonts && npm run check:destructive-contrast && npm run check:semantic-contrast && npm run check:tooltip-contrast && npm run check:docs && ng-packagr -p ng-package.json",
8
+ "prepublishOnly": "npm run build",
9
+ "check:destructive-contrast": "node scripts/check-destructive-contrast.mjs",
10
+ "check:semantic-contrast": "node scripts/check-semantic-contrast.mjs",
11
+ "check:tooltip-contrast": "node scripts/check-tooltip-contrast.mjs",
12
+ "check:docs": "node scripts/check-design-language.mjs"
13
+ },
14
+ "files": [
15
+ "CHANGELOG.md",
16
+ "dist",
17
+ "fonts",
18
+ "themes.css",
19
+ "inputs.css"
20
+ ],
21
+ "exports": {
22
+ "./themes.css": "./themes.css",
23
+ "./inputs.css": "./inputs.css",
24
+ "./components": {
25
+ "types": "./dist/types/wildmason-aegis.d.ts",
26
+ "default": "./dist/fesm2022/wildmason-aegis.mjs"
27
+ }
28
+ },
29
+ "peerDependencies": {
30
+ "@angular/cdk": ">=21",
31
+ "@angular/core": ">=21",
32
+ "@tauri-apps/api": ">=2",
33
+ "rxjs": ">=7",
34
+ "tslib": "^2.3.0"
35
+ },
36
+ "devDependencies": {
37
+ "@angular/cdk": "^21.0.0",
38
+ "@angular/common": "21.2.1",
39
+ "@angular/compiler": "21.2.1",
40
+ "@angular/compiler-cli": "21.2.1",
41
+ "@angular/core": "21.2.1",
42
+ "@angular/forms": "21.2.1",
43
+ "@angular/platform-browser": "21.2.1",
44
+ "@tauri-apps/api": "^2.10.1",
45
+ "ng-packagr": "^21.0.0",
46
+ "tslib": "^2.8.1",
47
+ "typescript": "^5.9.2"
48
+ },
49
+ "keywords": [
50
+ "design-system",
51
+ "css",
52
+ "angular",
53
+ "tokens"
54
+ ],
55
+ "license": "MIT",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/wildmason/aegis.git"
59
+ },
60
+ "publishConfig": {
61
+ "access": "public",
62
+ "registry": "https://registry.npmjs.org"
63
+ }
64
+ }