@slotted/styles 0.1.0-beta.1 → 0.1.0-beta.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slotted/styles",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.3",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -460,7 +460,18 @@
460
460
  * for fifty pixels stood at whatever the longest destination measured.
461
461
  */
462
462
  min-inline-size: 0;
463
- overflow: hidden;
463
+ /**
464
+ * The inline axis clips, which the width transition between open and rail
465
+ * needs. The block axis scrolls: a region holding one column taller than
466
+ * itself instead of `nav-header`, `nav-body` and `nav-footer` keeps its
467
+ * foot reachable. With the three parts the body scrolls and the region
468
+ * never overflows.
469
+ */
470
+ overflow-x: hidden;
471
+ overflow-y: auto;
472
+ overflow-inline: hidden;
473
+ overflow-block: auto;
474
+ overscroll-behavior: contain;
464
475
  transition:
465
476
  inline-size var(--_duration) var(--_easing),
466
477
  visibility var(--_duration) step-start;
@@ -787,6 +798,20 @@
787
798
  * content card begins, and nothing to its left.
788
799
  */
789
800
  [data-ui='app-shell'][data-canvas='inset'] > [data-part='nav'] {
801
+ /* The navigation stands on the sunken frame, which the secondary tone's
802
+ hover is 0.011 lighter than in the light scheme: a row's hover was the
803
+ frame itself. Both steps are recomputed from the ground it stands on. */
804
+ --slotted-tone-secondary-subtle-hover: color-mix(
805
+ in oklab,
806
+ var(--slotted-app-shell-nav-surface, var(--_frame)),
807
+ var(--slotted-text) 4%
808
+ );
809
+ --slotted-tone-secondary-subtle-active: color-mix(
810
+ in oklab,
811
+ var(--slotted-app-shell-nav-surface, var(--_frame)),
812
+ var(--slotted-text) 7%
813
+ );
814
+
790
815
  /* Invisible on the transparent default; a product that paints the column
791
816
  gets a region with the plane's corners rather than a square block. */
792
817
  border-radius: var(--_radius);
@@ -158,6 +158,19 @@
158
158
  --_edge: var(--_solid-active);
159
159
  }
160
160
 
161
+ /**
162
+ * Ink: the theme's `neutral` tone fills with the inverse surface and labels
163
+ * with the inverse text. A product that keeps the accent for data draws its
164
+ * primary action in it.
165
+ */
166
+ [data-ui='button'][data-tone='neutral'] {
167
+ --_solid: var(--slotted-tone-neutral-solid);
168
+ --_solid-hover: var(--slotted-tone-neutral-solid-hover);
169
+ --_solid-active: var(--slotted-tone-neutral-solid-active);
170
+ --_on-solid: var(--slotted-tone-neutral-on-solid);
171
+ --_text: var(--slotted-tone-neutral-text);
172
+ }
173
+
161
174
  [data-ui='button'][data-tone='success'] {
162
175
  --_solid: var(--slotted-tone-success-solid);
163
176
  --_solid-hover: var(--slotted-tone-success-solid-hover);
@@ -681,6 +694,26 @@
681
694
  inline-size: 100%;
682
695
  }
683
696
 
