@rak200/ui 0.2.12 → 0.2.13

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 CHANGED
@@ -86,8 +86,9 @@ components; `docs/` is what describes them.
86
86
 
87
87
  ## Status
88
88
 
89
- **v0.** Nine components — `<ui-button>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
90
- `<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select>` and `<ui-icon>` — the token layer
89
+ **v0.** Eleven components — `<ui-button>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
90
+ `<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select>`, `<ui-radio-group>`, `<ui-radio>`
91
+ and `<ui-icon>` — the token layer
91
92
  under them, and an adopted glyph set, built to the ecosystem's full quality bar rather than
92
93
  sketched: type-checked at the strictest available setting, formatted, tested in a real browser and
93
94
  **asserted against axe** for WCAG A/AA, 100% coverage and **100% mutation score**, scanned, and
package/dist/field.d.ts CHANGED
@@ -15,7 +15,12 @@ import { LitElement, type TemplateResult } from 'lit';
15
15
  *
16
16
  * **The control may sit below a wrapper**, and the same constraint is why: `<ui-input>` is
17
17
  * a box around a control that had to stay in the light DOM, so this element looks through
18
- * it. See {@link UiField.#control}.
18
+ * it. See {@link UiField.#control} — and the one wrapper it does *not* look through, which
19
+ * is a wrapper carrying a `role`, because a group is the control rather than a box for one.
20
+ *
21
+ * **Not every control can be labelled the platform's way**, and `<ui-radio-group>` is the
22
+ * first that cannot: `<label for>` reaches a labelable element and nothing else, so the
23
+ * name arrives as `aria-labelledby` there. See {@link UiField.#name}.
19
24
  *
20
25
  * @example
21
26
  * ```html
@@ -1 +1 @@
1
- {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAMjE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,OAAQ,SAAQ,UAAU;;IACnC,gBAAyB,MAAM,0BAuB7B;IAeO,iBAAiB,IAAI,IAAI;IASzB,oBAAoB,IAAI,IAAI;IAK5B,YAAY,IAAI,IAAI;IAIpB,MAAM,IAAI,cAAc;CA2GpC;AAQD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,UAAU,EAAE,OAAO,CAAC;KACvB;CACJ"}
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAMjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,OAAQ,SAAQ,UAAU;;IACnC,gBAAyB,MAAM,0BAuB7B;IAeO,iBAAiB,IAAI,IAAI;IASzB,oBAAoB,IAAI,IAAI;IAK5B,YAAY,IAAI,IAAI;IAIpB,MAAM,IAAI,cAAc;CA0JpC;AAQD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,UAAU,EAAE,OAAO,CAAC;KACvB;CACJ"}
package/dist/field.js CHANGED
@@ -18,7 +18,12 @@ let sequence = 0;
18
18
  *
19
19
  * **The control may sit below a wrapper**, and the same constraint is why: `<ui-input>` is
20
20
  * a box around a control that had to stay in the light DOM, so this element looks through
21
- * it. See {@link UiField.#control}.
21
+ * it. See {@link UiField.#control} — and the one wrapper it does *not* look through, which
22
+ * is a wrapper carrying a `role`, because a group is the control rather than a box for one.
23
+ *
24
+ * **Not every control can be labelled the platform's way**, and `<ui-radio-group>` is the
25
+ * first that cannot: `<label for>` reaches a labelable element and nothing else, so the
26
+ * name arrives as `aria-labelledby` there. See {@link UiField.#name}.
22
27
  *
23
28
  * @example
24
29
  * ```html
@@ -110,6 +115,14 @@ export class UiField extends LitElement {
110
115
  */
111
116
  #control() {
112
117
  const slotted = this.#slotted(null);
118
+ // A wrapper carrying a `role` is claiming to *be* the widget rather than to box
119
+ // one, and the descent stops there. `<ui-radio-group>` is the first: it sets
120
+ // `role="radiogroup"` on itself, and the thing this field names is that group —
121
+ // looking through it would reach the first radio, which names one option and
122
+ // leaves the group anonymous.
123
+ if (slotted?.hasAttribute('role') === true) {
124
+ return slotted;
125
+ }
113
126
  return slotted?.querySelector('input, textarea, select') ?? slotted;
114
127
  }
115
128
  /**
@@ -131,10 +144,7 @@ export class UiField extends LitElement {
131
144
  if (control.id === '') {
132
145
  control.id = `${uid}-control`;
133
146
  }
134
- const label = this.#slotted('label');
135
- if (label instanceof HTMLLabelElement) {
136
- label.htmlFor = control.id;
137
- }
147
+ this.#name(control, this.#slotted('label'), `${uid}-label`);
138
148
  const help = this.#describer(this.#slotted('help'), `${uid}-help`);
139
149
  const error = this.#describer(this.#slotted('error'), `${uid}-error`);
140
150
  // The error comes first: a screen reader announces descriptions in order, and the
@@ -158,6 +168,42 @@ export class UiField extends LitElement {
158
168
  control.setAttribute('aria-invalid', 'true');
159
169
  }
160
170
  }
171
+ /**
172
+ * Gives the control its accessible name from whatever fills the label slot.
173
+ *
174
+ * **`<label for>` is preferred wherever it reaches**, because it is the platform's own
175
+ * association and it does two things `aria-labelledby` does not: it names the control
176
+ * and it makes the label a click target for it. It only reaches a **labelable**
177
+ * element, though, and `<ui-radio-group>` is not one — a `<label for>` aimed at a
178
+ * custom element labels nothing, which axe reports at critical impact.
179
+ *
180
+ * Which elements are labelable is asked of the platform rather than listed here: those
181
+ * are exactly the ones it gives a `labels` collection to. A list kept in this file
182
+ * would be one the next labelable element ages out of, silently.
183
+ *
184
+ * The name is dropped when the slot empties, the way `aria-describedby` is: a group
185
+ * left pointing at a label that is no longer there has a dangling IDREF, which is the
186
+ * one failure mode nobody sees — the group simply stops having a name.
187
+ *
188
+ * @param fallbackId - Given to the label only on the path that needs one to point at.
189
+ * A host-supplied id survives, the same way the control's and the describers' do.
190
+ */
191
+ #name(control, label, fallbackId) {
192
+ if ('labels' in control) {
193
+ if (label instanceof HTMLLabelElement) {
194
+ label.htmlFor = control.id;
195
+ }
196
+ return;
197
+ }
198
+ if (label === undefined) {
199
+ control.removeAttribute('aria-labelledby');
200
+ return;
201
+ }
202
+ if (label.id === '') {
203
+ label.id = fallbackId;
204
+ }
205
+ control.setAttribute('aria-labelledby', label.id);
206
+ }
161
207
  /**
162
208
  * The id to describe the control by, or `undefined` when the element is absent or has
163
209
  * nothing to say. An element rendered empty — the shape a framework produces for
package/dist/field.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export { UiDialog } from './dialog.js';
4
4
  export { UiField } from './field.js';
5
5
  export { UiIcon, register } from './icon.js';
6
6
  export { UiInput, UiTextarea } from './input.js';
7
+ export { UiRadio, UiRadioGroup, type RadioOrientation } from './radio.js';
7
8
  export { UiSelect } from './select.js';
8
9
  export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, type Token, type DerivedToken, } from './tokens.js';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export { UiDialog } from './dialog.js';
4
4
  export { UiField } from './field.js';
5
5
  export { UiIcon, register } from './icon.js';
6
6
  export { UiInput, UiTextarea } from './input.js';
7
+ export { UiRadio, UiRadioGroup } from './radio.js';
7
8
  export { UiSelect } from './select.js';
8
9
  export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, } from './tokens.js';
9
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAyB,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,107 @@
1
+ import { LitElement, type TemplateResult } from 'lit';
2
+ /** Which way a group lays its options out, and announces that it did. */
3
+ export type RadioOrientation = 'vertical' | 'horizontal';
4
+ /**
5
+ * One option's control, drawn from the token layer rather than replaced.
6
+ *
7
+ * **The `<input type="radio">` is yours.** You write it, you set its `name`, `value`,
8
+ * `checked` and `disabled`, and it stays in the light DOM — the shape every control in
9
+ * this kit has, and forced by the same constraint: an ARIA relationship by IDREF does not
10
+ * cross a shadow boundary, so a control rendered in here could not be labelled by the
11
+ * `<label>` around it.
12
+ *
13
+ * **This element carries no state and no name.** It is a drawing, and everything that
14
+ * makes a set of radios *a group* — one tab stop, arrow keys that move and select, one
15
+ * value out of the set — is the platform's, because they are native radios sharing a
16
+ * `name`. Wrapping each one changes none of that: measured in this suite, a group whose
17
+ * radios sit inside custom elements is still one group.
18
+ *
19
+ * @example
20
+ * ```html
21
+ * <label>
22
+ * <ui-radio><input type="radio" name="plan" value="free" /></ui-radio>
23
+ * Free
24
+ * </label>
25
+ * ```
26
+ */
27
+ export declare class UiRadio extends LitElement {
28
+ static readonly styles: import("lit").CSSResult;
29
+ render(): TemplateResult;
30
+ }
31
+ /**
32
+ * A set of radios, laid out and named as one thing.
33
+ *
34
+ * **It hand-rolls no roving tabindex and it brings no state machine**, and that is this
35
+ * element's whole claim rather than a gap in it. Issue #15 asked which of the two it would
36
+ * be, because the APG **Radio Group** pattern — one tab stop, arrow keys that move *and*
37
+ * select, wrapping at the ends — is the first behaviour in the v0 cut that could have
38
+ * justified Zag. Neither was needed: native radios sharing a `name` **are** that pattern,
39
+ * and this suite measures it here rather than trusting it, wrappers and all.
40
+ *
41
+ * That is the test `ROADMAP.md` states, applied: Zag arrives where the platform has **no
42
+ * element** for the pattern, not merely where the pattern has state. `ui-menu` is where
43
+ * that question is open; a radio group is not.
44
+ *
45
+ * **What is left for this element is what the platform leaves out**: the layout, the
46
+ * `role` that makes a set of radios a group to a screen reader, and the group-level name,
47
+ * description and error state — which {@link UiField} wires onto *this* element rather
48
+ * than onto an option, because a `<label for>` naming one radio names one option and
49
+ * leaves the group anonymous.
50
+ *
51
+ * @example
52
+ * ```html
53
+ * <ui-field>
54
+ * <label slot="label">Plan</label>
55
+ * <ui-radio-group>
56
+ * <label><ui-radio><input type="radio" name="plan" value="free" /></ui-radio> Free</label>
57
+ * <label><ui-radio><input type="radio" name="plan" value="pro" /></ui-radio> Pro</label>
58
+ * </ui-radio-group>
59
+ * </ui-field>
60
+ * ```
61
+ */
62
+ export declare class UiRadioGroup extends LitElement {
63
+ static readonly styles: import("lit").CSSResult;
64
+ static readonly properties: {
65
+ orientation: {
66
+ type: StringConstructor;
67
+ reflect: boolean;
68
+ };
69
+ };
70
+ /**
71
+ * Which way the options are laid out.
72
+ *
73
+ * A plain field rather than the `accessor` keyword, for the reason `src/button.ts`
74
+ * gives beside its own: the browser this suite runs in does not implement
75
+ * auto-accessors, and the module would fail to parse.
76
+ */
77
+ orientation: RadioOrientation;
78
+ /**
79
+ * Marks the element a group before anything asks what it is.
80
+ *
81
+ * In `connectedCallback` rather than at first render, and the timing is the point:
82
+ * {@link UiField} reads this attribute to decide that the group — and not the first
83
+ * radio inside it — is the thing to name, and it does so on its own first update,
84
+ * which lands before this element's. Set a microtask later, the role would arrive
85
+ * after the only reader that needs it.
86
+ *
87
+ * A `role` the host wrote is never overwritten, the same way `<ui-switch>` never
88
+ * overwrites one on the control it announces.
89
+ */
90
+ connectedCallback(): void;
91
+ /**
92
+ * Keeps the announced orientation on the drawn one.
93
+ *
94
+ * Written unconditionally rather than only for the horizontal case: the default a
95
+ * screen reader assumes for a `radiogroup` is not something a component should have an
96
+ * opinion about, and the attribute costs nothing to state.
97
+ */
98
+ willUpdate(): void;
99
+ render(): TemplateResult;
100
+ }
101
+ declare global {
102
+ interface HTMLElementTagNameMap {
103
+ 'ui-radio': UiRadio;
104
+ 'ui-radio-group': UiRadioGroup;
105
+ }
106
+ }
107
+ //# sourceMappingURL=radio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radio.d.ts","sourceRoot":"","sources":["../src/radio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGjE,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,YAAY,CAAC;AAmCzD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,OAAQ,SAAQ,UAAU;IACnC,gBAAyB,MAAM,0BA8G7B;IAEO,MAAM,IAAI,cAAc;CAGpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAa,SAAQ,UAAU;IACxC,gBAAyB,MAAM,0BAiC7B;IAEF,gBAAyB,UAAU;;;;;MAEjC;IAEF;;;;;;OAMG;IACH,WAAW,EAAE,gBAAgB,CAAc;IAE3C;;;;;;;;;;;OAWG;IACM,iBAAiB,IAAI,IAAI;IAQlC;;;;;;OAMG;IACM,UAAU,IAAI,IAAI;IAIlB,MAAM,IAAI,cAAc;CAGpC;AAWD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,UAAU,EAAE,OAAO,CAAC;QACpB,gBAAgB,EAAE,YAAY,CAAC;KAClC;CACJ"}
package/dist/radio.js ADDED
@@ -0,0 +1,291 @@
1
+ import { LitElement, css, html } from 'lit';
2
+ import { reference } from './reference.js';
3
+ /**
4
+ * The size the control is drawn at, and a floor rather than a preference.
5
+ *
6
+ * The same measurement `src/checkbox.ts` carries, taken again on this element: a native
7
+ * radio is 13x13 in this engine, which is under WCAG 2.2's **2.5.8 Target Size (Minimum)**
8
+ * of 24x24 and escapes it only through that criterion's *user agent control* exception.
9
+ * Drawing it ourselves gives the exception up, so the floor becomes ours to hold, and
10
+ * `max()` is what holds it: a host who shrinks `--ui-space` cannot take this control below
11
+ * the floor without saying so in their own CSS.
12
+ *
13
+ * Written out rather than imported from the checkbox, for the reason `src/select.ts` gives
14
+ * about the box it shares with `src/input.ts` — and answered the same way, on the same
15
+ * terms: `tests/radio.test.ts` mounts both controls and asserts they agree on the size,
16
+ * the boundary, the fill and the focus ring, so something compares them and it fails when
17
+ * they drift.
18
+ */
19
+ const size = css `max(24px, calc(${reference('--ui-space')} * 3))`;
20
+ /** A mask layer covering the whole control, which the mark is then subtracted from. */
21
+ const whole = css `linear-gradient(#000, #000)`;
22
+ /**
23
+ * The mark, as a hole rather than a dot — the same decision `src/checkbox.ts` explains at
24
+ * length, reached here without a picture at all.
25
+ *
26
+ * A checkbox's tick needs a path and therefore a `data:` URI; a radio's mark is a circle,
27
+ * which is a gradient with a size. What both share is that the mark is **absent** rather
28
+ * than coloured: `mask-composite: exclude` punches it out of the accent fill, so what
29
+ * shows through is whatever the control sits on, and no colour is frozen anywhere a host
30
+ * could not override it.
31
+ */
32
+ const mark = css `radial-gradient(circle closest-side, #000 100%, transparent 100%)`;
33
+ /**
34
+ * One option's control, drawn from the token layer rather than replaced.
35
+ *
36
+ * **The `<input type="radio">` is yours.** You write it, you set its `name`, `value`,
37
+ * `checked` and `disabled`, and it stays in the light DOM — the shape every control in
38
+ * this kit has, and forced by the same constraint: an ARIA relationship by IDREF does not
39
+ * cross a shadow boundary, so a control rendered in here could not be labelled by the
40
+ * `<label>` around it.
41
+ *
42
+ * **This element carries no state and no name.** It is a drawing, and everything that
43
+ * makes a set of radios *a group* — one tab stop, arrow keys that move and select, one
44
+ * value out of the set — is the platform's, because they are native radios sharing a
45
+ * `name`. Wrapping each one changes none of that: measured in this suite, a group whose
46
+ * radios sit inside custom elements is still one group.
47
+ *
48
+ * @example
49
+ * ```html
50
+ * <label>
51
+ * <ui-radio><input type="radio" name="plan" value="free" /></ui-radio>
52
+ * Free
53
+ * </label>
54
+ * ```
55
+ */
56
+ export class UiRadio extends LitElement {
57
+ static { this.styles = css `
58
+ :host {
59
+ display: inline-flex;
60
+ }
61
+
62
+ /* Everything below reaches a control the *host* wrote, through ::slotted — the
63
+ whole shape of this component, and the same one ui-checkbox has.
64
+
65
+ appearance: none is its whole cost, and it is taken for the reason
66
+ ARCHITECTURE.md states as a debt rather than a free hand: it removes the
67
+ platform's dot, its target size and its behaviour under forced colors in one
68
+ declaration, and each of those is answered below. What it buys is a radio that
69
+ belongs beside the checkbox this kit already draws — a native 13px radio next to
70
+ a drawn 24px checkbox would argue cosmetically for a difference that is
71
+ semantic. */
72
+ ::slotted(input) {
73
+ appearance: none;
74
+ box-sizing: border-box;
75
+ /* The user agent puts 3px around a radio. Left in, it would sit inside the
76
+ host box and put the control off centre in any layout that aligns to it. */
77
+ margin: 0;
78
+ inline-size: ${size};
79
+ block-size: ${size};
80
+ /* A circle, and the one measurement here that is not a token: the shape is
81
+ what tells a radio from a checkbox before either is read, so it cannot
82
+ follow --ui-radius without a square radio becoming possible. */
83
+ border-radius: 50%;
84
+ background-color: ${reference('--ui-color-surface')};
85
+ background-repeat: no-repeat;
86
+ border: 1px solid ${reference('--ui-color-border')};
87
+ cursor: pointer;
88
+ /* The focus ring is deliberately not in this list, for the reason
89
+ src/button.ts gives beside its own: delaying the affordance that says *this
90
+ is where you are* is the opposite of what it exists to do. */
91
+ transition:
92
+ background-color ${reference('--ui-duration-state')}
93
+ ${reference('--ui-easing-state')},
94
+ border-color ${reference('--ui-duration-state')} ${reference('--ui-easing-state')};
95
+ }
96
+
97
+ /* The mark is a hole: the whole control, minus the circle, so what shows through
98
+ is whatever the control sits on. The docblock on the mask constant says why a
99
+ hole rather than a colour. */
100
+ ::slotted(input:checked) {
101
+ background-color: ${reference('--ui-color-accent')};
102
+ border-color: ${reference('--ui-color-accent')};
103
+ mask-image: ${whole}, ${mark};
104
+ mask-composite: exclude;
105
+ mask-size:
106
+ 100% 100%,
107
+ calc(${size} / 2);
108
+ mask-position: center;
109
+ mask-repeat: no-repeat;
110
+ }
111
+
112
+ /* Unselected, the boundary finishes the mix it already started — the same hover
113
+ ui-input and ui-checkbox have, because it is the same boundary. Selected, the
114
+ boundary is not what the eye is on, so the fill moves instead, the way
115
+ ui-button's does.
116
+
117
+ The disabled guard is measured rather than assumed: a disabled control still
118
+ matches :hover. */
119
+ ::slotted(input:hover:not(:disabled)) {
120
+ border-color: ${reference('--ui-color-text')};
121
+ }
122
+
123
+ ::slotted(input:checked:hover:not(:disabled)) {
124
+ background-color: ${reference('--ui-color-accent-hover')};
125
+ border-color: ${reference('--ui-color-accent-hover')};
126
+ }
127
+
128
+ /* A visible focus ring is not decoration: removing it is the single most common
129
+ way a component stops being usable by keyboard — and on a radio group it is the
130
+ only thing that says which option an arrow key just moved to. */
131
+ ::slotted(input:focus-visible) {
132
+ outline: 2px solid ${reference('--ui-color-focus')};
133
+ outline-offset: 2px;
134
+ }
135
+
136
+ ::slotted(input:disabled) {
137
+ cursor: not-allowed;
138
+ opacity: 0.5;
139
+ }
140
+
141
+ /* There is no error rule here, and its absence is the decision: what a radio group
142
+ gets wrong is the *choice*, not one option, so ui-radio-group paints the state
143
+ for the whole set. It does it by retargeting the boundary token over its own
144
+ subtree, which this rule reads without knowing anything about it. */
145
+
146
+ /* Forced colors replaces every author colour with one from the user's palette, so
147
+ the accent that says *selected* becomes the same Canvas as the surface that says
148
+ *not* — the state disappears, silently, for the people who turned the mode on to
149
+ see states more clearly. System colours are the ones forced colors keeps, so the
150
+ selected state names Highlight and the difference survives; the mark keeps
151
+ working untouched, because a mask has no colour to force.
152
+
153
+ Opacity is not a colour and is not forced, so the disabled control would still
154
+ be half-transparent against a palette chosen for contrast. GrayText is what that
155
+ mode has for *unavailable*, and it is a colour rather than a veil. */
156
+ @media (forced-colors: active) {
157
+ ::slotted(input:checked) {
158
+ background-color: Highlight;
159
+ border-color: Highlight;
160
+ }
161
+
162
+ ::slotted(input:disabled) {
163
+ border-color: GrayText;
164
+ opacity: 1;
165
+ }
166
+ }
167
+ `; }
168
+ render() {
169
+ return html `<slot></slot>`;
170
+ }
171
+ }
172
+ /**
173
+ * A set of radios, laid out and named as one thing.
174
+ *
175
+ * **It hand-rolls no roving tabindex and it brings no state machine**, and that is this
176
+ * element's whole claim rather than a gap in it. Issue #15 asked which of the two it would
177
+ * be, because the APG **Radio Group** pattern — one tab stop, arrow keys that move *and*
178
+ * select, wrapping at the ends — is the first behaviour in the v0 cut that could have
179
+ * justified Zag. Neither was needed: native radios sharing a `name` **are** that pattern,
180
+ * and this suite measures it here rather than trusting it, wrappers and all.
181
+ *
182
+ * That is the test `ROADMAP.md` states, applied: Zag arrives where the platform has **no
183
+ * element** for the pattern, not merely where the pattern has state. `ui-menu` is where
184
+ * that question is open; a radio group is not.
185
+ *
186
+ * **What is left for this element is what the platform leaves out**: the layout, the
187
+ * `role` that makes a set of radios a group to a screen reader, and the group-level name,
188
+ * description and error state — which {@link UiField} wires onto *this* element rather
189
+ * than onto an option, because a `<label for>` naming one radio names one option and
190
+ * leaves the group anonymous.
191
+ *
192
+ * @example
193
+ * ```html
194
+ * <ui-field>
195
+ * <label slot="label">Plan</label>
196
+ * <ui-radio-group>
197
+ * <label><ui-radio><input type="radio" name="plan" value="free" /></ui-radio> Free</label>
198
+ * <label><ui-radio><input type="radio" name="plan" value="pro" /></ui-radio> Pro</label>
199
+ * </ui-radio-group>
200
+ * </ui-field>
201
+ * ```
202
+ */
203
+ export class UiRadioGroup extends LitElement {
204
+ constructor() {
205
+ super(...arguments);
206
+ /**
207
+ * Which way the options are laid out.
208
+ *
209
+ * A plain field rather than the `accessor` keyword, for the reason `src/button.ts`
210
+ * gives beside its own: the browser this suite runs in does not implement
211
+ * auto-accessors, and the module would fail to parse.
212
+ */
213
+ this.orientation = 'vertical';
214
+ }
215
+ static { this.styles = css `
216
+ :host {
217
+ display: flex;
218
+ flex-direction: column;
219
+ gap: calc(${reference('--ui-space')} / 2);
220
+ }
221
+
222
+ /* Laid out by the attribute rather than by the host's own CSS, so the drawing and
223
+ the announcement cannot disagree: orientation sets both this rule and
224
+ aria-orientation, and a host who reached for flex-direction instead would
225
+ change one of the two. */
226
+ :host([orientation='horizontal']) {
227
+ flex-direction: row;
228
+ flex-wrap: wrap;
229
+ column-gap: calc(${reference('--ui-space')} * 2);
230
+ }
231
+
232
+ /* The error, painted for the whole set by retargeting the boundary token over this
233
+ subtree — which is the override surface the token layer already publishes,
234
+ pointed at the group's own descendants instead of at a page.
235
+
236
+ A custom property inherits through a shadow boundary and a selector does not, so
237
+ this is what reaches a control two elements down that this sheet cannot name:
238
+ ::slotted() stops at the group's own children, :host-context() is not
239
+ cross-engine, and :host(:has(...)) is invalid in this engine. The value still
240
+ arrives as a token reference, so a host who retunes danger retunes this too.
241
+
242
+ aria-invalid is not written here: ui-field sets it as part of the wiring it
243
+ already owns, so the message under the group and the boundary around its options
244
+ cannot disagree about whether there is an error. */
245
+ :host([aria-invalid='true']) {
246
+ --ui-color-border: ${reference('--ui-color-danger')};
247
+ }
248
+ `; }
249
+ static { this.properties = {
250
+ orientation: { type: String, reflect: true },
251
+ }; }
252
+ /**
253
+ * Marks the element a group before anything asks what it is.
254
+ *
255
+ * In `connectedCallback` rather than at first render, and the timing is the point:
256
+ * {@link UiField} reads this attribute to decide that the group — and not the first
257
+ * radio inside it — is the thing to name, and it does so on its own first update,
258
+ * which lands before this element's. Set a microtask later, the role would arrive
259
+ * after the only reader that needs it.
260
+ *
261
+ * A `role` the host wrote is never overwritten, the same way `<ui-switch>` never
262
+ * overwrites one on the control it announces.
263
+ */
264
+ connectedCallback() {
265
+ super.connectedCallback();
266
+ if (!this.hasAttribute('role')) {
267
+ this.setAttribute('role', 'radiogroup');
268
+ }
269
+ }
270
+ /**
271
+ * Keeps the announced orientation on the drawn one.
272
+ *
273
+ * Written unconditionally rather than only for the horizontal case: the default a
274
+ * screen reader assumes for a `radiogroup` is not something a component should have an
275
+ * opinion about, and the attribute costs nothing to state.
276
+ */
277
+ willUpdate() {
278
+ this.setAttribute('aria-orientation', this.orientation);
279
+ }
280
+ render() {
281
+ return html `<slot></slot>`;
282
+ }
283
+ }
284
+ // Stryker disable next-line StringLiteral: the registration runs once, at import, inside
285
+ // the warm process Stryker switches mutants in — so by the time a mutant on this line is
286
+ // active the element is already defined under the original name, and no test can observe
287
+ // the change. Outside the runner's reach, not an equivalent mutant.
288
+ customElements.define('ui-radio', UiRadio);
289
+ // Stryker disable next-line StringLiteral: the same, for the same reason.
290
+ customElements.define('ui-radio-group', UiRadioGroup);
291
+ //# sourceMappingURL=radio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radio.js","sourceRoot":"","sources":["../src/radio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAK3C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,IAAI,GAAG,GAAG,CAAA,kBAAkB,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC;AAElE,uFAAuF;AACvF,MAAM,KAAK,GAAG,GAAG,CAAA,6BAA6B,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,IAAI,GAAG,GAAG,CAAA,mEAAmE,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,OAAQ,SAAQ,UAAU;aACV,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;2BAqBd,IAAI;0BACL,IAAI;;;;;gCAKE,SAAS,CAAC,oBAAoB,CAAC;;gCAE/B,SAAS,CAAC,mBAAmB,CAAC;;;;;;mCAM3B,SAAS,CAAC,qBAAqB,CAAC;sBAC7C,SAAS,CAAC,mBAAmB,CAAC;+BACrB,SAAS,CAAC,qBAAqB,CAAC,IAAI,SAAS,CAAC,mBAAmB,CAAC;;;;;;;gCAOjE,SAAS,CAAC,mBAAmB,CAAC;4BAClC,SAAS,CAAC,mBAAmB,CAAC;0BAChC,KAAK,KAAK,IAAI;;;;uBAIjB,IAAI;;;;;;;;;;;;;4BAaC,SAAS,CAAC,iBAAiB,CAAC;;;;gCAIxB,SAAS,CAAC,yBAAyB,CAAC;4BACxC,SAAS,CAAC,yBAAyB,CAAC;;;;;;;iCAO/B,SAAS,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmCzD,CAAC;IAEO,MAAM;QACX,OAAO,IAAI,CAAA,eAAe,CAAC;IAC/B,CAAC;;AAGL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,OAAO,YAAa,SAAQ,UAAU;IAA5C;;QAwCI;;;;;;WAMG;QACH,gBAAW,GAAqB,UAAU,CAAC;IAoC/C,CAAC;aAlF4B,WAAM,GAAG,GAAG,CAAA;;;;wBAIjB,SAAS,CAAC,YAAY,CAAC;;;;;;;;;;+BAUhB,SAAS,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;iCAiBrB,SAAS,CAAC,mBAAmB,CAAC;;KAE1D,AAjC8B,CAiC7B;aAEuB,eAAU,GAAG;QAClC,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;KAC/C,AAFkC,CAEjC;IAWF;;;;;;;;;;;OAWG;IACM,iBAAiB;QACtB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAE1B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC5C,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACM,UAAU;QACf,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5D,CAAC;IAEQ,MAAM;QACX,OAAO,IAAI,CAAA,eAAe,CAAC;IAC/B,CAAC;;AAGL,yFAAyF;AACzF,yFAAyF;AACzF,yFAAyF;AACzF,oEAAoE;AACpE,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAE3C,0EAA0E;AAC1E,cAAc,CAAC,MAAM,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rak200/ui",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "Host-agnostic custom-element UI components for the rak200 ecosystem",
5
5
  "license": "MIT",
6
6
  "author": "rak200 <rak.ricardo@windowslive.com>",
@@ -45,7 +45,7 @@
45
45
  "test": "vitest run",
46
46
  "coverage": "coverage-floor",
47
47
  "scan": "rak200-scan",
48
- "mutation": "stryker run",
48
+ "mutation": "node tests/tooling/mutate-changed.mjs",
49
49
  "build": "tsc -p tsconfig.icons.json && tsc -p tsconfig.build.json",
50
50
  "storybook": "storybook dev -p 6006",
51
51
  "build-storybook": "storybook build"