@slotted/styles 0.0.0-stage → 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/package.json +276 -3
- package/src/accordion/accordion.css +96 -0
- package/src/accordion/accordion.tokens.json +14 -0
- package/src/action-bar/action-bar.css +455 -0
- package/src/action-bar/action-bar.tokens.json +50 -0
- package/src/action-sheet/action-sheet.css +462 -0
- package/src/action-sheet/action-sheet.tokens.json +59 -0
- package/src/alert/alert.css +764 -0
- package/src/alert/alert.tokens.json +77 -0
- package/src/app-shell/app-shell.css +1899 -0
- package/src/app-shell/app-shell.tokens.json +88 -0
- package/src/aspect-ratio/aspect-ratio.css +131 -0
- package/src/aspect-ratio/aspect-ratio.tokens.json +1 -0
- package/src/attachment/attachment.css +906 -0
- package/src/attachment/attachment.tokens.json +85 -0
- package/src/avatar/avatar.css +598 -0
- package/src/avatar/avatar.tokens.json +64 -0
- package/src/avatar-group/avatar-group.css +268 -0
- package/src/avatar-group/avatar-group.tokens.json +16 -0
- package/src/badge/badge.css +462 -0
- package/src/badge/badge.tokens.json +58 -0
- package/src/breadcrumb/breadcrumb.css +441 -0
- package/src/breadcrumb/breadcrumb.tokens.json +50 -0
- package/src/button/button.css +754 -0
- package/src/button/button.tokens.json +72 -0
- package/src/button-group/button-group.css +455 -0
- package/src/button-group/button-group.tokens.json +11 -0
- package/src/calendar/calendar.css +846 -0
- package/src/calendar/calendar.tokens.json +67 -0
- package/src/card/card.css +602 -0
- package/src/card/card.tokens.json +68 -0
- package/src/carousel/carousel.css +860 -0
- package/src/carousel/carousel.tokens.json +62 -0
- package/src/cascader/cascader.css +1320 -0
- package/src/cascader/cascader.tokens.json +103 -0
- package/src/chart/chart.css +1473 -0
- package/src/chart/chart.tokens.json +107 -0
- package/src/checkbox/checkbox.css +1011 -0
- package/src/checkbox/checkbox.tokens.json +102 -0
- package/src/checkbox-group/checkbox-group.css +70 -0
- package/src/checkbox-group/checkbox-group.tokens.json +6 -0
- package/src/code-block/code-block.css +555 -0
- package/src/code-block/code-block.tokens.json +69 -0
- package/src/collapsible/collapsible.css +546 -0
- package/src/collapsible/collapsible.tokens.json +55 -0
- package/src/color-picker/color-picker.css +607 -0
- package/src/color-picker/color-picker.tokens.json +58 -0
- package/src/column-manager/column-manager.css +452 -0
- package/src/column-manager/column-manager.tokens.json +43 -0
- package/src/combobox/combobox.css +1048 -0
- package/src/combobox/combobox.tokens.json +112 -0
- package/src/command-palette/command-palette.css +1000 -0
- package/src/command-palette/command-palette.tokens.json +75 -0
- package/src/container/container.css +198 -0
- package/src/container/container.tokens.json +18 -0
- package/src/context-menu/context-menu.css +104 -0
- package/src/context-menu/context-menu.tokens.json +11 -0
- package/src/currency-field/currency-field.css +787 -0
- package/src/currency-field/currency-field.tokens.json +80 -0
- package/src/dashboard-grid/dashboard-grid.css +748 -0
- package/src/dashboard-grid/dashboard-grid.tokens.json +67 -0
- package/src/data-grid/data-grid.css +2484 -0
- package/src/data-grid/data-grid.tokens.json +100 -0
- package/src/data-list/data-list.css +455 -0
- package/src/data-list/data-list.tokens.json +35 -0
- package/src/date-field/date-field.css +560 -0
- package/src/date-field/date-field.tokens.json +80 -0
- package/src/date-picker/date-picker.css +420 -0
- package/src/date-picker/date-picker.tokens.json +49 -0
- package/src/date-range-picker/date-range-picker.css +983 -0
- package/src/date-range-picker/date-range-picker.tokens.json +109 -0
- package/src/datetime-field/datetime-field.css +850 -0
- package/src/datetime-field/datetime-field.tokens.json +91 -0
- package/src/datetime-picker/datetime-picker.css +843 -0
- package/src/datetime-picker/datetime-picker.tokens.json +78 -0
- package/src/datetime-range-picker/datetime-range-picker.css +1375 -0
- package/src/datetime-range-picker/datetime-range-picker.tokens.json +135 -0
- package/src/description-list/description-list.css +675 -0
- package/src/description-list/description-list.tokens.json +66 -0
- package/src/detail-panel/detail-panel.css +329 -0
- package/src/detail-panel/detail-panel.tokens.json +33 -0
- package/src/dialog/dialog.css +968 -0
- package/src/dialog/dialog.tokens.json +66 -0
- package/src/divider/divider.css +131 -0
- package/src/divider/divider.tokens.json +18 -0
- package/src/drawer/drawer.css +1303 -0
- package/src/drawer/drawer.tokens.json +75 -0
- package/src/entity-picker/entity-picker.css +1367 -0
- package/src/entity-picker/entity-picker.tokens.json +118 -0
- package/src/event-calendar/event-calendar.css +2148 -0
- package/src/event-calendar/event-calendar.tokens.json +87 -0
- package/src/field/field.css +707 -0
- package/src/field/field.tokens.json +58 -0
- package/src/field-group/field-group.css +226 -0
- package/src/field-group/field-group.tokens.json +33 -0
- package/src/filter-bar/filter-bar.css +1306 -0
- package/src/filter-bar/filter-bar.tokens.json +78 -0
- package/src/form/form.css +218 -0
- package/src/form/form.tokens.json +32 -0
- package/src/fullscreen/fullscreen.css +376 -0
- package/src/fullscreen/fullscreen.tokens.json +50 -0
- package/src/grid/grid.css +173 -0
- package/src/grid/grid.tokens.json +20 -0
- package/src/highlight/highlight.css +135 -0
- package/src/highlight/highlight.tokens.json +16 -0
- package/src/hover-card/hover-card.css +93 -0
- package/src/hover-card/hover-card.tokens.json +17 -0
- package/src/icon/icon.css +136 -0
- package/src/icon/icon.tokens.json +14 -0
- package/src/image/image.css +449 -0
- package/src/image/image.tokens.json +27 -0
- package/src/import-export-center/import-export-center.css +1175 -0
- package/src/import-export-center/import-export-center.tokens.json +84 -0
- package/src/inline/inline.css +149 -0
- package/src/inline/inline.tokens.json +14 -0
- package/src/inline-message/inline-message.css +396 -0
- package/src/inline-message/inline-message.tokens.json +19 -0
- package/src/kbd/kbd.css +490 -0
- package/src/kbd/kbd.tokens.json +40 -0
- package/src/link/link.css +237 -0
- package/src/link/link.tokens.json +25 -0
- package/src/list/list.css +240 -0
- package/src/list/list.tokens.json +42 -0
- package/src/list-item/list-item.css +937 -0
- package/src/list-item/list-item.tokens.json +114 -0
- package/src/listbox/listbox.css +674 -0
- package/src/listbox/listbox.tokens.json +72 -0
- package/src/loading-overlay/loading-overlay.css +730 -0
- package/src/loading-overlay/loading-overlay.tokens.json +48 -0
- package/src/markdown/markdown.css +168 -0
- package/src/markdown/markdown.tokens.json +18 -0
- package/src/menu/menu.css +817 -0
- package/src/menu/menu.tokens.json +78 -0
- package/src/menubar/menubar.css +170 -0
- package/src/menubar/menubar.tokens.json +32 -0
- package/src/message/message.css +1453 -0
- package/src/message/message.tokens.json +85 -0
- package/src/message-scroller/message-scroller.css +371 -0
- package/src/message-scroller/message-scroller.tokens.json +47 -0
- package/src/multi-select/multi-select.css +1427 -0
- package/src/multi-select/multi-select.tokens.json +114 -0
- package/src/navigation-menu/navigation-menu.css +1029 -0
- package/src/navigation-menu/navigation-menu.tokens.json +70 -0
- package/src/notification-center/notification-center.css +856 -0
- package/src/notification-center/notification-center.tokens.json +84 -0
- package/src/number-field/number-field.css +794 -0
- package/src/number-field/number-field.tokens.json +80 -0
- package/src/otp-input/otp-input.css +525 -0
- package/src/otp-input/otp-input.tokens.json +69 -0
- package/src/page/page.css +179 -0
- package/src/page/page.tokens.json +20 -0
- package/src/page-header/page-header.css +1181 -0
- package/src/page-header/page-header.tokens.json +71 -0
- package/src/pagination/pagination.css +500 -0
- package/src/pagination/pagination.tokens.json +56 -0
- package/src/popover/popover.css +526 -0
- package/src/popover/popover.tokens.json +60 -0
- package/src/progress-indicator/progress-indicator.css +1584 -0
- package/src/progress-indicator/progress-indicator.tokens.json +50 -0
- package/src/prompt-composer/prompt-composer.css +680 -0
- package/src/prompt-composer/prompt-composer.tokens.json +99 -0
- package/src/prose/prose.css +733 -0
- package/src/prose/prose.tokens.json +79 -0
- package/src/radio-group/radio-group.css +778 -0
- package/src/radio-group/radio-group.tokens.json +103 -0
- package/src/rating/rating.css +1071 -0
- package/src/rating/rating.tokens.json +88 -0
- package/src/result-state/result-state.css +980 -0
- package/src/result-state/result-state.tokens.json +63 -0
- package/src/runtime-properties.mjs +321 -0
- package/src/saved-views/saved-views.css +394 -0
- package/src/saved-views/saved-views.tokens.json +41 -0
- package/src/scroll-area/scroll-area.css +481 -0
- package/src/scroll-area/scroll-area.tokens.json +15 -0
- package/src/section/section.css +177 -0
- package/src/section/section.tokens.json +39 -0
- package/src/segmented-control/segmented-control.css +564 -0
- package/src/segmented-control/segmented-control.tokens.json +67 -0
- package/src/select/select.css +1593 -0
- package/src/select/select.tokens.json +110 -0
- package/src/side-navigation/side-navigation.css +1096 -0
- package/src/side-navigation/side-navigation.tokens.json +87 -0
- package/src/skeleton/skeleton.css +431 -0
- package/src/skeleton/skeleton.tokens.json +29 -0
- package/src/slider/slider.css +792 -0
- package/src/slider/slider.tokens.json +75 -0
- package/src/sparkline/sparkline.css +282 -0
- package/src/sparkline/sparkline.tokens.json +45 -0
- package/src/spinner/spinner.css +224 -0
- package/src/spinner/spinner.tokens.json +8 -0
- package/src/split-button/split-button.css +253 -0
- package/src/split-button/split-button.tokens.json +12 -0
- package/src/splitter/splitter.css +336 -0
- package/src/splitter/splitter.tokens.json +24 -0
- package/src/stack/stack.css +128 -0
- package/src/stack/stack.tokens.json +14 -0
- package/src/stat-card/stat-card.css +641 -0
- package/src/stat-card/stat-card.tokens.json +95 -0
- package/src/status-indicator/status-indicator.css +481 -0
- package/src/status-indicator/status-indicator.tokens.json +38 -0
- package/src/stepper/stepper.css +629 -0
- package/src/stepper/stepper.tokens.json +72 -0
- package/src/switch/switch.css +703 -0
- package/src/switch/switch.tokens.json +75 -0
- package/src/table/table.css +1482 -0
- package/src/table/table.tokens.json +69 -0
- package/src/table-of-contents/table-of-contents.css +533 -0
- package/src/table-of-contents/table-of-contents.tokens.json +53 -0
- package/src/tabs/tabs.css +998 -0
- package/src/tabs/tabs.tokens.json +82 -0
- package/src/tag/tag.css +768 -0
- package/src/tag/tag.tokens.json +85 -0
- package/src/tags-input/tags-input.css +757 -0
- package/src/tags-input/tags-input.tokens.json +84 -0
- package/src/text-field/text-field.css +900 -0
- package/src/text-field/text-field.tokens.json +87 -0
- package/src/textarea/textarea.css +365 -0
- package/src/textarea/textarea.tokens.json +59 -0
- package/src/time-field/time-field.css +719 -0
- package/src/time-field/time-field.tokens.json +86 -0
- package/src/time-picker/time-picker.css +665 -0
- package/src/time-picker/time-picker.tokens.json +66 -0
- package/src/time-range-picker/time-range-picker.css +1287 -0
- package/src/time-range-picker/time-range-picker.tokens.json +134 -0
- package/src/timeline/timeline.css +1596 -0
- package/src/timeline/timeline.tokens.json +96 -0
- package/src/toast/toast.css +943 -0
- package/src/toast/toast.tokens.json +90 -0
- package/src/toggle/toggle.css +290 -0
- package/src/toggle/toggle.tokens.json +54 -0
- package/src/toggle-group/toggle-group.css +463 -0
- package/src/toggle-group/toggle-group.tokens.json +54 -0
- package/src/toolbar/toolbar.css +434 -0
- package/src/toolbar/toolbar.tokens.json +49 -0
- package/src/tooltip/tooltip.css +368 -0
- package/src/tooltip/tooltip.tokens.json +39 -0
- package/src/top-navigation/top-navigation.css +1287 -0
- package/src/top-navigation/top-navigation.tokens.json +91 -0
- package/src/tour/tour.css +680 -0
- package/src/tour/tour.tokens.json +67 -0
- package/src/transfer/transfer.css +571 -0
- package/src/transfer/transfer.tokens.json +49 -0
- package/src/tree/tree.css +1000 -0
- package/src/tree/tree.tokens.json +80 -0
- package/src/typography/typography.css +211 -0
- package/src/typography/typography.tokens.json +31 -0
- package/src/upload/upload.css +1706 -0
- package/src/upload/upload.tokens.json +123 -0
- package/src/visually-hidden/visually-hidden.css +52 -0
- package/src/visually-hidden/visually-hidden.tokens.json +1 -0
- package/src/wizard/wizard.css +740 -0
- package/src/wizard/wizard.tokens.json +63 -0
|
@@ -0,0 +1,1011 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── One choice, and the box its mark is drawn in ───────────────────────
|
|
4
|
+
*
|
|
5
|
+
* The element carrying `data-ui` is a `div` with no role. The `input` inside
|
|
6
|
+
* it is the control: it holds the value, the name, the identifier and every
|
|
7
|
+
* native attribute, and it draws the box — the border, the ground and the
|
|
8
|
+
* focus ring — with `appearance: none`.
|
|
9
|
+
*
|
|
10
|
+
* The mark is a sibling of the control and not a pseudo-element of it,
|
|
11
|
+
* because Firefox renders neither `::before` nor `::after` on an `input`. It
|
|
12
|
+
* is the `Glyph` element the binding renders inside that sibling, which
|
|
13
|
+
* inherits its ink rather than baking in a colour that answers one scheme
|
|
14
|
+
* out of three.
|
|
15
|
+
*
|
|
16
|
+
* Painting reads `:checked` and `:indeterminate` rather than the mirrored
|
|
17
|
+
* attributes, so the picture cannot disagree with the element: an
|
|
18
|
+
* uncontrolled control that the browser has already toggled is drawn toggled
|
|
19
|
+
* whether or not anything re-rendered. `data-checked` and
|
|
20
|
+
* `data-indeterminate` are still written, because they are what a consumer
|
|
21
|
+
* styles against.
|
|
22
|
+
*/
|
|
23
|
+
[data-ui='checkbox'][data-part='root'] {
|
|
24
|
+
/**
|
|
25
|
+
* The box is the selection scale's square: 16px at `md`, the same square
|
|
26
|
+
* Radix, Carbon and Primer all draw, and the theme states it once. It does
|
|
27
|
+
* not follow density — see the size rules below.
|
|
28
|
+
*/
|
|
29
|
+
--_size: var(--slotted-checkbox-size, var(--slotted-selection-size-md, 16px));
|
|
30
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
31
|
+
--_gap: var(--slotted-control-label-gap-md, 12px);
|
|
32
|
+
--_line-height: var(--slotted-checkbox-line-height, var(--slotted-line-height-md, 1.5));
|
|
33
|
+
/**
|
|
34
|
+
* The mark inside its box: the clearance between the edge of the square
|
|
35
|
+
* and the edge of the glyph's own box, the same two pixels on all four
|
|
36
|
+
* sides at every step, so the mark keeps one distance from the edge rather
|
|
37
|
+
* than one ratio (CK6).
|
|
38
|
+
*
|
|
39
|
+
* It is a whole number of pixels and not a fraction of the box, because a
|
|
40
|
+
* fraction is what separated the two centres. Fifteen hundredths of a 16px
|
|
41
|
+
* box is an 11.19px mark offset by 2.39px, and a browser paints a box on
|
|
42
|
+
* the pixel grid: the mark was rasterised at 11px from 27px while the
|
|
43
|
+
* square stood at 25px, so the ink landed half a pixel to the start side,
|
|
44
|
+
* and moved when the row's line height moved the whole item onto another
|
|
45
|
+
* subpixel. The same `md` control drew a mark of 8.70 × 6.40px in one
|
|
46
|
+
* density and 10.20 × 7.50px in another. A whole clearance subtracted from
|
|
47
|
+
* a whole square is a whole mark whose offset is a whole number, so
|
|
48
|
+
* whichever way the pair is snapped, both are snapped together.
|
|
49
|
+
*/
|
|
50
|
+
--_mark-inset: var(--slotted-checkbox-mark-inset, 2px);
|
|
51
|
+
--_mark-size: calc(var(--_size) - 2 * var(--_mark-inset));
|
|
52
|
+
/**
|
|
53
|
+
* The air the halo keeps around what it holds: half the gap between the
|
|
54
|
+
* box and the label, on every side of the box and past the end of the
|
|
55
|
+
* text. At `md` in `comfortable` it is 4px, and a one-line halo is 24px
|
|
56
|
+
* tall — the WCAG target it lights.
|
|
57
|
+
*/
|
|
58
|
+
--_halo-spread: calc(var(--_gap) / 2);
|
|
59
|
+
/**
|
|
60
|
+
* The target: the WCAG 2.2 minimum, which the box is under and the row no
|
|
61
|
+
* longer pads itself out to. It is the halo's own box (below), so a
|
|
62
|
+
* one-line row is exactly its line and the twenty-four pixels are hit
|
|
63
|
+
* around the box rather than laid out beside it. The label used to carry
|
|
64
|
+
* them as a minimum plus slack, and with `box-sizing: content-box` the two
|
|
65
|
+
* added up: the row stood 28.5px tall at `md` and 30px at `dense` for one
|
|
66
|
+
* line of text.
|
|
67
|
+
*/
|
|
68
|
+
--_target: var(--slotted-target-min, 24px);
|
|
69
|
+
/**
|
|
70
|
+
* The distance from the top of the label's box to the top of the square, so
|
|
71
|
+
* the box sits on the first line of a label that wraps to three rather than
|
|
72
|
+
* in the middle of the block. Stated rather than centred: `align-items:
|
|
73
|
+
* center` on a wrapped label is what puts a checkbox beside the second line,
|
|
74
|
+
* and `align-content: center` stops answering the moment the label wraps.
|
|
75
|
+
*/
|
|
76
|
+
--_lift: calc((var(--_font-size) * var(--_line-height) - var(--_size)) / 2);
|
|
77
|
+
/**
|
|
78
|
+
* The distance from the label's text to the description under it: the step
|
|
79
|
+
* `field` puts between its own rows, in both variants.
|
|
80
|
+
*/
|
|
81
|
+
--_description-gap: var(--slotted-space-3, 0.375rem);
|
|
82
|
+
|
|
83
|
+
/* The rows are as tall as they read. Stretched, the description drifted
|
|
84
|
+
away from the label it belongs to; the surplus is the container's. */
|
|
85
|
+
align-content: start;
|
|
86
|
+
color: var(--slotted-checkbox-color, var(--slotted-text, CanvasText));
|
|
87
|
+
display: grid;
|
|
88
|
+
font-family: var(--slotted-control-font-family, system-ui, sans-serif);
|
|
89
|
+
font-size: var(--_font-size);
|
|
90
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
91
|
+
/**
|
|
92
|
+
* The halo is drawn behind the box at a negative stack level, and the
|
|
93
|
+
* stacking context it is negative inside of is this one: without it the
|
|
94
|
+
* halo would sink behind whatever surface the checkbox stands on — a
|
|
95
|
+
* list row's wash, a card — and appear on none of them.
|
|
96
|
+
*/
|
|
97
|
+
isolation: isolate;
|
|
98
|
+
line-height: var(--_line-height);
|
|
99
|
+
/* The containing block for the halo. */
|
|
100
|
+
position: relative;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The direction the halo and the target are placed in. A positioned box's
|
|
105
|
+
* flow-relative insets follow its own writing mode, and the label's can
|
|
106
|
+
* differ from the root's — a label in Arabic inside a form in Portuguese —
|
|
107
|
+
* so the two pseudo-elements take the root's direction rather than the
|
|
108
|
+
* label's, or they land on the far side of the row.
|
|
109
|
+
*/
|
|
110
|
+
[data-ui='checkbox'][data-part='root'] {
|
|
111
|
+
--_direction: ltr;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-ui='checkbox'][data-part='root']:dir(rtl) {
|
|
115
|
+
--_direction: rtl;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* ── Size ─────────────────────────────────────────────────────────────
|
|
119
|
+
*
|
|
120
|
+
* Three steps. The box reads the selection scale, which the theme states
|
|
121
|
+
* once and density does not move: the control glyph scale it read before
|
|
122
|
+
* fell to twelve pixels at `dense`, below the size a mark can be drawn in,
|
|
123
|
+
* and a box that shrinks with the text around it is a target that shrinks
|
|
124
|
+
* with it. The text, the gap and the tile's inset still follow density.
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
[data-ui='checkbox'][data-part='root'][data-size='sm'] {
|
|
128
|
+
--_size: var(--slotted-checkbox-size, var(--slotted-selection-size-sm, 14px));
|
|
129
|
+
--_font-size: var(--slotted-control-font-size-sm, 12px);
|
|
130
|
+
--_gap: var(--slotted-control-label-gap-sm, 8px);
|
|
131
|
+
--_tile-inset: var(
|
|
132
|
+
--slotted-checkbox-tile-inset,
|
|
133
|
+
var(--slotted-control-padding-inline-sm, 10px)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
[data-ui='checkbox'][data-part='root'][data-size='md'] {
|
|
138
|
+
--_size: var(--slotted-checkbox-size, var(--slotted-selection-size-md, 16px));
|
|
139
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
140
|
+
--_gap: var(--slotted-control-label-gap-md, 12px);
|
|
141
|
+
--_tile-inset: var(
|
|
142
|
+
--slotted-checkbox-tile-inset,
|
|
143
|
+
var(--slotted-control-padding-inline-md, 13px)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
[data-ui='checkbox'][data-part='root'][data-size='lg'] {
|
|
148
|
+
--_size: var(--slotted-checkbox-size, var(--slotted-selection-size-lg, 18px));
|
|
149
|
+
--_font-size: var(--slotted-control-font-size-lg, 14px);
|
|
150
|
+
--_gap: var(--slotted-control-label-gap-lg, 12px);
|
|
151
|
+
--_tile-inset: var(
|
|
152
|
+
--slotted-checkbox-tile-inset,
|
|
153
|
+
var(--slotted-control-padding-inline-lg, 16px)
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* ── The box ──────────────────────────────────────────────────────────── */
|
|
158
|
+
|
|
159
|
+
[data-ui='checkbox'] [data-part='control'] {
|
|
160
|
+
appearance: none;
|
|
161
|
+
background-color: var(--slotted-checkbox-background, var(--slotted-surface, Canvas));
|
|
162
|
+
/**
|
|
163
|
+
* The medium step of the border scale in the strong border colour. A
|
|
164
|
+
* sixteen-pixel box is edged too lightly at one pixel and too heavily at
|
|
165
|
+
* two, and the control border colour that separates a field from its
|
|
166
|
+
* ground is a weaker grey than a box this small can carry.
|
|
167
|
+
*/
|
|
168
|
+
border: var(--slotted-checkbox-border-width, var(--slotted-border-width-medium, 1.5px)) solid
|
|
169
|
+
var(--slotted-checkbox-border-color, var(--slotted-border-strong, ButtonBorder));
|
|
170
|
+
/* The smallest radius: a rounder corner on a sixteen-pixel square reads
|
|
171
|
+
as a circle's cousin, and the circle is the radio's. */
|
|
172
|
+
border-radius: var(--slotted-checkbox-radius, var(--slotted-radius-xs, 2px));
|
|
173
|
+
block-size: var(--_size);
|
|
174
|
+
box-sizing: border-box;
|
|
175
|
+
cursor: pointer;
|
|
176
|
+
grid-area: 1 / 1;
|
|
177
|
+
inline-size: var(--_size);
|
|
178
|
+
margin: 0;
|
|
179
|
+
margin-block-start: var(--_lift);
|
|
180
|
+
/* The same air under the box, so a box with no text beside it is still a
|
|
181
|
+
line tall with the square in its middle. With the lift above and nothing
|
|
182
|
+
below it stood 17.75px tall against its floor, and beside a line of text
|
|
183
|
+
it sat 0.9px low. Beside a label the line is that tall already. */
|
|
184
|
+
margin-block-end: var(--_lift);
|
|
185
|
+
transition:
|
|
186
|
+
background-color var(--slotted-control-transition-duration)
|
|
187
|
+
var(--slotted-control-transition-easing),
|
|
188
|
+
border-color var(--slotted-control-transition-duration)
|
|
189
|
+
var(--slotted-control-transition-easing);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* ── The mark ─────────────────────────────────────────────────────────
|
|
193
|
+
*
|
|
194
|
+
* One element for two glyphs. The binding draws the check, or the dash while
|
|
195
|
+
* the control is indeterminate; this sheet only reveals whichever it drew,
|
|
196
|
+
* on `:checked` or `:indeterminate`.
|
|
197
|
+
*
|
|
198
|
+
* The element is the glyph's own box: the square less its clearance on each
|
|
199
|
+
* side, set in the same grid cell as the box, so the two are concentric by
|
|
200
|
+
* construction and `getBoundingClientRect()` on the mark is the mark. The
|
|
201
|
+
* `icon` box fills it, which is what keeps the artwork one distance from the
|
|
202
|
+
* edge at every step.
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
[data-ui='checkbox'] [data-part='indicator'] {
|
|
206
|
+
--slotted-icon-size: var(--_mark-size);
|
|
207
|
+
|
|
208
|
+
block-size: var(--_mark-size);
|
|
209
|
+
/**
|
|
210
|
+
* The mark arrives along its own stroke, not out of its middle (CK6). Both
|
|
211
|
+
* glyphs are drawn from the start side to the end side — the check from
|
|
212
|
+
* the short arm through the corner to the long one, the dash from one cap
|
|
213
|
+
* to the other — so the reveal is a clip that opens on the inline axis and
|
|
214
|
+
* the ink appears where the pen would have put it.
|
|
215
|
+
*
|
|
216
|
+
* It was `scale: 0.6` → `1` on the whole element, which grows the artwork
|
|
217
|
+
* out of the box's geometric centre: the two ends arrive at once, the
|
|
218
|
+
* stroke thickens as it goes, and every subpixel error in the mark's
|
|
219
|
+
* placement travels across the box while it runs. A clip moves no edge and
|
|
220
|
+
* changes no size, so no frame of it can put ink outside the square.
|
|
221
|
+
*
|
|
222
|
+
* The inset is physical rather than flow-relative on purpose: a check is
|
|
223
|
+
* not mirrored in a right-to-left row, so neither is the hand that draws
|
|
224
|
+
* it.
|
|
225
|
+
*/
|
|
226
|
+
clip-path: inset(0% 100% 0% 0%);
|
|
227
|
+
color: var(--slotted-checkbox-mark-color, var(--slotted-tone-accent-on-solid));
|
|
228
|
+
/* A flex box, so the icon sits in it as a box and not on a line of text. */
|
|
229
|
+
display: flex;
|
|
230
|
+
grid-area: 1 / 1;
|
|
231
|
+
inline-size: var(--_mark-size);
|
|
232
|
+
margin-block-start: calc(var(--_lift) + var(--_mark-inset));
|
|
233
|
+
margin-inline-start: var(--_mark-inset);
|
|
234
|
+
opacity: 0;
|
|
235
|
+
/** Nothing here answers the pointer; the control underneath does. */
|
|
236
|
+
pointer-events: none;
|
|
237
|
+
transition:
|
|
238
|
+
clip-path var(--slotted-duration-medium) var(--slotted-control-transition-easing),
|
|
239
|
+
opacity var(--slotted-duration-medium) var(--slotted-control-transition-easing);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
[data-ui='checkbox'] [data-part='control']:checked + [data-part='indicator'],
|
|
243
|
+
[data-ui='checkbox'] [data-part='control']:indeterminate + [data-part='indicator'] {
|
|
244
|
+
clip-path: inset(0% 0% 0% 0%);
|
|
245
|
+
opacity: 1;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Reduced motion keeps the fade and drops the hand (R7). The clip is removed
|
|
250
|
+
* rather than shortened, so the mark is whole in every frame it is painted
|
|
251
|
+
* in and only its opacity moves.
|
|
252
|
+
*/
|
|
253
|
+
@media (prefers-reduced-motion: reduce) {
|
|
254
|
+
[data-ui='checkbox'] [data-part='indicator'],
|
|
255
|
+
[data-ui='checkbox'] [data-part='control']:checked + [data-part='indicator'],
|
|
256
|
+
[data-ui='checkbox'] [data-part='control']:indeterminate + [data-part='indicator'] {
|
|
257
|
+
clip-path: none;
|
|
258
|
+
transition: opacity var(--slotted-duration-medium) var(--slotted-control-transition-easing);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* ── The label, and the target ────────────────────────────────────────
|
|
263
|
+
*
|
|
264
|
+
* The label is the control's own element and it names it through `for`, so a
|
|
265
|
+
* description below it can be read without toggling anything. Its box is its
|
|
266
|
+
* line: the target the square is too small to be is the label's own
|
|
267
|
+
* pseudo-element, and the row is not padded out to it.
|
|
268
|
+
*
|
|
269
|
+
* It hugs its text rather than stretching across the column, so the empty
|
|
270
|
+
* row beside the words does not toggle the box, and it is the containing
|
|
271
|
+
* block of the target and the halo, which stand on the box outside it.
|
|
272
|
+
*/
|
|
273
|
+
|
|
274
|
+
[data-ui='checkbox'] [data-part='label'] {
|
|
275
|
+
cursor: pointer;
|
|
276
|
+
grid-column: 2;
|
|
277
|
+
grid-row: 1;
|
|
278
|
+
/** A label that is one unbroken token — an id, a hash, a URL — breaks. */
|
|
279
|
+
overflow-wrap: anywhere;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* The gap between the control and its text is the grid's, not the label's
|
|
284
|
+
* padding: a padding is laid out in the label's own direction, and a label
|
|
285
|
+
* in Arabic inside a row in Portuguese put it on the far side, against
|
|
286
|
+
* nothing, with the text against the control.
|
|
287
|
+
*/
|
|
288
|
+
[data-ui='checkbox'][data-part='root']:has(> [data-part='label']) {
|
|
289
|
+
column-gap: var(--_gap);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
[data-ui='checkbox'][data-part='root']:not([data-variant='tile']) [data-part='label'] {
|
|
293
|
+
justify-self: start;
|
|
294
|
+
position: relative;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The halo, and the target. Both are the label's pseudo-elements and not
|
|
299
|
+
* the indicator's, because the label is the element that toggles the
|
|
300
|
+
* control, and both sit at a negative stack level inside the root, so the
|
|
301
|
+
* box and the text paint over them and the box takes the pointer where it
|
|
302
|
+
* stands.
|
|
303
|
+
*
|
|
304
|
+
* The target is `::before`: a square of the WCAG minimum, hit but never
|
|
305
|
+
* seen. It is not centred on the box. A positioned box that hangs past the
|
|
306
|
+
* root's end or bottom edge is scrollable overflow in whatever the checkbox
|
|
307
|
+
* stands in, and a centred square hangs four pixels past the end in the
|
|
308
|
+
* label-first arrangement and two past the bottom in both; hanging past the
|
|
309
|
+
* start or the top is overflow nobody can scroll to. So the square ends
|
|
310
|
+
* where the box ends, and the eight pixels it adds are above the box and on
|
|
311
|
+
* its start side — which for a pointer is the same twenty-four.
|
|
312
|
+
*
|
|
313
|
+
* The halo is `::after`: one wash behind the box and the label, the target
|
|
314
|
+
* the pointer is over, lit as a whole. It stands `--_halo-spread` off the
|
|
315
|
+
* box on every side, and its corner is the box's radius plus
|
|
316
|
+
* that distance, so the corner is concentric with the square's. Past the
|
|
317
|
+
* text it stands half as far again: a line of text ends in a glyph's side
|
|
318
|
+
* bearing, lighter than the square's solid edge, and at `dense` the bare
|
|
319
|
+
* 2px read as the wash cutting into the last letter. It was a
|
|
320
|
+
* 32px circle centred on the box, and a circle needs room the row does not
|
|
321
|
+
* have: it reached past the gap to the label in seven of nine
|
|
322
|
+
* size-and-density pairs and drew under the label's first letter, and a
|
|
323
|
+
* table cell a little taller than the box cut it off. Material pays for the
|
|
324
|
+
* circle by laying out a 40px box around every checkbox; the wash takes
|
|
325
|
+
* the room the label already occupies. Its block edges clear the box by the
|
|
326
|
+
* same distance, so a one-line wash is the box plus that distance on every
|
|
327
|
+
* side, and a wrapped label is washed to its last line. The description is
|
|
328
|
+
* not washed: it does not toggle anything.
|
|
329
|
+
*
|
|
330
|
+
* Its ink is the accent's text tone at the halo opacity, and not the accent
|
|
331
|
+
* surface (CK7). A surface tone is a ground, tuned to sit under text on the
|
|
332
|
+
* page rather than to be told apart from it: at four tenths it measured
|
|
333
|
+
* 1.087:1 against the page in `light`, 1.026:1 in `dark` and 1.000:1 in
|
|
334
|
+
* `high-contrast`, where the token is the page's own white — the halo was a
|
|
335
|
+
* gesture one scheme out of three could see. The text tone is the accent the
|
|
336
|
+
* theme guarantees legible on the page in every scheme, so a twelfth of it
|
|
337
|
+
* lands in the same place in all three. `accent-border` carries the same
|
|
338
|
+
* guarantee and was not taken: `high-contrast` paints every border black, so
|
|
339
|
+
* the halo would have turned grey there and blue elsewhere.
|
|
340
|
+
*
|
|
341
|
+
* The tile has neither. Its surface is the target and answers the pointer
|
|
342
|
+
* itself, and its label's `::after` stretches over that surface instead.
|
|
343
|
+
*/
|
|
344
|
+
[data-ui='checkbox'][data-part='root']:not([data-variant='tile']) [data-part='label']::before {
|
|
345
|
+
block-size: var(--_target);
|
|
346
|
+
content: '';
|
|
347
|
+
direction: var(--_direction);
|
|
348
|
+
inline-size: var(--_target);
|
|
349
|
+
inset-block-start: calc(var(--_lift) + var(--_size) - var(--_target));
|
|
350
|
+
inset-inline-start: calc((var(--_size) - var(--_target)) / 2 - var(--_size) - var(--_gap));
|
|
351
|
+
position: absolute;
|
|
352
|
+
z-index: -1;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
[data-ui='checkbox'][data-part='root']:not([data-variant='tile']) [data-part='label']::after {
|
|
356
|
+
background-color: var(--slotted-checkbox-halo-color, var(--slotted-tone-accent-text));
|
|
357
|
+
border-radius: calc(
|
|
358
|
+
var(--slotted-checkbox-radius, var(--slotted-radius-xs, 2px)) + var(--_halo-spread)
|
|
359
|
+
);
|
|
360
|
+
content: '';
|
|
361
|
+
direction: var(--_direction);
|
|
362
|
+
inset-block: calc(var(--_lift) - var(--_halo-spread));
|
|
363
|
+
inset-inline-end: calc(-1.5 * var(--_halo-spread));
|
|
364
|
+
inset-inline-start: calc(-1 * (var(--_size) + var(--_gap) + var(--_halo-spread)));
|
|
365
|
+
opacity: 0;
|
|
366
|
+
position: absolute;
|
|
367
|
+
transition: opacity var(--slotted-control-transition-duration)
|
|
368
|
+
var(--slotted-control-transition-easing);
|
|
369
|
+
z-index: -1;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* A label that shows nothing: empty — React renders one for a box named by
|
|
374
|
+
* `aria-label`, Angular whether or not content is projected — or holding
|
|
375
|
+
* only hidden text, which the bindings mark `data-label-hidden` because a
|
|
376
|
+
* stylesheet cannot tell a text node from a hidden element. Either is zero
|
|
377
|
+
* wide, and the column gap beside it pushed whatever followed the box eight
|
|
378
|
+
* pixels away and drew the wash to the gap and half again past the box: 14px
|
|
379
|
+
* on the end side against 4px on the start. So the gap goes, the target ends
|
|
380
|
+
* where the box ends, and the wash stands at the halo spread on all four sides.
|
|
381
|
+
*/
|
|
382
|
+
[data-ui='checkbox'][data-part='root']:is(
|
|
383
|
+
[data-label-hidden],
|
|
384
|
+
:has(> [data-part='label']:empty)
|
|
385
|
+
):not([data-label-placement='start']) {
|
|
386
|
+
column-gap: 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
[data-ui='checkbox'][data-part='root']:is(
|
|
390
|
+
[data-label-hidden],
|
|
391
|
+
:has(> [data-part='label']:empty)
|
|
392
|
+
):not([data-label-placement='start'], [data-variant='tile'])
|
|
393
|
+
[data-part='label']::before {
|
|
394
|
+
/* Ending where the box ends, as everywhere: the root ends there too. */
|
|
395
|
+
inset-inline-start: calc(-1 * var(--_target));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
[data-ui='checkbox'][data-part='root']:is(
|
|
399
|
+
[data-label-hidden],
|
|
400
|
+
:has(> [data-part='label']:empty)
|
|
401
|
+
):not([data-label-placement='start'], [data-variant='tile'])
|
|
402
|
+
[data-part='label']::after {
|
|
403
|
+
inset-inline-end: calc(-1 * var(--_halo-spread));
|
|
404
|
+
inset-inline-start: calc(-1 * (var(--_size) + var(--_halo-spread)));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Aligned under the label rather than under the box, and never inside the
|
|
409
|
+
* label: inside, it would join the accessible name, and a paragraph of help
|
|
410
|
+
* text that toggles a value when it is clicked is a paragraph nobody reads.
|
|
411
|
+
*/
|
|
412
|
+
[data-ui='checkbox'] [data-part='description'] {
|
|
413
|
+
color: var(--slotted-checkbox-description-color, var(--slotted-text-muted, GrayText));
|
|
414
|
+
font-size: var(--slotted-checkbox-description-font-size, var(--slotted-font-size-xs, 0.75rem));
|
|
415
|
+
grid-column: 2;
|
|
416
|
+
grid-row: 2;
|
|
417
|
+
line-height: var(--slotted-line-height-xs, 1.4);
|
|
418
|
+
margin: 0;
|
|
419
|
+
overflow-wrap: anywhere;
|
|
420
|
+
padding-block-start: var(--_description-gap);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* ── Placement ────────────────────────────────────────────────────────
|
|
424
|
+
*
|
|
425
|
+
* The box on the other side of its label. Two columns swap and the column
|
|
426
|
+
* gap stays between them, so the distance between box and text is the same
|
|
427
|
+
* number in both arrangements and in both writing directions.
|
|
428
|
+
*/
|
|
429
|
+
|
|
430
|
+
[data-ui='checkbox'][data-part='root'][data-label-placement='start'] {
|
|
431
|
+
/**
|
|
432
|
+
* `1fr` and not `max-content`, which was tried first and looked better in
|
|
433
|
+
* one row than in five: `max-content` sizes each item's first column from
|
|
434
|
+
* its own label, so a column of choices ends with the boxes at five
|
|
435
|
+
* different distances from the edge. A control that does not line up with
|
|
436
|
+
* the control above it is a worse picture than a control at the end of its
|
|
437
|
+
* row — and the end of the row is where a label-first arrangement puts a
|
|
438
|
+
* control everywhere it is used, from a settings list to a table cell.
|
|
439
|
+
*/
|
|
440
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
[data-ui='checkbox'][data-part='root'][data-label-placement='start'] [data-part='control'],
|
|
444
|
+
[data-ui='checkbox'][data-part='root'][data-label-placement='start'] [data-part='indicator'] {
|
|
445
|
+
grid-area: 1 / 2;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
[data-ui='checkbox'][data-part='root'][data-label-placement='start'] [data-part='label'],
|
|
449
|
+
[data-ui='checkbox'][data-part='root'][data-label-placement='start'] [data-part='description'] {
|
|
450
|
+
grid-column: 1;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
[data-ui='checkbox'][data-part='root'][data-label-placement='start'] [data-part='label'] {
|
|
454
|
+
justify-self: stretch;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
[data-ui='checkbox'][data-part='root'][data-label-placement='start'] [data-part='label']::before {
|
|
458
|
+
inset-inline-end: calc(-1 * (var(--_size) + var(--_gap)));
|
|
459
|
+
inset-inline-start: auto;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
[data-ui='checkbox'][data-part='root'][data-label-placement='start'] [data-part='label']::after {
|
|
463
|
+
inset-inline-end: calc(-1 * (var(--_size) + var(--_gap) + var(--_halo-spread)));
|
|
464
|
+
inset-inline-start: calc(-1.5 * var(--_halo-spread));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/* ── States ───────────────────────────────────────────────────────────
|
|
468
|
+
*
|
|
469
|
+
* In the order `STATE_PRECEDENCE` declares, each weaker one held back for
|
|
470
|
+
* every stronger one it can meet: a disabled control answers no hover, and a
|
|
471
|
+
* read-only one answers no press.
|
|
472
|
+
*/
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Hover is the edge and the halo, and the ground stays: the halo is the
|
|
476
|
+
* pointer's answer, and a ground that tinted with it was two answers.
|
|
477
|
+
* The halo lights from the box and from the label alike, because both are
|
|
478
|
+
* the same target — the label is what toggles the control.
|
|
479
|
+
*/
|
|
480
|
+
[data-ui='checkbox'][data-part='root']:not([data-readonly])
|
|
481
|
+
[data-part='control']:hover:not(:disabled) {
|
|
482
|
+
border-color: var(--slotted-checkbox-hover-border-color, var(--slotted-tone-accent-border));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
[data-ui='checkbox'][data-part='root']:not([data-readonly])
|
|
486
|
+
[data-part='control']:hover:not(:disabled)
|
|
487
|
+
~ [data-part='label']::after,
|
|
488
|
+
[data-ui='checkbox'][data-part='root']:not([data-readonly])
|
|
489
|
+
[data-part='control']:not(:disabled)
|
|
490
|
+
~ [data-part='label']:hover::after {
|
|
491
|
+
opacity: var(--slotted-selection-halo-opacity, 0.12);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
[data-ui='checkbox'][data-part='root']:not([data-readonly])
|
|
495
|
+
[data-part='control']:active:not(:disabled) {
|
|
496
|
+
background-color: var(
|
|
497
|
+
--slotted-checkbox-active-background,
|
|
498
|
+
var(--slotted-tone-accent-subtle-active)
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
[data-ui='checkbox'] [data-part='control']:checked,
|
|
503
|
+
[data-ui='checkbox'] [data-part='control']:indeterminate {
|
|
504
|
+
background-color: var(--slotted-checkbox-checked-background, var(--slotted-tone-accent-solid));
|
|
505
|
+
border-color: var(--slotted-checkbox-checked-border-color, var(--slotted-tone-accent-solid));
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
[data-ui='checkbox'][data-part='root']:not([data-readonly])
|
|
509
|
+
[data-part='control']:checked:hover:not(:disabled),
|
|
510
|
+
[data-ui='checkbox'][data-part='root']:not([data-readonly])
|
|
511
|
+
[data-part='control']:indeterminate:hover:not(:disabled) {
|
|
512
|
+
background-color: var(
|
|
513
|
+
--slotted-checkbox-checked-hover-background,
|
|
514
|
+
var(--slotted-tone-accent-solid-hover)
|
|
515
|
+
);
|
|
516
|
+
border-color: var(
|
|
517
|
+
--slotted-checkbox-checked-hover-background,
|
|
518
|
+
var(--slotted-tone-accent-solid-hover)
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
[data-ui='checkbox'][data-part='root']:not([data-readonly])
|
|
523
|
+
[data-part='control']:checked:active:not(:disabled),
|
|
524
|
+
[data-ui='checkbox'][data-part='root']:not([data-readonly])
|
|
525
|
+
[data-part='control']:indeterminate:active:not(:disabled) {
|
|
526
|
+
background-color: var(
|
|
527
|
+
--slotted-checkbox-checked-active-background,
|
|
528
|
+
var(--slotted-tone-accent-solid-active)
|
|
529
|
+
);
|
|
530
|
+
border-color: var(
|
|
531
|
+
--slotted-checkbox-checked-active-background,
|
|
532
|
+
var(--slotted-tone-accent-solid-active)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
[data-ui='checkbox'] [data-part='control']:focus-visible {
|
|
537
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
538
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* The ring is retargeted rather than repainted: every rule that draws focus
|
|
543
|
+
* reads one name, so pointing it at the invalid colour moves the outline
|
|
544
|
+
* without a second focus rule existing anywhere.
|
|
545
|
+
*/
|
|
546
|
+
[data-ui='checkbox'][data-part='root'][data-invalid] {
|
|
547
|
+
--slotted-focus-ring-color: var(
|
|
548
|
+
--slotted-checkbox-invalid-focus-ring-color,
|
|
549
|
+
var(--slotted-focus-ring-color-invalid, Highlight)
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* The border thickens as well as changing colour. In the `high-contrast`
|
|
555
|
+
* scheme the theme paints `--slotted-tone-danger-border` the same black as
|
|
556
|
+
* every other border, so colour alone would make an invalid control identical
|
|
557
|
+
* to a valid one — and `docs/component-features.md` §2.5 refuses a state that
|
|
558
|
+
* only colour carries. It thickens by the edge every invalid control adds,
|
|
559
|
+
* `--slotted-control-invalid-edge-width`, and as a border rather than the
|
|
560
|
+
* inset shadow a field draws, because a width survives forced colours and a
|
|
561
|
+
* shadow does not. The box keeps its size, because the border box is what is
|
|
562
|
+
* measured.
|
|
563
|
+
*/
|
|
564
|
+
[data-ui='checkbox'][data-part='root'][data-invalid] [data-part='control'] {
|
|
565
|
+
border-width: calc(
|
|
566
|
+
var(--slotted-checkbox-border-width, var(--slotted-border-width-medium, 1.5px)) +
|
|
567
|
+
var(--slotted-checkbox-invalid-edge-width, var(--slotted-control-invalid-edge-width, 1px))
|
|
568
|
+
);
|
|
569
|
+
border-color: var(--slotted-checkbox-invalid-border-color, var(--slotted-tone-danger-border));
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
[data-ui='checkbox'][data-part='root'][data-invalid] [data-part='control']:checked,
|
|
573
|
+
[data-ui='checkbox'][data-part='root'][data-invalid] [data-part='control']:indeterminate {
|
|
574
|
+
background-color: var(
|
|
575
|
+
--slotted-checkbox-invalid-checked-background,
|
|
576
|
+
var(--slotted-tone-danger-solid)
|
|
577
|
+
);
|
|
578
|
+
border-color: var(
|
|
579
|
+
--slotted-checkbox-invalid-checked-background,
|
|
580
|
+
var(--slotted-tone-danger-solid)
|
|
581
|
+
);
|
|
582
|
+
/*
|
|
583
|
+
* A filled box has its border in the fill's colour, so a wider border says
|
|
584
|
+
* nothing once the box is checked, and the fill's colour alone is what a
|
|
585
|
+
* colour cannot carry (WCAG 1.4.1). A line of the surface inside the
|
|
586
|
+
* border, the edge's width, parts the border from the fill: a checked
|
|
587
|
+
* invalid box is a ring around a smaller fill, a checked valid one is
|
|
588
|
+
* solid.
|
|
589
|
+
*/
|
|
590
|
+
box-shadow: inset 0 0 0
|
|
591
|
+
var(--slotted-checkbox-invalid-edge-width, var(--slotted-control-invalid-edge-width, 1px))
|
|
592
|
+
var(--slotted-surface, Canvas);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Read-only keeps the ink, the ring and the tab stop: the value is real and
|
|
597
|
+
* is submitted, and `docs/component-features.md` §2.5 refuses to let it look
|
|
598
|
+
* disabled. Only the ground and the pointer say the value is not the
|
|
599
|
+
* reader's to move.
|
|
600
|
+
*/
|
|
601
|
+
[data-ui='checkbox'][data-part='root'][data-readonly] [data-part='control'],
|
|
602
|
+
[data-ui='checkbox'][data-part='root'][data-readonly] [data-part='label'] {
|
|
603
|
+
cursor: default;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
[data-ui='checkbox'][data-part='root'][data-readonly]
|
|
607
|
+
[data-part='control']:not(:checked):not(:indeterminate) {
|
|
608
|
+
background-color: var(
|
|
609
|
+
--slotted-checkbox-readonly-background,
|
|
610
|
+
/**
|
|
611
|
+
* The ground `textarea` already gives a read-only value, and not the
|
|
612
|
+
* sunken surface: at the `light` scheme the sunken step is #f8fafd
|
|
613
|
+
* against a white surface, a difference a reader cannot see, so the state
|
|
614
|
+
* existed in the accessibility tree and nowhere on screen.
|
|
615
|
+
*/
|
|
616
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* `:disabled` beside `[data-disabled]`, because a disabled `fieldset`
|
|
622
|
+
* disables every control inside it and tells none of them.
|
|
623
|
+
*/
|
|
624
|
+
[data-ui='checkbox'][data-part='root'][data-disabled] [data-part='control'],
|
|
625
|
+
[data-ui='checkbox'] [data-part='control']:disabled {
|
|
626
|
+
background-color: var(
|
|
627
|
+
--slotted-checkbox-disabled-background,
|
|
628
|
+
var(--slotted-disabled-background)
|
|
629
|
+
);
|
|
630
|
+
border-color: var(--slotted-checkbox-disabled-border-color, var(--slotted-disabled-border));
|
|
631
|
+
cursor: not-allowed;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
[data-ui='checkbox'] [data-part='control']:disabled + [data-part='indicator'] {
|
|
635
|
+
color: var(--slotted-checkbox-disabled-mark-color, var(--slotted-disabled-foreground));
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* The element's own state reaches the text beside it through the general
|
|
640
|
+
* sibling combinator, and not only through the mirrored attribute: a disabled
|
|
641
|
+
* `fieldset` disables every control inside it and tells none of them, so a
|
|
642
|
+
* checkbox in a disabled group would otherwise keep a full-contrast label on
|
|
643
|
+
* a control nobody can reach.
|
|
644
|
+
*/
|
|
645
|
+
[data-ui='checkbox'][data-part='root'][data-disabled] [data-part='label'],
|
|
646
|
+
[data-ui='checkbox'][data-part='root'][data-disabled] [data-part='description'],
|
|
647
|
+
[data-ui='checkbox'] [data-part='control']:disabled ~ [data-part='label'],
|
|
648
|
+
[data-ui='checkbox'] [data-part='control']:disabled ~ [data-part='description'] {
|
|
649
|
+
color: var(--slotted-checkbox-disabled-color, var(--slotted-disabled-foreground));
|
|
650
|
+
cursor: not-allowed;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/* ── The tile ─────────────────────────────────────────────────────────
|
|
654
|
+
*
|
|
655
|
+
* A choice drawn as a framed surface, and the whole of that surface as its
|
|
656
|
+
* target. Three of the implementations surveyed put this inside the checkbox
|
|
657
|
+
* family — Mantine's `Checkbox.Card`, Chakra's `CheckboxCard`, React Aria's
|
|
658
|
+
* `CheckboxButton` — and the reason it is not `card`'s `data-part="selection"`
|
|
659
|
+
* is that `card` is a content container: it holds media, three regions and an
|
|
660
|
+
* elevation calibrated for content, and it knows nothing of a `size` arriving
|
|
661
|
+
* from a field, of `invalid`, of `readonly` or of `required`. `selection`
|
|
662
|
+
* stays what it is, a choice living inside real card content; this is a
|
|
663
|
+
* choice that is the whole of what the surface holds.
|
|
664
|
+
*
|
|
665
|
+
* The name is `tile` rather than `card`, which is what Mantine and Chakra
|
|
666
|
+
* call it, because `card` already names a family here. Carbon and Cloudscape
|
|
667
|
+
* both call the framed choice a tile.
|
|
668
|
+
*
|
|
669
|
+
* The tile does not arrange itself. `grid` already ships `columns`,
|
|
670
|
+
* `minItemWidth` and a stretching alignment, so a row of equal-width tiles of
|
|
671
|
+
* equal height is a `grid` around the items and nothing this family adds.
|
|
672
|
+
*/
|
|
673
|
+
|
|
674
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'] {
|
|
675
|
+
/* The description sits one step nearer its label inside a frame (CK2):
|
|
676
|
+
the tile's own inset already holds the pair apart from everything else,
|
|
677
|
+
and the plain variant's distance read as a third row inside the box. */
|
|
678
|
+
--_description-gap: var(
|
|
679
|
+
--slotted-checkbox-tile-description-gap,
|
|
680
|
+
var(--slotted-space-2, 0.25rem)
|
|
681
|
+
);
|
|
682
|
+
/* Current. The gated rules below move these three and nothing else, so a
|
|
683
|
+
state change is one declaration rather than a repainted rule. */
|
|
684
|
+
--_tile-surface: var(--slotted-checkbox-tile-background, var(--slotted-surface, Canvas));
|
|
685
|
+
--_tile-edge: var(
|
|
686
|
+
--slotted-checkbox-tile-border-color,
|
|
687
|
+
var(--slotted-control-border-color, ButtonBorder)
|
|
688
|
+
);
|
|
689
|
+
--_tile-edge-width: var(--slotted-control-border-width, 1px);
|
|
690
|
+
|
|
691
|
+
background-color: var(--_tile-surface);
|
|
692
|
+
border-radius: var(--slotted-checkbox-tile-radius, var(--slotted-radius-md, 6px));
|
|
693
|
+
/* The shadow a framed control carries at rest. A tile is a framed control,
|
|
694
|
+
so it reads the control step rather than a surface elevation: the two say
|
|
695
|
+
different things and the token foundation separates them for that. */
|
|
696
|
+
box-shadow: var(--slotted-control-shadow, none);
|
|
697
|
+
cursor: pointer;
|
|
698
|
+
padding: var(--_tile-inset);
|
|
699
|
+
/* The containing block for the edge and for the stretched target. */
|
|
700
|
+
position: relative;
|
|
701
|
+
transition:
|
|
702
|
+
background-color var(--slotted-control-transition-duration)
|
|
703
|
+
var(--slotted-control-transition-easing),
|
|
704
|
+
box-shadow var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* The edge is drawn over the border box and never in flow, so a hairline
|
|
709
|
+
* becoming a two-pixel chosen edge moves no line of text inside the tile. A
|
|
710
|
+
* real border would have to reserve its resting width, and reserving it is
|
|
711
|
+
* what makes the change shift every row by a pixel. It is a border rather
|
|
712
|
+
* than a shadow so that it survives forced colours, where a shadow is removed
|
|
713
|
+
* outright and a width is not. `card` draws its own edge the same way and for
|
|
714
|
+
* the same two reasons.
|
|
715
|
+
*/
|
|
716
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile']::before {
|
|
717
|
+
border: calc(var(--_tile-edge-width) + var(--_tile-invalid-edge, 0px)) solid var(--_tile-edge);
|
|
718
|
+
border-radius: inherit;
|
|
719
|
+
content: '';
|
|
720
|
+
inset: 0;
|
|
721
|
+
pointer-events: none;
|
|
722
|
+
position: absolute;
|
|
723
|
+
transition: border-color var(--slotted-control-transition-duration)
|
|
724
|
+
var(--slotted-control-transition-easing);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* The target is the tile. The label already owns the target in the plain
|
|
729
|
+
* variant; here its hit area stretches over the whole surface, which is the
|
|
730
|
+
* mechanism `card` uses for a card that resolves to one destination. The
|
|
731
|
+
* element stays a `<label for>`, so the name, the toggle and `Space` are the
|
|
732
|
+
* platform's and nothing is reimplemented.
|
|
733
|
+
*
|
|
734
|
+
* Anything a consumer raises above the surface — a link inside the
|
|
735
|
+
* description, a button in the corner — sets `position: relative` on itself,
|
|
736
|
+
* for the same reason a card's footer does.
|
|
737
|
+
*/
|
|
738
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'] [data-part='label']::after {
|
|
739
|
+
content: '';
|
|
740
|
+
inset: 0;
|
|
741
|
+
position: absolute;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/* ── The tile's states ────────────────────────────────────────────────
|
|
745
|
+
*
|
|
746
|
+
* Read on the root rather than on the control, because the stretched
|
|
747
|
+
* target means the pointer is over the label and not over the box for all but
|
|
748
|
+
* sixteen pixels of the surface. Each rule holds itself back for every
|
|
749
|
+
* stronger state it can meet, in the order `STATE_PRECEDENCE` declares —
|
|
750
|
+
* including `checked`, which outranks both: without that guard, hovering a
|
|
751
|
+
* chosen tile repainted it in the fainter tint an unchosen one shows, so the
|
|
752
|
+
* pointer made a tile look less chosen than it was.
|
|
753
|
+
*/
|
|
754
|
+
|
|
755
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile']:not([data-readonly]):not(
|
|
756
|
+
[data-disabled]
|
|
757
|
+
):has([data-part='control']:not(:disabled)):not(
|
|
758
|
+
:has([data-part='control']:is(:checked, :indeterminate))
|
|
759
|
+
):hover {
|
|
760
|
+
--_tile-surface: var(
|
|
761
|
+
--slotted-checkbox-tile-hover-background,
|
|
762
|
+
var(--slotted-tone-accent-subtle-hover)
|
|
763
|
+
);
|
|
764
|
+
--_tile-edge: var(
|
|
765
|
+
--slotted-checkbox-tile-hover-border-color,
|
|
766
|
+
var(--slotted-tone-accent-border)
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile']:not([data-readonly]):not(
|
|
771
|
+
[data-disabled]
|
|
772
|
+
):has([data-part='control']:not(:disabled)):not(
|
|
773
|
+
:has([data-part='control']:is(:checked, :indeterminate))
|
|
774
|
+
):active {
|
|
775
|
+
--_tile-surface: var(
|
|
776
|
+
--slotted-checkbox-tile-active-background,
|
|
777
|
+
var(--slotted-tone-accent-subtle-active)
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Chosen says it with the edge, at the solid step and at twice the width. The
|
|
783
|
+
* ground moves one tint with it, because at tile area an edge alone is a
|
|
784
|
+
* difference a reader has to look for across a row of five, and the tint
|
|
785
|
+
* alone is a difference the `high-contrast` scheme flattens to nothing.
|
|
786
|
+
*/
|
|
787
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile']:not([data-readonly]):has(
|
|
788
|
+
[data-part='control']:is(:checked, :indeterminate)
|
|
789
|
+
) {
|
|
790
|
+
--_tile-edge: var(
|
|
791
|
+
--slotted-checkbox-tile-checked-border-color,
|
|
792
|
+
var(--slotted-tone-accent-solid)
|
|
793
|
+
);
|
|
794
|
+
--_tile-edge-width: var(--slotted-border-width-thick, 2px);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* The chosen tint is the weaker half of what "chosen" says, so it holds
|
|
799
|
+
* itself back for read-only, which outranks it. The edge above does not: the
|
|
800
|
+
* value is real and stays visible, and a read-only set then reads as a set of
|
|
801
|
+
* secondary grounds with one accent edge on the choice that was made.
|
|
802
|
+
*/
|
|
803
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile']:not([data-readonly]):has(
|
|
804
|
+
[data-part='control']:is(:checked, :indeterminate)
|
|
805
|
+
) {
|
|
806
|
+
--_tile-surface: var(
|
|
807
|
+
--slotted-checkbox-tile-checked-background,
|
|
808
|
+
var(--slotted-tone-accent-surface)
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* A chosen tile still answers the pointer, and it is the edge that moves
|
|
814
|
+
* rather than the ground: the chosen tint is already the deepest step the
|
|
815
|
+
* accent tone has below its solid, so a ground that moved would either not
|
|
816
|
+
* move at all in `light` — where `accent-subtle-active` and `accent-surface`
|
|
817
|
+
* are the same value — or land on the tint an unchosen tile shows when it is
|
|
818
|
+
* merely hovered. Carbon moves the ground because its selected fill is not at
|
|
819
|
+
* the end of its own scale; here it is.
|
|
820
|
+
*/
|
|
821
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile']:not([data-readonly]):not(
|
|
822
|
+
[data-disabled]
|
|
823
|
+
):has([data-part='control']:is(:checked, :indeterminate):not(:disabled)):hover {
|
|
824
|
+
--_tile-edge: var(
|
|
825
|
+
--slotted-checkbox-tile-checked-hover-border-color,
|
|
826
|
+
var(--slotted-tone-accent-solid-hover)
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'][data-invalid] {
|
|
831
|
+
--_tile-edge: var(
|
|
832
|
+
--slotted-checkbox-tile-invalid-border-color,
|
|
833
|
+
var(--slotted-tone-danger-border)
|
|
834
|
+
);
|
|
835
|
+
/* Added to whatever width the tile's state draws, chosen or not, so an
|
|
836
|
+
invalid tile is wider than the same tile valid (WCAG 1.4.1). */
|
|
837
|
+
--_tile-invalid-edge: var(
|
|
838
|
+
--slotted-checkbox-invalid-edge-width,
|
|
839
|
+
var(--slotted-control-invalid-edge-width, 1px)
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'][data-invalid]:has(
|
|
844
|
+
[data-part='control']:is(:checked, :indeterminate)
|
|
845
|
+
) {
|
|
846
|
+
--_tile-surface: var(
|
|
847
|
+
--slotted-checkbox-tile-invalid-checked-background,
|
|
848
|
+
var(--slotted-tone-danger-surface)
|
|
849
|
+
);
|
|
850
|
+
--_tile-edge: var(
|
|
851
|
+
--slotted-checkbox-tile-invalid-checked-border-color,
|
|
852
|
+
var(--slotted-tone-danger-solid)
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/* Read-only keeps the ink and the tab stop; only the ground and the pointer
|
|
857
|
+
say the value is not the reader's to move. */
|
|
858
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'][data-readonly] {
|
|
859
|
+
--_tile-surface: var(
|
|
860
|
+
--slotted-checkbox-tile-readonly-background,
|
|
861
|
+
var(--slotted-tone-secondary-subtle-hover)
|
|
862
|
+
);
|
|
863
|
+
/**
|
|
864
|
+
* The frame holds back for read-only, edge and all. It was the ground
|
|
865
|
+
* alone, and in the `dark` scheme that ground is `#18181b` against a chosen
|
|
866
|
+
* tile's `#181b22`: seven units of blue, which is a state that exists in
|
|
867
|
+
* the accessibility tree and nowhere on screen. The box inside is still
|
|
868
|
+
* painted chosen — it is the mark that says what was chosen — and the frame
|
|
869
|
+
* says the value is not the reader's to move, in every scheme, because the
|
|
870
|
+
* neutral edge and the accent edge are different colours in all three.
|
|
871
|
+
*/
|
|
872
|
+
--_tile-edge: var(--slotted-checkbox-tile-readonly-border-color, var(--slotted-border-strong));
|
|
873
|
+
|
|
874
|
+
cursor: default;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'][data-disabled],
|
|
878
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile']:has([data-part='control']:disabled) {
|
|
879
|
+
--_tile-surface: var(
|
|
880
|
+
--slotted-checkbox-tile-disabled-background,
|
|
881
|
+
var(--slotted-disabled-background)
|
|
882
|
+
);
|
|
883
|
+
--_tile-edge: var(
|
|
884
|
+
--slotted-checkbox-tile-disabled-border-color,
|
|
885
|
+
var(--slotted-disabled-border)
|
|
886
|
+
);
|
|
887
|
+
|
|
888
|
+
/* A tile that cannot be used does not sit above the page. */
|
|
889
|
+
box-shadow: none;
|
|
890
|
+
cursor: not-allowed;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* The ring goes around the tile and the box loses its own. Drawing both left
|
|
895
|
+
* a ring around a sixteen-pixel box inside a ring around the whole surface,
|
|
896
|
+
* which reads as two controls. The tile is what the pointer and the keyboard
|
|
897
|
+
* are addressing, so the tile is what the ring is about.
|
|
898
|
+
*/
|
|
899
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile']:has(
|
|
900
|
+
[data-part='control']:focus-visible
|
|
901
|
+
) {
|
|
902
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
903
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'] [data-part='control']:focus-visible {
|
|
907
|
+
outline: none;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/* ── Forced colors ────────────────────────────────────────────────────
|
|
911
|
+
*
|
|
912
|
+
* The one place this family paints something the theme cannot reach. Forced
|
|
913
|
+
* colors overrides the box's `background-color` and the mark's `color`, so
|
|
914
|
+
* the mark and the ground it stands on would be forced to one colour and the
|
|
915
|
+
* check would disappear into the box it is drawn in. The pair is restated here in system
|
|
916
|
+
* colours, which is what the briefing allows a family to do exactly when the
|
|
917
|
+
* theme cannot.
|
|
918
|
+
*/
|
|
919
|
+
@media (forced-colors: active) {
|
|
920
|
+
[data-ui='checkbox'] [data-part='control'] {
|
|
921
|
+
background-color: Field;
|
|
922
|
+
border-color: FieldText;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
[data-ui='checkbox'] [data-part='control']:checked,
|
|
926
|
+
[data-ui='checkbox'] [data-part='control']:indeterminate {
|
|
927
|
+
background-color: Highlight;
|
|
928
|
+
border-color: Highlight;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/*
|
|
932
|
+
* A checked invalid box keeps the checked pair. The theme maps the danger
|
|
933
|
+
* fill to `ButtonFace`, so the invalid rule above painted fill, border and
|
|
934
|
+
* mark in the page's white and the box looked unchecked. The wider border
|
|
935
|
+
* takes `CanvasText`, and the parting line, a shadow forced colours remove,
|
|
936
|
+
* comes back as an outline in the page's colour inside it; a focused one
|
|
937
|
+
* draws its ring double.
|
|
938
|
+
*/
|
|
939
|
+
[data-ui='checkbox'][data-part='root'][data-invalid] [data-part='control']:checked,
|
|
940
|
+
[data-ui='checkbox'][data-part='root'][data-invalid] [data-part='control']:indeterminate {
|
|
941
|
+
background-color: Highlight;
|
|
942
|
+
border-color: CanvasText;
|
|
943
|
+
outline: var(
|
|
944
|
+
--slotted-checkbox-invalid-edge-width,
|
|
945
|
+
var(--slotted-control-invalid-edge-width, 1px)
|
|
946
|
+
)
|
|
947
|
+
solid Canvas;
|
|
948
|
+
outline-offset: calc(
|
|
949
|
+
-1 *
|
|
950
|
+
(
|
|
951
|
+
var(--slotted-checkbox-border-width, var(--slotted-border-width-medium, 1.5px)) +
|
|
952
|
+
var(
|
|
953
|
+
--slotted-checkbox-invalid-edge-width,
|
|
954
|
+
var(--slotted-control-invalid-edge-width, 1px)
|
|
955
|
+
) +
|
|
956
|
+
var(
|
|
957
|
+
--slotted-checkbox-invalid-edge-width,
|
|
958
|
+
var(--slotted-control-invalid-edge-width, 1px)
|
|
959
|
+
)
|
|
960
|
+
)
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
[data-ui='checkbox'][data-part='root'][data-invalid]
|
|
965
|
+
[data-part='control']:checked:focus-visible,
|
|
966
|
+
[data-ui='checkbox'][data-part='root'][data-invalid]
|
|
967
|
+
[data-part='control']:indeterminate:focus-visible {
|
|
968
|
+
outline: calc(
|
|
969
|
+
var(--slotted-focus-ring-width, 2px) +
|
|
970
|
+
var(
|
|
971
|
+
--slotted-checkbox-invalid-edge-width,
|
|
972
|
+
var(--slotted-control-invalid-edge-width, 1px)
|
|
973
|
+
)
|
|
974
|
+
)
|
|
975
|
+
double var(--slotted-focus-ring-color, Highlight);
|
|
976
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
[data-ui='checkbox'] [data-part='control']:checked + [data-part='indicator'],
|
|
980
|
+
[data-ui='checkbox'] [data-part='control']:indeterminate + [data-part='indicator'] {
|
|
981
|
+
color: HighlightText;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
[data-ui='checkbox'][data-part='root'][data-invalid]
|
|
985
|
+
[data-part='control']:checked
|
|
986
|
+
+ [data-part='indicator'],
|
|
987
|
+
[data-ui='checkbox'][data-part='root'][data-invalid]
|
|
988
|
+
[data-part='control']:indeterminate
|
|
989
|
+
+ [data-part='indicator'] {
|
|
990
|
+
color: HighlightText;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/* A tile's invalid edge is the danger pair, which the theme maps to the
|
|
994
|
+
page's white; it takes the system's text colour instead. */
|
|
995
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'][data-invalid],
|
|
996
|
+
[data-ui='checkbox'][data-part='root'][data-variant='tile'][data-invalid]:has(
|
|
997
|
+
[data-part='control']:is(:checked, :indeterminate)
|
|
998
|
+
) {
|
|
999
|
+
--_tile-edge: CanvasText;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
[data-ui='checkbox'] [data-part='control']:disabled {
|
|
1003
|
+
background-color: Field;
|
|
1004
|
+
border-color: GrayText;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
[data-ui='checkbox'] [data-part='control']:disabled + [data-part='indicator'] {
|
|
1008
|
+
color: GrayText;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|