697
+ /**
698
+ * A field trigger: the label at the start and `end` at the inline end. The
699
+ * label takes the free space, so `start` stays beside it and the end slot is
700
+ * pushed to the edge; flex follows the writing direction, so it mirrors.
701
+ */
702
+ [data-ui='button'][data-justify='between'] {
703
+ justify-content: space-between;
704
+ }
705
+
706
+ [data-ui='button'][data-justify='between'] > [data-part='label'] {
707
+ flex: 1 1 auto;
708
+ text-align: start;
709
+ }
710
+
711
+ /* What a trigger holds at its end — a shortcut, a count — is as wide as it is. */
712
+ [data-ui='button'][data-justify='between'] > [data-part='end'] {
713
+ inline-size: auto;
714
+ min-inline-size: var(--_icon-size);
715
+ }
716
+
684
717
  /**
685
718
  * An icon-only control is square from the control height, so its visual box
686
719
  * and its target are the same box. The two minimums are the WCAG 2.2 floor
@@ -52,6 +52,11 @@
52
52
  "--slotted-tone-danger-solid-active",
53
53
  "--slotted-tone-danger-solid-hover",
54
54
  "--slotted-tone-danger-text",
55
+ "--slotted-tone-neutral-on-solid",
56
+ "--slotted-tone-neutral-solid",
57
+ "--slotted-tone-neutral-solid-active",
58
+ "--slotted-tone-neutral-solid-hover",
59
+ "--slotted-tone-neutral-text",
55
60
  "--slotted-tone-secondary-on-solid",
56
61
  "--slotted-tone-secondary-solid",
57
62
  "--slotted-tone-secondary-solid-active",
@@ -341,6 +341,10 @@
341
341
  --_seam: var(--slotted-tone-secondary-solid-active);
342
342
  }
343
343
 
344
+ [data-ui='button-group'] > [data-tone='neutral'] {
345
+ --_seam: var(--slotted-tone-neutral-solid-active);
346
+ }
347
+
344
348
  [data-ui='button-group'] > [data-tone='success'] {
345
349
  --_seam: var(--slotted-tone-success-solid-active);
346
350
  }
@@ -5,6 +5,7 @@
5
5
  "--slotted-text-muted",
6
6
  "--slotted-tone-accent-solid-active",
7
7
  "--slotted-tone-danger-solid-active",
8
+ "--slotted-tone-neutral-solid-active",
8
9
  "--slotted-tone-secondary-solid-active",
9
10
  "--slotted-tone-success-solid-active",
10
11
  "--slotted-tone-warning-solid-active"
@@ -877,6 +877,13 @@
877
877
  overflow-wrap: anywhere;
878
878
  }
879
879
 
880
+ /* The label of a line on this category, the plot's own words for it. */
881
+ [data-ui='chart'] [data-part='tooltip-note'] {
882
+ color: var(--slotted-text-muted, GrayText);
883
+ grid-column: 1 / -1;
884
+ overflow-wrap: anywhere;
885
+ }
886
+
880
887
  [data-ui='chart'] :is([data-part='tooltip-item'], [data-part='tooltip-total']) {
881
888
  align-items: center;
882
889
  display: grid;
@@ -746,6 +746,18 @@
746
746
 
747
747
  /* ── Options ──────────────────────────────────────────────────────────── */
748
748
 
749
+ /**
750
+ * An option the query rejected, and a group whose options all went. Both
751
+ * carry `hidden` — Angular hides each rejected option rather than leaving the
752
+ * list, and both bindings hide an emptied group — and `display: grid` on
753
+ * either beats the user agent's own `[hidden] { display: none }`, because an
754
+ * author rule wins over the user agent sheet.
755
+ */
756
+ [data-ui='combobox'] [data-part='item'][hidden],
757
+ [data-ui='combobox'] [data-part='group'][hidden] {
758
+ display: none;
759
+ }
760
+
749
761
  [data-ui='combobox'] [data-part='item'] {
750
762
  align-content: center;
751
763
  border-radius: var(--_item-radius);
@@ -784,4 +784,128 @@
784
784
  [data-ui='currency-field'][data-part='root'][data-disabled] > [data-part='end'] {
785
785
  color: var(--slotted-disabled-foreground);
786
786
  }
787
+
788
+ /**
789
+ * ── The display step ───────────────────────────────────────────────────
790
+ *
791
+ * The headline amount of an entry sheet: the figure at the 4xl step in
792
+ * tabular figures with the tighter tracking large type takes, and the
793
+ * currency's mark apart from it, muted at the xl step and on the baseline
794
+ * of the digits. Where the platform sizes a field to its content the
795
+ * control is as wide as the amount it holds, so the mark stays beside the
796
+ * digits; elsewhere the control takes the line and `align` places the
797
+ * amount in it.
798
+ */
799
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'] {
800
+ --_font-size: var(
801
+ --slotted-currency-field-display-font-size,
802
+ var(--slotted-font-size-4xl, 2rem)
803
+ );
804
+ --_height: calc(
805
+ var(--_font-size) * var(--slotted-line-height-4xl, 1.2) + 2 * var(--slotted-space-2, 0.25rem)
806
+ );
807
+ --_padding-inline: var(--slotted-control-padding-inline-lg, 16px);
808
+ --_gap: var(--slotted-space-3, 0.375rem);
809
+
810
+ align-items: baseline;
811
+ font-weight: var(--slotted-font-weight-medium, 500);
812
+ letter-spacing: var(--slotted-letter-spacing-tighter, -0.03em);
813
+ line-height: var(--slotted-line-height-4xl, 1.2);
814
+ }
815
+
816
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'] > [data-part='symbol'] {
817
+ color: var(--slotted-currency-field-symbol-color, var(--slotted-text-muted, GrayText));
818
+ flex: none;
819
+ font-size: var(
820
+ --slotted-currency-field-symbol-font-size,
821
+ var(--slotted-font-size-xl, 1.125rem)
822
+ );
823
+ font-weight: var(--slotted-font-weight-regular, 400);
824
+ letter-spacing: 0;
825
+ }
826
+
827
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'] > [data-part='input'] {
828
+ align-self: auto;
829
+ margin-block: 0;
830
+ }
831
+
832
+ @supports (field-sizing: content) {
833
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'] {
834
+ inline-size: fit-content;
835
+ max-inline-size: 100%;
836
+ }
837
+
838
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'] > [data-part='input'] {
839
+ field-sizing: content;
840
+ flex: 0 1 auto;
841
+ min-inline-size: 1ch;
842
+ }
843
+ }
844
+
845
+ /**
846
+ * No box: a rule under the amount. On focus the rule takes the text colour
847
+ * and doubles, which is the focus indicator; invalid, it takes the danger
848
+ * colour. No ground, no corner and no resting lift.
849
+ */
850
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'] {
851
+ background-color: transparent;
852
+ border-color: transparent;
853
+ border-block-end-color: var(
854
+ --slotted-currency-field-underline-color,
855
+ var(--slotted-control-border-color, ButtonBorder)
856
+ );
857
+ border-radius: var(--slotted-radius-none, 0px);
858
+ box-shadow: none;
859
+ padding-inline: 0;
860
+ }
861
+
862
+ [data-ui='currency-field'][data-part='root'][data-appearance='display']:has(
863
+ > [data-part='input']:focus-visible
864
+ ) {
865
+ border-block-end-color: var(--slotted-text, CanvasText);
866
+ box-shadow: inset 0 calc(-1 * var(--slotted-border-width-thin, 1px)) 0
867
+ var(--slotted-text, CanvasText);
868
+ outline: none;
869
+ }
870
+
871
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'][data-invalid] {
872
+ border-block-end-color: var(--slotted-tone-danger-border, CanvasText);
873
+ box-shadow: none;
874
+ }
875
+
876
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'][data-invalid]:has(
877
+ > [data-part='input']:focus-visible
878
+ ) {
879
+ box-shadow: inset 0 calc(-1 * var(--_invalid-edge-width)) 0 var(--_invalid-edge-color);
880
+ }
881
+
882
+ [data-ui='currency-field'][data-part='root'][data-appearance='display']:has(
883
+ > [data-part='input']:disabled
884
+ ) {
885
+ border-block-end-color: var(--slotted-disabled-border, GrayText);
886
+ }
887
+
888
+ @media (forced-colors: active) {
889
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'] {
890
+ border-block-end-color: ButtonBorder;
891
+ }
892
+
893
+ /* A shadow is not painted under forced colours, so the doubled rule of
894
+ focus is a wider border there. */
895
+ [data-ui='currency-field'][data-part='root'][data-appearance='display']:has(
896
+ > [data-part='input']:focus-visible
897
+ ) {
898
+ border-block-end-color: Highlight;
899
+ border-block-end-width: calc(2 * var(--slotted-border-width-thin, 1px));
900
+ }
901
+
902
+ [data-ui='currency-field'][data-part='root'][data-appearance='display'][data-invalid]:has(
903
+ > [data-part='input']:focus-visible
904
+ ) {
905
+ border-block-end-color: Highlight;
906
+ border-block-end-width: calc(
907
+ var(--slotted-border-width-thin, 1px) + var(--_invalid-edge-width)
908
+ );
909
+ }
910
+ }
787
911
  }
@@ -1,5 +1,6 @@
1
1
  [
2
2
  "--slotted-border-subtle",
3
+ "--slotted-border-width-thin",
3
4
  "--slotted-control-border-color",
4
5
  "--slotted-control-border-width",
5
6
  "--slotted-control-font-family",
@@ -36,6 +37,7 @@
36
37
  "--slotted-currency-field-disabled-background",
37
38
  "--slotted-currency-field-disabled-border-color",
38
39
  "--slotted-currency-field-disabled-color",
40
+ "--slotted-currency-field-display-font-size",
39
41
  "--slotted-currency-field-hover-border-color",
40
42
  "--slotted-currency-field-icon-size-min",
41
43
  "--slotted-currency-field-invalid-border-color",
@@ -57,6 +59,9 @@
57
59
  "--slotted-currency-field-subtle-background",
58
60
  "--slotted-currency-field-subtle-border-color",
59
61
  "--slotted-currency-field-subtle-hover-background",
62
+ "--slotted-currency-field-symbol-color",
63
+ "--slotted-currency-field-symbol-font-size",
64
+ "--slotted-currency-field-underline-color",
60
65
  "--slotted-disabled-background",
61
66
  "--slotted-disabled-border",
62
67
  "--slotted-disabled-foreground",
@@ -64,10 +69,17 @@
64
69
  "--slotted-focus-ring-color-invalid",
65
70
  "--slotted-focus-ring-offset",
66
71
  "--slotted-focus-ring-width",
72
+ "--slotted-font-size-4xl",
73
+ "--slotted-font-size-xl",
74
+ "--slotted-font-weight-medium",
75
+ "--slotted-font-weight-regular",
76
+ "--slotted-letter-spacing-tighter",
77
+ "--slotted-line-height-4xl",
67
78
  "--slotted-radius-none",
68
79
  "--slotted-radius-xs",
69
80
  "--slotted-space-1",
70
81
  "--slotted-space-2",
82
+ "--slotted-space-3",
71
83
  "--slotted-surface-sunken",
72
84
  "--slotted-target-comfortable",
73
85
  "--slotted-target-min",
@@ -350,8 +350,11 @@
350
350
  }
