use-wc 1.0.0-pre.53 → 1.0.0-pre.55

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.
@@ -89,149 +89,6 @@
89
89
  }
90
90
  ]
91
91
  },
92
- {
93
- "kind": "javascript-module",
94
- "path": "src/elements/use-badge/use-badge.ts",
95
- "declarations": [
96
- {
97
- "kind": "class",
98
- "description": "`use-badge` renders a small indicator anchored to another element's corner, using\n[CSS anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)\ninstead of the classic `position: absolute` + negative-offset trick. This lets the badge live\nanywhere in the DOM rather than being a child of the element it decorates. A badge's shape,\ncolor, and positioning ship built in — reading from the design system's tokens when\n`tokens.css` is loaded, and falling back to sensible defaults otherwise — so no theme is\nrequired to get a working, visible badge.\n\nPoint a badge at its target with the `anchortarget` attribute, set to the target's `id`\n(named to avoid colliding with the `anchor` content attribute HTML is gaining natively):\n\n```html\n<button type=\"button\" id=\"inbox\">Inbox</button>\n<use-badge anchortarget=\"inbox\">99+</use-badge>\n```\n\nThe badge and its anchor must live in the same DOM tree — a badge cannot reach an anchor\ninside another component's shadow root, and an anchor's `anchor-name` set inside a shadow\ntree's stylesheet is not visible here. No positioned ancestor or shared containing block is\nrequired: anchor positioning resolves the badge's location straight from the anchor's own\ngeometry, so the badge can sit anywhere else in the DOM tree.\n\nWithout a resolved anchor — `anchortarget` unset, pointing at a missing id, or `anchorElement`\nnever assigned — the badge has no `:state(anchored)` and renders as a plain inline chip in\nnormal document flow, still using the same color/shape styling.\n\nSome components render their host as `display: contents`, which has no box of its own for CSS\nanchor positioning to resolve against. If the anchor computes a non-empty `--usewc-anchor-name`\ncustom property, the badge uses that dashed-ident as its `position-anchor` directly instead of\ngenerating and writing its own onto the anchor — letting a component publish the name of a\nreal, box-generating part of itself (already established for its own purposes) for badges to\ntarget instead of its host, even though that part lives inside the component's own shadow root.\nThis mechanism is shared with `use-menu`/`use-anchored` via `AnchorController`; components\nwithout a `display: contents` host don't need to publish anything.\n\n**Known Firefox limitation:** anchoring across a shadow root boundary this way — the case\n`--usewc-anchor-name` exists for — is mispositioned in Firefox as of this writing. Verified\ndirectly: `anchor()`'s logical `start`/`end` keywords resolve backwards on both axes\nspecifically for a cross-shadow-root anchor reference, in Firefox only — the same anchor()\ncalls resolve correctly for a same-tree anchor in Firefox, and cross-shadow anchoring resolves\ncorrectly in Chromium and Safari. A badge anchored to a plain element (button, input, anything\nthat isn't itself relying on this escape hatch) is unaffected in every engine, including\nFirefox.\n\n## Side effects\n\nThe badge is a sibling of its anchor rather than a child, so **as a side effect, a badge\nmutates its anchor element**: it appends its own id to the anchor's `aria-describedby`\n(preserving any ids already there, the same way `anchor-name` is appended rather than\nreplaced) to keep its content connected to the anchor for assistive technology. This means a\nbadge's content, or its `aria-label` when set, is announced as part of interacting with the\nanchor, e.g. \"Inbox button, 3\". A `dot` badge has no content of its own, so set `aria-label`\ndirectly on `<use-badge>` for a meaningful description (e.g. `aria-label=\"3 unread messages\"`);\nwithout one, the description contributes nothing and the anchor announces as before.\n\nAvoid adding `aria-hidden` to `<use-badge>` if you rely on this: while the description\ncomputation is specified to still read a hidden node when it's the direct target of\n`aria-describedby`, support for that specific case is inconsistent across browsers and screen\nreaders.\n\nSet `options=\"noaria\"` to opt a badge out of this and leave the anchor's `aria-describedby`\nuntouched, e.g. when the anchor already documents the badge's meaning some other way.",
99
- "name": "UseBadge",
100
- "slots": [
101
- {
102
- "description": "Badge content, e.g. a count or short label. Ignored when `dot` is set.",
103
- "name": ""
104
- }
105
- ],
106
- "members": [
107
- {
108
- "kind": "field",
109
- "name": "anchortarget",
110
- "type": {
111
- "text": "string | undefined"
112
- },
113
- "attribute": "anchortarget",
114
- "reflects": true
115
- },
116
- {
117
- "kind": "field",
118
- "name": "dot",
119
- "type": {
120
- "text": "boolean"
121
- },
122
- "default": "false",
123
- "attribute": "dot",
124
- "reflects": true
125
- },
126
- {
127
- "kind": "field",
128
- "name": "blockalign",
129
- "type": {
130
- "text": "\"start\" | \"end\""
131
- },
132
- "default": "\"start\"",
133
- "attribute": "blockalign",
134
- "reflects": true
135
- },
136
- {
137
- "kind": "field",
138
- "name": "inlinealign",
139
- "type": {
140
- "text": "\"start\" | \"end\""
141
- },
142
- "default": "\"end\"",
143
- "attribute": "inlinealign",
144
- "reflects": true
145
- },
146
- {
147
- "kind": "field",
148
- "name": "options",
149
- "type": {
150
- "text": "string"
151
- },
152
- "default": "\"\"",
153
- "attribute": "options",
154
- "reflects": true
155
- },
156
- {
157
- "kind": "field",
158
- "name": "anchorElement",
159
- "description": "The element this badge is anchored to. Reading returns whatever element was last resolved,\neither from the `anchortarget` id or set here directly. Setting it anchors to that element\ninstead of resolving `anchortarget`, for cases where the target has no id to reference."
160
- }
161
- ],
162
- "attributes": [
163
- {
164
- "description": "Id of the element to anchor to, resolved in the badge's own tree.",
165
- "name": "anchortarget",
166
- "type": {
167
- "text": "string | undefined"
168
- },
169
- "fieldName": "anchortarget"
170
- },
171
- {
172
- "description": "Renders as a fixed-size dot instead of the slotted content.",
173
- "name": "dot",
174
- "type": {
175
- "text": "boolean"
176
- },
177
- "default": "false",
178
- "fieldName": "dot"
179
- },
180
- {
181
- "description": "`\"start\" | \"end\"`: Aligns the badge along the block axis relative to its anchor.",
182
- "name": "blockalign",
183
- "type": {
184
- "text": "\"start\" | \"end\""
185
- },
186
- "default": "\"start\"",
187
- "fieldName": "blockalign"
188
- },
189
- {
190
- "description": "`\"start\" | \"end\"`: Aligns the badge along the inline axis relative to its anchor.",
191
- "name": "inlinealign",
192
- "type": {
193
- "text": "\"start\" | \"end\""
194
- },
195
- "default": "\"end\"",
196
- "fieldName": "inlinealign"
197
- },
198
- {
199
- "description": "Space-separated feature flags. `noaria` opts out of the automatic `aria-describedby` wiring described above.",
200
- "name": "options",
201
- "type": {
202
- "text": "string"
203
- },
204
- "default": "\"\"",
205
- "fieldName": "options"
206
- }
207
- ],
208
- "superclass": {
209
- "name": "LitElement",
210
- "package": "lit"
211
- },
212
- "tagName": "use-badge",
213
- "customElement": true
214
- }
215
- ],
216
- "exports": [
217
- {
218
- "kind": "js",
219
- "name": "UseBadge",
220
- "declaration": {
221
- "name": "UseBadge",
222
- "module": "src/elements/use-badge/use-badge.ts"
223
- }
224
- },
225
- {
226
- "kind": "custom-element-definition",
227
- "name": "use-badge",
228
- "declaration": {
229
- "name": "UseBadge",
230
- "module": "src/elements/use-badge/use-badge.ts"
231
- }
232
- }
233
- ]
234
- },
235
92
  {
236
93
  "kind": "javascript-module",
237
94
  "path": "src/elements/use-calendar-base/use-calendar-base.ts",
@@ -637,6 +494,149 @@
637
494
  }
