luxen-ui 0.18.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/cdn/color-scheme.d.ts +76 -0
- package/cdn/color-scheme.d.ts.map +1 -0
- package/cdn/color-scheme.js +2 -0
- package/cdn/color-scheme.js.map +1 -0
- package/cdn/config.d.ts +23 -0
- package/cdn/config.d.ts.map +1 -1
- package/cdn/custom-elements.json +142 -2
- package/cdn/elements/color-scheme-icon/color-scheme-icon.d.ts +55 -0
- package/cdn/elements/color-scheme-icon/color-scheme-icon.d.ts.map +1 -0
- package/cdn/elements/color-scheme-icon/color-scheme-icon.js +44 -0
- package/cdn/elements/color-scheme-icon/color-scheme-icon.js.map +1 -0
- package/cdn/elements/color-scheme-icon/index.d.ts +2 -0
- package/cdn/elements/color-scheme-icon/index.d.ts.map +1 -0
- package/cdn/elements/color-scheme-icon/index.js +2 -0
- package/cdn/elements/color-scheme-icon/index.js.map +1 -0
- package/cdn/elements/dropdown-item/dropdown-item.d.ts +9 -1
- package/cdn/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.js +32 -23
- package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
- package/cdn/elements/segmented-control/segmented-control.d.ts +2 -0
- package/cdn/elements/segmented-control/segmented-control.d.ts.map +1 -1
- package/cdn/elements/segmented-control/segmented-control.js +1 -1
- package/cdn/elements/segmented-control/segmented-control.js.map +1 -1
- package/cdn/elements/tabs/tabs.d.ts +28 -1
- package/cdn/elements/tabs/tabs.d.ts.map +1 -1
- package/cdn/elements/tabs/tabs.js +1 -1
- package/cdn/elements/tabs/tabs.js.map +1 -1
- package/cdn/index.d.ts +2 -0
- package/cdn/index.d.ts.map +1 -1
- package/cdn/index.js +1 -1
- package/cdn/registry.d.ts +1 -1
- package/cdn/registry.d.ts.map +1 -1
- package/cdn/registry.js.map +1 -1
- package/cdn/standalone.css +23 -4
- package/cdn/standalone.js.map +1 -1
- package/cdn/styles/elements/tabs/enclosed.css +9 -1
- package/cdn/styles/elements/tabs/line.css +14 -3
- package/dist/color-scheme.d.ts +76 -0
- package/dist/color-scheme.d.ts.map +1 -0
- package/dist/color-scheme.js +203 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/css/elements/tabs/enclosed.css +9 -1
- package/dist/css/elements/tabs/line.css +14 -3
- package/dist/custom-elements.json +142 -2
- package/dist/elements/color-scheme-icon/color-scheme-icon.css +98 -0
- package/dist/elements/color-scheme-icon/color-scheme-icon.d.ts +55 -0
- package/dist/elements/color-scheme-icon/color-scheme-icon.d.ts.map +1 -0
- package/dist/elements/color-scheme-icon/color-scheme-icon.js +138 -0
- package/dist/elements/color-scheme-icon/index.d.ts +2 -0
- package/dist/elements/color-scheme-icon/index.d.ts.map +1 -0
- package/dist/elements/color-scheme-icon/index.js +4 -0
- package/dist/elements/dropdown-item/dropdown-item.d.ts +9 -1
- package/dist/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
- package/dist/elements/dropdown-item/dropdown-item.js +49 -26
- package/dist/elements/segmented-control/segmented-control.d.ts +2 -0
- package/dist/elements/segmented-control/segmented-control.d.ts.map +1 -1
- package/dist/elements/segmented-control/segmented-control.js +11 -1
- package/dist/elements/tabs/tabs.d.ts +28 -1
- package/dist/elements/tabs/tabs.d.ts.map +1 -1
- package/dist/elements/tabs/tabs.js +121 -26
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/metadata/color-scheme-icon.json +66 -0
- package/dist/metadata/dropdown-item.json +9 -1
- package/dist/metadata/index.json +82 -3
- package/dist/metadata/tabs.json +6 -1
- package/dist/registry.d.ts +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/templates/elements/color-scheme-icon.md +279 -0
- package/dist/templates/elements/dropdown.md +54 -1
- package/dist/templates/elements/tabs.md +40 -7
- package/elements.json +8 -0
- package/package.json +5 -1
- package/vite-plugin.ts +71 -8
package/dist/metadata/index.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.19.0",
|
|
3
3
|
"elements": [
|
|
4
4
|
{
|
|
5
5
|
"name": "alert",
|
|
@@ -2313,6 +2313,14 @@
|
|
|
2313
2313
|
"reflects": true,
|
|
2314
2314
|
"description": "Whether this item's submenu is open. Managed by the parent `l-dropdown`."
|
|
2315
2315
|
},
|
|
2316
|
+
{
|
|
2317
|
+
"name": "checkPlacement",
|
|
2318
|
+
"attribute": "check-placement",
|
|
2319
|
+
"type": "'start' | 'end'",
|
|
2320
|
+
"default": "'start'",
|
|
2321
|
+
"reflects": true,
|
|
2322
|
+
"description": "Which side of a `type=\"checkbox\"` item carries the check mark. `start` (the\ndefault) puts it in the leading column, where it takes the place of any\n`prefix`. `end` moves it to the trailing edge, freeing that column so the\nitem can show its own icon alongside the state."
|
|
2323
|
+
},
|
|
2316
2324
|
{
|
|
2317
2325
|
"name": "hasSubmenu",
|
|
2318
2326
|
"attribute": null,
|
|
@@ -2364,7 +2372,7 @@
|
|
|
2364
2372
|
},
|
|
2365
2373
|
{
|
|
2366
2374
|
"name": "prefix",
|
|
2367
|
-
"description": "Leading content (e.g. icon)."
|
|
2375
|
+
"description": "Leading content (e.g. icon). A `type=\"checkbox\"` item ignores it unless `check-placement=\"end\"` frees the leading column."
|
|
2368
2376
|
},
|
|
2369
2377
|
{
|
|
2370
2378
|
"name": "suffix",
|
|
@@ -5325,7 +5333,7 @@
|
|
|
5325
5333
|
"events": [
|
|
5326
5334
|
{
|
|
5327
5335
|
"name": "change",
|
|
5328
|
-
"description": "Fired when the active tab changes. Bubbles. Properties: `index: number`, `name: string | null`.",
|
|
5336
|
+
"description": "Fired when the active tab changes. Bubbles. Not cancelable. Properties: `index: number`, `name: string | null`.",
|
|
5329
5337
|
"cancelable": false
|
|
5330
5338
|
}
|
|
5331
5339
|
],
|
|
@@ -5371,6 +5379,11 @@
|
|
|
5371
5379
|
"title": "Mark the initially-active tab with `aria-selected=\"true\"` so it is styled as",
|
|
5372
5380
|
"language": "html",
|
|
5373
5381
|
"code": "selected before the script upgrades the element (progressive enhancement).\n```html\n<l-tabs variant=\"enclosed\">\n <div>\n <button aria-selected=\"true\">Tab 1</button>\n <button>Tab 2</button>\n </div>\n <div>Content 1</div>\n <div>Content 2</div>\n</l-tabs>\n```"
|
|
5382
|
+
},
|
|
5383
|
+
{
|
|
5384
|
+
"title": "A tab button marked `disabled` (or `aria-disabled=\"true\"`) is skipped by",
|
|
5385
|
+
"language": "html",
|
|
5386
|
+
"code": "arrow keys, `Home` and `End`, and cannot be selected by a click or by\n`value` — it still owns its panel, so the tab/panel order is unchanged.\n```html\n<button disabled>Billing</button>\n```"
|
|
5374
5387
|
}
|
|
5375
5388
|
]
|
|
5376
5389
|
},
|
|
@@ -5667,6 +5680,72 @@
|
|
|
5667
5680
|
}
|
|
5668
5681
|
]
|
|
5669
5682
|
},
|
|
5683
|
+
{
|
|
5684
|
+
"name": "color-scheme-icon",
|
|
5685
|
+
"displayName": "Color scheme icon",
|
|
5686
|
+
"type": "shadow",
|
|
5687
|
+
"isCustomElement": true,
|
|
5688
|
+
"tag": "l-color-scheme-icon",
|
|
5689
|
+
"nativeTag": null,
|
|
5690
|
+
"selector": "l-color-scheme-icon",
|
|
5691
|
+
"subItemOf": null,
|
|
5692
|
+
"summary": "A sun that morphs into a moon. Purely presentational: it shows a\ncolor scheme, it does not choose or store one.\n\nThe two glyphs are a single shape — the disc grows, a mask slides in to carve\nthe crescent, and the rays retract in a ripple — so the change reads as one\nobject turning rather than two icons swapping. That is why this is authored\nSVG rather than an `<l-icon>`: an Iconify icon is opaque, and you cannot grow\none into another or animate a mask inside it.\n\nUse it wherever a scheme needs a glyph and the surrounding element is already\nthe control — a `menuitemcheckbox` row, a button, a legend. With no `scheme`\nit follows the page's scheme from `luxen-ui/color-scheme`, so it needs no\nwiring; set `scheme` to pin it.",
|
|
5693
|
+
"status": "stable",
|
|
5694
|
+
"appearances": [],
|
|
5695
|
+
"import": {
|
|
5696
|
+
"css": null,
|
|
5697
|
+
"js": "luxen-ui/color-scheme-icon"
|
|
5698
|
+
},
|
|
5699
|
+
"properties": [
|
|
5700
|
+
{
|
|
5701
|
+
"name": "scheme",
|
|
5702
|
+
"attribute": "scheme",
|
|
5703
|
+
"type": "ColorScheme | undefined",
|
|
5704
|
+
"default": null,
|
|
5705
|
+
"reflects": true,
|
|
5706
|
+
"description": "Which glyph to show. Leave it unset and the icon follows the page's scheme\n(`colorScheme.current` — a stored override, else the OS preference), so it\nstays in step with every other control without wiring. Set it to pin the\nglyph, for a legend or a preview that must not move."
|
|
5707
|
+
},
|
|
5708
|
+
{
|
|
5709
|
+
"name": "label",
|
|
5710
|
+
"attribute": "label",
|
|
5711
|
+
"type": "string | undefined",
|
|
5712
|
+
"default": null,
|
|
5713
|
+
"reflects": false,
|
|
5714
|
+
"description": "Accessible label. When set, the glyph becomes meaningful (`role=\"img\"`);\nwhen absent it is decorative and hidden from assistive tech — the right\ndefault inside a row or button that already carries the name."
|
|
5715
|
+
}
|
|
5716
|
+
],
|
|
5717
|
+
"attributes": [],
|
|
5718
|
+
"events": [],
|
|
5719
|
+
"methods": [],
|
|
5720
|
+
"slots": [],
|
|
5721
|
+
"cssClasses": [],
|
|
5722
|
+
"cssParts": [
|
|
5723
|
+
{
|
|
5724
|
+
"name": "base",
|
|
5725
|
+
"description": "The root `<svg>`, for overriding the morph's timing or tilt."
|
|
5726
|
+
}
|
|
5727
|
+
],
|
|
5728
|
+
"cssProperties": [
|
|
5729
|
+
{
|
|
5730
|
+
"name": "--size",
|
|
5731
|
+
"default": "1em",
|
|
5732
|
+
"description": "Width and height of the glyph."
|
|
5733
|
+
},
|
|
5734
|
+
{
|
|
5735
|
+
"name": "--color",
|
|
5736
|
+
"default": "currentColor",
|
|
5737
|
+
"description": "Glyph color."
|
|
5738
|
+
}
|
|
5739
|
+
],
|
|
5740
|
+
"commands": [],
|
|
5741
|
+
"examples": [
|
|
5742
|
+
{
|
|
5743
|
+
"title": "In a menu row, where the row is the control",
|
|
5744
|
+
"language": "html",
|
|
5745
|
+
"code": "```html\n<l-dropdown-item type=\"checkbox\" checked>\n<l-color-scheme-icon slot=\"prefix\" scheme=\"dark\"></l-color-scheme-icon>\nDark theme\n</l-dropdown-item>\n```"
|
|
5746
|
+
}
|
|
5747
|
+
]
|
|
5748
|
+
},
|
|
5670
5749
|
{
|
|
5671
5750
|
"name": "toast",
|
|
5672
5751
|
"displayName": "Toast",
|
package/dist/metadata/tabs.json
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"events": [
|
|
56
56
|
{
|
|
57
57
|
"name": "change",
|
|
58
|
-
"description": "Fired when the active tab changes. Bubbles. Properties: `index: number`, `name: string | null`.",
|
|
58
|
+
"description": "Fired when the active tab changes. Bubbles. Not cancelable. Properties: `index: number`, `name: string | null`.",
|
|
59
59
|
"cancelable": false
|
|
60
60
|
}
|
|
61
61
|
],
|
|
@@ -101,6 +101,11 @@
|
|
|
101
101
|
"title": "Mark the initially-active tab with `aria-selected=\"true\"` so it is styled as",
|
|
102
102
|
"language": "html",
|
|
103
103
|
"code": "selected before the script upgrades the element (progressive enhancement).\n```html\n<l-tabs variant=\"enclosed\">\n <div>\n <button aria-selected=\"true\">Tab 1</button>\n <button>Tab 2</button>\n </div>\n <div>Content 1</div>\n <div>Content 2</div>\n</l-tabs>\n```"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"title": "A tab button marked `disabled` (or `aria-disabled=\"true\"`) is skipped by",
|
|
107
|
+
"language": "html",
|
|
108
|
+
"code": "arrow keys, `Home` and `End`, and cannot be selected by a click or by\n`value` — it still owns its panel, so the tab/panel order is unchanged.\n```html\n<button disabled>Billing</button>\n```"
|
|
104
109
|
}
|
|
105
110
|
]
|
|
106
111
|
}
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Every component base name the library ships. */
|
|
2
|
-
export type ElementBaseName = 'alert' | 'alert-dialog' | 'avatar' | 'badge' | 'button-group' | 'carousel' | 'carousel-item' | 'combobox' | 'dialog' | 'divider' | 'drawer' | 'dropdown' | 'dropdown-item' | 'dropdown-label' | 'form-field' | 'icon' | 'input-group' | 'input-otp' | 'input-stepper' | 'popover' | 'prose-editor' | 'skeleton' | 'slider' | 'spinner' | 'rating' | 'segmented-control' | 'select' | 'sticky-bar' | 'stories' | 'story' | 'stories-viewer' | 'tabs' | 'tag' | 'toast' | 'toast-item' | 'tooltip' | 'tree' | 'tree-item';
|
|
2
|
+
export type ElementBaseName = 'alert' | 'alert-dialog' | 'avatar' | 'badge' | 'button-group' | 'carousel' | 'carousel-item' | 'combobox' | 'dialog' | 'divider' | 'drawer' | 'dropdown' | 'dropdown-item' | 'dropdown-label' | 'form-field' | 'icon' | 'input-group' | 'input-otp' | 'input-stepper' | 'popover' | 'prose-editor' | 'skeleton' | 'slider' | 'spinner' | 'rating' | 'segmented-control' | 'select' | 'sticky-bar' | 'stories' | 'story' | 'stories-viewer' | 'tabs' | 'tag' | 'color-scheme-icon' | 'toast' | 'toast-item' | 'tooltip' | 'tree' | 'tree-item';
|
|
3
3
|
/** Set the global prefix. Must be called before defining elements. */
|
|
4
4
|
export declare function setPrefix(prefix: {
|
|
5
5
|
element: string;
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/html/registry.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,MAAM,MAAM,eAAe,GACvB,OAAO,GACP,cAAc,GACd,QAAQ,GACR,OAAO,GACP,cAAc,GACd,UAAU,GACV,eAAe,GACf,UAAU,GACV,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,eAAe,GACf,gBAAgB,GAChB,YAAY,GACZ,MAAM,GACN,aAAa,GACb,WAAW,GACX,eAAe,GACf,SAAS,GACT,cAAc,GACd,UAAU,GACV,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,mBAAmB,GACnB,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,OAAO,GACP,gBAAgB,GAChB,MAAM,GACN,KAAK,GACL,OAAO,GACP,YAAY,GACZ,SAAS,GACT,MAAM,GACN,WAAW,CAAC;AAMhB,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGxE;AAED,gCAAgC;AAChC,wBAAgB,SAAS,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAE5D;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAEzD;AAED,sEAAsE;AACtE,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAID,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAE/D;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAE9D"}
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/html/registry.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,MAAM,MAAM,eAAe,GACvB,OAAO,GACP,cAAc,GACd,QAAQ,GACR,OAAO,GACP,cAAc,GACd,UAAU,GACV,eAAe,GACf,UAAU,GACV,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,eAAe,GACf,gBAAgB,GAChB,YAAY,GACZ,MAAM,GACN,aAAa,GACb,WAAW,GACX,eAAe,GACf,SAAS,GACT,cAAc,GACd,UAAU,GACV,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,mBAAmB,GACnB,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,OAAO,GACP,gBAAgB,GAChB,MAAM,GACN,KAAK,GACL,mBAAmB,GACnB,OAAO,GACP,YAAY,GACZ,SAAS,GACT,MAAM,GACN,WAAW,CAAC;AAMhB,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGxE;AAED,gCAAgC;AAChC,wBAAgB,SAAS,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAE5D;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAEzD;AAED,sEAAsE;AACtE,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAID,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAE/D;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAE9D"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Color scheme icon
|
|
6
|
+
|
|
7
|
+
A sun that morphs into a moon.
|
|
8
|
+
|
|
9
|
+
**`<l-color-scheme-icon>`** — Custom Element · Shadow DOM
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<l-color-scheme-icon scheme="dark"></l-color-scheme-icon>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The two glyphs are one shape: the disc grows, a mask slides in to carve the crescent, and the rays retract in a ripple. Nothing is added or removed, so the change reads as a single object turning rather than two icons swapping.
|
|
16
|
+
|
|
17
|
+
Its home is an icon-only button in a header — the button owns the role, the name and the pressed state; the glyph shows which scheme is in effect.
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<header
|
|
21
|
+
class="flex w-full items-center justify-between gap-4 rounded-xl border px-4 py-3 border-[var(--l-color-border)] bg-[var(--l-color-surface)]"
|
|
22
|
+
>
|
|
23
|
+
<div class="flex items-center gap-6">
|
|
24
|
+
<span class="text-sm font-semibold text-[var(--l-color-text-primary)]">Acme</span>
|
|
25
|
+
|
|
26
|
+
<nav class="flex gap-4 text-sm text-[var(--l-color-text-secondary)]">
|
|
27
|
+
<a href="#">Dashboard</a>
|
|
28
|
+
<a href="#">Reports</a>
|
|
29
|
+
<a href="#">Settings</a>
|
|
30
|
+
</nav>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<button
|
|
34
|
+
class="l-button"
|
|
35
|
+
data-icon-only
|
|
36
|
+
aria-pressed="false"
|
|
37
|
+
aria-label="Dark theme"
|
|
38
|
+
>
|
|
39
|
+
<l-color-scheme-icon></l-color-scheme-icon>
|
|
40
|
+
</button>
|
|
41
|
+
</header>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
It is **presentational only** — it shows a scheme, it never chooses or stores one. With no `scheme` it follows [`colorScheme.current`](/overview/color-scheme), so it stays in step with every other control on the page without wiring; set `scheme` to pin it.
|
|
45
|
+
|
|
46
|
+
The example above uses inline handlers to stay readable. In an application, the click and the pressed state are what you wire — the glyph needs nothing:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
import { colorScheme } from 'luxen-ui/color-scheme';
|
|
50
|
+
|
|
51
|
+
const button = document.querySelector('[aria-label="Dark theme"]');
|
|
52
|
+
|
|
53
|
+
button.addEventListener('click', () => colorScheme.toggle());
|
|
54
|
+
|
|
55
|
+
// Called once with the current scheme, then on every change — no initial pass.
|
|
56
|
+
colorScheme.subscribe((scheme) => {
|
|
57
|
+
button.setAttribute('aria-pressed', String(scheme === 'dark'));
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Which scheme does the glyph show?
|
|
62
|
+
|
|
63
|
+
The one **in effect**, not the one a click would move to: dark mode shows the moon. That is what makes it correct inside a `role="switch"` thumb or beside an `aria-pressed` button, where the state is already announced and the glyph should agree with it.
|
|
64
|
+
|
|
65
|
+
To advertise the destination instead — a sun meaning "switch to light" — pass the opposite of your current scheme. Pick one convention and hold it across the app; mixing them is how a toggle stops being readable.
|
|
66
|
+
|
|
67
|
+
## Options
|
|
68
|
+
|
|
69
|
+
### Scheme
|
|
70
|
+
|
|
71
|
+
Leave `scheme` unset and the icon follows the page's [color scheme](/overview/color-scheme) — a stored override, else the OS preference — updating itself when it changes anywhere, including in another tab.
|
|
72
|
+
|
|
73
|
+
Set `scheme` and the store is out of the picture: the glyph shows what you tell it and nothing else moves it. That is the hook for an app that already owns its light/dark state — a framework store, a class on `<html>` written by the server, a user setting fetched from an API — and also what pins a glyph in a legend or a preview.
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
// Your state, your rules. The icon just draws it.
|
|
77
|
+
theme.subscribe((value) => (icon.scheme = value));
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<div class="flex items-center gap-8">
|
|
82
|
+
<div class="flex flex-col items-center gap-2">
|
|
83
|
+
<l-color-scheme-icon
|
|
84
|
+
scheme="light"
|
|
85
|
+
class="[--size:2rem]"
|
|
86
|
+
></l-color-scheme-icon>
|
|
87
|
+
<code class="text-xs text-[var(--l-color-text-secondary)]">scheme="light"</code>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="flex flex-col items-center gap-2">
|
|
91
|
+
<l-color-scheme-icon
|
|
92
|
+
scheme="dark"
|
|
93
|
+
class="[--size:2rem]"
|
|
94
|
+
></l-color-scheme-icon>
|
|
95
|
+
<code class="text-xs text-[var(--l-color-text-secondary)]">scheme="dark"</code>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<div class="flex flex-col items-center gap-2">
|
|
99
|
+
<l-color-scheme-icon class="[--size:2rem]"></l-color-scheme-icon>
|
|
100
|
+
<code class="text-xs text-[var(--l-color-text-secondary)]">follows the page</code>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Size & color
|
|
106
|
+
|
|
107
|
+
`--size` sets width and height (default `1em`, so it scales with the surrounding text). `--color` defaults to `currentColor`.
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<div class="flex items-center gap-8">
|
|
111
|
+
<l-color-scheme-icon scheme="dark"></l-color-scheme-icon>
|
|
112
|
+
|
|
113
|
+
<l-color-scheme-icon
|
|
114
|
+
scheme="dark"
|
|
115
|
+
class="[--size:2rem]"
|
|
116
|
+
></l-color-scheme-icon>
|
|
117
|
+
|
|
118
|
+
<l-color-scheme-icon
|
|
119
|
+
scheme="dark"
|
|
120
|
+
class="[--size:3rem] [--color:var(--l-color-text-info)]"
|
|
121
|
+
></l-color-scheme-icon>
|
|
122
|
+
</div>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Accessible name
|
|
126
|
+
|
|
127
|
+
The icon is decorative by default and hidden from assistive tech, which is what you want inside a row or button that already carries the name. Set `label` to make it meaningful — it then exposes `role="img"` with that name.
|
|
128
|
+
|
|
129
|
+
Both icons below look identical; the difference exists only in the accessibility tree.
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<div class="flex items-center gap-8">
|
|
133
|
+
<button
|
|
134
|
+
class="l-button"
|
|
135
|
+
data-icon-only
|
|
136
|
+
aria-label="Dark theme"
|
|
137
|
+
>
|
|
138
|
+
<l-color-scheme-icon></l-color-scheme-icon>
|
|
139
|
+
</button>
|
|
140
|
+
|
|
141
|
+
<l-color-scheme-icon
|
|
142
|
+
label="Currently in dark theme"
|
|
143
|
+
scheme="dark"
|
|
144
|
+
class="[--size:2rem]"
|
|
145
|
+
></l-color-scheme-icon>
|
|
146
|
+
</div>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Examples
|
|
150
|
+
|
|
151
|
+
### In a menu row
|
|
152
|
+
|
|
153
|
+
Put it in a `type="checkbox"` [dropdown item](/elements/dropdown#theme-row) and the row is the control: whole row clickable, `menuitemcheckbox` role, menu stays open. The icon stays decorative.
|
|
154
|
+
|
|
155
|
+
Add `check-placement="end"` so the check moves to the trailing edge and leaves the leading column free — the glyph then lines up with every other row's icon.
|
|
156
|
+
|
|
157
|
+
```html
|
|
158
|
+
<header
|
|
159
|
+
class="flex w-full items-center justify-between gap-4 rounded-xl border px-4 py-3 border-[var(--l-color-border)] bg-[var(--l-color-surface)]"
|
|
160
|
+
>
|
|
161
|
+
<div class="flex items-center gap-6">
|
|
162
|
+
<span class="text-sm font-semibold text-[var(--l-color-text-primary)]">Acme</span>
|
|
163
|
+
|
|
164
|
+
<nav class="flex gap-4 text-sm text-[var(--l-color-text-secondary)]">
|
|
165
|
+
<a href="#">Dashboard</a>
|
|
166
|
+
<a href="#">Reports</a>
|
|
167
|
+
</nav>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<l-dropdown placement="bottom-end">
|
|
171
|
+
<l-avatar
|
|
172
|
+
slot="trigger"
|
|
173
|
+
interactive
|
|
174
|
+
name="Jane Cooper"
|
|
175
|
+
aria-label="Account"
|
|
176
|
+
class="[--appearance:circle] [--color:var(--l-color-blue-200)]"
|
|
177
|
+
></l-avatar>
|
|
178
|
+
|
|
179
|
+
<div
|
|
180
|
+
slot="header"
|
|
181
|
+
class="flex flex-col px-2 py-1.5"
|
|
182
|
+
>
|
|
183
|
+
<span class="text-sm font-medium text-[var(--l-color-text-primary)]">Jane Cooper</span>
|
|
184
|
+
<span class="text-xs text-[var(--l-color-text-secondary)]">jane.cooper@acme.com</span>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<l-divider></l-divider>
|
|
188
|
+
|
|
189
|
+
<l-dropdown-item
|
|
190
|
+
type="checkbox"
|
|
191
|
+
check-placement="end"
|
|
192
|
+
value="theme"
|
|
193
|
+
>
|
|
194
|
+
<l-color-scheme-icon slot="prefix"></l-color-scheme-icon>
|
|
195
|
+
Dark theme
|
|
196
|
+
</l-dropdown-item>
|
|
197
|
+
|
|
198
|
+
<l-dropdown-item
|
|
199
|
+
type="checkbox"
|
|
200
|
+
check-placement="end"
|
|
201
|
+
value="compact"
|
|
202
|
+
checked
|
|
203
|
+
>
|
|
204
|
+
<iconify-icon
|
|
205
|
+
slot="prefix"
|
|
206
|
+
icon="lucide:rows-3"
|
|
207
|
+
></iconify-icon>
|
|
208
|
+
Compact rows
|
|
209
|
+
</l-dropdown-item>
|
|
210
|
+
|
|
211
|
+
<l-divider></l-divider>
|
|
212
|
+
|
|
213
|
+
<l-dropdown-item value="signout">
|
|
214
|
+
<iconify-icon
|
|
215
|
+
slot="prefix"
|
|
216
|
+
icon="lucide:log-out"
|
|
217
|
+
></iconify-icon>
|
|
218
|
+
Sign out
|
|
219
|
+
</l-dropdown-item>
|
|
220
|
+
</l-dropdown>
|
|
221
|
+
</header>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Here the row carries the state, so `checked` is what you keep in step:
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
import { colorScheme } from 'luxen-ui/color-scheme';
|
|
228
|
+
|
|
229
|
+
const item = document.querySelector('l-dropdown-item[value="theme"]');
|
|
230
|
+
|
|
231
|
+
document.querySelector('l-dropdown').addEventListener('select', (event) => {
|
|
232
|
+
if (event.item === item) colorScheme.set(item.checked ? 'dark' : 'light');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
colorScheme.subscribe((scheme) => (item.checked = scheme === 'dark'));
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Do not put a [switch](/elements/switch) in a menu row instead: a `role="menu"` may only own `menuitem`, `menuitemcheckbox` and `menuitemradio` children, and a nested control gives the row two competing click targets.
|
|
239
|
+
|
|
240
|
+
## Accessibility
|
|
241
|
+
|
|
242
|
+
### Criteria
|
|
243
|
+
|
|
244
|
+
- **Decorative by default** — With no `label` the icon is `aria-hidden` — the surrounding row or button carries the accessible name, so nothing is announced twice
|
|
245
|
+
- **Meaningful when named** — Setting `label` exposes `role=img` with that name, for the rare case where the glyph stands alone
|
|
246
|
+
- **Reduced motion** — The morph collapses to 0ms under `prefers-reduced-motion`; the glyph still changes, it just does not animate
|
|
247
|
+
|
|
248
|
+
### Rules
|
|
249
|
+
|
|
250
|
+
- Leave the icon decorative when it sits inside a control that already has a name — a menu row, a button
|
|
251
|
+
- Never rely on the glyph alone to convey the state; pair it with a label or an `aria-checked` row
|
|
252
|
+
- Leave `scheme` unset unless the glyph must be pinned — it then follows the page and cannot disagree with it
|
|
253
|
+
|
|
254
|
+
## API reference
|
|
255
|
+
|
|
256
|
+
### Importing
|
|
257
|
+
|
|
258
|
+
```js
|
|
259
|
+
import 'luxen-ui/color-scheme-icon';
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Properties
|
|
263
|
+
|
|
264
|
+
- **scheme**: `ColorScheme | undefined` — Which glyph to show. Leave it unset and the icon follows the page's scheme
|
|
265
|
+
(`colorScheme.current` — a stored override, else the OS preference), so it
|
|
266
|
+
stays in step with every other control without wiring. Set it to pin the
|
|
267
|
+
glyph, for a legend or a preview that must not move.
|
|
268
|
+
- **label**: `string | undefined` — Accessible label. When set, the glyph becomes meaningful (`role="img"`);
|
|
269
|
+
when absent it is decorative and hidden from assistive tech — the right
|
|
270
|
+
default inside a row or button that already carries the name.
|
|
271
|
+
|
|
272
|
+
### CSS custom properties
|
|
273
|
+
|
|
274
|
+
- `--size` (default: `1em`) — Width and height of the glyph.
|
|
275
|
+
- `--color` (default: `currentColor`) — Glyph color.
|
|
276
|
+
|
|
277
|
+
### CSS parts
|
|
278
|
+
|
|
279
|
+
- `base` — The root `<svg>`, for overriding the morph's timing or tilt.
|
|
@@ -394,6 +394,55 @@ Use the `header` slot for a profile row, the `prefix` slot on each `<l-dropdown-
|
|
|
394
394
|
</l-dropdown>
|
|
395
395
|
```
|
|
396
396
|
|
|
397
|
+
### Theme row {#theme-row}
|
|
398
|
+
|
|
399
|
+
A display setting belongs in the menu as a `type="checkbox"` item, not as a switch nested inside one. The whole row is then the control — one click target, `role="menuitemcheckbox"`, and the menu stays open so the change is visible immediately. A nested switch would give the row two competing targets, and `role="menu"` may only own `menuitem`, `menuitemcheckbox` and `menuitemradio` children.
|
|
400
|
+
|
|
401
|
+
A checkbox item puts its check in the leading column, which leaves no room for an icon. Set `check-placement="end"` to move the check to the trailing edge and free that column, so the row keeps its icon **and** shows whether it is on — and its icon lines up with every other row in the menu.
|
|
402
|
+
|
|
403
|
+
[`<l-color-scheme-icon>`](/elements/color-scheme-icon) in the `prefix` slot then morphs from sun to moon as the row toggles. It stays decorative: the row carries the name, the role and the state.
|
|
404
|
+
|
|
405
|
+
```html
|
|
406
|
+
<l-dropdown>
|
|
407
|
+
<l-avatar
|
|
408
|
+
slot="trigger"
|
|
409
|
+
interactive
|
|
410
|
+
name="Jane Cooper"
|
|
411
|
+
aria-label="Account"
|
|
412
|
+
style="--appearance: circle; --color: var(--color-purple-200)"
|
|
413
|
+
></l-avatar>
|
|
414
|
+
|
|
415
|
+
<l-dropdown-item value="whats-new">
|
|
416
|
+
<iconify-icon
|
|
417
|
+
slot="prefix"
|
|
418
|
+
icon="lucide:megaphone"
|
|
419
|
+
></iconify-icon>
|
|
420
|
+
What's new
|
|
421
|
+
</l-dropdown-item>
|
|
422
|
+
|
|
423
|
+
<l-dropdown-item
|
|
424
|
+
type="checkbox"
|
|
425
|
+
check-placement="end"
|
|
426
|
+
value="theme"
|
|
427
|
+
>
|
|
428
|
+
<l-color-scheme-icon slot="prefix"></l-color-scheme-icon>
|
|
429
|
+
Dark theme
|
|
430
|
+
</l-dropdown-item>
|
|
431
|
+
|
|
432
|
+
<l-divider></l-divider>
|
|
433
|
+
|
|
434
|
+
<l-dropdown-item value="signout">
|
|
435
|
+
<iconify-icon
|
|
436
|
+
slot="prefix"
|
|
437
|
+
icon="lucide:log-out"
|
|
438
|
+
></iconify-icon>
|
|
439
|
+
Sign out
|
|
440
|
+
</l-dropdown-item>
|
|
441
|
+
</l-dropdown>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
The row reflects and changes the page's [color scheme](/overview/color-scheme); the same glyph works in a header button, outside any menu.
|
|
445
|
+
|
|
397
446
|
## Accessibility
|
|
398
447
|
|
|
399
448
|
### Criteria
|
|
@@ -487,12 +536,16 @@ while open.
|
|
|
487
536
|
- **variant**: `'destructive' | undefined` (default: `undefined`) — Visual variant. `destructive` marks a delete-style action in red.
|
|
488
537
|
- **checked**: `boolean` (default: `false`) — Whether the checkbox item is checked.
|
|
489
538
|
- **submenu-open**: `boolean` (default: `false`) — Whether this item's submenu is open. Managed by the parent `l-dropdown`.
|
|
539
|
+
- **check-placement**: `'start' | 'end'` (default: `'start'`) — Which side of a `type="checkbox"` item carries the check mark. `start` (the
|
|
540
|
+
default) puts it in the leading column, where it takes the place of any
|
|
541
|
+
`prefix`. `end` moves it to the trailing edge, freeing that column so the
|
|
542
|
+
item can show its own icon alongside the state.
|
|
490
543
|
- **hasSubmenu**: `boolean` — Whether this item has nested `slot="submenu"` items.
|
|
491
544
|
|
|
492
545
|
### `dropdown-item` Slots
|
|
493
546
|
|
|
494
547
|
- **(default)** — Label text.
|
|
495
|
-
- **prefix** — Leading content (e.g. icon).
|
|
548
|
+
- **prefix** — Leading content (e.g. icon). A `type="checkbox"` item ignores it unless `check-placement="end"` frees the leading column.
|
|
496
549
|
- **suffix** — Trailing content.
|
|
497
550
|
- **submenu** — Nested `l-dropdown-item` elements rendered in a submenu panel anchored to this item. Drop an `<hr>` between them for a separator.
|
|
498
551
|
|
|
@@ -132,6 +132,37 @@ Set `value="1"` to activate a specific tab on load (0-based index).
|
|
|
132
132
|
</l-tabs>
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
+
### Disabled tab
|
|
136
|
+
|
|
137
|
+
Add `disabled` to a tab button to take it out of play. It is skipped by arrow keys, `Home` and `End`, it cannot be selected by a click or by `value`, and it never shows the hover pill. `aria-disabled="true"` behaves identically — reach for it only when the native attribute is out of reach (a framework binding, a non-`button` element).
|
|
138
|
+
|
|
139
|
+
`l-tabs` itself takes no `disabled` attribute. To freeze tab switching — while a page loads, say — put `inert` on the tablist `div`: the tabs leave the tab sequence and stop answering clicks, and the active panel stays reachable.
|
|
140
|
+
|
|
141
|
+
```html
|
|
142
|
+
<l-tabs variant="line">
|
|
143
|
+
<div>
|
|
144
|
+
<button
|
|
145
|
+
name="account"
|
|
146
|
+
aria-selected="true"
|
|
147
|
+
>
|
|
148
|
+
Account
|
|
149
|
+
</button>
|
|
150
|
+
<button name="password">Password</button>
|
|
151
|
+
<button
|
|
152
|
+
name="billing"
|
|
153
|
+
disabled
|
|
154
|
+
>
|
|
155
|
+
Billing
|
|
156
|
+
</button>
|
|
157
|
+
<button name="notifications">Notifications</button>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="p-4 text-sm text-secondary">Make changes to your account here.</div>
|
|
160
|
+
<div class="p-4 text-sm text-secondary">Change your password here.</div>
|
|
161
|
+
<div class="p-4 text-sm text-secondary">Upgrade your plan to manage billing.</div>
|
|
162
|
+
<div class="p-4 text-sm text-secondary">Manage your notification preferences.</div>
|
|
163
|
+
</l-tabs>
|
|
164
|
+
```
|
|
165
|
+
|
|
135
166
|
## Accessibility
|
|
136
167
|
|
|
137
168
|
### Criteria
|
|
@@ -140,6 +171,7 @@ Set `value="1"` to activate a specific tab on load (0-based index).
|
|
|
140
171
|
- **Linked controls** — Each tab has `aria-controls` pointing to its panel; each panel has `aria-labelledby` pointing back to its tab
|
|
141
172
|
- **Selection state** — Active tab has `aria-selected="true"`; inactive tabs have `aria-selected="false"`
|
|
142
173
|
- **Focus management** — Roving tabindex — active tab has `tabindex="0"`, others `tabindex="-1"`. Panels have `tabindex="0"` for keyboard access
|
|
174
|
+
- **Disabled tabs** — Tabs marked `disabled` or `aria-disabled="true"` are skipped by arrow keys, `Home` and `End`, and never take the roving `tabindex="0"` — so the tablist always keeps a reachable entry point
|
|
143
175
|
- **Hidden content** — Inactive panels use the `hidden` attribute
|
|
144
176
|
- **Motion** — Indicator animation respects `prefers-reduced-motion`
|
|
145
177
|
|
|
@@ -148,15 +180,16 @@ Set `value="1"` to activate a specific tab on load (0-based index).
|
|
|
148
180
|
- The first child of `l-tabs` must be a `div` containing `button` elements for the tab triggers
|
|
149
181
|
- Each remaining child `div` maps to a tab panel in order
|
|
150
182
|
- Do not add `role` or `aria-*` attributes manually — the custom element sets them automatically
|
|
183
|
+
- `l-tabs` has no host-level `disabled` attribute — put `inert` on the tablist `div` to freeze tab switching
|
|
151
184
|
|
|
152
185
|
### Keyboard interactions
|
|
153
186
|
|
|
154
|
-
- `ArrowRight` — Moves focus to the next tab and activates it (horizontal orientation)
|
|
155
|
-
- `ArrowLeft` — Moves focus to the previous tab and activates it (horizontal orientation)
|
|
156
|
-
- `ArrowDown` — Moves focus to the next tab and activates it (vertical orientation)
|
|
157
|
-
- `ArrowUp` — Moves focus to the previous tab and activates it (vertical orientation)
|
|
158
|
-
- `Home` — Moves focus to the first tab and activates it
|
|
159
|
-
- `End` — Moves focus to the last tab and activates it
|
|
187
|
+
- `ArrowRight` — Moves focus to the next enabled tab and activates it (horizontal orientation)
|
|
188
|
+
- `ArrowLeft` — Moves focus to the previous enabled tab and activates it (horizontal orientation)
|
|
189
|
+
- `ArrowDown` — Moves focus to the next enabled tab and activates it (vertical orientation)
|
|
190
|
+
- `ArrowUp` — Moves focus to the previous enabled tab and activates it (vertical orientation)
|
|
191
|
+
- `Home` — Moves focus to the first enabled tab and activates it
|
|
192
|
+
- `End` — Moves focus to the last enabled tab and activates it
|
|
160
193
|
- `Tab` — Moves focus out of the tablist to the active panel
|
|
161
194
|
|
|
162
195
|
## API reference
|
|
@@ -184,7 +217,7 @@ import 'luxen-ui/tabs';
|
|
|
184
217
|
|
|
185
218
|
### Events
|
|
186
219
|
|
|
187
|
-
- **change** — Fired when the active tab changes. Bubbles. Properties: `index: number`, `name: string | null`.
|
|
220
|
+
- **change** — Fired when the active tab changes. Bubbles. Not cancelable. Properties: `index: number`, `name: string | null`.
|
|
188
221
|
|
|
189
222
|
### CSS custom properties
|
|
190
223
|
|
package/elements.json
CHANGED
|
@@ -373,6 +373,14 @@
|
|
|
373
373
|
"inMockups": true,
|
|
374
374
|
"selector": ".l-textarea"
|
|
375
375
|
},
|
|
376
|
+
{
|
|
377
|
+
"name": "color-scheme-icon",
|
|
378
|
+
"displayName": "Color scheme icon",
|
|
379
|
+
"kind": "element",
|
|
380
|
+
"type": "shadow",
|
|
381
|
+
"inSkill": true,
|
|
382
|
+
"inMockups": false
|
|
383
|
+
},
|
|
376
384
|
{
|
|
377
385
|
"name": "toast",
|
|
378
386
|
"displayName": "Toast",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "luxen-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Modern web components and CSS-first UI library built with Lit. Framework-agnostic, customizable prefix, design tokens.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"custom-elements",
|
|
@@ -117,6 +117,10 @@
|
|
|
117
117
|
"types": "./dist/shared/localize.d.ts",
|
|
118
118
|
"import": "./dist/shared/localize.js"
|
|
119
119
|
},
|
|
120
|
+
"./color-scheme": {
|
|
121
|
+
"types": "./dist/color-scheme.d.ts",
|
|
122
|
+
"import": "./dist/color-scheme.js"
|
|
123
|
+
},
|
|
120
124
|
"./translations/*": {
|
|
121
125
|
"types": "./dist/translations/*.d.ts",
|
|
122
126
|
"import": "./dist/translations/*.js"
|