351
351
 
352
352
  /* While a widget is carried or pulled the lattice is what it snaps to, so
353
- it comes forward. */
354
- [data-ui='dashboard-grid'][data-gesture] > [data-part='grid']::before {
353
+ it comes forward. The attribute is on the grid, in the compound it styles:
354
+ a browser invalidates by attribute name, and on the root ahead of
355
+ `> [data-part='grid']` it restyled every element with a `data-part`
356
+ under the root as a widget was picked up and as it was dropped. */
357
+ [data-ui='dashboard-grid'] > [data-part='grid'][data-gesture]::before {
355
358
  opacity: 1;
356
359
  }
357
360
 
@@ -654,20 +657,18 @@
654
657
  /* ── Editing: a widget in hand ───────────────────────────────────────── */
655
658
 
656
659
  /**
657
- * A widget carried or pulled leaves its cell and floats over the grid: it
658
- * is placed where it was picked up, `--_dg-origin-*`, and follows the pointer
659
- * by `--_dg-lift-*`, all written by script. The cell it would land in is the
660
- * placeholder, and the widgets around it make room as the placeholder moves.
660
+ * A widget carried or pulled leaves its cell and floats over the grid. The
661
+ * script writes where it was picked up (`inset-inline-start`,
662
+ * `inset-block-start`, `inline-size`, `block-size`) and how far the pointer
663
+ * took it (`translate`) on the widget itself. A custom property in their
664
+ * place restyled everything under the widget on every frame. The cell it
665
+ * would land in is the placeholder, and the widgets around it make room as
666
+ * the placeholder moves.
661
667
  */
662
668
  [data-ui='dashboard-grid'] [data-part='widget']:is([data-dragging], [data-resizing]) {
663
- block-size: var(--_dg-origin-block-size);
664
669
  grid-column: auto;
665
670
  grid-row: auto;
666
- inline-size: var(--_dg-origin-inline-size);
667
- inset-block-start: var(--_dg-origin-block-start);
668
- inset-inline-start: var(--_dg-origin-inline-start);
669
671
  position: absolute;
670
- translate: var(--_dg-lift-x, 0) var(--_dg-lift-y, 0);
671
672
  z-index: 3;
672
673
  }
673
674
 
@@ -746,3 +747,37 @@
746
747
  min-block-size: calc(var(--_row) * 2 + var(--_gap));
747
748
  }
748
749
  }
750
+
751
+ /**
752
+ * A widget's cell, written on the widget by script and read only by the
753
+ * widget's own placement. Unregistered, each one inherited into the card and
754
+ * everything in it, and a preview that moved a widget restyled its whole
755
+ * content. Registered without inheritance and without a type, a widget no
756
+ * script placed still takes the fallback of `var()`. These sit outside
757
+ * `@layer slotted.components` because `@property` is a registration, not a
758
+ * rule a cascade layer holds.
759
+ */
760
+ @property --_dg-x {
761
+ syntax: '*';
762
+ inherits: false;
763
+ }
764
+
765
+ @property --_dg-y {
766
+ syntax: '*';
767
+ inherits: false;
768
+ }
769
+
770
+ @property --_dg-w {
771
+ syntax: '*';
772
+ inherits: false;
773
+ }
774
+
775
+ @property --_dg-h {
776
+ syntax: '*';
777
+ inherits: false;
778
+ }
779
+
780
+ @property --_dg-span-medium {
781
+ syntax: '*';
782
+ inherits: false;
783
+ }
@@ -52,8 +52,12 @@
52
52
  --_measure: var(--slotted-dialog-measure, 32rem);
53
53
  /** The air between the panel and the edge of the layer. */
54
54
  --_layer-padding: var(--slotted-space-8, 1.5rem);
55
- /** The panel's own inset, which every row reads. */
56
- --_padding-inline: var(--slotted-space-8, 1.5rem);
55
+ /**
56
+ * The panel's own inset, which every row reads. The panel draws none of it
57
+ * itself: the header, the body and the footer carry it, and a layout of the
58
+ * consumer's own that skips them reads the same two public tokens.
59
+ */
60
+ --_padding-inline: var(--slotted-dialog-padding-inline, var(--slotted-space-8, 1.5rem));
57
61
  /**
58
62
  * Three insets, not one number used three times, because the three rows
59
63
  * hold three different things.
@@ -72,7 +76,7 @@
72
76
  * paragraph reads as a slab around a button: twenty above and below a
73
77
  * thirty-four pixel control is a seventy-four pixel bar for one line.
74
78
  */
75
- --_padding-block: var(--slotted-space-7, 1.25rem);
79
+ --_padding-block: var(--slotted-dialog-padding-block, var(--slotted-space-7, 1.25rem));
76
80
  --_header-padding-block: var(--slotted-space-6, 1rem);
77
81
  --_footer-padding-block: var(--slotted-space-5, 0.75rem);
78
82
  --_gap: var(--slotted-space-5, 0.75rem);
@@ -182,8 +186,8 @@
182
186
 
