use-wc 1.0.0-pre.50 → 1.0.0-pre.52
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 +208 -65
- package/dist/design-system.css +119 -1
- package/dist/docs/use-badge.md +88 -0
- package/dist/tokens.css +18 -0
- package/dist/use-wc.d.mts +103 -1
- package/dist/use-wc.mjs +158 -2
- package/package.json +1 -1
- package/vscode.html-custom-data.json +32 -0
package/custom-elements.json
CHANGED
|
@@ -89,71 +89,6 @@
|
|
|
89
89
|
}
|
|
90
90
|
]
|
|
91
91
|
},
|
|
92
|
-
{
|
|
93
|
-
"kind": "javascript-module",
|
|
94
|
-
"path": "src/elements/use-calendarday/use-calendarday.ts",
|
|
95
|
-
"declarations": [
|
|
96
|
-
{
|
|
97
|
-
"kind": "class",
|
|
98
|
-
"description": "A helper component for customizing specific days within `use-calendar`.\nContent placed inside this element will replace the default day number display.",
|
|
99
|
-
"name": "UseCalendarday",
|
|
100
|
-
"slots": [
|
|
101
|
-
{
|
|
102
|
-
"description": "slot for custom day content (replaces the day number)",
|
|
103
|
-
"name": "default"
|
|
104
|
-
}
|
|
105
|
-
],
|
|
106
|
-
"members": [
|
|
107
|
-
{
|
|
108
|
-
"kind": "field",
|
|
109
|
-
"name": "date",
|
|
110
|
-
"type": {
|
|
111
|
-
"text": "string"
|
|
112
|
-
},
|
|
113
|
-
"attribute": "date"
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
"kind": "field",
|
|
117
|
-
"name": "day",
|
|
118
|
-
"readonly": true
|
|
119
|
-
}
|
|
120
|
-
],
|
|
121
|
-
"attributes": [
|
|
122
|
-
{
|
|
123
|
-
"name": "date",
|
|
124
|
-
"type": {
|
|
125
|
-
"text": "string"
|
|
126
|
-
},
|
|
127
|
-
"fieldName": "date"
|
|
128
|
-
}
|
|
129
|
-
],
|
|
130
|
-
"superclass": {
|
|
131
|
-
"name": "LitElement",
|
|
132
|
-
"package": "lit"
|
|
133
|
-
},
|
|
134
|
-
"tagName": "use-calendarday",
|
|
135
|
-
"customElement": true
|
|
136
|
-
}
|
|
137
|
-
],
|
|
138
|
-
"exports": [
|
|
139
|
-
{
|
|
140
|
-
"kind": "js",
|
|
141
|
-
"name": "UseCalendarday",
|
|
142
|
-
"declaration": {
|
|
143
|
-
"name": "UseCalendarday",
|
|
144
|
-
"module": "src/elements/use-calendarday/use-calendarday.ts"
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
"kind": "custom-element-definition",
|
|
149
|
-
"name": "use-calendarday",
|
|
150
|
-
"declaration": {
|
|
151
|
-
"name": "UseCalendarday",
|
|
152
|
-
"module": "src/elements/use-calendarday/use-calendarday.ts"
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
]
|
|
156
|
-
},
|
|
157
92
|
{
|
|
158
93
|
"kind": "javascript-module",
|
|
159
94
|
"path": "src/elements/use-calendar-base/use-calendar-base.ts",
|
|
@@ -559,6 +494,214 @@
|
|
|
559
494
|
}
|
|
560
495
|
]
|
|
561
496
|
},
|
|
497
|
+
{
|
|
498
|
+
"kind": "javascript-module",
|
|
499
|
+
"path": "src/elements/use-badge/use-badge.ts",
|
|
500
|
+
"declarations": [
|
|
501
|
+
{
|
|
502
|
+
"kind": "class",
|
|
503
|
+
"description": "`use-badge` renders a small indicator anchored to another element's corner, using\n[CSS anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)\ninstead of the classic `position: absolute` + negative-offset trick. This lets the badge live\nanywhere in the DOM rather than being a child of the element it decorates. A badge's shape,\ncolor, and positioning ship built in — reading from the design system's tokens when\n`tokens.css` is loaded, and falling back to sensible defaults otherwise — so no theme is\nrequired to get a working, visible badge.\n\nPoint a badge at its target with the `anchortarget` attribute, set to the target's `id`\n(named to avoid colliding with the `anchor` content attribute HTML is gaining natively):\n\n```html\n<button type=\"button\" id=\"inbox\">Inbox</button>\n<use-badge anchortarget=\"inbox\">99+</use-badge>\n```\n\nThe badge and its anchor must live in the same DOM tree — a badge cannot reach an anchor\ninside another component's shadow root, and an anchor's `anchor-name` set inside a shadow\ntree's stylesheet is not visible here. No positioned ancestor or shared containing block is\nrequired: anchor positioning resolves the badge's location straight from the anchor's own\ngeometry, so the badge can sit anywhere else in the DOM tree.\n\nWithout a resolved anchor — `anchortarget` unset, pointing at a missing id, or `anchorElement`\nnever assigned — the badge has no `:state(anchored)` and renders as a plain inline chip in\nnormal document flow, still using the same color/shape styling.\n\nSome components render their host as `display: contents`, which has no box of its own for CSS\nanchor positioning to resolve against. If the anchor computes a non-empty `--usewc-anchor-name`\ncustom property, the badge uses that dashed-ident as its `position-anchor` directly instead of\ngenerating and writing its own onto the anchor — letting a component publish the name of a\nreal, box-generating part of itself (already established for its own purposes) for badges to\ntarget instead of its host, even though that part lives inside the component's own shadow root.\nThis mechanism is shared with `use-menu`/`use-anchored` via `AnchorController`; components\nwithout a `display: contents` host don't need to publish anything.\n\n**Known Firefox limitation:** anchoring across a shadow root boundary this way — the case\n`--usewc-anchor-name` exists for — is mispositioned in Firefox as of this writing. Verified\ndirectly: `anchor()`'s logical `start`/`end` keywords resolve backwards on both axes\nspecifically for a cross-shadow-root anchor reference, in Firefox only — the same anchor()\ncalls resolve correctly for a same-tree anchor in Firefox, and cross-shadow anchoring resolves\ncorrectly in Chromium and Safari. A badge anchored to a plain element (button, input, anything\nthat isn't itself relying on this escape hatch) is unaffected in every engine, including\nFirefox.\n\n## Side effects\n\nThe badge is a sibling of its anchor rather than a child, so **as a side effect, a badge\nmutates its anchor element**: it appends its own id to the anchor's `aria-describedby`\n(preserving any ids already there, the same way `anchor-name` is appended rather than\nreplaced) to keep its content connected to the anchor for assistive technology. This means a\nbadge's content, or its `aria-label` when set, is announced as part of interacting with the\nanchor, e.g. \"Inbox button, 3\". A `dot` badge has no content of its own, so set `aria-label`\ndirectly on `<use-badge>` for a meaningful description (e.g. `aria-label=\"3 unread messages\"`);\nwithout one, the description contributes nothing and the anchor announces as before.\n\nAvoid adding `aria-hidden` to `<use-badge>` if you rely on this: while the description\ncomputation is specified to still read a hidden node when it's the direct target of\n`aria-describedby`, support for that specific case is inconsistent across browsers and screen\nreaders.\n\nSet `options=\"noaria\"` to opt a badge out of this and leave the anchor's `aria-describedby`\nuntouched, e.g. when the anchor already documents the badge's meaning some other way.",
|
|
504
|
+
"name": "UseBadge",
|
|
505
|
+
"slots": [
|
|
506
|
+
{
|
|
507
|
+
"description": "Badge content, e.g. a count or short label. Ignored when `dot` is set.",
|
|
508
|
+
"name": ""
|
|
509
|
+
}
|
|
510
|
+
],
|
|
511
|
+
"members": [
|
|
512
|
+
{
|
|
513
|
+
"kind": "field",
|
|
514
|
+
"name": "anchortarget",
|
|
515
|
+
"type": {
|
|
516
|
+
"text": "string | undefined"
|
|
517
|
+
},
|
|
518
|
+
"attribute": "anchortarget",
|
|
519
|
+
"reflects": true
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
"kind": "field",
|
|
523
|
+
"name": "dot",
|
|
524
|
+
"type": {
|
|
525
|
+
"text": "boolean"
|
|
526
|
+
},
|
|
527
|
+
"default": "false",
|
|
528
|
+
"attribute": "dot",
|
|
529
|
+
"reflects": true
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
"kind": "field",
|
|
533
|
+
"name": "blockalign",
|
|
534
|
+
"type": {
|
|
535
|
+
"text": "\"start\" | \"end\""
|
|
536
|
+
},
|
|
537
|
+
"default": "\"start\"",
|
|
538
|
+
"attribute": "blockalign",
|
|
539
|
+
"reflects": true
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
"kind": "field",
|
|
543
|
+
"name": "inlinealign",
|
|
544
|
+
"type": {
|
|
545
|
+
"text": "\"start\" | \"end\""
|
|
546
|
+
},
|
|
547
|
+
"default": "\"end\"",
|
|
548
|
+
"attribute": "inlinealign",
|
|
549
|
+
"reflects": true
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
"kind": "field",
|
|
553
|
+
"name": "options",
|
|
554
|
+
"type": {
|
|
555
|
+
"text": "string"
|
|
556
|
+
},
|
|
557
|
+
"default": "\"\"",
|
|
558
|
+
"attribute": "options",
|
|
559
|
+
"reflects": true
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
"kind": "field",
|
|
563
|
+
"name": "anchorElement",
|
|
564
|
+
"description": "The element this badge is anchored to. Reading returns whatever element was last resolved,\neither from the `anchortarget` id or set here directly. Setting it anchors to that element\ninstead of resolving `anchortarget`, for cases where the target has no id to reference."
|
|
565
|
+
}
|
|
566
|
+
],
|
|
567
|
+
"attributes": [
|
|
568
|
+
{
|
|
569
|
+
"description": "Id of the element to anchor to, resolved in the badge's own tree.",
|
|
570
|
+
"name": "anchortarget",
|
|
571
|
+
"type": {
|
|
572
|
+
"text": "string | undefined"
|
|
573
|
+
},
|
|
574
|
+
"fieldName": "anchortarget"
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
"description": "Renders as a fixed-size dot instead of the slotted content.",
|
|
578
|
+
"name": "dot",
|
|
579
|
+
"type": {
|
|
580
|
+
"text": "boolean"
|
|
581
|
+
},
|
|
582
|
+
"default": "false",
|
|
583
|
+
"fieldName": "dot"
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
"description": "`\"start\" | \"end\"`: Aligns the badge along the block axis relative to its anchor.",
|
|
587
|
+
"name": "blockalign",
|
|
588
|
+
"type": {
|
|
589
|
+
"text": "\"start\" | \"end\""
|
|
590
|
+
},
|
|
591
|
+
"default": "\"start\"",
|
|
592
|
+
"fieldName": "blockalign"
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
"description": "`\"start\" | \"end\"`: Aligns the badge along the inline axis relative to its anchor.",
|
|
596
|
+
"name": "inlinealign",
|
|
597
|
+
"type": {
|
|
598
|
+
"text": "\"start\" | \"end\""
|
|
599
|
+
},
|
|
600
|
+
"default": "\"end\"",
|
|
601
|
+
"fieldName": "inlinealign"
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
"description": "Space-separated feature flags. `noaria` opts out of the automatic `aria-describedby` wiring described above.",
|
|
605
|
+
"name": "options",
|
|
606
|
+
"type": {
|
|
607
|
+
"text": "string"
|
|
608
|
+
},
|
|
609
|
+
"default": "\"\"",
|
|
610
|
+
"fieldName": "options"
|
|
611
|
+
}
|
|
612
|
+
],
|
|
613
|
+
"superclass": {
|
|
614
|
+
"name": "LitElement",
|
|
615
|
+
"package": "lit"
|
|
616
|
+
},
|
|
617
|
+
"tagName": "use-badge",
|
|
618
|
+
"customElement": true
|
|
619
|
+
}
|
|
620
|
+
],
|
|
621
|
+
"exports": [
|
|
622
|
+
{
|
|
623
|
+
"kind": "js",
|
|
624
|
+
"name": "UseBadge",
|
|
625
|
+
"declaration": {
|
|
626
|
+
"name": "UseBadge",
|
|
627
|
+
"module": "src/elements/use-badge/use-badge.ts"
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
"kind": "custom-element-definition",
|
|
632
|
+
"name": "use-badge",
|
|
633
|
+
"declaration": {
|
|
634
|
+
"name": "UseBadge",
|
|
635
|
+
"module": "src/elements/use-badge/use-badge.ts"
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
]
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
"kind": "javascript-module",
|
|
642
|
+
"path": "src/elements/use-calendarday/use-calendarday.ts",
|
|
643
|
+
"declarations": [
|
|
644
|
+
{
|
|
645
|
+
"kind": "class",
|
|
646
|
+
"description": "A helper component for customizing specific days within `use-calendar`.\nContent placed inside this element will replace the default day number display.",
|
|
647
|
+
"name": "UseCalendarday",
|
|
648
|
+
"slots": [
|
|
649
|
+
{
|
|
650
|
+
"description": "slot for custom day content (replaces the day number)",
|
|
651
|
+
"name": "default"
|
|
652
|
+
}
|
|
653
|
+
],
|
|
654
|
+
"members": [
|
|
655
|
+
{
|
|
656
|
+
"kind": "field",
|
|
657
|
+
"name": "date",
|
|
658
|
+
"type": {
|
|
659
|
+
"text": "string"
|
|
660
|
+
},
|
|
661
|
+
"attribute": "date"
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
"kind": "field",
|
|
665
|
+
"name": "day",
|
|
666
|
+
"readonly": true
|
|
667
|
+
}
|
|
668
|
+
],
|
|
669
|
+
"attributes": [
|
|
670
|
+
{
|
|
671
|
+
"name": "date",
|
|
672
|
+
"type": {
|
|
673
|
+
"text": "string"
|
|
674
|
+
},
|
|
675
|
+
"fieldName": "date"
|
|
676
|
+
}
|
|
677
|
+
],
|
|
678
|
+
"superclass": {
|
|
679
|
+
"name": "LitElement",
|
|
680
|
+
"package": "lit"
|
|
681
|
+
},
|
|
682
|
+
"tagName": "use-calendarday",
|
|
683
|
+
"customElement": true
|
|
684
|
+
}
|
|
685
|
+
],
|
|
686
|
+
"exports": [
|
|
687
|
+
{
|
|
688
|
+
"kind": "js",
|
|
689
|
+
"name": "UseCalendarday",
|
|
690
|
+
"declaration": {
|
|
691
|
+
"name": "UseCalendarday",
|
|
692
|
+
"module": "src/elements/use-calendarday/use-calendarday.ts"
|
|
693
|
+
}
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
"kind": "custom-element-definition",
|
|
697
|
+
"name": "use-calendarday",
|
|
698
|
+
"declaration": {
|
|
699
|
+
"name": "UseCalendarday",
|
|
700
|
+
"module": "src/elements/use-calendarday/use-calendarday.ts"
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
]
|
|
704
|
+
},
|
|
562
705
|
{
|
|
563
706
|
"kind": "javascript-module",
|
|
564
707
|
"path": "src/elements/use-caret/use-caret.ts",
|
package/dist/design-system.css
CHANGED
|
@@ -842,7 +842,6 @@
|
|
|
842
842
|
--min-size: 30vh;
|
|
843
843
|
--max-size: 80vh;
|
|
844
844
|
|
|
845
|
-
min-block-size: var(--min-size);
|
|
846
845
|
max-block-size: min(100% - var(--usewc-layout-popover-menu-anchor-offset), var(--max-size));
|
|
847
846
|
margin-block: var(--usewc-layout-popover-menu-anchor-offset);
|
|
848
847
|
overflow: auto;
|
|
@@ -868,12 +867,16 @@
|
|
|
868
867
|
--usewc-color-popover-menu-item-text: var(--usewc-color-popover-menu-item-text-static);
|
|
869
868
|
--usewc-effect-outline-width: 0;
|
|
870
869
|
|
|
870
|
+
appearance: none;
|
|
871
871
|
border: none;
|
|
872
|
+
box-shadow: none;
|
|
873
|
+
font: inherit;
|
|
872
874
|
text-align: start;
|
|
873
875
|
cursor: default;
|
|
874
876
|
display: flex;
|
|
875
877
|
flex-direction: row;
|
|
876
878
|
justify-content: start;
|
|
879
|
+
width: 100%;
|
|
877
880
|
gap: var(--usewc-layout-popover-menu-item-padding-inline);
|
|
878
881
|
line-height: var(--usewc-layout-popover-menu-item-line-height);
|
|
879
882
|
padding-inline: var(--usewc-layout-popover-menu-item-padding-inline);
|
|
@@ -1010,3 +1013,118 @@
|
|
|
1010
1013
|
}
|
|
1011
1014
|
}
|
|
1012
1015
|
|
|
1016
|
+
/* Badge — use-badge.ts ships fixed, literal default styles with no knowledge of the token
|
|
1017
|
+
system, so a themed badge looks identical whether or not this rule is present; this layer
|
|
1018
|
+
replaces those defaults with the design system's token-driven look wherever it's loaded. An
|
|
1019
|
+
outer rule targeting a custom element by tag name overrides that element's own :host rules
|
|
1020
|
+
of equal specificity, so this only needs to match `use-badge` directly.
|
|
1021
|
+
|
|
1022
|
+
Unlike most native elements this family themes, use-badge's own :host styles already are a
|
|
1023
|
+
complete, self-sufficient look (shape, color, and anchor-corner positioning), not a bare
|
|
1024
|
+
browser default — so `use-theme-escape & { all: revert-layer; }` on every rule below reverts
|
|
1025
|
+
cleanly back to that built-in appearance, the same as everywhere else in this family. */
|
|
1026
|
+
@layer use-theme.badge {
|
|
1027
|
+
use-badge {
|
|
1028
|
+
--usewc-layout-badge-offset: calc(var(--usewc-layout-badge-dot-size) / 3);
|
|
1029
|
+
display: inline-block;
|
|
1030
|
+
line-height: 1.5;
|
|
1031
|
+
border-radius: var(--usewc-effect-badge-border-radius);
|
|
1032
|
+
background: var(--usewc-color-badge-background-static);
|
|
1033
|
+
color: var(--usewc-color-badge-text-static);
|
|
1034
|
+
font-size: var(--usewc-font-badge-size);
|
|
1035
|
+
font-weight: var(--usewc-font-badge-weight);
|
|
1036
|
+
border: var(--usewc-layout-badge-border) solid var(--usewc-color-badge-border);
|
|
1037
|
+
|
|
1038
|
+
use-theme-escape & {
|
|
1039
|
+
all: revert-layer;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
use-badge:not([dot]) {
|
|
1044
|
+
--usewc-layout-badge-offset: 0.5lh;
|
|
1045
|
+
padding-inline: var(--usewc-layout-badge-padding-inline);
|
|
1046
|
+
|
|
1047
|
+
use-theme-escape & {
|
|
1048
|
+
all: revert-layer;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
use-badge[dot] {
|
|
1053
|
+
inline-size: var(--usewc-layout-badge-dot-size);
|
|
1054
|
+
block-size: var(--usewc-layout-badge-dot-size);
|
|
1055
|
+
|
|
1056
|
+
use-theme-escape & {
|
|
1057
|
+
all: revert-layer;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/* Positioning pins via plain anchor() — never calc(anchor() - <offset>) — because calc()
|
|
1062
|
+
silently drops its arithmetic when the anchor() inside it crosses a shadow root boundary
|
|
1063
|
+
(confirmed with both var() and literal operands; plain anchor() with no calc() is
|
|
1064
|
+
unaffected, as is calc() that doesn't contain an anchor()). The offset instead lives in
|
|
1065
|
+
translate, a separate post-layout transform that never touches anchor() at all, so it's
|
|
1066
|
+
immune to that bug — using a per-axis sign multiplier since translate is always physical
|
|
1067
|
+
x/y, unlike the logical inset-*-start/-end pair used to pin each edge. inline-axis signs are
|
|
1068
|
+
flipped again under :dir(rtl) for the same reason: translate doesn't auto-mirror the way the
|
|
1069
|
+
logical inset properties and anchor() arguments do, so without this a badge would extend
|
|
1070
|
+
into its anchor instead of away from it in RTL.
|
|
1071
|
+
|
|
1072
|
+
The default corner (block-start/inline-end) is set unconditionally in this base rule, both
|
|
1073
|
+
insets at once, rather than via :is(:not([blockalign]), [blockalign="start"]) — deliberately
|
|
1074
|
+
avoiding :is()/:not() compound selectors here: at least one browser has been observed to mis-
|
|
1075
|
+
resolve which of two same-property declarations wins when the selectors get this compound,
|
|
1076
|
+
even though .matches() correctly reports only one of them as matching. The two attribute
|
|
1077
|
+
overrides below are plain, unambiguous attribute selectors instead, each resetting the axis
|
|
1078
|
+
the base rule set and pinning its own edge — simple enough to be safe across engines. */
|
|
1079
|
+
use-badge:state(anchored) {
|
|
1080
|
+
--usewc-badge-translate-x: 1;
|
|
1081
|
+
--usewc-badge-translate-y: -1;
|
|
1082
|
+
translate: calc(var(--usewc-badge-translate-x) * var(--usewc-layout-badge-offset))
|
|
1083
|
+
calc(var(--usewc-badge-translate-y) * var(--usewc-layout-badge-offset));
|
|
1084
|
+
inset-inline-start: unset;
|
|
1085
|
+
inset-inline-end: anchor(end);
|
|
1086
|
+
inset-block-end: unset;
|
|
1087
|
+
inset-block-start: anchor(start);
|
|
1088
|
+
|
|
1089
|
+
use-theme-escape & {
|
|
1090
|
+
all: revert-layer;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
use-badge:state(anchored)[inlinealign="start"] {
|
|
1095
|
+
inset-inline-end: unset;
|
|
1096
|
+
inset-inline-start: anchor(start);
|
|
1097
|
+
--usewc-badge-translate-x: -1;
|
|
1098
|
+
|
|
1099
|
+
use-theme-escape & {
|
|
1100
|
+
all: revert-layer;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
use-badge:state(anchored)[blockalign="end"] {
|
|
1105
|
+
inset-block-start: unset;
|
|
1106
|
+
inset-block-end: anchor(end);
|
|
1107
|
+
--usewc-badge-translate-y: 1;
|
|
1108
|
+
|
|
1109
|
+
use-theme-escape & {
|
|
1110
|
+
all: revert-layer;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
use-badge:dir(rtl):state(anchored) {
|
|
1115
|
+
--usewc-badge-translate-x: -1;
|
|
1116
|
+
|
|
1117
|
+
use-theme-escape & {
|
|
1118
|
+
all: revert-layer;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
use-badge:dir(rtl):state(anchored)[inlinealign="start"] {
|
|
1123
|
+
--usewc-badge-translate-x: 1;
|
|
1124
|
+
|
|
1125
|
+
use-theme-escape & {
|
|
1126
|
+
all: revert-layer;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# `use-badge`
|
|
2
|
+
|
|
3
|
+
`use-badge` renders a small indicator anchored to another element's corner, using
|
|
4
|
+
[CSS anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)
|
|
5
|
+
instead of the classic `position: absolute` + negative-offset trick. This lets the badge live
|
|
6
|
+
anywhere in the DOM rather than being a child of the element it decorates. A badge's shape,
|
|
7
|
+
color, and positioning ship built in — reading from the design system's tokens when
|
|
8
|
+
`tokens.css` is loaded, and falling back to sensible defaults otherwise — so no theme is
|
|
9
|
+
required to get a working, visible badge.
|
|
10
|
+
|
|
11
|
+
Point a badge at its target with the `anchortarget` attribute, set to the target's `id`
|
|
12
|
+
(named to avoid colliding with the `anchor` content attribute HTML is gaining natively):
|
|
13
|
+
|
|
14
|
+
```html
|
|
15
|
+
<button type="button" id="inbox">Inbox</button>
|
|
16
|
+
<use-badge anchortarget="inbox">99+</use-badge>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The badge and its anchor must live in the same DOM tree — a badge cannot reach an anchor
|
|
20
|
+
inside another component's shadow root, and an anchor's `anchor-name` set inside a shadow
|
|
21
|
+
tree's stylesheet is not visible here. No positioned ancestor or shared containing block is
|
|
22
|
+
required: anchor positioning resolves the badge's location straight from the anchor's own
|
|
23
|
+
geometry, so the badge can sit anywhere else in the DOM tree.
|
|
24
|
+
|
|
25
|
+
Without a resolved anchor — `anchortarget` unset, pointing at a missing id, or `anchorElement`
|
|
26
|
+
never assigned — the badge has no `:state(anchored)` and renders as a plain inline chip in
|
|
27
|
+
normal document flow, still using the same color/shape styling.
|
|
28
|
+
|
|
29
|
+
Some components render their host as `display: contents`, which has no box of its own for CSS
|
|
30
|
+
anchor positioning to resolve against. If the anchor computes a non-empty `--usewc-anchor-name`
|
|
31
|
+
custom property, the badge uses that dashed-ident as its `position-anchor` directly instead of
|
|
32
|
+
generating and writing its own onto the anchor — letting a component publish the name of a
|
|
33
|
+
real, box-generating part of itself (already established for its own purposes) for badges to
|
|
34
|
+
target instead of its host, even though that part lives inside the component's own shadow root.
|
|
35
|
+
This mechanism is shared with `use-menu`/`use-anchored` via `AnchorController`; components
|
|
36
|
+
without a `display: contents` host don't need to publish anything.
|
|
37
|
+
|
|
38
|
+
**Known Firefox limitation:** anchoring across a shadow root boundary this way — the case
|
|
39
|
+
`--usewc-anchor-name` exists for — is mispositioned in Firefox as of this writing. Verified
|
|
40
|
+
directly: `anchor()`'s logical `start`/`end` keywords resolve backwards on both axes
|
|
41
|
+
specifically for a cross-shadow-root anchor reference, in Firefox only — the same anchor()
|
|
42
|
+
calls resolve correctly for a same-tree anchor in Firefox, and cross-shadow anchoring resolves
|
|
43
|
+
correctly in Chromium and Safari. A badge anchored to a plain element (button, input, anything
|
|
44
|
+
that isn't itself relying on this escape hatch) is unaffected in every engine, including
|
|
45
|
+
Firefox.
|
|
46
|
+
|
|
47
|
+
## Side effects
|
|
48
|
+
|
|
49
|
+
The badge is a sibling of its anchor rather than a child, so **as a side effect, a badge
|
|
50
|
+
mutates its anchor element**: it appends its own id to the anchor's `aria-describedby`
|
|
51
|
+
(preserving any ids already there, the same way `anchor-name` is appended rather than
|
|
52
|
+
replaced) to keep its content connected to the anchor for assistive technology. This means a
|
|
53
|
+
badge's content, or its `aria-label` when set, is announced as part of interacting with the
|
|
54
|
+
anchor, e.g. "Inbox button, 3". A `dot` badge has no content of its own, so set `aria-label`
|
|
55
|
+
directly on `<use-badge>` for a meaningful description (e.g. `aria-label="3 unread messages"`);
|
|
56
|
+
without one, the description contributes nothing and the anchor announces as before.
|
|
57
|
+
|
|
58
|
+
Avoid adding `aria-hidden` to `<use-badge>` if you rely on this: while the description
|
|
59
|
+
computation is specified to still read a hidden node when it's the direct target of
|
|
60
|
+
`aria-describedby`, support for that specific case is inconsistent across browsers and screen
|
|
61
|
+
readers.
|
|
62
|
+
|
|
63
|
+
Set `options="noaria"` to opt a badge out of this and leave the anchor's `aria-describedby`
|
|
64
|
+
untouched, e.g. when the anchor already documents the badge's meaning some other way.
|
|
65
|
+
|
|
66
|
+
## Attributes
|
|
67
|
+
|
|
68
|
+
| Name | Type | Default | Description |
|
|
69
|
+
| --- | --- | --- | --- |
|
|
70
|
+
| anchortarget | `string \| undefined` | - | Id of the element to anchor to, resolved in the badge's own tree. |
|
|
71
|
+
| dot | `boolean` | `false` | Renders as a fixed-size dot instead of the slotted content. |
|
|
72
|
+
| blockalign | `"start" \| "end"` | `"start"` | `"start" \| "end"`: Aligns the badge along the block axis relative to its anchor. |
|
|
73
|
+
| inlinealign | `"start" \| "end"` | `"end"` | `"start" \| "end"`: Aligns the badge along the inline axis relative to its anchor. |
|
|
74
|
+
| options | `string` | `""` | Space-separated feature flags. `noaria` opts out of the automatic `aria-describedby` wiring described above. |
|
|
75
|
+
|
|
76
|
+
## Properties
|
|
77
|
+
|
|
78
|
+
| Name | Type | Default | Description |
|
|
79
|
+
| --- | --- | --- | --- |
|
|
80
|
+
| anchorElement | - | - | The element this badge is anchored to. Reading returns whatever element was last resolved,
|
|
81
|
+
either from the `anchortarget` id or set here directly. Setting it anchors to that element
|
|
82
|
+
instead of resolving `anchortarget`, for cases where the target has no id to reference. |
|
|
83
|
+
|
|
84
|
+
## Slots
|
|
85
|
+
|
|
86
|
+
| Name | Description |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `` | Badge content, e.g. a count or short label. Ignored when `dot` is set. |
|
package/dist/tokens.css
CHANGED
|
@@ -118,6 +118,7 @@
|
|
|
118
118
|
--usewc-effect-border-radius-6: 0.375rem;
|
|
119
119
|
--usewc-effect-border-radius-8: 0.5rem;
|
|
120
120
|
--usewc-effect-border-radius-full: 100%;
|
|
121
|
+
--usewc-effect-border-radius-pill: 9999px;
|
|
121
122
|
|
|
122
123
|
/** Motion */
|
|
123
124
|
--usewc-transition-duration-fast: 120ms;
|
|
@@ -133,6 +134,8 @@
|
|
|
133
134
|
:root {
|
|
134
135
|
/* Brand */
|
|
135
136
|
--usewc-color-brand-primary: var(--usewc-color-blue-600);
|
|
137
|
+
/* A brighter oklch lift off #f76a15, still in-gamut */
|
|
138
|
+
--usewc-color-brand-accent: oklch(0.73 0.165 44.7);
|
|
136
139
|
|
|
137
140
|
/* Document */
|
|
138
141
|
/* - Color */
|
|
@@ -489,6 +492,21 @@
|
|
|
489
492
|
--usewc-font-button-extra-large-size: var(--usewc-font-size-20);
|
|
490
493
|
--usewc-layout-button-extra-large-line-height: var(--usewc-line-height-32);
|
|
491
494
|
|
|
495
|
+
/* Badge */
|
|
496
|
+
--usewc-color-badge-background-static: var(--usewc-color-brand-accent);
|
|
497
|
+
/* Scaled rather than a fixed white/black: the accent is light enough (2.99:1 with white text,
|
|
498
|
+
vs. 7.03:1 with black) that a static color would misjudge it, and would silently stop
|
|
499
|
+
matching if the accent itself is ever overridden. */
|
|
500
|
+
--usewc-color-badge-text-static: contrast-color(var(--usewc-color-badge-background-static));
|
|
501
|
+
--usewc-color-badge-border: var(--usewc-color-document-background);
|
|
502
|
+
--usewc-layout-badge-padding-inline: var(--usewc-space-6);
|
|
503
|
+
--usewc-layout-badge-min-size: var(--usewc-space-16);
|
|
504
|
+
--usewc-layout-badge-dot-size: var(--usewc-space-12);
|
|
505
|
+
--usewc-layout-badge-border: var(--usewc-space-1);
|
|
506
|
+
--usewc-font-badge-size: var(--usewc-font-size-12);
|
|
507
|
+
--usewc-font-badge-weight: var(--usewc-font-weight-medium);
|
|
508
|
+
--usewc-effect-badge-border-radius: var(--usewc-effect-border-radius-pill);
|
|
509
|
+
|
|
492
510
|
/* Menu */
|
|
493
511
|
--usewc-layout-popover-menu-item-padding-inline: var(--usewc-space-4);
|
|
494
512
|
--usewc-layout-popover-menu-item-padding-block: var(--usewc-space-2);
|
package/dist/use-wc.d.mts
CHANGED
|
@@ -1229,4 +1229,106 @@ declare global {
|
|
|
1229
1229
|
}
|
|
1230
1230
|
}
|
|
1231
1231
|
//#endregion
|
|
1232
|
-
|
|
1232
|
+
//#region src/elements/use-badge/use-badge.d.ts
|
|
1233
|
+
/**
|
|
1234
|
+
* `use-badge` renders a small indicator anchored to another element's corner, using
|
|
1235
|
+
* [CSS anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)
|
|
1236
|
+
* instead of the classic `position: absolute` + negative-offset trick. This lets the badge live
|
|
1237
|
+
* anywhere in the DOM rather than being a child of the element it decorates. A badge's shape,
|
|
1238
|
+
* color, and positioning ship built in — reading from the design system's tokens when
|
|
1239
|
+
* `tokens.css` is loaded, and falling back to sensible defaults otherwise — so no theme is
|
|
1240
|
+
* required to get a working, visible badge.
|
|
1241
|
+
*
|
|
1242
|
+
* Point a badge at its target with the `anchortarget` attribute, set to the target's `id`
|
|
1243
|
+
* (named to avoid colliding with the `anchor` content attribute HTML is gaining natively):
|
|
1244
|
+
*
|
|
1245
|
+
* ```html
|
|
1246
|
+
* <button type="button" id="inbox">Inbox</button>
|
|
1247
|
+
* <use-badge anchortarget="inbox">99+</use-badge>
|
|
1248
|
+
* ```
|
|
1249
|
+
*
|
|
1250
|
+
* The badge and its anchor must live in the same DOM tree — a badge cannot reach an anchor
|
|
1251
|
+
* inside another component's shadow root, and an anchor's `anchor-name` set inside a shadow
|
|
1252
|
+
* tree's stylesheet is not visible here. No positioned ancestor or shared containing block is
|
|
1253
|
+
* required: anchor positioning resolves the badge's location straight from the anchor's own
|
|
1254
|
+
* geometry, so the badge can sit anywhere else in the DOM tree.
|
|
1255
|
+
*
|
|
1256
|
+
* Without a resolved anchor — `anchortarget` unset, pointing at a missing id, or `anchorElement`
|
|
1257
|
+
* never assigned — the badge has no `:state(anchored)` and renders as a plain inline chip in
|
|
1258
|
+
* normal document flow, still using the same color/shape styling.
|
|
1259
|
+
*
|
|
1260
|
+
* Some components render their host as `display: contents`, which has no box of its own for CSS
|
|
1261
|
+
* anchor positioning to resolve against. If the anchor computes a non-empty `--usewc-anchor-name`
|
|
1262
|
+
* custom property, the badge uses that dashed-ident as its `position-anchor` directly instead of
|
|
1263
|
+
* generating and writing its own onto the anchor — letting a component publish the name of a
|
|
1264
|
+
* real, box-generating part of itself (already established for its own purposes) for badges to
|
|
1265
|
+
* target instead of its host, even though that part lives inside the component's own shadow root.
|
|
1266
|
+
* This mechanism is shared with `use-menu`/`use-anchored` via `AnchorController`; components
|
|
1267
|
+
* without a `display: contents` host don't need to publish anything.
|
|
1268
|
+
*
|
|
1269
|
+
* **Known Firefox limitation:** anchoring across a shadow root boundary this way — the case
|
|
1270
|
+
* `--usewc-anchor-name` exists for — is mispositioned in Firefox as of this writing. Verified
|
|
1271
|
+
* directly: `anchor()`'s logical `start`/`end` keywords resolve backwards on both axes
|
|
1272
|
+
* specifically for a cross-shadow-root anchor reference, in Firefox only — the same anchor()
|
|
1273
|
+
* calls resolve correctly for a same-tree anchor in Firefox, and cross-shadow anchoring resolves
|
|
1274
|
+
* correctly in Chromium and Safari. A badge anchored to a plain element (button, input, anything
|
|
1275
|
+
* that isn't itself relying on this escape hatch) is unaffected in every engine, including
|
|
1276
|
+
* Firefox.
|
|
1277
|
+
*
|
|
1278
|
+
* ## Side effects
|
|
1279
|
+
*
|
|
1280
|
+
* The badge is a sibling of its anchor rather than a child, so **as a side effect, a badge
|
|
1281
|
+
* mutates its anchor element**: it appends its own id to the anchor's `aria-describedby`
|
|
1282
|
+
* (preserving any ids already there, the same way `anchor-name` is appended rather than
|
|
1283
|
+
* replaced) to keep its content connected to the anchor for assistive technology. This means a
|
|
1284
|
+
* badge's content, or its `aria-label` when set, is announced as part of interacting with the
|
|
1285
|
+
* anchor, e.g. "Inbox button, 3". A `dot` badge has no content of its own, so set `aria-label`
|
|
1286
|
+
* directly on `<use-badge>` for a meaningful description (e.g. `aria-label="3 unread messages"`);
|
|
1287
|
+
* without one, the description contributes nothing and the anchor announces as before.
|
|
1288
|
+
*
|
|
1289
|
+
* Avoid adding `aria-hidden` to `<use-badge>` if you rely on this: while the description
|
|
1290
|
+
* computation is specified to still read a hidden node when it's the direct target of
|
|
1291
|
+
* `aria-describedby`, support for that specific case is inconsistent across browsers and screen
|
|
1292
|
+
* readers.
|
|
1293
|
+
*
|
|
1294
|
+
* Set `options="noaria"` to opt a badge out of this and leave the anchor's `aria-describedby`
|
|
1295
|
+
* untouched, e.g. when the anchor already documents the badge's meaning some other way.
|
|
1296
|
+
*
|
|
1297
|
+
* @slot - Badge content, e.g. a count or short label. Ignored when `dot` is set.
|
|
1298
|
+
* @state anchored `use-badge:state(anchored)`: Set once the badge has a resolved anchor element;
|
|
1299
|
+
* this is what switches the badge from normal-flow to anchor-positioned.
|
|
1300
|
+
* @attr anchortarget - Id of the element to anchor to, resolved in the badge's own tree.
|
|
1301
|
+
* @attr dot - Renders as a fixed-size dot instead of the slotted content.
|
|
1302
|
+
* @attr blockalign `"start" | "end"`: Aligns the badge along the block axis relative to its anchor.
|
|
1303
|
+
* @attr inlinealign `"start" | "end"`: Aligns the badge along the inline axis relative to its anchor.
|
|
1304
|
+
* @attr options - Space-separated feature flags. `noaria` opts out of the automatic
|
|
1305
|
+
* `aria-describedby` wiring described above.
|
|
1306
|
+
*/
|
|
1307
|
+
declare class UseBadge extends LitElement {
|
|
1308
|
+
#private;
|
|
1309
|
+
anchortarget?: string;
|
|
1310
|
+
dot: boolean;
|
|
1311
|
+
blockalign: "start" | "end";
|
|
1312
|
+
inlinealign: "start" | "end";
|
|
1313
|
+
options: string;
|
|
1314
|
+
constructor();
|
|
1315
|
+
/**
|
|
1316
|
+
* The element this badge is anchored to. Reading returns whatever element was last resolved,
|
|
1317
|
+
* either from the `anchortarget` id or set here directly. Setting it anchors to that element
|
|
1318
|
+
* instead of resolving `anchortarget`, for cases where the target has no id to reference.
|
|
1319
|
+
*/
|
|
1320
|
+
get anchorElement(): Element | null;
|
|
1321
|
+
set anchorElement(element: Element | null);
|
|
1322
|
+
connectedCallback(): void;
|
|
1323
|
+
disconnectedCallback(): void;
|
|
1324
|
+
updated(changed: PropertyValues<this>): void;
|
|
1325
|
+
render(): TemplateResult$1<1> | null;
|
|
1326
|
+
static styles: import("lit").CSSResult;
|
|
1327
|
+
}
|
|
1328
|
+
declare global {
|
|
1329
|
+
interface HTMLElementTagNameMap {
|
|
1330
|
+
"use-badge": UseBadge;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
//#endregion
|
|
1334
|
+
export { UseAnchored, UseBadge, UseCalendarBase, UseCalendarRenderDay, UseCaret, UseDateInput, UseDatePicker, UseDatetimeInput, UseDurationInput, UseFocusgroup, UseIntlDatetime, UseListboxInput, UseMenu, UseMonthPicker, UseMonthPickerRenderMonth, UseOption, UseScrollarea, UseTimeInput, UseTree, UseTreeitem, UseWeekPicker, UseWidget };
|
package/dist/use-wc.mjs
CHANGED
|
@@ -2499,7 +2499,7 @@ let UseMenu = class UseMenu extends LitElement {
|
|
|
2499
2499
|
:host(:popover-open) [part="menu"] {
|
|
2500
2500
|
display: flex;
|
|
2501
2501
|
flex-direction: column;
|
|
2502
|
-
|
|
2502
|
+
align-items: stretch;
|
|
2503
2503
|
}
|
|
2504
2504
|
|
|
2505
2505
|
::slotted(hr) {
|
|
@@ -4230,4 +4230,160 @@ __decorate([property({ type: String })], UseIntlDatetime.prototype, "datestyle",
|
|
|
4230
4230
|
__decorate([property({ type: String })], UseIntlDatetime.prototype, "timestyle", void 0);
|
|
4231
4231
|
UseIntlDatetime = __decorate([customElement("use-intl-datetime")], UseIntlDatetime);
|
|
4232
4232
|
//#endregion
|
|
4233
|
-
|
|
4233
|
+
//#region src/elements/use-badge/use-badge.ts
|
|
4234
|
+
let UseBadge = class UseBadge extends LitElement {
|
|
4235
|
+
#internals;
|
|
4236
|
+
#anchorController;
|
|
4237
|
+
get #noAria() {
|
|
4238
|
+
return this.options.split(/\s+/).includes("noaria");
|
|
4239
|
+
}
|
|
4240
|
+
constructor() {
|
|
4241
|
+
super();
|
|
4242
|
+
this.dot = false;
|
|
4243
|
+
this.blockalign = "start";
|
|
4244
|
+
this.inlinealign = "end";
|
|
4245
|
+
this.options = "";
|
|
4246
|
+
this.#internals = this.attachInternals();
|
|
4247
|
+
this.#anchorController = new AnchorController(this, this.#internals, "--usewc-badge");
|
|
4248
|
+
if (!this.id) this.id = createId();
|
|
4249
|
+
}
|
|
4250
|
+
/**
|
|
4251
|
+
* The element this badge is anchored to. Reading returns whatever element was last resolved,
|
|
4252
|
+
* either from the `anchortarget` id or set here directly. Setting it anchors to that element
|
|
4253
|
+
* instead of resolving `anchortarget`, for cases where the target has no id to reference.
|
|
4254
|
+
*/
|
|
4255
|
+
get anchorElement() {
|
|
4256
|
+
return this.#anchorController.anchorElement;
|
|
4257
|
+
}
|
|
4258
|
+
set anchorElement(element) {
|
|
4259
|
+
const previousAnchor = this.#anchorController.anchorElement;
|
|
4260
|
+
this.#detachDescribedBy(previousAnchor);
|
|
4261
|
+
this.#anchorController.anchorElement = element;
|
|
4262
|
+
this.#attachDescribedBy(this.#anchorController.anchorElement);
|
|
4263
|
+
}
|
|
4264
|
+
connectedCallback() {
|
|
4265
|
+
super.connectedCallback();
|
|
4266
|
+
this.#resolveAnchor();
|
|
4267
|
+
}
|
|
4268
|
+
disconnectedCallback() {
|
|
4269
|
+
this.#detachDescribedBy(this.#anchorController.anchorElement);
|
|
4270
|
+
super.disconnectedCallback();
|
|
4271
|
+
}
|
|
4272
|
+
updated(changed) {
|
|
4273
|
+
if (changed.has("anchortarget")) this.#resolveAnchor();
|
|
4274
|
+
else if (changed.has("options")) if (this.#noAria) this.#detachDescribedBy(this.#anchorController.anchorElement);
|
|
4275
|
+
else this.#attachDescribedBy(this.#anchorController.anchorElement);
|
|
4276
|
+
}
|
|
4277
|
+
#resolveAnchor() {
|
|
4278
|
+
const previousAnchor = this.#anchorController.anchorElement;
|
|
4279
|
+
this.#detachDescribedBy(previousAnchor);
|
|
4280
|
+
this.#anchorController.resolve(this.anchortarget);
|
|
4281
|
+
this.#attachDescribedBy(this.#anchorController.anchorElement);
|
|
4282
|
+
}
|
|
4283
|
+
#attachDescribedBy(anchorElement) {
|
|
4284
|
+
if (!(anchorElement instanceof HTMLElement) || this.#noAria) return;
|
|
4285
|
+
const describedByIds = (anchorElement.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean);
|
|
4286
|
+
if (!describedByIds.includes(this.id)) {
|
|
4287
|
+
describedByIds.push(this.id);
|
|
4288
|
+
anchorElement.setAttribute("aria-describedby", describedByIds.join(" "));
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
#detachDescribedBy(anchorElement) {
|
|
4292
|
+
if (!(anchorElement instanceof HTMLElement)) return;
|
|
4293
|
+
const describedByIds = (anchorElement.getAttribute("aria-describedby") ?? "").split(/\s+/).filter((id) => id && id !== this.id);
|
|
4294
|
+
if (describedByIds.length > 0) anchorElement.setAttribute("aria-describedby", describedByIds.join(" "));
|
|
4295
|
+
else anchorElement.removeAttribute("aria-describedby");
|
|
4296
|
+
}
|
|
4297
|
+
render() {
|
|
4298
|
+
return this.dot ? null : html`<slot></slot>`;
|
|
4299
|
+
}
|
|
4300
|
+
static {
|
|
4301
|
+
this.styles = css`
|
|
4302
|
+
:host {
|
|
4303
|
+
--usewc-badge-offset: calc(0.5rem / 3);
|
|
4304
|
+
display: inline-flex;
|
|
4305
|
+
align-items: center;
|
|
4306
|
+
justify-content: center;
|
|
4307
|
+
box-sizing: border-box;
|
|
4308
|
+
min-inline-size: 1rem;
|
|
4309
|
+
min-block-size: 1rem;
|
|
4310
|
+
padding-inline: 0.375rem;
|
|
4311
|
+
border-radius: 9999px;
|
|
4312
|
+
background: CanvasText;
|
|
4313
|
+
border: 1px solid currentColor;
|
|
4314
|
+
color: Canvas;
|
|
4315
|
+
font-size: 0.75rem;
|
|
4316
|
+
font-weight: 500;
|
|
4317
|
+
line-height: 1;
|
|
4318
|
+
white-space: nowrap;
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4321
|
+
:host(:not([dot])) {
|
|
4322
|
+
--usewc-badge-offset: 0.5lh;
|
|
4323
|
+
}
|
|
4324
|
+
|
|
4325
|
+
:host(:state(anchored)) {
|
|
4326
|
+
position: absolute;
|
|
4327
|
+
--usewc-badge-translate-x: 1;
|
|
4328
|
+
--usewc-badge-translate-y: -1;
|
|
4329
|
+
translate: calc(var(--usewc-badge-translate-x) * var(--usewc-badge-offset))
|
|
4330
|
+
calc(var(--usewc-badge-translate-y) * var(--usewc-badge-offset));
|
|
4331
|
+
inset-inline-start: unset;
|
|
4332
|
+
inset-inline-end: anchor(end);
|
|
4333
|
+
inset-block-end: unset;
|
|
4334
|
+
inset-block-start: anchor(start);
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4337
|
+
:host(:state(anchored)[inlinealign="start"]) {
|
|
4338
|
+
inset-inline-end: unset;
|
|
4339
|
+
inset-inline-start: anchor(start);
|
|
4340
|
+
--usewc-badge-translate-x: -1;
|
|
4341
|
+
}
|
|
4342
|
+
|
|
4343
|
+
:host(:state(anchored)[blockalign="end"]) {
|
|
4344
|
+
inset-block-start: unset;
|
|
4345
|
+
inset-block-end: anchor(end);
|
|
4346
|
+
--usewc-badge-translate-y: 1;
|
|
4347
|
+
}
|
|
4348
|
+
|
|
4349
|
+
:host(:dir(rtl):state(anchored)) {
|
|
4350
|
+
--usewc-badge-translate-x: -1;
|
|
4351
|
+
}
|
|
4352
|
+
|
|
4353
|
+
:host(:dir(rtl):state(anchored)[inlinealign="start"]) {
|
|
4354
|
+
--usewc-badge-translate-x: 1;
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
:host([dot]) {
|
|
4358
|
+
inline-size: 0.5rem;
|
|
4359
|
+
block-size: 0.5rem;
|
|
4360
|
+
min-inline-size: 0;
|
|
4361
|
+
min-block-size: 0;
|
|
4362
|
+
padding-inline: 0;
|
|
4363
|
+
}
|
|
4364
|
+
`;
|
|
4365
|
+
}
|
|
4366
|
+
};
|
|
4367
|
+
__decorate([property({
|
|
4368
|
+
type: String,
|
|
4369
|
+
reflect: true
|
|
4370
|
+
})], UseBadge.prototype, "anchortarget", void 0);
|
|
4371
|
+
__decorate([property({
|
|
4372
|
+
type: Boolean,
|
|
4373
|
+
reflect: true
|
|
4374
|
+
})], UseBadge.prototype, "dot", void 0);
|
|
4375
|
+
__decorate([property({
|
|
4376
|
+
type: String,
|
|
4377
|
+
reflect: true
|
|
4378
|
+
})], UseBadge.prototype, "blockalign", void 0);
|
|
4379
|
+
__decorate([property({
|
|
4380
|
+
type: String,
|
|
4381
|
+
reflect: true
|
|
4382
|
+
})], UseBadge.prototype, "inlinealign", void 0);
|
|
4383
|
+
__decorate([property({
|
|
4384
|
+
type: String,
|
|
4385
|
+
reflect: true
|
|
4386
|
+
})], UseBadge.prototype, "options", void 0);
|
|
4387
|
+
UseBadge = __decorate([customElement("use-badge")], UseBadge);
|
|
4388
|
+
//#endregion
|
|
4389
|
+
export { UseAnchored, UseBadge, UseCalendarBase, UseCaret, UseDateInput, UseDatePicker, UseDatetimeInput, UseDurationInput, UseFocusgroup, UseIntlDatetime, UseListboxInput, UseMenu, UseMonthPicker, UseOption, UseScrollarea, UseTimeInput, UseTree, UseTreeitem, UseWeekPicker, UseWidget };
|
package/package.json
CHANGED
|
@@ -19,6 +19,38 @@
|
|
|
19
19
|
],
|
|
20
20
|
"references": []
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
"name": "use-badge",
|
|
24
|
+
"description": "`use-badge` renders a small indicator anchored to another element's corner, using\n[CSS anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)\ninstead of the classic `position: absolute` + negative-offset trick. This lets the badge live\nanywhere in the DOM rather than being a child of the element it decorates. A badge's shape,\ncolor, and positioning ship built in — reading from the design system's tokens when\n`tokens.css` is loaded, and falling back to sensible defaults otherwise — so no theme is\nrequired to get a working, visible badge.\n\nPoint a badge at its target with the `anchortarget` attribute, set to the target's `id`\n(named to avoid colliding with the `anchor` content attribute HTML is gaining natively):\n\n```html\n<button type=\"button\" id=\"inbox\">Inbox</button>\n<use-badge anchortarget=\"inbox\">99+</use-badge>\n```\n\nThe badge and its anchor must live in the same DOM tree — a badge cannot reach an anchor\ninside another component's shadow root, and an anchor's `anchor-name` set inside a shadow\ntree's stylesheet is not visible here. No positioned ancestor or shared containing block is\nrequired: anchor positioning resolves the badge's location straight from the anchor's own\ngeometry, so the badge can sit anywhere else in the DOM tree.\n\nWithout a resolved anchor — `anchortarget` unset, pointing at a missing id, or `anchorElement`\nnever assigned — the badge has no `:state(anchored)` and renders as a plain inline chip in\nnormal document flow, still using the same color/shape styling.\n\nSome components render their host as `display: contents`, which has no box of its own for CSS\nanchor positioning to resolve against. If the anchor computes a non-empty `--usewc-anchor-name`\ncustom property, the badge uses that dashed-ident as its `position-anchor` directly instead of\ngenerating and writing its own onto the anchor — letting a component publish the name of a\nreal, box-generating part of itself (already established for its own purposes) for badges to\ntarget instead of its host, even though that part lives inside the component's own shadow root.\nThis mechanism is shared with `use-menu`/`use-anchored` via `AnchorController`; components\nwithout a `display: contents` host don't need to publish anything.\n\n**Known Firefox limitation:** anchoring across a shadow root boundary this way — the case\n`--usewc-anchor-name` exists for — is mispositioned in Firefox as of this writing. Verified\ndirectly: `anchor()`'s logical `start`/`end` keywords resolve backwards on both axes\nspecifically for a cross-shadow-root anchor reference, in Firefox only — the same anchor()\ncalls resolve correctly for a same-tree anchor in Firefox, and cross-shadow anchoring resolves\ncorrectly in Chromium and Safari. A badge anchored to a plain element (button, input, anything\nthat isn't itself relying on this escape hatch) is unaffected in every engine, including\nFirefox.\n\n## Side effects\n\nThe badge is a sibling of its anchor rather than a child, so **as a side effect, a badge\nmutates its anchor element**: it appends its own id to the anchor's `aria-describedby`\n(preserving any ids already there, the same way `anchor-name` is appended rather than\nreplaced) to keep its content connected to the anchor for assistive technology. This means a\nbadge's content, or its `aria-label` when set, is announced as part of interacting with the\nanchor, e.g. \"Inbox button, 3\". A `dot` badge has no content of its own, so set `aria-label`\ndirectly on `<use-badge>` for a meaningful description (e.g. `aria-label=\"3 unread messages\"`);\nwithout one, the description contributes nothing and the anchor announces as before.\n\nAvoid adding `aria-hidden` to `<use-badge>` if you rely on this: while the description\ncomputation is specified to still read a hidden node when it's the direct target of\n`aria-describedby`, support for that specific case is inconsistent across browsers and screen\nreaders.\n\nSet `options=\"noaria\"` to opt a badge out of this and leave the anchor's `aria-describedby`\nuntouched, e.g. when the anchor already documents the badge's meaning some other way.\n---\n\n\n### **Slots:**\n - _default_ - Badge content, e.g. a count or short label. Ignored when `dot` is set.",
|
|
25
|
+
"attributes": [
|
|
26
|
+
{
|
|
27
|
+
"name": "anchortarget",
|
|
28
|
+
"description": "Id of the element to anchor to, resolved in the badge's own tree.",
|
|
29
|
+
"values": []
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "dot",
|
|
33
|
+
"description": "Renders as a fixed-size dot instead of the slotted content.",
|
|
34
|
+
"values": []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "blockalign",
|
|
38
|
+
"description": "`\"start\" | \"end\"`: Aligns the badge along the block axis relative to its anchor.",
|
|
39
|
+
"values": [{ "name": "start" }, { "name": "end" }]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "inlinealign",
|
|
43
|
+
"description": "`\"start\" | \"end\"`: Aligns the badge along the inline axis relative to its anchor.",
|
|
44
|
+
"values": [{ "name": "start" }, { "name": "end" }]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "options",
|
|
48
|
+
"description": "Space-separated feature flags. `noaria` opts out of the automatic `aria-describedby` wiring described above.",
|
|
49
|
+
"values": []
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"references": []
|
|
53
|
+
},
|
|
22
54
|
{
|
|
23
55
|
"name": "use-calendarday",
|
|
24
56
|
"description": "A helper component for customizing specific days within `use-calendar`.\nContent placed inside this element will replace the default day number display.\n---\n\n\n### **Slots:**\n - **default** - slot for custom day content (replaces the day number)",
|