638
495
  ]
639
496
  },
497
+ {
498
+ "kind": "javascript-module",
499
+ "path": "src/elements/use-badge/use-badge.ts",
500
+ "declarations": [
501
+ {
502
+ "kind": "class",
503
+ "description": "`use-badge` renders a small indicator anchored to another element's corner, using\n[CSS anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)\ninstead of the classic `position: absolute` + negative-offset trick. This lets the badge live\nanywhere in the DOM rather than being a child of the element it decorates. A badge's shape,\ncolor, and positioning ship built in — reading from the design system's tokens when\n`tokens.css` is loaded, and falling back to sensible defaults otherwise — so no theme is\nrequired to get a working, visible badge.\n\nPoint a badge at its target with the `anchortarget` attribute, set to the target's `id`\n(named to avoid colliding with the `anchor` content attribute HTML is gaining natively):\n\n```html\n<button type=\"button\" id=\"inbox\">Inbox</button>\n<use-badge anchortarget=\"inbox\">99+</use-badge>\n```\n\nThe badge and its anchor must live in the same DOM tree — a badge cannot reach an anchor\ninside another component's shadow root, and an anchor's `anchor-name` set inside a shadow\ntree's stylesheet is not visible here. No positioned ancestor or shared containing block is\nrequired: anchor positioning resolves the badge's location straight from the anchor's own\ngeometry, so the badge can sit anywhere else in the DOM tree.\n\nWithout a resolved anchor — `anchortarget` unset, pointing at a missing id, or `anchorElement`\nnever assigned — the badge has no `:state(anchored)` and renders as a plain inline chip in\nnormal document flow, still using the same color/shape styling.\n\nSome components render their host as `display: contents`, which has no box of its own for CSS\nanchor positioning to resolve against. If the anchor computes a non-empty `--usewc-anchor-name`\ncustom property, the badge uses that dashed-ident as its `position-anchor` directly instead of\ngenerating and writing its own onto the anchor — letting a component publish the name of a\nreal, box-generating part of itself (already established for its own purposes) for badges to\ntarget instead of its host, even though that part lives inside the component's own shadow root.\nThis mechanism is shared with `use-menu`/`use-anchored` via `AnchorController`; components\nwithout a `display: contents` host don't need to publish anything.\n\n**Known Firefox limitation:** anchoring across a shadow root boundary this way — the case\n`--usewc-anchor-name` exists for — is mispositioned in Firefox as of this writing. Verified\ndirectly: `anchor()`'s logical `start`/`end` keywords resolve backwards on both axes\nspecifically for a cross-shadow-root anchor reference, in Firefox only — the same anchor()\ncalls resolve correctly for a same-tree anchor in Firefox, and cross-shadow anchoring resolves\ncorrectly in Chromium and Safari. A badge anchored to a plain element (button, input, anything\nthat isn't itself relying on this escape hatch) is unaffected in every engine, including\nFirefox.\n\n## Side effects\n\nThe badge is a sibling of its anchor rather than a child, so **as a side effect, a badge\nmutates its anchor element**: it appends its own id to the anchor's `aria-describedby`\n(preserving any ids already there, the same way `anchor-name` is appended rather than\nreplaced) to keep its content connected to the anchor for assistive technology. This means a\nbadge's content, or its `aria-label` when set, is announced as part of interacting with the\nanchor, e.g. \"Inbox button, 3\". A `dot` badge has no content of its own, so set `aria-label`\ndirectly on `<use-badge>` for a meaningful description (e.g. `aria-label=\"3 unread messages\"`);\nwithout one, the description contributes nothing and the anchor announces as before.\n\nAvoid adding `aria-hidden` to `<use-badge>` if you rely on this: while the description\ncomputation is specified to still read a hidden node when it's the direct target of\n`aria-describedby`, support for that specific case is inconsistent across browsers and screen\nreaders.\n\nSet `options=\"noaria\"` to opt a badge out of this and leave the anchor's `aria-describedby`\nuntouched, e.g. when the anchor already documents the badge's meaning some other way.",
504
+ "name": "UseBadge",
505
+ "slots": [
506
+ {
507
+ "description": "Badge content, e.g. a count or short label. Ignored when `dot` is set.",
508
+ "name": ""
509
+ }
510
+ ],
511
+ "members": [
512
+ {
513
+ "kind": "field",
514
+ "name": "anchortarget",
515
+ "type": {
516
+ "text": "string | undefined"
517
+ },
518
+ "attribute": "anchortarget",
519
+ "reflects": true
520
+ },
521
+ {
522
+ "kind": "field",
523
+ "name": "dot",
524
+ "type": {
525
+ "text": "boolean"
526
+ },
527
+ "default": "false",
528
+ "attribute": "dot",
529
+ "reflects": true
530
+ },
531
+ {
532
+ "kind": "field",
533
+ "name": "blockalign",
534
+ "type": {
535
+ "text": "\"start\" | \"end\""
536
+ },
537
+ "default": "\"start\"",
538
+ "attribute": "blockalign",
539
+ "reflects": true
540
+ },
541
+ {
542
+ "kind": "field",
543
+ "name": "inlinealign",
544
+ "type": {
545
+ "text": "\"start\" | \"end\""
546
+ },
547
+ "default": "\"end\"",
548
+ "attribute": "inlinealign",
549
+ "reflects": true
550
+ },
551
+ {
552
+ "kind": "field",
553
+ "name": "options",
554
+ "type": {
555
+ "text": "string"
556
+ },
557
+ "default": "\"\"",
558
+ "attribute": "options",
559
+ "reflects": true
560
+ },
561
+ {
562
+ "kind": "field",
563
+ "name": "anchorElement",
564
+ "description": "The element this badge is anchored to. Reading returns whatever element was last resolved,\neither from the `anchortarget` id or set here directly. Setting it anchors to that element\ninstead of resolving `anchortarget`, for cases where the target has no id to reference."
565
+ }
566
+ ],
567
+ "attributes": [
568
+ {
569
+ "description": "Id of the element to anchor to, resolved in the badge's own tree.",
570
+ "name": "anchortarget",
571
+ "type": {
572
+ "text": "string | undefined"
573
+ },
574
+ "fieldName": "anchortarget"
575
+ },
576
+ {
577
+ "description": "Renders as a fixed-size dot instead of the slotted content.",
578
+ "name": "dot",
579
+ "type": {
580
+ "text": "boolean"
581
+ },
582
+ "default": "false",
583
+ "fieldName": "dot"
584
+ },
585
+ {
586
+ "description": "`\"start\" | \"end\"`: Aligns the badge along the block axis relative to its anchor.",
587
+ "name": "blockalign",
588
+ "type": {
589
+ "text": "\"start\" | \"end\""
590
+ },
591
+ "default": "\"start\"",
592
+ "fieldName": "blockalign"
593
+ },
594
+ {
595
+ "description": "`\"start\" | \"end\"`: Aligns the badge along the inline axis relative to its anchor.",
596
+ "name": "inlinealign",
597
+ "type": {
598
+ "text": "\"start\" | \"end\""
599
+ },
600
+ "default": "\"end\"",
601
+ "fieldName": "inlinealign"
602
+ },
603
+ {
604
+ "description": "Space-separated feature flags. `noaria` opts out of the automatic `aria-describedby` wiring described above.",
605
+ "name": "options",
606
+ "type": {
607
+ "text": "string"
608
+ },
609
+ "default": "\"\"",
610
+ "fieldName": "options"
611
+ }
612
+ ],
613
+ "superclass": {
614
+ "name": "LitElement",
615
+ "package": "lit"
616
+ },
617
+ "tagName": "use-badge",
618
+ "customElement": true
619
+ }
620
+ ],
621
+ "exports": [
622
+ {
623
+ "kind": "js",
624
+ "name": "UseBadge",
625
+ "declaration": {
626
+ "name": "UseBadge",
627
+ "module": "src/elements/use-badge/use-badge.ts"
628
+ }
629
+ },
630
+ {
631
+ "kind": "custom-element-definition",
632
+ "name": "use-badge",
633
+ "declaration": {
634
+ "name": "UseBadge",
635
+ "module": "src/elements/use-badge/use-badge.ts"
636
+ }
637
+ }
638
+ ]
639
+ },
640
640
  {
641
641
  "kind": "javascript-module",
642
642
  "path": "src/elements/use-calendarday/use-calendarday.ts",
@@ -708,7 +708,7 @@
708
708
  "declarations": [
709
709
  {
710
710
  "kind": "class",
711
- "description": "`use-caret` supplies the caret icon and label layout for a consumer who wants to own the\ninvoking button itself. It renders inside a plain `<button>`\nthat carries the native `popovertarget` attribute, pointing at a `use-menu`:\n\n```html\n<button id=\"thing\" popovertarget=\"thing-menu\">\n <use-caret>Menu</use-caret>\n</button>\n<use-menu id=\"thing-menu\" aria-label=\"Menu\">\n <button role=\"menuitem\">First thing</button>\n</use-menu>\n```\n\n`use-menu` finds this button on its own via `popovertarget` and anchors to it — no\n`anchortarget` needed here; that's only for a menu with more than one invoker. See `use-menu`'s\nown docs for that case.\n\nThe button is entirely the consumer's own element — their classes, their content, their event\nhandlers. `use-caret` contributes no interactivity of its own: no internal button, no focus\ntarget, no state. It only renders the label and caret glyph, so consumers get a consistent\nvisual trigger without giving up control of the button.\n\nThe caret direction is derived rather than configured: a trigger nested inside a `use-menu`\nis understood to open a submenu and renders pointing sideways; a top-level trigger renders\npointing down.\n\nSwapping in a different caret **per instance** is a slot: put your own element in the `icon`\nslot and it replaces `part(icon-default)` entirely for that trigger.\n\nSwapping the default caret **for every trigger in the app** doesn't need touching this\ncomponent's `render()` at all — style `::part(icon-default)` from an outside stylesheet, the\nsame way any shadow DOM part is themed:\n\n```css\nuse-caret::part(icon-default) {\n mask-image: url(\"/icons/my-caret.svg\");\n}\n```\n\n(When the design system's `theme.css` is loaded, this is already wired to a token —\n`--usewc-effect-dropdown-trigger-icon-mask-image` — so redefining that custom property has\nthe same effect without writing a `::part()` rule at all.)\n\n`::part()` only reaches CSS-expressible changes (a mask, a background, a transform). For\nanything else — different markup, an icon font, logic beyond a mask — set the static\n`customIcon` hook. Every `<use-caret>` in the page picks it up; consumers keep writing the\nsame tag, no new element to register or remember to use instead. Set it once, as early as\npossible (an app's entry point, before any triggers connect) — Lit calls `render()` fresh on\nevery update, so an already-connected instance only reflects the change once something causes\nit to re-render:\n\n```ts\n// A bare string or TemplateResult is used as-is, on every trigger, nested or not:\nUseCaret.customIcon = \"→\";\n\n// A function is called on every render instead, and told whether this particular trigger is\n// nested — the same distinction the built-in ▼/▶ caret already makes for submenus:\nUseCaret.customIcon = (html, isNested) =>\n isNested\n ? html`<my-icon name=\"chevron-right\"></my-icon>`\n : html`<my-icon name=\"chevron-down\"></my-icon>`;\n```\n\nThe function form is called with the component's own `html` tag as its (optional) first\nargument rather than requiring the caller to import one — a page can end up with more than one\ncopy of `lit-html` in its module graph, and a `TemplateResult` built from a *different* copy\nthan the one this component renders with can silently fail to render. Taking `html` in as a\nparameter sidesteps that: whatever's returned is guaranteed to come from the same `lit-html`\nthis component already uses. A bare string skips the concern entirely, at the cost of not\nbeing able to vary by `isNested`.\n\nOnly need to change the icon on **some** triggers, not every one in the app? Extend the\nclass, override `render()` normally, and register the subclass under a different tag name so\nit coexists with the built-in element instead of replacing it everywhere:\n\n```ts\nclass MyCaret extends UseCaret {\n override render() {\n return html`<slot></slot><slot name=\"icon\"><my-icon name=\"chevron\"></my-icon></slot>`;\n }\n}\ncustomElements.define(\"my-caret\", MyCaret);\n```",
711
+ "description": "`use-caret` supplies the caret icon and label layout for a consumer who wants to own the\ninvoking button itself. It renders inside a plain `<button>`\nthat carries the native `popovertarget` attribute, pointing at a `use-menu`:\n\n```html\n<button id=\"thing\" popovertarget=\"thing-menu\">\n <use-caret>Menu</use-caret>\n</button>\n<use-menu id=\"thing-menu\" aria-label=\"Menu\">\n <button role=\"menuitem\">First thing</button>\n</use-menu>\n```\n\n`use-menu` finds this button on its own via `popovertarget` and anchors to it — no\n`anchortarget` needed here; that's only for a menu with more than one invoker. See `use-menu`'s\nown docs for that case.\n\nThe button is entirely the consumer's own element — their classes, their content, their event\nhandlers. `use-caret` contributes no interactivity of its own: no internal button, no focus\ntarget, no state. It only renders the label and caret glyph, so consumers get a consistent\nvisual trigger without giving up control of the button.\n\nThe caret direction is derived rather than configured: a trigger nested inside a `use-menu`\nis understood to open a submenu and renders pointing sideways; a top-level trigger renders\npointing down.\n\nSwapping in a different caret **per instance** is a slot: put your own element in the `icon`\nslot and it replaces `part(icon-default)` entirely for that trigger.\n\nSwapping the default caret **for every trigger in the app** doesn't need touching this\ncomponent's `render()` at all — style `::part(icon-default)` from an outside stylesheet, the\nsame way any shadow DOM part is themed:\n\n```css\nuse-caret::part(icon-default) {\n mask-image: url(\"/icons/my-caret.svg\");\n}\n```\n\n(When the design system's `theme.css` is loaded, this is already wired to a token —\n`--usewc-effect-dropdown-trigger-icon-mask-image` — so redefining that custom property has\nthe same effect without writing a `::part()` rule at all.)\n\n`::part()` only reaches CSS-expressible changes (a mask, a background, a transform). For\nanything else — different markup, an icon font, logic beyond a mask — set the static `icon`\nand `nestedIcon` hooks. Every `<use-caret>` in the page picks them up; consumers keep writing\nthe same tag, no new element to register or remember to use instead. `icon` supplies a\ntop-level trigger's glyph, `nestedIcon` a submenu trigger's — set whichever apply; an unset one\nkeeps the built-in ▼/▶ for that case. Set them once, as early as possible (an app's entry\npoint, before any triggers connect) — Lit calls `render()` fresh on every update, so an\nalready-connected instance only reflects the change once something causes it to re-render:\n\n```ts\n// A bare string or TemplateResult is used as-is, on every trigger of that kind:\nUseCaret.icon = \"▾\";\nUseCaret.nestedIcon = \"▸\";\n\n// A function is called on every render instead:\nUseCaret.nestedIcon = (html) => html`<my-icon name=\"chevron-right\"></my-icon>`;\n```\n\nThe function form is called with the component's own `html` tag as its (optional) first\nargument rather than requiring the caller to import one — a page can end up with more than one\ncopy of `lit-html` in its module graph, and a `TemplateResult` built from a *different* copy\nthan the one this component renders with can silently fail to render. Taking `html` in as a\nparameter sidesteps that: whatever's returned is guaranteed to come from the same `lit-html`\nthis component already uses. A bare string skips the concern entirely.\n\nOnly need to change the icon on **some** triggers, not every one in the app? Extend the\nclass, override `render()` normally, and register the subclass under a different tag name so\nit coexists with the built-in element instead of replacing it everywhere:\n\n```ts\nclass MyCaret extends UseCaret {\n override render() {\n return html`<slot></slot><slot name=\"icon\"><my-icon name=\"chevron\"></my-icon></slot>`;\n }\n}\ncustomElements.define(\"my-caret\", MyCaret);\n```",
712
712
  "name": "UseCaret",
713
713
  "slots": [
714
714
  {
@@ -723,12 +723,21 @@
723
723
  "members": [
724
724
  {
725
725
  "kind": "field",
726
- "name": "customIcon",
726
+ "name": "icon",
727
+ "type": {
728
+ "text": "((html: HtmlTag) => TemplateResult | string) | TemplateResult | string | undefined"
729
+ },
730
+ "static": true,
731
+ "description": "Global override for a top-level trigger's caret. When set, every non-nested `<use-caret>` in\nthe page renders this inside `part(icon-default)` instead of the built-in ▼ character. A\nplain string or `TemplateResult` is used as-is; the function form is called with the\ncomponent's own `html` tag, so a returned template is guaranteed to come from the same\n`lit-html` instance this component renders with. Unset (the default) keeps the built-in ▼."
732
+ },
733
+ {
734
+ "kind": "field",
735
+ "name": "nestedIcon",
727
736
  "type": {
728
- "text": "| ((html: HtmlTag, isNested: boolean) => TemplateResult | string)\n | TemplateResult\n | string | undefined"
737
+ "text": "((html: HtmlTag) => TemplateResult | string) | TemplateResult | string | undefined"
729
738
  },
730
739
  "static": true,
731
- "description": "Global override for the default caret. When set, every `<use-caret>` in the page\nrenders this inside `part(icon-default)` instead of the built-in ▼/▶ character. A plain\nstring or `TemplateResult` is used as-is for every trigger, nested or not — for a caret that\nstill needs to point sideways on a submenu trigger, use the function form instead: it's\ncalled with the component's own `html` tag and whether *this* trigger is nested, so a\nreturned template is guaranteed to come from the same `lit-html` instance this component\nrenders with, and can still vary the same way the built-in caret does. Unset (the default)\nkeeps the built-in caret."
740
+ "description": "Global override for a submenu trigger's caret — the counterpart to icon for a\n`<use-caret>` nested inside a `use-menu`. Unset (the default) keeps the built-in ▶."
732
741
  }
733
742
  ],
734
743
  "superclass": {
@@ -2751,9 +2760,6 @@
2751
2760
  {
2752
2761
  "description": "NodeList of `use-option` elements",
2753
2762
  "name": "default"
2754
- },
2755
- {
2756
- "name": "arrow"
2757
2763
  }
2758
2764
  ],
2759
2765
  "members": [
@@ -3318,15 +3324,41 @@
3318
3324
  "declarations": [
3319
3325
  {
3320
3326
  "kind": "class",
3321
- "description": "`use-option` is a custom element that represents an option in a `use-listbox-input` custom element.\n\nThe children of this element are the content of the option and must not contain any interactive elements.",
3327
+ "description": "`use-option` is a custom element that represents an option in a `use-listbox-input` custom\nelement.\n\nThe children of this element are the content of the option and must not contain any\ninteractive elements.\n\nSelection is shown by one of two indicators, swapped based on the option's `selected` state:\na checkmark when selected, nothing (a reserved-space placeholder) when not.\n\nSwapping an indicator **per instance** is a slot: put your own element in the `selected-indicator`\nor `deselected-indicator` slot and it replaces `part(selected-indicator-default)` /\n`part(deselected-indicator-default)` entirely for that option.\n\nSwapping an indicator **for every option in the app** doesn't need touching this component's\n`render()` at all — style `::part(selected-indicator-default)` /\n`::part(deselected-indicator-default)` from an outside stylesheet, the same way any shadow DOM\npart is themed:\n\n```css\nuse-option::part(selected-indicator-default) {\n mask-image: url(\"/icons/my-checkmark.svg\");\n}\n```\n\n(When the design system's `theme.css` is loaded, this is already wired to a pair of tokens —\n`--usewc-effect-selected-indicator-mask-image` for the shape and\n`--usewc-effect-selected-indicator-color` for what paints it (`--usewc-effect-deselected-…`\nfor the other state) — so redefining those custom properties has the same effect without\nwriting a `::part()` rule at all.)\n\n`::part()` only reaches CSS-expressible changes (a mask, a background, a transform). For\nanything else — different markup, an icon font, logic beyond a mask — set the static\n`selectedIcon` and `deselectedIcon` hooks. Every `<use-option>` in the page picks them up;\nconsumers keep writing the same tag, no new element to register or remember to use instead.\nSet them once, as early as possible (an app's entry point, before any options connect) — Lit\ncalls `render()` fresh on every update, so an already-connected instance only reflects the\nchange once something causes it to re-render:\n\n```ts\n// A bare string or TemplateResult is used as-is, on every option:\nUseOption.selectedIcon = \"✓\";\nUseOption.deselectedIcon = \"\";\n\n// A function is called on every render instead:\nUseOption.selectedIcon = (html) => html`<my-icon name=\"check\"></my-icon>`;\n```\n\nThe function form is called with the component's own `html` tag as its (optional) first\nargument rather than requiring the caller to import one — a page can end up with more than one\ncopy of `lit-html` in its module graph, and a `TemplateResult` built from a *different* copy\nthan the one this component renders with can silently fail to render. Taking `html` in as a\nparameter sidesteps that: whatever's returned is guaranteed to come from the same `lit-html`\nthis component already uses. A bare string skips the concern entirely.\n\nA set icon still renders under `part(selected-indicator-default)` /\n`part(deselected-indicator-default)` — the same part the design system's theme masks into the\nbuilt-in glyph. Combining a static hook with `theme.css` means the theme's masking still\napplies to the custom content; reset the relevant `mask-image` token or `::part()` rule if that\nfights with what was set.\n\nOnly need to change the icon on **some** options, not every one in the app? Extend the class,\noverride `render()` normally, and register the subclass under a different tag name so it\ncoexists with the built-in element instead of replacing it everywhere:\n\n```ts\nclass MyOption extends UseOption {\n override render() {\n return html`<slot></slot><slot name=\"selected-indicator\"><my-icon name=\"check\"></my-icon></slot>`;\n }\n}\ncustomElements.define(\"my-option\", MyOption);\n```",
3322
3328
  "name": "UseOption",
3323
3329
  "slots": [
3324
3330
  {
3331
+ "description": "Replaces the default checkmark shown when the option is selected.",
3325
3332
  "name": "selected-indicator"
3326
3333
  },
3327
- {}
3334
+ {
3335
+ "description": "Replaces the default (empty) indicator shown when the option is not selected.",
3336
+ "name": "deselected-indicator"
3337
+ },
3338
+ {
3339
+ "description": "Option content.",
3340
+ "name": "default"
3341
+ }
3328
3342
  ],
3329
3343
  "members": [
3344
+ {
3345
+ "kind": "field",
3346
+ "name": "selectedIcon",
3347
+ "type": {
3348
+ "text": "IconOverride | undefined"
3349
+ },
3350
+ "static": true,
3351
+ "description": "Global override for the selected-state indicator. When set, every `<use-option>` in the page\nrenders this inside `part(selected-indicator-default)` instead of the built-in ✔ character.\nUnset (the default) keeps the built-in checkmark."
3352
+ },
3353
+ {
3354
+ "kind": "field",
3355
+ "name": "deselectedIcon",
3356
+ "type": {
3357
+ "text": "IconOverride | undefined"
3358
+ },
3359
+ "static": true,
3360
+ "description": "Global override for the deselected-state indicator — the counterpart to selectedIcon.\nUnset (the default) keeps the built-in (empty) placeholder."
3361
+ },
3330
3362
  {
3331
3363
  "kind": "field",
3332
3364
  "name": "selected",
@@ -3728,7 +3760,7 @@
3728
3760
  "declarations": [
3729
3761
  {
3730
3762
  "kind": "class",
3731
- "description": "Custom input control for managing selected values in a hierarchy.",
3763
+ "description": "Custom input control for managing selected values in a hierarchy.\n\nA convenience for distributing a custom indicator to every item at once: slot an icon into\n`use-tree` itself under `expanded-indicator`, `collapsed-indicator`, `selected-indicator`, or\n`deselected-indicator`, and it's cloned into each lazily-discovered `use-treeitem`'s matching\nslot. This is per-item slotting, just done once for the whole tree — it doesn't replace\n`use-treeitem`'s own static hooks (`expandedIcon`, `collapsedIcon`, `selectedIcon`,\n`deselectedIcon`), which still apply for indicators the tree hasn't cloned one over.",
3732
3764
  "name": "UseTree",
3733
3765
  "slots": [
3734
3766
  {
@@ -3736,7 +3768,20 @@
3736
3768
  "name": "default"
3737
3769
  },
3738
3770
  {
3739
- "name": "arrow"
3771
+ "description": "Cloned into every item's `expanded-indicator` slot.",
3772
+ "name": "expanded-indicator"
3773
+ },
3774
+ {
3775
+ "description": "Cloned into every item's `collapsed-indicator` slot.",
3776
+ "name": "collapsed-indicator"
3777
+ },
3778
+ {
3779
+ "description": "Cloned into every item's `selected-indicator` slot.",
3780
+ "name": "selected-indicator"
3781
+ },
3782
+ {
3783
+ "description": "Cloned into every item's `deselected-indicator` slot.",
3784
+ "name": "deselected-indicator"
3740
3785
  }
3741
3786
  ],
3742
3787
  "members": [
@@ -3874,15 +3919,67 @@
3874
3919
  "declarations": [
3875
3920
  {
3876
3921
  "kind": "class",
3877
- "description": "`use-option` is a custom element that represents an option in a `use-listbox-input` custom element.\n\nThe children of this element are the content of the option and must not contain any interactive elements.\n\n## To Do\n\n- [ ] When the parent is no longer disabled, update the items without their own `[disabled]`",
3922
+ "description": "`use-treeitem` is a custom element that represents a node in a `use-tree` custom element.\n\nThe children of this element are the content of the item, plus any nested `use-treeitem`\nelements representing its children, and must not otherwise contain interactive elements.\n\nFour indicators render alongside an item's content: an expanded/collapsed disclosure glyph\n(only one of the pair shows, depending on state), and a selected/deselected glyph (same).\n\nSwapping an indicator **per instance** is a slot: put your own element in the\n`expanded-indicator`, `collapsed-indicator`, `selected-indicator`, or `deselected-indicator`\nslot and it replaces that indicator's `part(*-default)` entirely for that item. `use-tree`\nalso offers a shortcut for this: slot an icon into `use-tree` itself and it's cloned into every\nitem's matching slot — see `use-tree`'s own docs.\n\nSwapping an indicator **for every item in the app** doesn't need touching this component's\n`render()` at all — style `::part(*-indicator-default)` from an outside stylesheet, the same\nway any shadow DOM part is themed:\n\n```css\nuse-treeitem::part(collapsed-indicator-default) {\n mask-image: url(\"/icons/my-chevron.svg\");\n}\n```\n\n(When the design system's `theme.css` is loaded, this is already wired to tokens — a\nmask-image/color pair per indicator: `--usewc-effect-tree-item-expanded-indicator-mask-image`\n/ `-color`, `--usewc-effect-tree-item-collapsed-indicator-mask-image` / `-color`, and\n`--usewc-effect-selected-indicator-mask-image` / `-color` (`--usewc-effect-deselected-…` for\nthe other state) — so redefining those custom properties has the same effect without writing a\n`::part()` rule at all.)\n\n`::part()` only reaches CSS-expressible changes (a mask, a background, a transform). For\nanything else — different markup, an icon font, logic beyond a mask — set the matching static\nhook: `expandedIcon`, `collapsedIcon`, `selectedIcon`, `deselectedIcon`. Every `<use-treeitem>`\nin the page picks them up; consumers keep writing the same tag, no new element to register or\nremember to use instead. Set them once, as early as possible (an app's entry point, before any\nitems connect) — Lit calls `render()` fresh on every update, so an already-connected instance\nonly reflects the change once something causes it to re-render:\n\n```ts\n// A bare string or TemplateResult is used as-is, on every item:\nUseTreeitem.collapsedIcon = \"▸\";\nUseTreeitem.expandedIcon = \"▾\";\n\n// A function is called on every render instead:\nUseTreeitem.collapsedIcon = (html) => html`<my-icon name=\"chevron-right\"></my-icon>`;\n```\n\nThe function form is called with the component's own `html` tag as its (optional) first\nargument rather than requiring the caller to import one — a page can end up with more than one\ncopy of `lit-html` in its module graph, and a `TemplateResult` built from a *different* copy\nthan the one this component renders with can silently fail to render. Taking `html` in as a\nparameter sidesteps that: whatever's returned is guaranteed to come from the same `lit-html`\nthis component already uses. A bare string skips the concern entirely.\n\nA set icon still renders under that indicator's own `part(*-indicator-default)` — the same part\nthe design system's theme masks into the built-in glyph. Combining a static hook with\n`theme.css` means the theme's masking still applies to the custom content; reset the relevant\n`mask-image` token or `::part()` rule if that fights with what was set.\n\nOnly need to change an indicator on **some** items, not every one in the app? Extend the class,\noverride `render()` normally, and register the subclass under a different tag name so it\ncoexists with the built-in element instead of replacing it everywhere.\n\n## To Do\n\n- [ ] When the parent is no longer disabled, update the items without their own `[disabled]`",
3878
3923
  "name": "UseTreeitem",
3879
3924
  "slots": [
3880
3925
  {
3926
+ "description": "Replaces the default disclosure glyph shown when the item is expanded.",
3927
+ "name": "expanded-indicator"
3928
+ },
3929
+ {
3930
+ "description": "Replaces the default disclosure glyph shown when the item is collapsed.",
3931
+ "name": "collapsed-indicator"
3932
+ },
3933
+ {
3934
+ "description": "Replaces the default glyph shown when the item is selected.",
3881
3935
  "name": "selected-indicator"
3882
3936
  },
3883
- {}
3937
+ {
3938
+ "description": "Replaces the default (empty) glyph shown when the item is not selected.",
3939
+ "name": "deselected-indicator"
3940
+ },
3941
+ {
3942
+ "description": "Item content, plus any nested `use-treeitem` children.",
3943
+ "name": "default"
3944
+ }
3884
3945
  ],
3885
3946
  "members": [
3947
+ {
3948
+ "kind": "field",
3949
+ "name": "expandedIcon",
3950
+ "type": {
3951
+ "text": "IconOverride | undefined"
3952
+ },
3953
+ "static": true,
3954
+ "description": "Global override for the expanded-state disclosure indicator. When set, every\n`<use-treeitem>` in the page renders this inside `part(expanded-indicator-default)` instead\nof the built-in `-` character. Unset (the default) keeps the built-in glyph."
3955
+ },
3956
+ {
3957
+ "kind": "field",
3958
+ "name": "collapsedIcon",
3959
+ "type": {
3960
+ "text": "IconOverride | undefined"
3961
+ },
3962
+ "static": true,
3963
+ "description": "Global override for the collapsed-state disclosure indicator — the counterpart to\nexpandedIcon. Unset (the default) keeps the built-in `+` character."
3964
+ },
3965
+ {
3966
+ "kind": "field",
3967
+ "name": "selectedIcon",
3968
+ "type": {
3969
+ "text": "IconOverride | undefined"
3970
+ },
3971
+ "static": true,
3972
+ "description": "Global override for the selected-state indicator. Unset (the default) keeps the built-in ✔\ncharacter."
3973
+ },
3974
+ {
3975
+ "kind": "field",
3976
+ "name": "deselectedIcon",
3977
+ "type": {
3978
+ "text": "IconOverride | undefined"
3979
+ },
3980
+ "static": true,
3981
+ "description": "Global override for the deselected-state indicator — the counterpart to\nselectedIcon. Unset (the default) keeps the built-in (empty) placeholder."
3982
+ },
3886
3983
  {
3887
3984
  "kind": "field",
3888
3985
  "name": "selected",