183
187
  [data-ui='dialog'][data-size='sm'] {
184
188
  --_measure: var(--slotted-dialog-measure, 24rem);
185
- --_padding-inline: var(--slotted-space-7, 1.25rem);
186
- --_padding-block: var(--slotted-space-6, 1rem);
189
+ --_padding-inline: var(--slotted-dialog-padding-inline-sm, var(--slotted-space-7, 1.25rem));
190
+ --_padding-block: var(--slotted-dialog-padding-block-sm, var(--slotted-space-6, 1rem));
187
191
  --_header-padding-block: var(--slotted-space-5, 0.75rem);
188
192
  --_footer-padding-block: var(--slotted-space-4, 0.5rem);
189
193
  --_title-font-size: var(--slotted-font-size-lg, 1rem);
@@ -281,6 +285,15 @@
281
285
  /* ── The panel ────────────────────────────────────────────────────────── */
282
286
 
283
287
  [data-ui='dialog'] > [data-part='viewport'] > [data-part='content'] {
288
+ /**
289
+ * The inset the rows carry, stated where a layout of the consumer's own can
290
+ * read it: the panel draws no padding, so content written straight inside
291
+ * it reads `--slotted-dialog-inset-inline` for its padding and stands
292
+ * in the column the header, the body and the footer stand in.
293
+ */
294
+ --slotted-dialog-inset-inline: var(--_padding-inline);
295
+ --slotted-dialog-inset-block: var(--_padding-block);
296
+
284
297
  display: grid;
285
298
  grid-template-rows: auto minmax(0, 1fr) auto;
286
299
  inline-size: 100%;
@@ -17,6 +17,10 @@
17
17
  "--slotted-dialog-description-color",
18
18
  "--slotted-dialog-footer-surface",
19
19
  "--slotted-dialog-measure",
20
+ "--slotted-dialog-padding-block",
21
+ "--slotted-dialog-padding-block-sm",
22
+ "--slotted-dialog-padding-inline",
23
+ "--slotted-dialog-padding-inline-sm",
20
24
  "--slotted-dialog-radius",
21
25
  "--slotted-dialog-rule-color",
22
26
  "--slotted-dialog-scrim",
package/src/list/list.css CHANGED
@@ -193,6 +193,33 @@
193
193
  * level is the library's own sticky step, so a heading never rises above an
194
194
  * overlay that is above the page.
195
195
  */
196
+ /* A heading with an end sets the name at the start and the end content on
197
+ the same line, at the end. */
198
+ [data-ui='list'] [data-part='section-label']:has(> [data-part='section-end']) {
199
+ align-items: baseline;
200
+ display: flex;
201
+ gap: var(--slotted-space-4, 0.5rem);
202
+ justify-content: space-between;
203
+ }
204
+
205
+ [data-ui='list'] [data-part='section-end'] {
206
+ flex: none;
207
+ font-variant-numeric: tabular-nums;
208
+ white-space: nowrap;
209
+ }
210
+
211
+ /**
212
+ * `ruled`: the name in the text colour over a rule, with no ground behind it
213
+ * — a day of a phone list with its balance at the end. The sticky ground is
214
+ * still the set's surface, so the rows passing under it are covered.
215
+ */
216
+ [data-ui='list'] [data-part='section-label'][data-variant='ruled'] {
217
+ border-block-end: var(--slotted-border-width-thin, 1px) solid
218
+ var(--slotted-list-section-rule-color, var(--slotted-border-subtle, GrayText));
219
+ color: var(--slotted-list-section-ruled-color, var(--slotted-text, CanvasText));
220
+ font-weight: var(--slotted-font-weight-medium, 500);
221
+ }
222
+
196
223
  [data-ui='list'] [data-part='section-label'][data-sticky] {
197
224
  background-color: var(--_surface);
198
225
  inset-block-start: 0;
@@ -5,6 +5,7 @@
5
5
  "--slotted-control-padding-inline-md",
6
6
  "--slotted-control-padding-inline-sm",
7
7
  "--slotted-font-size-xs",
8
+ "--slotted-font-weight-medium",
8
9
  "--slotted-font-weight-semibold",
9
10
  "--slotted-line-height-sm",
10
11
  "--slotted-list-background",
@@ -28,6 +29,8 @@
28
29
  "--slotted-list-section-padding-block",
29
30
  "--slotted-list-section-padding-block-lg",
30
31
  "--slotted-list-section-padding-block-sm",
32
+ "--slotted-list-section-rule-color",
33
+ "--slotted-list-section-ruled-color",
31
34
  "--slotted-radius-md",
32
35
  "--slotted-space-2",
33
36
  "--slotted-space-3",
@@ -37,6 +40,7 @@
37
40
  "--slotted-space-8",
38
41
  "--slotted-space-9",
39
42
  "--slotted-surface",
43
+ "--slotted-text",
40
44
  "--slotted-text-muted",
41
45
  "--slotted-z-index-sticky"
42
46
  ]
@@ -323,6 +323,9 @@
323
323
  touch-action: none;
324
324
  transition: color var(--slotted-control-transition-duration)
325
325
  var(--slotted-control-transition-easing);
326
+ /* Its own bidi run: as a flex container its text is a new block, and a
327
+ mark of more than one character read backwards in a right-to-left page. */
328
+ unicode-bidi: plaintext;
326
329
  user-select: none;
327
330
  -webkit-user-select: none;
328
331
  }
@@ -123,8 +123,9 @@
123
123
  * scheme instead, because the ink is the far end of each scheme's own
124
124
  * range. Twenty-four per cent is measured rather than chosen: it is the
125
125
  * lowest ratio at which the track separates from the page in all three
126
- * schemes at the 2-pixel `sm` thickness, and low enough that a column of
127
- * bars does not read as a column of filled bars.
126
+ * schemes at a 2-pixel bar, half the thinnest one this family draws,
127
+ * and low enough that a column of bars does not read as a column of
128
+ * filled bars.
128
129
  */
129
130
  --_track-color: var(
130
131
  --slotted-progress-indicator-track-color,
@@ -152,14 +153,14 @@
152
153
  --_font-size: var(--slotted-control-font-size-md, 13px);
153
154
  --_text-gap: var(--slotted-space-4, 0.5rem);
154
155
  --_row-gap: var(--slotted-space-3, 0.375rem);
155
- --_thickness: var(--slotted-progress-indicator-thickness, var(--slotted-space-2, 0.25rem));
156
+ --_thickness: var(--slotted-progress-indicator-thickness, var(--slotted-space-3, 0.375rem));
156
157
  --_size: var(--slotted-progress-indicator-size, var(--slotted-control-height-md, 34px));
157
158
  --_radius: var(--slotted-progress-indicator-radius, var(--slotted-radius-full, 9999px));
158
159
  /**
159
160
  * M3's indicator–track gap, and the stop indicator's diameter, are both
160
161
  * 4dp at the 4dp track. Making them one thickness is the proportional form
161
- * of that decision rather than a copy of the number: a 4-pixel gap in a
162
- * 2-pixel bar would be wider than the bar is tall.
162
+ * of that decision rather than a copy of the number: a fixed 4-pixel gap
163
+ * would be the whole `sm` bar and half the `lg` one.
163
164
  */
164
165
  --_gap: var(--slotted-progress-indicator-gap, var(--_thickness));
165
166
 
@@ -278,7 +279,7 @@
278
279
  /* ── Size ─────────────────────────────────────────────────────────────
279
280
  *
280
281
  * One axis, three geometries. The bar reads a thickness from the space
281
- * scale — 2, 4 and 8 pixels, and M3's own default track is the middle one —
282
+ * scale — 4, 6 and 8 pixels, so no step draws a hairline in a table cell —
282
283
  * while the circle and the loader read the control height scale, so a
283
284
  * spinner standing beside a button is the same height as the button at
284
285
  * every density. Neither needs a scale of this family's own, and the
@@ -288,7 +289,7 @@
288
289
  [data-ui='progress-indicator'][data-part='root'][data-size='sm'] {
289
290
  --_font-size: var(--slotted-control-font-size-sm, 12px);
290
291
  --_text-gap: var(--slotted-space-3, 0.375rem);
291
- --_thickness: var(--slotted-progress-indicator-thickness, var(--slotted-space-1, 0.125rem));
292
+ --_thickness: var(--slotted-progress-indicator-thickness, var(--slotted-space-2, 0.25rem));
292
293
  --_size: var(--slotted-progress-indicator-size, var(--slotted-control-height-sm, 30px));
293
294
  }
294
295
 
@@ -35,7 +35,6 @@
35
35
  "--slotted-progress-indicator-wave-phase",
36
36
  "--slotted-radius-full",
37
37
  "--slotted-radius-none",
38
- "--slotted-space-1",
39
38
  "--slotted-space-2",
40
39
  "--slotted-space-3",
41
40
  "--slotted-space-4",
@@ -520,6 +520,86 @@
520
520
  box-shadow: none;
521
521
  }
522
522
 
523
+ /**
524
+ * ── Underline ──────────────────────────────────────────────────────────
525
+ *
526
+ * The same radio group with no track: the words alone, sized to their own
527
+ * width, the chosen one in the text colour and underlined. Every other word
528
+ * is muted and takes the text colour under the pointer. No thumb, no
529
+ * dividers, no ground — the underline is the mark, and it sits under the
530
+ * word rather than on an edge of a box (R1).
531
+ */
532
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline'] {
533
+ --_inner: var(--_height);
534
+
535
+ background: none;
536
+ border: 0;
537
+ border-radius: 0;
538
+ box-shadow: none;
539
+ column-gap: var(--slotted-segmented-control-underline-gap, var(--slotted-space-6, 1rem));
540
+ grid-auto-columns: auto;
541
+ padding: 0;
542
+ }
543
+
544
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline'] [data-part='indicator'],
545
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline']
546
+ [data-part='item']
547
+ + [data-part='item']::before {
548
+ display: none;
549
+ }
550
+
551
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline'] [data-part='item'] {
552
+ color: var(--slotted-segmented-control-underline-color, var(--slotted-text-muted, GrayText));
553
+ font-weight: var(--slotted-font-weight-medium, 500);
554
+ min-inline-size: var(--slotted-target-min, 24px);
555
+ padding-inline: 0;
556
+ }
557
+
558
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline']
559
+ [data-part='item'][data-checked] {
560
+ color: var(
561
+ --slotted-segmented-control-underline-checked-color,
562
+ var(--slotted-text, CanvasText)
563
+ );
564
+ }
565
+
566
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline']
567
+ [data-part='item'][data-checked]
568
+ [data-part='label'] {
569
+ text-decoration-color: currentColor;
570
+ text-decoration-line: underline;
571
+ text-decoration-thickness: var(--slotted-border-width-thick, 2px);
572
+ text-underline-offset: 0.35em;
573
+ }
574
+
575
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline'][data-invalid]
576
+ [data-part='item'][data-checked]
577
+ [data-part='label'] {
578
+ text-decoration-color: var(--slotted-tone-danger-border, CanvasText);
579
+ }
580
+
581
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline']:not([data-readonly])
582
+ [data-part='item']:hover:not([data-disabled]):not([data-checked]):has(
583
+ [data-part='control']:enabled
584
+ ),
585
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline']:not([data-readonly])
586
+ [data-part='item']:active:not([data-disabled]):not([data-checked]):has(
587
+ [data-part='control']:enabled
588
+ ) {
589
+ background: none;
590
+ color: var(--slotted-text, CanvasText);
591
+ }
592
+
593
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline']
594
+ [data-part='item']:has([data-part='control']:focus-visible) {
595
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
596
+ }
597
+
598
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline']
599
+ [data-part='item'][data-disabled] {
600
+ color: var(--slotted-disabled-foreground, GrayText);
601
+ }
602
+
523
603
  /* ── Condensed ────────────────────────────────────────────────────────── */
