@slotted/styles 0.1.0-beta.4 → 0.1.0-beta.6

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.4",
3
+ "version": "0.1.0-beta.6",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1258,8 +1258,14 @@
1258
1258
  direction: rtl;
1259
1259
  }
1260
1260
 
1261
- /* The region scrolls, so it takes focus to be scrolled by keyboard. */
1262
- [data-ui='chart'] [data-part='table-region']:focus-visible {
1261
+ /* The region scrolls, so it takes focus to be scrolled by keyboard. The ring
1262
+ is drawn for a focus from the keyboard: a click on a cell focuses the region
1263
+ too, and the next Shift would make it match `:focus-visible`
1264
+ (`createFocusOrigin`). The browser's own ring is suppressed for the same focus. */
1265
+ [data-ui='chart'] [data-part='table-region']:focus {
1266
+ outline: none;
1267
+ }
1268
+ [data-ui='chart'] [data-part='table-region']:focus-visible[data-focus-origin='keyboard'] {
1263
1269
  outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
1264
1270
  outline-offset: calc(var(--slotted-focus-ring-width, 2px) * -1);
1265
1271
  }
@@ -234,6 +234,43 @@
234
234
  z-index: 2;
235
235
  }
236
236
 
237
+ /**
238
+ * The title area paints the ground only where something passes under it:
239
+ * the header area, which it covers while it collapses. Without one, the
240
+ * root's ground is the one layer under the title, so a ground with alpha is
241
+ * composited once — painted on both, 0.5 read as 0.75. With a header area
242
+ * the title keeps its own ground, because a title that let the area show
243
+ * through would print the area's words under its own.
244
+ */
245
+ [data-ui='page-header']:not(:has(> * > [data-part='header-area']))
246
+ > *
247
+ > [data-part='title-area'] {
248
+ background-color: transparent;
249
+ }
250
+
251
+ /**
252
+ * Stuck, the chrome stands over the content, and a theme may blur what passes
253
+ * under it: `--slotted-page-header-backdrop-filter`, `none` unless set. It
254
+ * composes with a ground that has alpha, which is what lets the content show
255
+ * at all. The filter is the stuck state's alone, so a `plain` header,
256
+ * transparent at rest, filters nothing at rest.
257
+ */
258
+ [data-ui='page-header'][data-stuck] {
259
+ -webkit-backdrop-filter: var(--slotted-page-header-backdrop-filter, none);
260
+ backdrop-filter: var(--slotted-page-header-backdrop-filter, none);
261
+ }
262
+
263
+ /**
264
+ * A reader who asked for less transparency gets none of the effect, and under
265
+ * forced colours the ground is the system's and the blur has nothing to add.
266
+ */
267
+ @media (prefers-reduced-transparency: reduce), (forced-colors: active) {
268
+ [data-ui='page-header'][data-stuck] {
269
+ -webkit-backdrop-filter: none;
270
+ backdrop-filter: none;
271
+ }
272
+ }
273
+
237
274
  /**
238
275
  * The pill straddles the chrome's edge, so whatever ends at that edge
239
276
  * keeps half a pill clear of it: the title area, because stuck the edge
@@ -31,6 +31,7 @@
31
31
  "--slotted-line-height-md",
32
32
  "--slotted-line-height-sm",
33
33
  "--slotted-line-height-xl",
34
+ "--slotted-page-header-backdrop-filter",
34
35
  "--slotted-page-header-background",
35
36
  "--slotted-page-header-border",
36
37
  "--slotted-page-header-control-size",
@@ -410,8 +410,18 @@
410
410
  *
411
411
  * `:has()` rather than `:focus-within`, because a link inside the content is
412
412
  * focus *within* the area and is not the area being focused.
413
+ *
414
+ * The ring is drawn for a focus from the keyboard: a click on the content
415
+ * focuses the viewport too, and the next Shift would make it match
416
+ * `:focus-visible` (`createFocusOrigin`). The browser's own ring on the
417
+ * viewport is suppressed: the frame draws the one ring.
413
418
  */
