@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,906 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* ── A file that already belongs to something ──────────────────────────
|
|
4
|
+
*
|
|
5
|
+
* An attachment is a file shown inside a message, a record, a timeline event
|
|
6
|
+
* or a composer's tray. It says what the file is — the kind's tile, the
|
|
7
|
+
* name, a line of detail — gives the reader the file through its trigger, and
|
|
8
|
+
* carries the actions the application offers on it. `upload` acquires and
|
|
9
|
+
* transfers files; this family does neither. The two share the file
|
|
10
|
+
* vocabulary of `@slotted/core/upload` and the kind scale
|
|
11
|
+
* `--slotted-upload-kind-*`, and nothing else
|
|
12
|
+
* (`docs/history/designs/2026-09-30-attachment-design.md` §1).
|
|
13
|
+
*
|
|
14
|
+
* **The whole attachment is the target, and the actions stay beside it.**
|
|
15
|
+
* The trigger is a real `a` or `button` holding the file name, and a
|
|
16
|
+
* transparent `::after` on it covers the root. `actions` is a sibling of the
|
|
17
|
+
* body and stands above that layer, because interactive content may not nest
|
|
18
|
+
* inside `a` or `button` — the arrangement `list-item` states for `content`
|
|
19
|
+
* and `actions`.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The private properties, on both boxes that stand on their own. The set
|
|
23
|
+
* resolves the same size step its attachments do, because a grid of cards
|
|
24
|
+
* sizes its tracks from the card.
|
|
25
|
+
*/
|
|
26
|
+
[data-ui='attachment'][data-part='root'],
|
|
27
|
+
[data-ui='attachment'][data-part='group'] {
|
|
28
|
+
/**
|
|
29
|
+
* Type and glyph come from the control scales, the ones the theme restates
|
|
30
|
+
* per density, so an attachment inside a dense form shrinks with the
|
|
31
|
+
* fields around it. The insets come from the space scale, which does not
|
|
32
|
+
* vary by density — the cost `upload` and `card` both accepted.
|
|
33
|
+
*/
|
|
34
|
+
--_font-size: var(--slotted-control-font-size-md, 13px);
|
|
35
|
+
--_glyph-size: var(--slotted-control-icon-size-md, 16px);
|
|
36
|
+
--_gap: var(--slotted-control-gap-md, 8px);
|
|
37
|
+
--_inset: var(--slotted-attachment-padding-md, var(--slotted-space-4, 0.5rem));
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The two lines, each rounded up to a whole pixel. Thirteen at 1.5 is 19.5,
|
|
41
|
+
* and an attachment built from it stood at 59.5 with every edge below the
|
|
42
|
+
* first line between two device pixels. The rule is `upload`'s and
|
|
43
|
+
* `timeline`'s `--_leading`, stated here from a length rather than from
|
|
44
|
+
* `1em` so the tile below can be sized from it outside the text.
|
|
45
|
+
*/
|
|
46
|
+
--_line-height: var(--slotted-line-height-md, 1.5);
|
|
47
|
+
--_title-leading: round(up, calc(var(--_font-size) * var(--_line-height)), 1px);
|
|
48
|
+
--_detail-size: var(--slotted-font-size-xs, 0.75rem);
|
|
49
|
+
--_detail-leading: round(up, calc(var(--_detail-size) * var(--_line-height)), 1px);
|
|
50
|
+
--_line-gap: var(--slotted-attachment-line-gap, var(--slotted-space-1, 0.125rem));
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The tile is the height of the two lines beside it — 20 + 2 + 18 = 40 at
|
|
54
|
+
* `md`, the 40 ReUI measures (`c-attachment-5`) — so its edges line up with
|
|
55
|
+
* the cap of the name and the foot of the detail instead of floating
|
|
56
|
+
* between them. It steps with the type, which steps with the density.
|
|
57
|
+
*/
|
|
58
|
+
--_preview-size: var(
|
|
59
|
+
--slotted-attachment-preview-size,
|
|
60
|
+
calc(var(--_title-leading) + var(--_line-gap) + var(--_detail-leading))
|
|
61
|
+
);
|
|
62
|
+
--_preview-radius: var(--slotted-attachment-preview-radius, var(--slotted-radius-sm, 4px));
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A card is three tiles wide: 120 at `md`, ReUI's vertical card, with the
|
|
66
|
+
* picture 102 square inside it. Derived rather than stated, so a card at
|
|
67
|
+
* `sm` or at `dense` is still three of its own tiles and still a whole
|
|
68
|
+
* pixel.
|
|
69
|
+
*/
|
|
70
|
+
--_card-size: var(--slotted-attachment-card-size, calc(3 * var(--_preview-size)));
|
|
71
|
+
--_border-width: var(--slotted-attachment-border-width, var(--slotted-border-width-thin, 1px));
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The actions are inner controls (R2): the glyph plus a step of air on each
|
|
75
|
+
* side, floored at WCAG 2.2 SC 2.5.8's twenty-four pixels so the target
|
|
76
|
+
* holds at every density, and the glyph floored at fourteen for the reason
|
|
77
|
+
* `upload` floors its own — the control glyph scale falls to twelve at
|
|
78
|
+
* `dense`, and a cross that small beside a name is a speck.
|
|
79
|
+
*/
|
|
80
|
+
--_action-glyph-size: max(var(--slotted-attachment-icon-size-min, 14px), var(--_glyph-size));
|
|
81
|
+
--_action-size: var(
|
|
82
|
+
--slotted-attachment-action-size,
|
|
83
|
+
max(
|
|
84
|
+
var(--slotted-target-min, 24px),
|
|
85
|
+
calc(var(--_action-glyph-size) + 2 * var(--slotted-space-2, 0.25rem))
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[data-ui='attachment'][data-size='sm']:is([data-part='root'], [data-part='group']) {
|
|
91
|
+
--_font-size: var(--slotted-control-font-size-sm, 12px);
|
|
92
|
+
--_glyph-size: var(--slotted-control-icon-size-sm, 14px);
|
|
93
|
+
--_gap: var(--slotted-control-gap-sm, 6px);
|
|
94
|
+
--_inset: var(--slotted-attachment-padding-sm, var(--slotted-space-3, 0.375rem));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
[data-ui='attachment'][data-size='lg']:is([data-part='root'], [data-part='group']) {
|
|
98
|
+
--_font-size: var(--slotted-control-font-size-lg, 14px);
|
|
99
|
+
--_glyph-size: var(--slotted-control-icon-size-lg, 18px);
|
|
100
|
+
--_gap: var(--slotted-control-gap-lg, 8px);
|
|
101
|
+
--_inset: var(--slotted-attachment-padding-lg, var(--slotted-space-5, 0.75rem));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* ── One attachment ────────────────────────────────────────────────────
|
|
105
|
+
*
|
|
106
|
+
* Laid out as a grid rather than a flex line, for the reason `upload` gives
|
|
107
|
+
* its row: the tile and the actions are centred on the name and its detail,
|
|
108
|
+
* and a sentence under them — the message — grows the attachment downwards
|
|
109
|
+
* without moving either. The message starts under the name, not under the
|
|
110
|
+
* tile, so its first letter lines up with the name's.
|
|
111
|
+
*/
|
|
112
|
+
[data-ui='attachment'][data-part='root'] {
|
|
113
|
+
/**
|
|
114
|
+
* What the attachment is painted with at rest. A state of the file —
|
|
115
|
+
* invalid — moves these two; a pointer on the trigger moves `--_fill`,
|
|
116
|
+
* which mixes the ink into whichever ground the state chose.
|
|
117
|
+
*/
|
|
118
|
+
--_surface: var(--slotted-attachment-background, var(--slotted-surface));
|
|
119
|
+
--_edge: var(--slotted-attachment-border-color, var(--slotted-border-subtle));
|
|
120
|
+
--_ink: var(--slotted-text);
|
|
121
|
+
--_message-color: var(--slotted-text-muted);
|
|
122
|
+
/**
|
|
123
|
+
* The pointer's answer is a state layer of the ink over the ground rather
|
|
124
|
+
* than a step on the tone ramp: `--slotted-tone-danger-subtle-hover` is
|
|
125
|
+
* lighter than `--slotted-tone-danger-surface`, so a failed attachment
|
|
126
|
+
* would have brightened under the pointer. Four per cent of the ink is
|
|
127
|
+
* 0.034 of OKLab lightness on `light`'s white and 0.031 on `dark`'s
|
|
128
|
+
* surface, over the 0.02 step `theme-conformance` asks of a hover; eight
|
|
129
|
+
* is twice that for the press, so the two never read as one.
|
|
130
|
+
*/
|
|
131
|
+
--_fill-hover: color-mix(
|
|
132
|
+
in oklab,
|
|
133
|
+
var(--_ink) var(--slotted-attachment-state-layer-hover, 4%),
|
|
134
|
+
var(--_surface)
|
|
135
|
+
);
|
|
136
|
+
--_fill-active: color-mix(
|
|
137
|
+
in oklab,
|
|
138
|
+
var(--_ink) var(--slotted-attachment-state-layer-active, 8%),
|
|
139
|
+
var(--_surface)
|
|
140
|
+
);
|
|
141
|
+
--_fill: var(--_surface);
|
|
142
|
+
/*
|
|
143
|
+
* One step larger than the tile's four, so the two corners read as a
|
|
144
|
+
* surface holding a tile rather than as one curve cut twice. ReUI's 14
|
|
145
|
+
* has no step on the radius scale.
|
|
146
|
+
*/
|
|
147
|
+
--_radius: var(--slotted-attachment-radius, var(--slotted-radius-lg, 8px));
|
|
148
|
+
|
|
149
|
+
align-items: center;
|
|
150
|
+
background-color: var(--_fill);
|
|
151
|
+
border: var(--_border-width) solid var(--_edge);
|
|
152
|
+
border-radius: var(--_radius);
|
|
153
|
+
box-sizing: border-box;
|
|
154
|
+
color: var(--_ink);
|
|
155
|
+
column-gap: var(--_gap);
|
|
156
|
+
display: grid;
|
|
157
|
+
font-family: var(--slotted-attachment-font-family, var(--slotted-control-font-family));
|
|
158
|
+
font-size: var(--_font-size);
|
|
159
|
+
grid-template-areas:
|
|
160
|
+
'preview body actions'
|
|
161
|
+
'. message message';
|
|
162
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
163
|
+
line-height: var(--_title-leading);
|
|
164
|
+
list-style: none;
|
|
165
|
+
margin: 0;
|
|
166
|
+
min-inline-size: 0;
|
|
167
|
+
/*
|
|
168
|
+
* The bar is a child placed against this box, and the clip is what bends
|
|
169
|
+
* it to the corner. `clip` rather than `hidden`, because this box never
|
|
170
|
+
* scrolls and `hidden` would make it a scroll container.
|
|
171
|
+
*/
|
|
172
|
+
overflow: clip;
|
|
173
|
+
padding: var(--_inset);
|
|
174
|
+
position: relative;
|
|
175
|
+
text-align: start;
|
|
176
|
+
transition:
|
|
177
|
+
background-color var(--slotted-control-transition-duration)
|
|
178
|
+
var(--slotted-control-transition-easing),
|
|
179
|
+
border-color var(--slotted-control-transition-duration)
|
|
180
|
+
var(--slotted-control-transition-easing),
|
|
181
|
+
opacity var(--slotted-duration-medium) var(--slotted-easing-enter),
|
|
182
|
+
translate var(--slotted-duration-medium) var(--slotted-easing-enter);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/*
|
|
186
|
+
* An absent part leaves no column behind. A column of nought still carries
|
|
187
|
+
* the gap beside it, and an attachment without actions ended its name eight
|
|
188
|
+
* pixels short of the edge it could have used.
|
|
189
|
+
*/
|
|
190
|
+
[data-ui='attachment'][data-part='root']:not(:has(> [data-part='actions'])) {
|
|
191
|
+
grid-template-areas:
|
|
192
|
+
'preview body'
|
|
193
|
+
'. message';
|
|
194
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
[data-ui='attachment'][data-part='root']:not(:has(> [data-part='preview'])) {
|
|
198
|
+
grid-template-areas:
|
|
199
|
+
'body actions'
|
|
200
|
+
'message message';
|
|
201
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
[data-ui='attachment'][data-part='root']:not(:has(> [data-part='preview'])):not(
|
|
205
|
+
:has(> [data-part='actions'])
|
|
206
|
+
) {
|
|
207
|
+
grid-template-areas:
|
|
208
|
+
'body'
|
|
209
|
+
'message';
|
|
210
|
+
grid-template-columns: minmax(0, 1fr);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* A file arrives: it fades in from a step above its place, so an attachment
|
|
215
|
+
* that lands in a tray is seen landing rather than found there. The start
|
|
216
|
+
* style applies only on the frame the attachment is first rendered.
|
|
217
|
+
*
|
|
218
|
+
* An attachment that leaves does not animate: the binding removes it when
|
|
219
|
+
* the consumer's list changes, and holding it for an exit would need a second
|
|
220
|
+
* list beside the consumer's — the debt `upload` records for its rows.
|
|
221
|
+
*/
|
|
222
|
+
@starting-style {
|
|
223
|
+
[data-ui='attachment'][data-part='root'] {
|
|
224
|
+
opacity: 0;
|
|
225
|
+
translate: 0 calc(-1 * var(--slotted-space-2, 0.25rem));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* ── The tile ──────────────────────────────────────────────────────────
|
|
230
|
+
*
|
|
231
|
+
* A solid tile in the file's own colour with the mark knocked out of it, or
|
|
232
|
+
* the picture when the page has one. The colour argument and its
|
|
233
|
+
* measurements are under **File kind** in `docs/token-foundation.md`; the
|
|
234
|
+
* scale is `upload`'s and is read here under its own name.
|
|
235
|
+
*/
|
|
236
|
+
[data-ui='attachment'] [data-part='preview'] {
|
|
237
|
+
--_kind-tile: var(--slotted-upload-kind-other);
|
|
238
|
+
|
|
239
|
+
align-items: center;
|
|
240
|
+
background-color: var(--_kind-tile);
|
|
241
|
+
block-size: var(--_preview-size);
|
|
242
|
+
border-radius: var(--_preview-radius);
|
|
243
|
+
box-sizing: border-box;
|
|
244
|
+
color: var(--slotted-upload-kind-foreground);
|
|
245
|
+
display: inline-flex;
|
|
246
|
+
flex-direction: column;
|
|
247
|
+
grid-area: preview;
|
|
248
|
+
inline-size: var(--_preview-size);
|
|
249
|
+
justify-content: center;
|
|
250
|
+
overflow: hidden;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
[data-ui='attachment'] [data-part='preview'] > img {
|
|
254
|
+
block-size: 100%;
|
|
255
|
+
display: block;
|
|
256
|
+
inline-size: 100%;
|
|
257
|
+
object-fit: cover;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/*
|
|
261
|
+
* Half the tile, rounded to a whole pixel: 20 in a 40 tile. The row glyph
|
|
262
|
+
* (16) in a tile two lines tall read as a mark lost in its box.
|
|
263
|
+
*/
|
|
264
|
+
[data-ui='attachment'] [data-part='preview'] > [data-ui='icon'] {
|
|
265
|
+
--slotted-icon-size: round(calc(var(--_preview-size) / 2), 1px);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
[data-ui='attachment'] [data-part='preview'][data-kind='pdf'] {
|
|
269
|
+
--_kind-tile: var(--slotted-upload-kind-pdf);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
[data-ui='attachment'] [data-part='preview'][data-kind='document'] {
|
|
273
|
+
--_kind-tile: var(--slotted-upload-kind-document);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
[data-ui='attachment'] [data-part='preview'][data-kind='spreadsheet'] {
|
|
277
|
+
--_kind-tile: var(--slotted-upload-kind-spreadsheet);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
[data-ui='attachment'] [data-part='preview'][data-kind='presentation'] {
|
|
281
|
+
--_kind-tile: var(--slotted-upload-kind-presentation);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
[data-ui='attachment'] [data-part='preview'][data-kind='image'] {
|
|
285
|
+
--_kind-tile: var(--slotted-upload-kind-image);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
[data-ui='attachment'] [data-part='preview'][data-kind='video'] {
|
|
289
|
+
--_kind-tile: var(--slotted-upload-kind-video);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
[data-ui='attachment'] [data-part='preview'][data-kind='audio'] {
|
|
293
|
+
--_kind-tile: var(--slotted-upload-kind-audio);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
[data-ui='attachment'] [data-part='preview'][data-kind='archive'] {
|
|
297
|
+
--_kind-tile: var(--slotted-upload-kind-archive);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* ── What the attachment says ──────────────────────────────────────────
|
|
301
|
+
*
|
|
302
|
+
* The name, and one line of detail under it. The body is the one box with
|
|
303
|
+
* `min-inline-size: 0`, which is what makes a long name truncate instead of
|
|
304
|
+
* pushing the actions off the end.
|
|
305
|
+
*/
|
|
306
|
+
[data-ui='attachment'] [data-part='body'] {
|
|
307
|
+
display: flex;
|
|
308
|
+
flex-direction: column;
|
|
309
|
+
gap: var(--_line-gap);
|
|
310
|
+
grid-area: body;
|
|
311
|
+
min-inline-size: 0;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* The file's target. A real `a` when the file has an address — so `href`,
|
|
316
|
+
* `download` and the modified click a browser gives a link stay the
|
|
317
|
+
* platform's — and a real `button` when the page opens it itself. Nothing
|
|
318
|
+
* about it looks like a control at rest; the attachment is the control, and
|
|
319
|
+
* it answers as one.
|
|
320
|
+
*/
|
|
321
|
+
[data-ui='attachment'] [data-part='trigger'] {
|
|
322
|
+
appearance: none;
|
|
323
|
+
background: none;
|
|
324
|
+
border: 0;
|
|
325
|
+
color: inherit;
|
|
326
|
+
cursor: pointer;
|
|
327
|
+
display: flex;
|
|
328
|
+
font: inherit;
|
|
329
|
+
line-height: inherit;
|
|
330
|
+
margin: 0;
|
|
331
|
+
min-inline-size: 0;
|
|
332
|
+
padding: 0;
|
|
333
|
+
text-align: start;
|
|
334
|
+
text-decoration: none;
|
|
335
|
+
transition: translate var(--slotted-duration-instant) var(--slotted-control-transition-easing);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* The stretched target. Transparent, over the whole attachment, and below
|
|
340
|
+
* `actions`, which is positioned after it in the same stacking context.
|
|
341
|
+
*/
|
|
342
|
+
[data-ui='attachment'] [data-part='trigger']::after {
|
|
343
|
+
content: '';
|
|
344
|
+
inset: 0;
|
|
345
|
+
position: absolute;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/*
|
|
349
|
+
* The name answers as well as the ground, because the ground's step is
|
|
350
|
+
* 0.03 of lightness and a reader looking at the words should not have to
|
|
351
|
+
* find it at the edge. The underline says link; the press settles the name
|
|
352
|
+
* by a hairline — the answer `upload` gives its file name — so press and
|
|
353
|
+
* hover differ on the trigger itself and not only on the attachment.
|
|
354
|
+
*/
|
|
355
|
+
[data-ui='attachment'] [data-part='trigger']:not(:disabled):not([aria-disabled='true']):hover {
|
|
356
|
+
text-decoration-line: underline;
|
|
357
|
+
text-underline-offset: 0.2em;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
[data-ui='attachment'] [data-part='trigger']:not(:disabled):not([aria-disabled='true']):active {
|
|
361
|
+
text-decoration-line: underline;
|
|
362
|
+
text-underline-offset: 0.2em;
|
|
363
|
+
translate: 0 var(--slotted-border-width-thin, 1px);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
[data-ui='attachment'] [data-part='trigger']:is(:disabled, [aria-disabled='true']) {
|
|
367
|
+
cursor: default;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* The ring is drawn around the attachment, because the attachment is the
|
|
372
|
+
* target the pointer sees; a ring around the name alone would say that only
|
|
373
|
+
* the words can be pressed. The browser's own ring on the trigger is
|
|
374
|
+
* suppressed so the two are never drawn at once.
|
|
375
|
+
*/
|
|
376
|
+
[data-ui='attachment'] [data-part='trigger']:focus-visible {
|
|
377
|
+
outline: none;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
[data-ui='attachment'][data-part='root']:has(
|
|
381
|
+
> [data-part='body'] > [data-part='trigger']:focus-visible
|
|
382
|
+
) {
|
|
383
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
384
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/*
|
|
388
|
+
* The pointer's answer, on the whole attachment. Held back while the file
|
|
389
|
+
* cannot be opened: a disabled or loading attachment refuses its trigger,
|
|
390
|
+
* and a fill would promise the press it refuses.
|
|
391
|
+
*/
|
|
392
|
+
[data-ui='attachment'][data-part='root']:not([data-disabled]):not([data-loading]):has(
|
|
393
|
+
> [data-part='body'] > [data-part='trigger']:hover
|
|
394
|
+
) {
|
|
395
|
+
--_fill: var(--_fill-hover);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
[data-ui='attachment'][data-part='root']:not([data-disabled]):not([data-loading]):has(
|
|
399
|
+
> [data-part='body'] > [data-part='trigger']:active
|
|
400
|
+
) {
|
|
401
|
+
--_fill: var(--_fill-active);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* The name and its extension are one run, laid out in the name's own
|
|
406
|
+
* direction. The binding writes `dir="auto"`, so `claim-form.pdf` reads in
|
|
407
|
+
* that order inside a right-to-left page instead of `pdf.claim-form` — the
|
|
408
|
+
* defect `upload` found in its capture at `dir="rtl"`.
|
|
409
|
+
*/
|
|
410
|
+
[data-ui='attachment'] [data-part='filename'] {
|
|
411
|
+
display: flex;
|
|
412
|
+
font-weight: var(--slotted-attachment-name-font-weight, var(--slotted-font-weight-medium, 500));
|
|
413
|
+
line-height: var(--_title-leading);
|
|
414
|
+
min-inline-size: 0;
|
|
415
|
+
unicode-bidi: isolate;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/*
|
|
419
|
+
* A name that is not a trigger is its own item in the body's column, and
|
|
420
|
+
* stretched to the column it laid `policy.pdf` out from the left edge of a
|
|
421
|
+
* right-to-left page while the detail under it stood at the right: `dir="auto"`
|
|
422
|
+
* gives the run its own direction, so it must also be only as wide as the run.
|
|
423
|
+
* Found in the capture at `dir="rtl"`.
|
|
424
|
+
*/
|
|
425
|
+
[data-ui='attachment'] [data-part='body'] > [data-part='filename'],
|
|
426
|
+
[data-ui='attachment'] [data-part='body'] > [data-part='trigger'] {
|
|
427
|
+
align-self: start;
|
|
428
|
+
max-inline-size: 100%;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* The stem shrinks to an ellipsis and the extension does not, so a long
|
|
433
|
+
* name keeps the part that says what the file is. CSS cannot truncate in the
|
|
434
|
+
* middle; `splitName` makes the split in the binding, and the whole name
|
|
435
|
+
* stays in the document and in the trigger's `title`.
|
|
436
|
+
*/
|
|
437
|
+
[data-ui='attachment'] [data-part='name'] {
|
|
438
|
+
flex: 0 1 auto;
|
|
439
|
+
min-inline-size: 0;
|
|
440
|
+
overflow: hidden;
|
|
441
|
+
text-overflow: ellipsis;
|
|
442
|
+
white-space: nowrap;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
[data-ui='attachment'] [data-part='extension'] {
|
|
446
|
+
flex: none;
|
|
447
|
+
white-space: nowrap;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** The second line: a size, a date, a sender — whatever the page says. */
|
|
451
|
+
[data-ui='attachment'] [data-part='detail'] {
|
|
452
|
+
color: var(--slotted-text-muted);
|
|
453
|
+
font-size: var(--_detail-size);
|
|
454
|
+
font-variant-numeric: tabular-nums;
|
|
455
|
+
line-height: var(--_detail-leading);
|
|
456
|
+
margin: 0;
|
|
457
|
+
min-inline-size: 0;
|
|
458
|
+
overflow: hidden;
|
|
459
|
+
text-overflow: ellipsis;
|
|
460
|
+
white-space: nowrap;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Why the file failed, or anything else the page must say about it at more
|
|
465
|
+
* length than a line of detail. It wraps, because a reason cut off by an
|
|
466
|
+
* ellipsis is a reason nobody reads.
|
|
467
|
+
*/
|
|
468
|
+
[data-ui='attachment'] [data-part='message'] {
|
|
469
|
+
color: var(--_message-color);
|
|
470
|
+
font-size: var(--_detail-size);
|
|
471
|
+
grid-area: message;
|
|
472
|
+
line-height: var(--_detail-leading);
|
|
473
|
+
margin: 0;
|
|
474
|
+
margin-block-start: var(--_line-gap);
|
|
475
|
+
min-inline-size: 0;
|
|
476
|
+
overflow-wrap: anywhere;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* The transfer. It is `progress-indicator`'s bar, drawn where this family
|
|
481
|
+
* places it: a second bar would be a second answer to one question. Out of
|
|
482
|
+
* the flow, so the attachment is the same height whether it is sending or
|
|
483
|
+
* sent.
|
|
484
|
+
*
|
|
485
|
+
* It stands in the text's column and inside the bottom inset, not on the
|
|
486
|
+
* attachment's edge. Laid along the edge from corner to corner — `upload`'s
|
|
487
|
+
* placement — it ran under the tile, and on a failed attachment the danger
|
|
488
|
+
* bar sat against the danger edge and its inset second line: three red
|
|
489
|
+
* lines at the bottom, read as the edge doubled wherever the bar reached.
|
|
490
|
+
* Centred in the inset, the bar clears the edge by the same distance it
|
|
491
|
+
* clears the detail line above it.
|
|
492
|
+
*/
|
|
493
|
+
[data-ui='attachment'] [data-part='progress'] {
|
|
494
|
+
display: block;
|
|
495
|
+
inset-block-end: round(
|
|
496
|
+
down,
|
|
497
|
+
/* the `sm` bar is one step of the space scale thick (`progress-indicator`) */
|
|
498
|
+
calc((var(--_inset) - var(--slotted-space-1, 0.125rem)) / 2),
|
|
499
|
+
1px
|
|
500
|
+
);
|
|
501
|
+
inset-inline-end: var(--_inset);
|
|
502
|
+
inset-inline-start: calc(var(--_inset) + var(--_preview-size) + var(--_gap));
|
|
503
|
+
position: absolute;
|
|
504
|
+
transition: opacity var(--slotted-duration-medium) var(--slotted-easing-enter);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/* Without a tile, or on a card, the text's column starts at the inset. */
|
|
508
|
+
[data-ui='attachment'][data-part='root']:not(:has(> [data-part='preview']))
|
|
509
|
+
> [data-part='progress'],
|
|
510
|
+
[data-ui='attachment'][data-orientation='vertical'] > [data-part='progress'] {
|
|
511
|
+
inset-inline-start: var(--_inset);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
@starting-style {
|
|
515
|
+
[data-ui='attachment'] [data-part='progress'] {
|
|
516
|
+
opacity: 0;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/* ── The actions ───────────────────────────────────────────────────────
|
|
521
|
+
*
|
|
522
|
+
* Above the stretched target, so a press on a control is the control's.
|
|
523
|
+
*/
|
|
524
|
+
[data-ui='attachment'] [data-part='actions'] {
|
|
525
|
+
align-items: center;
|
|
526
|
+
display: inline-flex;
|
|
527
|
+
gap: var(--slotted-space-1, 0.125rem);
|
|
528
|
+
grid-area: actions;
|
|
529
|
+
justify-content: flex-end;
|
|
530
|
+
position: relative;
|
|
531
|
+
z-index: 1;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Inner controls (R2): no frame and no ground at rest, one ground step on
|
|
536
|
+
* hover and one more on press, the theme's ring on focus. The ground is a
|
|
537
|
+
* state layer of `currentcolor`, for the reason `upload` gives its row
|
|
538
|
+
* controls: it is legible on whatever ground the attachment has, including
|
|
539
|
+
* a failed one's tint.
|
|
540
|
+
*/
|
|
541
|
+
[data-ui='attachment'] [data-part='action'],
|
|
542
|
+
[data-ui='attachment'] [data-part='remove'] {
|
|
543
|
+
--_state-hover: color-mix(
|
|
544
|
+
in oklab,
|
|
545
|
+
currentcolor var(--slotted-attachment-action-state-layer-hover, 8%),
|
|
546
|
+
transparent
|
|
547
|
+
);
|
|
548
|
+
--_state-active: color-mix(
|
|
549
|
+
in oklab,
|
|
550
|
+
currentcolor var(--slotted-attachment-action-state-layer-active, 14%),
|
|
551
|
+
transparent
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
align-items: center;
|
|
555
|
+
appearance: none;
|
|
556
|
+
background-color: transparent;
|
|
557
|
+
block-size: var(--_action-size);
|
|
558
|
+
border: 0;
|
|
559
|
+
border-radius: var(--slotted-attachment-action-radius, var(--slotted-control-radius, 4px));
|
|
560
|
+
box-sizing: border-box;
|
|
561
|
+
color: inherit;
|
|
562
|
+
cursor: pointer;
|
|
563
|
+
display: inline-flex;
|
|
564
|
+
flex: none;
|
|
565
|
+
font: inherit;
|
|
566
|
+
inline-size: var(--_action-size);
|
|
567
|
+
justify-content: center;
|
|
568
|
+
margin: 0;
|
|
569
|
+
padding: 0;
|
|
570
|
+
text-decoration: none;
|
|
571
|
+
transition:
|
|
572
|
+
background-color var(--slotted-control-transition-duration)
|
|
573
|
+
var(--slotted-control-transition-easing),
|
|
574
|
+
color var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
[data-ui='attachment'] [data-part='action'] > [data-ui='icon'],
|
|
578
|
+
[data-ui='attachment'] [data-part='remove'] > [data-ui='icon'] {
|
|
579
|
+
--slotted-icon-size: var(--_action-glyph-size);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* `:not(:disabled)` guards a `button`; `aria-disabled` guards an `a`, which
|
|
584
|
+
* has no native disabled state. A control that cannot act does not answer a
|
|
585
|
+
* pointer.
|
|
586
|
+
*/
|
|
587
|
+
[data-ui='attachment'] [data-part='action']:not(:disabled):not([aria-disabled='true']):hover,
|
|
588
|
+
[data-ui='attachment'] [data-part='remove']:not(:disabled):not([aria-disabled='true']):hover {
|
|
589
|
+
background-color: var(--_state-hover);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
[data-ui='attachment'] [data-part='action']:not(:disabled):not([aria-disabled='true']):active,
|
|
593
|
+
[data-ui='attachment'] [data-part='remove']:not(:disabled):not([aria-disabled='true']):active {
|
|
594
|
+
background-color: var(--_state-active);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
[data-ui='attachment'] [data-part='action']:is(:disabled, [aria-disabled='true']),
|
|
598
|
+
[data-ui='attachment'] [data-part='remove']:is(:disabled, [aria-disabled='true']) {
|
|
599
|
+
color: var(--slotted-disabled-foreground);
|
|
600
|
+
cursor: default;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/*
|
|
604
|
+
* Inside the control, one ring width in: the controls of one attachment
|
|
605
|
+
* stand two pixels apart, and an outward ring would be drawn over the
|
|
606
|
+
* neighbour's box.
|
|
607
|
+
*/
|
|
608
|
+
[data-ui='attachment'] [data-part='action']:focus-visible,
|
|
609
|
+
[data-ui='attachment'] [data-part='remove']:focus-visible {
|
|
610
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
611
|
+
outline-offset: calc(-1 * var(--slotted-focus-ring-width, 2px));
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* On a finger the target is the comfortable size and the box is the one a
|
|
616
|
+
* mouse sees: a transparent `::after` centred on each control reaches 44
|
|
617
|
+
* pixels in both axes, and the ground and the ring stay square. The rule is
|
|
618
|
+
* `upload`'s.
|
|
619
|
+
*/
|
|
620
|
+
@media (pointer: coarse) {
|
|
621
|
+
[data-ui='attachment'] :is([data-part='action'], [data-part='remove']) {
|
|
622
|
+
position: relative;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
[data-ui='attachment'] :is([data-part='action'], [data-part='remove'])::after {
|
|
626
|
+
content: '';
|
|
627
|
+
inset: min(0px, calc((100% - var(--slotted-target-comfortable, 44px)) / 2));
|
|
628
|
+
position: absolute;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/* ── States of the file ────────────────────────────────────────────────
|
|
633
|
+
*
|
|
634
|
+
* A file that failed: its ground, its edge and its sentence turn, and the
|
|
635
|
+
* tile keeps the kind's colour. `upload` measured why — a refused archive
|
|
636
|
+
* that turned red stopped saying it was an archive, on the one row whose
|
|
637
|
+
* message was about archives — and every kind clears 4.03 : 1 against the
|
|
638
|
+
* failed ground in `light` (`default-theme.test.mjs`).
|
|
639
|
+
*/
|
|
640
|
+
[data-ui='attachment'][data-part='root'][data-invalid] {
|
|
641
|
+
--_surface: var(--slotted-tone-danger-surface);
|
|
642
|
+
--_edge: var(--slotted-tone-danger-border);
|
|
643
|
+
--_message-color: var(--slotted-tone-danger-text);
|
|
644
|
+
|
|
645
|
+
/*
|
|
646
|
+
* The edge doubles inward. Every tone surface is the page's own white in
|
|
647
|
+
* `high-contrast`, so the failed attachment's tint vanished there and the
|
|
648
|
+
* sentence's colour was the one signal left — colour alone, which WCAG 2.2
|
|
649
|
+
* SC 1.4.1 refuses. A second pixel of edge says it in line, in every
|
|
650
|
+
* scheme, and moves nothing: an inset shadow takes no room.
|
|
651
|
+
*/
|
|
652
|
+
--_invalid-edge-width: var(--slotted-control-invalid-edge-width, var(--_border-width));
|
|
653
|
+
|
|
654
|
+
box-shadow: inset 0 0 0 var(--_invalid-edge-width) var(--_edge);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* An unavailable file. The words take the disabled ink the foundation holds
|
|
659
|
+
* at its contrast, and the tile gives up the kind's colour, because a file
|
|
660
|
+
* that cannot be opened should not still be the brightest thing in the row.
|
|
661
|
+
*/
|
|
662
|
+
[data-ui='attachment'][data-part='root'][data-disabled] {
|
|
663
|
+
--_ink: var(--slotted-disabled-foreground);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
[data-ui='attachment'][data-part='root'][data-disabled] [data-part='detail'] {
|
|
667
|
+
color: var(--slotted-disabled-foreground);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
[data-ui='attachment'][data-part='root'][data-disabled] [data-part='preview'] {
|
|
671
|
+
--_kind-tile: var(--slotted-disabled-background);
|
|
672
|
+
|
|
673
|
+
color: var(--slotted-disabled-foreground);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/*
|
|
677
|
+
* A picture has no ground to give up, so it gives up its colour: the
|
|
678
|
+
* whole-strength grey is the one value that says unavailable without
|
|
679
|
+
* dimming the thumbnail below legibility.
|
|
680
|
+
*/
|
|
681
|
+
[data-ui='attachment'][data-part='root'][data-disabled] [data-part='preview'] > img {
|
|
682
|
+
filter: grayscale(1);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/* ── The card ──────────────────────────────────────────────────────────
|
|
686
|
+
*
|
|
687
|
+
* The same attachment stood on end: the picture takes the card and the name
|
|
688
|
+
* sits under it. The actions move onto the picture's corner, over the thing
|
|
689
|
+
* the reader is pointing at, on a plate of their own — the consumer's
|
|
690
|
+
* photograph is any colour, and a dark mark on a mid-blue sky was what
|
|
691
|
+
* `upload`'s first grid capture showed.
|
|
692
|
+
*/
|
|
693
|
+
[data-ui='attachment'][data-part='root'][data-orientation='vertical'] {
|
|
694
|
+
align-items: stretch;
|
|
695
|
+
grid-template-areas:
|
|
696
|
+
'preview'
|
|
697
|
+
'body'
|
|
698
|
+
'message';
|
|
699
|
+
grid-template-columns: minmax(0, 1fr);
|
|
700
|
+
inline-size: var(--_card-size);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/*
|
|
704
|
+
* The picture fills the card's content box. Its block size is stated from
|
|
705
|
+
* the card rather than from `aspect-ratio`, which carried a fractional
|
|
706
|
+
* width into a fractional height in `upload`'s grid.
|
|
707
|
+
*/
|
|
708
|
+
[data-ui='attachment'][data-orientation='vertical'] > [data-part='preview'] {
|
|
709
|
+
--_preview-radius: var(--slotted-attachment-card-preview-radius, var(--slotted-radius-md, 6px));
|
|
710
|
+
|
|
711
|
+
block-size: calc(var(--_card-size) - 2 * var(--_inset) - 2 * var(--_border-width));
|
|
712
|
+
inline-size: 100%;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
[data-ui='attachment'][data-orientation='vertical'] > [data-part='preview'] > [data-ui='icon'] {
|
|
716
|
+
--slotted-icon-size: calc(2 * var(--_glyph-size));
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* A card has room for the extension in words, and a reader scanning a wall
|
|
721
|
+
* of thumbnails for the one PDF finds it faster by the three letters than by
|
|
722
|
+
* the silhouette. It is text, drawn from the attribute the binding writes.
|
|
723
|
+
*/
|
|
724
|
+
[data-ui='attachment'][data-orientation='vertical']
|
|
725
|
+
> [data-part='preview']:not(:has(img))::after {
|
|
726
|
+
content: attr(data-extension);
|
|
727
|
+
font-size: var(--_detail-size);
|
|
728
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
729
|
+
/* Three capitals set tight read as one blot at twelve pixels; the
|
|
730
|
+
tracking is the one `upload`'s card label carries. */
|
|
731
|
+
letter-spacing: 0.06em;
|
|
732
|
+
line-height: var(--_detail-leading);
|
|
733
|
+
margin-block-start: var(--slotted-space-2, 0.25rem);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
[data-ui='attachment'][data-orientation='vertical'] > [data-part='body'] {
|
|
737
|
+
margin-block-start: var(--_gap);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
[data-ui='attachment'][data-orientation='vertical'] > [data-part='actions'] {
|
|
741
|
+
background-color: color-mix(
|
|
742
|
+
in oklab,
|
|
743
|
+
var(--slotted-surface) var(--slotted-attachment-card-actions-opacity, 96%),
|
|
744
|
+
transparent
|
|
745
|
+
);
|
|
746
|
+
border-radius: var(--slotted-attachment-action-radius, var(--slotted-control-radius, 4px));
|
|
747
|
+
box-shadow: var(--slotted-shadow-xs);
|
|
748
|
+
color: var(--slotted-text);
|
|
749
|
+
grid-area: auto;
|
|
750
|
+
inset-block-start: calc(var(--_inset) + var(--slotted-space-2, 0.25rem));
|
|
751
|
+
inset-inline-end: calc(var(--_inset) + var(--slotted-space-2, 0.25rem));
|
|
752
|
+
padding: var(--slotted-space-1, 0.125rem);
|
|
753
|
+
position: absolute;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/*
|
|
757
|
+
* On a card three glyphs wide the plate stands over the kind's mark: at 120
|
|
758
|
+
* pixels the mark is centred at 51 and the plate reaches 36 from the top
|
|
759
|
+
* across the inline end, so a PDF card showed half its page under the
|
|
760
|
+
* controls. Where there is a pointer that can hover, the plate appears with
|
|
761
|
+
* the pointer or with focus anywhere in the card — the keyboard reaches the
|
|
762
|
+
* controls by tabbing into them, which is what shows them. On a finger it
|
|
763
|
+
* stays, because there is no hover to summon it with, and on a disabled
|
|
764
|
+
* card it stays too: a card that answers no pointer cannot summon it either.
|
|
765
|
+
*/
|
|
766
|
+
@media (hover: hover) and (pointer: fine) {
|
|
767
|
+
[data-ui='attachment'][data-orientation='vertical']:not([data-disabled])
|
|
768
|
+
> [data-part='actions'] {
|
|
769
|
+
opacity: 0;
|
|
770
|
+
transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
[data-ui='attachment'][data-orientation='vertical']:not([data-disabled]):is(
|
|
774
|
+
:hover,
|
|
775
|
+
:focus-within
|
|
776
|
+
)
|
|
777
|
+
> [data-part='actions'] {
|
|
778
|
+
opacity: 1;
|
|
779
|
+
transition: opacity var(--slotted-duration-short) var(--slotted-easing-enter);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/* ── The set ───────────────────────────────────────────────────────────
|
|
784
|
+
*
|
|
785
|
+
* A native list. It takes focus only when its last attachment is removed
|
|
786
|
+
* with the keyboard, which is what `tabindex="-1"` is for.
|
|
787
|
+
*/
|
|
788
|
+
[data-ui='attachment'][data-part='group'] {
|
|
789
|
+
box-sizing: border-box;
|
|
790
|
+
display: flex;
|
|
791
|
+
flex-direction: column;
|
|
792
|
+
gap: var(--slotted-attachment-group-gap, var(--slotted-space-4, 0.5rem));
|
|
793
|
+
list-style: none;
|
|
794
|
+
margin: 0;
|
|
795
|
+
min-inline-size: 0;
|
|
796
|
+
padding: 0;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
[data-ui='attachment'][data-part='group']:focus-visible {
|
|
800
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
801
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/*
|
|
805
|
+
* An empty set takes no room: a message whose last file was removed does
|
|
806
|
+
* not keep a gap where the files were.
|
|
807
|
+
*/
|
|
808
|
+
[data-ui='attachment'][data-part='group']:empty {
|
|
809
|
+
display: none;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/** Rows that share the width: two or three to a line, never narrower than a name. */
|
|
813
|
+
[data-ui='attachment'][data-part='group'][data-layout='grid'] {
|
|
814
|
+
display: grid;
|
|
815
|
+
grid-template-columns: repeat(
|
|
816
|
+
auto-fill,
|
|
817
|
+
minmax(min(100%, var(--slotted-attachment-grid-min, 16rem)), 1fr)
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/* Cards keep their own width, so every card is a whole pixel wide. */
|
|
822
|
+
[data-ui='attachment'][data-part='group'][data-layout='grid'][data-orientation='vertical'] {
|
|
823
|
+
grid-template-columns: repeat(auto-fill, var(--_card-size));
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* One line that scrolls — a composer's tray. The strip is padded by the
|
|
828
|
+
* ring's reach and pulled back by the same amount, so a focused attachment
|
|
829
|
+
* at either end keeps its whole ring inside the scroll box instead of
|
|
830
|
+
* losing two pixels of it to the clip.
|
|
831
|
+
*/
|
|
832
|
+
[data-ui='attachment'][data-part='group'][data-layout='strip'] {
|
|
833
|
+
--_ring-reach: calc(
|
|
834
|
+
var(--slotted-focus-ring-width, 2px) + var(--slotted-focus-ring-offset, 2px)
|
|
835
|
+
);
|
|
836
|
+
|
|
837
|
+
flex-direction: row;
|
|
838
|
+
margin: calc(-1 * var(--_ring-reach));
|
|
839
|
+
overflow-x: auto;
|
|
840
|
+
overscroll-behavior-inline: contain;
|
|
841
|
+
padding: var(--_ring-reach);
|
|
842
|
+
scroll-padding-inline: var(--_ring-reach);
|
|
843
|
+
scroll-snap-type: inline proximity;
|
|
844
|
+
scrollbar-color: var(--slotted-border-strong) transparent;
|
|
845
|
+
scrollbar-width: thin;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
[data-ui='attachment'][data-part='group'][data-layout='strip'] > [data-part='root'] {
|
|
849
|
+
flex: none;
|
|
850
|
+
scroll-snap-align: start;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
[data-ui='attachment'][data-part='group'][data-layout='strip']
|
|
854
|
+
> [data-part='root'][data-orientation='horizontal'] {
|
|
855
|
+
inline-size: var(--slotted-attachment-strip-item-size, 15rem);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/* ── Forced colours ────────────────────────────────────────────────────
|
|
859
|
+
*
|
|
860
|
+
* The theme remaps every token this sheet reads. What it cannot reach is a
|
|
861
|
+
* state said by a mix into a ground the system flattens to `Canvas`: the
|
|
862
|
+
* pointer on the attachment and on its controls changed nothing a reader
|
|
863
|
+
* could see. The system's own pair answers instead, as it does for
|
|
864
|
+
* `upload`'s controls, and the failed attachment keeps its edge.
|
|
865
|
+
*/
|
|
866
|
+
@media (forced-colors: active) {
|
|
867
|
+
[data-ui='attachment'][data-part='root'][data-invalid] {
|
|
868
|
+
--_edge: CanvasText;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/* The doubled edge is a shadow, which forced colours remove: it comes back
|
|
872
|
+
as an outline inside the border, and a focused failed attachment draws
|
|
873
|
+
its ring double. */
|
|
874
|
+
[data-ui='attachment'][data-part='root'][data-invalid] {
|
|
875
|
+
outline: var(--_invalid-edge-width) solid CanvasText;
|
|
876
|
+
outline-offset: calc(-1 * (var(--_border-width) + var(--_invalid-edge-width)));
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
[data-ui='attachment'][data-part='root'][data-invalid]:has(
|
|
880
|
+
> [data-part='body'] > [data-part='trigger']:focus-visible
|
|
881
|
+
) {
|
|
882
|
+
outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
|
|
883
|
+
var(--slotted-focus-ring-color, Highlight);
|
|
884
|
+
outline-offset: var(--slotted-focus-ring-offset, 2px);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
[data-ui='attachment'][data-part='root']:not([data-disabled]):not([data-loading]):has(
|
|
888
|
+
> [data-part='body'] > [data-part='trigger']:is(:hover, :active)
|
|
889
|
+
) {
|
|
890
|
+
border-color: Highlight;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
[data-ui='attachment'] [data-part='preview'] {
|
|
894
|
+
border: var(--_border-width) solid ButtonBorder;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
[data-ui='attachment']
|
|
898
|
+
:is([data-part='action'], [data-part='remove']):not(:disabled):not([aria-disabled='true']):is(
|
|
899
|
+
:hover,
|
|
900
|
+
:active
|
|
901
|
+
) {
|
|
902
|
+
background-color: Highlight;
|
|
903
|
+
color: HighlightText;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|