524
604
 
525
605
  /**
@@ -1,5 +1,6 @@
1
1
  [
2
2
  "--slotted-border-subtle",
3
+ "--slotted-border-width-thick",
3
4
  "--slotted-control-border-color",
4
5
  "--slotted-control-border-width",
5
6
  "--slotted-control-font-family",
@@ -35,7 +36,9 @@
35
36
  "--slotted-easing-standard",
36
37
  "--slotted-focus-ring-color",
37
38
  "--slotted-focus-ring-color-invalid",
39
+ "--slotted-focus-ring-offset",
38
40
  "--slotted-focus-ring-width",
41
+ "--slotted-font-weight-medium",
39
42
  "--slotted-radius-xs",
40
43
  "--slotted-segmented-control-active-background",
41
44
  "--slotted-segmented-control-background",
@@ -55,9 +58,14 @@
55
58
  "--slotted-segmented-control-invalid-focus-ring-color",
56
59
  "--slotted-segmented-control-radius",
57
60
  "--slotted-segmented-control-shadow",
61
+ "--slotted-segmented-control-underline-checked-color",
62
+ "--slotted-segmented-control-underline-color",
63
+ "--slotted-segmented-control-underline-gap",
58
64
  "--slotted-space-1",
65
+ "--slotted-space-6",
59
66
  "--slotted-target-min",
60
67
  "--slotted-text",
68
+ "--slotted-text-muted",
61
69
  "--slotted-tone-accent-border",
62
70
  "--slotted-tone-accent-subtle-active",
63
71
  "--slotted-tone-accent-text",
@@ -383,6 +383,24 @@
383
383
  margin-inline-start: auto;
384
384
  }
385
385
 
386
+ /* On a finger the card's one control is a full target among 44-pixel
387
+ controls: a transparent `::before` centred on the link or button — the
388
+ part itself when `render` made it one — reaches
389
+ the comfortable size in both axes, and the header keeps its height. */
390
+ @media (pointer: coarse) {
391
+ [data-ui='stat-card']
392
+ :is([data-part='action']:is(a, button), [data-part='action'] > :is(a, button)) {
393
+ position: relative;
394
+ }
395
+
396
+ [data-ui='stat-card']
397
+ :is([data-part='action']:is(a, button), [data-part='action'] > :is(a, button))::before {
398
+ content: '';
399
+ inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
400
+ position: absolute;
401
+ }
402
+ }
403
+
386
404
  /* ── Figure: the value, its unit, the delta ───────────────────────────── */
387
405
 
388
406
  /**
@@ -428,7 +446,9 @@
428
446
  font-size: var(--_value-size);
429
447
  font-variant-numeric: proportional-nums;
430
448
  font-weight: var(--slotted-font-weight-semibold, 600);
431
- letter-spacing: -0.02em;
449
+ /* The figure is set at the 3xl or 4xl step, where the theme's tighter
450
+ tracking applies. */
451
+ letter-spacing: var(--slotted-letter-spacing-tighter, -0.03em);
432
452
  unicode-bidi: plaintext;
433
453
  line-height: var(--_value-line);
434
454
  margin: 0;
@@ -13,6 +13,7 @@
13
13
  "--slotted-font-size-xs",
14
14
  "--slotted-font-weight-medium",
15
15
  "--slotted-font-weight-semibold",
