@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,707 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── The field ──────────────────────────────────────────────────────────
|
|
4
|
+
*
|
|
5
|
+
* One control, its name, its necessity, its help and its error. The box has
|
|
6
|
+
* no ground and no border of its own: a field inherits the rhythm of whatever
|
|
7
|
+
* placed it — a `Stack`, a `field-group`, a table cell — and everything it
|
|
8
|
+
* draws is text beside a control it does not own.
|
|
9
|
+
*
|
|
10
|
+
* The grid is two columns and every child spans both, so the only parts that
|
|
11
|
+
* need a rule of their own are the two that do not: the label takes the first
|
|
12
|
+
* column of the first row, and the counter the second. That is what lets the
|
|
13
|
+
* description and the error be another family's elements — they are
|
|
14
|
+
* `inline-message` roots, they carry `data-ui='inline-message'`, and the field
|
|
15
|
+
* places them without ever selecting them.
|
|
16
|
+
*
|
|
17
|
+
* The field is its own container. `labelPlacement='side'` is a layout that
|
|
18
|
+
* depends on how wide the field is rather than on how wide the window is, and
|
|
19
|
+
* a field in a 240-pixel drawer has to stack while the same field in a page
|
|
20
|
+
* grid stays beside its control.
|
|
21
|
+
*/
|
|
22
|
+
[data-ui='field'][data-part='root'] {
|
|
23
|
+
/**
|
|
24
|
+
* Type, control height and glyph come from the control scales, the only
|
|
25
|
+
* ones the theme restates per density, so a field in a dense table shrinks
|
|
26
|
+
* whole rather than in parts. The control height is read for one reason:
|
|
27
|
+
* the side label is centred on the control's first line, and that is the
|
|
28
|
+
* only distance in the family that depends on the control's own size.
|
|
29
|
+
*/
|
|
30
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
31
|
+
--_control-block-size: var(--slotted-control-height-md, 34px);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The distance between the label, the control and the message. One value
|
|
35
|
+
* for all three rows: a label closer to its control than the message is
|
|
36
|
+
* would say the message belongs to the field below, which is the mistake a
|
|
37
|
+
* form makes exactly once and then everywhere.
|
|
38
|
+
*/
|
|
39
|
+
--_row-gap: var(--slotted-field-row-gap, var(--slotted-space-3, 0.375rem));
|
|
40
|
+
/** The distance from a side label to the control it names. */
|
|
41
|
+
--_column-gap: var(--slotted-field-column-gap, var(--slotted-space-5, 0.75rem));
|
|
42
|
+
|
|
43
|
+
/* The field is as tall as its rows. Stretched — a grid track taller than
|
|
44
|
+
its content, a dialog body filling a phone's height — the rows would
|
|
45
|
+
share the surplus and stand the label a finger's width from the control
|
|
46
|
+
it names. The surplus belongs to whatever stretched the field. */
|
|
47
|
+
align-content: start;
|
|
48
|
+
container-name: slotted-field;
|
|
49
|
+
container-type: inline-size;
|
|
50
|
+
display: grid;
|
|
51
|
+
font-family: var(--slotted-field-font-family, var(--slotted-control-font-family, sans-serif));
|
|
52
|
+
/**
|
|
53
|
+
* Three tracks, stated once and never restated. An element cannot query its
|
|
54
|
+
* own container — `container-type` makes a box a container for what is
|
|
55
|
+
* *inside* it — so a rule that changed the template under `@container`
|
|
56
|
+
* would apply to nothing, silently, while the child rules beside it applied
|
|
57
|
+
* normally. The picture would still look right at one width and be laid out
|
|
58
|
+
* on the wrong tracks at every other, which is what the first capture of
|
|
59
|
+
* this family showed: a label holding 235 pixels and its control 185.
|
|
60
|
+
*
|
|
61
|
+
* So the tracks never change, and only the placement of the children does.
|
|
62
|
+
* With the label above the control every child spans all three, and the
|
|
63
|
+
* split between the first two is invisible; with the label beside it, the
|
|
64
|
+
* first track is the label's and the cap is what stops a long one from
|
|
65
|
+
* taking the row.
|
|
66
|
+
*
|
|
67
|
+
* There is no `column-gap`. A gutter beside an empty counter track would
|
|
68
|
+
* inset every label by that distance for a part that is not there, so the
|
|
69
|
+
* two parts that need separation carry it as their own padding.
|
|
70
|
+
*/
|
|
71
|
+
grid-template-columns:
|
|
72
|
+
minmax(0, var(--slotted-field-label-inline-size, 12rem))
|
|
73
|
+
minmax(0, 1fr)
|
|
74
|
+
auto;
|
|
75
|
+
/* A field inside a grid track narrower than its content would otherwise
|
|
76
|
+
refuse to shrink, and take the control's placeholder with it. */
|
|
77
|
+
min-inline-size: 0;
|
|
78
|
+
row-gap: var(--_row-gap);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Every child spans the field, and the two that do not say so below. */
|
|
82
|
+
[data-ui='field'][data-part='root'] > * {
|
|
83
|
+
grid-column: 1 / -1;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The whole of what the field does to the control it does not own. A grid item
|
|
88
|
+
* refuses to shrink below its own intrinsic width — about twenty characters
|
|
89
|
+
* for an `input` — so a field in a 240-pixel drawer would overflow the drawer
|
|
90
|
+
* rather than the control. Nothing here paints the control, sizes its type or
|
|
91
|
+
* touches its border: `text-field` owns all three.
|
|
92
|
+
*/
|
|
93
|
+
[data-ui='field'] > [data-part='control'] {
|
|
94
|
+
min-inline-size: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ── Size ─────────────────────────────────────────────────────────────
|
|
98
|
+
*
|
|
99
|
+
* Three steps, matching the control the field wraps. Fluent ships the same
|
|
100
|
+
* three; Angular Material ships none and sizes its field from the control
|
|
101
|
+
* inside it, which makes a field with no control an unsized box.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
[data-ui='field'][data-part='root'][data-size='sm'] {
|
|
105
|
+
--_help-glyph: max(
|
|
106
|
+
var(--slotted-field-icon-size-min, 14px),
|
|
107
|
+
var(--slotted-control-icon-size-sm, 14px)
|
|
108
|
+
);
|
|
109
|
+
--_help-inner-size: var(--slotted-control-inner-size-sm, 22px);
|
|
110
|
+
--_font-size: var(--slotted-control-font-size-sm, 12px);
|
|
111
|
+
--_control-block-size: var(--slotted-control-height-sm, 30px);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-ui='field'][data-part='root'][data-size='md'] {
|
|
115
|
+
--_help-glyph: max(
|
|
116
|
+
var(--slotted-field-icon-size-min, 14px),
|
|
117
|
+
var(--slotted-control-icon-size-sm, 14px)
|
|
118
|
+
);
|
|
119
|
+
--_help-inner-size: var(--slotted-control-inner-size-sm, 22px);
|
|
120
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
121
|
+
--_control-block-size: var(--slotted-control-height-md, 34px);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
[data-ui='field'][data-part='root'][data-size='lg'] {
|
|
125
|
+
--_help-glyph: max(
|
|
126
|
+
var(--slotted-field-icon-size-min, 14px),
|
|
127
|
+
var(--slotted-control-icon-size-md, 16px)
|
|
128
|
+
);
|
|
129
|
+
--_help-inner-size: var(--slotted-control-inner-size-md, 24px);
|
|
130
|
+
--_font-size: var(--slotted-control-font-size-lg, 14px);
|
|
131
|
+
--_control-block-size: var(--slotted-control-height-lg, 38px);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* ── The label ────────────────────────────────────────────────────────
|
|
135
|
+
*
|
|
136
|
+
* A native `label`, carrying `for`. The APG's naming practice calls the
|
|
137
|
+
* explicit association the one with the broadest support, and the implicit
|
|
138
|
+
* form — wrapping the control — the one some assistive technologies fail to
|
|
139
|
+
* treat as a name at all.
|
|
140
|
+
*
|
|
141
|
+
* The leading is the text scale rather than the control's `1`: a control's
|
|
142
|
+
* line height is for a box holding one line, and a label of twelve words
|
|
143
|
+
* beside a narrow control holds three.
|
|
144
|
+
*/
|
|
145
|
+
[data-ui='field'] > [data-part='label'],
|
|
146
|
+
[data-ui='field'] > [data-part='header'] > [data-part='label'] {
|
|
147
|
+
color: var(--slotted-field-label-color, var(--slotted-text));
|
|
148
|
+
font-size: var(--_font-size);
|
|
149
|
+
font-weight: var(--slotted-field-label-font-weight, var(--slotted-font-weight-medium, 500));
|
|
150
|
+
line-height: var(--slotted-field-line-height, var(--slotted-line-height-sm, 1.45));
|
|
151
|
+
min-inline-size: 0;
|
|
152
|
+
/* A compound noun no language can break — a German insurance number, a
|
|
153
|
+
column name from a database — otherwise runs out of the label's track and
|
|
154
|
+
under the control, which is where the first capture of this family put
|
|
155
|
+
`Kraftfahrzeug-Haftpflichtversicherungsnummer`. Nothing is removed: the
|
|
156
|
+
whole word stays in the DOM and in the accessible name. */
|
|
157
|
+
overflow-wrap: anywhere;
|
|
158
|
+
transition: color var(--slotted-control-transition-duration)
|
|
159
|
+
var(--slotted-control-transition-easing);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The first row is the label's, or the header's when the label has company.
|
|
164
|
+
* Either spans the field when the label sits above the control.
|
|
165
|
+
*/
|
|
166
|
+
[data-ui='field'] > [data-part='label'],
|
|
167
|
+
[data-ui='field'] > [data-part='header'] {
|
|
168
|
+
grid-column: 1 / -1;
|
|
169
|
+
grid-row: 1;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The label stops before the counter's track only when there is a counter.
|
|
174
|
+
* Two grid items in one cell overlap, and a field with a count would print its
|
|
175
|
+
* label under it; a field without one would otherwise lose the counter's track
|
|
176
|
+
* from the label's width for a part that is not there.
|
|
177
|
+
*/
|
|
178
|
+
[data-ui='field'][data-part='root']:has(> [data-part='counter']) > [data-part='label'] {
|
|
179
|
+
grid-column: 1 / 3;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The necessity marker. Real text rather than generated content, because the
|
|
184
|
+
* two word forms — `(required)` and `(optional)` — are information a reader
|
|
185
|
+
* hears and a page search finds, and because generated content cannot be
|
|
186
|
+
* translated by an application.
|
|
187
|
+
*
|
|
188
|
+
* The glyph form is the one this rule paints. It takes the danger ink because
|
|
189
|
+
* a red asterisk is the convention every surveyed implementation follows, and
|
|
190
|
+
* the field deliberately does *not* repaint the label when it is invalid: a
|
|
191
|
+
* red marker on a red label is a marker nobody can see, and the error already
|
|
192
|
+
* says the same thing in words, in colour and in a glyph.
|
|
193
|
+
*/
|
|
194
|
+
[data-ui='field'] > [data-part='label'] > [data-part='mark'],
|
|
195
|
+
[data-ui='field'] > [data-part='header'] > [data-part='label'] > [data-part='mark'] {
|
|
196
|
+
color: var(--slotted-field-mark-color, var(--slotted-tone-danger-text));
|
|
197
|
+
margin-inline-start: var(--slotted-field-mark-gap, var(--slotted-space-2, 0.25rem));
|
|
198
|
+
/* The label breaks anywhere; the necessity word never does. `(required)`
|
|
199
|
+
split across two lines as `(re` and `quired)` in the 240-pixel capture,
|
|
200
|
+
because a rule for one unbreakable German noun had reached the one part
|
|
201
|
+
of the label that is always a word. */
|
|
202
|
+
overflow-wrap: normal;
|
|
203
|
+
/**
|
|
204
|
+
* Its own bidi run. Latin label text in a right-to-left document forms one
|
|
205
|
+
* left-to-right run, and the marker joined it — so the distance written as
|
|
206
|
+
* `margin-inline-start` landed outside the run instead of between the two,
|
|
207
|
+
* and `CNPJ` and `(required)` touched. Isolated, the marker is ordered by
|
|
208
|
+
* the field's direction rather than by the label's characters.
|
|
209
|
+
*/
|
|
210
|
+
unicode-bidi: isolate;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The word forms are not a severity, so they are not painted like one. Each is
|
|
215
|
+
* the quietest thing in the row: muted ink, the regular weight the label is
|
|
216
|
+
* not, and one type step down, so `E-mail (optional)` reads as one label with
|
|
217
|
+
* a qualifier rather than as two labels. `optional` writes the word only on
|
|
218
|
+
* the fields a reader may skip; `words` writes both.
|
|
219
|
+
*/
|
|
220
|
+
[data-ui='field']:is([data-necessity='optional'], [data-necessity='words'])
|
|
221
|
+
> [data-part='label']
|
|
222
|
+
> [data-part='mark'],
|
|
223
|
+
[data-ui='field']:is([data-necessity='optional'], [data-necessity='words'])
|
|
224
|
+
> [data-part='header']
|
|
225
|
+
> [data-part='label']
|
|
226
|
+
> [data-part='mark'] {
|
|
227
|
+
color: var(--slotted-field-mark-text-color, var(--slotted-text-muted));
|
|
228
|
+
font-size: var(--slotted-control-font-size-sm, 12px);
|
|
229
|
+
font-weight: var(--slotted-font-weight-regular, 400);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* ── The header ───────────────────────────────────────────────────────
|
|
233
|
+
*
|
|
234
|
+
* The label row, when the label has company: the help behind an icon, an
|
|
235
|
+
* indicator such as `Beta`, an action such as `Forgot password?`, and the
|
|
236
|
+
* counter. ReUI writes the same row by hand in four of its examples — a
|
|
237
|
+
* `div` with `justify-between` around the label — and centres every item in
|
|
238
|
+
* it; here the row is a part, and it aligns its items on the label's first
|
|
239
|
+
* baseline instead, so a 13-pixel label, a 12-pixel count and an emblem with
|
|
240
|
+
* a box of its own sit on one line of text rather than on one centre.
|
|
241
|
+
*
|
|
242
|
+
* The row takes the label's type, so `1lh`, `1cap` and `1ex` measured by
|
|
243
|
+
* anything inside it are the label's line. It wraps: in a narrow field the
|
|
244
|
+
* action and the counter move to a second line, still at its end.
|
|
245
|
+
*/
|
|
246
|
+
[data-ui='field'] > [data-part='header'] {
|
|
247
|
+
align-items: baseline;
|
|
248
|
+
column-gap: var(--slotted-field-header-gap, var(--slotted-space-3, 0.375rem));
|
|
249
|
+
display: flex;
|
|
250
|
+
flex-wrap: wrap;
|
|
251
|
+
font-size: var(--_font-size);
|
|
252
|
+
line-height: var(--slotted-field-line-height, var(--slotted-line-height-sm, 1.45));
|
|
253
|
+
min-inline-size: 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
[data-ui='field'] > [data-part='header'] > [data-part='label'] {
|
|
257
|
+
flex: 0 1 auto;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* The action and the counter belong to the end of the row. The first of the
|
|
262
|
+
* two that is present takes the free space; a counter after an action stands
|
|
263
|
+
* a second step of the row gap away from it, twelve pixels in all, the same
|
|
264
|
+
* distance a side label keeps from its control, so a link and a number do
|
|
265
|
+
* not read as one phrase.
|
|
266
|
+
*/
|
|
267
|
+
[data-ui='field'] > [data-part='header'] > [data-part='action'],
|
|
268
|
+
[data-ui='field'] > [data-part='header'] > [data-part='counter'] {
|
|
269
|
+
margin-inline-start: auto;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
[data-ui='field'] > [data-part='header'] > [data-part='action'] ~ [data-part='counter'] {
|
|
273
|
+
margin-inline-start: var(--slotted-space-3, 0.375rem);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* ── The help ─────────────────────────────────────────────────────────
|
|
277
|
+
*
|
|
278
|
+
* A glyph beside the label that opens the tooltip holding the field's help.
|
|
279
|
+
* It is an inner control (R2, `inner-control.test.mjs`): no frame, no ground
|
|
280
|
+
* at rest, and hover and press move the ground one step each and nothing
|
|
281
|
+
* else — ReUI's trigger changes its ink on hover and draws the same picture
|
|
282
|
+
* on press, which is the press the craft bar fails.
|
|
283
|
+
*
|
|
284
|
+
* The box is the glyph and a step of air a side, capped at the inside of a
|
|
285
|
+
* control at the field's step (`--slotted-control-inner-size-*`), the box
|
|
286
|
+
* `tag` gives its remove; the pointer target is a transparent halo that
|
|
287
|
+
* reaches twenty-four pixels around it. The box holds one line of the
|
|
288
|
+
* label's type at its own height and gives the difference back in the block
|
|
289
|
+
* axis, so a field with a help icon is exactly as tall as one without. The
|
|
290
|
+
* line inside it is the label's own — `font: inherit` — and a `button`
|
|
291
|
+
* centres its content in the block axis, so the button's baseline stands
|
|
292
|
+
* where the label's does; a line as tall as the box put it 0.44 pixels lower
|
|
293
|
+
* at comfortable and 0.69 at dense, and the row grew by that much. Only
|
|
294
|
+
* the block axis gives it back: a negative inline margin would pull the box
|
|
295
|
+
* under the last word of the label and strike the focus ring through it,
|
|
296
|
+
* which is what `description-list` measured on its copy control. The row gap
|
|
297
|
+
* before it is taken down to one step instead, so the glyph stands six
|
|
298
|
+
* pixels from the label at `md`.
|
|
299
|
+
*
|
|
300
|
+
* The glyph is placed on the label's text rather than on its line box: the
|
|
301
|
+
* button holds one line of the label's type, and the glyph sits on that
|
|
302
|
+
* line's baseline and centred on the band between the middle of the
|
|
303
|
+
* capitals and the middle of the lowercase — `(1cap + 1ex) / 2` — the band
|
|
304
|
+
* `toast` settled on (`docs/build-briefing.md`, a mark beside a line of
|
|
305
|
+
* text). The first capture raised the glyph's bottom by the band less half
|
|
306
|
+
* the glyph, which put its centre on the band's top: four pixels high, and a
|
|
307
|
+
* line box 3.3 pixels taller than the label's.
|
|
308
|
+
*/
|
|
309
|
+
[data-ui='field'] > [data-part='header'] > [data-part='help'] {
|
|
310
|
+
/* Never shorter than the label's line: at dense the inside of a control is
|
|
311
|
+
16 pixels and the line 17.39, and a button shorter than its own line
|
|
312
|
+
overflows downward and pushes the row 0.69 pixels taller. */
|
|
313
|
+
--_box: max(
|
|
314
|
+
min(calc(var(--_help-glyph) + 2 * var(--slotted-space-2, 0.25rem)), var(--_help-inner-size)),
|
|
315
|
+
1lh
|
|
316
|
+
);
|
|
317
|
+
--slotted-icon-size: var(--_help-glyph);
|
|
318
|
+
|
|
319
|
+
appearance: none;
|
|
320
|
+
background-color: transparent;
|
|
321
|
+
block-size: var(--_box);
|
|
322
|
+
border: 0;
|
|
323
|
+
border-radius: var(--slotted-field-help-radius, var(--slotted-control-radius, 4px));
|
|
324
|
+
color: var(--slotted-field-help-color, var(--slotted-text-muted));
|
|
325
|
+
cursor: pointer;
|
|
326
|
+
display: inline-block;
|
|
327
|
+
flex: none;
|
|
328
|
+
font: inherit;
|
|
329
|
+
inline-size: var(--_box);
|
|
330
|
+
margin-block: calc(
|
|
331
|
+
(
|
|
332
|
+
var(--_font-size) *
|
|
333
|
+
var(--slotted-field-line-height, var(--slotted-line-height-sm, 1.45)) - var(--_box)
|
|
334
|
+
) /
|
|
335
|
+
2
|
|
336
|
+
);
|
|
337
|
+
margin-inline-start: calc(
|
|
338
|
+
var(--slotted-space-1, 0.125rem) -
|
|
339
|
+
var(--slotted-field-header-gap, var(--slotted-space-3, 0.375rem))
|
|
340
|
+
);
|
|
341
|
+
padding: 0;
|
|
342
|
+
position: relative;
|
|
343
|
+
text-align: center;
|
|
344
|
+
transition: background-color var(--slotted-control-transition-duration)
|
|
345
|
+
var(--slotted-control-transition-easing);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* The pointer target: a transparent square centred on the box that reaches
|
|
349
|
+
the WCAG minimum wherever the box falls short of it — 22 pixels at
|
|
350
|
+
comfortable, 16 at dense. It takes no room, so the row keeps its height. */
|
|
351
|
+
[data-ui='field'] > [data-part='header'] > [data-part='help']::before {
|
|
352
|
+
content: '';
|
|
353
|
+
inset: min(0px, calc((100% - var(--slotted-target-min, 24px)) / 2));
|
|
354
|
+
position: absolute;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
[data-ui='field'] > [data-part='header'] > [data-part='help'] > [data-ui='icon'] {
|
|
358
|
+
vertical-align: calc(((1cap + 1ex) / 2 - var(--_help-glyph)) / 2);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Hover and press move the ground and nothing else. An open hint keeps the
|
|
363
|
+
* hover ground while its tooltip is showing, so the reader sees which icon
|
|
364
|
+
* the layer belongs to after the pointer has moved onto the layer.
|
|
365
|
+
*/
|
|
366
|
+
[data-ui='field'] > [data-part='header'] > [data-part='help']:enabled:hover,
|
|
367
|
+
[data-ui='field'] > [data-part='header'] > [data-part='help'][data-open] {
|
|
368
|
+
background-color: var(
|
|
369
|
+
--slotted-field-help-surface-hover,
|
|
370
|
+
var(--slotted-tone-secondary-subtle-hover, ButtonFace)
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
[data-ui='field'] > [data-part='header'] > [data-part='help']:enabled:active {
|
|
375
|
+
background-color: var(
|
|
376
|
+
--slotted-field-help-surface-active,
|
|
377
|
+
var(--slotted-tone-secondary-subtle-active, ButtonFace)
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
[data-ui='field'] > [data-part='header'] > [data-part='help']:focus-visible {
|
|
382
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
383
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* ── The indicator ────────────────────────────────────────────────────
|
|
387
|
+
*
|
|
388
|
+
* What qualifies the field — `Beta`, `Pro`, `New` — as one or more `badge`s
|
|
389
|
+
* after the label. It stands outside the `label` so the necessity mark stays
|
|
390
|
+
* against the label's last word, and it is read with the control's
|
|
391
|
+
* description rather than with its name. The badges are the badge family's;
|
|
392
|
+
* the field only spaces them.
|
|
393
|
+
*/
|
|
394
|
+
[data-ui='field'] > [data-part='header'] > [data-part='indicator'] {
|
|
395
|
+
align-items: center;
|
|
396
|
+
/* A badge is a box, not a line of text. Set on the label's baseline, its
|
|
397
|
+
own inset stood it 1.77 pixels above the label's line and the row grew
|
|
398
|
+
by that much; centred on the line, an 18.8-pixel badge fits in the
|
|
399
|
+
18.85-pixel line and the row is the label's height with or without it. */
|
|
400
|
+
align-self: center;
|
|
401
|
+
column-gap: var(--slotted-space-2, 0.25rem);
|
|
402
|
+
display: inline-flex;
|
|
403
|
+
flex-wrap: wrap;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* ── The action ───────────────────────────────────────────────────────
|
|
407
|
+
*
|
|
408
|
+
* A link or a button at the end of the label row: `Forgot password?`,
|
|
409
|
+
* `Generate`, `Use the billing address`. It is a text control, so it speaks
|
|
410
|
+
* the vocabulary `link` settled for text that answers a pointer: accent ink
|
|
411
|
+
* at rest, a hover a third of the way to the body colour with the underline
|
|
412
|
+
* drawn, and a press two thirds of the way. ReUI paints the same action in
|
|
413
|
+
* muted ink, where it reads as inert text beside the counter, and answers
|
|
414
|
+
* press with a one-pixel shift of a line of 12-pixel type.
|
|
415
|
+
*
|
|
416
|
+
* The box is 24 pixels in the block axis for the pointer and gives the line
|
|
417
|
+
* its height back; in the inline axis the text is the box, so the focus ring
|
|
418
|
+
* hugs the words. It is a flex box so its height is stated rather than summed
|
|
419
|
+
* from a padding and a line — the sum measured 23.98 — and a flex box's
|
|
420
|
+
* baseline is its words', so the row still aligns on it.
|
|
421
|
+
*/
|
|
422
|
+
[data-ui='field'] > [data-part='header'] > [data-part='action'] {
|
|
423
|
+
--_rest: var(--slotted-field-action-color, var(--slotted-tone-accent-text, LinkText));
|
|
424
|
+
|
|
425
|
+
align-items: center;
|
|
426
|
+
appearance: none;
|
|
427
|
+
background: none;
|
|
428
|
+
block-size: var(--slotted-target-min, 24px);
|
|
429
|
+
border: 0;
|
|
430
|
+
border-radius: var(--slotted-field-action-radius, var(--slotted-control-radius, 4px));
|
|
431
|
+
color: var(--_rest);
|
|
432
|
+
cursor: pointer;
|
|
433
|
+
display: inline-flex;
|
|
434
|
+
flex: none;
|
|
435
|
+
font-family: inherit;
|
|
436
|
+
font-size: var(--slotted-control-font-size-sm, 12px);
|
|
437
|
+
font-weight: var(--slotted-font-weight-regular, 400);
|
|
438
|
+
line-height: var(--slotted-field-line-height, var(--slotted-line-height-sm, 1.45));
|
|
439
|
+
margin-block: calc((1lh - var(--slotted-target-min, 24px)) / 2);
|
|
440
|
+
padding: 0;
|
|
441
|
+
text-decoration-color: transparent;
|
|
442
|
+
text-decoration-line: underline;
|
|
443
|
+
text-decoration-thickness: from-font;
|
|
444
|
+
text-underline-offset: 0.15em;
|
|
445
|
+
transition:
|
|
446
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing),
|
|
447
|
+
text-decoration-color var(--slotted-control-transition-duration)
|
|
448
|
+
var(--slotted-control-transition-easing);
|
|
449
|
+
/* Ordered by its own words rather than by the page's direction. A Latin
|
|
450
|
+
`Forgot password?` on a right-to-left page printed its question mark
|
|
451
|
+
first, because the run took the page's direction; an Arabic action on
|
|
452
|
+
the same page is still ordered right to left. */
|
|
453
|
+
unicode-bidi: plaintext;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
[data-ui='field']
|
|
457
|
+
> [data-part='header']
|
|
458
|
+
> [data-part='action']:not(:disabled, [aria-disabled='true']):hover {
|
|
459
|
+
color: var(
|
|
460
|
+
--slotted-field-action-color-hover,
|
|
461
|
+
color-mix(in oklab, var(--_rest) 65%, var(--slotted-text, CanvasText))
|
|
462
|
+
);
|
|
463
|
+
text-decoration-color: currentColor;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
[data-ui='field']
|
|
467
|
+
> [data-part='header']
|
|
468
|
+
> [data-part='action']:not(:disabled, [aria-disabled='true']):active {
|
|
469
|
+
color: var(
|
|
470
|
+
--slotted-field-action-color-active,
|
|
471
|
+
color-mix(in oklab, var(--_rest) 35%, var(--slotted-text, CanvasText))
|
|
472
|
+
);
|
|
473
|
+
text-decoration-color: currentColor;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
[data-ui='field'] > [data-part='header'] > [data-part='action']:focus-visible {
|
|
477
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
478
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
479
|
+
text-decoration-color: currentColor;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
[data-ui='field']
|
|
483
|
+
> [data-part='header']
|
|
484
|
+
> [data-part='action']:is(:disabled, [aria-disabled='true']) {
|
|
485
|
+
color: var(--slotted-disabled-foreground, GrayText);
|
|
486
|
+
cursor: not-allowed;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* On a finger both controls of the row reach the comfortable target with a
|
|
491
|
+
* transparent halo centred on the box. The halo takes no room, so the row
|
|
492
|
+
* keeps its height and the ground and the ring stay the box a mouse sees.
|
|
493
|
+
*/
|
|
494
|
+
@media (pointer: coarse) {
|
|
495
|
+
[data-ui='field'] > [data-part='header'] > [data-part='action'] {
|
|
496
|
+
position: relative;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
[data-ui='field'] > [data-part='header'] > [data-part='help']::before,
|
|
500
|
+
[data-ui='field'] > [data-part='header'] > [data-part='action']::before {
|
|
501
|
+
content: '';
|
|
502
|
+
inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
|
|
503
|
+
position: absolute;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/* ── The counter ──────────────────────────────────────────────────────
|
|
508
|
+
*
|
|
509
|
+
* A count the consumer owns — `text-field` will own it in wave 3 — sitting at
|
|
510
|
+
* the end of the label row, which is where Carbon puts it. Angular Material
|
|
511
|
+
* puts it in the subscript beside the error, where a count that appears on the
|
|
512
|
+
* first keystroke shares a row with the message and pushes it.
|
|
513
|
+
*
|
|
514
|
+
* Tabular figures, because a counter that jitters as it counts is a counter
|
|
515
|
+
* the eye follows instead of the field.
|
|
516
|
+
*/
|
|
517
|
+
[data-ui='field'] > [data-part='counter'],
|
|
518
|
+
[data-ui='field'] > [data-part='header'] > [data-part='counter'] {
|
|
519
|
+
color: var(--slotted-field-counter-color, var(--slotted-text-muted));
|
|
520
|
+
font-size: var(--slotted-control-font-size-sm, 12px);
|
|
521
|
+
font-variant-numeric: tabular-nums;
|
|
522
|
+
line-height: var(--slotted-field-line-height, var(--slotted-line-height-sm, 1.45));
|
|
523
|
+
transition: color var(--slotted-control-transition-duration)
|
|
524
|
+
var(--slotted-control-transition-easing);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* The counter the family draws on a field with no header: the end of the
|
|
529
|
+
* label row, in the third track, apart from the label by the side gap.
|
|
530
|
+
*/
|
|
531
|
+
[data-ui='field'] > [data-part='counter'] {
|
|
532
|
+
align-self: end;
|
|
533
|
+
grid-column: 3;
|
|
534
|
+
grid-row: 1;
|
|
535
|
+
justify-self: end;
|
|
536
|
+
padding-inline-start: var(--_column-gap);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Past the limit. The counter says so before the form does: a count can pass
|
|
541
|
+
* its maximum while validation has not yet run, and a counter that only turned
|
|
542
|
+
* at submit would say nothing at the moment it is being read.
|
|
543
|
+
*/
|
|
544
|
+
[data-ui='field'] > [data-part='counter'][data-invalid],
|
|
545
|
+
[data-ui='field'] > [data-part='header'] > [data-part='counter'][data-invalid] {
|
|
546
|
+
color: var(--slotted-field-counter-invalid-color, var(--slotted-tone-danger-text));
|
|
547
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* ── The messages ─────────────────────────────────────────────────────
|
|
551
|
+
*
|
|
552
|
+
* `inline-message` roots, placed by the grid and styled by their own family.
|
|
553
|
+
* The field adds the rules below: one withdraws, and two set the type of
|
|
554
|
+
* what a field says — the size and, for an outcome, the weight.
|
|
555
|
+
*
|
|
556
|
+
* A message with nothing to say is withdrawn rather than left as an empty
|
|
557
|
+
* line. `inline-message` already withdraws its mark and its severity word in
|
|
558
|
+
* that case; what it cannot do from inside is remove its own row from a grid
|
|
559
|
+
* it knows nothing about. `[data-reserve]` is the deliberate exception — a
|
|
560
|
+
* form that renders its error unconditionally so nothing below it moves when
|
|
561
|
+
* the error arrives.
|
|
562
|
+
*/
|
|
563
|
+
[data-ui='field'] > [data-ui='inline-message']:not([data-reserve]):has([data-part='text']:empty) {
|
|
564
|
+
display: none;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* What a field says under its control, and how (F1). The type is one step
|
|
569
|
+
* under the smallest control type, from the type scale rather than the
|
|
570
|
+
* control scale: a message is quieter than the control it is about at every
|
|
571
|
+
* size the field takes, and it does not grow with the control. The message's
|
|
572
|
+
* own `size` still places its mark — sized in `em`, it follows this.
|
|
573
|
+
*
|
|
574
|
+
* The selector carries the root's part so it outweighs the message family's
|
|
575
|
+
* own root rule, which has the same two attributes; equal specificity would
|
|
576
|
+
* leave the winner to stylesheet order.
|
|
577
|
+
*/
|
|
578
|
+
[data-ui='field'][data-part='root'] > [data-ui='inline-message'] {
|
|
579
|
+
font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* An outcome — the value is wrong, or it was accepted — is set a weight up
|
|
584
|
+
* from the help under it. The help is muted ink at the regular weight and
|
|
585
|
+
* draws no mark; the outcome draws the tone's own silhouette at the type's
|
|
586
|
+
* size (`inline-message`, R5) and its own ink, and the weight is what makes
|
|
587
|
+
* the two read as two kinds of sentence rather than one.
|
|
588
|
+
*/
|
|
589
|
+
[data-ui='field'][data-part='root'] > [data-ui='inline-message'][data-tone='danger'],
|
|
590
|
+
[data-ui='field'][data-part='root'] > [data-ui='inline-message'][data-tone='success'] {
|
|
591
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/* ── Placement: the label above the control, or beside it ─────────────
|
|
595
|
+
*
|
|
596
|
+
* The axis Fluent calls `orientation`, Primer `layout` and Spectrum
|
|
597
|
+
* `labelPosition`. A leading track for the label or the header, everything
|
|
598
|
+
* else in the second, and the counter at the end of the label's track.
|
|
599
|
+
*
|
|
600
|
+
* `--slotted-field-label-inline-size` is the one literal in the family, and
|
|
601
|
+
* it is a line length rather than a step on a scale: `12rem` is the width at
|
|
602
|
+
* which a label of about twenty characters at the `md` step sits on one line.
|
|
603
|
+
* No scale in `docs/token-foundation.md` holds a line length, and inventing
|
|
604
|
+
* one for a single family is what the component layer's own rule forbids.
|
|
605
|
+
*/
|
|
606
|
+
@container slotted-field (min-width: 320px) {
|
|
607
|
+
/**
|
|
608
|
+
* The second track alone, not the second through the last. A control
|
|
609
|
+
* spanning to the end would meet the counter's cell on the first row and be
|
|
610
|
+
* pushed to a row of its own — which is the side layout undone, and it is
|
|
611
|
+
* what the second capture of this family showed: `Assunto` above its input
|
|
612
|
+
* rather than beside it.
|
|
613
|
+
*
|
|
614
|
+
* Nothing is lost when there is no counter: the third track is then zero
|
|
615
|
+
* wide, and there is no gutter for it to leave behind.
|
|
616
|
+
*
|
|
617
|
+
* Every child keeps its own height. A row is as tall as its tallest cell,
|
|
618
|
+
* and a label of three lines — or a header whose action wrapped under the
|
|
619
|
+
* label — stretched a one-line input beside it to 43 pixels.
|
|
620
|
+
*/
|
|
621
|
+
[data-ui='field'][data-part='root'][data-label-placement='side'] > * {
|
|
622
|
+
align-self: start;
|
|
623
|
+
grid-column: 2 / 3;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Centred on the control's first line rather than on the control, so the
|
|
628
|
+
* label stays level with the text a reader is typing when the control is a
|
|
629
|
+
* textarea four lines tall. `max()` keeps a label taller than its control —
|
|
630
|
+
* three wrapped lines beside a 30-pixel input — from being pushed down.
|
|
631
|
+
*/
|
|
632
|
+
[data-ui='field'][data-part='root'][data-label-placement='side'] > [data-part='label'],
|
|
633
|
+
[data-ui='field'][data-part='root'][data-label-placement='side'] > [data-part='header'] {
|
|
634
|
+
grid-column: 1;
|
|
635
|
+
grid-row: 1;
|
|
636
|
+
padding-block-start: max(0px, calc((var(--_control-block-size) - 1lh) / 2));
|
|
637
|
+
padding-inline-end: var(--_column-gap);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Under the label, at the end of the label's track, which is where a header
|
|
642
|
+
* puts its own counter as well (below), so a side field says its count in
|
|
643
|
+
* one place whether the page wrote a header or not; the count after the
|
|
644
|
+
* control, where it sat until the header existed, was a second place. Restated inside the query because `> *` above is one selector
|
|
645
|
+
* more specific than the counter's own rule.
|
|
646
|
+
*/
|
|
647
|
+
[data-ui='field'][data-part='root'][data-label-placement='side'] > [data-part='counter'] {
|
|
648
|
+
grid-column: 1;
|
|
649
|
+
grid-row: 2;
|
|
650
|
+
justify-self: end;
|
|
651
|
+
padding-inline: 0 var(--_column-gap);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* The same place for the counter a header holds: a line of its own under
|
|
656
|
+
* the label, at the end of the label's track. Beside the label it read as
|
|
657
|
+
* part of the name — `Assunto 12/60` — and it stood one line higher than
|
|
658
|
+
* the counter a field without a header draws.
|
|
659
|
+
*/
|
|
660
|
+
[data-ui='field'][data-part='root'][data-label-placement='side']
|
|
661
|
+
> [data-part='header']
|
|
662
|
+
> [data-part='counter'] {
|
|
663
|
+
flex-basis: 100%;
|
|
664
|
+
margin-inline-start: 0;
|
|
665
|
+
text-align: end;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/* ── Disabled ─────────────────────────────────────────────────────────
|
|
670
|
+
*
|
|
671
|
+
* Muted to the colour the theme measures rather than faded by opacity: a
|
|
672
|
+
* label nobody can read is a field nobody can identify, and a form that
|
|
673
|
+
* disables a section still has to say what the section was for.
|
|
674
|
+
*
|
|
675
|
+
* The messages need no rule here. The field passes `disabled` to them, and
|
|
676
|
+
* `inline-message` mutes itself to the same colour and stops its own ring.
|
|
677
|
+
*/
|
|
678
|
+
[data-ui='field'][data-part='root'][data-disabled] > [data-part='label'],
|
|
679
|
+
[data-ui='field'][data-part='root'][data-disabled] > [data-part='counter'],
|
|
680
|
+
[data-ui='field'][data-part='root'][data-disabled] > [data-part='header'] > [data-part='label'],
|
|
681
|
+
[data-ui='field'][data-part='root'][data-disabled]
|
|
682
|
+
> [data-part='header']
|
|
683
|
+
> [data-part='counter'] {
|
|
684
|
+
color: var(--slotted-disabled-foreground);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
[data-ui='field'][data-part='root'][data-disabled] > [data-part='label'] > [data-part='mark'],
|
|
688
|
+
[data-ui='field'][data-part='root'][data-disabled]
|
|
689
|
+
> [data-part='header']
|
|
690
|
+
> [data-part='label']
|
|
691
|
+
> [data-part='mark'] {
|
|
692
|
+
color: var(--slotted-disabled-foreground);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* `disabled` sits above `invalid` in `STATE_PRECEDENCE`, so a disabled field
|
|
697
|
+
* whose counter is over its limit paints the counter muted rather than red.
|
|
698
|
+
* A control nobody can type in is not over a limit; it is off.
|
|
699
|
+
*/
|
|
700
|
+
[data-ui='field'][data-part='root'][data-disabled] > [data-part='counter'][data-invalid],
|
|
701
|
+
[data-ui='field'][data-part='root'][data-disabled]
|
|
702
|
+
> [data-part='header']
|
|
703
|
+
> [data-part='counter'][data-invalid] {
|
|
704
|
+
color: var(--slotted-disabled-foreground);
|
|
705
|
+
font-weight: var(--slotted-font-weight-regular, 400);
|
|
706
|
+
}
|
|
707
|
+
}
|