@rak200/ui 0.2.14 → 0.2.16

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,9 +86,9 @@ components; `docs/` is what describes them.
86
86
 
87
87
  ## Status
88
88
 
89
- **v0.** Twelve components — `<ui-button>`, `<ui-card>`, `<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
89
+ **v0.** Fifteen components — `<ui-button>`, `<ui-card>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
90
+ `<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select>`, `<ui-radio-group>`, `<ui-radio>`,
91
+ `<ui-tooltip>`, `<ui-toaster>`, `<ui-toast>` and `<ui-icon>` — the token layer
92
92
  under them, and an adopted glyph set, built to the ecosystem's full quality bar rather than
93
93
  sketched: type-checked at the strictest available setting, formatted, tested in a real browser and
94
94
  **asserted against axe** for WCAG A/AA, 100% coverage and **100% mutation score**, scanned, and
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
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;CAmLpC;AAQD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,UAAU,EAAE,OAAO,CAAC;KACvB;CACJ"}
package/dist/field.js CHANGED
@@ -154,7 +154,14 @@ export class UiField extends LitElement {
154
154
  // format requirement, which is exactly the error suggestion the user needs in order
155
155
  // to recover (WCAG 3.3.3) — dropping it at the moment it becomes useful is the
156
156
  // opposite of helping.
157
- const described = [error, help].filter((id) => id !== undefined);
157
+ //
158
+ // And a description this field did not write survives all of it, which is the same
159
+ // courtesy the `id` above gets and for the same reason: something this element
160
+ // cannot see may have put it there. `<ui-tooltip>` is the first to do so, and it
161
+ // was measured being erased — not on the first association, which runs before the
162
+ // tooltip wires, but on the next one, when a re-render moves the help text and the
163
+ // list is rebuilt from what this field alone knows about.
164
+ const described = [error, help, ...this.#foreign(control, uid, [error, help])].filter((id) => id !== undefined);
158
165
  if (described.length > 0) {
159
166
  control.setAttribute('aria-describedby', described.join(' '));
160
167
  }
@@ -168,6 +175,20 @@ export class UiField extends LitElement {
168
175
  control.setAttribute('aria-invalid', 'true');
169
176
  }
170
177
  }
178
+ /**
179
+ * The ids already describing `control` that this field is not responsible for.
180
+ *
181
+ * Everything it could have written is excluded rather than remembered: the two ids it
182
+ * generates from `uid`, and whatever the help and error elements are called right now,
183
+ * which may be ids the host supplied. What is left belongs to somebody else and is
184
+ * carried forward in place rather than dropped.
185
+ */
186
+ #foreign(control, uid, mine) {
187
+ const written = new Set([...mine, `${uid}-help`, `${uid}-error`]);
188
+ return (control.getAttribute('aria-describedby') ?? '')
189
+ .split(' ')
190
+ .filter((id) => id !== '' && !written.has(id));
191
+ }
171
192
  /**
172
193
  * Gives the control its accessible name from whatever fills the label slot.
173
194
  *
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
@@ -7,5 +7,7 @@ export { UiIcon, register } from './icon.js';
7
7
  export { UiInput, UiTextarea } from './input.js';
8
8
  export { UiRadio, UiRadioGroup, type RadioOrientation } from './radio.js';
9
9
  export { UiSelect } from './select.js';
10
+ export { UiToast, UiToaster, type ToastVariant } from './toast.js';
11
+ export { UiTooltip } from './tooltip.js';
10
12
  export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, type Token, type DerivedToken, } from './tokens.js';
11
13
  //# 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,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,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"}
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,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,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,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,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
@@ -7,5 +7,7 @@ export { UiIcon, register } from './icon.js';
7
7
  export { UiInput, UiTextarea } from './input.js';
8
8
  export { UiRadio, UiRadioGroup } from './radio.js';
9
9
  export { UiSelect } from './select.js';
10
+ export { UiToast, UiToaster } from './toast.js';
11
+ export { UiTooltip } from './tooltip.js';
10
12
  export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, } from './tokens.js';
11
13
  //# 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,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,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,EAAE,OAAO,EAAE,SAAS,EAAqB,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,158 @@
1
+ import { LitElement, type TemplateResult } from 'lit';
2
+ /** What a toast is telling the reader, which decides how it looks *and* how it is announced. */
3
+ export type ToastVariant = 'info' | 'success' | 'warning' | 'danger';
4
+ /**
5
+ * The live regions a toast is announced through, and the box the stack is drawn in.
6
+ *
7
+ * **One container, two regions, and the count is the number of politenesses rather than
8
+ * the number of messages.** A live region has to be in the document *before* the text is
9
+ * put into it — a region that arrives with its content already inside is the single most
10
+ * common way a toast is never announced — so the regions are rendered on connection and
11
+ * never removed, and a toast is added to whichever one its variant names.
12
+ *
13
+ * That is the whole reason there are two. `aria-live` cannot vary per message inside one
14
+ * region, and giving each toast its own `aria-live` would make every message its own
15
+ * region root, which is the shape this component exists to avoid. Two regions that outlive
16
+ * every message answer *politeness is per toast* without ever creating one.
17
+ *
18
+ * **Neither region carries `role="alert"` or `role="status"`**, and that is not a shortcut.
19
+ * Both roles imply `aria-atomic="true"`, so every arrival re-announces the whole stack —
20
+ * three toasts up means the third one reads all three. `aria-live` alone leaves
21
+ * `aria-atomic` at its `false` default, which announces the toast that arrived and nothing
22
+ * else. The semantics of `role="alert"` are `aria-live="assertive"` plus that atomicity;
23
+ * this keeps the half that is right for a stack.
24
+ *
25
+ * ## Where it sits
26
+ *
27
+ * A `popover`, for the reason `<ui-tooltip>` is one: the top layer escapes every stacking
28
+ * context and every `overflow: hidden` above it, so this package still names no `z-index`
29
+ * and opens no layering category — the third overlay in a row not to. `manual`, because an
30
+ * `auto` popover light-dismisses, and a toast that vanished on the next click somewhere
31
+ * else would be a notice the user never read.
32
+ *
33
+ * It is shown once, on connection, and never hidden — which costs the one thing promotion
34
+ * would have bought. A modal `<dialog>` opened *after* the toaster connects sits above it
35
+ * in the top layer, and re-promoting the stack per message would fix that ordering and
36
+ * break the announcement, by taking the region out of the tree and putting it back with
37
+ * content already in it. The trade is not close, and it is not the real limit anyway: a
38
+ * modal makes the rest of the document **inert**, so a toaster outside an open modal is out
39
+ * of the accessibility tree whichever layer it is in. `docs/toast.md` says where to put one
40
+ * for a page that toasts from inside a dialog.
41
+ *
42
+ * @example
43
+ * ```html
44
+ * <ui-toaster></ui-toaster>
45
+ * ```
46
+ */
47
+ export declare class UiToaster extends LitElement {
48
+ static readonly styles: import("lit").CSSResult;
49
+ connectedCallback(): void;
50
+ render(): TemplateResult;
51
+ }
52
+ /**
53
+ * One message in a {@link UiToaster}.
54
+ *
55
+ * ## The variant decides three things, and that is the design
56
+ *
57
+ * `variant` is not only how the toast looks. It decides how urgently the message is
58
+ * announced and whether it expires, because those three answers are one answer: **an error
59
+ * is the message the reader has to see, hear, and keep.** So `danger` is announced
60
+ * assertively and never times out, and every other variant is polite and does.
61
+ *
62
+ * The alternative was a second attribute for politeness, and it was rejected for what it
63
+ * makes possible rather than for what it costs: two knobs can disagree, and a polite error
64
+ * or an assertive success is a defect nothing reports. The issue behind this component
65
+ * asked for `assertive` to be *reserved for errors* — a rule the API can hold is worth more
66
+ * than a rule the documentation states. What it gives up is stated in `docs/toast.md`:
67
+ * there is no way to write an assertive success.
68
+ *
69
+ * **The colour is redundant and must stay redundant.** WCAG 1.4.1 asks that colour never be
70
+ * the only carrier of information, and here it never is: the message says what happened and
71
+ * the edge agrees with it. That is also what makes the component correct under
72
+ * `forced-colors`, where the edge goes flat and nothing is lost.
73
+ *
74
+ * ## Dismissal
75
+ *
76
+ * Manual always, timed unless the toast is an error or `duration` is `0`. There is
77
+ * deliberately no way to remove the dismiss button: a toast that cannot be dismissed *and*
78
+ * does not expire is a permanent obstruction, and that combination is one attribute away in
79
+ * every kit that offers the knob.
80
+ *
81
+ * The timer stops while the pointer is over the toast or the focus is inside it, and starts
82
+ * again when both have left — a notice must not expire while it is being read, which is
83
+ * WCAG 2.2.1 aimed at the only time limit this component has.
84
+ *
85
+ * **A dismissed toast removes itself.** `ui-dismiss` is dispatched first, while the element
86
+ * is still in the tree so the event can reach a listener above it; a framework rendering a
87
+ * list of toasts drops the entry there rather than leaving the element to be put back.
88
+ *
89
+ * @example
90
+ * ```html
91
+ * <ui-toaster>
92
+ * <ui-toast variant="success">Saved.</ui-toast>
93
+ * </ui-toaster>
94
+ * ```
95
+ */
96
+ export declare class UiToast extends LitElement {
97
+ #private;
98
+ static readonly styles: import("lit").CSSResult;
99
+ static readonly properties: {
100
+ variant: {
101
+ type: StringConstructor;
102
+ reflect: boolean;
103
+ };
104
+ duration: {
105
+ type: NumberConstructor;
106
+ };
107
+ dismissLabel: {
108
+ type: StringConstructor;
109
+ attribute: string;
110
+ };
111
+ };
112
+ /**
113
+ * What this toast is telling the reader.
114
+ *
115
+ * Reflected, because it is what the stylesheet selects on — and because it is the
116
+ * attribute a host reads back to find out how the toast will behave.
117
+ *
118
+ * A plain field rather than the `accessor` keyword, for the reason `src/button.ts`
119
+ * states beside its own.
120
+ */
121
+ variant: ToastVariant;
122
+ /**
123
+ * How long the toast waits before dismissing itself, in milliseconds; `0` never.
124
+ *
125
+ * Ignored when the variant is `danger`, which never expires. Not a token, for the
126
+ * reason the default carries: every duration name collapses under reduced motion, and
127
+ * a dwell time that collapsed would take the notice away from the reader who asked for
128
+ * less movement.
129
+ */
130
+ duration: number;
131
+ /**
132
+ * The accessible name of the dismiss button.
133
+ *
134
+ * This package's one user-facing string, and an attribute precisely because it is one:
135
+ * a page that is not in English replaces it rather than forking the component. It names
136
+ * the button and never appears on screen, which is why there is no way to leave it
137
+ * empty — an unnamed control is the failure this default exists to prevent.
138
+ */
139
+ dismissLabel: string;
140
+ connectedCallback(): void;
141
+ disconnectedCallback(): void;
142
+ willUpdate(): void;
143
+ render(): TemplateResult;
144
+ /**
145
+ * Runs the exit, announces it, and takes the toast off the page.
146
+ *
147
+ * Bound as a field so it can be handed to a listener and called as a method without
148
+ * either losing `this`.
149
+ */
150
+ readonly dismiss: () => void;
151
+ }
152
+ declare global {
153
+ interface HTMLElementTagNameMap {
154
+ 'ui-toaster': UiToaster;
155
+ 'ui-toast': UiToast;
156
+ }
157
+ }
158
+ //# sourceMappingURL=toast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../src/toast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGjE,gGAAgG;AAChG,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAqBrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qBAAa,SAAU,SAAQ,UAAU;IACrC,gBAAyB,MAAM,0BAiD7B;IAEO,iBAAiB,IAAI,IAAI;IAazB,MAAM,IAAI,cAAc;CAMpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,OAAQ,SAAQ,UAAU;;IACnC,gBAAyB,MAAM,0BAqH7B;IAEF,gBAAyB,UAAU;;;;;;;;;;;;MAIjC;IAEF;;;;;;;;OAQG;IACH,OAAO,EAAE,YAAY,CAAU;IAE/B;;;;;;;OAOG;IACH,QAAQ,SAAS;IAEjB;;;;;;;OAOG;IACH,YAAY,SAAa;IAoChB,iBAAiB,IAAI,IAAI;IAwBzB,oBAAoB,IAAI,IAAI;IAO5B,UAAU,IAAI,IAAI;IAQlB,MAAM,IAAI,cAAc;IAsBjC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,QAAO,IAAI,CAEzB;CA0DL;AAWD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,YAAY,EAAE,SAAS,CAAC;QACxB,UAAU,EAAE,OAAO,CAAC;KACvB;CACJ"}
package/dist/toast.js ADDED
@@ -0,0 +1,494 @@
1
+ import { LitElement, css, html } from 'lit';
2
+ import { reference } from './reference.js';
3
+ /** The class the exit is carried by, on the toast for exactly as long as the exit runs. */
4
+ const closing = 'closing';
5
+ /**
6
+ * How long a toast that is not an error stays, in milliseconds.
7
+ *
8
+ * **Deliberately not a token, and that is the sharpest thing in this file.** Every
9
+ * `--ui-duration-*` name is collapsed to `0.01ms` by `tokenStyleSheet()` when the reader
10
+ * has asked for less motion — which is right for a transition and catastrophic for a dwell
11
+ * time: a notice would vanish before it could be read, and only for the people least able
12
+ * to chase it. A duration a component *waits* is not motion, so it is an attribute with a
13
+ * default rather than a name in the scale.
14
+ *
15
+ * Five seconds is read off a rate rather than chosen: a short notice is eight or ten words,
16
+ * and 180 words a minute is the unhurried end of silent reading — about three seconds — with
17
+ * the rest of it spent noticing that something arrived at all.
18
+ */
19
+ const dwell = 5000;
20
+ /**
21
+ * The live regions a toast is announced through, and the box the stack is drawn in.
22
+ *
23
+ * **One container, two regions, and the count is the number of politenesses rather than
24
+ * the number of messages.** A live region has to be in the document *before* the text is
25
+ * put into it — a region that arrives with its content already inside is the single most
26
+ * common way a toast is never announced — so the regions are rendered on connection and
27
+ * never removed, and a toast is added to whichever one its variant names.
28
+ *
29
+ * That is the whole reason there are two. `aria-live` cannot vary per message inside one
30
+ * region, and giving each toast its own `aria-live` would make every message its own
31
+ * region root, which is the shape this component exists to avoid. Two regions that outlive
32
+ * every message answer *politeness is per toast* without ever creating one.
33
+ *
34
+ * **Neither region carries `role="alert"` or `role="status"`**, and that is not a shortcut.
35
+ * Both roles imply `aria-atomic="true"`, so every arrival re-announces the whole stack —
36
+ * three toasts up means the third one reads all three. `aria-live` alone leaves
37
+ * `aria-atomic` at its `false` default, which announces the toast that arrived and nothing
38
+ * else. The semantics of `role="alert"` are `aria-live="assertive"` plus that atomicity;
39
+ * this keeps the half that is right for a stack.
40
+ *
41
+ * ## Where it sits
42
+ *
43
+ * A `popover`, for the reason `<ui-tooltip>` is one: the top layer escapes every stacking
44
+ * context and every `overflow: hidden` above it, so this package still names no `z-index`
45
+ * and opens no layering category — the third overlay in a row not to. `manual`, because an
46
+ * `auto` popover light-dismisses, and a toast that vanished on the next click somewhere
47
+ * else would be a notice the user never read.
48
+ *
49
+ * It is shown once, on connection, and never hidden — which costs the one thing promotion
50
+ * would have bought. A modal `<dialog>` opened *after* the toaster connects sits above it
51
+ * in the top layer, and re-promoting the stack per message would fix that ordering and
52
+ * break the announcement, by taking the region out of the tree and putting it back with
53
+ * content already in it. The trade is not close, and it is not the real limit anyway: a
54
+ * modal makes the rest of the document **inert**, so a toaster outside an open modal is out
55
+ * of the accessibility tree whichever layer it is in. `docs/toast.md` says where to put one
56
+ * for a page that toasts from inside a dialog.
57
+ *
58
+ * @example
59
+ * ```html
60
+ * <ui-toaster></ui-toaster>
61
+ * ```
62
+ */
63
+ export class UiToaster extends LitElement {
64
+ static { this.styles = css `
65
+ :host {
66
+ /* The user agent's own popover box is a centred panel with a border and a
67
+ padding of its own. All of it is displaced, because what this is is a
68
+ corner of the viewport rather than a dialog. */
69
+ position: fixed;
70
+ inset-block: auto 0;
71
+ inset-inline: auto 0;
72
+ margin: 0;
73
+ border: none;
74
+ background: none;
75
+ /* The stack is wider than a phrase and narrower than the page. Capped in the
76
+ space token so it scales with everything else a host retunes, and floored
77
+ against the viewport so a narrow screen gets the margin rather than a
78
+ horizontal scrollbar. */
79
+ box-sizing: border-box;
80
+ inline-size: min(100%, calc(${reference('--ui-space')} * 44));
81
+ padding: calc(${reference('--ui-space')} * 2);
82
+ /* The box covers a corner of the page whether or not it holds anything, so it
83
+ takes no clicks; a toast puts them back on itself. Without this a page has a
84
+ dead zone in the corner for as long as a toaster is on it. */
85
+ pointer-events: none;
86
+ }
87
+
88
+ /* display is named under :popover-open and nowhere else, and that is the rule
89
+ src/dialog.ts states beside its own: the user agent hides a closed popover with
90
+ display: none, and a rule here that names display unconditionally outranks it —
91
+ so a toaster whose popover never opened would render in the page flow instead of
92
+ being invisible. */
93
+ :host(:popover-open) {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: ${reference('--ui-space')};
97
+ }
98
+
99
+ /* The polite region is first and the assertive one second, so an error sits nearest
100
+ the corner the stack grows from — which is where the eye already is. The order is
101
+ a rendering decision and nothing else: what a screen reader does with either
102
+ region is decided by its aria-live, not by which came first. */
103
+ div {
104
+ display: flex;
105
+ flex-direction: column;
106
+ gap: ${reference('--ui-space')};
107
+ min-inline-size: 0;
108
+ }
109
+
110
+ slot::slotted(*) {
111
+ pointer-events: auto;
112
+ }
113
+ `; }
114
+ connectedCallback() {
115
+ super.connectedCallback();
116
+ // Written here rather than in the template, because it is the host's own attribute
117
+ // and Lit renders into the shadow root. Set before it is shown, which is the order
118
+ // showPopover() requires: it throws on an element carrying no popover attribute.
119
+ this.setAttribute('popover', 'manual');
120
+ // No guard, and none is reachable: removing a showing popover from the document
121
+ // hides it, so an element arriving at connectedCallback is never already open.
122
+ this.showPopover();
123
+ }
124
+ render() {
125
+ return html `
126
+ <div aria-live="polite" part="polite"><slot name="polite"></slot></div>
127
+ <div aria-live="assertive" part="assertive"><slot name="assertive"></slot></div>
128
+ `;
129
+ }
130
+ }
131
+ /**
132
+ * One message in a {@link UiToaster}.
133
+ *
134
+ * ## The variant decides three things, and that is the design
135
+ *
136
+ * `variant` is not only how the toast looks. It decides how urgently the message is
137
+ * announced and whether it expires, because those three answers are one answer: **an error
138
+ * is the message the reader has to see, hear, and keep.** So `danger` is announced
139
+ * assertively and never times out, and every other variant is polite and does.
140
+ *
141
+ * The alternative was a second attribute for politeness, and it was rejected for what it
142
+ * makes possible rather than for what it costs: two knobs can disagree, and a polite error
143
+ * or an assertive success is a defect nothing reports. The issue behind this component
144
+ * asked for `assertive` to be *reserved for errors* — a rule the API can hold is worth more
145
+ * than a rule the documentation states. What it gives up is stated in `docs/toast.md`:
146
+ * there is no way to write an assertive success.
147
+ *
148
+ * **The colour is redundant and must stay redundant.** WCAG 1.4.1 asks that colour never be
149
+ * the only carrier of information, and here it never is: the message says what happened and
150
+ * the edge agrees with it. That is also what makes the component correct under
151
+ * `forced-colors`, where the edge goes flat and nothing is lost.
152
+ *
153
+ * ## Dismissal
154
+ *
155
+ * Manual always, timed unless the toast is an error or `duration` is `0`. There is
156
+ * deliberately no way to remove the dismiss button: a toast that cannot be dismissed *and*
157
+ * does not expire is a permanent obstruction, and that combination is one attribute away in
158
+ * every kit that offers the knob.
159
+ *
160
+ * The timer stops while the pointer is over the toast or the focus is inside it, and starts
161
+ * again when both have left — a notice must not expire while it is being read, which is
162
+ * WCAG 2.2.1 aimed at the only time limit this component has.
163
+ *
164
+ * **A dismissed toast removes itself.** `ui-dismiss` is dispatched first, while the element
165
+ * is still in the tree so the event can reach a listener above it; a framework rendering a
166
+ * list of toasts drops the entry there rather than leaving the element to be put back.
167
+ *
168
+ * @example
169
+ * ```html
170
+ * <ui-toaster>
171
+ * <ui-toast variant="success">Saved.</ui-toast>
172
+ * </ui-toaster>
173
+ * ```
174
+ */
175
+ export class UiToast extends LitElement {
176
+ constructor() {
177
+ super(...arguments);
178
+ /**
179
+ * What this toast is telling the reader.
180
+ *
181
+ * Reflected, because it is what the stylesheet selects on — and because it is the
182
+ * attribute a host reads back to find out how the toast will behave.
183
+ *
184
+ * A plain field rather than the `accessor` keyword, for the reason `src/button.ts`
185
+ * states beside its own.
186
+ */
187
+ this.variant = 'info';
188
+ /**
189
+ * How long the toast waits before dismissing itself, in milliseconds; `0` never.
190
+ *
191
+ * Ignored when the variant is `danger`, which never expires. Not a token, for the
192
+ * reason the default carries: every duration name collapses under reduced motion, and
193
+ * a dwell time that collapsed would take the notice away from the reader who asked for
194
+ * less movement.
195
+ */
196
+ this.duration = dwell;
197
+ /**
198
+ * The accessible name of the dismiss button.
199
+ *
200
+ * This package's one user-facing string, and an attribute precisely because it is one:
201
+ * a page that is not in English replaces it rather than forking the component. It names
202
+ * the button and never appears on screen, which is why there is no way to leave it
203
+ * empty — an unnamed control is the failure this default exists to prevent.
204
+ */
205
+ this.dismissLabel = 'Dismiss';
206
+ /** The pending auto-dismissal; `0` when there is none, which is what `clearTimeout` reads. */
207
+ this.#timer = 0;
208
+ /**
209
+ * Takes every listener off at once, for the reason `src/tooltip.ts` gives beside its
210
+ * own: half of these leak invisibly if they are removed by name, and one `abort()` is
211
+ * one thing to get right.
212
+ */
213
+ this.#listeners = new AbortController();
214
+ /** Holds the clock while the toast is being read, which is the whole of WCAG 2.2.1 here. */
215
+ this.#hold = () => {
216
+ this.#stop();
217
+ };
218
+ /** Starts it again once the pointer and the focus have both left. */
219
+ this.#resume = () => {
220
+ this.#start();
221
+ };
222
+ /**
223
+ * Dismisses on Escape.
224
+ *
225
+ * Bound to this element rather than to the document, which is the opposite of what
226
+ * `<ui-tooltip>` needed and for the opposite reason: a tooltip is shown by a pointer
227
+ * that leaves the focus elsewhere, and a toast is only reached by putting focus in it.
228
+ * A document listener here would dismiss a toast the reader was not looking at.
229
+ */
230
+ this.#onKey = (event) => {
231
+ if (event.key === 'Escape') {
232
+ this.dismiss();
233
+ }
234
+ };
235
+ /**
236
+ * Runs the exit, announces it, and takes the toast off the page.
237
+ *
238
+ * Bound as a field so it can be handed to a listener and called as a method without
239
+ * either losing `this`.
240
+ */
241
+ this.dismiss = () => {
242
+ void this.#exit();
243
+ };
244
+ }
245
+ static { this.styles = css `
246
+ :host {
247
+ display: flex;
248
+ align-items: flex-start;
249
+ gap: ${reference('--ui-space')};
250
+ box-sizing: border-box;
251
+ padding: ${reference('--ui-space')} calc(${reference('--ui-space')} * 1.5);
252
+ border: 1px solid ${reference('--ui-color-border')};
253
+ /* The edge, and the shorthand after the border above rather than a colour
254
+ beside it: the width and the style are this rule's as much as the hue is.
255
+ info reads the accent, which is the colour a page already answers with. */
256
+ border-inline-start: calc(${reference('--ui-space')} / 2) solid
257
+ ${reference('--ui-color-accent')};
258
+ border-radius: ${reference('--ui-radius')};
259
+ background: ${reference('--ui-color-surface')};
260
+ color: ${reference('--ui-color-text')};
261
+ font-family: ${reference('--ui-font')};
262
+ /* Lifted, because it sits over the page rather than in it — and bounded as
263
+ well as lifted, for the reason src/card.ts gives: a shadow is one value in
264
+ both schemes and does almost nothing on a dark page, where the derived
265
+ boundary is what separates the surface from what is under it. */
266
+ box-shadow: ${reference('--ui-elevation-raised')};
267
+ /* The toaster takes no clicks so it does not steal a corner of the page. This
268
+ is the part that is not the corner. */
269
+ pointer-events: auto;
270
+
271
+ opacity: 1;
272
+ translate: 0 0;
273
+ transition:
274
+ opacity ${reference('--ui-duration-state')} ${reference('--ui-easing-enter')},
275
+ translate ${reference('--ui-duration-state')} ${reference('--ui-easing-enter')};
276
+ }
277
+
278
+ :host([variant='success']) {
279
+ border-inline-start-color: ${reference('--ui-color-success')};
280
+ }
281
+
282
+ :host([variant='warning']) {
283
+ border-inline-start-color: ${reference('--ui-color-warning')};
284
+ }
285
+
286
+ :host([variant='danger']) {
287
+ border-inline-start-color: ${reference('--ui-color-danger')};
288
+ }
289
+
290
+ /* Where the entrance comes from. Without it there is nothing to transition out of:
291
+ a toast goes from not rendered to laid out in one frame, and a transition needs a
292
+ before-change style that an unrendered element does not have. */
293
+ @starting-style {
294
+ :host {
295
+ opacity: 0;
296
+ translate: 0 calc(${reference('--ui-space')} * 2);
297
+ }
298
+ }
299
+
300
+ /* The exit, driven from a class rather than from the element leaving: the removal
301
+ is what this is waiting for, so an exit keyed on it would never run. */
302
+ :host(.closing) {
303
+ opacity: 0;
304
+ translate: 0 calc(${reference('--ui-space')} * 2);
305
+ transition-timing-function: ${reference('--ui-easing-exit')};
306
+ }
307
+
308
+ div {
309
+ /* The message takes the room the button does not, and may wrap inside it — a
310
+ flex item refuses to shrink below its content without this. */
311
+ flex: 1;
312
+ min-inline-size: 0;
313
+ }
314
+
315
+ button {
316
+ flex: none;
317
+ display: grid;
318
+ place-items: center;
319
+ /* WCAG 2.2 2.5.8 asks 24 by 24, and an icon-only button is the shape that
320
+ lands under it: the glyph is drawn at 1.25em, which is 20 pixels at the
321
+ default text size. The platform floors nothing, so this does. */
322
+ min-inline-size: 24px;
323
+ min-block-size: 24px;
324
+ margin: 0;
325
+ padding: 0;
326
+ border: none;
327
+ border-radius: ${reference('--ui-radius')};
328
+ background: none;
329
+ /* Inherited so the glyph, sized in em, is the size of the text beside it. */
330
+ font: inherit;
331
+ color: ${reference('--ui-color-text-muted')};
332
+ cursor: pointer;
333
+ transition: background-color ${reference('--ui-duration-state')}
334
+ ${reference('--ui-easing-state')};
335
+ }
336
+
337
+ /* A hover and a focus ring, and no pressed step — which is where every component
338
+ here stops except ui-button. Pressing a button *is* the action, so its own
339
+ pressed colour is the feedback for the thing being done; dismissing a notice is
340
+ secondary, and a third colour for it would be a rule the suite cannot reach.
341
+ Measured: a scripted pointer cannot be driven onto a box in the top layer from
342
+ inside the frame the suite runs in, so an :active here would ship unmeasured. */
343
+ button:hover {
344
+ background: ${reference('--ui-color-hover')};
345
+ }
346
+
347
+ /* A visible focus ring is not decoration: removing it is the single most common way
348
+ a component stops being usable by keyboard. */
349
+ button:focus-visible {
350
+ outline: 2px solid ${reference('--ui-color-focus')};
351
+ outline-offset: 2px;
352
+ }
353
+
354
+ /* One dimension, not two, and the mutation floor is what proved it: an svg carrying
355
+ a viewBox is a replaced element with an intrinsic ratio, so a square glyph given
356
+ a width takes the same height from the ratio. Writing both was the same fact
357
+ twice — and a fact written twice is a rule whose removal nothing can observe. */
358
+ svg {
359
+ inline-size: ${reference('--ui-icon-size')};
360
+ stroke-width: ${reference('--ui-icon-stroke')};
361
+ }
362
+ `; }
363
+ static { this.properties = {
364
+ variant: { type: String, reflect: true },
365
+ duration: { type: Number },
366
+ dismissLabel: { type: String, attribute: 'dismiss-label' },
367
+ }; }
368
+ /** The pending auto-dismissal; `0` when there is none, which is what `clearTimeout` reads. */
369
+ #timer;
370
+ /**
371
+ * Takes every listener off at once, for the reason `src/tooltip.ts` gives beside its
372
+ * own: half of these leak invisibly if they are removed by name, and one `abort()` is
373
+ * one thing to get right.
374
+ */
375
+ #listeners;
376
+ /** Holds the clock while the toast is being read, which is the whole of WCAG 2.2.1 here. */
377
+ #hold;
378
+ /** Starts it again once the pointer and the focus have both left. */
379
+ #resume;
380
+ /**
381
+ * Dismisses on Escape.
382
+ *
383
+ * Bound to this element rather than to the document, which is the opposite of what
384
+ * `<ui-tooltip>` needed and for the opposite reason: a tooltip is shown by a pointer
385
+ * that leaves the focus elsewhere, and a toast is only reached by putting focus in it.
386
+ * A document listener here would dismiss a toast the reader was not looking at.
387
+ */
388
+ #onKey;
389
+ connectedCallback() {
390
+ super.connectedCallback();
391
+ // Synchronously, and that is the point of doing it here as well as on update: a
392
+ // custom element upgrades with its attributes already read, so the toast is in the
393
+ // region its variant names in the same task it was inserted — nothing ever observes
394
+ // it in the other one, and no message is announced twice by being moved.
395
+ this.#route();
396
+ // A fresh one per connection: an aborted signal stays aborted, so a toast a
397
+ // framework moved in the tree would come back deaf.
398
+ this.#listeners = new AbortController();
399
+ const watched = { signal: this.#listeners.signal };
400
+ this.addEventListener('pointerenter', this.#hold, watched);
401
+ this.addEventListener('pointerleave', this.#resume, watched);
402
+ this.addEventListener('focusin', this.#hold, watched);
403
+ this.addEventListener('focusout', this.#resume, watched);
404
+ this.addEventListener('keydown', this.#onKey, watched);
405
+ this.#start();
406
+ }
407
+ disconnectedCallback() {
408
+ this.#stop();
409
+ this.#listeners.abort();
410
+ super.disconnectedCallback();
411
+ }
412
+ willUpdate() {
413
+ // Unconditionally rather than on a change to `variant`: routing is writing the
414
+ // value the variant already implies, so doing it every update and doing it only
415
+ // when the variant moved are the same write — and the guard would be a branch no
416
+ // input can tell apart from its absence.
417
+ this.#route();
418
+ }
419
+ render() {
420
+ return html `
421
+ <div part="message"><slot></slot></div>
422
+ <button
423
+ type="button"
424
+ part="dismiss"
425
+ aria-label=${this.dismissLabel}
426
+ @click=${this.dismiss}
427
+ >
428
+ <svg
429
+ viewBox="0 0 24 24"
430
+ fill="none"
431
+ stroke="currentColor"
432
+ stroke-linecap="round"
433
+ aria-hidden="true"
434
+ >
435
+ <path d="M18 6 6 18M6 6l12 12" />
436
+ </svg>
437
+ </button>
438
+ `;
439
+ }
440
+ /**
441
+ * Puts the toast in the region its variant names.
442
+ *
443
+ * The `slot` attribute is the routing, which is what makes it observable: a host can
444
+ * read back which region a toast landed in without this component exporting a second
445
+ * name for it. The regions are `<ui-toaster>`'s, and a toast outside one simply carries
446
+ * an attribute that assigns it to nothing.
447
+ */
448
+ #route() {
449
+ this.slot = this.variant === 'danger' ? 'assertive' : 'polite';
450
+ }
451
+ /** Starts the clock, unless this toast has none to start. */
452
+ #start() {
453
+ // An error is never taken away from the reader, and `0` is how a host asks for the
454
+ // same thing on any other variant.
455
+ if (this.variant === 'danger' || this.duration === 0) {
456
+ return;
457
+ }
458
+ this.#stop();
459
+ this.#timer = window.setTimeout(this.dismiss, this.duration);
460
+ }
461
+ /** Stops it, whether or not one was running. */
462
+ #stop() {
463
+ window.clearTimeout(this.#timer);
464
+ this.#timer = 0;
465
+ }
466
+ /** Fades the toast out, tells whoever is listening, and then removes it. */
467
+ async #exit() {
468
+ // Dismissed twice — the button clicked while the clock was already up, say — is one
469
+ // dismissal. Without this the second pass fires a second `ui-dismiss` for a toast
470
+ // that has already left.
471
+ if (this.classList.contains(closing)) {
472
+ return;
473
+ }
474
+ this.#stop();
475
+ this.classList.add(closing);
476
+ // `finished` rejects when an animation is cancelled, so what this waits for is
477
+ // settlement rather than fulfilment. `getAnimations` flushes the pending style
478
+ // change before it answers, so the transition the line above started is in the list.
479
+ await Promise.allSettled(this.getAnimations().map((animation) => animation.finished));
480
+ // Before the removal, and that order is forced rather than preferred: a listener on
481
+ // the toaster or on anything above it hears a bubbling event only while the element
482
+ // is still in the tree.
483
+ this.dispatchEvent(new Event('ui-dismiss', { bubbles: true, composed: true }));
484
+ this.remove();
485
+ }
486
+ }
487
+ // Stryker disable next-line StringLiteral: the registration runs once, at import, inside
488
+ // the warm process Stryker switches mutants in — so by the time a mutant on this line is
489
+ // active the element is already defined under the original name, and no test can observe
490
+ // the change. Outside the runner's reach, not an equivalent mutant.
491
+ customElements.define('ui-toaster', UiToaster);
492
+ // Stryker disable next-line StringLiteral: as above.
493
+ customElements.define('ui-toast', UiToast);
494
+ //# sourceMappingURL=toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.js","sourceRoot":"","sources":["../src/toast.ts"],"names":[],"mappings":"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"}
package/dist/tokens.d.ts CHANGED
@@ -31,7 +31,7 @@
31
31
  * it does break is code that **enumerates** the set, an exhaustive `Record<Token, string>`
32
32
  * above all. The supported shape is the partial map, which is what a theme is anyway.
33
33
  */
34
- export declare const tokens: readonly ["--ui-color-accent", "--ui-color-accent-contrast", "--ui-color-surface", "--ui-color-text", "--ui-color-focus", "--ui-color-danger", "--ui-color-scrim", "--ui-radius", "--ui-space", "--ui-font", "--ui-duration-100", "--ui-easing-state", "--ui-easing-enter", "--ui-easing-exit", "--ui-icon-size", "--ui-icon-stroke", "--ui-elevation-100"];
34
+ export declare const tokens: readonly ["--ui-color-accent", "--ui-color-accent-contrast", "--ui-color-surface", "--ui-color-text", "--ui-color-focus", "--ui-color-danger", "--ui-color-success", "--ui-color-warning", "--ui-color-scrim", "--ui-radius", "--ui-space", "--ui-font", "--ui-duration-100", "--ui-easing-state", "--ui-easing-enter", "--ui-easing-exit", "--ui-icon-size", "--ui-icon-stroke", "--ui-elevation-100"];
35
35
  /** A CSS custom property this package defines and gives a default. */
36
36
  export type Token = (typeof tokens)[number];
37
37
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,MAAM,6VA4ET,CAAC;AAEX,sEAAsE;AACtE,MAAM,MAAM,KAAK,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,2MAwBhB,CAAC;AAEX,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CA+CpD,CAAC;AAqBF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAwB3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAW/D,CAAC;AAKF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAsBxC"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,MAAM,yYAsFT,CAAC;AAEX,sEAAsE;AACtE,MAAM,MAAM,KAAK,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,2MAwBhB,CAAC;AAEX,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CA2DpD,CAAC;AAqBF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAwB3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAgB/D,CAAC;AAKF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAsBxC"}
package/dist/tokens.js CHANGED
@@ -38,6 +38,16 @@ export const tokens = [
38
38
  '--ui-color-text',
39
39
  '--ui-color-focus',
40
40
  '--ui-color-danger',
41
+ // The other two outcomes, arriving with `ui-toast` — the component `ROADMAP.md` said
42
+ // would bring them. They complete a set `--ui-color-danger` has been half of since the
43
+ // first field: a form told you what went wrong and had no way to say that anything
44
+ // went right.
45
+ //
46
+ // Grounds rather than derivations, and for the reason `--ui-color-danger` is one: a
47
+ // hue cannot be mixed out of the grounds that exist. Both are values a host is
48
+ // expected to replace with their own brand's, which is what a ground is for.
49
+ '--ui-color-success',
50
+ '--ui-color-warning',
41
51
  // The dim behind a modal, and a *ground* rather than a derivation even though every
42
52
  // other neutral here is derived. A derived neutral mixes toward the text, which is
43
53
  // what makes one formula right in both schemes — and it is exactly wrong for this
@@ -164,6 +174,18 @@ export const defaults = {
164
174
  // because no axe rule does.
165
175
  '--ui-color-focus': '#b45309',
166
176
  '--ui-color-danger': '#b91c1c',
177
+ // Green-700 and amber-700, each the darkest step of its hue that still reads as that
178
+ // hue. Both clear **4.5:1** against the surface rather than the 3:1 a coloured edge
179
+ // would owe, because the floor a value has to clear is the strictest use it is put to
180
+ // and nothing stops a host using one as text — `--ui-color-danger` was chosen the same
181
+ // way and is text in `ui-field` today.
182
+ //
183
+ // The warning is the same amber as `--ui-color-focus`, and the two arrived at it
184
+ // independently: both are *the darkest amber that clears its floor in both schemes*,
185
+ // and that question has one answer. They stay separate names because they are separate
186
+ // knobs — a host who retunes the focus ring has said nothing about warnings.
187
+ '--ui-color-success': '#15803d',
188
+ '--ui-color-warning': '#b45309',
167
189
  // Half black. Enough to push the page behind a modal out of the reading order for the
168
190
  // eye as well as for the accessibility tree, and not so much that the context a modal
169
191
  // is *about* stops being visible. The alpha is the whole point, so this is the one
@@ -295,6 +317,11 @@ export const darkScheme = {
295
317
  // Red-700 is 2.74:1 on the dark surface — under the 4.5:1 floor for text, and error
296
318
  // text is the one thing in this set that must never be hard to read. Red-400 is 6.41.
297
319
  '--ui-color-danger': '#f87171',
320
+ // The same inversion, for the same reason: green-700 is 3.54:1 on the dark surface and
321
+ // amber-700 is 3.53, so both would clear a coloured edge and fail as text — which is
322
+ // the half of the pair a reader is most likely to need.
323
+ '--ui-color-success': '#4ade80',
324
+ '--ui-color-warning': '#fbbf24',
298
325
  };
299
326
  /** The category every duration name shares, which is what reduced motion collapses. */
300
327
  const duration = '--ui-duration-';
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB,mBAAmB;IACnB,4BAA4B;IAC5B,oBAAoB;IACpB,iBAAiB;IACjB,kBAAkB;IAClB,mBAAmB;IACnB,oFAAoF;IACpF,mFAAmF;IACnF,kFAAkF;IAClF,iFAAiF;IACjF,oFAAoF;IACpF,wEAAwE;IACxE,EAAE;IACF,iFAAiF;IACjF,oFAAoF;IACpF,iFAAiF;IACjF,qFAAqF;IACrF,+EAA+E;IAC/E,kBAAkB;IAClB,aAAa;IACb,YAAY;IACZ,WAAW;IACX,kFAAkF;IAClF,sFAAsF;IACtF,+CAA+C;IAC/C,EAAE;IACF,uFAAuF;IACvF,sFAAsF;IACtF,uFAAuF;IACvF,oFAAoF;IACpF,4CAA4C;IAC5C,mBAAmB;IACnB,mBAAmB;IACnB,qFAAqF;IACrF,oFAAoF;IACpF,sFAAsF;IACtF,qDAAqD;IACrD,EAAE;IACF,uFAAuF;IACvF,uFAAuF;IACvF,mFAAmF;IACnF,kFAAkF;IAClF,mBAAmB;IACnB,kBAAkB;IAClB,oFAAoF;IACpF,mFAAmF;IACnF,mFAAmF;IACnF,2BAA2B;IAC3B,EAAE;IACF,mFAAmF;IACnF,kFAAkF;IAClF,6EAA6E;IAC7E,EAAE;IACF,8EAA8E;IAC9E,sFAAsF;IACtF,yDAAyD;IACzD,gBAAgB;IAChB,kBAAkB;IAClB,uFAAuF;IACvF,0CAA0C;IAC1C,EAAE;IACF,sFAAsF;IACtF,sFAAsF;IACtF,+EAA+E;IAC/E,qFAAqF;IACrF,mFAAmF;IACnF,qCAAqC;IACrC,EAAE;IACF,4EAA4E;IAC5E,qFAAqF;IACrF,sFAAsF;IACtF,sFAAsF;IACtF,mFAAmF;IACnF,uBAAuB;IACvB,oBAAoB;CACd,CAAC;AAKX;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IACzB,qBAAqB;IACrB,mFAAmF;IACnF,sFAAsF;IACtF,mFAAmF;IACnF,6EAA6E;IAC7E,EAAE;IACF,sFAAsF;IACtF,qFAAqF;IACrF,iFAAiF;IACjF,mBAAmB;IACnB,uBAAuB;IACvB,oFAAoF;IACpF,iFAAiF;IACjF,6EAA6E;IAC7E,kBAAkB;IAClB,oBAAoB;IACpB,yBAAyB;IACzB,2BAA2B;IAC3B,qFAAqF;IACrF,qFAAqF;IACrF,kFAAkF;IAClF,iDAAiD;IACjD,uBAAuB;CACjB,CAAC;AAKX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAoC;IACrD,mBAAmB,EAAE,SAAS;IAC9B,4BAA4B,EAAE,SAAS;IACvC,oBAAoB,EAAE,SAAS;IAC/B,iBAAiB,EAAE,SAAS;IAC5B,mFAAmF;IACnF,qFAAqF;IACrF,gFAAgF;IAChF,mFAAmF;IACnF,iFAAiF;IACjF,oFAAoF;IACpF,iFAAiF;IACjF,4BAA4B;IAC5B,kBAAkB,EAAE,SAAS;IAC7B,mBAAmB,EAAE,SAAS;IAC9B,sFAAsF;IACtF,sFAAsF;IACtF,mFAAmF;IACnF,qCAAqC;IACrC,kBAAkB,EAAE,kBAAkB;IACtC,aAAa,EAAE,UAAU;IACzB,YAAY,EAAE,QAAQ;IACtB,WAAW,EAAE,uBAAuB;IACpC,sFAAsF;IACtF,iFAAiF;IACjF,yEAAyE;IACzE,mBAAmB,EAAE,OAAO;IAC5B,kFAAkF;IAClF,qFAAqF;IACrF,4EAA4E;IAC5E,qFAAqF;IACrF,qFAAqF;IACrF,mDAAmD;IACnD,mBAAmB,EAAE,aAAa;IAClC,gFAAgF;IAChF,sFAAsF;IACtF,qFAAqF;IACrF,mBAAmB,EAAE,UAAU;IAC/B,kBAAkB,EAAE,SAAS;IAC7B,gBAAgB,EAAE,QAAQ;IAC1B,kBAAkB,EAAE,GAAG;IACvB,qFAAqF;IACrF,sFAAsF;IACtF,oFAAoF;IACpF,qFAAqF;IACrF,gDAAgD;IAChD,oBAAoB,EAAE,kEAAkE;CAC3F,CAAC;AAEF;;;;;;;;;GASG;AACH,SAAS,MAAM,CAAC,IAAW;IACvB,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;AAC7C,CAAC;AAED,gFAAgF;AAChF,SAAS,GAAG,CAAC,UAAiB,EAAE,MAAc,EAAE,UAAiB;IAC7D,OAAO,uBAAuB,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;AAClG,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,QAAQ,GAA2C;IAC5D,oFAAoF;IACpF,oFAAoF;IACpF,qBAAqB,EAAE,MAAM,CAAC,mBAAmB,CAAC;IAClD,qFAAqF;IACrF,sFAAsF;IACtF,uFAAuF;IACvF,qEAAqE;IACrE,kBAAkB,EAAE,GAAG,CAAC,iBAAiB,EAAE,CAAC,EAAE,oBAAoB,CAAC;IACnE,oBAAoB,EAAE,GAAG,CAAC,iBAAiB,EAAE,EAAE,EAAE,oBAAoB,CAAC;IACtE,yBAAyB,EAAE,GAAG,CAAC,iBAAiB,EAAE,EAAE,EAAE,mBAAmB,CAAC;IAC1E,2BAA2B,EAAE,GAAG,CAAC,iBAAiB,EAAE,EAAE,EAAE,mBAAmB,CAAC;IAC5E,oFAAoF;IACpF,oFAAoF;IACpF,0BAA0B;IAC1B,mBAAmB,EAAE,GAAG,CAAC,iBAAiB,EAAE,EAAE,EAAE,oBAAoB,CAAC;IACrE,qFAAqF;IACrF,mFAAmF;IACnF,gEAAgE;IAChE,uBAAuB,EAAE,GAAG,CAAC,iBAAiB,EAAE,EAAE,EAAE,oBAAoB,CAAC;IACzE,mFAAmF;IACnF,+EAA+E;IAC/E,iDAAiD;IACjD,uBAAuB,EAAE,MAAM,CAAC,oBAAoB,CAAC;CACxD,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,UAAU,GAA6C;IAChE,qFAAqF;IACrF,iFAAiF;IACjF,oCAAoC;IACpC,mBAAmB,EAAE,SAAS;IAC9B,4BAA4B,EAAE,SAAS;IACvC,oBAAoB,EAAE,SAAS;IAC/B,iBAAiB,EAAE,SAAS;IAC5B,oFAAoF;IACpF,sFAAsF;IACtF,mBAAmB,EAAE,SAAS;CACjC,CAAC;AAEF,uFAAuF;AACvF,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,eAAe;IAC3B,MAAM,OAAO,GAAG,MAAM;SACjB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACX,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAE/B,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,GAAG,CAAC;IAC9G,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEhB,kFAAkF;IAClF,qFAAqF;IACrF,sFAAsF;IACtF,gFAAgF;IAChF,MAAM,SAAS,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,aAAa,CAAC;SAC1C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;SAC7C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,WAAW,CAAC;SACvC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEhB,OAAO;QACH,yCAAyC,OAAO,KAAK;QACrD,yDAAyD,SAAS,UAAU;KAC/E,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import { LitElement, type TemplateResult } from 'lit';
2
+ /**
3
+ * Supplementary text on hover and on keyboard focus.
4
+ *
5
+ * **Everything is slotted**, the way `<ui-field>` slots everything and for the same
6
+ * measured reason: an IDREF does not cross a shadow boundary, so a tip rendered in here
7
+ * could not be the target of the trigger's `aria-describedby`. The trigger is the child
8
+ * with no `slot`; the tip is `slot="tip"`.
9
+ *
10
+ * ## What the platform does, and what is left
11
+ *
12
+ * The tip is a **popover**, which buys the two things a hand-rolled overlay gets wrong:
13
+ * it is promoted to the top layer, so it escapes `overflow: hidden` and every stacking
14
+ * context above it — measured — and it needs no `z-index`, which is why the second overlay
15
+ * in this package brings no layering category either.
16
+ *
17
+ * `popover="manual"` rather than `auto`, and the difference is the point: an `auto`
18
+ * popover light-dismisses, and dismissing one closes the others — so a tooltip appearing
19
+ * over an open menu would close the menu. What `manual` costs is Escape, which it does not
20
+ * handle: measured, a manual popover stays open on Escape. So this element wires it, which
21
+ * WCAG 2.2's **1.4.13 Content on Hover or Focus** requires anyway.
22
+ *
23
+ * ## Why the placement is written here
24
+ *
25
+ * The issue asked whether a clipping tooltip is solved with CSS anchor positioning or with
26
+ * a dependency. Measured in this engine, anchor positioning is **entirely present** —
27
+ * `anchor-name`, `position-anchor`, `position-area` and `position-try-fallbacks` all
28
+ * parse, and an anchored popover really is placed against its anchor rather than merely
29
+ * accepting the rule. It is still not adopted, and the reason is the one `docs/select.md`
30
+ * gives about `appearance: base-select`: a feature one engine has makes a kit look like
31
+ * two kits.
32
+ *
33
+ * **Here that is not a cosmetic difference but a broken component.** Measured: a popover
34
+ * whose anchor rules are ignored does not fall back to somewhere near the trigger — it is
35
+ * `position: fixed` at `inset: 0`, which is the corner of the screen. And the flip at a
36
+ * viewport edge is `position-try-fallbacks`, so an engine without it needs the script
37
+ * anyway. Adopting the CSS would mean writing the placement *and* a second code path for
38
+ * it, which is two answers to one problem.
39
+ *
40
+ * A positioning dependency was the other option the issue named. It solves scroll
41
+ * containers, virtual anchors and continuous auto-update, none of which a tooltip against
42
+ * a real element needs — and it would be this package's second runtime dependency, where
43
+ * RFC 0016's adoption was a *behaviour* library and not a layout one.
44
+ *
45
+ * So the placement is thirty lines here, measured at the edges by a suite that runs in a
46
+ * real browser. When anchor positioning is available broadly it replaces them and the
47
+ * tests stay: they assert where the tip lands, not who put it there.
48
+ *
49
+ * @example
50
+ * ```html
51
+ * <ui-tooltip>
52
+ * <button type="button">Save</button>
53
+ * <span slot="tip">Saves without closing the dialog.</span>
54
+ * </ui-tooltip>
55
+ * ```
56
+ */
57
+ export declare class UiTooltip extends LitElement {
58
+ #private;
59
+ static readonly styles: import("lit").CSSResult;
60
+ connectedCallback(): void;
61
+ disconnectedCallback(): void;
62
+ render(): TemplateResult;
63
+ }
64
+ declare global {
65
+ interface HTMLElementTagNameMap {
66
+ 'ui-tooltip': UiTooltip;
67
+ }
68
+ }
69
+ //# sourceMappingURL=tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AASjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,qBAAa,SAAU,SAAQ,UAAU;;IACrC,gBAAyB,MAAM,0BAkD7B;IAiDO,iBAAiB,IAAI,IAAI;IAqBzB,oBAAoB,IAAI,IAAI;IAU5B,MAAM,IAAI,cAAc;CAqKpC;AAQD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,YAAY,EAAE,SAAS,CAAC;KAC3B;CACJ"}
@@ -0,0 +1,317 @@
1
+ import { LitElement, css, html } from 'lit';
2
+ import { reference } from './reference.js';
3
+ /** Distinguishes one tooltip's generated id from another's. */
4
+ let sequence = 0;
5
+ /** How far the tip sits from the trigger, and which way the script placed it. */
6
+ const side = 'data-side';
7
+ /**
8
+ * Supplementary text on hover and on keyboard focus.
9
+ *
10
+ * **Everything is slotted**, the way `<ui-field>` slots everything and for the same
11
+ * measured reason: an IDREF does not cross a shadow boundary, so a tip rendered in here
12
+ * could not be the target of the trigger's `aria-describedby`. The trigger is the child
13
+ * with no `slot`; the tip is `slot="tip"`.
14
+ *
15
+ * ## What the platform does, and what is left
16
+ *
17
+ * The tip is a **popover**, which buys the two things a hand-rolled overlay gets wrong:
18
+ * it is promoted to the top layer, so it escapes `overflow: hidden` and every stacking
19
+ * context above it — measured — and it needs no `z-index`, which is why the second overlay
20
+ * in this package brings no layering category either.
21
+ *
22
+ * `popover="manual"` rather than `auto`, and the difference is the point: an `auto`
23
+ * popover light-dismisses, and dismissing one closes the others — so a tooltip appearing
24
+ * over an open menu would close the menu. What `manual` costs is Escape, which it does not
25
+ * handle: measured, a manual popover stays open on Escape. So this element wires it, which
26
+ * WCAG 2.2's **1.4.13 Content on Hover or Focus** requires anyway.
27
+ *
28
+ * ## Why the placement is written here
29
+ *
30
+ * The issue asked whether a clipping tooltip is solved with CSS anchor positioning or with
31
+ * a dependency. Measured in this engine, anchor positioning is **entirely present** —
32
+ * `anchor-name`, `position-anchor`, `position-area` and `position-try-fallbacks` all
33
+ * parse, and an anchored popover really is placed against its anchor rather than merely
34
+ * accepting the rule. It is still not adopted, and the reason is the one `docs/select.md`
35
+ * gives about `appearance: base-select`: a feature one engine has makes a kit look like
36
+ * two kits.
37
+ *
38
+ * **Here that is not a cosmetic difference but a broken component.** Measured: a popover
39
+ * whose anchor rules are ignored does not fall back to somewhere near the trigger — it is
40
+ * `position: fixed` at `inset: 0`, which is the corner of the screen. And the flip at a
41
+ * viewport edge is `position-try-fallbacks`, so an engine without it needs the script
42
+ * anyway. Adopting the CSS would mean writing the placement *and* a second code path for
43
+ * it, which is two answers to one problem.
44
+ *
45
+ * A positioning dependency was the other option the issue named. It solves scroll
46
+ * containers, virtual anchors and continuous auto-update, none of which a tooltip against
47
+ * a real element needs — and it would be this package's second runtime dependency, where
48
+ * RFC 0016's adoption was a *behaviour* library and not a layout one.
49
+ *
50
+ * So the placement is thirty lines here, measured at the edges by a suite that runs in a
51
+ * real browser. When anchor positioning is available broadly it replaces them and the
52
+ * tests stay: they assert where the tip lands, not who put it there.
53
+ *
54
+ * @example
55
+ * ```html
56
+ * <ui-tooltip>
57
+ * <button type="button">Save</button>
58
+ * <span slot="tip">Saves without closing the dialog.</span>
59
+ * </ui-tooltip>
60
+ * ```
61
+ */
62
+ export class UiTooltip extends LitElement {
63
+ static { this.styles = css `
64
+ /* No box of its own: a wrapper that laid something out would move the trigger it
65
+ wraps, and every call site would have to undo it. */
66
+ :host {
67
+ display: contents;
68
+ }
69
+
70
+ /* The tip is the host's own element, reached through ::slotted — the shape every
71
+ component in this kit has. position: fixed is what the popover already is;
72
+ it is restated because the script writes inset-block-start and
73
+ inset-inline-start against the viewport, and a reader of those two lines
74
+ should not have to know which stylesheet made them mean that.
75
+
76
+ max-content up to a ceiling: a tip is a phrase, and a phrase that wraps at the
77
+ width of whatever it happens to sit near is unreadable. The ceiling is in the
78
+ space token so it scales with everything else a host retunes. */
79
+ slot[name='tip']::slotted(*) {
80
+ position: fixed;
81
+ margin: 0;
82
+ inline-size: max-content;
83
+ max-inline-size: calc(${reference('--ui-space')} * 40);
84
+ padding: calc(${reference('--ui-space')} / 2) ${reference('--ui-space')};
85
+ font-family: ${reference('--ui-font')};
86
+ font-size: 0.875em;
87
+ /* The pair, never half of it: a surface without the colour chosen against it
88
+ inherits whatever the page set, and the contrast measured for that pair
89
+ stops holding. */
90
+ color: ${reference('--ui-color-text')};
91
+ background: ${reference('--ui-color-surface')};
92
+ border: 1px solid ${reference('--ui-color-border')};
93
+ border-radius: ${reference('--ui-radius')};
94
+ box-shadow: ${reference('--ui-elevation-raised')};
95
+ }
96
+
97
+ /* The gap between the tip and its trigger, in CSS because it is a token and the
98
+ script has no way to resolve one: a custom property reads back as it was
99
+ written, so 0.5rem would arrive at a function that needs pixels.
100
+
101
+ It is deliberately small. WCAG 2.2's 1.4.13 asks that a pointer be able to
102
+ travel onto the tip without it vanishing, and it travels across this gap — the
103
+ tip stays open because it is a DOM child of this element, so entering it never
104
+ leaves the host, but only if the pointer does not leave through the gap on the
105
+ way. */
106
+ slot[name='tip']::slotted([data-side='block-start']) {
107
+ translate: 0 calc(${reference('--ui-space')} / -2);
108
+ }
109
+
110
+ slot[name='tip']::slotted([data-side='block-end']) {
111
+ translate: 0 calc(${reference('--ui-space')} / 2);
112
+ }
113
+ `; }
114
+ /** Whether the tip is currently shown, which decides what the listeners cost. */
115
+ #shown = false;
116
+ /**
117
+ * What is watching while the tip is open, dropped in one call when it closes.
118
+ *
119
+ * These three are added on show rather than on connection because two of them are
120
+ * per-frame: a page holding fifty tooltips would run fifty no-op scroll handlers for
121
+ * every frame of every scroll, and at most one tooltip is ever open.
122
+ *
123
+ * Built here rather than on the first show so it is never absent — the reason
124
+ * `src/field.ts` builds its observer the same way: a close that has to ask whether the
125
+ * controller exists is a branch no test can reach, because nothing closes what was
126
+ * never opened.
127
+ */
128
+ #watching = new AbortController();
129
+ /** Re-places the tip under anything that moves the trigger while it is open. */
130
+ #reflow = () => {
131
+ this.#place();
132
+ };
133
+ /**
134
+ * Dismisses on Escape, which is 1.4.13's first requirement and `manual`'s one gap.
135
+ *
136
+ * On the document rather than on this element: a tip shown by the pointer leaves the
137
+ * focus wherever it was, so a key listener bound here would never hear the key that is
138
+ * supposed to dismiss it.
139
+ */
140
+ #dismiss = (event) => {
141
+ if (event.key === 'Escape') {
142
+ this.#hide();
143
+ }
144
+ };
145
+ /**
146
+ * Takes every listener off at once, and the reason is not brevity.
147
+ *
148
+ * Removing them one by one needs the event name repeated at both ends, and half of
149
+ * those repetitions cannot be checked: a leaked `pointerenter` is caught the moment it
150
+ * fires, because it opens a popover that is no longer in a document and that throws —
151
+ * but a leaked `pointerleave` reaches `#hide`, which returns at its own guard, and
152
+ * nothing anywhere can tell it happened. One `abort()` is one thing to get right, and
153
+ * a leak of it is observable through the half that is.
154
+ */
155
+ #listeners = new AbortController();
156
+ connectedCallback() {
157
+ super.connectedCallback();
158
+ // A fresh one per connection: an aborted signal stays aborted, so a tooltip a
159
+ // framework moves in the tree would come back deaf.
160
+ this.#listeners = new AbortController();
161
+ // One options object for the four, and that is the same argument as the controller
162
+ // itself: written per call, each `{ signal }` is its own thing to get right, and
163
+ // half of them cannot be checked — a leaked `pointerleave` reaches `#hide`, which
164
+ // returns at its own guard, and nothing can tell. Shared, all four ride on the one
165
+ // that does report: a leaked `pointerenter` opens a popover that is no longer in a
166
+ // document, which throws.
167
+ const watched = { signal: this.#listeners.signal };
168
+ this.addEventListener('pointerenter', this.#onPointer, watched);
169
+ this.addEventListener('pointerleave', this.#onLeave, watched);
170
+ this.addEventListener('focusin', this.#onFocus, watched);
171
+ this.addEventListener('focusout', this.#onLeave, watched);
172
+ }
173
+ disconnectedCallback() {
174
+ // Removed while shown, the tip takes the window listeners with it. A tooltip that
175
+ // a framework unmounted mid-hover would otherwise keep re-placing an element that
176
+ // is no longer in a document.
177
+ this.#hide();
178
+ this.#listeners.abort();
179
+ super.disconnectedCallback();
180
+ }
181
+ render() {
182
+ return html `
183
+ <slot></slot>
184
+ <slot name="tip" @slotchange=${this.#associate}></slot>
185
+ `;
186
+ }
187
+ /** The element the tip is about: the one child carrying no `slot` attribute. */
188
+ #trigger() {
189
+ const found = this.querySelector(':scope > :not([slot])');
190
+ return found instanceof HTMLElement ? found : undefined;
191
+ }
192
+ /** The tip itself, which is the host's own element and stays in their tree. */
193
+ #tip() {
194
+ const found = this.querySelector(":scope > [slot='tip']");
195
+ return found instanceof HTMLElement ? found : undefined;
196
+ }
197
+ /**
198
+ * Makes the tip a popover, names it, and points the trigger at it.
199
+ *
200
+ * On `slotchange` as well as at first render, because a framework re-render replaces
201
+ * the element rather than mutating it — and a tip that is only wired the first time is
202
+ * one that stops being announced at a moment nothing reports.
203
+ *
204
+ * A `role` or an `id` the host wrote is never overwritten, the same courtesy
205
+ * `<ui-field>` and `<ui-switch>` extend. `aria-describedby` is **added to** rather than
206
+ * replaced: the trigger may already be described by a field's help text, and a
207
+ * description that silently replaced another is the failure nobody sees.
208
+ */
209
+ #associate = () => {
210
+ const trigger = this.#trigger();
211
+ const tip = this.#tip();
212
+ if (trigger === undefined || tip === undefined) {
213
+ return;
214
+ }
215
+ if (tip.id === '') {
216
+ // Stryker disable next-line UpdateOperator: two tooltips on one page must get
217
+ // different ids, and a decrementing counter delivers that as well as an
218
+ // incrementing one. No input distinguishes them — an equivalent mutant, the
219
+ // same one `src/field.ts` carries beside its own counter.
220
+ tip.id = `ui-tooltip-${String(++sequence)}`;
221
+ }
222
+ if (!tip.hasAttribute('role')) {
223
+ tip.setAttribute('role', 'tooltip');
224
+ }
225
+ tip.setAttribute('popover', 'manual');
226
+ const described = (trigger.getAttribute('aria-describedby') ?? '')
227
+ .split(' ')
228
+ .filter((id) => id !== '');
229
+ if (!described.includes(tip.id)) {
230
+ trigger.setAttribute('aria-describedby', [...described, tip.id].join(' '));
231
+ }
232
+ };
233
+ /**
234
+ * Shows on the pointer, except the pointer a touch screen reports.
235
+ *
236
+ * There is no hover on a touch screen, so a tap fires `pointerenter` and then focus —
237
+ * and showing on both means the tip arrives, is dismissed by the tap that follows, and
238
+ * arrives again. Focus is the one that survives, so this defers to it.
239
+ */
240
+ #onPointer = (event) => {
241
+ if (event.pointerType !== 'touch') {
242
+ this.#show();
243
+ }
244
+ };
245
+ #onFocus = () => {
246
+ this.#show();
247
+ };
248
+ #onLeave = () => {
249
+ this.#hide();
250
+ };
251
+ /** Opens the tip and starts watching whatever can move its trigger. */
252
+ #show() {
253
+ const tip = this.#tip();
254
+ if (tip === undefined || this.#shown) {
255
+ return;
256
+ }
257
+ this.#shown = true;
258
+ tip.showPopover();
259
+ this.#place();
260
+ this.#watching = new AbortController();
261
+ // Shared for the same reason the four above are, and it pays off in the same
262
+ // direction: a leaked `keydown` is invisible — every later Escape returns at
263
+ // `#hide`'s guard — while a leaked `resize` re-places a tip that is not on screen,
264
+ // which a test reads straight off the written inset.
265
+ const watched = { signal: this.#watching.signal };
266
+ // Capturing, because the thing that scrolls is rarely the window: a scroll inside
267
+ // any ancestor moves the trigger and no event reaches the window from it.
268
+ //
269
+ // Not `passive`, and its absence is deliberate rather than an omission: `scroll` is
270
+ // not cancelable, so the flag changes nothing a test could observe — and an option
271
+ // no input can distinguish is one the mutation floor has to be told to ignore. The
272
+ // cheaper answer is not to write it.
273
+ window.addEventListener('scroll', this.#reflow, { capture: true, ...watched });
274
+ window.addEventListener('resize', this.#reflow, watched);
275
+ document.addEventListener('keydown', this.#dismiss, watched);
276
+ }
277
+ /** Closes the tip and stops watching. */
278
+ #hide() {
279
+ if (!this.#shown) {
280
+ return;
281
+ }
282
+ this.#shown = false;
283
+ this.#tip()?.hidePopover();
284
+ this.#watching.abort();
285
+ }
286
+ /**
287
+ * Puts the tip above its trigger, below it when there is no room, and inside the
288
+ * viewport either way.
289
+ *
290
+ * Read rather than assumed: the tip's own box is measured after it is shown, because
291
+ * its height depends on how its text wrapped at this width in this font — a number
292
+ * this file cannot know and must not guess.
293
+ */
294
+ #place() {
295
+ const trigger = this.#trigger();
296
+ const tip = this.#tip();
297
+ if (trigger === undefined || tip === undefined) {
298
+ return;
299
+ }
300
+ const anchor = trigger.getBoundingClientRect();
301
+ const box = tip.getBoundingClientRect();
302
+ const above = anchor.top >= box.height;
303
+ tip.setAttribute(side, above ? 'block-start' : 'block-end');
304
+ tip.style.insetBlockStart = `${String(above ? anchor.top - box.height : anchor.bottom)}px`;
305
+ // Centred on the trigger, then pulled back inside the viewport — in that order,
306
+ // because a tip centred on a trigger near the edge is a tip half off the screen.
307
+ // `Math.max` last so a tip wider than the viewport starts at the leading edge
308
+ // rather than at a negative one.
309
+ tip.style.insetInlineStart = `${String(Math.max(0, Math.min(anchor.left + anchor.width / 2 - box.width / 2, document.documentElement.clientWidth - box.width)))}px`;
310
+ }
311
+ }
312
+ // Stryker disable next-line StringLiteral: the registration runs once, at import, inside
313
+ // the warm process Stryker switches mutants in — so by the time a mutant on this line is
314
+ // active the element is already defined under the original name, and no test can observe
315
+ // the change. Outside the runner's reach, not an equivalent mutant.
316
+ customElements.define('ui-tooltip', UiTooltip);
317
+ //# sourceMappingURL=tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rak200/ui",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
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>",