16
+ "--slotted-letter-spacing-tighter",
16
17
  "--slotted-line-height-2xl",
17
18
  "--slotted-line-height-3xl",
18
19
  "--slotted-line-height-4xl",
@@ -85,6 +86,7 @@
85
86
  "--slotted-stat-card-value-line-height-sm",
86
87
  "--slotted-surface-raised",
87
88
  "--slotted-surface-sunken",
89
+ "--slotted-target-comfortable",
88
90
  "--slotted-text",
89
91
  "--slotted-text-muted",
90
92
  "--slotted-tone-accent-text",
@@ -461,6 +461,84 @@
461
461
  color: var(--slotted-disabled-foreground, GrayText);
462
462
  }
463
463
 
464
+ /**
465
+ * The row the page shows elsewhere — the record open in a detail pane. A
466
+ * neutral ground a step beyond hover, so the pointer passing over it changes
467
+ * nothing, and no edge on the start side (R1). It is opaque, because a
468
+ * pinned cell copies the row's ground and a translucent one shows the
469
+ * scrolled cells through it. Selection is stronger and paints over it.
470
+ */
471
+ [data-ui='table'] [data-part='body'] > [data-part='row'][data-current]:not([data-selected]) {
472
+ background-color: var(
473
+ --slotted-table-row-surface-current,
474
+ var(--slotted-tone-secondary-subtle-active, ButtonFace)
475
+ );
476
+ }
477
+
478
+ /* A row with an action of its own is a thing the pointer presses. */
479
+ [data-ui='table'] [data-part='body'] > [data-part='row'][data-interactive]:not([data-disabled]) {
480
+ cursor: pointer;
481
+ }
482
+
483
+ /**
484
+ * ── The band that opens a group ────────────────────────────────────────
485
+ *
486
+ * One header cell across every column: the group's name at the start and
487
+ * its total at the end, in tabular figures so a column of daily nets lines
488
+ * up. The ground is the stripe's step, opaque for the same reason a pinned
489
+ * cell's is, and the band stays where it is when the pointer passes.
490
+ */
491
+ [data-ui='table'] [data-part='group-header'] {
492
+ background-color: var(--slotted-table-group-surface, var(--_stripe));
493
+ border-block-end: var(--_rule-width) solid var(--_rule);
494
+ color: var(--slotted-table-group-color, var(--_text));
495
+ font-weight: var(--slotted-font-weight-semibold, 600);
496
+ padding-block: var(--_pad-block);
497
+ padding-inline: var(--_pad-inline);
498
+ text-align: start;
499
+ }
500
+
501
+ [data-ui='table'] [data-part='group-content'] {
502
+ align-items: baseline;
503
+ display: flex;
504
+ gap: var(--_gap);
505
+ justify-content: space-between;
506
+ min-inline-size: 0;
507
+ }
508
+
509
+ [data-ui='table'] [data-part='group-end'] {
510
+ font-variant-numeric: tabular-nums;
511
+ text-align: end;
512
+ white-space: nowrap;
513
+ }
514
+
515
+ /**
516
+ * A sticky band stops under the header when the header sticks too. The
517
+ * header's height is one row of the control scale and its rule; a header
518
+ * that wraps sets `--slotted-table-group-sticky-offset` itself.
519
+ */
520
+ [data-ui='table'] [data-part='group-row'][data-sticky] > [data-part='group-header'] {
521
+ inset-block-start: var(--slotted-table-group-sticky-offset, 0);
522
+ position: sticky;
523
+ z-index: var(--slotted-z-index-sticky, 100);
524
+ }
525
+
526
+ [data-ui='table'][data-sticky-header]
527
+ [data-part='group-row'][data-sticky]
528
+ > [data-part='group-header'] {
529
+ inset-block-start: var(
530
+ --slotted-table-group-sticky-offset,
531
+ calc(var(--_row-min-block) + var(--_rule-width))
532
+ );
533
+ }
534
+
535
+ @media (forced-colors: active) {
536
+ [data-ui='table'] [data-part='body'] > [data-part='row'][data-current] {
537
+ outline: var(--_rule-width) solid Highlight;
538
+ outline-offset: calc(var(--_rule-width) * -1);
539
+ }
540
+ }
541
+
464
542
  /**
465
543
  * ── Alignment and numerals ─────────────────────────────────────────────
466
544
  *
@@ -542,20 +620,32 @@
542
620
  * written where the choice is made: a table wider than the page pushes the
543
621
  * page sideways instead of scrolling inside its frame.
544
622
  *
545
- * The offset is where the header stops. An application with a bar of its own
546
- * sets `--slotted-table-sticky-offset` once, and every table under it stops
547
- * below the bar rather than under it.
623
+ * The offset is where the header stops under `page`. An application with a
624
+ * bar of its own sets `--slotted-table-sticky-offset` once, and every table
625
+ * under it stops below the bar rather than under it. Under `scroll` the
626
+ * header stops at the top of the family's own scroller and reads no offset:
627
+ * `page.css` binds the offset to the page's chrome for every table inside
628
+ * the page, and a header reading it stood that far below its own scroller's
629
+ * top, over the first rows. A `page` table inside a scrolling box of the
630
+ * consumer's own sets the offset on that box, `0` for its top edge.
548
631
  */
549
632
  [data-ui='table'][data-sticky-header] [data-part='column-header'],
550
633
  /* The chooser's own header is a `selection` cell and not a `column-header`,
551
634
  and a rule naming only the second left the select-all scrolling away while
552
635
  every column beside it stayed. */
553
636
  [data-ui='table'][data-sticky-header] [data-part='head'] [data-part='selection'] {
554
- inset-block-start: var(--slotted-table-sticky-offset, 0);
637
+ inset-block-start: 0;
555
638
  position: sticky;
556
639
  z-index: calc(var(--slotted-z-index-sticky, 100) + 1);
557
640
  }
558
641
 
642
+ [data-ui='table'][data-sticky-to='page'][data-sticky-header] [data-part='column-header'],
643
+ [data-ui='table'][data-sticky-to='page'][data-sticky-header]
644
+ [data-part='head']
645
+ [data-part='selection'] {
646
+ inset-block-start: var(--slotted-table-sticky-offset, 0);
647
+ }
648
+
559
649
  [data-ui='table'][data-sticky-to='page'] [data-part='scroll'] {
560
650
  overflow: visible;
561
651
  }
@@ -572,11 +662,18 @@
572
662
  * sibling of the scroller and never leaves it in the first place.
573
663
  */
574
664
  [data-ui='table'][data-sticky-footer] [data-part='summary'] > [data-part='row'] > * {
575
- inset-block-end: var(--slotted-table-sticky-offset-end, 0);
665
+ inset-block-end: 0;
576
666
  position: sticky;
577
667
  z-index: calc(var(--slotted-z-index-sticky, 100) + 1);
578
668
  }
579
669
 