414
- [data-ui='scroll-area']:has(> [data-part='viewport']:focus-visible) {
419
+ [data-ui='scroll-area'] > [data-part='viewport']:focus {
420
+ outline: none;
421
+ }
422
+ [data-ui='scroll-area']:has(
423
+ > [data-part='viewport']:focus-visible[data-focus-origin='keyboard']
424
+ ) {
415
425
  outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
416
426
  outline-offset: var(--slotted-focus-ring-offset, 2px);
417
427
  }
@@ -59,6 +59,8 @@
59
59
  --_inset: var(--slotted-segmented-control-inset, var(--slotted-space-1, 0.125rem));
60
60
  /** What is left for a segment once the track has taken its edge and ring. */
61
61
  --_inner: calc(var(--_height) - (var(--_inset) + var(--_border-width)) * 2);
62
+ /** The space between two segments: the ring's own step. */
63
+ --_spacing: var(--slotted-segmented-control-gap, var(--_inset));
62
64
  /** The thumb's corner, kept concentric with the track's own. */
63
65
  --_thumb-radius: max(calc(var(--_radius) - var(--_inset)), var(--slotted-radius-xs, 2px));
64
66
  /**
@@ -69,15 +71,22 @@
69
71
  */
70
72
  --_travel: var(--slotted-duration-medium);
71
73
 
72
- /* A control in a row of controls: the fill, edge and lift a field has. */
73
- background: var(--slotted-segmented-control-background, var(--slotted-control-surface, Field));
74
- border: var(--_border-width) solid
75
- var(
76
- --slotted-segmented-control-border-color,
77
- var(--slotted-control-border-color, ButtonBorder)
78
- );
74
+ /**
75
+ * A neutral track: the page's own ink at a low alpha rather than an opaque
76
+ * fill, so it darkens whatever it stands on — a surface, a sunken panel, a
77
+ * card header — by the same step, and reads as a recess in each. The edge
78
+ * keeps its width and is transparent by default, so the arithmetic below is
79
+ * the same with a theme's edge and without one. An accent track with an
80
+ * accent thumb edge was the first appearance: a coloured outline on a row
81
+ * of neutral controls read as an alert before it read as a choice.
82
+ */
83
+ background: var(
84
+ --slotted-segmented-control-background,
85
+ color-mix(in oklab, var(--slotted-text, CanvasText) 6%, transparent)
86
+ );
87
+ border: var(--_border-width) solid var(--slotted-segmented-control-border-color, transparent);
79
88
  border-radius: var(--_radius);
80
- box-shadow: var(--slotted-segmented-control-shadow, var(--slotted-control-shadow, none));
89
+ box-shadow: var(--slotted-segmented-control-shadow, none);
81
90
  box-sizing: border-box;
82
91
  display: inline-grid;
83
92
  font-family: var(--slotted-control-font-family, system-ui, sans-serif);
@@ -87,8 +96,15 @@
87
96
  * sized grid resolves every track to the largest content contribution, so
88
97
  * the rule Carbon states — base the width on the longest label, and give
89
98
  * every segment that width — falls out of the layout.
99
+ *
100
+ * The column carries the segment's floor, and the segment none of its own:
101
+ * a segment with a minimum inside a column that could shrink below it ran
102
+ * past its column in a narrow container, while the thumb, a column wide,
103
+ * did not — the thumb and its segment no longer matched, and the label
104
+ * overflowed the track.
90
105
  */
91
- grid-auto-columns: minmax(0, 1fr);
106
+ gap: var(--_spacing);
107
+ grid-auto-columns: minmax(max(var(--_inner), var(--slotted-target-min, 24px)), 1fr);
92
108
  grid-auto-flow: column;
93
109
  isolation: isolate;
94
110
  min-inline-size: 0;
@@ -172,7 +188,10 @@
172
188
  color: var(--slotted-segmented-control-color, var(--slotted-text, ButtonText));
173
189
  cursor: pointer;
174
190
  display: flex;
175
- font-weight: var(--slotted-control-font-weight, 600);
191
+ font-weight: var(
192
+ --slotted-segmented-control-font-weight,
193
+ var(--slotted-font-weight-medium, 500)
194
+ );
176
195
  gap: var(--_gap);
177
196
  justify-content: center;
178
197
  letter-spacing: var(--slotted-control-letter-spacing, 0);
@@ -180,18 +199,34 @@
180
199
  /**
181
200
  * The thumb is shorter than the control it lives in, so at the smallest
182
201
  * step and the tightest density it is eighteen pixels tall. The WCAG
183
- * minimum is the floor across the segment, and the radio below reaches the
184
- * track's own edge to meet it in the other axis.
202
+ * minimum is the column's floor across the segment, and the radio below
203
+ * reaches the track's own edge to meet it in the other axis.
185
204
  */
186
- min-inline-size: max(var(--_inner), var(--slotted-target-min, 24px));
205
+ min-inline-size: 0;
187
206
  padding-inline: var(--_padding-inline);
207
+ /**
208
+ * Positioned without a `z-index`: the segment paints after the thumb, which
209
+ * comes first in the DOM, so its words lie over the thumb, and its divider
210
+ * and its hover ground — pseudo-elements at `z-index: -1` — lie under it.
211
+ * A segment at `z-index: 1` lifted both over the thumb as well, and a thumb
212
+ * crossing a hovered segment or a divider lost part of its edge for the
213
+ * length of the slide.
214
+ */
188
215
  position: relative;
189
- transition:
190
- background-color var(--slotted-control-transition-duration)
191
- var(--slotted-control-transition-easing),
192
- color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
193
- /* Above the thumb, which is painted behind every segment. */
194
- z-index: 1;
216
+ transition: color var(--slotted-control-transition-duration)
217
+ var(--slotted-control-transition-easing);
218
+ }
219
+
220
+ /** The hover and press ground, under the thumb; see the segment above. */
221
+ [data-ui='segmented-control'] [data-part='item']::after {
222
+ background: transparent;
223
+ border-radius: inherit;
224
+ content: '';
225
+ inset: 0;
226
+ position: absolute;
227
+ transition: background-color var(--slotted-control-transition-duration)
228
+ var(--slotted-control-transition-easing);
229
+ z-index: -1;
195
230
  }
