use-wc 1.0.0-pre.67 → 1.0.0-pre.68
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/custom-elements.json +65 -65
- package/dist/design-system.css +350 -92
- package/dist/tokens.css +78 -10
- package/package.json +1 -1
- package/vscode.html-custom-data.json +6 -6
package/custom-elements.json
CHANGED
|
@@ -772,6 +772,71 @@
|
|
|
772
772
|
}
|
|
773
773
|
]
|
|
774
774
|
},
|
|
775
|
+
{
|
|
776
|
+
"kind": "javascript-module",
|
|
777
|
+
"path": "src/elements/use-caret/use-caret.ts",
|
|
778
|
+
"declarations": [
|
|
779
|
+
{
|
|
780
|
+
"kind": "class",
|
|
781
|
+
"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```",
|
|
782
|
+
"name": "UseCaret",
|
|
783
|
+
"slots": [
|
|
784
|
+
{
|
|
785
|
+
"description": "Trigger label content.",
|
|
786
|
+
"name": "default"
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
"description": "Replaces the default caret glyph.",
|
|
790
|
+
"name": "icon"
|
|
791
|
+
}
|
|
792
|
+
],
|
|
793
|
+
"members": [
|
|
794
|
+
{
|
|
795
|
+
"kind": "field",
|
|
796
|
+
"name": "icon",
|
|
797
|
+
"type": {
|
|
798
|
+
"text": "((html: HtmlTag) => TemplateResult | string) | TemplateResult | string | undefined"
|
|
799
|
+
},
|
|
800
|
+
"static": true,
|
|
801
|
+
"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 ▼."
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
"kind": "field",
|
|
805
|
+
"name": "nestedIcon",
|
|
806
|
+
"type": {
|
|
807
|
+
"text": "((html: HtmlTag) => TemplateResult | string) | TemplateResult | string | undefined"
|
|
808
|
+
},
|
|
809
|
+
"static": true,
|
|
810
|
+
"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 ▶."
|
|
811
|
+
}
|
|
812
|
+
],
|
|
813
|
+
"superclass": {
|
|
814
|
+
"name": "LitElement",
|
|
815
|
+
"package": "lit"
|
|
816
|
+
},
|
|
817
|
+
"tagName": "use-caret",
|
|
818
|
+
"customElement": true
|
|
819
|
+
}
|
|
820
|
+
],
|
|
821
|
+
"exports": [
|
|
822
|
+
{
|
|
823
|
+
"kind": "js",
|
|
824
|
+
"name": "UseCaret",
|
|
825
|
+
"declaration": {
|
|
826
|
+
"name": "UseCaret",
|
|
827
|
+
"module": "src/elements/use-caret/use-caret.ts"
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
"kind": "custom-element-definition",
|
|
832
|
+
"name": "use-caret",
|
|
833
|
+
"declaration": {
|
|
834
|
+
"name": "UseCaret",
|
|
835
|
+
"module": "src/elements/use-caret/use-caret.ts"
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
]
|
|
839
|
+
},
|
|
775
840
|
{
|
|
776
841
|
"kind": "javascript-module",
|
|
777
842
|
"path": "src/elements/use-date-input/use-date-input.ts",
|
|
@@ -898,71 +963,6 @@
|
|
|
898
963
|
}
|
|
899
964
|
]
|
|
900
965
|
},
|
|
901
|
-
{
|
|
902
|
-
"kind": "javascript-module",
|
|
903
|
-
"path": "src/elements/use-caret/use-caret.ts",
|
|
904
|
-
"declarations": [
|
|
905
|
-
{
|
|
906
|
-
"kind": "class",
|
|
907
|
-
"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```",
|
|
908
|
-
"name": "UseCaret",
|
|
909
|
-
"slots": [
|
|
910
|
-
{
|
|
911
|
-
"description": "Trigger label content.",
|
|
912
|
-
"name": "default"
|
|
913
|
-
},
|
|
914
|
-
{
|
|
915
|
-
"description": "Replaces the default caret glyph.",
|
|
916
|
-
"name": "icon"
|
|
917
|
-
}
|
|
918
|
-
],
|
|
919
|
-
"members": [
|
|
920
|
-
{
|
|
921
|
-
"kind": "field",
|
|
922
|
-
"name": "icon",
|
|
923
|
-
"type": {
|
|
924
|
-
"text": "((html: HtmlTag) => TemplateResult | string) | TemplateResult | string | undefined"
|
|
925
|
-
},
|
|
926
|
-
"static": true,
|
|
927
|
-
"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 ▼."
|
|
928
|
-
},
|
|
929
|
-
{
|
|
930
|
-
"kind": "field",
|
|
931
|
-
"name": "nestedIcon",
|
|
932
|
-
"type": {
|
|
933
|
-
"text": "((html: HtmlTag) => TemplateResult | string) | TemplateResult | string | undefined"
|
|
934
|
-
},
|
|
935
|
-
"static": true,
|
|
936
|
-
"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 ▶."
|
|
937
|
-
}
|
|
938
|
-
],
|
|
939
|
-
"superclass": {
|
|
940
|
-
"name": "LitElement",
|
|
941
|
-
"package": "lit"
|
|
942
|
-
},
|
|
943
|
-
"tagName": "use-caret",
|
|
944
|
-
"customElement": true
|
|
945
|
-
}
|
|
946
|
-
],
|
|
947
|
-
"exports": [
|
|
948
|
-
{
|
|
949
|
-
"kind": "js",
|
|
950
|
-
"name": "UseCaret",
|
|
951
|
-
"declaration": {
|
|
952
|
-
"name": "UseCaret",
|
|
953
|
-
"module": "src/elements/use-caret/use-caret.ts"
|
|
954
|
-
}
|
|
955
|
-
},
|
|
956
|
-
{
|
|
957
|
-
"kind": "custom-element-definition",
|
|
958
|
-
"name": "use-caret",
|
|
959
|
-
"declaration": {
|
|
960
|
-
"name": "UseCaret",
|
|
961
|
-
"module": "src/elements/use-caret/use-caret.ts"
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
]
|
|
965
|
-
},
|
|
966
966
|
{
|
|
967
967
|
"kind": "javascript-module",
|
|
968
968
|
"path": "src/elements/use-date-picker/use-date-picker.ts",
|
package/dist/design-system.css
CHANGED
|
@@ -1996,8 +1996,8 @@
|
|
|
1996
1996
|
`<main>` / `<footer>` regions are arranged to match:
|
|
1997
1997
|
|
|
1998
1998
|
use-layout.page an app frame — ruled topbar and footer, roomy
|
|
1999
|
-
use-layout.
|
|
2000
|
-
use-layout.message a chat or comment —
|
|
1999
|
+
use-layout.entry a row in a list — a leading avatar rail with the regions beside it
|
|
2000
|
+
use-layout.message a chat or comment — `.entry`'s layout at chat density
|
|
2001
2001
|
use-layout.card a panel — avatar + hgroup + actions, end-aligned ruled
|
|
2002
2002
|
footer, compact, plus the chrome at end of file
|
|
2003
2003
|
|
|
@@ -2008,10 +2008,9 @@
|
|
|
2008
2008
|
|
|
2009
2009
|
Rather than four arrangements, a variant class sets *only* a density scale on itself
|
|
2010
2010
|
(`--usewc-layout-region-padding`, `-gap`, `-avatar-size`, `--usewc-layout-header-rule`,
|
|
2011
|
-
`-footer-rule`, `--usewc-layout-footer-justify`,
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
overrides. The mechanic isn't repeated per variant either: a `use-layout.page` is a
|
|
2011
|
+
`-footer-rule`, `--usewc-layout-footer-justify`, `-row-gap`) and one generic set of region
|
|
2012
|
+
rules reads them. Only a couple of arrangements can't be said in a property — the entry and
|
|
2013
|
+
message rail, and message's inline hgroup — and those follow as structural overrides. The mechanic isn't repeated per variant either: a `use-layout.page` is a
|
|
2015
2014
|
`use-layout`, so the base pin/grow rules above already cover it.
|
|
2016
2015
|
|
|
2017
2016
|
Every selector stays compound (`use-layout.page`, never a bare `.page`) so none of these
|
|
@@ -2213,8 +2212,8 @@
|
|
|
2213
2212
|
--usewc-layout-region-padding-block: var(--usewc-layout-page-padding-block);
|
|
2214
2213
|
--usewc-layout-region-gap: var(--usewc-layout-page-gap);
|
|
2215
2214
|
--usewc-layout-region-avatar-size: var(--usewc-layout-page-avatar-size);
|
|
2216
|
-
--usewc-layout-header-rule: var(--usewc-
|
|
2217
|
-
--usewc-layout-footer-rule: var(--usewc-
|
|
2215
|
+
--usewc-layout-header-rule: var(--usewc-layout-region-rule);
|
|
2216
|
+
--usewc-layout-footer-rule: var(--usewc-layout-region-rule);
|
|
2218
2217
|
--usewc-layout-footer-justify: space-between;
|
|
2219
2218
|
|
|
2220
2219
|
use-theme-escape & {
|
|
@@ -2222,12 +2221,11 @@
|
|
|
2222
2221
|
}
|
|
2223
2222
|
}
|
|
2224
2223
|
|
|
2225
|
-
use-layout.
|
|
2226
|
-
--usewc-layout-region-padding: var(--usewc-layout-
|
|
2227
|
-
--usewc-layout-region-gap: var(--usewc-layout-
|
|
2228
|
-
--usewc-layout-region-avatar-size: var(--usewc-layout-
|
|
2229
|
-
--usewc-layout-
|
|
2230
|
-
--usewc-layout-footer-rule: var(--usewc-color-region-border);
|
|
2224
|
+
use-layout.entry {
|
|
2225
|
+
--usewc-layout-region-padding: var(--usewc-layout-entry-padding);
|
|
2226
|
+
--usewc-layout-region-gap: var(--usewc-layout-entry-gap);
|
|
2227
|
+
--usewc-layout-region-avatar-size: var(--usewc-layout-entry-avatar-size);
|
|
2228
|
+
--usewc-layout-region-row-gap: var(--usewc-layout-entry-gap);
|
|
2231
2229
|
--usewc-layout-footer-justify: flex-start;
|
|
2232
2230
|
|
|
2233
2231
|
use-theme-escape & {
|
|
@@ -2235,10 +2233,12 @@
|
|
|
2235
2233
|
}
|
|
2236
2234
|
}
|
|
2237
2235
|
|
|
2236
|
+
/* Chat density on top of `.entry`'s layout — tighter, since a message repeats down a feed. */
|
|
2238
2237
|
use-layout.message {
|
|
2239
2238
|
--usewc-layout-region-padding: var(--usewc-layout-message-padding);
|
|
2240
2239
|
--usewc-layout-region-gap: var(--usewc-layout-message-gap);
|
|
2241
2240
|
--usewc-layout-region-avatar-size: var(--usewc-layout-message-rail);
|
|
2241
|
+
--usewc-layout-region-row-gap: var(--usewc-layout-region-title-gap);
|
|
2242
2242
|
--usewc-layout-footer-justify: flex-start;
|
|
2243
2243
|
|
|
2244
2244
|
use-theme-escape & {
|
|
@@ -2250,7 +2250,7 @@
|
|
|
2250
2250
|
--usewc-layout-region-padding: var(--usewc-layout-card-padding);
|
|
2251
2251
|
--usewc-layout-region-gap: var(--usewc-layout-card-gap);
|
|
2252
2252
|
--usewc-layout-region-avatar-size: var(--usewc-layout-card-avatar-size);
|
|
2253
|
-
--usewc-layout-footer-rule: var(--usewc-
|
|
2253
|
+
--usewc-layout-footer-rule: var(--usewc-layout-region-rule);
|
|
2254
2254
|
--usewc-layout-footer-justify: flex-end;
|
|
2255
2255
|
|
|
2256
2256
|
use-theme-escape & {
|
|
@@ -2258,11 +2258,15 @@
|
|
|
2258
2258
|
}
|
|
2259
2259
|
}
|
|
2260
2260
|
|
|
2261
|
+
/* A rule token carries the whole border value, not just its colour, so a variant that never
|
|
2262
|
+
names one gets `0 solid transparent` — no width. Declaring only a colour and leaving the
|
|
2263
|
+
width at 1px would give every ruleless header and footer an invisible pixel of border, which
|
|
2264
|
+
is enough to push a 36px control into a 37px region. */
|
|
2261
2265
|
/* ── Generic regions ────────────────────────────────────────────────────────────────────────
|
|
2262
|
-
One ruleset for
|
|
2266
|
+
One ruleset for every variant, reading the properties above. A rule colour left unset
|
|
2263
2267
|
falls back to `transparent`, so a variant opts into a hairline by naming one rather than by
|
|
2264
2268
|
having a rule of its own. */
|
|
2265
|
-
:where(use-layout.page,
|
|
2269
|
+
:where(use-layout:is(.page, .entry, .message, .card)) {
|
|
2266
2270
|
min-block-size: 0;
|
|
2267
2271
|
|
|
2268
2272
|
use-theme-escape & {
|
|
@@ -2270,7 +2274,7 @@
|
|
|
2270
2274
|
}
|
|
2271
2275
|
}
|
|
2272
2276
|
|
|
2273
|
-
:where(use-layout.page,
|
|
2277
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > use-layout[fill] {
|
|
2274
2278
|
overflow: auto;
|
|
2275
2279
|
padding: var(--usewc-layout-region-padding);
|
|
2276
2280
|
|
|
@@ -2279,9 +2283,8 @@
|
|
|
2279
2283
|
}
|
|
2280
2284
|
}
|
|
2281
2285
|
|
|
2282
|
-
:where(use-layout.page,
|
|
2286
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > main {
|
|
2283
2287
|
padding: var(--usewc-layout-region-padding);
|
|
2284
|
-
max-inline-size: var(--usewc-layout-measure, none);
|
|
2285
2288
|
|
|
2286
2289
|
use-theme-escape & {
|
|
2287
2290
|
all: revert-layer;
|
|
@@ -2292,9 +2295,7 @@
|
|
|
2292
2295
|
they'd otherwise stack on top of the padding — a leading `<p>`, `<ul>`, `<ol>` or heading
|
|
2293
2296
|
would otherwise push itself away from an edge that's already spaced. Matched by position
|
|
2294
2297
|
rather than by tag so it holds for whatever flow content lands there. */
|
|
2295
|
-
:where(use-layout.page,
|
|
2296
|
-
> :is(main, use-layout[fill])
|
|
2297
|
-
> :first-child {
|
|
2298
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(main, use-layout[fill]) > :first-child {
|
|
2298
2299
|
margin-block-start: 0;
|
|
2299
2300
|
|
|
2300
2301
|
use-theme-escape & {
|
|
@@ -2302,9 +2303,7 @@
|
|
|
2302
2303
|
}
|
|
2303
2304
|
}
|
|
2304
2305
|
|
|
2305
|
-
:where(use-layout.page,
|
|
2306
|
-
> :is(main, use-layout[fill])
|
|
2307
|
-
> :last-child {
|
|
2306
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(main, use-layout[fill]) > :last-child {
|
|
2308
2307
|
margin-block-end: 0;
|
|
2309
2308
|
|
|
2310
2309
|
use-theme-escape & {
|
|
@@ -2314,9 +2313,7 @@
|
|
|
2314
2313
|
|
|
2315
2314
|
/* A header already supplies the block-start padding, so the body region drops its own and the
|
|
2316
2315
|
two read as one padded column rather than a double gap under the header. */
|
|
2317
|
-
:where(use-layout.page,
|
|
2318
|
-
> header
|
|
2319
|
-
+ :is(main, use-layout[fill]) {
|
|
2316
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > header + :is(main, use-layout[fill]) {
|
|
2320
2317
|
padding-block-start: 0;
|
|
2321
2318
|
|
|
2322
2319
|
use-theme-escape & {
|
|
@@ -2324,7 +2321,7 @@
|
|
|
2324
2321
|
}
|
|
2325
2322
|
}
|
|
2326
2323
|
|
|
2327
|
-
:where(use-layout.page,
|
|
2324
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > figure {
|
|
2328
2325
|
margin: 0;
|
|
2329
2326
|
|
|
2330
2327
|
use-theme-escape & {
|
|
@@ -2332,7 +2329,7 @@
|
|
|
2332
2329
|
}
|
|
2333
2330
|
}
|
|
2334
2331
|
|
|
2335
|
-
:where(use-layout.page,
|
|
2332
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > figure > img {
|
|
2336
2333
|
display: block;
|
|
2337
2334
|
inline-size: 100%;
|
|
2338
2335
|
block-size: auto;
|
|
@@ -2343,28 +2340,28 @@
|
|
|
2343
2340
|
}
|
|
2344
2341
|
}
|
|
2345
2342
|
|
|
2346
|
-
:where(use-layout.page,
|
|
2343
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > header {
|
|
2347
2344
|
display: flex;
|
|
2348
2345
|
align-items: center;
|
|
2349
2346
|
justify-content: flex-start;
|
|
2350
2347
|
gap: var(--usewc-layout-region-gap);
|
|
2351
2348
|
padding-block: var(--usewc-layout-region-padding-block, var(--usewc-layout-region-padding));
|
|
2352
2349
|
padding-inline: var(--usewc-layout-region-padding);
|
|
2353
|
-
border-block-end: var(--usewc-layout-
|
|
2350
|
+
border-block-end: var(--usewc-layout-header-rule, 0 solid transparent);
|
|
2354
2351
|
|
|
2355
2352
|
use-theme-escape & {
|
|
2356
2353
|
all: revert-layer;
|
|
2357
2354
|
}
|
|
2358
2355
|
}
|
|
2359
2356
|
|
|
2360
|
-
:where(use-layout.page,
|
|
2357
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > footer {
|
|
2361
2358
|
display: flex;
|
|
2362
2359
|
align-items: center;
|
|
2363
2360
|
justify-content: var(--usewc-layout-footer-justify, flex-end);
|
|
2364
2361
|
gap: var(--usewc-layout-region-gap);
|
|
2365
2362
|
padding-block: var(--usewc-layout-region-padding-block, var(--usewc-layout-region-padding));
|
|
2366
2363
|
padding-inline: var(--usewc-layout-region-padding);
|
|
2367
|
-
border-block-start: var(--usewc-layout-
|
|
2364
|
+
border-block-start: var(--usewc-layout-footer-rule, 0 solid transparent);
|
|
2368
2365
|
|
|
2369
2366
|
use-theme-escape & {
|
|
2370
2367
|
all: revert-layer;
|
|
@@ -2375,7 +2372,7 @@
|
|
|
2375
2372
|
since a bare `<img>` in a header is as likely to be a logo as a face, and a class would have
|
|
2376
2373
|
to be remembered where the element already says it. Both are squared to the variant's avatar
|
|
2377
2374
|
size; a `<figure>` centres whatever it holds, the same way the message rail does. */
|
|
2378
|
-
:where(use-layout.page,
|
|
2375
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > header > :is(use-avatar, figure) {
|
|
2379
2376
|
flex: none;
|
|
2380
2377
|
inline-size: var(--usewc-layout-region-avatar-size);
|
|
2381
2378
|
block-size: var(--usewc-layout-region-avatar-size);
|
|
@@ -2387,7 +2384,7 @@
|
|
|
2387
2384
|
}
|
|
2388
2385
|
}
|
|
2389
2386
|
|
|
2390
|
-
:where(use-layout.page,
|
|
2387
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > header > figure {
|
|
2391
2388
|
display: flex;
|
|
2392
2389
|
align-items: center;
|
|
2393
2390
|
justify-content: center;
|
|
@@ -2400,7 +2397,7 @@
|
|
|
2400
2397
|
}
|
|
2401
2398
|
}
|
|
2402
2399
|
|
|
2403
|
-
:where(use-layout.page,
|
|
2400
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > header > figure > img {
|
|
2404
2401
|
inline-size: 100%;
|
|
2405
2402
|
block-size: 100%;
|
|
2406
2403
|
object-fit: cover;
|
|
@@ -2414,7 +2411,7 @@
|
|
|
2414
2411
|
`<section>` in a topbar — so it's matched by descent rather than as a direct child. Only the
|
|
2415
2412
|
one sitting directly in a header grows, since that's what pushes trailing actions to the
|
|
2416
2413
|
far end. */
|
|
2417
|
-
:where(use-layout.page,
|
|
2414
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(header, footer) hgroup {
|
|
2418
2415
|
display: flex;
|
|
2419
2416
|
flex-direction: column;
|
|
2420
2417
|
gap: var(--usewc-layout-region-title-gap);
|
|
@@ -2425,7 +2422,7 @@
|
|
|
2425
2422
|
}
|
|
2426
2423
|
}
|
|
2427
2424
|
|
|
2428
|
-
:where(use-layout.page,
|
|
2425
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > header > hgroup {
|
|
2429
2426
|
flex: 1;
|
|
2430
2427
|
|
|
2431
2428
|
use-theme-escape & {
|
|
@@ -2433,16 +2430,35 @@
|
|
|
2433
2430
|
}
|
|
2434
2431
|
}
|
|
2435
2432
|
|
|
2433
|
+
/* An `hgroup`'s lines get explicit line boxes so a stacked title and subtitle add up to a
|
|
2434
|
+
predictable height beside a fixed-size avatar, and everything after the first steps down a
|
|
2435
|
+
size — a timestamp, a byline, a location is secondary to the title it hangs off. */
|
|
2436
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(header, footer) hgroup > * {
|
|
2437
|
+
line-height: var(--usewc-layout-region-title-line-height);
|
|
2438
|
+
|
|
2439
|
+
use-theme-escape & {
|
|
2440
|
+
all: revert-layer;
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(header, footer) hgroup > * + * {
|
|
2445
|
+
font-size: var(--usewc-font-region-subtitle-size);
|
|
2446
|
+
line-height: var(--usewc-layout-region-subtitle-line-height);
|
|
2447
|
+
color: var(--usewc-color-document-text-muted);
|
|
2448
|
+
|
|
2449
|
+
use-theme-escape & {
|
|
2450
|
+
all: revert-layer;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2436
2454
|
/* A header and footer are flex containers and an `hgroup` is a flex column, so `gap` is the
|
|
2437
2455
|
spacing — a default block margin stacks on top of it and blows the region apart. Headings are
|
|
2438
2456
|
matched at any depth, since a topbar routinely nests them a `<section>` and an `<hgroup>`
|
|
2439
2457
|
down; `<p>` and friends only need it where they sit directly in the flex flow. Body copy in
|
|
2440
2458
|
`<main>` keeps its margins, which is the whole point of a paragraph there. */
|
|
2441
|
-
:where(use-layout.page,
|
|
2442
|
-
:where(use-layout.page,
|
|
2443
|
-
:where(use-layout.page,
|
|
2444
|
-
> :is(header, footer)
|
|
2445
|
-
:is(h1, h2, h3, h4, h5, h6) {
|
|
2459
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(header, footer) > *,
|
|
2460
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(header, footer) hgroup > *,
|
|
2461
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(header, footer) :is(h1, h2, h3, h4, h5, h6) {
|
|
2446
2462
|
margin-block: 0;
|
|
2447
2463
|
|
|
2448
2464
|
use-theme-escape & {
|
|
@@ -2453,7 +2469,7 @@
|
|
|
2453
2469
|
/* A `<section>` inside a header or footer is one cluster of related controls. Two or more of
|
|
2454
2470
|
them means the region has distinct ends, so it splits them apart regardless of the variant's
|
|
2455
2471
|
default justification. */
|
|
2456
|
-
:where(use-layout.page,
|
|
2472
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(header, footer) > section {
|
|
2457
2473
|
display: flex;
|
|
2458
2474
|
align-items: center;
|
|
2459
2475
|
gap: var(--usewc-layout-region-gap);
|
|
@@ -2463,8 +2479,7 @@
|
|
|
2463
2479
|
}
|
|
2464
2480
|
}
|
|
2465
2481
|
|
|
2466
|
-
:where(use-layout.page,
|
|
2467
|
-
> :is(header, footer):has(> section ~ section) {
|
|
2482
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(header, footer):has(> section ~ section) {
|
|
2468
2483
|
justify-content: space-between;
|
|
2469
2484
|
|
|
2470
2485
|
use-theme-escape & {
|
|
@@ -2473,40 +2488,47 @@
|
|
|
2473
2488
|
}
|
|
2474
2489
|
|
|
2475
2490
|
/* ── Structural overrides ───────────────────────────────────────────────────────────────────
|
|
2476
|
-
The
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
/*
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2491
|
+
The arrangements a density property can't express. Same `:where()` weight as the generic
|
|
2492
|
+
rules, winning on source order. */
|
|
2493
|
+
|
|
2494
|
+
/* `.entry` is the one variant that isn't a column: a leading avatar rail with every other
|
|
2495
|
+
region stacked beside it — a comment, a notification, a search result, a row in a people
|
|
2496
|
+
list. `.message` is the same layout at chat density, so both are matched here.
|
|
2497
|
+
|
|
2498
|
+
`align-self: start` on the rail is what keeps it honest. Left to stretch, a rail spanning
|
|
2499
|
+
every row forces the rows to add up to at least its own height, which pads out a short
|
|
2500
|
+
header and drops an awkward gap between the name and the body. Held at the start it sizes
|
|
2501
|
+
to itself, the rows keep their content heights, and spanning still guarantees the entry is
|
|
2502
|
+
never shorter than the rail.
|
|
2503
|
+
|
|
2504
|
+
Each variant sets its own row gap. `.entry` spaces its regions evenly, since a title, a body
|
|
2505
|
+
and a row of actions are three separate things. `.message` keeps them tight — a name and the
|
|
2506
|
+
message under it read as one unit — and buys the footer's separation back with a margin, so
|
|
2507
|
+
reactions still sit apart from the text. The rail's column is reserved by `grid-template-columns` whether or not
|
|
2508
|
+
a rail is present, so a grouped entry that omits it still lines its body up with the rest. */
|
|
2509
|
+
use-layout:is(.entry, .message) {
|
|
2510
|
+
display: grid;
|
|
2511
|
+
grid-template-columns: var(--usewc-layout-region-avatar-size) 1fr;
|
|
2512
|
+
column-gap: var(--usewc-layout-region-gap);
|
|
2513
|
+
row-gap: var(--usewc-layout-region-row-gap);
|
|
2514
|
+
padding: var(--usewc-layout-region-padding);
|
|
2487
2515
|
|
|
2488
2516
|
use-theme-escape & {
|
|
2489
2517
|
all: revert-layer;
|
|
2490
2518
|
}
|
|
2491
2519
|
}
|
|
2492
2520
|
|
|
2493
|
-
:where(use-layout.
|
|
2494
|
-
|
|
2521
|
+
:where(use-layout:is(.entry, .message)) > :is(header, main, footer) {
|
|
2522
|
+
grid-column: 2;
|
|
2523
|
+
padding: 0;
|
|
2495
2524
|
|
|
2496
2525
|
use-theme-escape & {
|
|
2497
2526
|
all: revert-layer;
|
|
2498
2527
|
}
|
|
2499
2528
|
}
|
|
2500
2529
|
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
when a grouped message omits its header and leaves the rail empty — bodies stay aligned down
|
|
2504
|
-
the feed either way. The container owns the padding, so the regions drop theirs. */
|
|
2505
|
-
use-layout.message {
|
|
2506
|
-
display: grid;
|
|
2507
|
-
grid-template-columns: var(--usewc-layout-message-rail) 1fr;
|
|
2508
|
-
gap: var(--usewc-layout-region-gap);
|
|
2509
|
-
padding: var(--usewc-layout-region-padding);
|
|
2530
|
+
:where(use-layout.message) > footer {
|
|
2531
|
+
margin-block-start: var(--usewc-layout-region-gap);
|
|
2510
2532
|
|
|
2511
2533
|
use-theme-escape & {
|
|
2512
2534
|
all: revert-layer;
|
|
@@ -2517,11 +2539,12 @@
|
|
|
2517
2539
|
brings its own initials fallback and shape — and a bare `<figure>` works too, for a date
|
|
2518
2540
|
badge or an icon that isn't a person. A `<figure>` centres whatever it's given rather than
|
|
2519
2541
|
assuming an `<img>`; a photo inside one fills the square and crops. */
|
|
2520
|
-
:where(use-layout.message) > :is(use-avatar, figure) {
|
|
2542
|
+
:where(use-layout:is(.entry, .message)) > :is(use-avatar, figure) {
|
|
2521
2543
|
grid-column: 1;
|
|
2522
|
-
grid-row: 1
|
|
2523
|
-
|
|
2524
|
-
|
|
2544
|
+
grid-row: 1;
|
|
2545
|
+
align-self: start;
|
|
2546
|
+
inline-size: var(--usewc-layout-region-avatar-size);
|
|
2547
|
+
block-size: var(--usewc-layout-region-avatar-size);
|
|
2525
2548
|
overflow: hidden;
|
|
2526
2549
|
border-radius: var(--usewc-effect-surface-avatar-border-radius);
|
|
2527
2550
|
|
|
@@ -2530,11 +2553,26 @@
|
|
|
2530
2553
|
}
|
|
2531
2554
|
}
|
|
2532
2555
|
|
|
2533
|
-
|
|
2556
|
+
/* The rail shares a centre with the title block rather than being pinned to the top of it, so
|
|
2557
|
+
a stacked title over a subtitle and a single line each sit level with the avatar without
|
|
2558
|
+
either being measured against the other. The rail's own row grows to hold it, so it can
|
|
2559
|
+
never overhang, and everything after the header falls below both.
|
|
2560
|
+
|
|
2561
|
+
With no header the rail has no title to centre against — the body starts at the top of the
|
|
2562
|
+
column and the rail starts with it. */
|
|
2563
|
+
:where(use-layout:is(.entry, .message):has(> header)) > :is(use-avatar, figure) {
|
|
2564
|
+
align-self: center;
|
|
2565
|
+
|
|
2566
|
+
use-theme-escape & {
|
|
2567
|
+
all: revert-layer;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
:where(use-layout:is(.entry, .message)) > figure {
|
|
2534
2572
|
display: flex;
|
|
2535
2573
|
align-items: center;
|
|
2536
2574
|
justify-content: center;
|
|
2537
|
-
font-size: calc(var(--usewc-layout-
|
|
2575
|
+
font-size: calc(var(--usewc-layout-region-avatar-size) * 0.4);
|
|
2538
2576
|
line-height: 1;
|
|
2539
2577
|
|
|
2540
2578
|
use-theme-escape & {
|
|
@@ -2542,7 +2580,7 @@
|
|
|
2542
2580
|
}
|
|
2543
2581
|
}
|
|
2544
2582
|
|
|
2545
|
-
:where(use-layout.message) > figure > img {
|
|
2583
|
+
:where(use-layout:is(.entry, .message)) > figure > img {
|
|
2546
2584
|
inline-size: 100%;
|
|
2547
2585
|
block-size: 100%;
|
|
2548
2586
|
object-fit: cover;
|
|
@@ -2554,7 +2592,7 @@
|
|
|
2554
2592
|
|
|
2555
2593
|
/* A `<figure>` inside the body is an attachment, not the rail — it keeps the flow but sheds the
|
|
2556
2594
|
UA margin so the region's own spacing is the only spacing. */
|
|
2557
|
-
:where(use-layout.page,
|
|
2595
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(main, use-layout[fill]) figure {
|
|
2558
2596
|
margin-inline: 0;
|
|
2559
2597
|
|
|
2560
2598
|
use-theme-escape & {
|
|
@@ -2562,10 +2600,7 @@
|
|
|
2562
2600
|
}
|
|
2563
2601
|
}
|
|
2564
2602
|
|
|
2565
|
-
:where(use-layout.page,
|
|
2566
|
-
> :is(main, use-layout[fill])
|
|
2567
|
-
figure
|
|
2568
|
-
> img {
|
|
2603
|
+
:where(use-layout:is(.page, .entry, .message, .card)) > :is(main, use-layout[fill]) figure > img {
|
|
2569
2604
|
display: block;
|
|
2570
2605
|
inline-size: 100%;
|
|
2571
2606
|
block-size: auto;
|
|
@@ -2575,16 +2610,6 @@
|
|
|
2575
2610
|
}
|
|
2576
2611
|
}
|
|
2577
2612
|
|
|
2578
|
-
:where(use-layout.message) > :is(header, main, footer) {
|
|
2579
|
-
grid-column: 2;
|
|
2580
|
-
padding: 0;
|
|
2581
|
-
max-inline-size: none;
|
|
2582
|
-
|
|
2583
|
-
use-theme-escape & {
|
|
2584
|
-
all: revert-layer;
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
|
|
2588
2613
|
/* Name and timestamp sit on one line here rather than stacking, and the line never wraps —
|
|
2589
2614
|
trailing actions stay put as the name grows. */
|
|
2590
2615
|
:where(use-layout.message) > header > hgroup {
|
|
@@ -2630,3 +2655,236 @@
|
|
|
2630
2655
|
}
|
|
2631
2656
|
}
|
|
2632
2657
|
|
|
2658
|
+
/* Prose — `use-prose` is a theme-only tag (no custom element registration, styled by tag name
|
|
2659
|
+
like `use-layout`/`use-field`), giving a block of authored text a typographic scale: a heading
|
|
2660
|
+
ramp, the rhythm between blocks, list indentation, quotes, code blocks, rules and tables.
|
|
2661
|
+
|
|
2662
|
+
This is the only place the system sets any of that. Outside a `use-prose`, `h1`-`h6`, `p` and
|
|
2663
|
+
`ul` keep the browser's own defaults, so adopting the design system never silently restyles an
|
|
2664
|
+
app's existing copy — you opt a region of text in.
|
|
2665
|
+
|
|
2666
|
+
<use-prose>
|
|
2667
|
+
<article>
|
|
2668
|
+
<h1>On keeping a field notebook</h1>
|
|
2669
|
+
<p>The point of a field notebook is not to be complete…</p>
|
|
2670
|
+
</article>
|
|
2671
|
+
</use-prose>
|
|
2672
|
+
|
|
2673
|
+
Everything is matched by descent, not as direct children, so `<article>`, `<section>`,
|
|
2674
|
+
`<div>`, `<li>` and `<blockquote>` are transparent at any depth — markdown output nests freely
|
|
2675
|
+
and still reads as prose.
|
|
2676
|
+
|
|
2677
|
+
The rhythm is the space *between* blocks (`+` on siblings) rather than a margin on each, so a
|
|
2678
|
+
heading's spacing above it is stated once instead of emerging from two margins colliding. That
|
|
2679
|
+
also means the first block in any container has no leading margin, and prose drops into a
|
|
2680
|
+
layout region without fighting its padding.
|
|
2681
|
+
|
|
2682
|
+
Prose reaches every descendant, which includes the slotted content of a nested component — a
|
|
2683
|
+
`<p>` inside a `use-layout.card` picks up prose's margins today. That is deliberate for a first
|
|
2684
|
+
pass: the alternative was a hand-maintained list of every `use-` tag to revert inside, which is
|
|
2685
|
+
a lot of upkeep for a problem nobody has hit yet. Wrap the exception in `use-theme-escape` if
|
|
2686
|
+
it bites, and revisit once there's a real case to design against.
|
|
2687
|
+
|
|
2688
|
+
Every rule here is held at the weight of `use-prose` plus one type selector, with `:where()`
|
|
2689
|
+
absorbing the rest, so an author's own selector out-ranks prose without a fight — and a
|
|
2690
|
+
boundary rule can be reintroduced later at equal specificity if it's wanted. */
|
|
2691
|
+
@layer use-theme.prose {
|
|
2692
|
+
use-prose {
|
|
2693
|
+
display: block;
|
|
2694
|
+
max-inline-size: var(--usewc-layout-prose-measure);
|
|
2695
|
+
line-height: var(--usewc-layout-prose-line-height);
|
|
2696
|
+
|
|
2697
|
+
use-theme-escape & {
|
|
2698
|
+
all: revert-layer;
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
/* The blocks prose owns. Their own margins are cleared so the rhythm below is the only source
|
|
2703
|
+
of spacing between them. */
|
|
2704
|
+
use-prose :is(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, pre, table, figure, hr) {
|
|
2705
|
+
margin-block: 0;
|
|
2706
|
+
|
|
2707
|
+
use-theme-escape & {
|
|
2708
|
+
all: revert-layer;
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
use-prose
|
|
2713
|
+
:where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, pre, table, figure, hr)
|
|
2714
|
+
+ :is(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, pre, table, figure, hr) {
|
|
2715
|
+
margin-block-start: var(--usewc-layout-prose-flow-gap);
|
|
2716
|
+
|
|
2717
|
+
use-theme-escape & {
|
|
2718
|
+
all: revert-layer;
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
/* A heading opens a new section, so it takes more room above it than a paragraph does — but
|
|
2723
|
+
only when something precedes it. */
|
|
2724
|
+
use-prose
|
|
2725
|
+
:where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, pre, table, figure, hr)
|
|
2726
|
+
+ :is(h1, h2, h3, h4, h5, h6) {
|
|
2727
|
+
margin-block-start: var(--usewc-layout-prose-heading-gap);
|
|
2728
|
+
|
|
2729
|
+
use-theme-escape & {
|
|
2730
|
+
all: revert-layer;
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
use-prose :is(h1, h2, h3, h4, h5, h6) {
|
|
2735
|
+
font-weight: var(--usewc-font-prose-heading-weight);
|
|
2736
|
+
|
|
2737
|
+
use-theme-escape & {
|
|
2738
|
+
all: revert-layer;
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
use-prose h1 {
|
|
2743
|
+
font-size: var(--usewc-font-prose-h1-size);
|
|
2744
|
+
line-height: var(--usewc-layout-prose-h1-line-height);
|
|
2745
|
+
|
|
2746
|
+
use-theme-escape & {
|
|
2747
|
+
all: revert-layer;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
use-prose h2 {
|
|
2752
|
+
font-size: var(--usewc-font-prose-h2-size);
|
|
2753
|
+
line-height: var(--usewc-layout-prose-h2-line-height);
|
|
2754
|
+
|
|
2755
|
+
use-theme-escape & {
|
|
2756
|
+
all: revert-layer;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
use-prose h3 {
|
|
2761
|
+
font-size: var(--usewc-font-prose-h3-size);
|
|
2762
|
+
line-height: var(--usewc-layout-prose-h3-line-height);
|
|
2763
|
+
|
|
2764
|
+
use-theme-escape & {
|
|
2765
|
+
all: revert-layer;
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
use-prose h4 {
|
|
2770
|
+
font-size: var(--usewc-font-prose-h4-size);
|
|
2771
|
+
line-height: var(--usewc-layout-prose-h4-line-height);
|
|
2772
|
+
|
|
2773
|
+
use-theme-escape & {
|
|
2774
|
+
all: revert-layer;
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
use-prose h5 {
|
|
2779
|
+
font-size: var(--usewc-font-prose-h5-size);
|
|
2780
|
+
line-height: var(--usewc-layout-prose-h5-line-height);
|
|
2781
|
+
|
|
2782
|
+
use-theme-escape & {
|
|
2783
|
+
all: revert-layer;
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
use-prose h6 {
|
|
2788
|
+
font-size: var(--usewc-font-prose-h6-size);
|
|
2789
|
+
line-height: var(--usewc-layout-prose-h6-line-height);
|
|
2790
|
+
|
|
2791
|
+
use-theme-escape & {
|
|
2792
|
+
all: revert-layer;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
use-prose :is(ul, ol) {
|
|
2797
|
+
padding-inline-start: var(--usewc-layout-prose-list-indent);
|
|
2798
|
+
|
|
2799
|
+
use-theme-escape & {
|
|
2800
|
+
all: revert-layer;
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
use-prose blockquote {
|
|
2805
|
+
border-inline-start: var(--usewc-layout-prose-quote-border) solid var(--usewc-color-prose-quote-border);
|
|
2806
|
+
padding-inline-start: var(--usewc-layout-prose-quote-gap);
|
|
2807
|
+
color: var(--usewc-color-prose-quote-text);
|
|
2808
|
+
|
|
2809
|
+
use-theme-escape & {
|
|
2810
|
+
all: revert-layer;
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
use-prose pre {
|
|
2815
|
+
padding: var(--usewc-layout-prose-block-padding);
|
|
2816
|
+
overflow-x: auto;
|
|
2817
|
+
background: var(--usewc-color-prose-code-background);
|
|
2818
|
+
border: var(--usewc-layout-prose-rule-size) solid var(--usewc-color-prose-code-border);
|
|
2819
|
+
border-radius: var(--usewc-effect-prose-code-border-radius);
|
|
2820
|
+
|
|
2821
|
+
use-theme-escape & {
|
|
2822
|
+
all: revert-layer;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
/* `code.css` paints an inline pill — a tinted, padded, rounded inline-block. Inside a `<pre>`
|
|
2827
|
+
that reads as a badge floating in a slab, so the pill is undone and the code fills the block
|
|
2828
|
+
instead. The monospace family and size stay; only the inline chrome goes. */
|
|
2829
|
+
use-prose :where(pre) > code {
|
|
2830
|
+
display: block;
|
|
2831
|
+
padding: 0;
|
|
2832
|
+
border: none;
|
|
2833
|
+
border-radius: 0;
|
|
2834
|
+
background: none;
|
|
2835
|
+
color: inherit;
|
|
2836
|
+
|
|
2837
|
+
use-theme-escape & {
|
|
2838
|
+
all: revert-layer;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
use-prose hr {
|
|
2843
|
+
border: none;
|
|
2844
|
+
border-block-start: var(--usewc-layout-prose-rule-size) solid var(--usewc-color-prose-rule);
|
|
2845
|
+
|
|
2846
|
+
use-theme-escape & {
|
|
2847
|
+
all: revert-layer;
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
use-prose table {
|
|
2852
|
+
inline-size: 100%;
|
|
2853
|
+
border-collapse: collapse;
|
|
2854
|
+
|
|
2855
|
+
use-theme-escape & {
|
|
2856
|
+
all: revert-layer;
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
use-prose :is(th, td) {
|
|
2861
|
+
padding-block: var(--usewc-layout-prose-cell-padding-block);
|
|
2862
|
+
padding-inline: var(--usewc-layout-prose-cell-padding-inline);
|
|
2863
|
+
border-block-end: var(--usewc-layout-prose-rule-size) solid var(--usewc-color-prose-cell-border);
|
|
2864
|
+
text-align: start;
|
|
2865
|
+
|
|
2866
|
+
use-theme-escape & {
|
|
2867
|
+
all: revert-layer;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
use-prose figcaption {
|
|
2872
|
+
font-size: var(--usewc-font-prose-caption-size);
|
|
2873
|
+
color: var(--usewc-color-prose-caption-text);
|
|
2874
|
+
|
|
2875
|
+
use-theme-escape & {
|
|
2876
|
+
all: revert-layer;
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
use-prose :where(figure) > :is(img, picture, video) {
|
|
2881
|
+
display: block;
|
|
2882
|
+
inline-size: 100%;
|
|
2883
|
+
block-size: auto;
|
|
2884
|
+
|
|
2885
|
+
use-theme-escape & {
|
|
2886
|
+
all: revert-layer;
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
|
package/dist/tokens.css
CHANGED
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
--usewc-font-family-monospace: monospace;
|
|
53
53
|
|
|
54
54
|
/** Line height */
|
|
55
|
+
--usewc-line-height-14: 0.875rem;
|
|
55
56
|
--usewc-line-height-18: 1.125rem;
|
|
56
57
|
--usewc-line-height-20: 1.25rem;
|
|
57
58
|
--usewc-line-height-24: 1.5rem;
|
|
@@ -657,34 +658,100 @@
|
|
|
657
658
|
--usewc-layout-flex-gap-xlarge: var(--usewc-space-24);
|
|
658
659
|
--usewc-layout-flex-gap-super: var(--usewc-space-32);
|
|
659
660
|
|
|
661
|
+
/* Prose — the typographic scale for a block of authored text (see `theme/prose.css`). It is
|
|
662
|
+
the only place this system sets heading sizes, paragraph rhythm or list indentation: outside
|
|
663
|
+
a `use-prose`, flow content keeps the browser's own defaults, so adopting the design system
|
|
664
|
+
never silently restyles an app's existing copy.
|
|
665
|
+
|
|
666
|
+
The rhythm is expressed as the space *between* blocks rather than a margin on each, so the
|
|
667
|
+
scale reads as one ramp instead of a set of colliding margins. */
|
|
668
|
+
--usewc-layout-prose-measure: 68ch;
|
|
669
|
+
--usewc-layout-prose-flow-gap: var(--usewc-space-16);
|
|
670
|
+
--usewc-layout-prose-heading-gap: var(--usewc-space-32);
|
|
671
|
+
--usewc-layout-prose-list-indent: var(--usewc-space-24);
|
|
672
|
+
--usewc-layout-prose-quote-border: var(--usewc-space-2);
|
|
673
|
+
--usewc-layout-prose-quote-gap: var(--usewc-space-16);
|
|
674
|
+
--usewc-layout-prose-rule-size: var(--usewc-space-1);
|
|
675
|
+
--usewc-layout-prose-block-padding: var(--usewc-space-16);
|
|
676
|
+
--usewc-layout-prose-cell-padding-block: var(--usewc-space-8);
|
|
677
|
+
--usewc-layout-prose-cell-padding-inline: var(--usewc-space-12);
|
|
678
|
+
|
|
679
|
+
--usewc-font-prose-h1-size: var(--usewc-font-size-24);
|
|
680
|
+
--usewc-font-prose-h2-size: var(--usewc-font-size-20);
|
|
681
|
+
--usewc-font-prose-h3-size: var(--usewc-font-size-18);
|
|
682
|
+
--usewc-font-prose-h4-size: var(--usewc-font-size-16);
|
|
683
|
+
--usewc-font-prose-h5-size: var(--usewc-font-size-14);
|
|
684
|
+
--usewc-font-prose-h6-size: var(--usewc-font-size-14);
|
|
685
|
+
--usewc-font-prose-heading-weight: 600;
|
|
686
|
+
--usewc-font-prose-caption-size: var(--usewc-font-size-14);
|
|
687
|
+
|
|
688
|
+
--usewc-layout-prose-h1-line-height: var(--usewc-line-height-32);
|
|
689
|
+
--usewc-layout-prose-h2-line-height: var(--usewc-line-height-28);
|
|
690
|
+
--usewc-layout-prose-h3-line-height: var(--usewc-line-height-24);
|
|
691
|
+
--usewc-layout-prose-h4-line-height: var(--usewc-line-height-24);
|
|
692
|
+
--usewc-layout-prose-h5-line-height: var(--usewc-line-height-20);
|
|
693
|
+
--usewc-layout-prose-h6-line-height: var(--usewc-line-height-20);
|
|
694
|
+
/* The body line box every flow block inherits — paragraphs, list items, table cells, quotes.
|
|
695
|
+
Headings declare their own below, so this is the one value that sets reading rhythm. */
|
|
696
|
+
--usewc-layout-prose-line-height: var(--usewc-line-height-24);
|
|
697
|
+
|
|
698
|
+
--usewc-color-prose-quote-border: var(--usewc-color-surface-border);
|
|
699
|
+
--usewc-color-prose-quote-text: var(--usewc-color-document-text-muted);
|
|
700
|
+
--usewc-color-prose-caption-text: var(--usewc-color-document-text-muted);
|
|
701
|
+
--usewc-color-prose-rule: var(--usewc-color-surface-border);
|
|
702
|
+
--usewc-color-prose-cell-border: var(--usewc-color-surface-border);
|
|
703
|
+
/* A code block is a slab, not the inline pill `code.css` paints — it gets a neutral ground of
|
|
704
|
+
its own so the page's own background can sit behind it. */
|
|
705
|
+
--usewc-color-prose-code-background: var(--usewc-color-gray-50);
|
|
706
|
+
--usewc-color-prose-code-border: var(--usewc-color-surface-border);
|
|
707
|
+
--usewc-effect-prose-code-border-radius: var(--usewc-effect-border-radius-6);
|
|
708
|
+
|
|
660
709
|
/* Layout regions — the density scale each `use-layout` variant sets on itself, which the one
|
|
661
710
|
generic set of header / main / footer rules in `theme/layout.css` then reads. The four
|
|
662
711
|
variants are deliberately far apart: a document and a card in a feed can't share one
|
|
663
712
|
padding step and still read as themselves, so each gets its own rather than overriding a
|
|
664
713
|
shared base. The hairline colour is shared, since a rule is a rule wherever it lands. */
|
|
665
714
|
--usewc-color-region-border: var(--usewc-color-surface-border);
|
|
715
|
+
--usewc-layout-region-rule: var(--usewc-layout-surface-border) solid var(--usewc-color-region-border);
|
|
666
716
|
--usewc-layout-region-title-gap: var(--usewc-space-2);
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
--usewc-layout-
|
|
717
|
+
/* A title group sits beside a fixed-size avatar, so its lines are given explicit line boxes
|
|
718
|
+
rather than the document's ratio — a stacked title and subtitle then add up to a known
|
|
719
|
+
height instead of drifting with the inherited leading. The subtitle steps down a size, since
|
|
720
|
+
a timestamp or a byline is secondary to the thing it belongs to. */
|
|
721
|
+
--usewc-layout-region-title-line-height: var(--usewc-line-height-20);
|
|
722
|
+
--usewc-font-region-subtitle-size: var(--usewc-font-size-14);
|
|
723
|
+
--usewc-layout-region-subtitle-line-height: var(--usewc-line-height-14);
|
|
724
|
+
/* A stacked title and subtitle add up to this, and it's what an avatar beside them is sized
|
|
725
|
+
to — so the two always agree, and both land on a base button's 36px height rather than
|
|
726
|
+
being pinned there by a magic number. */
|
|
727
|
+
--usewc-layout-region-title-block-size: calc(
|
|
728
|
+
var(--usewc-layout-region-title-line-height) + var(--usewc-layout-region-title-gap) +
|
|
729
|
+
var(--usewc-layout-region-subtitle-line-height)
|
|
730
|
+
);
|
|
672
731
|
|
|
673
732
|
--usewc-layout-page-padding: var(--usewc-space-20);
|
|
674
733
|
--usewc-layout-page-padding-block: var(--usewc-space-12);
|
|
675
734
|
--usewc-layout-page-gap: var(--usewc-space-12);
|
|
676
|
-
--usewc-layout-page-avatar-size: var(--usewc-
|
|
735
|
+
--usewc-layout-page-avatar-size: var(--usewc-layout-region-title-block-size);
|
|
677
736
|
|
|
678
737
|
--usewc-layout-card-padding: var(--usewc-space-12);
|
|
679
738
|
--usewc-layout-card-gap: var(--usewc-space-8);
|
|
680
739
|
--usewc-layout-card-avatar-size: var(--usewc-space-24);
|
|
681
740
|
|
|
682
|
-
/*
|
|
683
|
-
header
|
|
684
|
-
|
|
741
|
+
/* Avatar sizes match a base button's 36px height, so a likeness and the controls beside it in
|
|
742
|
+
a header line up on both edges rather than one floating inside the other.
|
|
743
|
+
|
|
744
|
+
An entry's avatar size doubles as the width of the rail its regions sit beside — a grouped
|
|
745
|
+
entry omits the avatar entirely, and the bodies only stay aligned down a list if the
|
|
746
|
+
reserved strip is exactly as wide as the avatar it replaces. `.message` is the same layout
|
|
747
|
+
at chat density, since it repeats far more often in a feed. */
|
|
748
|
+
--usewc-layout-entry-padding: var(--usewc-space-16);
|
|
749
|
+
--usewc-layout-entry-gap: var(--usewc-space-12);
|
|
750
|
+
--usewc-layout-entry-avatar-size: var(--usewc-layout-region-title-block-size);
|
|
751
|
+
|
|
685
752
|
--usewc-layout-message-padding: var(--usewc-space-12);
|
|
686
753
|
--usewc-layout-message-gap: var(--usewc-space-12);
|
|
687
|
-
--usewc-layout-message-rail: var(--usewc-
|
|
754
|
+
--usewc-layout-message-rail: var(--usewc-layout-region-title-block-size);
|
|
688
755
|
|
|
689
756
|
/* Surface — the visible chrome for a surface host (background/border/radius/shadow), the
|
|
690
757
|
backdrop behind a modal `<dialog>` (non-modal `[popover]` keeps its transparent UA
|
|
@@ -712,6 +779,7 @@
|
|
|
712
779
|
/* The primary/danger fill stays the same shade in both themes — only the
|
|
713
780
|
focus ring lightens in the dark. */
|
|
714
781
|
--usewc-color-outline: var(--usewc-color-blue-400);
|
|
782
|
+
--usewc-color-prose-code-background: var(--usewc-color-gray-900);
|
|
715
783
|
--usewc-color-input-background-static: oklch(from var(--usewc-color-gray-100) l c h / 0.025);
|
|
716
784
|
--usewc-color-input-border-static: var(--usewc-color-gray-600);
|
|
717
785
|
--usewc-color-input-text-static: var(--usewc-color-gray-200);
|
package/package.json
CHANGED
|
@@ -69,6 +69,12 @@
|
|
|
69
69
|
"attributes": [{ "name": "date", "values": [] }],
|
|
70
70
|
"references": []
|
|
71
71
|
},
|
|
72
|
+
{
|
|
73
|
+
"name": "use-caret",
|
|
74
|
+
"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```\n---\n\n\n### **Slots:**\n - **default** - Trigger label content.\n- **icon** - Replaces the default caret glyph.",
|
|
75
|
+
"attributes": [],
|
|
76
|
+
"references": []
|
|
77
|
+
},
|
|
72
78
|
{
|
|
73
79
|
"name": "use-date-input",
|
|
74
80
|
"description": "Displays a date picker using segmented inputs for year, month, and day.\n\nUses browser locale for formatting and ARIA labels.\n---\n",
|
|
@@ -80,12 +86,6 @@
|
|
|
80
86
|
],
|
|
81
87
|
"references": []
|
|
82
88
|
},
|
|
83
|
-
{
|
|
84
|
-
"name": "use-caret",
|
|
85
|
-
"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```\n---\n\n\n### **Slots:**\n - **default** - Trigger label content.\n- **icon** - Replaces the default caret glyph.",
|
|
86
|
-
"attributes": [],
|
|
87
|
-
"references": []
|
|
88
|
-
},
|
|
89
89
|
{
|
|
90
90
|
"name": "use-date-picker",
|
|
91
91
|
"description": "A day-granularity date picker. Value format: YYYY-MM-DD.\n\nCustomize day rendering on an instance:\n```ts\ndatePicker.renderDay = ({ day, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${day}</strong>` : String(day);\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyDatePicker extends UseDatePicker {\n renderDay(data: { day: number; date: string }, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.day}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```\n---\n\n\n### **Events:**\n - **use-change** - Fired on selection with detail `{ value: string }`\n\n### **Methods:**\n - **renderDay(data: _{ day: number; date: string }_, _html: _LitHtml_): _TemplateResult | string_** - See UseCalendarRenderDay. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.\n\n### **Slots:**\n - **header-start** - Content before the title in the header\n- **title** - Title showing the month and year\n- **controls** - Navigation controls (previous, today, next buttons)\n- **header-end** - Content after the controls in the header\n- **date-{YYYY-MM-DD}** - Per-date content that replaces the default day number\n\n### **CSS Parts:**\n - **header** - The header container\n- **title** - The title text\n- **title-month** - The month part of the title\n- **title-literal** - Literal text in the title\n- **title-year** - The year part of the title\n- **controls** - The controls slot container\n- **control** - Individual control button\n- **control-previous** - Previous month button\n- **control-today** - Today button\n- **control-next** - Next month button\n- **grid** - The main calendar grid\n- **grid-header** - The header row with day names\n- **grid-header-cell** - Individual day name header cell\n- **grid-body** - The container for calendar weeks\n- **row** - A row of days\n- **day** - A day cell\n- **day-empty** - An empty day cell (from previous/next month)\n- **day-selected** - A selected day cell\n- **day-today** - The current date cell\n- **day-disabled** - A disabled date cell\n- **title-button** - The clickable button wrapping the month/year title\n- **picker** - The scrollable month/year picker panel (replaces grid when open)\n- **picker-year-section** - Container for one year's label and month grid\n- **picker-year-label** - The year heading inside a year section\n- **picker-months** - The 4-column month grid inside a year section\n- **picker-month** - An individual month button\n- **picker-month-current** - Applied to the currently selected month button",
|