@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,1453 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── One message of a conversation ──────────────────────────────────────
|
|
4
|
+
*
|
|
5
|
+
* Two members under one identifier: the row a sender speaks in
|
|
6
|
+
* (`data-part="root"`, an `article`) and the surface the words are painted
|
|
7
|
+
* on (`data-part="bubble"`). A bubble nests inside a row, so its root is not
|
|
8
|
+
* written `root` — every selector for the row would match it
|
|
9
|
+
* (`specs/components/message/contract.json` › `rootPartReason`).
|
|
10
|
+
*
|
|
11
|
+
* The side is asked once, on the row. The bubble, its reactions and the
|
|
12
|
+
* header follow `data-align`; the surveyed shadcn examples pass `align` to
|
|
13
|
+
* both `Message` and `Bubble`, and two answers to one question can disagree
|
|
14
|
+
* (`docs/history/designs/2026-09-30-message-design.md` §2.2).
|
|
15
|
+
*/
|
|
16
|
+
[data-ui='message'][data-part='root'] {
|
|
17
|
+
/**
|
|
18
|
+
* The avatar column is the small avatar step, which the theme restates per
|
|
19
|
+
* density; ReUI's 32 has no step there (dossier §3), and a row that follows
|
|
20
|
+
* the density of the page around it is worth four pixels.
|
|
21
|
+
*/
|
|
22
|
+
--_avatar-size: var(--slotted-message-avatar-size, var(--slotted-avatar-size-sm, 28px));
|
|
23
|
+
--_column-gap: var(--slotted-message-column-gap, var(--slotted-space-4, 0.5rem));
|
|
24
|
+
--_stack-gap: var(--slotted-message-stack-gap, var(--slotted-space-2, 0.25rem));
|
|
25
|
+
--_meta-size: var(--slotted-font-size-xs, 0.75rem);
|
|
26
|
+
--_meta-leading: round(up, calc(var(--_meta-size) * var(--slotted-line-height-xs, 1.4)), 1px);
|
|
27
|
+
/**
|
|
28
|
+
* The inset of the words inside a bubble, which the header and the footer
|
|
29
|
+
* repeat so a name stands over the first letter it names. Eight and twelve
|
|
30
|
+
* at comfortable, ReUI's measure (`c-bubble-2`), falling with the control
|
|
31
|
+
* gap the theme restates per density.
|
|
32
|
+
*/
|
|
33
|
+
--_pad-block: var(--slotted-message-bubble-padding-block, var(--slotted-control-gap-md, 8px));
|
|
34
|
+
--_pad-inline: var(
|
|
35
|
+
--slotted-message-bubble-padding-inline,
|
|
36
|
+
calc(var(--slotted-control-gap-md, 8px) + var(--slotted-space-2, 0.25rem))
|
|
37
|
+
);
|
|
38
|
+
/**
|
|
39
|
+
* The inner controls of a row — the version pager — at the R2 size: the
|
|
40
|
+
* glyph plus a step of air on each side, never under the WCAG 2.2 target.
|
|
41
|
+
*/
|
|
42
|
+
--_glyph-size: var(--slotted-control-icon-size-sm, 14px);
|
|
43
|
+
--_inner-size: max(
|
|
44
|
+
var(--slotted-target-min, 24px),
|
|
45
|
+
calc(var(--_glyph-size) + 2 * var(--slotted-space-2, 0.25rem))
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
display: grid;
|
|
49
|
+
grid-template-areas: 'body';
|
|
50
|
+
grid-template-columns: minmax(0, 1fr);
|
|
51
|
+
column-gap: var(--_column-gap);
|
|
52
|
+
position: relative;
|
|
53
|
+
min-inline-size: 0;
|
|
54
|
+
color: var(--slotted-text);
|
|
55
|
+
font-family: var(--slotted-font-family-sans, sans-serif);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A row with an avatar, and every row of a run that holds one anywhere,
|
|
60
|
+
* gives the avatar a column. The earlier rows of a run carry no avatar and
|
|
61
|
+
* still stand on the spine the last one sits on — shadcn asks for an empty
|
|
62
|
+
* `MessageAvatar` in each of them (`c-message-6`); the column is read here
|
|
63
|
+
* from the DOM instead.
|
|
64
|
+
*/
|
|
65
|
+
[data-ui='message'][data-part='root']:has(> [data-part='avatar']),
|
|
66
|
+
[data-ui='message'][data-part='group']:has([data-part='avatar'])
|
|
67
|
+
> [data-ui='message'][data-part='root'] {
|
|
68
|
+
grid-template-areas: 'avatar body';
|
|
69
|
+
grid-template-columns: var(--_avatar-size) minmax(0, 1fr);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
[data-ui='message'][data-part='root'][data-align='end']:has(> [data-part='avatar']),
|
|
73
|
+
[data-ui='message'][data-part='group']:has([data-part='avatar'])
|
|
74
|
+
> [data-ui='message'][data-part='root'][data-align='end'] {
|
|
75
|
+
grid-template-areas: 'body avatar';
|
|
76
|
+
grid-template-columns: minmax(0, 1fr) var(--_avatar-size);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The avatar sits at the foot of the row, on the line of the last bubble
|
|
81
|
+
* rather than of the footer under it: in a run it marks where the run ends,
|
|
82
|
+
* which is where the reader's eye arrives.
|
|
83
|
+
*/
|
|
84
|
+
[data-ui='message'][data-part='root'] > [data-part='avatar'] {
|
|
85
|
+
grid-area: avatar;
|
|
86
|
+
display: flex;
|
|
87
|
+
align-self: end;
|
|
88
|
+
justify-content: center;
|
|
89
|
+
inline-size: var(--_avatar-size);
|
|
90
|
+
min-block-size: var(--_avatar-size);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
[data-ui='message'][data-part='root']:has(> [data-part='body'] > [data-part='footer'])
|
|
94
|
+
> [data-part='avatar'] {
|
|
95
|
+
margin-block-end: calc(var(--_meta-leading) + var(--_stack-gap));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-ui='message'][data-part='root'] > [data-part='body'] {
|
|
99
|
+
grid-area: body;
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-direction: column;
|
|
102
|
+
align-items: flex-start;
|
|
103
|
+
gap: var(--_stack-gap);
|
|
104
|
+
min-inline-size: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[data-ui='message'][data-part='root'][data-align='end'] > [data-part='body'] {
|
|
108
|
+
align-items: flex-end;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* ── A run from one sender ──────────────────────────────────────────────
|
|
113
|
+
*
|
|
114
|
+
* Two pixels between the rows of a run, where ReUI puts eight (`c-bubble-3`).
|
|
115
|
+
* At eight the run read as four messages; at two, with the corners on the
|
|
116
|
+
* sender's side drawn in, it reads as one speaker holding the floor. The
|
|
117
|
+
* gap between runs belongs to whatever stacks them — the scroller gives it.
|
|
118
|
+
*/
|
|
119
|
+
[data-ui='message'][data-part='group'] {
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
gap: var(--slotted-message-run-gap, var(--slotted-space-1, 0.125rem));
|
|
123
|
+
min-inline-size: 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* ── Header and footer ──────────────────────────────────────────────── */
|
|
127
|
+
|
|
128
|
+
[data-ui='message'][data-part='root'] :is([data-part='header'], [data-part='footer']) {
|
|
129
|
+
display: flex;
|
|
130
|
+
flex-wrap: wrap;
|
|
131
|
+
align-items: center;
|
|
132
|
+
column-gap: var(--slotted-space-3, 0.375rem);
|
|
133
|
+
max-inline-size: 100%;
|
|
134
|
+
min-inline-size: 0;
|
|
135
|
+
padding-inline: var(--_pad-inline);
|
|
136
|
+
color: var(--slotted-text-muted);
|
|
137
|
+
font-size: var(--_meta-size);
|
|
138
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
139
|
+
line-height: var(--_meta-leading);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
[data-ui='message'][data-part='root'][data-align='end']
|
|
143
|
+
:is([data-part='header'], [data-part='footer']) {
|
|
144
|
+
justify-content: flex-end;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The name and the hour are two facts, and the dot between them is drawn by
|
|
149
|
+
* the stylesheet rather than written into the text: a character would be
|
|
150
|
+
* read aloud, copied with the name and wrapped onto a line of its own.
|
|
151
|
+
* ReUI asks the consumer to write the dot (`c-message-3`).
|
|
152
|
+
*/
|
|
153
|
+
[data-ui='message'][data-part='root'] [data-part='header'] > * + [data-part='time']::before {
|
|
154
|
+
content: '';
|
|
155
|
+
display: inline-block;
|
|
156
|
+
inline-size: var(--slotted-space-2, 0.25rem);
|
|
157
|
+
block-size: var(--slotted-space-2, 0.25rem);
|
|
158
|
+
margin-inline-end: var(--slotted-space-3, 0.375rem);
|
|
159
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
160
|
+
background-color: currentColor;
|
|
161
|
+
opacity: 0.5;
|
|
162
|
+
vertical-align: middle;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
[data-ui='message'][data-part='root'] [data-part='author'] {
|
|
166
|
+
min-inline-size: 0;
|
|
167
|
+
overflow: hidden;
|
|
168
|
+
text-overflow: ellipsis;
|
|
169
|
+
white-space: nowrap;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
[data-ui='message'][data-part='root'] [data-part='time'] {
|
|
173
|
+
font-variant-numeric: tabular-nums;
|
|
174
|
+
white-space: nowrap;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* ── Receipt ──────────────────────────────────────────────────────────
|
|
178
|
+
*
|
|
179
|
+
* Where a message is in its delivery is said inside the bubble, in `meta`
|
|
180
|
+
* (below), as a mark beside the hour — the convention WhatsApp and Telegram
|
|
181
|
+
* taught. Outside a bubble — an unframed reply, a thread — the receipt is a
|
|
182
|
+
* word in the footer, set in the meta type, with no mark: a glyph beside a
|
|
183
|
+
* word of twelve pixels is two voices saying one thing, and the two never
|
|
184
|
+
* shared a centre line.
|
|
185
|
+
*/
|
|
186
|
+
[data-ui='message'][data-part='root'] [data-part='footer'] > [data-part='receipt'] {
|
|
187
|
+
white-space: nowrap;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
[data-ui='message'][data-part='root']
|
|
191
|
+
[data-part='footer']
|
|
192
|
+
> [data-part='receipt']
|
|
193
|
+
> [data-ui='icon'] {
|
|
194
|
+
display: none;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* A message that did not go says so in a word under its surface, in the
|
|
199
|
+
* danger ink, lined up with the surface's own edge on the sender's side —
|
|
200
|
+
* not with the words inside it — so the note reads as the bubble's caption.
|
|
201
|
+
* The control that sends it again stands beside the surface (`retry`).
|
|
202
|
+
*/
|
|
203
|
+
[data-ui='message'][data-part='root'][data-invalid] > [data-part='body'] > [data-part='footer'] {
|
|
204
|
+
padding-inline: var(--slotted-space-2, 0.25rem);
|
|
205
|
+
color: var(--slotted-tone-danger-text);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* ── Actions ────────────────────────────────────────────────────────────
|
|
209
|
+
*
|
|
210
|
+
* The actions keep their room at rest, so a revealed toolbar moves nothing.
|
|
211
|
+
* `hover` reveals them on the pointer, on focus anywhere in the row, and
|
|
212
|
+
* always on a screen that cannot hover: a reveal by hover alone would hide
|
|
213
|
+
* them from the keyboard and from a finger.
|
|
214
|
+
*/
|
|
215
|
+
[data-ui='message'][data-part='root'] [data-part='actions'] {
|
|
216
|
+
display: inline-flex;
|
|
217
|
+
align-items: center;
|
|
218
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
219
|
+
transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit, ease-in);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@media (hover: hover) {
|
|
223
|
+
[data-ui='message'][data-part='root'][data-actions='hover'] [data-part='actions'] {
|
|
224
|
+
opacity: 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
[data-ui='message'][data-part='root'][data-actions='hover']:is(
|
|
228
|
+
:hover:not([data-disabled]),
|
|
229
|
+
:focus-within
|
|
230
|
+
)
|
|
231
|
+
[data-part='actions'] {
|
|
232
|
+
opacity: 1;
|
|
233
|
+
transition-timing-function: var(--slotted-easing-enter, ease-out);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* ── Activity ───────────────────────────────────────────────────────────
|
|
238
|
+
*
|
|
239
|
+
* What a reply is doing before it has words, as a short log of steps on a
|
|
240
|
+
* raised card: the steps already done carry a tick and step back into the
|
|
241
|
+
* muted ink, and the step in progress carries the shared ring (`spinner`)
|
|
242
|
+
* and a band of light that travels across its words. A single step is the
|
|
243
|
+
* same card with one line. ReUI's `c-message-7` draws one muted line that
|
|
244
|
+
* pulses; the log says what has been done, not only that something is.
|
|
245
|
+
*
|
|
246
|
+
* The band is the one place the family paints text with a gradient, which
|
|
247
|
+
* the polish lens refuses as decoration: here it is the wait itself, it
|
|
248
|
+
* moves only while the step is current, and under reduced motion the words
|
|
249
|
+
* stand in the plain ink.
|
|
250
|
+
*/
|
|
251
|
+
[data-ui='message'][data-part='root'] [data-part='activity'] {
|
|
252
|
+
display: inline-flex;
|
|
253
|
+
flex-direction: column;
|
|
254
|
+
gap: var(--slotted-space-3, 0.375rem);
|
|
255
|
+
box-sizing: border-box;
|
|
256
|
+
max-inline-size: 100%;
|
|
257
|
+
padding-block: var(--_pad-block);
|
|
258
|
+
padding-inline: var(--_pad-inline) calc(var(--_pad-inline) + var(--slotted-space-2, 0.25rem));
|
|
259
|
+
border: var(--slotted-border-width-thin, 1px) solid var(--slotted-border-subtle);
|
|
260
|
+
border-radius: calc(var(--_pad-block) * 2);
|
|
261
|
+
background-color: var(--slotted-surface-raised);
|
|
262
|
+
box-shadow: var(--slotted-shadow-xs);
|
|
263
|
+
color: var(--slotted-text-muted);
|
|
264
|
+
font-size: var(--slotted-control-font-size-md, 13px);
|
|
265
|
+
line-height: round(up, calc(1em * var(--slotted-line-height-md, 1.5)), 1px);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
[data-ui='message'][data-part='root'] [data-part='activity-step'] {
|
|
269
|
+
display: flex;
|
|
270
|
+
align-items: center;
|
|
271
|
+
gap: var(--slotted-space-4, 0.5rem);
|
|
272
|
+
min-inline-size: 0;
|
|
273
|
+
transition:
|
|
274
|
+
opacity var(--slotted-duration-medium) var(--slotted-easing-enter, ease-out),
|
|
275
|
+
translate var(--slotted-duration-medium) var(--slotted-easing-enter, ease-out),
|
|
276
|
+
color var(--slotted-duration-medium) var(--slotted-easing-standard, ease);
|
|
277
|
+
|
|
278
|
+
@starting-style {
|
|
279
|
+
opacity: 0;
|
|
280
|
+
translate: 0 var(--slotted-space-2, 0.25rem);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** The mark column: a tick, or the ring, in one box so the words line up. */
|
|
285
|
+
[data-ui='message'][data-part='root'] [data-part='activity-step'] > :first-child {
|
|
286
|
+
--slotted-icon-size: var(--_glyph-size);
|
|
287
|
+
--slotted-spinner-size: var(--_glyph-size);
|
|
288
|
+
|
|
289
|
+
display: inline-grid;
|
|
290
|
+
flex: none;
|
|
291
|
+
place-items: center;
|
|
292
|
+
inline-size: var(--_glyph-size);
|
|
293
|
+
block-size: var(--_glyph-size);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
[data-ui='message'][data-part='root'] [data-part='activity-step'][data-complete] > :first-child {
|
|
297
|
+
color: var(--slotted-tone-success-text);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
[data-ui='message'][data-part='root'] [data-part='activity-step'][data-current] {
|
|
301
|
+
color: var(--slotted-text);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
[data-ui='message'][data-part='root'] [data-part='activity-step'][data-current] > :first-child {
|
|
305
|
+
color: var(--slotted-tone-accent-text);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
[data-ui='message'][data-part='root'] [data-part='activity-step'][data-current] > :last-child {
|
|
309
|
+
--_base: color-mix(in oklab, var(--slotted-text) 72%, var(--slotted-text-muted));
|
|
310
|
+
|
|
311
|
+
background-image: linear-gradient(
|
|
312
|
+
90deg,
|
|
313
|
+
var(--_base) 0%,
|
|
314
|
+
var(--_base) 38%,
|
|
315
|
+
var(--slotted-text-muted) 50%,
|
|
316
|
+
var(--_base) 62%,
|
|
317
|
+
var(--_base) 100%
|
|
318
|
+
);
|
|
319
|
+
background-size: 220% 100%;
|
|
320
|
+
background-clip: text;
|
|
321
|
+
color: transparent;
|
|
322
|
+
animation: slotted-message-sweep var(--slotted-duration-loop-slow) linear infinite;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
@keyframes slotted-message-sweep {
|
|
326
|
+
from {
|
|
327
|
+
background-position: 100% 0;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
to {
|
|
331
|
+
background-position: 0% 0;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* ── Versions ───────────────────────────────────────────────────────────
|
|
336
|
+
*
|
|
337
|
+
* `2 de 3` between two inner controls. The chevron is one glyph turned for
|
|
338
|
+
* the previous control, and a right-to-left document turns both.
|
|
339
|
+
*/
|
|
340
|
+
[data-ui='message'][data-part='root'] [data-part='versions'] {
|
|
341
|
+
display: inline-flex;
|
|
342
|
+
align-items: center;
|
|
343
|
+
color: var(--slotted-text-muted);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
[data-ui='message'][data-part='root'] [data-part='version-label'] {
|
|
347
|
+
padding-inline: var(--slotted-space-2, 0.25rem);
|
|
348
|
+
font-variant-numeric: tabular-nums;
|
|
349
|
+
white-space: nowrap;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
[data-ui='message'][data-part='root']
|
|
353
|
+
:is([data-part='version-previous'], [data-part='version-next']) {
|
|
354
|
+
display: inline-grid;
|
|
355
|
+
place-items: center;
|
|
356
|
+
inline-size: var(--_inner-size);
|
|
357
|
+
block-size: var(--_inner-size);
|
|
358
|
+
margin: 0;
|
|
359
|
+
padding: 0;
|
|
360
|
+
border: 0;
|
|
361
|
+
border-radius: var(--slotted-control-radius, 4px);
|
|
362
|
+
background-color: transparent;
|
|
363
|
+
color: inherit;
|
|
364
|
+
font: inherit;
|
|
365
|
+
transition:
|
|
366
|
+
background-color var(--slotted-duration-short) var(--slotted-easing-standard, ease),
|
|
367
|
+
color var(--slotted-duration-short) var(--slotted-easing-standard, ease);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
[data-ui='message'][data-part='root']
|
|
371
|
+
:is([data-part='version-previous'], [data-part='version-next'])
|
|
372
|
+
> [data-ui='icon'] {
|
|
373
|
+
--slotted-icon-size: var(--_glyph-size);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
[data-ui='message'][data-part='root'] [data-part='version-previous'] > [data-ui='icon'] {
|
|
377
|
+
scale: -1 1;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
[data-ui='message'][data-part='root']:dir(rtl)
|
|
381
|
+
:is([data-part='version-previous'], [data-part='version-next'])
|
|
382
|
+
> [data-ui='icon'] {
|
|
383
|
+
scale: -1 1;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
[data-ui='message'][data-part='root']:dir(rtl) [data-part='version-previous'] > [data-ui='icon'] {
|
|
387
|
+
scale: 1 1;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
[data-ui='message'][data-part='root']
|
|
391
|
+
:is([data-part='version-previous'], [data-part='version-next']):hover:not(:disabled) {
|
|
392
|
+
background-color: var(--slotted-tone-secondary-subtle-hover);
|
|
393
|
+
color: var(--slotted-text);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
[data-ui='message'][data-part='root']
|
|
397
|
+
:is([data-part='version-previous'], [data-part='version-next']):active:not(:disabled) {
|
|
398
|
+
background-color: var(--slotted-tone-secondary-subtle-active);
|
|
399
|
+
color: var(--slotted-text);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
[data-ui='message'][data-part='root']
|
|
403
|
+
:is([data-part='version-previous'], [data-part='version-next']):disabled {
|
|
404
|
+
color: var(--slotted-disabled-foreground);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
[data-ui='message'][data-part='root']
|
|
408
|
+
:is([data-part='version-previous'], [data-part='version-next']):focus-visible {
|
|
409
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color);
|
|
410
|
+
outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/* ── A message that did not go ─────────────────────────────────────────
|
|
414
|
+
*
|
|
415
|
+
* The words stay as they were written, on their own surface. A red rim round
|
|
416
|
+
* a blue bubble said "error" twice and read as a second, clashing bubble,
|
|
417
|
+
* and a faded fill dropped white words on the accent under 4.5:1 (4.2 at
|
|
418
|
+
* 85%). What says it is a round control beside the surface, in the danger
|
|
419
|
+
* tone, which is also the way to send it again (`bubble` › `retry`,
|
|
420
|
+
* iMessage's placement), and the receipt under it, in words.
|
|
421
|
+
*/
|
|
422
|
+
|
|
423
|
+
/* ── Layout: thread ─────────────────────────────────────────────────────
|
|
424
|
+
*
|
|
425
|
+
* The comment thread (Slack, ReUI `sheet-5`): every row at the start, the
|
|
426
|
+
* avatar at the top beside the name, the words unframed under it, and a
|
|
427
|
+
* band under the pointer that says which message the actions belong to.
|
|
428
|
+
*/
|
|
429
|
+
[data-ui='message'][data-part='root'][data-layout='thread'] {
|
|
430
|
+
--_avatar-size: var(--slotted-message-thread-avatar-size, var(--slotted-avatar-size-md, 36px));
|
|
431
|
+
--_pad-inline: 0px;
|
|
432
|
+
|
|
433
|
+
padding-block: var(--slotted-space-3, 0.375rem);
|
|
434
|
+
padding-inline: var(--slotted-space-4, 0.5rem);
|
|
435
|
+
border-radius: var(--slotted-radius-md, 6px);
|
|
436
|
+
transition: background-color var(--slotted-duration-short) var(--slotted-easing-standard, ease);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
[data-ui='message'][data-part='root'][data-layout='thread'] > [data-part='avatar'] {
|
|
440
|
+
align-self: start;
|
|
441
|
+
margin-block-end: 0;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
[data-ui='message'][data-part='root'][data-layout='thread'] [data-part='author'] {
|
|
445
|
+
color: var(--slotted-text);
|
|
446
|
+
font-size: var(--slotted-control-font-size-md, 13px);
|
|
447
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
[data-ui='message'][data-part='root'][data-layout='thread']:hover:not([data-disabled]) {
|
|
451
|
+
background-color: var(--slotted-tone-secondary-subtle-hover);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* In a thread the actions stand in the flow under the words, on the words'
|
|
456
|
+
* column. Where the pointer can hover and the row reveals them, they float
|
|
457
|
+
* instead at the row's top end over the band, the way every comment surface
|
|
458
|
+
* places them — a toolbar that appears only under the pointer takes no room.
|
|
459
|
+
* A plate that is always painted, on a touch screen or with `always`, floated
|
|
460
|
+
* over the words and covered them at 360 pixels.
|
|
461
|
+
*/
|
|
462
|
+
[data-ui='message'][data-part='root'][data-layout='thread'] > [data-part='actions'] {
|
|
463
|
+
grid-column: body;
|
|
464
|
+
justify-self: start;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
@media (hover: hover) {
|
|
468
|
+
[data-ui='message'][data-part='root'][data-layout='thread'][data-actions='hover']
|
|
469
|
+
> [data-part='actions'] {
|
|
470
|
+
position: absolute;
|
|
471
|
+
inset-block-start: calc(var(--slotted-space-2, 0.25rem) * -1);
|
|
472
|
+
inset-inline-end: var(--slotted-space-4, 0.5rem);
|
|
473
|
+
padding: var(--slotted-space-1, 0.125rem);
|
|
474
|
+
border: var(--slotted-border-width-thin, 1px) solid var(--slotted-border-subtle);
|
|
475
|
+
border-radius: var(--slotted-radius-md, 6px);
|
|
476
|
+
background-color: var(--slotted-surface-raised);
|
|
477
|
+
box-shadow: var(--slotted-shadow-sm);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/* ══ The bubble ════════════════════════════════════════════════════════ */
|
|
482
|
+
|
|
483
|
+
[data-ui='message'][data-part='bubble'] {
|
|
484
|
+
/**
|
|
485
|
+
* The five tones, read into the roles a painting needs. `soft` writes in
|
|
486
|
+
* the page's ink on the accent and secondary grounds — a paragraph set in
|
|
487
|
+
* blue is a link — and in the tone's own ink on the three status tones,
|
|
488
|
+
* where the colour of the words is the point.
|
|
489
|
+
*/
|
|
490
|
+
--_solid: var(--slotted-tone-secondary-solid);
|
|
491
|
+
--_on-solid: var(--slotted-tone-secondary-on-solid);
|
|
492
|
+
--_surface: var(--slotted-tone-secondary-surface);
|
|
493
|
+
--_border: var(--slotted-border-subtle);
|
|
494
|
+
--_soft-ink: var(--slotted-text);
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Twice the block inset: sixteen at comfortable, where ReUI draws fourteen
|
|
498
|
+
* (no step), falling to twelve and eight with the density. A one-line
|
|
499
|
+
* bubble is 39, 34 and 28 pixels tall at the three densities, so the
|
|
500
|
+
* corner never meets its twin and a bubble stays a bubble rather than a
|
|
501
|
+
* pill; a stated sixteen made the dense bubble a capsule.
|
|
502
|
+
*/
|
|
503
|
+
--_radius: var(--slotted-message-bubble-radius, calc(var(--_pad-block) * 2));
|
|
504
|
+
--_radius-joined: var(--slotted-message-bubble-radius-joined, var(--slotted-radius-sm, 4px));
|
|
505
|
+
/* The row's inset, restated so a bubble outside a row keeps the density. */
|
|
506
|
+
--_pad-block: var(--slotted-message-bubble-padding-block, var(--slotted-control-gap-md, 8px));
|
|
507
|
+
--_pad-inline: var(
|
|
508
|
+
--slotted-message-bubble-padding-inline,
|
|
509
|
+
calc(var(--slotted-control-gap-md, 8px) + var(--slotted-space-2, 0.25rem))
|
|
510
|
+
);
|
|
511
|
+
--_top-side: var(--_radius);
|
|
512
|
+
--_bottom-side: var(--_radius);
|
|
513
|
+
--_font-size: var(--slotted-control-font-size-lg, 14px);
|
|
514
|
+
/**
|
|
515
|
+
* The line rounded up to a whole pixel: fourteen at 1.5 is 21, and a
|
|
516
|
+
* one-line bubble stands at 21 + 2 × 8 + 2 × 1 = 39. ReUI's 1.625 put it at
|
|
517
|
+
* 40.75, with every edge below the first line between two device pixels
|
|
518
|
+
* (dossier §3).
|
|
519
|
+
*/
|
|
520
|
+
--_leading: round(up, calc(var(--_font-size) * var(--slotted-line-height-md, 1.5)), 1px);
|
|
521
|
+
--_reactions-size: max(
|
|
522
|
+
var(--slotted-target-min, 24px),
|
|
523
|
+
var(--slotted-control-inner-size-md, 24px)
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
display: flex;
|
|
527
|
+
flex-direction: column;
|
|
528
|
+
align-items: flex-start;
|
|
529
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
530
|
+
position: relative;
|
|
531
|
+
inline-size: fit-content;
|
|
532
|
+
min-inline-size: 0;
|
|
533
|
+
/**
|
|
534
|
+
* A bubble never reaches the far side of its row, so the side it hangs
|
|
535
|
+
* from stays readable: ReUI's 80%. It is a share of the row, not a measure
|
|
536
|
+
* of the text — the measure belongs to `container`.
|
|
537
|
+
*/
|
|
538
|
+
max-inline-size: var(--slotted-message-bubble-share, 80%);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
[data-ui='message'][data-part='bubble'][data-tone='accent'] {
|
|
542
|
+
--_solid: var(--slotted-tone-accent-solid);
|
|
543
|
+
--_on-solid: var(--slotted-tone-accent-on-solid);
|
|
544
|
+
--_surface: var(--slotted-tone-accent-surface);
|
|
545
|
+
--_border: var(--slotted-tone-accent-border);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
[data-ui='message'][data-part='bubble'][data-tone='success'] {
|
|
549
|
+
--_solid: var(--slotted-tone-success-solid);
|
|
550
|
+
--_on-solid: var(--slotted-tone-success-on-solid);
|
|
551
|
+
--_surface: var(--slotted-tone-success-surface);
|
|
552
|
+
--_border: var(--slotted-tone-success-border);
|
|
553
|
+
--_soft-ink: var(--slotted-tone-success-text);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
[data-ui='message'][data-part='bubble'][data-tone='warning'] {
|
|
557
|
+
--_solid: var(--slotted-tone-warning-solid);
|
|
558
|
+
--_on-solid: var(--slotted-tone-warning-on-solid);
|
|
559
|
+
--_surface: var(--slotted-tone-warning-surface);
|
|
560
|
+
--_border: var(--slotted-tone-warning-border);
|
|
561
|
+
--_soft-ink: var(--slotted-tone-warning-text);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
[data-ui='message'][data-part='bubble'][data-tone='danger'] {
|
|
565
|
+
--_solid: var(--slotted-tone-danger-solid);
|
|
566
|
+
--_on-solid: var(--slotted-tone-danger-on-solid);
|
|
567
|
+
--_surface: var(--slotted-tone-danger-surface);
|
|
568
|
+
--_border: var(--slotted-tone-danger-border);
|
|
569
|
+
--_soft-ink: var(--slotted-tone-danger-text);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* ── Variant: four paintings ────────────────────────────────────────────
|
|
574
|
+
*
|
|
575
|
+
* `solid` is the tone's fill with its own ink and no rim. `soft` is the
|
|
576
|
+
* tone's tinted ground; its rim is that ground mixed half-way to the subtle
|
|
577
|
+
* rule, which is next to invisible in `light` and `dark` and a grey line in
|
|
578
|
+
* `high-contrast`, where every tone surface is the page's white and a bubble
|
|
579
|
+
* without an edge is a paragraph. `outline` is the tone's edge around the
|
|
580
|
+
* page's ground, `plain` no box at all — the unframed assistant answer that
|
|
581
|
+
* takes the whole row.
|
|
582
|
+
*/
|
|
583
|
+
[data-ui='message'][data-part='bubble'] > [data-part='content'] {
|
|
584
|
+
--_ground: var(--_surface);
|
|
585
|
+
--_ink: var(--_soft-ink);
|
|
586
|
+
--_edge: color-mix(in oklab, var(--_surface), var(--slotted-border-subtle));
|
|
587
|
+
|
|
588
|
+
display: block;
|
|
589
|
+
box-sizing: border-box;
|
|
590
|
+
min-inline-size: 0;
|
|
591
|
+
max-inline-size: 100%;
|
|
592
|
+
margin: 0;
|
|
593
|
+
padding-block: var(--_pad-block, 8px);
|
|
594
|
+
padding-inline: var(--_pad-inline, 12px);
|
|
595
|
+
border: var(--slotted-border-width-thin, 1px) solid var(--_edge);
|
|
596
|
+
border-start-start-radius: var(--_top-side);
|
|
597
|
+
border-start-end-radius: var(--_radius);
|
|
598
|
+
border-end-start-radius: var(--_bottom-side);
|
|
599
|
+
border-end-end-radius: var(--_radius);
|
|
600
|
+
background-color: var(--_ground);
|
|
601
|
+
color: var(--_ink);
|
|
602
|
+
font-family: inherit;
|
|
603
|
+
font-size: var(--_font-size);
|
|
604
|
+
font-weight: var(--slotted-font-weight-regular, 400);
|
|
605
|
+
line-height: var(--_leading);
|
|
606
|
+
text-align: start;
|
|
607
|
+
text-decoration: none;
|
|
608
|
+
/**
|
|
609
|
+
* A URL or a hash of three hundred characters breaks where it must rather
|
|
610
|
+
* than pushing the bubble past its share, and each paragraph takes the
|
|
611
|
+
* direction of its own first strong letter: Arabic in an English thread
|
|
612
|
+
* reads from its own side.
|
|
613
|
+
*/
|
|
614
|
+
overflow-wrap: anywhere;
|
|
615
|
+
unicode-bidi: plaintext;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
[data-ui='message'][data-part='bubble'][data-variant='solid'] > [data-part='content'] {
|
|
619
|
+
--_ground: var(--_solid);
|
|
620
|
+
--_ink: var(--_on-solid);
|
|
621
|
+
--_edge: var(--_solid);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
[data-ui='message'][data-part='bubble'][data-variant='outline'] > [data-part='content'] {
|
|
625
|
+
--_ground: var(--slotted-surface);
|
|
626
|
+
--_ink: var(--slotted-text);
|
|
627
|
+
--_edge: var(--_border);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
[data-ui='message'][data-part='bubble'][data-variant='plain'] {
|
|
631
|
+
--_pad-inline: 0px;
|
|
632
|
+
|
|
633
|
+
max-inline-size: 100%;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
[data-ui='message'][data-part='bubble'][data-variant='plain'] > [data-part='content'] {
|
|
637
|
+
--_ground: transparent;
|
|
638
|
+
--_ink: var(--slotted-text);
|
|
639
|
+
--_edge: transparent;
|
|
640
|
+
|
|
641
|
+
padding: 0;
|
|
642
|
+
border-width: 0;
|
|
643
|
+
border-radius: 0;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* The bubble sits on the sender's side, and so does everything it carries:
|
|
648
|
+
* the corners that join a run are the ones that face the avatar.
|
|
649
|
+
*/
|
|
650
|
+
[data-ui='message'][data-part='root'][data-align='end'] [data-part='bubble'] {
|
|
651
|
+
align-items: flex-end;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
[data-ui='message'][data-part='root'][data-align='end']
|
|
655
|
+
[data-part='bubble']
|
|
656
|
+
> [data-part='content'] {
|
|
657
|
+
border-start-start-radius: var(--_radius);
|
|
658
|
+
border-start-end-radius: var(--_top-side);
|
|
659
|
+
border-end-start-radius: var(--_radius);
|
|
660
|
+
border-end-end-radius: var(--_bottom-side);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* ── The corners of a run ───────────────────────────────────────────────
|
|
665
|
+
*
|
|
666
|
+
* Bubbles that follow one another from one sender draw in the corners that
|
|
667
|
+
* face each other on the sender's side, inside one row and across the rows
|
|
668
|
+
* of a group. A run reads as one speaker by its shape, with no position
|
|
669
|
+
* prop on each message (Chatscope asks for `first`, `normal`, `last`).
|
|
670
|
+
*/
|
|
671
|
+
[data-ui='message'][data-part='bubble']:has(~ [data-part='bubble']),
|
|
672
|
+
[data-ui='message'][data-part='group']
|
|
673
|
+
> [data-ui='message'][data-part='root']:not(:last-child)
|
|
674
|
+
> [data-part='body']
|
|
675
|
+
> [data-part='bubble']:not(:has(~ [data-part='bubble'])) {
|
|
676
|
+
--_bottom-side: var(--_radius-joined);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
[data-ui='message'][data-part='bubble'] ~ [data-part='bubble'],
|
|
680
|
+
[data-ui='message'][data-part='group']
|
|
681
|
+
> [data-ui='message'][data-part='root']:not(:first-child)
|
|
682
|
+
> [data-part='body']
|
|
683
|
+
> [data-part='bubble']:not([data-part='bubble'] ~ [data-part='bubble']) {
|
|
684
|
+
--_top-side: var(--_radius-joined);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* ── A bubble that is a control ─────────────────────────────────────────
|
|
689
|
+
*
|
|
690
|
+
* A quick reply is the content itself as a `button`, a way out of the
|
|
691
|
+
* thread the content as an `a`. The pointer's answer is the ink laid over
|
|
692
|
+
* the ground — six and twelve per cent — which answers every painting and
|
|
693
|
+
* every tone with one rule: 0.03 OKLab lightness over the accent fill in
|
|
694
|
+
* `light`, 0.044 under the secondary ground, over the 0.02 step
|
|
695
|
+
* `theme-conformance` asks of a hover, and the press twice that.
|
|
696
|
+
*/
|
|
697
|
+
[data-ui='message'][data-part='bubble'] > [data-part='content']:is(button, a) {
|
|
698
|
+
--_ground-hover: color-mix(
|
|
699
|
+
in oklab,
|
|
700
|
+
var(--_ink) var(--slotted-message-state-layer-hover, 6%),
|
|
701
|
+
var(--_ground)
|
|
702
|
+
);
|
|
703
|
+
--_ground-active: color-mix(
|
|
704
|
+
in oklab,
|
|
705
|
+
var(--_ink) var(--slotted-message-state-layer-active, 12%),
|
|
706
|
+
var(--_ground)
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
appearance: none;
|
|
710
|
+
cursor: pointer;
|
|
711
|
+
transition:
|
|
712
|
+
background-color var(--slotted-duration-short) var(--slotted-easing-standard, ease),
|
|
713
|
+
border-color var(--slotted-duration-short) var(--slotted-easing-standard, ease);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
[data-ui='message'][data-part='bubble'][data-variant='plain']
|
|
717
|
+
> [data-part='content']:is(button, a) {
|
|
718
|
+
padding-block: var(--slotted-space-1, 0.125rem);
|
|
719
|
+
padding-inline: var(--slotted-space-2, 0.25rem);
|
|
720
|
+
margin-inline: calc(var(--slotted-space-2, 0.25rem) * -1);
|
|
721
|
+
border-radius: var(--slotted-radius-sm, 4px);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
[data-ui='message'][data-part='bubble']
|
|
725
|
+
> [data-part='content']:is(button, a):hover:not(:disabled):not([data-disabled]) {
|
|
726
|
+
background-color: var(--_ground-hover);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
[data-ui='message'][data-part='bubble']
|
|
730
|
+
> [data-part='content']:is(button, a):active:not(:disabled):not([data-disabled]) {
|
|
731
|
+
background-color: var(--_ground-active);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
[data-ui='message'][data-part='bubble'] > [data-part='content']:is(button, a):focus-visible {
|
|
735
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color);
|
|
736
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* ── Meta: the hour and the ticks inside the bubble ────────────────────
|
|
741
|
+
*
|
|
742
|
+
* The hour and the delivery mark ride at the end of the last line, inside
|
|
743
|
+
* the surface, in the bubble's own ink a step back: a float at the end of
|
|
744
|
+
* the words, so it shares the last line when there is room and takes a line
|
|
745
|
+
* of its own, still at the end, when there is not. The bubble never grows a
|
|
746
|
+
* caption under itself for a fact this small.
|
|
747
|
+
*
|
|
748
|
+
* The box is the height of the words' line and its contents stand on that
|
|
749
|
+
* line's centre, a pixel down, where the twelve-pixel digits share the
|
|
750
|
+
* fourteen-pixel words' baseline to within a pixel and the mark sits on the
|
|
751
|
+
* same axis as the digits. WhatsApp hangs its meta below the baseline; set
|
|
752
|
+
* beside a line of text, the hanging meta read as misaligned. Read is the one state that changes the ink: full strength on a
|
|
753
|
+
* solid fill, the accent on a tinted one; the other marks differ by shape.
|
|
754
|
+
*/
|
|
755
|
+
[data-ui='message'][data-part='bubble'] [data-part='meta'] {
|
|
756
|
+
/*
|
|
757
|
+
* Ninety per cent of the bubble's ink: the lowest share at which the hour
|
|
758
|
+
* keeps 4.5:1 on every fill — 4.50 on the accent in `dark`, 5.74 in
|
|
759
|
+
* `light`, 8.39 in `high-contrast` against its 7:1 — measured in sRGB.
|
|
760
|
+
* Two thirds, the first choice, left it at 3.24 on the dark accent.
|
|
761
|
+
*/
|
|
762
|
+
--_meta-ink: color-mix(in srgb, currentColor 90%, transparent);
|
|
763
|
+
--_read-ink: var(--slotted-tone-accent-text);
|
|
764
|
+
|
|
765
|
+
display: inline-flex;
|
|
766
|
+
float: inline-end;
|
|
767
|
+
align-items: center;
|
|
768
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
769
|
+
/* The line rounded down to an even length, so a fourteen-pixel mark and a fourteen-pixel line of digits centre on whole pixels. */
|
|
770
|
+
block-size: round(down, var(--_leading), 2px);
|
|
771
|
+
margin-inline-start: var(--slotted-space-4, 0.5rem);
|
|
772
|
+
color: var(--_meta-ink);
|
|
773
|
+
font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
774
|
+
font-weight: var(--slotted-font-weight-regular, 400);
|
|
775
|
+
line-height: var(--slotted-control-icon-size-sm, 14px);
|
|
776
|
+
font-variant-numeric: tabular-nums;
|
|
777
|
+
white-space: nowrap;
|
|
778
|
+
translate: 0 1px;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
[data-ui='message'][data-part='bubble'][data-variant='solid'] [data-part='meta'] {
|
|
782
|
+
--_read-ink: var(--_on-solid);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
[data-ui='message'][data-part='bubble'] [data-part='meta'] [data-part='receipt'] {
|
|
786
|
+
display: inline-grid;
|
|
787
|
+
place-items: center;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* The mark is the small control glyph, the height of the digits' line, so
|
|
792
|
+
* the two share one box and one centre; two ticks at twelve pixels were two
|
|
793
|
+
* scratches.
|
|
794
|
+
*/
|
|
795
|
+
[data-ui='message'][data-part='bubble']
|
|
796
|
+
[data-part='meta']
|
|
797
|
+
[data-part='receipt']
|
|
798
|
+
> [data-ui='icon'] {
|
|
799
|
+
--slotted-icon-size: var(--slotted-control-icon-size-sm, 14px);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
[data-ui='message'][data-part='bubble']
|
|
803
|
+
[data-part='meta']
|
|
804
|
+
[data-part='receipt']
|
|
805
|
+
> span:not([data-ui]) {
|
|
806
|
+
position: absolute;
|
|
807
|
+
inline-size: 1px;
|
|
808
|
+
block-size: 1px;
|
|
809
|
+
overflow: hidden;
|
|
810
|
+
clip-path: inset(50%);
|
|
811
|
+
white-space: nowrap;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
[data-ui='message'][data-part='bubble']
|
|
815
|
+
[data-part='meta']
|
|
816
|
+
[data-part='receipt'][data-status='read'] {
|
|
817
|
+
color: var(--_read-ink);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/** A clock that breathes while the message is on its way. */
|
|
821
|
+
[data-ui='message'][data-part='bubble']
|
|
822
|
+
[data-part='meta']
|
|
823
|
+
[data-part='receipt'][data-status='sending']
|
|
824
|
+
> [data-ui='icon'] {
|
|
825
|
+
animation: slotted-message-pending var(--slotted-duration-loop-slow)
|
|
826
|
+
var(--slotted-easing-standard, ease) infinite alternate;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
@keyframes slotted-message-pending {
|
|
830
|
+
from {
|
|
831
|
+
opacity: 1;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
to {
|
|
835
|
+
opacity: 0.4;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/** The meta of an unframed reply has no surface to ride in; it is the footer's. */
|
|
840
|
+
[data-ui='message'][data-part='bubble'][data-variant='plain'] [data-part='meta'] {
|
|
841
|
+
display: none;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/* ── Clamp ──────────────────────────────────────────────────────────────
|
|
845
|
+
*
|
|
846
|
+
* A long bubble shows its first lines and the rest stays in the DOM — for
|
|
847
|
+
* copying, for finding in the page and for a screen reader — behind a line
|
|
848
|
+
* clamp. `more` shows it whole; the binding draws `more` only while the
|
|
849
|
+
* text overflows.
|
|
850
|
+
*
|
|
851
|
+
* The reveal snaps. A line clamp is not an interpolable length, and the
|
|
852
|
+
* alternative — a measured height transitioned through script — makes the
|
|
853
|
+
* text jump twice when a reply streaming above it reflows the column. The
|
|
854
|
+
* chevron turning is the motion that answers the press.
|
|
855
|
+
*/
|
|
856
|
+
[data-ui='message'][data-part='bubble'][data-clamped]:not([data-revealed])
|
|
857
|
+
> [data-part='content'] {
|
|
858
|
+
display: -webkit-box;
|
|
859
|
+
-webkit-box-orient: vertical;
|
|
860
|
+
-webkit-line-clamp: var(--slotted-message-max-lines, 6);
|
|
861
|
+
/**
|
|
862
|
+
* The clamp stops painting after the last line it keeps, but a box clips
|
|
863
|
+
* its overflow at the padding edge, and the inset under that line painted
|
|
864
|
+
* the top of the next one. Clipping at the content edge keeps the inset
|
|
865
|
+
* clean; the ellipsis is still the clamp's.
|
|
866
|
+
*/
|
|
867
|
+
overflow: clip;
|
|
868
|
+
overflow-clip-margin: content-box;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
[data-ui='message'][data-part='bubble'] > [data-part='more'] {
|
|
872
|
+
display: inline-flex;
|
|
873
|
+
align-items: center;
|
|
874
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
875
|
+
min-block-size: var(--slotted-target-min, 24px);
|
|
876
|
+
margin: 0;
|
|
877
|
+
padding-block: 0;
|
|
878
|
+
padding-inline: var(--_pad-inline, 12px);
|
|
879
|
+
border: 0;
|
|
880
|
+
border-radius: var(--slotted-control-radius, 4px);
|
|
881
|
+
background-color: transparent;
|
|
882
|
+
color: var(--slotted-text-muted);
|
|
883
|
+
font: inherit;
|
|
884
|
+
font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
885
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
886
|
+
transition: color var(--slotted-duration-short) var(--slotted-easing-standard, ease);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
[data-ui='message'][data-part='bubble'] > [data-part='more'] > [data-ui='icon'] {
|
|
890
|
+
--slotted-icon-size: var(--slotted-control-icon-size-sm, 14px);
|
|
891
|
+
|
|
892
|
+
transition: rotate var(--slotted-duration-medium) var(--slotted-easing-standard, ease);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
[data-ui='message'][data-part='bubble'][data-revealed] > [data-part='more'] > [data-ui='icon'] {
|
|
896
|
+
rotate: 180deg;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
[data-ui='message'][data-part='bubble'] > [data-part='more']:hover:not(:disabled) {
|
|
900
|
+
color: var(--slotted-text);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
[data-ui='message'][data-part='bubble'] > [data-part='more']:active:not(:disabled) {
|
|
904
|
+
color: var(--slotted-tone-accent-text);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
[data-ui='message'][data-part='bubble'] > [data-part='more']:focus-visible {
|
|
908
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color);
|
|
909
|
+
outline-offset: calc(var(--slotted-focus-ring-offset, 2px) * -1);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/* ── Quote ──────────────────────────────────────────────────────────────
|
|
913
|
+
*
|
|
914
|
+
* The message a reply answers, at the head of the surface: a block tinted
|
|
915
|
+
* with the surface's own ink, the author over two lines of the words. No
|
|
916
|
+
* rail on its start edge — a tinted block reads as quoted in every painting,
|
|
917
|
+
* the accent fill included, where a rail in the ink would be the only
|
|
918
|
+
* saturated stroke in the thread.
|
|
919
|
+
*/
|
|
920
|
+
[data-ui='message'][data-part='bubble'] [data-part='quote'] {
|
|
921
|
+
display: block;
|
|
922
|
+
box-sizing: border-box;
|
|
923
|
+
inline-size: 100%;
|
|
924
|
+
margin: 0;
|
|
925
|
+
margin-block-end: var(--slotted-space-3, 0.375rem);
|
|
926
|
+
padding-block: var(--slotted-space-2, 0.25rem);
|
|
927
|
+
padding-inline: var(--slotted-space-4, 0.5rem);
|
|
928
|
+
border: 0;
|
|
929
|
+
border-radius: calc(var(--_radius) - var(--_pad-block, 8px));
|
|
930
|
+
background-color: color-mix(in oklab, currentColor 8%, transparent);
|
|
931
|
+
color: inherit;
|
|
932
|
+
font: inherit;
|
|
933
|
+
font-size: var(--slotted-control-font-size-md, 13px);
|
|
934
|
+
line-height: round(up, calc(1em * var(--slotted-line-height-md, 1.5)), 1px);
|
|
935
|
+
text-align: start;
|
|
936
|
+
unicode-bidi: plaintext;
|
|
937
|
+
overflow: hidden;
|
|
938
|
+
display: -webkit-box;
|
|
939
|
+
-webkit-box-orient: vertical;
|
|
940
|
+
-webkit-line-clamp: 3;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
[data-ui='message'][data-part='bubble'] [data-part='quote-author'] {
|
|
944
|
+
display: block;
|
|
945
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
[data-ui='message'][data-part='bubble'] button[data-part='quote'] {
|
|
949
|
+
cursor: pointer;
|
|
950
|
+
transition: background-color var(--slotted-duration-short) var(--slotted-easing-standard, ease);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
[data-ui='message'][data-part='bubble'] button[data-part='quote']:hover:not(:disabled) {
|
|
954
|
+
background-color: color-mix(in oklab, currentColor 13%, transparent);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
[data-ui='message'][data-part='bubble'] button[data-part='quote']:active:not(:disabled) {
|
|
958
|
+
background-color: color-mix(in oklab, currentColor 18%, transparent);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
[data-ui='message'][data-part='bubble'] button[data-part='quote']:focus-visible {
|
|
962
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color);
|
|
963
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/* ── Streaming text, caret and typing ──────────────────────────────────
|
|
967
|
+
*
|
|
968
|
+
* A streamed reply enters word by word. `stream` holds the settled words as
|
|
969
|
+
* plain text and each word still entering as a `stream-chunk`, whose delay
|
|
970
|
+
* the binding writes from `@slotted/core/text-stream`: a burst of forty
|
|
971
|
+
* words enters as a wave of at most 320 ms instead of landing as a block.
|
|
972
|
+
* A word comes out of a blur of a sixth of its own size, at nothing, and
|
|
973
|
+
* the ink and the edge arrive together: a word that is opaque while still
|
|
974
|
+
* blurred reads as a smudge, and five of them in a row as a smeared line.
|
|
975
|
+
*
|
|
976
|
+
* The caret is a dot after the last word, centred on the middle of the
|
|
977
|
+
* x-height, breathing between full and 72 % while the reply is open. A bar
|
|
978
|
+
* blinking in two steps read as a text field waiting for input; the reply is
|
|
979
|
+
* not waiting for the reader, it is still being written. While words are
|
|
980
|
+
* entering, the wave is the leading edge and the dot steps aside: a word
|
|
981
|
+
* still invisible already holds its place in the line, and a dot after it
|
|
982
|
+
* would stand apart from the last word painted. It returns once the wave has
|
|
983
|
+
* landed and the transport has been quiet for a long transition.
|
|
984
|
+
*
|
|
985
|
+
* Under reduced motion the binding settles every word on arrival, the caret
|
|
986
|
+
* stands lit and the typing dots stand at three strengths, so the state is
|
|
987
|
+
* still drawn.
|
|
988
|
+
*/
|
|
989
|
+
[data-ui='message'][data-part='bubble'] [data-part='stream'] {
|
|
990
|
+
white-space: pre-wrap;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
[data-ui='message'][data-part='bubble'] [data-part='stream-chunk'] {
|
|
994
|
+
animation: slotted-message-word var(--slotted-duration-long) var(--slotted-easing-standard) both;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
@keyframes slotted-message-word {
|
|
998
|
+
from {
|
|
999
|
+
opacity: 0;
|
|
1000
|
+
filter: blur(0.16em);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
to {
|
|
1004
|
+
opacity: 1;
|
|
1005
|
+
filter: blur(0);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
[data-ui='message'][data-part='bubble'] [data-part='caret'] {
|
|
1010
|
+
--_dot: round(nearest, 0.6em, 2px);
|
|
1011
|
+
|
|
1012
|
+
display: inline-block;
|
|
1013
|
+
inline-size: var(--_dot);
|
|
1014
|
+
block-size: var(--_dot);
|
|
1015
|
+
/* A streamed word carries its own space; the margin only parts the dot from a word cut mid-way. */
|
|
1016
|
+
margin-inline-start: var(--slotted-space-1, 0.125rem);
|
|
1017
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
1018
|
+
background-color: currentColor;
|
|
1019
|
+
vertical-align: middle;
|
|
1020
|
+
transition: filter var(--slotted-duration-medium) var(--slotted-easing-enter)
|
|
1021
|
+
var(--slotted-duration-long);
|
|
1022
|
+
animation:
|
|
1023
|
+
slotted-message-dot-in var(--slotted-duration-medium) var(--slotted-easing-enter),
|
|
1024
|
+
slotted-message-breathe var(--slotted-duration-loop) var(--slotted-easing-standard)
|
|
1025
|
+
var(--slotted-duration-medium) infinite alternate;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/* `filter`, not `opacity`: the breath animates opacity, and an animation outranks the rule. */
|
|
1029
|
+
[data-ui='message'][data-part='bubble']
|
|
1030
|
+
[data-part='stream']:has(> [data-part='stream-chunk'])
|
|
1031
|
+
+ [data-part='caret'] {
|
|
1032
|
+
filter: opacity(0);
|
|
1033
|
+
transition: filter var(--slotted-duration-short) var(--slotted-easing-exit);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
@keyframes slotted-message-dot-in {
|
|
1037
|
+
from {
|
|
1038
|
+
opacity: 0;
|
|
1039
|
+
scale: 0;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/* 0.72 of 8 px is 5.76: the breath is seen, and the dot never shrinks to a full stop's 2 px. */
|
|
1044
|
+
@keyframes slotted-message-breathe {
|
|
1045
|
+
from {
|
|
1046
|
+
opacity: 1;
|
|
1047
|
+
scale: 1;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
to {
|
|
1051
|
+
opacity: 0.5;
|
|
1052
|
+
scale: 0.72;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Three dots a third of the line tall — seven pixels at comfortable — that
|
|
1058
|
+
* rise and brighten in a wave, each a sixth of the loop behind the one
|
|
1059
|
+
* before, in the bubble of the person writing. The wave travels half a dot:
|
|
1060
|
+
* enough to read as a voice, not so much that the bubble seems to bounce.
|
|
1061
|
+
*/
|
|
1062
|
+
[data-ui='message'][data-part='bubble'] [data-part='typing'] {
|
|
1063
|
+
--_dot: round(calc(var(--_leading) / 3), 1px);
|
|
1064
|
+
|
|
1065
|
+
display: inline-flex;
|
|
1066
|
+
align-items: center;
|
|
1067
|
+
gap: calc(var(--_dot) * 0.6);
|
|
1068
|
+
block-size: var(--_leading);
|
|
1069
|
+
padding-inline: var(--slotted-space-1, 0.125rem);
|
|
1070
|
+
vertical-align: top;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* The bubble of someone writing trails two small circles from its foot on
|
|
1075
|
+
* the sender's side, the way a thought is drawn, and arrives from them: it
|
|
1076
|
+
* rises and fades in on the enter curve, and its trail follows one step
|
|
1077
|
+
* behind. The circles are the surface's own fill and edge, so they answer
|
|
1078
|
+
* every painting and every scheme with the bubble.
|
|
1079
|
+
*/
|
|
1080
|
+
[data-ui='message'][data-part='bubble'] > [data-part='content']:has(> [data-part='typing']) {
|
|
1081
|
+
position: relative;
|
|
1082
|
+
padding-inline: calc(var(--_pad-inline) + var(--slotted-space-1, 0.125rem));
|
|
1083
|
+
transition:
|
|
1084
|
+
opacity var(--slotted-duration-medium) var(--slotted-easing-enter, ease-out),
|
|
1085
|
+
translate var(--slotted-duration-medium) var(--slotted-easing-enter, ease-out);
|
|
1086
|
+
|
|
1087
|
+
@starting-style {
|
|
1088
|
+
opacity: 0;
|
|
1089
|
+
translate: 0 var(--slotted-space-3, 0.375rem);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
[data-ui='message'][data-part='bubble']
|
|
1094
|
+
> [data-part='content']:has(> [data-part='typing'])::before,
|
|
1095
|
+
[data-ui='message'][data-part='bubble']
|
|
1096
|
+
> [data-part='content']:has(> [data-part='typing'])::after {
|
|
1097
|
+
content: '';
|
|
1098
|
+
position: absolute;
|
|
1099
|
+
box-sizing: border-box;
|
|
1100
|
+
border: inherit;
|
|
1101
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
1102
|
+
background-color: inherit;
|
|
1103
|
+
animation: slotted-message-trail var(--slotted-duration-loop-slow)
|
|
1104
|
+
var(--slotted-easing-standard, ease) infinite alternate;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
[data-ui='message'][data-part='bubble']
|
|
1108
|
+
> [data-part='content']:has(> [data-part='typing'])::before {
|
|
1109
|
+
inset-block-end: calc(var(--slotted-space-1, 0.125rem) * -1);
|
|
1110
|
+
inset-inline-start: calc(var(--slotted-space-1, 0.125rem) * -1);
|
|
1111
|
+
inline-size: var(--slotted-space-4, 0.5rem);
|
|
1112
|
+
block-size: var(--slotted-space-4, 0.5rem);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
[data-ui='message'][data-part='bubble']
|
|
1116
|
+
> [data-part='content']:has(> [data-part='typing'])::after {
|
|
1117
|
+
inset-block-end: calc(var(--slotted-space-3, 0.375rem) * -1);
|
|
1118
|
+
inset-inline-start: calc(var(--slotted-space-3, 0.375rem) * -1);
|
|
1119
|
+
inline-size: var(--slotted-space-2, 0.25rem);
|
|
1120
|
+
block-size: var(--slotted-space-2, 0.25rem);
|
|
1121
|
+
animation-delay: calc(var(--slotted-duration-loop-slow) / -2);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/** The line under a writing bubble clears the trail. */
|
|
1125
|
+
[data-ui='message'][data-part='root']
|
|
1126
|
+
> [data-part='body']:has(> [data-part='bubble'] [data-part='typing'])
|
|
1127
|
+
> [data-part='footer'] {
|
|
1128
|
+
margin-block-start: var(--slotted-space-2, 0.25rem);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
@keyframes slotted-message-trail {
|
|
1132
|
+
from {
|
|
1133
|
+
opacity: 1;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
to {
|
|
1137
|
+
opacity: 0.55;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
[data-ui='message'][data-part='bubble'] [data-part='typing'] > span {
|
|
1142
|
+
inline-size: var(--_dot);
|
|
1143
|
+
block-size: var(--_dot);
|
|
1144
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
1145
|
+
background-color: currentColor;
|
|
1146
|
+
opacity: 0.3;
|
|
1147
|
+
animation: slotted-message-typing var(--slotted-duration-loop)
|
|
1148
|
+
var(--slotted-easing-standard, ease) infinite;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
[data-ui='message'][data-part='bubble'] [data-part='typing'] > span:nth-child(2) {
|
|
1152
|
+
animation-delay: calc(var(--slotted-duration-loop) / 6);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
[data-ui='message'][data-part='bubble'] [data-part='typing'] > span:nth-child(3) {
|
|
1156
|
+
animation-delay: calc(var(--slotted-duration-loop) / 3);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
@keyframes slotted-message-typing {
|
|
1160
|
+
0%,
|
|
1161
|
+
55%,
|
|
1162
|
+
100% {
|
|
1163
|
+
opacity: 0.3;
|
|
1164
|
+
translate: 0 0;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
25% {
|
|
1168
|
+
opacity: 0.85;
|
|
1169
|
+
translate: 0 calc(var(--_dot) * -0.5);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* ── Retry ──────────────────────────────────────────────────────────────
|
|
1175
|
+
*
|
|
1176
|
+
* A message that did not go carries a round control beside its surface, on
|
|
1177
|
+
* the side away from the sender, in the danger tone, centred on the last
|
|
1178
|
+
* line. It is the way to send it again, and its arrow turns a little under
|
|
1179
|
+
* the pointer toward the way it will go.
|
|
1180
|
+
*/
|
|
1181
|
+
[data-ui='message'][data-part='bubble'] > [data-part='retry'] {
|
|
1182
|
+
--_size: max(var(--slotted-target-min, 24px), var(--slotted-control-inner-size-md, 24px));
|
|
1183
|
+
--_ground: var(--slotted-tone-danger-solid);
|
|
1184
|
+
|
|
1185
|
+
display: inline-grid;
|
|
1186
|
+
place-items: center;
|
|
1187
|
+
position: absolute;
|
|
1188
|
+
inset-block-end: calc(
|
|
1189
|
+
(
|
|
1190
|
+
var(--_leading) + 2 * var(--_pad-block) + 2 * var(--slotted-border-width-thin, 1px) -
|
|
1191
|
+
var(--_size)
|
|
1192
|
+
) /
|
|
1193
|
+
2
|
|
1194
|
+
);
|
|
1195
|
+
inset-inline-start: calc(100% + var(--slotted-space-4, 0.5rem));
|
|
1196
|
+
box-sizing: border-box;
|
|
1197
|
+
inline-size: var(--_size);
|
|
1198
|
+
block-size: var(--_size);
|
|
1199
|
+
margin: 0;
|
|
1200
|
+
padding: 0;
|
|
1201
|
+
border: 0;
|
|
1202
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
1203
|
+
background-color: var(--_ground);
|
|
1204
|
+
color: var(--slotted-tone-danger-on-solid);
|
|
1205
|
+
cursor: pointer;
|
|
1206
|
+
transition: background-color var(--slotted-duration-short) var(--slotted-easing-standard, ease);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
[data-ui='message'][data-part='root'][data-align='end']
|
|
1210
|
+
[data-part='bubble']
|
|
1211
|
+
> [data-part='retry'] {
|
|
1212
|
+
inset-inline: auto calc(100% + var(--slotted-space-4, 0.5rem));
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
[data-ui='message'][data-part='bubble'] > [data-part='retry'] > [data-ui='icon'] {
|
|
1216
|
+
--slotted-icon-size: var(--slotted-control-icon-size-sm, 14px);
|
|
1217
|
+
|
|
1218
|
+
transition: rotate var(--slotted-duration-medium) var(--slotted-easing-emphasized, ease);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* The tone's own hover step sits 0.01 OKLab lightness from its fill, under
|
|
1223
|
+
* the 0.02 `theme-conformance` asks; the control's ink laid over the fill at
|
|
1224
|
+
* fourteen per cent answers visibly in every scheme.
|
|
1225
|
+
*/
|
|
1226
|
+
[data-ui='message'][data-part='bubble']
|
|
1227
|
+
> [data-part='retry']:hover:not(:disabled):not([data-disabled]) {
|
|
1228
|
+
background-color: color-mix(
|
|
1229
|
+
in oklab,
|
|
1230
|
+
var(--slotted-tone-danger-on-solid) var(--slotted-message-retry-state-layer, 14%),
|
|
1231
|
+
var(--_ground)
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
[data-ui='message'][data-part='bubble']
|
|
1236
|
+
> [data-part='retry']:hover:not(:disabled):not([data-disabled])
|
|
1237
|
+
> [data-ui='icon'] {
|
|
1238
|
+
rotate: -45deg;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
[data-ui='message'][data-part='bubble']
|
|
1242
|
+
> [data-part='retry']:active:not(:disabled):not([data-disabled]) {
|
|
1243
|
+
background-color: var(--slotted-tone-danger-solid-active);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
[data-ui='message'][data-part='bubble'] > [data-part='retry']:focus-visible {
|
|
1247
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color);
|
|
1248
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1252
|
+
[data-ui='message'][data-part='bubble'] [data-part='caret'],
|
|
1253
|
+
[data-ui='message'][data-part='bubble'] [data-part='stream-chunk'],
|
|
1254
|
+
[data-ui='message'][data-part='bubble'] [data-part='typing'] > span,
|
|
1255
|
+
[data-ui='message'][data-part='bubble']
|
|
1256
|
+
> [data-part='content']:has(> [data-part='typing'])::before,
|
|
1257
|
+
[data-ui='message'][data-part='bubble']
|
|
1258
|
+
> [data-part='content']:has(> [data-part='typing'])::after,
|
|
1259
|
+
[data-ui='message'][data-part='bubble']
|
|
1260
|
+
[data-part='meta']
|
|
1261
|
+
[data-part='receipt'][data-status='sending']
|
|
1262
|
+
> [data-ui='icon'] {
|
|
1263
|
+
animation: none;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
[data-ui='message'][data-part='root'] [data-part='activity-step'][data-current] > :last-child {
|
|
1267
|
+
animation: none;
|
|
1268
|
+
background-image: none;
|
|
1269
|
+
color: inherit;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
[data-ui='message'][data-part='bubble'] [data-part='typing'] > span:nth-child(2) {
|
|
1273
|
+
opacity: 0.55;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
[data-ui='message'][data-part='bubble'] [data-part='typing'] > span:nth-child(3) {
|
|
1277
|
+
opacity: 0.8;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/* ── Reactions ──────────────────────────────────────────────────────────
|
|
1282
|
+
*
|
|
1283
|
+
* A raised pill straddling the lower edge of the surface on the side away
|
|
1284
|
+
* from the sender. It rises into the surface by half the surface's own
|
|
1285
|
+
* inset, clear of the descenders of the last line; the bubble keeps the rest as room
|
|
1286
|
+
* below itself, so the pill never lands on the next message. ReUI hangs
|
|
1287
|
+
* the row three quarters out and its example needs a 32-pixel gap to clear
|
|
1288
|
+
* it (`c-bubble-4`). The pill draws its own edge and lift rather than a
|
|
1289
|
+
* ring in the colour of a card it assumes it is on.
|
|
1290
|
+
*/
|
|
1291
|
+
[data-ui='message'][data-part='bubble']:has(> [data-part='reactions']) {
|
|
1292
|
+
padding-block-end: calc(var(--_reactions-size) - var(--_pad-block, 8px) / 2);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
[data-ui='message'][data-part='bubble'] > [data-part='reactions'] {
|
|
1296
|
+
display: inline-flex;
|
|
1297
|
+
align-items: center;
|
|
1298
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
1299
|
+
position: absolute;
|
|
1300
|
+
inset-block-end: 0;
|
|
1301
|
+
inset-inline-end: var(--_pad-inline, 12px);
|
|
1302
|
+
padding: var(--slotted-space-1, 0.125rem);
|
|
1303
|
+
border: var(--slotted-border-width-thin, 1px) solid var(--slotted-border-subtle);
|
|
1304
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
1305
|
+
background-color: var(--slotted-surface-raised);
|
|
1306
|
+
box-shadow: var(--slotted-shadow-xs);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
[data-ui='message'][data-part='root'][data-align='end']
|
|
1310
|
+
[data-part='bubble']
|
|
1311
|
+
> [data-part='reactions'] {
|
|
1312
|
+
inset-inline: var(--_pad-inline, 12px) auto;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
/**
|
|
1316
|
+
* In a thread there is no surface to straddle: the reactions are a row in
|
|
1317
|
+
* the flow under the words, the way Slack and GitHub set them.
|
|
1318
|
+
*/
|
|
1319
|
+
[data-ui='message'][data-part='bubble'][data-variant='plain']:has(> [data-part='reactions']) {
|
|
1320
|
+
padding-block-end: 0;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
[data-ui='message'][data-part='bubble'][data-variant='plain'] > [data-part='reactions'] {
|
|
1324
|
+
position: static;
|
|
1325
|
+
padding: 0;
|
|
1326
|
+
border: 0;
|
|
1327
|
+
background-color: transparent;
|
|
1328
|
+
box-shadow: none;
|
|
1329
|
+
translate: none;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
[data-ui='message'][data-part='bubble'] [data-part='reaction'] {
|
|
1333
|
+
display: inline-flex;
|
|
1334
|
+
align-items: center;
|
|
1335
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
1336
|
+
box-sizing: border-box;
|
|
1337
|
+
min-inline-size: var(--_reactions-size);
|
|
1338
|
+
block-size: var(--_reactions-size);
|
|
1339
|
+
margin: 0;
|
|
1340
|
+
padding-block: 0;
|
|
1341
|
+
padding-inline: var(--slotted-space-3, 0.375rem);
|
|
1342
|
+
--_reaction-ground: transparent;
|
|
1343
|
+
|
|
1344
|
+
border: var(--slotted-border-width-thin, 1px) solid transparent;
|
|
1345
|
+
border-radius: var(--slotted-radius-full, 9999px);
|
|
1346
|
+
background-color: var(--_reaction-ground);
|
|
1347
|
+
color: var(--slotted-text);
|
|
1348
|
+
font: inherit;
|
|
1349
|
+
font-size: var(--slotted-font-size-xs, 0.75rem);
|
|
1350
|
+
line-height: 1;
|
|
1351
|
+
transition:
|
|
1352
|
+
background-color var(--slotted-duration-short) var(--slotted-easing-standard, ease),
|
|
1353
|
+
border-color var(--slotted-duration-short) var(--slotted-easing-standard, ease),
|
|
1354
|
+
color var(--slotted-duration-short) var(--slotted-easing-standard, ease);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
[data-ui='message'][data-part='bubble'][data-variant='plain'] [data-part='reaction'] {
|
|
1358
|
+
--_reaction-ground: var(--slotted-surface);
|
|
1359
|
+
|
|
1360
|
+
border-color: var(--slotted-border-subtle);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
[data-ui='message'][data-part='bubble'] [data-part='reaction-count'] {
|
|
1364
|
+
color: var(--slotted-text-muted);
|
|
1365
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
1366
|
+
font-variant-numeric: tabular-nums;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
/**
|
|
1370
|
+
* The pointer answers on whatever ground the reaction stands on — none, the
|
|
1371
|
+
* page, or the accent's tint when it is the reader's own — with the text's
|
|
1372
|
+
* ink laid over it at six and twelve per cent, so a pressed reaction answers
|
|
1373
|
+
* a hover and a press as clearly as a bare one and the two never match.
|
|
1374
|
+
*/
|
|
1375
|
+
[data-ui='message'][data-part='bubble']
|
|
1376
|
+
[data-part='reaction']:hover:not(:disabled):not([data-disabled]) {
|
|
1377
|
+
background-color: color-mix(
|
|
1378
|
+
in oklab,
|
|
1379
|
+
var(--slotted-text) var(--slotted-message-state-layer-hover, 6%),
|
|
1380
|
+
var(--_reaction-ground)
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
[data-ui='message'][data-part='bubble']
|
|
1385
|
+
[data-part='reaction']:active:not(:disabled):not([data-disabled]) {
|
|
1386
|
+
background-color: color-mix(
|
|
1387
|
+
in oklab,
|
|
1388
|
+
var(--slotted-text) var(--slotted-message-state-layer-active, 12%),
|
|
1389
|
+
var(--_reaction-ground)
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/**
|
|
1394
|
+
* The reader's own reaction: the accent's tinted ground inside the accent's
|
|
1395
|
+
* edge, and the count in the accent's ink. The pair carries the ratio the
|
|
1396
|
+
* ground alone cannot (`docs/token-foundation.md` › Semantic colour).
|
|
1397
|
+
*/
|
|
1398
|
+
[data-ui='message'][data-part='bubble'] [data-part='reaction'][data-pressed] {
|
|
1399
|
+
--_reaction-ground: var(--slotted-tone-accent-surface);
|
|
1400
|
+
|
|
1401
|
+
border-color: var(--slotted-tone-accent-border);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
[data-ui='message'][data-part='bubble']
|
|
1405
|
+
[data-part='reaction'][data-pressed]
|
|
1406
|
+
> [data-part='reaction-count'] {
|
|
1407
|
+
color: var(--slotted-tone-accent-text);
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
[data-ui='message'][data-part='bubble'] [data-part='reaction']:is(:disabled, [data-disabled]) {
|
|
1411
|
+
color: var(--slotted-disabled-foreground);
|
|
1412
|
+
cursor: default;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
[data-ui='message'][data-part='bubble'] [data-part='reaction']:focus-visible {
|
|
1416
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color);
|
|
1417
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/* ── Forced colours ─────────────────────────────────────────────────────
|
|
1421
|
+
*
|
|
1422
|
+
* The system owns the palette: every bubble is a framed box of text, the
|
|
1423
|
+
* reader's own reaction is `Highlight`, and the tints of the quote and the
|
|
1424
|
+
* pointer go, because `color-mix` over system colours is not a colour the
|
|
1425
|
+
* reader chose.
|
|
1426
|
+
*/
|
|
1427
|
+
@media (forced-colors: active) {
|
|
1428
|
+
[data-ui='message'][data-part='bubble'] > [data-part='content'] {
|
|
1429
|
+
border-color: CanvasText;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
/* A background is the system's to repaint; the dot is ink, so it keeps the text's colour. */
|
|
1433
|
+
[data-ui='message'][data-part='bubble'] [data-part='caret'] {
|
|
1434
|
+
forced-color-adjust: none;
|
|
1435
|
+
background-color: CanvasText;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
[data-ui='message'][data-part='bubble'][data-variant='plain'] > [data-part='content'] {
|
|
1439
|
+
border-color: transparent;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
[data-ui='message'][data-part='bubble'] [data-part='reaction'][data-pressed] {
|
|
1443
|
+
border-color: Highlight;
|
|
1444
|
+
forced-color-adjust: none;
|
|
1445
|
+
background-color: Highlight;
|
|
1446
|
+
color: HighlightText;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
[data-ui='message'][data-part='bubble'] [data-part='quote'] {
|
|
1450
|
+
border: var(--slotted-border-width-thin, 1px) solid CanvasText;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
}
|