196
231
 
197
232
  /* An empty label is no label: a binding that always renders the span must
@@ -205,27 +240,36 @@
205
240
  }
206
241
 
207
242
  /**
208
- * ── The rule between two segments ─────────────────────────────────────
243
+ * ── Between two segments ──────────────────────────────────────────────
244
+ *
245
+ * Segments stand apart by the same step as the ring of track around them,
246
+ * so each one — its hover ground, the thumb on it — is a shape of its own
247
+ * on the track, and a rule in the middle of that room says the control is
248
+ * divided: a track of words with nothing between them reads as a sentence.
209
249
  *
210
- * A track of words with nothing between them reads as a sentence. The rule
211
- * is what says the control is divided, and it is what Mantine ships on by
212
- * default, Apple draws, and Carbon draws. It is shorter than the segment, so
213
- * it reads as a divider rather than as a second frame, and it gives way
214
- * around the thumb — a grey line touching the accent edge is the "border
215
- * inside a border" this family had to correct elsewhere.
250
+ * The rule is centred in the gap rather than on a segment's edge, half the
251
+ * segment's height, rounded at its ends, in a light tint of the ink rather
252
+ * than an opaque border colour. It gives way wherever it would touch a
253
+ * shape: beside the thumb, and beside the segment under the pointer, whose
254
+ * ground would otherwise meet it. It lies under the thumb, so a thumb that
255
+ * crosses it while sliding passes over it.
216
256
  */
217
257
  [data-ui='segmented-control'] [data-part='item'] + [data-part='item']::before {
218
258
  /* No system colour behind it: an unthemed decoration disappears rather
219
259
  than shouting, which is what `fallbacks.test.mjs` holds a pseudo-element
220
- to. The rule is a decoration — what divides the segments in forced
221
- colours is the thumb's own border. */
222
- background: var(--slotted-segmented-control-divider-color, var(--slotted-border-subtle));
260
+ to. In forced colours the thumb's own edge is what divides. */
261
+ background: var(
262
+ --slotted-segmented-control-divider-color,
263
+ color-mix(in oklab, var(--slotted-text) 14%, transparent)
264
+ );
265
+ border-radius: var(--slotted-radius-full, 9999px);
223
266
  content: '';
224
267
  inline-size: var(--_border-width);
225
- inset-block: 22%;
226
- inset-inline-start: 0;
268
+ inset-block: 25%;
269
+ inset-inline-start: calc((var(--_spacing) + var(--_border-width)) / -2);
227
270
  position: absolute;
228
271
  transition: opacity var(--_travel) var(--slotted-easing-standard);
272
+ z-index: -1;
229
273
  }