670
+ [data-ui='table'][data-sticky-to='page'][data-sticky-footer]
671
+ [data-part='summary']
672
+ > [data-part='row']
673
+ > * {
674
+ inset-block-end: var(--slotted-table-sticky-offset-end, 0);
675
+ }
676
+
580
677
  /* The corner again, at the bottom: a summary cell that is also a pinned
581
678
  column takes both insets and the step above both. */
582
679
  [data-ui='table'][data-sticky-footer] [data-part='summary'] [data-sticky] {
@@ -41,12 +41,16 @@
41
41
  "--slotted-table-font-family",
42
42
  "--slotted-table-font-size",
43
43
  "--slotted-table-gap",
44
+ "--slotted-table-group-color",
45
+ "--slotted-table-group-sticky-offset",
46
+ "--slotted-table-group-surface",
44
47
  "--slotted-table-header-surface",
45
48
  "--slotted-table-header-surface-hover",
46
49
  "--slotted-table-icon-size",
47
50
  "--slotted-table-radius",
48
51
  "--slotted-table-row-color-selected",
49
52
  "--slotted-table-row-min-block-size",
53
+ "--slotted-table-row-surface-current",
50
54
  "--slotted-table-row-surface-hover",
51
55
  "--slotted-table-row-surface-selected",
52
56
  "--slotted-table-row-surface-selected-hover",
@@ -64,6 +68,7 @@
64
68
  "--slotted-tone-accent-subtle-hover",
65
69
  "--slotted-tone-accent-surface",
66
70
  "--slotted-tone-accent-text",
71
+ "--slotted-tone-secondary-subtle-active",
67
72
  "--slotted-tone-secondary-subtle-hover",
68
73
  "--slotted-z-index-sticky"
69
74
  ]
package/src/tabs/tabs.css CHANGED
@@ -631,12 +631,14 @@
631
631
  * through `aria-keyshortcuts`.
632
632
  *
633
633
  * It is aligned by its mark rather than by its box. The box overhangs the X
634
- * on every side, so a box set flush against the label leaves a hole where the
635
- * overhang is; both margins take the overhang back, which puts the mark one
636
- * inner gap from the last letter and the tab's own inset from the end of the
637
- * item. The first capture showed the other arrangement: a square the height of
638
- * the control, hard against the words, with the whole tab's padding on the
639
- * far side of it.
634
+ * on every side, and both margins take the overhang back, which puts the mark
635
+ * the tab's own inset from the last letter and from the end of the item. An
636
+ * inner gap before the mark left the round ground of the hover touching the
637
+ * words; the first capture showed a square the height of the control, hard
638
+ * against the words, with the whole tab's padding on the far side of it.
639
+ *
640
+ * Neutral, as the dialog's close and the paging controls: removing a tab
641
+ * closes a view and destroys nothing, so the danger tone overstated it.
640
642
  */
641
643
  [data-ui='tabs']
642
644
  > :where([data-part='list'])
@@ -650,7 +652,7 @@
650
652
  background-color: transparent;
651
653
  block-size: var(--_dismiss-size);
652
654
  border: 0;
653
- border-radius: var(--slotted-control-radius, 4px);
655
+ border-radius: var(--slotted-radius-full, 9999px);
654
656
  color: var(--slotted-tabs-dismiss-color, var(--slotted-text-muted, GrayText));
655
657
  cursor: pointer;
656
658
  display: inline-flex;
@@ -658,7 +660,7 @@
658
660
  inline-size: var(--_dismiss-size);
659
661
  justify-content: center;
660
662
  margin-inline-end: calc(var(--_padding-inline) - var(--_dismiss-overhang));
661
- margin-inline-start: max(0px, calc(var(--_inner-gap) - var(--_dismiss-overhang)));
663
+ margin-inline-start: calc(var(--_padding-inline) - var(--_dismiss-overhang));
662
664
  padding: 0;
663
665
  transition:
664
666
  background-color var(--slotted-control-transition-duration)
@@ -693,9 +695,9 @@
693
695
  > [data-part='dismiss']:not(:disabled):hover {
694
696
  background-color: var(
695
697
  --slotted-tabs-dismiss-hover-background,
696
- var(--slotted-tone-danger-surface)
698
+ var(--slotted-tone-secondary-subtle-hover)
697
699
  );
698
- color: var(--slotted-tabs-dismiss-hover-color, var(--slotted-tone-danger-text));
700
+ color: var(--slotted-tabs-dismiss-hover-color, var(--slotted-text, CanvasText));
699
701
  }
700
702
 
701
703
  [data-ui='tabs']
@@ -704,9 +706,9 @@
704
706
  > [data-part='dismiss']:not(:disabled):active {
705
707
  background-color: var(
706
708
  --slotted-tabs-dismiss-active-background,
707
- var(--slotted-tone-danger-border)
709
+ var(--slotted-tone-secondary-subtle-active)
708
710
  );
709
- color: var(--slotted-tabs-dismiss-active-color, var(--slotted-text-inverse));
711
+ color: var(--slotted-tabs-dismiss-active-color, var(--slotted-text, CanvasText));
710
712
  }
711
713
 
712
714
  [data-ui='tabs']
@@ -36,6 +36,7 @@
36
36
  "--slotted-focus-ring-color",
37
37
  "--slotted-focus-ring-offset",
38
38
  "--slotted-focus-ring-width",
39
+ "--slotted-radius-full",
39
40
  "--slotted-radius-md",
40
41
  "--slotted-radius-sm",
41
42
  "--slotted-radius-xs",
@@ -70,13 +71,9 @@
70
71
  "--slotted-target-comfortable",
71
72
  "--slotted-target-min",
72
73
  "--slotted-text",
73
- "--slotted-text-inverse",
74
74
  "--slotted-text-muted",
75
75
  "--slotted-tone-accent-solid",
76
76
  "--slotted-tone-accent-text",
77
- "--slotted-tone-danger-border",
78
- "--slotted-tone-danger-surface",
79
- "--slotted-tone-danger-text",
80
77
  "--slotted-tone-secondary-subtle-active",
81
78
  "--slotted-tone-secondary-subtle-hover"
82
79
  ]
@@ -235,6 +235,47 @@
235
235
  transition-duration: var(--slotted-duration-instant);
236
236
  }
237
237
 
