@slotted/styles 0.0.0-stage → 0.1.0-beta.1
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,85 @@
|
|
|
1
|
+
[
|
|
2
|
+
"--slotted-attachment-action-radius",
|
|
3
|
+
"--slotted-attachment-action-size",
|
|
4
|
+
"--slotted-attachment-action-state-layer-active",
|
|
5
|
+
"--slotted-attachment-action-state-layer-hover",
|
|
6
|
+
"--slotted-attachment-background",
|
|
7
|
+
"--slotted-attachment-border-color",
|
|
8
|
+
"--slotted-attachment-border-width",
|
|
9
|
+
"--slotted-attachment-card-actions-opacity",
|
|
10
|
+
"--slotted-attachment-card-preview-radius",
|
|
11
|
+
"--slotted-attachment-card-size",
|
|
12
|
+
"--slotted-attachment-font-family",
|
|
13
|
+
"--slotted-attachment-grid-min",
|
|
14
|
+
"--slotted-attachment-group-gap",
|
|
15
|
+
"--slotted-attachment-icon-size-min",
|
|
16
|
+
"--slotted-attachment-line-gap",
|
|
17
|
+
"--slotted-attachment-name-font-weight",
|
|
18
|
+
"--slotted-attachment-padding-lg",
|
|
19
|
+
"--slotted-attachment-padding-md",
|
|
20
|
+
"--slotted-attachment-padding-sm",
|
|
21
|
+
"--slotted-attachment-preview-radius",
|
|
22
|
+
"--slotted-attachment-preview-size",
|
|
23
|
+
"--slotted-attachment-radius",
|
|
24
|
+
"--slotted-attachment-state-layer-active",
|
|
25
|
+
"--slotted-attachment-state-layer-hover",
|
|
26
|
+
"--slotted-attachment-strip-item-size",
|
|
27
|
+
"--slotted-border-strong",
|
|
28
|
+
"--slotted-border-subtle",
|
|
29
|
+
"--slotted-border-width-thin",
|
|
30
|
+
"--slotted-control-font-family",
|
|
31
|
+
"--slotted-control-font-size-lg",
|
|
32
|
+
"--slotted-control-font-size-md",
|
|
33
|
+
"--slotted-control-font-size-sm",
|
|
34
|
+
"--slotted-control-gap-lg",
|
|
35
|
+
"--slotted-control-gap-md",
|
|
36
|
+
"--slotted-control-gap-sm",
|
|
37
|
+
"--slotted-control-icon-size-lg",
|
|
38
|
+
"--slotted-control-icon-size-md",
|
|
39
|
+
"--slotted-control-icon-size-sm",
|
|
40
|
+
"--slotted-control-invalid-edge-width",
|
|
41
|
+
"--slotted-control-radius",
|
|
42
|
+
"--slotted-control-transition-duration",
|
|
43
|
+
"--slotted-control-transition-easing",
|
|
44
|
+
"--slotted-disabled-background",
|
|
45
|
+
"--slotted-disabled-foreground",
|
|
46
|
+
"--slotted-duration-instant",
|
|
47
|
+
"--slotted-duration-medium",
|
|
48
|
+
"--slotted-duration-short",
|
|
49
|
+
"--slotted-easing-enter",
|
|
50
|
+
"--slotted-easing-exit",
|
|
51
|
+
"--slotted-focus-ring-color",
|
|
52
|
+
"--slotted-focus-ring-offset",
|
|
53
|
+
"--slotted-focus-ring-width",
|
|
54
|
+
"--slotted-font-size-xs",
|
|
55
|
+
"--slotted-font-weight-medium",
|
|
56
|
+
"--slotted-font-weight-semibold",
|
|
57
|
+
"--slotted-line-height-md",
|
|
58
|
+
"--slotted-radius-lg",
|
|
59
|
+
"--slotted-radius-md",
|
|
60
|
+
"--slotted-radius-sm",
|
|
61
|
+
"--slotted-shadow-xs",
|
|
62
|
+
"--slotted-space-1",
|
|
63
|
+
"--slotted-space-2",
|
|
64
|
+
"--slotted-space-3",
|
|
65
|
+
"--slotted-space-4",
|
|
66
|
+
"--slotted-space-5",
|
|
67
|
+
"--slotted-surface",
|
|
68
|
+
"--slotted-target-comfortable",
|
|
69
|
+
"--slotted-target-min",
|
|
70
|
+
"--slotted-text",
|
|
71
|
+
"--slotted-text-muted",
|
|
72
|
+
"--slotted-tone-danger-border",
|
|
73
|
+
"--slotted-tone-danger-surface",
|
|
74
|
+
"--slotted-tone-danger-text",
|
|
75
|
+
"--slotted-upload-kind-archive",
|
|
76
|
+
"--slotted-upload-kind-audio",
|
|
77
|
+
"--slotted-upload-kind-document",
|
|
78
|
+
"--slotted-upload-kind-foreground",
|
|
79
|
+
"--slotted-upload-kind-image",
|
|
80
|
+
"--slotted-upload-kind-other",
|
|
81
|
+
"--slotted-upload-kind-pdf",
|
|
82
|
+
"--slotted-upload-kind-presentation",
|
|
83
|
+
"--slotted-upload-kind-spreadsheet",
|
|
84
|
+
"--slotted-upload-kind-video"
|
|
85
|
+
]
|
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── The avatar box ─────────────────────────────────────────────────────
|
|
4
|
+
*
|
|
5
|
+
* A picture of a person or an entity, and what stands in when there is none.
|
|
6
|
+
* The order is the family's whole behaviour: the picture, then the initials
|
|
7
|
+
* taken from the name, then a glyph the family owns — so a request that fails
|
|
8
|
+
* never paints the browser's torn-page mark.
|
|
9
|
+
*
|
|
10
|
+
* Two attributes on every selector, always. Both parts sit inside the root,
|
|
11
|
+
* so the pair is what stops a rule meant for the box from reaching the
|
|
12
|
+
* picture inside it.
|
|
13
|
+
*
|
|
14
|
+
* The box is square by construction: one length, read from the avatar size
|
|
15
|
+
* scale the theme restates per density. Everything the family scales beside
|
|
16
|
+
* it — the type in the initials, the glyph, the square corner — is a
|
|
17
|
+
* proportion of that length rather than a scale of its own, which is the
|
|
18
|
+
* reasoning `badge` used for the overhang of an anchored count.
|
|
19
|
+
*/
|
|
20
|
+
[data-ui='avatar'][data-part='root'] {
|
|
21
|
+
/**
|
|
22
|
+
* Tone. `secondary` reads the `solid-hover` step rather than `solid` for
|
|
23
|
+
* the reason `badge` records: `secondary.solid` is the surface colour in
|
|
24
|
+
* all three schemes, so a filled neutral avatar would be a hole in the page
|
|
25
|
+
* rather than a disc.
|
|
26
|
+
*/
|
|
27
|
+
--_solid: var(--slotted-tone-secondary-solid-hover);
|
|
28
|
+
--_on-solid: var(--slotted-tone-secondary-on-solid);
|
|
29
|
+
--_surface: var(--slotted-tone-secondary-surface);
|
|
30
|
+
--_border: var(--slotted-tone-secondary-border);
|
|
31
|
+
--_text: var(--slotted-tone-secondary-text);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The size step. Written as the public token rather than into `--_size`
|
|
35
|
+
* directly, so an instance can state a size no step holds — a 96-pixel
|
|
36
|
+
* avatar on a profile header — with `style="--slotted-avatar-size: 6rem"`.
|
|
37
|
+
* It is the mechanism `icon` established, and the precedence it gives is
|
|
38
|
+
* the right one: a declaration on the element beats one inherited from a
|
|
39
|
+
* container, so `size` on the avatar always wins over the container's
|
|
40
|
+
* opinion of it.
|
|
41
|
+
*/
|
|
42
|
+
--_size: var(--slotted-avatar-size, var(--slotted-avatar-size-md, 36px));
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The initials fill a proportion of the box, not a step of the type scale.
|
|
46
|
+
* The smallest avatar is twenty pixels at `dense` and the type scale stops
|
|
47
|
+
* at twelve, so a scale step would be half the circle; and the whole point
|
|
48
|
+
* of the box is that the letters inside it look the same size at every
|
|
49
|
+
* step. Exposed as a token because the right proportion depends on the
|
|
50
|
+
* theme's typeface: a narrow grotesque wants more than a wide one.
|
|
51
|
+
*/
|
|
52
|
+
--_initials-size: var(--slotted-avatar-initials-size, calc(var(--_size) * 0.4));
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The silhouette is drawn a little larger than the letters, because it is
|
|
56
|
+
* a shape rather than a word and reads small at the same ratio.
|
|
57
|
+
*/
|
|
58
|
+
--_glyph-size: var(--slotted-avatar-glyph-size, calc(var(--_size) * 0.68));
|
|
59
|
+
|
|
60
|
+
--_radius: var(--slotted-radius-full, 9999px);
|
|
61
|
+
--_edge-width: var(--slotted-avatar-border-width, var(--slotted-border-width-thin, 1px));
|
|
62
|
+
--_edge: var(--_border);
|
|
63
|
+
/**
|
|
64
|
+
* What a filled avatar draws around itself, and for four of the five tones
|
|
65
|
+
* the answer is its own fill: the edge is the disc, so there is no rim.
|
|
66
|
+
*
|
|
67
|
+
* `secondary` is the exception, and it is the only one, for the reason
|
|
68
|
+
* `badge` records against the same colour: the neutral fill is `#e6e6e6`
|
|
69
|
+
* against the page's own white in `high-contrast`, so a filled neutral
|
|
70
|
+
* avatar with no edge is not a disc, it is a smudge. Every other tone
|
|
71
|
+
* carries its own boundary there.
|
|
72
|
+
*/
|
|
73
|
+
--_solid-edge: var(--_border);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* What separates this avatar from whatever it is drawn over. Nought until a
|
|
77
|
+
* container asks for it, because a lone avatar overlaps nothing; a group
|
|
78
|
+
* turns it on for every member. It is a colour and not a boolean, because
|
|
79
|
+
* an avatar inside a card, a toolbar or a coloured row is not sitting on
|
|
80
|
+
* the page's own surface — the same reason `badge` gives for its own ring.
|
|
81
|
+
*/
|
|
82
|
+
--_ring-width: var(--slotted-avatar-ring-width, 0);
|
|
83
|
+
--_ring-color: var(--slotted-avatar-ring-color, var(--slotted-surface, Canvas));
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* What the avatar is lifted off, and nothing at rest. A lone avatar sits on
|
|
87
|
+
* the page; a container that raises one of its members off the others says
|
|
88
|
+
* so here rather than restating the whole shadow — the ring below it is the
|
|
89
|
+
* avatar's own decision and a container has no business overwriting it,
|
|
90
|
+
* which is what a second `box-shadow` on the same box would do (AG1).
|
|
91
|
+
*/
|
|
92
|
+
--_elevation: var(--slotted-avatar-elevation, 0 0 transparent);
|
|
93
|
+
|
|
94
|
+
align-items: center;
|
|
95
|
+
/**
|
|
96
|
+
* `render` can make the root a `<button>`, and the browser draws two
|
|
97
|
+
* pixels of `outset ButtonBorder` on one of those. The family states its
|
|
98
|
+
* own edge and its own inset below, so whatever the user agent had in mind
|
|
99
|
+
* is replaced rather than merely reset.
|
|
100
|
+
*/
|
|
101
|
+
appearance: none;
|
|
102
|
+
block-size: var(--_size);
|
|
103
|
+
/**
|
|
104
|
+
* ── The edge ─────────────────────────────────────────────────────────
|
|
105
|
+
*
|
|
106
|
+
* The element's own border, and not a ring drawn over it.
|
|
107
|
+
*
|
|
108
|
+
* A pseudo-element was the first answer, so the rim could sit above the
|
|
109
|
+
* photograph. It cost a seam: the disc and the ring are two rounded shapes
|
|
110
|
+
* rasterised apart — `box-sizing` is not inherited, so the pseudo-element
|
|
111
|
+
* did not even use the same box — and two antialiased curves over the same
|
|
112
|
+
* path do not agree in the sub-pixel. On a circle the disagreement is a
|
|
113
|
+
* hairline of the page showing through between the fill and its rim.
|
|
114
|
+
*
|
|
115
|
+
* One border on one box has one curve, and the engine computes the inner
|
|
116
|
+
* radius from the outer one. `box-sizing: border-box` keeps the avatar
|
|
117
|
+
* exactly `--_size` however thick the rim is, and the picture is cropped
|
|
118
|
+
* inside the rim rather than under it — which is the right look anyway: a
|
|
119
|
+
* photograph in a frame, not a photograph with a line across it.
|
|
120
|
+
*/
|
|
121
|
+
border-color: var(--_edge);
|
|
122
|
+
border-radius: var(--_radius);
|
|
123
|
+
/* Three longhands rather than the shorthand: an undefined colour makes a
|
|
124
|
+
`var()` shorthand invalid at computed-value time, and the width would
|
|
125
|
+
fall back to `medium` — three pixels — rather than to this one. */
|
|
126
|
+
border-style: solid;
|
|
127
|
+
border-width: var(--_edge-width);
|
|
128
|
+
box-shadow:
|
|
129
|
+
0 0 0 var(--_ring-width) var(--_ring-color),
|
|
130
|
+
var(--_elevation);
|
|
131
|
+
box-sizing: border-box;
|
|
132
|
+
display: inline-flex;
|
|
133
|
+
/* A box that shrinks is not a box: an avatar keeps its size in a flex row. */
|
|
134
|
+
flex: none;
|
|
135
|
+
font-family: var(--slotted-avatar-font-family, var(--slotted-control-font-family, sans-serif));
|
|
136
|
+
font-size: var(--_initials-size);
|
|
137
|
+
font-weight: var(--slotted-avatar-font-weight, var(--slotted-font-weight-medium, 500));
|
|
138
|
+
inline-size: var(--_size);
|
|
139
|
+
justify-content: center;
|
|
140
|
+
line-height: 1;
|
|
141
|
+
padding: 0;
|
|
142
|
+
/**
|
|
143
|
+
* An avatar is one thing, and it paints as one thing.
|
|
144
|
+
*
|
|
145
|
+
* The rim used to be a pseudo-element carrying `z-index: 1`, and a positive
|
|
146
|
+
* `z-index` on a pseudo-element of a box whose own `z-index` is `auto`
|
|
147
|
+
* promotes it into the nearest ancestor stacking context rather than into
|
|
148
|
+
* its own. In a group every member's rim was drawn across the face on top
|
|
149
|
+
* of it, and the row was inconsistent as well as wrong: the one member the
|
|
150
|
+
* pointer had raised did create a stacking context, so it alone behaved.
|
|
151
|
+
* The rim is the element's own border now, and the isolation is what keeps
|
|
152
|
+
* the picture inside the avatar it belongs to.
|
|
153
|
+
*/
|
|
154
|
+
isolation: isolate;
|
|
155
|
+
/**
|
|
156
|
+
* The picture and the initials are stacked rather than swapped, so the
|
|
157
|
+
* picture can arrive over the letters instead of replacing them in one
|
|
158
|
+
* frame. This is the containing block for both.
|
|
159
|
+
*/
|
|
160
|
+
position: relative;
|
|
161
|
+
/**
|
|
162
|
+
* `render` can make the root an `<a>`, and a rule the browser writes on
|
|
163
|
+
* every anchor would put a line under two initials.
|
|
164
|
+
*/
|
|
165
|
+
text-decoration: none;
|
|
166
|
+
/**
|
|
167
|
+
* The edge changes colour at the moment the picture arrives — the tone's
|
|
168
|
+
* own rim gives way to the neutral one a photograph wants — so it crosses
|
|
169
|
+
* on the same clock as the picture. Without it the rim snapped from red to
|
|
170
|
+
* grey a frame before the face had finished appearing, which was the one
|
|
171
|
+
* thing left moving on its own in the loading sequence.
|
|
172
|
+
*
|
|
173
|
+
* The ground crosses on the same clock, because hover and press answer on
|
|
174
|
+
* it; the ring and the transform are what a group raises a member with, and
|
|
175
|
+
* they are listed here so the group states only the curve and never
|
|
176
|
+
* replaces this list — it did, and the edge snapped inside a stack.
|
|
177
|
+
*/
|
|
178
|
+
transition:
|
|
179
|
+
border-color var(--slotted-duration-short) var(--slotted-easing-standard),
|
|
180
|
+
background-color var(--slotted-duration-short) var(--slotted-easing-standard),
|
|
181
|
+
box-shadow var(--slotted-duration-short) var(--slotted-easing-standard),
|
|
182
|
+
transform var(--slotted-duration-short) var(--slotted-easing-standard);
|
|
183
|
+
/* Initials are a label, not a selection: dragging across a row of faces
|
|
184
|
+
should not highlight a column of letters. */
|
|
185
|
+
user-select: none;
|
|
186
|
+
/* Written beside a name, an avatar sits on the text's centre. */
|
|
187
|
+
vertical-align: middle;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* ── Tone: which of the five semantic colours the name resolves to ─────── */
|
|
191
|
+
|
|
192
|
+
[data-ui='avatar'][data-part='root'][data-tone='accent'] {
|
|
193
|
+
--_solid: var(--slotted-tone-accent-solid);
|
|
194
|
+
--_solid-edge: var(--slotted-tone-accent-solid);
|
|
195
|
+
--_on-solid: var(--slotted-tone-accent-on-solid);
|
|
196
|
+
--_surface: var(--slotted-tone-accent-surface);
|
|
197
|
+
--_border: var(--slotted-tone-accent-border);
|
|
198
|
+
--_text: var(--slotted-tone-accent-text);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
[data-ui='avatar'][data-part='root'][data-tone='success'] {
|
|
202
|
+
--_solid: var(--slotted-tone-success-solid);
|
|
203
|
+
--_solid-edge: var(--slotted-tone-success-solid);
|
|
204
|
+
--_on-solid: var(--slotted-tone-success-on-solid);
|
|
205
|
+
--_surface: var(--slotted-tone-success-surface);
|
|
206
|
+
--_border: var(--slotted-tone-success-border);
|
|
207
|
+
--_text: var(--slotted-tone-success-text);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
[data-ui='avatar'][data-part='root'][data-tone='warning'] {
|
|
211
|
+
--_solid: var(--slotted-tone-warning-solid);
|
|
212
|
+
--_solid-edge: var(--slotted-tone-warning-solid);
|
|
213
|
+
--_on-solid: var(--slotted-tone-warning-on-solid);
|
|
214
|
+
--_surface: var(--slotted-tone-warning-surface);
|
|
215
|
+
--_border: var(--slotted-tone-warning-border);
|
|
216
|
+
--_text: var(--slotted-tone-warning-text);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
[data-ui='avatar'][data-part='root'][data-tone='danger'] {
|
|
220
|
+
--_solid: var(--slotted-tone-danger-solid);
|
|
221
|
+
--_solid-edge: var(--slotted-tone-danger-solid);
|
|
222
|
+
--_on-solid: var(--slotted-tone-danger-on-solid);
|
|
223
|
+
--_surface: var(--slotted-tone-danger-surface);
|
|
224
|
+
--_border: var(--slotted-tone-danger-border);
|
|
225
|
+
--_text: var(--slotted-tone-danger-text);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* ── Variant: two paintings ─────────────────────────────────────────────
|
|
230
|
+
*
|
|
231
|
+
* `soft` tints and keeps the tone's own edge, which is what holds the disc
|
|
232
|
+
* together in the `high-contrast` scheme where every tone surface collapses
|
|
233
|
+
* to the page's own white. `solid` fills and draws no rim, because its edge
|
|
234
|
+
* is its own fill.
|
|
235
|
+
*
|
|
236
|
+
* There is no `outline`: an avatar drawn as a ring around nothing is not an
|
|
237
|
+
* avatar, and every one of the fourteen surveyed implementations fills.
|
|
238
|
+
*/
|
|
239
|
+
[data-ui='avatar'][data-part='root'][data-variant='soft'] {
|
|
240
|
+
--_ground: var(--_surface, ButtonFace);
|
|
241
|
+
|
|
242
|
+
background-color: var(--_ground);
|
|
243
|
+
color: var(--_text, ButtonText);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
[data-ui='avatar'][data-part='root'][data-variant='solid'] {
|
|
247
|
+
--_edge: var(--_solid-edge);
|
|
248
|
+
--_ground: var(--_solid, ButtonFace);
|
|
249
|
+
|
|
250
|
+
background-color: var(--_ground);
|
|
251
|
+
color: var(--_on-solid, ButtonText);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* ── Shape ────────────────────────────────────────────────────────────── */
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Primer's rule, taken with its meaning: a circle is a person and a square is
|
|
258
|
+
* a team, an organisation or a bot.
|
|
259
|
+
*
|
|
260
|
+
* The corner is a sixth of the box rather than a step of the radius scale.
|
|
261
|
+
* `radius-md` is six pixels, which is a gentle round on a 64-pixel square and
|
|
262
|
+
* a quarter-circle on a 20-pixel one; a proportion is the same shape at every
|
|
263
|
+
* step. A theme that disagrees states the token.
|
|
264
|
+
*
|
|
265
|
+
* Rounded to the pixel: a sixth of 64 is 10.67 and of 28 is 4.67, and a
|
|
266
|
+
* fractional radius spreads the curve over partial pixels at every corner.
|
|
267
|
+
*/
|
|
268
|
+
[data-ui='avatar'][data-part='root'][data-shape='square'] {
|
|
269
|
+
--_radius: var(--slotted-avatar-radius, round(nearest, calc(var(--_size) / 6), 1px));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* ── Size ─────────────────────────────────────────────────────────────── */
|
|
273
|
+
|
|
274
|
+
[data-ui='avatar'][data-part='root'][data-size='xs'] {
|
|
275
|
+
--slotted-avatar-size: var(--slotted-avatar-size-xs, 24px);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
[data-ui='avatar'][data-part='root'][data-size='sm'] {
|
|
279
|
+
--slotted-avatar-size: var(--slotted-avatar-size-sm, 28px);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
[data-ui='avatar'][data-part='root'][data-size='md'] {
|
|
283
|
+
--slotted-avatar-size: var(--slotted-avatar-size-md, 36px);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
[data-ui='avatar'][data-part='root'][data-size='lg'] {
|
|
287
|
+
--slotted-avatar-size: var(--slotted-avatar-size-lg, 48px);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
[data-ui='avatar'][data-part='root'][data-size='xl'] {
|
|
291
|
+
--slotted-avatar-size: var(--slotted-avatar-size-xl, 64px);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* ── The picture ──────────────────────────────────────────────────────── */
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Always rendered, never in the flow. It lies over the initials at opacity
|
|
299
|
+
* nought and rises to one when the load succeeds, so a photograph that
|
|
300
|
+
* arrives four hundred milliseconds after the page fades in over the letters
|
|
301
|
+
* instead of replacing them between two frames. A list of twenty avatars
|
|
302
|
+
* without the transition snaps letter-by-photograph as each request returns.
|
|
303
|
+
*
|
|
304
|
+
* `prefers-reduced-motion` collapses the duration in the theme, and nothing
|
|
305
|
+
* here loops.
|
|
306
|
+
*/
|
|
307
|
+
[data-ui='avatar'] [data-part='image'] {
|
|
308
|
+
block-size: 100%;
|
|
309
|
+
border-radius: inherit;
|
|
310
|
+
inline-size: 100%;
|
|
311
|
+
inset: 0;
|
|
312
|
+
object-fit: cover;
|
|
313
|
+
opacity: 0;
|
|
314
|
+
position: absolute;
|
|
315
|
+
transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* A transition runs on the curve of the state it arrives at: the picture
|
|
320
|
+
* enters on the entering curve and, when a later request fails, leaves on the
|
|
321
|
+
* exiting one. The fallback below is the mirror of it.
|
|
322
|
+
*/
|
|
323
|
+
[data-ui='avatar'][data-status='loaded'] [data-part='image'] {
|
|
324
|
+
opacity: 1;
|
|
325
|
+
transition-timing-function: var(--slotted-easing-enter);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* ── What stands in for it ────────────────────────────────────────────── */
|
|
329
|
+
|
|
330
|
+
[data-ui='avatar'] [data-part='fallback'] {
|
|
331
|
+
/**
|
|
332
|
+
* The last step of the fallback order, and the only artwork this family
|
|
333
|
+
* owns. It is reached when there is no picture and no name to take
|
|
334
|
+
* initials from — an unassigned ticket, an anonymous visitor — and the
|
|
335
|
+
* binding draws it as the `person` glyph in an `Icon` rather than as a
|
|
336
|
+
* character, which no font can be relied on to carry.
|
|
337
|
+
*
|
|
338
|
+
* The size is set on the slot rather than on the icon, so an icon the
|
|
339
|
+
* consumer projects without a size of its own is drawn at the same step,
|
|
340
|
+
* and one that states a size keeps it.
|
|
341
|
+
*/
|
|
342
|
+
--slotted-icon-size: var(--_glyph-size);
|
|
343
|
+
|
|
344
|
+
align-items: center;
|
|
345
|
+
block-size: 100%;
|
|
346
|
+
border-radius: inherit;
|
|
347
|
+
display: flex;
|
|
348
|
+
inline-size: 100%;
|
|
349
|
+
justify-content: center;
|
|
350
|
+
/**
|
|
351
|
+
* Initials are two graphemes, and a consumer who writes a word into the
|
|
352
|
+
* slot has written a word into a circle. Clipping is visual only:
|
|
353
|
+
* `overflow` removes nothing from the document or from the accessibility
|
|
354
|
+
* tree, which is what the briefing's rule about truncation protects.
|
|
355
|
+
*/
|
|
356
|
+
overflow: hidden;
|
|
357
|
+
transition: opacity var(--slotted-duration-short) var(--slotted-easing-enter);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
[data-ui='avatar'][data-status='loaded'] [data-part='fallback'] {
|
|
361
|
+
/**
|
|
362
|
+
* It leaves as the picture arrives rather than the instant the load
|
|
363
|
+
* reports, so the two cross rather than flicker. It is still painted
|
|
364
|
+
* underneath, which is what a photograph with transparency is drawn on.
|
|
365
|
+
*/
|
|
366
|
+
opacity: 0;
|
|
367
|
+
transition-timing-function: var(--slotted-easing-exit);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* A flex container centres the line box, and a line box is not symmetric
|
|
372
|
+
* around the letters inside it: the ascender reaches further above the cap
|
|
373
|
+
* than the descender reaches below the baseline, and two initials have no
|
|
374
|
+
* descender. Measured with the Geist face at `compact`, `AL` sat 0.56 pixels
|
|
375
|
+
* high in the 32-pixel disc and 0.68 high in the 26-pixel one; at
|
|
376
|
+
* `comfortable`, 1.13 high in the 64-pixel one. `text-box` trims the line to
|
|
377
|
+
* the cap and the baseline, so what is centred is the ink rather than the
|
|
378
|
+
* font's metrics — the rule `badge`, `kbd`, `tag` and `button` each carry.
|
|
379
|
+
*
|
|
380
|
+
* Those four have a label part to carry it. The initials are written straight
|
|
381
|
+
* into the fallback, and `text-box-trim` needs a block container — a flex
|
|
382
|
+
* container's text lives in an anonymous item no selector can reach — so the
|
|
383
|
+
* fallback itself becomes the block and centres its one line with
|
|
384
|
+
* `align-content`, which block containers have taken since Chrome 123 and
|
|
385
|
+
* Safari 17.4, both earlier than the trim. The box keeps its full size, so
|
|
386
|
+
* the disc still clips a long word and no descender is beheaded: the clip is
|
|
387
|
+
* the whole fallback, not the trimmed line, which is the room `badge` had to
|
|
388
|
+
* buy with padding.
|
|
389
|
+
*
|
|
390
|
+
* Only a fallback holding nothing but text is trimmed. A projected logo or
|
|
391
|
+
* icon is not a line of type: it sits on the baseline of a line whose trim is
|
|
392
|
+
* measured from the font, and trimming that line would move the artwork
|
|
393
|
+
* rather than centre it. `:has(> *)` is the whole test, and it excludes the
|
|
394
|
+
* family's own glyph as well, which is an icon and needs no line at all.
|
|
395
|
+
*/
|
|
396
|
+
@supports (text-box: trim-both cap alphabetic) {
|
|
397
|
+
[data-ui='avatar'] [data-part='fallback']:not(:has(> *)) {
|
|
398
|
+
align-content: center;
|
|
399
|
+
display: block;
|
|
400
|
+
text-align: center;
|
|
401
|
+
text-box: trim-both cap alphabetic;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* ── The picture that arrived, and the one that did not ───────────────── */
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* A photograph does not want a coloured rim, and a photograph of a white wall
|
|
409
|
+
* on a white surface with no rim at all has no boundary. `border-subtle` is
|
|
410
|
+
* the token that separates one surface from another everywhere else.
|
|
411
|
+
*
|
|
412
|
+
* Written after the variants and before `disabled`, so the three states
|
|
413
|
+
* resolve in the order `STATE_PRECEDENCE` declares with no selector weight
|
|
414
|
+
* standing in for the order.
|
|
415
|
+
*/
|
|
416
|
+
[data-ui='avatar'][data-part='root'][data-status='loaded'] {
|
|
417
|
+
--_edge: var(--slotted-border-subtle, ButtonBorder);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* ── Disabled ─────────────────────────────────────────────────────────── */
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* A deactivated account, or an avatar inside a control the page has turned
|
|
424
|
+
* off. It stays legible rather than merely faded: the letters keep the
|
|
425
|
+
* disabled foreground, which the theme holds against the disabled ground, and
|
|
426
|
+
* the photograph loses its colour rather than its contrast.
|
|
427
|
+
*/
|
|
428
|
+
[data-ui='avatar'][data-part='root'][data-disabled] {
|
|
429
|
+
--_solid: var(--slotted-disabled-background, Canvas);
|
|
430
|
+
--_on-solid: var(--slotted-disabled-foreground, GrayText);
|
|
431
|
+
--_surface: var(--slotted-disabled-background, Canvas);
|
|
432
|
+
--_text: var(--slotted-disabled-foreground, GrayText);
|
|
433
|
+
--_edge: var(--slotted-disabled-border, GrayText);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Colour alone would not be enough and colour alone is not what changes: the
|
|
438
|
+
* photograph loses its hue and part of its opacity, so the disabled ground
|
|
439
|
+
* shows through and a switched-off face reads as switched off beside one that
|
|
440
|
+
* has no photograph at all. Grey alone kept every luminance, which made a
|
|
441
|
+
* deactivated portrait the strongest thing in a row of disabled avatars.
|
|
442
|
+
*/
|
|
443
|
+
[data-ui='avatar'][data-part='root'][data-disabled] [data-part='image'] {
|
|
444
|
+
filter: var(--slotted-avatar-disabled-image-filter, grayscale(1) opacity(0.5));
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* ── Interaction ──────────────────────────────────────────────────────── */
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* An avatar is a picture, not a command, and paints nothing at rest. These
|
|
451
|
+
* three selectors are the ones the browser itself treats as interactive, and
|
|
452
|
+
* they are written apart rather than gathered into `:is()` because
|
|
453
|
+
* `packages/styles/src/states.test.mjs` reads one comma-separated selector at
|
|
454
|
+
* a time and a guard inside a list would be invisible to it.
|
|
455
|
+
*
|
|
456
|
+
* A weak state held back for the stronger one: a disabled avatar answers no
|
|
457
|
+
* hover, which is the rule that test exists to keep.
|
|
458
|
+
*/
|
|
459
|
+
[data-ui='avatar'][data-part='root']:any-link,
|
|
460
|
+
button[data-ui='avatar'][data-part='root'],
|
|
461
|
+
[data-ui='avatar'][data-part='root'][tabindex] {
|
|
462
|
+
cursor: pointer;
|
|
463
|
+
/**
|
|
464
|
+
* The target below hangs outside the box on purpose. A box whose content
|
|
465
|
+
* can leave it is a different statement, and it is the one an avatar never
|
|
466
|
+
* makes: a face is cropped by its own edge, and the only thing standing
|
|
467
|
+
* outside the disc is a hit area that draws nothing. `clip` says so, and
|
|
468
|
+
* saying so is what separates the two — `tools/harness/audit.mjs` read the
|
|
469
|
+
* hit area as content leaving the box nine times per density at `dense`
|
|
470
|
+
* (AV1), which is the reading `visible` invites.
|
|
471
|
+
*
|
|
472
|
+
* The margin keeps the target painted and pressed: the clip shape is the
|
|
473
|
+
* padding box grown by this length, corner radii grown with it, so a
|
|
474
|
+
* circular avatar is clipped to a larger circle rather than to a square,
|
|
475
|
+
* and the corners of the 24-pixel square still answer a press. One target's
|
|
476
|
+
* length is more than any centred target can reach past a box — half is the
|
|
477
|
+
* exact figure — and the property takes a plain length: Chrome rejects
|
|
478
|
+
* `calc()` here, and a rejected value computes to nought, which clips the
|
|
479
|
+
* target to the face.
|
|
480
|
+
*/
|
|
481
|
+
overflow: clip;
|
|
482
|
+
overflow-clip-margin: var(--slotted-target-min, 24px);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Hover and press answer on the ground, the way `tag` answers on its own: the
|
|
487
|
+
* letters' colour laid over the fill at eight and fourteen per cent. The rim
|
|
488
|
+
* keeps the tone. It used to turn `border-strong` on hover, which took the
|
|
489
|
+
* colour out of the one thing the pointer was on, and in `high-contrast` the
|
|
490
|
+
* tone's rim and `border-strong` are both black, so the hover changed nothing.
|
|
491
|
+
*
|
|
492
|
+
* Press exists because `render` makes the root a link or a button, and a
|
|
493
|
+
* command that does not answer the press reads as one that did not register.
|
|
494
|
+
*/
|
|
495
|
+
[data-ui='avatar'][data-part='root']:any-link:not([data-disabled]):hover,
|
|
496
|
+
button[data-ui='avatar'][data-part='root']:not([data-disabled]):hover,
|
|
497
|
+
[data-ui='avatar'][data-part='root'][tabindex]:not([data-disabled]):hover {
|
|
498
|
+
background-color: color-mix(
|
|
499
|
+
in oklab,
|
|
500
|
+
var(--_ground),
|
|
501
|
+
currentColor var(--slotted-avatar-state-layer-hover, 8%)
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
[data-ui='avatar'][data-part='root']:any-link:not([data-disabled]):active,
|
|
506
|
+
button[data-ui='avatar'][data-part='root']:not([data-disabled]):active,
|
|
507
|
+
[data-ui='avatar'][data-part='root'][tabindex]:not([data-disabled]):active {
|
|
508
|
+
background-color: color-mix(
|
|
509
|
+
in oklab,
|
|
510
|
+
var(--_ground),
|
|
511
|
+
currentColor var(--slotted-avatar-state-layer-active, 14%)
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* A photograph covers the ground, so a face answers on its frame instead: the
|
|
517
|
+
* rim steps to `border-strong` and a ring of the same colour grows outside it,
|
|
518
|
+
* one pixel under the pointer and two under the press. The ring is what keeps
|
|
519
|
+
* the hover visible in `high-contrast`, where the rim is black before and
|
|
520
|
+
* after.
|
|
521
|
+
*
|
|
522
|
+
* One attribute fewer than the group's raise on purpose. Inside a stack the
|
|
523
|
+
* ring belongs to the group — the surface-coloured gap between a raised face
|
|
524
|
+
* and the one it covers — and the lower weight is what lets the group's
|
|
525
|
+
* declaration of the same token win without either file knowing the order
|
|
526
|
+
* the other is loaded in.
|
|
527
|
+
*/
|
|
528
|
+
[data-ui='avatar'][data-status='loaded']:any-link:not([data-disabled]):hover,
|
|
529
|
+
button[data-ui='avatar'][data-status='loaded']:not([data-disabled]):hover,
|
|
530
|
+
[data-ui='avatar'][data-status='loaded'][tabindex]:not([data-disabled]):hover {
|
|
531
|
+
--_edge: var(--slotted-border-strong, CanvasText);
|
|
532
|
+
--slotted-avatar-ring-width: var(--slotted-border-width-thin, 1px);
|
|
533
|
+
--slotted-avatar-ring-color: var(--slotted-border-strong, CanvasText);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
[data-ui='avatar'][data-status='loaded']:any-link:not([data-disabled]):active,
|
|
537
|
+
button[data-ui='avatar'][data-status='loaded']:not([data-disabled]):active,
|
|
538
|
+
[data-ui='avatar'][data-status='loaded'][tabindex]:not([data-disabled]):active {
|
|
539
|
+
--slotted-avatar-ring-width: var(--slotted-border-width-thick, 2px);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Painted whatever the element turned out to be. An avatar is not focusable
|
|
544
|
+
* on its own, and a ring that appears only where a family expects focus is a
|
|
545
|
+
* ring that goes missing the first time a consumer makes one a link.
|
|
546
|
+
*/
|
|
547
|
+
[data-ui='avatar'][data-part='root']:focus-visible {
|
|
548
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
549
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* WCAG 2.2 2.5.8: an interactive target is at least 24 by 24 CSS pixels, and
|
|
554
|
+
* `--slotted-avatar-size-xs` is twenty at the two tighter densities. The
|
|
555
|
+
* overlay grows the hit area without moving anything: it is out of flow, and
|
|
556
|
+
* it is never larger than the box it belongs to when the box already passes.
|
|
557
|
+
*
|
|
558
|
+
* One negative inset on all four sides, and not a size with `margin: auto`.
|
|
559
|
+
* Auto margins centre a box that fits; a box wider than the space it is
|
|
560
|
+
* placed in is over-constrained, and the rule the engine applies then is to
|
|
561
|
+
* keep the start margin and put the whole difference on the end one. So the
|
|
562
|
+
* 24-pixel target sat against the leading edge of a 20-pixel avatar and hung
|
|
563
|
+
* four pixels off the trailing one: half the target was outside the face on
|
|
564
|
+
* one side and the other half was missing on the other, which is what AV1
|
|
565
|
+
* measured as four pixels of overflow. `inset` states the same target from
|
|
566
|
+
* both edges at once, so it cannot be resolved to one side, and `min(0px, …)`
|
|
567
|
+
* makes it inert on every box that already reaches the minimum. It is the
|
|
568
|
+
* idiom `tag` and `breadcrumb` use for the same requirement.
|
|
569
|
+
*
|
|
570
|
+
* Fluent ships a 16-pixel avatar and Atlassian a 16- and a 20-pixel one, and
|
|
571
|
+
* both accept a click on it as it stands.
|
|
572
|
+
*/
|
|
573
|
+
[data-ui='avatar'][data-part='root']:any-link::after,
|
|
574
|
+
button[data-ui='avatar'][data-part='root']::after,
|
|
575
|
+
[data-ui='avatar'][data-part='root'][tabindex]::after {
|
|
576
|
+
content: '';
|
|
577
|
+
inset: min(0px, calc((100% - var(--slotted-target-min, 24px)) / 2));
|
|
578
|
+
position: absolute;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* ── Forced colours ─────────────────────────────────────────────────────
|
|
583
|
+
*
|
|
584
|
+
* The theme maps every tone ground to `ButtonFace` or `Canvas`, so a tinted
|
|
585
|
+
* avatar and a filled one become the same disc and an avatar whose edge
|
|
586
|
+
* happened to match its fill has no edge at all. `currentColor` is the only
|
|
587
|
+
* paint left that still separates a face from the page.
|
|
588
|
+
*
|
|
589
|
+
* The picture is exempt: `forced-color-adjust` is not applied to images by
|
|
590
|
+
* the user agent, and a photograph reduced to two system colours is not a
|
|
591
|
+
* photograph.
|
|
592
|
+
*/
|
|
593
|
+
@media (forced-colors: active) {
|
|
594
|
+
[data-ui='avatar'][data-part='root'] {
|
|
595
|
+
--_edge: currentColor;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|