230
274
 
231
275
  [data-ui='segmented-control'][data-part='root'][data-orientation='vertical']
@@ -233,13 +277,17 @@
233
277
  + [data-part='item']::before {
234
278
  block-size: var(--_border-width);
235
279
  inline-size: auto;
236
- inset-block: auto;
237
- inset-block-start: 0;
238
- inset-inline: 22%;
280
+ inset-block: calc((var(--_spacing) + var(--_border-width)) / -2) auto;
281
+ inset-inline: 25%;
239
282
  }
240
283
 
241
284
  [data-ui='segmented-control'] [data-part='item'][data-checked]::before,
242
- [data-ui='segmented-control'] [data-part='item'][data-checked] + [data-part='item']::before {
285
+ [data-ui='segmented-control'] [data-part='item'][data-checked] + [data-part='item']::before,
286
+ [data-ui='segmented-control'][data-part='root']:not([data-readonly])
287
+ [data-part='item']:hover:has([data-part='control']:enabled)::before,
288
+ [data-ui='segmented-control'][data-part='root']:not([data-readonly])
289
+ [data-part='item']:hover:has([data-part='control']:enabled)
290
+ + [data-part='item']::before {
243
291
  opacity: 0;
244
292
  }
245
293
 
@@ -257,7 +305,9 @@
257
305
  border-radius: inherit;
258
306
  cursor: inherit;
259
307
  inset-block: calc(-1 * (var(--_inset) + var(--_border-width)));
260
- inset-inline: 0;
308
+ /* Half the space on each side, so the room between two segments is not a
309
+ place a press lands on nothing. */
310
+ inset-inline: calc(var(--_spacing) / -2);
261
311
  margin: 0;
262
312
  opacity: 0;
263
313
  padding: 0;
@@ -277,7 +327,12 @@
277
327
  * string stays whole in the DOM: truncation never removes content.
278
328
  */
279
329
  [data-ui='segmented-control'] [data-part='label'] {
280
- overflow: hidden;
330
+ /* Clipped along the line only: `hidden` on a one-line box cut the
331
+ descenders and the accents above the cap height. A clipped box is not a
332
+ scroll container, so the flex minimum is given back by hand, or the words
333
+ would hold their full width and run into the next segment. */
334
+ min-inline-size: 0;
335
+ overflow-x: clip;
281
336
  text-overflow: ellipsis;
282
337
  white-space: nowrap;
283
338
  }
@@ -307,20 +362,33 @@
307
362
  * never seen — which is what Mantine does.
308
363
  */
309
364
  [data-ui='segmented-control'] [data-part='indicator'] {
365
+ /* A raised surface with a hairline and the lightest elevation: the chosen
366
+ segment sits on the track rather than being painted into it. In a dark
367
+ scheme the raised surface is darker than the tinted track, and a thumb
368
+ darker than its track reads as a hole, so it takes a step of the ink. */
310
369
  background: var(
311
370
  --slotted-segmented-control-indicator-background,
312
- var(--slotted-tone-accent-subtle-active, Highlight)
371
+ light-dark(
372
+ var(--slotted-surface-raised, Canvas),
373
+ color-mix(
374
+ in oklab,
375
+ var(--slotted-text, CanvasText) 14%,
376
+ var(--slotted-surface-raised, Canvas)
377
+ )
378
+ )
313
379
  );
314
380
  block-size: calc(100% - var(--_inset) * 2);
315
381
  border: var(--_border-width) solid
316
382
  var(
317
383
  --slotted-segmented-control-indicator-border-color,
318
- var(--slotted-tone-accent-border, Highlight)
384
+ color-mix(in oklab, var(--slotted-text, CanvasText) 10%, transparent)
319
385
  );
320
386
  border-radius: var(--_thumb-radius);
321
- box-shadow: var(--slotted-control-shadow, none);
387
+ box-shadow: var(--slotted-segmented-control-indicator-shadow, var(--slotted-shadow-xs, none));
322
388
  box-sizing: border-box;
323
- inline-size: calc((100% - var(--_inset) * 2) / var(--_count));
389
+ inline-size: calc(
390
+ (100% - var(--_inset) * 2 - var(--_spacing) * (var(--_count) - 1)) / var(--_count)
391
+ );
324
392
  inset-block-start: var(--_inset);
325
393
  inset-inline-start: var(--_inset);
326
394
  pointer-events: none;
@@ -328,7 +396,7 @@
328
396
  transition:
329
397
  opacity var(--slotted-control-transition-duration) var(--slotted-easing-standard),
330
398
  translate var(--_travel) var(--slotted-easing-standard);
331
- translate: calc(var(--_index) * 100%);
399
+ translate: calc(var(--_index) * (100% + var(--_spacing)));
332
400
  z-index: 0;
333
401
 
334
402
  @starting-style {
@@ -337,14 +405,16 @@
337
405
  }
338
406
 
339
407
  [data-ui='segmented-control'] [data-part='indicator']:dir(rtl) {
340
- translate: calc(var(--_index) * -100%);
408
+ translate: calc(var(--_index) * (-100% - var(--_spacing)));
341
409
  }
342
410
 
343
411
  [data-ui='segmented-control'][data-part='root'][data-orientation='vertical']
344
412
  [data-part='indicator'] {
345
- block-size: calc((100% - var(--_inset) * 2) / var(--_count));
413
+ block-size: calc(
414
+ (100% - var(--_inset) * 2 - var(--_spacing) * (var(--_count) - 1)) / var(--_count)
415
+ );
346
416
  inline-size: calc(100% - var(--_inset) * 2);
347
- translate: 0 calc(var(--_index) * 100%);
417
+ translate: 0 calc(var(--_index) * (100% + var(--_spacing)));
348
418
  }
349
419
 
350
420
  /**
@@ -356,10 +426,7 @@
356
426
  * beside it.
357
427
  */
358
428
  [data-ui='segmented-control'] [data-part='item'][data-checked] {
359
- color: var(
360
- --slotted-segmented-control-checked-color,
361
- var(--slotted-tone-accent-text, HighlightText)
362
- );
429
+ color: var(--slotted-segmented-control-checked-color, var(--slotted-text, CanvasText));
363
430
  }
364
431
 
365
432
  /* ── Hover and press, held back for every stronger state ──────────────── */
@@ -376,10 +443,10 @@
376
443
  [data-ui='segmented-control'][data-part='root']:not([data-readonly])
377
444
  [data-part='item']:hover:not([data-disabled]):not([data-checked]):has(
378
445
  [data-part='control']:enabled
379
- ) {
446
+ )::after {
380
447
  background: var(
381
448
  --slotted-segmented-control-hover-background,
382
- var(--slotted-tone-secondary-subtle-hover, ButtonFace)
449
+ color-mix(in oklab, var(--slotted-text) 5%, transparent)
383
450
  );
384
451
  }
385
452
 
@@ -391,10 +458,10 @@
391
458
  [data-ui='segmented-control'][data-part='root']:not([data-readonly])
392
459
  [data-part='item']:active:not([data-disabled]):not([data-checked]):has(
393
460
  [data-part='control']:enabled
394
- ) {
461
+ )::after {
395
462
  background: var(
396
463
  --slotted-segmented-control-active-background,
397
- var(--slotted-tone-secondary-subtle-active, ButtonFace)
464
+ color-mix(in oklab, var(--slotted-text) 9%, transparent)
398
465
  );
399
466
  transition-duration: var(--slotted-duration-instant);
400
467
  }
@@ -544,7 +611,9 @@
544
611
  [data-ui='segmented-control'][data-part='root'][data-variant='underline'] [data-part='indicator'],
545
612
  [data-ui='segmented-control'][data-part='root'][data-variant='underline']
546
613
  [data-part='item']
547
- + [data-part='item']::before {
614
+ + [data-part='item']::before,
615
+ [data-ui='segmented-control'][data-part='root'][data-variant='underline']
616
+ [data-part='item']::after {
548
617
  display: none;
549
618
  }
550
619
 
@@ -636,6 +705,16 @@
636
705
  * ring belongs to a segment and leaves it alone.
637
706
  */
638
707
  @media (forced-colors: active) {
708
+ /* The neutral track is a tint, and a tint flattens to Canvas: the edge
709
+ says where the control is, and the thumb's edge says which is chosen. */
710
+ [data-ui='segmented-control'][data-part='root']:not([data-variant='underline']) {
711
+ border-color: ButtonBorder;
712
+ }
713
+
714
+ [data-ui='segmented-control'] [data-part='indicator'] {
715
+ border-color: Highlight;
716
+ }
717
+
639
718
  [data-ui='segmented-control'][data-part='root'][data-invalid] {
640
719
  outline: var(--_invalid-edge-width) solid CanvasText;
641
720
  outline-offset: calc(-1 * (var(--_border-width) + var(--_invalid-edge-width)));
@@ -1,13 +1,10 @@
1
1
  [
2
- "--slotted-border-subtle",
3
2
  "--slotted-border-width-thick",
4
- "--slotted-control-border-color",
5
3
  "--slotted-control-border-width",
6
4
  "--slotted-control-font-family",
7
5
  "--slotted-control-font-size-lg",
8
6
  "--slotted-control-font-size-md",
9
7
  "--slotted-control-font-size-sm",
10
- "--slotted-control-font-weight",
11
8
  "--slotted-control-gap-lg",
12
9
  "--slotted-control-gap-md",
13
10
  "--slotted-control-gap-sm",
@@ -24,8 +21,6 @@
24
21
  "--slotted-control-padding-inline-md",
25
22
  "--slotted-control-padding-inline-sm",
26
23
  "--slotted-control-radius",
27
- "--slotted-control-shadow",
28
- "--slotted-control-surface",
29
24
  "--slotted-control-transition-duration",
30
25
  "--slotted-control-transition-easing",
31
26
  "--slotted-disabled-background",
@@ -39,6 +34,7 @@
39
34
  "--slotted-focus-ring-offset",
40
35
  "--slotted-focus-ring-width",
41
36
  "--slotted-font-weight-medium",
37
+ "--slotted-radius-full",
42
38
  "--slotted-radius-xs",
43
39
  "--slotted-segmented-control-active-background",
44
40
  "--slotted-segmented-control-background",
@@ -49,9 +45,12 @@
49
45
  "--slotted-segmented-control-disabled-color",
50
46
  "--slotted-segmented-control-disabled-indicator-background",
51
47
  "--slotted-segmented-control-divider-color",
48
+ "--slotted-segmented-control-font-weight",
49
+ "--slotted-segmented-control-gap",
52
50
  "--slotted-segmented-control-hover-background",
53
51
  "--slotted-segmented-control-indicator-background",
54
52
  "--slotted-segmented-control-indicator-border-color",
53
+ "--slotted-segmented-control-indicator-shadow",
55
54
  "--slotted-segmented-control-inset",
56
55
  "--slotted-segmented-control-invalid-border-color",
57
56
  "--slotted-segmented-control-invalid-edge-width",
@@ -61,15 +60,12 @@
61
60
  "--slotted-segmented-control-underline-checked-color",
62
61
  "--slotted-segmented-control-underline-color",
63
62
  "--slotted-segmented-control-underline-gap",
63
+ "--slotted-shadow-xs",
64
64
  "--slotted-space-1",
65
65
  "--slotted-space-6",
66
+ "--slotted-surface-raised",
66
67
  "--slotted-target-min",
67
68
  "--slotted-text",
68
69
  "--slotted-text-muted",
69
- "--slotted-tone-accent-border",
70
- "--slotted-tone-accent-subtle-active",
71
- "--slotted-tone-accent-text",
72
- "--slotted-tone-danger-border",
73
- "--slotted-tone-secondary-subtle-active",
74
- "--slotted-tone-secondary-subtle-hover"
70
+ "--slotted-tone-danger-border"
75
71
  ]