@wildmason/aegis 2.0.0 → 2.2.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/CHANGELOG.md +353 -1
- package/dist/fesm2022/wildmason-aegis.mjs +914 -39
- package/dist/fesm2022/wildmason-aegis.mjs.map +1 -1
- package/dist/types/wildmason-aegis.d.ts +542 -23
- package/inputs.css +85 -6
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,355 @@ that 404s.
|
|
|
18
18
|
|
|
19
19
|
## [Unreleased]
|
|
20
20
|
|
|
21
|
+
## [2.2.0] - 2026-10-02
|
|
22
|
+
|
|
23
|
+
Minor: new components, variants and inputs, and accessibility fixes. Nothing is
|
|
24
|
+
renamed or removed, so a consumer on `^2.0.0` picks this up on its next install
|
|
25
|
+
and nothing it already names moves. Two fixes change the ARIA that renders, so
|
|
26
|
+
a consumer test that reads it can see a difference: the `<wm-popover>` host no
|
|
27
|
+
longer carries the panel's role, and a `dialog` or `alertdialog` panel now has
|
|
28
|
+
`aria-modal="true"`.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- `WmTabs` accepts `activeValue` `null` for "no tab selected". Use it when the
|
|
33
|
+
stage shows something that is not any tab's panel, such as a document opened
|
|
34
|
+
from a second tablist in the same row. Every tab reports
|
|
35
|
+
`aria-selected="false"` and none paints as active, and the tablist keeps one
|
|
36
|
+
tab stop (see Fixed). The input type widens from `string` to
|
|
37
|
+
`string | null`, so every existing template binding still compiles.
|
|
38
|
+
- `WmTabs` `ariaLabel` and `ariaLabelledby` inputs name the inner
|
|
39
|
+
`role="tablist"`; `ariaLabelledby` wins when both are set. The tablist had no
|
|
40
|
+
way to take a name, so two tablists in one row sounded identical to a screen
|
|
41
|
+
reader. An `aria-label` attribute on the `<wm-tabs>` element still names
|
|
42
|
+
nothing, because that element has no role.
|
|
43
|
+
- `WmButtonDirective` (`button[wmButton]`, `a[wmButton]`) puts the Aegis
|
|
44
|
+
button classes on a native element the consumer writes, from the same
|
|
45
|
+
`variant`, `size` and `iconOnly` inputs as `<wm-button>`. `<wm-button>`
|
|
46
|
+
renders its own `<button>` inside a host that has no role and cannot take
|
|
47
|
+
focus, and passes it only `disabled`, `type`, `ariaLabel` and `btnTitle`.
|
|
48
|
+
So `aria-pressed`, `aria-expanded`, `aria-controls`, `cdkFocusInitial`, an
|
|
49
|
+
`id`, a popover `trigger`, `[wmTooltip]`, `data-tooltip` and layout classes
|
|
50
|
+
all stayed on the wrapper. On the directive's element they reach the button
|
|
51
|
+
with no forwarding list. Both forms compute their classes in one function,
|
|
52
|
+
so they cannot drift apart. A `<button>` with no `type` gets
|
|
53
|
+
`type="button"`, the `<wm-button>` default, so moving from the component to
|
|
54
|
+
the directive does not create a submit button; a written or bound `type`
|
|
55
|
+
wins. The consumer's own classes, static or bound, are kept. `WmButton` is
|
|
56
|
+
unchanged.
|
|
57
|
+
- `.wm-btn--dashed`, and `variant="dashed"` on `<wm-button>` and
|
|
58
|
+
`[wmButton]`: the empty slot, for the place a new item goes at the end of
|
|
59
|
+
the list it adds to. It is transparent, with a 1px dashed edge and a
|
|
60
|
+
`--wm-text-secondary` label. On hover it fills with `--wm-bg-sunken`, the
|
|
61
|
+
edge turns solid and the label moves to `--wm-text-primary`. Focus uses the
|
|
62
|
+
pulse, as `ghost` does. `ButtonVariant` gains `'dashed'`. Helm hand-rolled
|
|
63
|
+
this twice (`workflow-chain-add`, `lfs-add-pattern`), and the two copies
|
|
64
|
+
disagreed on edge token, opacity and padding. At full strength, neither
|
|
65
|
+
copy's edge token reached 3:1 on every theme and surface: `--wm-border` is
|
|
66
|
+
1.04:1 on warm-light, and `--wm-border-subtle` is 1.00:1 on arctic-night.
|
|
67
|
+
Their 80% and 78% mixes toward transparent fall below 3:1 on all 48 theme
|
|
68
|
+
and surface pairs. The shipped edge mixes `--wm-border-input` 70/30 toward
|
|
69
|
+
`--wm-text-primary`, which clears 3:1 everywhere (worst 3.34:1, wildmason
|
|
70
|
+
on `--wm-bg-float`).
|
|
71
|
+
- `scripts/check-dashed-contrast.mjs` (`npm run check:dashed-contrast`, part
|
|
72
|
+
of `build`) reads the variant's rules out of inputs.css and holds them on
|
|
73
|
+
every theme and on `--wm-bg-base`, `-raised`, `-float` and `-sunken`: a
|
|
74
|
+
4.5:1 label at rest and on hover, APCA Lc 60 on hover, and a 3:1 edge at
|
|
75
|
+
rest and on hover, against the fill as well as the surface.
|
|
76
|
+
- `scripts/check-focus-pulse.mjs` (`npm run check:focus-pulse`, part of
|
|
77
|
+
`build`) fails when a selector animates `wm-focus-pulse` and has no
|
|
78
|
+
reduced-motion rule in the same file that stops the animation and sets a
|
|
79
|
+
static `box-shadow` ring.
|
|
80
|
+
- `WmToggleGroup` and `WmToggleGroupItem` (`<wm-toggle-group>`,
|
|
81
|
+
`<wm-toggle-group-item>`): a single choice among a few visible options,
|
|
82
|
+
drawn as joined `segmented` items in one sunken track or as separate
|
|
83
|
+
`chip`s that wrap, in `size` `sm` (24px) or `md` (28px). Aegis had no
|
|
84
|
+
single-select control of this kind, so each consumer hand-rolled one: Helm
|
|
85
|
+
has six, each a `role="group"` row of `aria-pressed` buttons in which every
|
|
86
|
+
button is its own Tab stop, the arrow keys do nothing, and nothing tells a
|
|
87
|
+
screen reader that the options exclude each other. The component is the
|
|
88
|
+
WAI-ARIA radio group: the group element is `role="radiogroup"`, each item
|
|
89
|
+
element is `role="radio"` with `aria-checked`, the group keeps one Tab stop
|
|
90
|
+
(the focused item while focus is inside, otherwise the checked item,
|
|
91
|
+
otherwise the first item that is not disabled), and the arrow keys move
|
|
92
|
+
focus and check, wrapping and stepping over disabled items, with Left and
|
|
93
|
+
Right mirrored in a right-to-left layout. `Home` and `End` go to the ends,
|
|
94
|
+
`Space` checks, and `Enter` does not. `(valueChange)` fires once per user
|
|
95
|
+
choice and never with `null`, so `[(value)]` type-checks against a signal
|
|
96
|
+
that cannot hold `null`. Values compare by `Object.is` and keep their type.
|
|
97
|
+
It is a ControlValueAccessor and marks the control touched when focus
|
|
98
|
+
leaves the group. The items are the radios, so `aria-label` on an
|
|
99
|
+
icon-only item, `data-testid` and `[wmTooltip]` land on the control. New
|
|
100
|
+
exported types: `WmToggleGroupAppearance`, `WmToggleGroupSize`.
|
|
101
|
+
- `scripts/check-toggle-group-contrast.mjs` (`npm run
|
|
102
|
+
check:toggle-group-contrast`, part of `build`) reads the toggle group's
|
|
103
|
+
rules out of its stylesheet and holds both appearances on every theme and
|
|
104
|
+
on `--wm-bg-base`, `-raised`, `-float` and `-sunken`: 4.5:1 labels
|
|
105
|
+
unchecked, hovered and checked, APCA Lc 60 on hover, and a 3:1 checked edge
|
|
106
|
+
and focus ring. It measured out the first choices: a bare
|
|
107
|
+
`--wm-color-accent` fill is 2.91:1 against `--wm-bg-float` on
|
|
108
|
+
posh-sandalwood, so the checked item carries a
|
|
109
|
+
`--wm-color-accent-readable` edge, and primary text on `--wm-bg-hover` is
|
|
110
|
+
4.41:1 on vampires-kiss, so segmented hover fills with `--wm-bg-float`.
|
|
111
|
+
|
|
112
|
+
- `.wm-hatch` and `.wm-hatch--warning` in inputs.css: the stand-in texture,
|
|
113
|
+
for an elided node that stands for items the view does not draw and for a
|
|
114
|
+
value that nobody has filled in. The class paints 135deg bands, 4px of tone
|
|
115
|
+
then 4px clear, as `background-image` over the element's own fill. The
|
|
116
|
+
neutral tone is `--wm-text-primary` at 10% and the warning tone is
|
|
117
|
+
`--wm-color-warning` at 14%, both mixed toward `transparent`;
|
|
118
|
+
`--wm-hatch-tone` replaces the band colour. Aegis had no texture, so two
|
|
119
|
+
consumers hand-rolled the same geometry: Helm's Stacks overflow node
|
|
120
|
+
(`+N`) and Mortar's unset path variable. Helm's copy built its band from
|
|
121
|
+
`--wm-bg-hover` mixed into `--wm-bg-raised`, which measures 1.08:1 on
|
|
122
|
+
thistle-mocha, close to invisible, and 2.24:1 on vampires-kiss. Each
|
|
123
|
+
shipped share is the smallest that shows on every theme and surface,
|
|
124
|
+
because every point above it costs the text on top contrast.
|
|
125
|
+
- `scripts/check-hatch-contrast.mjs` (`npm run check:hatch-contrast`, part of
|
|
126
|
+
`build`) reads the hatch rules out of inputs.css and holds both tones on
|
|
127
|
+
every theme and on `--wm-bg-base`, `-raised`, `-float` and `-sunken`: a band
|
|
128
|
+
of at least 1.15:1 against the surface, `--wm-text-primary` on the band at
|
|
129
|
+
4.5:1 and APCA Lc 60, every token the tone reads defined by the theme, the
|
|
130
|
+
135deg 4px/8px geometry, and a translucent tone. One pair has no APCA bar:
|
|
131
|
+
on solarized-precision's `--wm-bg-float` the bare text is Lc 65.4, and no
|
|
132
|
+
share of either tone gives a visible band with Lc 60 text (the neutral band
|
|
133
|
+
leaves Lc 57.5 and 5.87:1). The guard proves that on every run and fails if
|
|
134
|
+
a theme change makes the pair reachable.
|
|
135
|
+
|
|
136
|
+
### Fixed
|
|
137
|
+
|
|
138
|
+
- `.wm-btn--flat` and the `.wm-checkbox` class kept the animated focus pulse
|
|
139
|
+
under `prefers-reduced-motion: reduce`. The reduced-motion rule in
|
|
140
|
+
inputs.css lists each pulsing selector by hand, and these two were missing,
|
|
141
|
+
although the Accessibility page said every pulse becomes a static ring. Both
|
|
142
|
+
now get the static 3px ring. `check:focus-pulse` found them.
|
|
143
|
+
|
|
144
|
+
- `WmTabs` no longer drops out of the page's Tab order when no tab is
|
|
145
|
+
selected. It gave the tab stop to the selected tab only, so an `activeValue`
|
|
146
|
+
that matched no tab left every tab at `tabindex="-1"`, and a consumer could
|
|
147
|
+
not repair it because the binding re-applied `-1` on each change detection.
|
|
148
|
+
The stop is now the focused tab while focus is inside, otherwise the
|
|
149
|
+
selected tab, otherwise the first tab that is not `unavailable` (the first
|
|
150
|
+
tab when all are). Helm's Navigator hid the selected pill in CSS to avoid
|
|
151
|
+
this, which left `aria-selected="true"` on a tool that did not look
|
|
152
|
+
selected.
|
|
153
|
+
- `WmTabs` tracks the focused tab by value instead of by index. A tab added or
|
|
154
|
+
removed before the focused one shifted the index, so the stop moved to a
|
|
155
|
+
different tab or past the end of the list, where no tab had
|
|
156
|
+
`tabindex="0"`.
|
|
157
|
+
- `WmTabs` returns its tab stop to the selected tab when focus moves to a
|
|
158
|
+
second `<wm-tabs>`. Its blur check asked whether focus was inside any
|
|
159
|
+
`.wm-tabs` element, so focus in the other tablist counted as inside this
|
|
160
|
+
one and left the stop on the tab last focused.
|
|
161
|
+
|
|
162
|
+
- `WmPopover` sets `aria-modal="true"` on a `role="dialog"` or
|
|
163
|
+
`role="alertdialog"` panel. The panel always opens over a click-catching
|
|
164
|
+
backdrop and traps focus, so it is modal, and without the attribute a screen
|
|
165
|
+
reader kept reading the page behind it. Other roles get no `aria-modal`,
|
|
166
|
+
since the attribute is defined for those two only. A panel with
|
|
167
|
+
`[trapFocusAutoCapture]="false"` gets none either: focus stays on the trigger
|
|
168
|
+
outside the panel, and `aria-modal` would hide that focused trigger. Mortar's
|
|
169
|
+
combobox is that case (default `dialog` role, auto-capture off), and keeps
|
|
170
|
+
its current semantics. A modal panel whose content has nothing tabbable
|
|
171
|
+
(text only, or a lone disabled button) takes focus itself, through
|
|
172
|
+
`tabindex="-1"`, and Tab and Shift+Tab keep focus on it. The focus trap
|
|
173
|
+
finds nothing to focus there, so focus used to stay on the trigger, which
|
|
174
|
+
`aria-modal` hides, and a second Tab walked out to the page behind the
|
|
175
|
+
backdrop. A non-modal panel gets no `tabindex`, so a click on it cannot pull
|
|
176
|
+
focus off a typeahead input.
|
|
177
|
+
- The `<wm-popover>` host no longer carries the panel's role. A consumer
|
|
178
|
+
writes `role="dialog"` as a plain attribute, and Angular copies a static
|
|
179
|
+
attribute onto the host as well as into the input, so each popover written
|
|
180
|
+
that way wrapped its trigger in a second, unnamed `dialog` — or in a `menu`
|
|
181
|
+
that owned no `menuitem`. The role now lives on the panel alone.
|
|
182
|
+
- `WmModal` lays out its own backdrop. It used Tailwind utilities for this
|
|
183
|
+
(`fixed inset-0 flex items-center justify-center z-50`, and `items-start
|
|
184
|
+
pt-20` for `align="top"`), which Aegis does not ship. A consumer's Tailwind
|
|
185
|
+
build emits only the classes its own sources name, so the backdrop was fixed
|
|
186
|
+
and centred only where a consumer happened to use the same utilities; in an
|
|
187
|
+
app without Tailwind, the docs app included, it rendered in the page flow.
|
|
188
|
+
Helm's sources name every centred-layout utility but not `pt-20`, so
|
|
189
|
+
`align="top"` never got its offset there; Helm does not use `align="top"`.
|
|
190
|
+
The rendered values are unchanged: fixed, full viewport, z-index 50, and
|
|
191
|
+
`5rem` from the top for `align="top"`. The inline
|
|
192
|
+
`background: var(--wm-modal-backdrop)` stays, because consumers detect an
|
|
193
|
+
open modal by `[style*="--wm-modal-backdrop"]`.
|
|
194
|
+
|
|
195
|
+
### Documentation
|
|
196
|
+
|
|
197
|
+
- The `popover` guide page, which the MCP serves, documented the docs app's
|
|
198
|
+
private copy of the popover rather than the shipped `WmPopover`: a required
|
|
199
|
+
`data-popover-trigger`, two-way `[(isOpen)]`, `align="center"` and CSS-string
|
|
200
|
+
widths, none of which the shipped component has. The page now runs its demos
|
|
201
|
+
on `WmPopover`, documents `(closed)` and `trapFocusAutoCapture`, and adds an
|
|
202
|
+
Accessibility section. The private copy is deleted.
|
|
203
|
+
- The `popover` page, DESIGN_LANGUAGE.md §7.13 and `WmPopover`'s doc comment
|
|
204
|
+
now say what a consumer following the old page got wrong. The `trigger`
|
|
205
|
+
attribute is the only thing the component reads; `data-popover-trigger` is
|
|
206
|
+
not. The component writes `aria-haspopup`, `aria-expanded` and
|
|
207
|
+
`aria-controls` to the trigger on every change to `isOpen` or `hasPopup`, so
|
|
208
|
+
the template does not bind them, and a static `aria-haspopup` is replaced by
|
|
209
|
+
`hasPopup`. A `<wm-button>` cannot be the trigger: the attribute stays on
|
|
210
|
+
its host, which then wraps a second button role around the real `<button>`,
|
|
211
|
+
so use a native `<button trigger>` with the `wm-btn` classes. The
|
|
212
|
+
Accessibility page's trigger row now lists `aria-controls` too. Helm carries
|
|
213
|
+
nine dead `data-popover-trigger` attributes, eight hand-written
|
|
214
|
+
`aria-expanded` bindings and two `<wm-button trigger>` sites from the old
|
|
215
|
+
page; Mortar carries five dead attributes and one `<mortar-button trigger>`.
|
|
216
|
+
- The `modal` guide page (the Dialog topic) never mentioned
|
|
217
|
+
`--wm-dialog-width`, so it read as though `size="lg"` (640px) were the widest
|
|
218
|
+
`wm-dialog` can go. The property overrides every preset, and Helm sets it on
|
|
219
|
+
ten dialogs, up to 960px. The page now documents it, with a demo, and ran its
|
|
220
|
+
demos on a private copy of the dialog that ignored it; the page now runs on
|
|
221
|
+
the shipped `WmDialog` and `WmButton`, and the dialog copy is deleted. The
|
|
222
|
+
page also documents `wm-modal` for the first time: when to choose it over
|
|
223
|
+
`wm-dialog`, its inputs, what it supplies (backdrop, dismissal, focus trap)
|
|
224
|
+
and what the consumer supplies (`role`, `aria-modal`, the accessible name, a
|
|
225
|
+
close control), with a working demo. `WmDialog`'s own doc comment wrongly
|
|
226
|
+
credited the browser with returning focus on close; `cdkTrapFocusAutoCapture`
|
|
227
|
+
does it.
|
|
228
|
+
- DESIGN_LANGUAGE.md lists `<wm-modal>` among the shipped components (§20 says
|
|
229
|
+
anything unlisted is not shipped), §8.6 names both components and
|
|
230
|
+
`--wm-dialog-width`, and §15.2 says the shipped overlays trap focus
|
|
231
|
+
themselves instead of telling consumers to implement a trap.
|
|
232
|
+
- The `tabs` guide page, which the MCP serves, ran its demos on a private
|
|
233
|
+
copy of the tabs in the docs app; it now runs on the shipped `WmTabs`, and
|
|
234
|
+
the copy is deleted. The page adds a "No Tab Selected" section with a
|
|
235
|
+
working demo of two named tablists that hand the stage to each other, names
|
|
236
|
+
every demo tablist, and documents `ariaLabel`, `ariaLabelledby`, the tab
|
|
237
|
+
stop rule, the `wm-tab-{value}`/`wm-panel-{value}` ids and `WmTab`'s
|
|
238
|
+
`unavailable`, `unavailableReason` and `separatorBefore` inputs, which its
|
|
239
|
+
API table left out. Its request and response demos both used the values
|
|
240
|
+
`body` and `headers`, so the page rendered `wm-tab-body` and
|
|
241
|
+
`wm-tab-headers` twice; the response demo now uses its own values.
|
|
242
|
+
- DESIGN_LANGUAGE.md §10 documents `<wm-tabs>` for the first time (§10.0).
|
|
243
|
+
The section taught only hand-rolled switchers, with no tablist semantics.
|
|
244
|
+
- The Quick Reference component table listed an `[options]` input on
|
|
245
|
+
`<wm-tabs>`, which does not exist, and gave `<wm-popover>` the export name
|
|
246
|
+
`Popover` and a two-way `[(isOpen)]`. The rows now name `WmTabs`'s real
|
|
247
|
+
inputs, and `WmPopover` with `[isOpen]` and `(closed)`.
|
|
248
|
+
- The `button` guide page, which the MCP serves, said `<wm-button>` and the
|
|
249
|
+
`.wm-btn` classes "are interchangeable" and "produce identical output".
|
|
250
|
+
They are not: attributes written on `<wm-button>` stay on its host. Helm
|
|
251
|
+
met this twice on 2026-08-09 (an `aria-pressed` that could not reach the
|
|
252
|
+
button, and a `cdkFocusInitial` that CDK reported as "not focusable") and
|
|
253
|
+
fell back to writing the classes by hand. The page now opens with a
|
|
254
|
+
"Component, Directive or Class"
|
|
255
|
+
section: use `<wm-button>` by default, `[wmButton]` whenever something has
|
|
256
|
+
to land on the focusable element, and the bare classes only outside
|
|
257
|
+
Angular, with a table of what does not reach the inner button and a working
|
|
258
|
+
`aria-pressed` toggle. `data-tooltip` is in that table: it opens on
|
|
259
|
+
`:focus-visible`, which the wrapper never matches, so an icon-only
|
|
260
|
+
`<wm-button>` never showed its tooltip to a keyboard user. DESIGN_LANGUAGE.md
|
|
261
|
+
gains the same rule as §6.5, and `WmButton` gains a doc comment that says
|
|
262
|
+
what it forwards.
|
|
263
|
+
- The `button` page ran on a private copy of `WmButton` in the docs app that
|
|
264
|
+
had drifted from the library (no `xs`, `iconOnly`, `ariaLabel` or
|
|
265
|
+
`btnTitle`). The page and the Composition, Form Patterns and Toast pages now
|
|
266
|
+
use the library component, and the copy is deleted. The page documents the
|
|
267
|
+
`xs` size, `iconOnly`, `ariaLabel` and `btnTitle`, which its API table left
|
|
268
|
+
out. Its own stylesheet set `.btn-icon-only` to 36 × 36px, so every
|
|
269
|
+
icon-only demo rendered at the default size whatever its size class, against
|
|
270
|
+
the 28px and 40px its own table promised; that rule is removed.
|
|
271
|
+
- DESIGN_LANGUAGE.md §8.4 (toolbar button) and §12.3 (notification dot) wrote
|
|
272
|
+
`class="btn btn-sm btn-ghost"`, which Aegis does not ship; the docs check
|
|
273
|
+
cannot see it because the names have no `wm-` prefix. Both now use
|
|
274
|
+
`[wmButton]`, and §8.4 tells a toolbar toggle to expose its state with
|
|
275
|
+
`aria-pressed` or `aria-expanded` rather than colour alone. §20 said
|
|
276
|
+
anything it did not list is not shipped, and it did not list `<wm-button>`,
|
|
277
|
+
`<wm-checkbox>`, `<wm-toggle>`, `<wm-ghost-field>` or `<wm-rich-tooltip>`;
|
|
278
|
+
it lists them now, with `[wmButton]`.
|
|
279
|
+
- The `popover` page, DESIGN_LANGUAGE.md §7.13 and `WmPopover`'s doc comment
|
|
280
|
+
now point an Aegis-styled trigger at `<button wmButton trigger>`, and the
|
|
281
|
+
page's demo triggers use it. The Quick Reference and Accessibility pages
|
|
282
|
+
list `[wmButton]` beside `<wm-button>`.
|
|
283
|
+
- The `button` page gains an Empty Slot section: when to use `dashed`, a demo
|
|
284
|
+
of both Helm shapes (a full-width slot and one at its own width), what each
|
|
285
|
+
state paints, and why the edge is mixed. It says that width is the
|
|
286
|
+
consumer's layout, so a full-width slot is `[wmButton]` plus a width class.
|
|
287
|
+
The Variants demo, table and both API tables list `dashed`. The Quick
|
|
288
|
+
Reference page lists `.wm-btn--flat`, `.wm-btn--dashed` and `.wm-btn--xs`;
|
|
289
|
+
it listed none of the three before. The Accessibility page's pulse card
|
|
290
|
+
lists every control that pulses. DESIGN_LANGUAGE.md §6.3 and §15.1 and the
|
|
291
|
+
inputs.css button header are updated.
|
|
292
|
+
- A `toggle-group` guide page (served by the MCP) documents
|
|
293
|
+
`<wm-toggle-group>` with working demos of both appearances, `size="sm"`,
|
|
294
|
+
icon-only items, a reactive form with numeric values, disabled items and
|
|
295
|
+
groups, the keyboard and ARIA contract, and how to replace a hand-rolled
|
|
296
|
+
`aria-pressed` row. DESIGN_LANGUAGE.md gains §10.4. §10's opening splits
|
|
297
|
+
tabs from toggle groups by what the control does (choose a panel, or set a
|
|
298
|
+
value), and §7.5, §10.2, §10.3 and §12.5 point at the component. §15.1
|
|
299
|
+
adds the outline focus treatment its items use, and §20 lists it.
|
|
300
|
+
- The Checkbox, Combobox, Select and Toggle pages sent exclusive choices to
|
|
301
|
+
"radio buttons", which Aegis does not ship, and the Slider page and
|
|
302
|
+
`WmSlider`'s doc comment sent them to "a pill tab group", which is for
|
|
303
|
+
switching panels. All now name `<wm-toggle-group>`. The Tabs page adds a
|
|
304
|
+
Don't: pill tabs do not set a value. The Quick Reference and Accessibility
|
|
305
|
+
pages list the component, its keys, its roles and its reduced-motion rule.
|
|
306
|
+
- DESIGN_LANGUAGE.md gains §2.8, the hatch: what it marks and what it must
|
|
307
|
+
not, the geometry, why the fill must be `background-color` (the
|
|
308
|
+
`background` shorthand resets `background-image`, and a component
|
|
309
|
+
stylesheet outranks `.wm-hatch`), and the rules. Text on a hatch is
|
|
310
|
+
`--wm-text-primary`: under the neutral band `--wm-text-secondary` falls to
|
|
311
|
+
3.70:1 on vampires-kiss. The hatch never carries the meaning alone, because
|
|
312
|
+
forced-colors mode computes a gradient `background-image` and `box-shadow`
|
|
313
|
+
to `none`; a border survives. §6.3 and §18.3 point at it. The `color` guide
|
|
314
|
+
page gains a Hatch section with demos on all four surfaces, and Quick
|
|
315
|
+
Reference gains rows for both classes.
|
|
316
|
+
- DESIGN_LANGUAGE.md §2.7 said Aegis ships 10 themes and left `sage-linen`
|
|
317
|
+
and `wildmason` out of its table; it ships 12. §20 now lists the
|
|
318
|
+
`.wm-shadow` utilities, which it left out, and the hatch classes.
|
|
319
|
+
|
|
320
|
+
## [2.1.0] - 2026-09-20
|
|
321
|
+
|
|
322
|
+
Minor: one new component and its inputs. Nothing existing changes, so a consumer
|
|
323
|
+
on `^2.0.0` picks this up on its next install and nothing it already names moves.
|
|
324
|
+
|
|
325
|
+
### Added
|
|
326
|
+
|
|
327
|
+
- `<wm-slider>` (`WmSlider`) — bounded continuous value selection, exported from
|
|
328
|
+
`@wildmason/aegis/components` with its `WmSliderTick` type. Built on a native
|
|
329
|
+
`<input type="range">`, so pointer drag, touch, arrow keys, `Page Up`/`Page
|
|
330
|
+
Down`, `Home`/`End`, RTL mirroring and the `role="slider"` semantics come from
|
|
331
|
+
the browser. Every value — two-way `[(value)]` binding, form write, or drag —
|
|
332
|
+
is clamped into `min`…`max` and snapped onto the step grid measured from
|
|
333
|
+
`min`, then written back, so the binding, the form control and the thumb never
|
|
334
|
+
disagree. Implements `ControlValueAccessor`. `ticks` draws labelled stops under
|
|
335
|
+
the rail; `showValue` renders a readout, and `formatValue` formats it and sets
|
|
336
|
+
`aria-valuetext`. Host knobs: `--wm-slider-thumb`, `--wm-slider-track`.
|
|
337
|
+
Documented at the `slider` guide page, served by the MCP, and specified in
|
|
338
|
+
`DESIGN_LANGUAGE.md` §7.6a.
|
|
339
|
+
- `WmSlider` `track` input — `'rail'` (default) or `'segmented'`. A segmented
|
|
340
|
+
track breaks the rail into blocks bounded by the resolved tick grid plus the
|
|
341
|
+
ends of the range, sized by their own spans, with the block holding the value
|
|
342
|
+
filling partially. Knob: `--wm-slider-gap` (3px). There is no `'auto'`
|
|
343
|
+
degradation, deliberately.
|
|
344
|
+
- `WmSlider` `ticks` accepts a **number** (a stop every N from `min`), a
|
|
345
|
+
`WmSliderTick[]`, or a `WmSliderTickFn` of `{ min, max, step }`, all exported.
|
|
346
|
+
`WmSliderTick` gains `level?: 'major' | 'minor'` for a second grid density.
|
|
347
|
+
The resolved grid is clamped, sorted and de-duplicated, and it both draws the
|
|
348
|
+
marks and bounds the segments — one source, two renderings, so a block and the
|
|
349
|
+
mark on its edge cannot disagree. A numeric interval that would draw over 1000
|
|
350
|
+
marks draws none rather than truncating or hanging. The grid stays independent
|
|
351
|
+
of `step`, so a 0–100 slider stepping by 1 can carry an 11-mark grid.
|
|
352
|
+
- `WmSlider` `showValue` reserves its column from the widest string the readout
|
|
353
|
+
can hold — the formatted endpoints plus every labelled stop — instead of from
|
|
354
|
+
the current value. The shell is `1fr auto`, so a column that grew with its text
|
|
355
|
+
took width from the rail and gave it back on the next value; with word labels
|
|
356
|
+
the bar moved while the user dragged it. Unlabelled stops are ignored, so a
|
|
357
|
+
101-mark numeric grid reserves two strings rather than 101.
|
|
358
|
+
|
|
359
|
+
### Fixed
|
|
360
|
+
|
|
361
|
+
- The MCP server served every guide page with its code samples missing. The
|
|
362
|
+
extractor decoded HTML entities in its `<pre>` pass, before the document-wide
|
|
363
|
+
tag strip, so an escaped sample — every Angular template in the docs — became
|
|
364
|
+
real tags and was deleted as markup. Entities now stay encoded until after the
|
|
365
|
+
strip and are decoded once at the end, with `&` last so a double-escaped
|
|
366
|
+
sequence cannot be decoded twice. `{` and `}` are decoded for the
|
|
367
|
+
first time, so Angular interpolation braces survive. `mcp/` gains its first
|
|
368
|
+
tests: 8 cases under `node --test`, run by `npm test` in that package.
|
|
369
|
+
|
|
21
370
|
## [2.0.0] - 2026-09-15
|
|
22
371
|
|
|
23
372
|
Major version because two exports go away: the license UI leaves
|
|
@@ -411,7 +760,10 @@ a `^1.x` range does not pick this release up by accident.
|
|
|
411
760
|
Releases before 1.8.0 are in `git log`. See also `wiki/products/Aegis - Health.md`
|
|
412
761
|
for the decisions behind these changes, including the ones decided against.
|
|
413
762
|
|
|
414
|
-
[Unreleased]: https://github.com/wildmason/aegis/compare/
|
|
763
|
+
[Unreleased]: https://github.com/wildmason/aegis/compare/v2.2.0...HEAD
|
|
764
|
+
[2.2.0]: https://github.com/wildmason/aegis/releases/tag/v2.2.0
|
|
765
|
+
[2.1.0]: https://github.com/wildmason/aegis/releases/tag/v2.1.0
|
|
766
|
+
[2.0.0]: https://github.com/wildmason/aegis/releases/tag/v2.0.0
|
|
415
767
|
[1.18.0]: https://github.com/wildmason/aegis/releases/tag/v1.18.0
|
|
416
768
|
[1.17.0]: https://github.com/wildmason/aegis/releases/tag/v1.17.0
|
|
417
769
|
[1.16.0]: https://github.com/wildmason/aegis/releases/tag/v1.16.0
|