238
+ /**
239
+ * ── Chips ──────────────────────────────────────────────────────────────
240
+ *
241
+ * The same chip `toggle-group` draws: a rounded item with the control edge,
242
+ * pressed in the neutral tone's ink.
243
+ */
244
+ [data-ui='toggle'][data-part='root'][data-variant='chips'] {
245
+ border-color: var(--_border-color);
246
+ border-radius: var(--slotted-toggle-chips-radius, var(--slotted-radius-full, 9999px));
247
+ font-weight: var(--slotted-font-weight-medium, 500);
248
+ }
249
+
250
+ [data-ui='toggle'][data-part='root'][data-variant='chips'][data-pressed]:not(:disabled):not(
251
+ [data-disabled]
252
+ ),
253
+ [data-ui='toggle'][data-part='root'][data-variant='chips'][data-pressed]:active:not(
254
+ :disabled
255
+ ):not([data-disabled]) {
256
+ background: var(
257
+ --slotted-toggle-chips-pressed-background,
258
+ var(--slotted-tone-neutral-solid, CanvasText)
259
+ );
260
+ border-color: var(
261
+ --slotted-toggle-chips-pressed-background,
262
+ var(--slotted-tone-neutral-solid, CanvasText)
263
+ );
264
+ color: var(--slotted-toggle-chips-pressed-color, var(--slotted-tone-neutral-on-solid, Canvas));
265
+ }
266
+
267
+ [data-ui='toggle'][data-part='root'][data-variant='chips'][data-pressed]:hover:not(:disabled):not(
268
+ [data-disabled]
269
+ ) {
270
+ background: var(--slotted-tone-neutral-solid-hover, CanvasText);
271
+ border-color: var(--slotted-tone-neutral-solid-hover, CanvasText);
272
+ }
273
+
274
+ [data-ui='toggle'][data-part='root'][data-variant='chips']:disabled,
275
+ [data-ui='toggle'][data-part='root'][data-variant='chips'][data-disabled] {
276
+ border-color: var(--slotted-disabled-border, GrayText);
277
+ }
278
+
238
279
  /* ── Focus ────────────────────────────────────────────────────────────── */
239
280
 
240
281
  /**
@@ -31,9 +31,14 @@
31
31
  "--slotted-focus-ring-color",
32
32
  "--slotted-focus-ring-offset",
33
33
  "--slotted-focus-ring-width",
34
+ "--slotted-font-weight-medium",
35
+ "--slotted-radius-full",
34
36
  "--slotted-text",
35
37
  "--slotted-toggle-active-background",
36
38
  "--slotted-toggle-border-color",
39
+ "--slotted-toggle-chips-pressed-background",
40
+ "--slotted-toggle-chips-pressed-color",
41
+ "--slotted-toggle-chips-radius",
37
42
  "--slotted-toggle-color",
38
43
  "--slotted-toggle-disabled-border-color",
39
44
  "--slotted-toggle-disabled-color",
@@ -49,6 +54,9 @@
49
54
  "--slotted-tone-accent-subtle-active",
50
55
  "--slotted-tone-accent-subtle-hover",
51
56
  "--slotted-tone-accent-text",
57
+ "--slotted-tone-neutral-on-solid",
58
+ "--slotted-tone-neutral-solid",
59
+ "--slotted-tone-neutral-solid-hover",
52
60
  "--slotted-tone-secondary-subtle-active",
53
61
  "--slotted-tone-secondary-subtle-hover"
54
62
  ]
@@ -392,6 +392,56 @@
392
392
  transition-duration: var(--slotted-duration-instant);
393
393
  }
394
394
 
395
+ /**
396
+ * ── Chips ──────────────────────────────────────────────────────────────
397
+ *
398
+ * A short list of choices as separate rounded items that wrap onto a second
399
+ * line rather than overflow, never joined into a bar. A chip at rest has the
400
+ * control edge; the pressed one is the neutral tone's ink — its solid ground
401
+ * under its own label — which is the one state a chip has to say at a
402
+ * glance. Hover and press move the ground of a chip at rest only.
403
+ */
404
+ [data-ui='toggle-group'][data-part='root'][data-variant='chips'] {
405
+ flex-wrap: wrap;
406
+ gap: var(--slotted-toggle-group-chips-gap, var(--slotted-space-3, 0.375rem));
407
+ }
408
+
409
+ [data-ui='toggle-group'][data-part='root'][data-variant='chips'] [data-part='item'] {
410
+ border-color: var(--_border-color);
411
+ border-radius: var(--slotted-toggle-group-chips-radius, var(--slotted-radius-full, 9999px));
412
+ font-weight: var(--slotted-font-weight-medium, 500);
413
+ }
414
+
415
+ [data-ui='toggle-group'][data-part='root'][data-variant='chips']
416
+ [data-part='item'][data-pressed]:not(:disabled):not([data-disabled]),
417
+ [data-ui='toggle-group'][data-part='root'][data-variant='chips']
418
+ [data-part='item'][data-pressed]:active:not(:disabled):not([data-disabled]) {
419
+ background: var(
420
+ --slotted-toggle-group-chips-pressed-background,
421
+ var(--slotted-tone-neutral-solid, CanvasText)
422
+ );
423
+ border-color: var(
424
+ --slotted-toggle-group-chips-pressed-background,
425
+ var(--slotted-tone-neutral-solid, CanvasText)
426
+ );
427
+ color: var(
428
+ --slotted-toggle-group-chips-pressed-color,
429
+ var(--slotted-tone-neutral-on-solid, Canvas)
430
+ );
431
+ }
432
+
433
+ [data-ui='toggle-group'][data-part='root'][data-variant='chips']
434
+ [data-part='item'][data-pressed]:hover:not(:disabled):not([data-disabled]) {
435
+ background: var(--slotted-tone-neutral-solid-hover, CanvasText);
436
+ border-color: var(--slotted-tone-neutral-solid-hover, CanvasText);
437
+ }
438
+
439
+ [data-ui='toggle-group'][data-part='root'][data-variant='chips'] [data-part='item']:disabled,
440
+ [data-ui='toggle-group'][data-part='root'][data-variant='chips']
441
+ [data-part='item'][data-disabled] {
442
+ border-color: var(--slotted-disabled-border, GrayText);
443
+ }
444
+
395
445
  /* ── Focus ────────────────────────────────────────────────────────────── */
396
446
 
397
447
  /**
@@ -31,9 +31,16 @@
31
31
  "--slotted-focus-ring-color",
32
32
  "--slotted-focus-ring-offset",
33
33
  "--slotted-focus-ring-width",
34
+ "--slotted-font-weight-medium",
35
+ "--slotted-radius-full",
36
+ "--slotted-space-3",
34
37
  "--slotted-text",
35
38
  "--slotted-toggle-group-active-background",
36
39
  "--slotted-toggle-group-border-color",
40
+ "--slotted-toggle-group-chips-gap",
41
+ "--slotted-toggle-group-chips-pressed-background",
42
+ "--slotted-toggle-group-chips-pressed-color",
43
+ "--slotted-toggle-group-chips-radius",
37
44
  "--slotted-toggle-group-color",
38
45
  "--slotted-toggle-group-disabled-border-color",
39
46
  "--slotted-toggle-group-disabled-color",
@@ -49,6 +56,9 @@
49
56
  "--slotted-tone-accent-subtle-active",
50
57
  "--slotted-tone-accent-subtle-hover",
51
58
  "--slotted-tone-accent-text",
59
+ "--slotted-tone-neutral-on-solid",
60
+ "--slotted-tone-neutral-solid",
61
+ "--slotted-tone-neutral-solid-hover",
52
62
  "--slotted-tone-secondary-subtle-active",
53
63
  "--slotted-tone-secondary-subtle-hover"
54
64
  ]