@slotted/styles 0.0.0-stage → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/package.json +276 -3
- package/src/accordion/accordion.css +96 -0
- package/src/accordion/accordion.tokens.json +14 -0
- package/src/action-bar/action-bar.css +455 -0
- package/src/action-bar/action-bar.tokens.json +50 -0
- package/src/action-sheet/action-sheet.css +462 -0
- package/src/action-sheet/action-sheet.tokens.json +59 -0
- package/src/alert/alert.css +764 -0
- package/src/alert/alert.tokens.json +77 -0
- package/src/app-shell/app-shell.css +1899 -0
- package/src/app-shell/app-shell.tokens.json +88 -0
- package/src/aspect-ratio/aspect-ratio.css +131 -0
- package/src/aspect-ratio/aspect-ratio.tokens.json +1 -0
- package/src/attachment/attachment.css +906 -0
- package/src/attachment/attachment.tokens.json +85 -0
- package/src/avatar/avatar.css +598 -0
- package/src/avatar/avatar.tokens.json +64 -0
- package/src/avatar-group/avatar-group.css +268 -0
- package/src/avatar-group/avatar-group.tokens.json +16 -0
- package/src/badge/badge.css +462 -0
- package/src/badge/badge.tokens.json +58 -0
- package/src/breadcrumb/breadcrumb.css +441 -0
- package/src/breadcrumb/breadcrumb.tokens.json +50 -0
- package/src/button/button.css +754 -0
- package/src/button/button.tokens.json +72 -0
- package/src/button-group/button-group.css +455 -0
- package/src/button-group/button-group.tokens.json +11 -0
- package/src/calendar/calendar.css +846 -0
- package/src/calendar/calendar.tokens.json +67 -0
- package/src/card/card.css +602 -0
- package/src/card/card.tokens.json +68 -0
- package/src/carousel/carousel.css +860 -0
- package/src/carousel/carousel.tokens.json +62 -0
- package/src/cascader/cascader.css +1320 -0
- package/src/cascader/cascader.tokens.json +103 -0
- package/src/chart/chart.css +1473 -0
- package/src/chart/chart.tokens.json +107 -0
- package/src/checkbox/checkbox.css +1011 -0
- package/src/checkbox/checkbox.tokens.json +102 -0
- package/src/checkbox-group/checkbox-group.css +70 -0
- package/src/checkbox-group/checkbox-group.tokens.json +6 -0
- package/src/code-block/code-block.css +555 -0
- package/src/code-block/code-block.tokens.json +69 -0
- package/src/collapsible/collapsible.css +546 -0
- package/src/collapsible/collapsible.tokens.json +55 -0
- package/src/color-picker/color-picker.css +607 -0
- package/src/color-picker/color-picker.tokens.json +58 -0
- package/src/column-manager/column-manager.css +452 -0
- package/src/column-manager/column-manager.tokens.json +43 -0
- package/src/combobox/combobox.css +1048 -0
- package/src/combobox/combobox.tokens.json +112 -0
- package/src/command-palette/command-palette.css +1000 -0
- package/src/command-palette/command-palette.tokens.json +75 -0
- package/src/container/container.css +198 -0
- package/src/container/container.tokens.json +18 -0
- package/src/context-menu/context-menu.css +104 -0
- package/src/context-menu/context-menu.tokens.json +11 -0
- package/src/currency-field/currency-field.css +787 -0
- package/src/currency-field/currency-field.tokens.json +80 -0
- package/src/dashboard-grid/dashboard-grid.css +748 -0
- package/src/dashboard-grid/dashboard-grid.tokens.json +67 -0
- package/src/data-grid/data-grid.css +2484 -0
- package/src/data-grid/data-grid.tokens.json +100 -0
- package/src/data-list/data-list.css +455 -0
- package/src/data-list/data-list.tokens.json +35 -0
- package/src/date-field/date-field.css +560 -0
- package/src/date-field/date-field.tokens.json +80 -0
- package/src/date-picker/date-picker.css +420 -0
- package/src/date-picker/date-picker.tokens.json +49 -0
- package/src/date-range-picker/date-range-picker.css +983 -0
- package/src/date-range-picker/date-range-picker.tokens.json +109 -0
- package/src/datetime-field/datetime-field.css +850 -0
- package/src/datetime-field/datetime-field.tokens.json +91 -0
- package/src/datetime-picker/datetime-picker.css +843 -0
- package/src/datetime-picker/datetime-picker.tokens.json +78 -0
- package/src/datetime-range-picker/datetime-range-picker.css +1375 -0
- package/src/datetime-range-picker/datetime-range-picker.tokens.json +135 -0
- package/src/description-list/description-list.css +675 -0
- package/src/description-list/description-list.tokens.json +66 -0
- package/src/detail-panel/detail-panel.css +329 -0
- package/src/detail-panel/detail-panel.tokens.json +33 -0
- package/src/dialog/dialog.css +968 -0
- package/src/dialog/dialog.tokens.json +66 -0
- package/src/divider/divider.css +131 -0
- package/src/divider/divider.tokens.json +18 -0
- package/src/drawer/drawer.css +1303 -0
- package/src/drawer/drawer.tokens.json +75 -0
- package/src/entity-picker/entity-picker.css +1367 -0
- package/src/entity-picker/entity-picker.tokens.json +118 -0
- package/src/event-calendar/event-calendar.css +2148 -0
- package/src/event-calendar/event-calendar.tokens.json +87 -0
- package/src/field/field.css +707 -0
- package/src/field/field.tokens.json +58 -0
- package/src/field-group/field-group.css +226 -0
- package/src/field-group/field-group.tokens.json +33 -0
- package/src/filter-bar/filter-bar.css +1306 -0
- package/src/filter-bar/filter-bar.tokens.json +78 -0
- package/src/form/form.css +218 -0
- package/src/form/form.tokens.json +32 -0
- package/src/fullscreen/fullscreen.css +376 -0
- package/src/fullscreen/fullscreen.tokens.json +50 -0
- package/src/grid/grid.css +173 -0
- package/src/grid/grid.tokens.json +20 -0
- package/src/highlight/highlight.css +135 -0
- package/src/highlight/highlight.tokens.json +16 -0
- package/src/hover-card/hover-card.css +93 -0
- package/src/hover-card/hover-card.tokens.json +17 -0
- package/src/icon/icon.css +136 -0
- package/src/icon/icon.tokens.json +14 -0
- package/src/image/image.css +449 -0
- package/src/image/image.tokens.json +27 -0
- package/src/import-export-center/import-export-center.css +1175 -0
- package/src/import-export-center/import-export-center.tokens.json +84 -0
- package/src/inline/inline.css +149 -0
- package/src/inline/inline.tokens.json +14 -0
- package/src/inline-message/inline-message.css +396 -0
- package/src/inline-message/inline-message.tokens.json +19 -0
- package/src/kbd/kbd.css +490 -0
- package/src/kbd/kbd.tokens.json +40 -0
- package/src/link/link.css +237 -0
- package/src/link/link.tokens.json +25 -0
- package/src/list/list.css +240 -0
- package/src/list/list.tokens.json +42 -0
- package/src/list-item/list-item.css +937 -0
- package/src/list-item/list-item.tokens.json +114 -0
- package/src/listbox/listbox.css +674 -0
- package/src/listbox/listbox.tokens.json +72 -0
- package/src/loading-overlay/loading-overlay.css +730 -0
- package/src/loading-overlay/loading-overlay.tokens.json +48 -0
- package/src/markdown/markdown.css +168 -0
- package/src/markdown/markdown.tokens.json +18 -0
- package/src/menu/menu.css +817 -0
- package/src/menu/menu.tokens.json +78 -0
- package/src/menubar/menubar.css +170 -0
- package/src/menubar/menubar.tokens.json +32 -0
- package/src/message/message.css +1453 -0
- package/src/message/message.tokens.json +85 -0
- package/src/message-scroller/message-scroller.css +371 -0
- package/src/message-scroller/message-scroller.tokens.json +47 -0
- package/src/multi-select/multi-select.css +1427 -0
- package/src/multi-select/multi-select.tokens.json +114 -0
- package/src/navigation-menu/navigation-menu.css +1029 -0
- package/src/navigation-menu/navigation-menu.tokens.json +70 -0
- package/src/notification-center/notification-center.css +856 -0
- package/src/notification-center/notification-center.tokens.json +84 -0
- package/src/number-field/number-field.css +794 -0
- package/src/number-field/number-field.tokens.json +80 -0
- package/src/otp-input/otp-input.css +525 -0
- package/src/otp-input/otp-input.tokens.json +69 -0
- package/src/page/page.css +179 -0
- package/src/page/page.tokens.json +20 -0
- package/src/page-header/page-header.css +1181 -0
- package/src/page-header/page-header.tokens.json +71 -0
- package/src/pagination/pagination.css +500 -0
- package/src/pagination/pagination.tokens.json +56 -0
- package/src/popover/popover.css +526 -0
- package/src/popover/popover.tokens.json +60 -0
- package/src/progress-indicator/progress-indicator.css +1584 -0
- package/src/progress-indicator/progress-indicator.tokens.json +50 -0
- package/src/prompt-composer/prompt-composer.css +680 -0
- package/src/prompt-composer/prompt-composer.tokens.json +99 -0
- package/src/prose/prose.css +733 -0
- package/src/prose/prose.tokens.json +79 -0
- package/src/radio-group/radio-group.css +778 -0
- package/src/radio-group/radio-group.tokens.json +103 -0
- package/src/rating/rating.css +1071 -0
- package/src/rating/rating.tokens.json +88 -0
- package/src/result-state/result-state.css +980 -0
- package/src/result-state/result-state.tokens.json +63 -0
- package/src/runtime-properties.mjs +321 -0
- package/src/saved-views/saved-views.css +394 -0
- package/src/saved-views/saved-views.tokens.json +41 -0
- package/src/scroll-area/scroll-area.css +481 -0
- package/src/scroll-area/scroll-area.tokens.json +15 -0
- package/src/section/section.css +177 -0
- package/src/section/section.tokens.json +39 -0
- package/src/segmented-control/segmented-control.css +564 -0
- package/src/segmented-control/segmented-control.tokens.json +67 -0
- package/src/select/select.css +1593 -0
- package/src/select/select.tokens.json +110 -0
- package/src/side-navigation/side-navigation.css +1096 -0
- package/src/side-navigation/side-navigation.tokens.json +87 -0
- package/src/skeleton/skeleton.css +431 -0
- package/src/skeleton/skeleton.tokens.json +29 -0
- package/src/slider/slider.css +792 -0
- package/src/slider/slider.tokens.json +75 -0
- package/src/sparkline/sparkline.css +282 -0
- package/src/sparkline/sparkline.tokens.json +45 -0
- package/src/spinner/spinner.css +224 -0
- package/src/spinner/spinner.tokens.json +8 -0
- package/src/split-button/split-button.css +253 -0
- package/src/split-button/split-button.tokens.json +12 -0
- package/src/splitter/splitter.css +336 -0
- package/src/splitter/splitter.tokens.json +24 -0
- package/src/stack/stack.css +128 -0
- package/src/stack/stack.tokens.json +14 -0
- package/src/stat-card/stat-card.css +641 -0
- package/src/stat-card/stat-card.tokens.json +95 -0
- package/src/status-indicator/status-indicator.css +481 -0
- package/src/status-indicator/status-indicator.tokens.json +38 -0
- package/src/stepper/stepper.css +629 -0
- package/src/stepper/stepper.tokens.json +72 -0
- package/src/switch/switch.css +703 -0
- package/src/switch/switch.tokens.json +75 -0
- package/src/table/table.css +1482 -0
- package/src/table/table.tokens.json +69 -0
- package/src/table-of-contents/table-of-contents.css +533 -0
- package/src/table-of-contents/table-of-contents.tokens.json +53 -0
- package/src/tabs/tabs.css +998 -0
- package/src/tabs/tabs.tokens.json +82 -0
- package/src/tag/tag.css +768 -0
- package/src/tag/tag.tokens.json +85 -0
- package/src/tags-input/tags-input.css +757 -0
- package/src/tags-input/tags-input.tokens.json +84 -0
- package/src/text-field/text-field.css +900 -0
- package/src/text-field/text-field.tokens.json +87 -0
- package/src/textarea/textarea.css +365 -0
- package/src/textarea/textarea.tokens.json +59 -0
- package/src/time-field/time-field.css +719 -0
- package/src/time-field/time-field.tokens.json +86 -0
- package/src/time-picker/time-picker.css +665 -0
- package/src/time-picker/time-picker.tokens.json +66 -0
- package/src/time-range-picker/time-range-picker.css +1287 -0
- package/src/time-range-picker/time-range-picker.tokens.json +134 -0
- package/src/timeline/timeline.css +1596 -0
- package/src/timeline/timeline.tokens.json +96 -0
- package/src/toast/toast.css +943 -0
- package/src/toast/toast.tokens.json +90 -0
- package/src/toggle/toggle.css +290 -0
- package/src/toggle/toggle.tokens.json +54 -0
- package/src/toggle-group/toggle-group.css +463 -0
- package/src/toggle-group/toggle-group.tokens.json +54 -0
- package/src/toolbar/toolbar.css +434 -0
- package/src/toolbar/toolbar.tokens.json +49 -0
- package/src/tooltip/tooltip.css +368 -0
- package/src/tooltip/tooltip.tokens.json +39 -0
- package/src/top-navigation/top-navigation.css +1287 -0
- package/src/top-navigation/top-navigation.tokens.json +91 -0
- package/src/tour/tour.css +680 -0
- package/src/tour/tour.tokens.json +67 -0
- package/src/transfer/transfer.css +571 -0
- package/src/transfer/transfer.tokens.json +49 -0
- package/src/tree/tree.css +1000 -0
- package/src/tree/tree.tokens.json +80 -0
- package/src/typography/typography.css +211 -0
- package/src/typography/typography.tokens.json +31 -0
- package/src/upload/upload.css +1706 -0
- package/src/upload/upload.tokens.json +123 -0
- package/src/visually-hidden/visually-hidden.css +52 -0
- package/src/visually-hidden/visually-hidden.tokens.json +1 -0
- package/src/wizard/wizard.css +740 -0
- package/src/wizard/wizard.tokens.json +63 -0
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
@layer slotted.components {
|
|
2
|
+
/**
|
|
3
|
+
* Prose is the typography of a region the application did not author element
|
|
4
|
+
* by element. The tree arrives from a Markdown renderer, a CMS or a language
|
|
5
|
+
* model, and the page never gets to put an attribute on the `h2`.
|
|
6
|
+
*
|
|
7
|
+
* Three things style those bare tags, and this family must lose to the other
|
|
8
|
+
* two: a Slotted family placed inside the region, selected by `data-ui` at
|
|
9
|
+
* 0-1-0, and the consumer's own rule. So every content rule below is written
|
|
10
|
+
*
|
|
11
|
+
* [data-ui='prose'] :where(<tag>):not(:where(<the donut>))
|
|
12
|
+
*
|
|
13
|
+
* which weighs 0-1-0 — the root and nothing else. `:where()` has no weight,
|
|
14
|
+
* and `:not()` takes the weight of its argument, which here is also none.
|
|
15
|
+
*
|
|
16
|
+
* The donut is the opt-out: `data-part='exclude'` and everything inside it.
|
|
17
|
+
* `@scope ([data-ui='prose']) to ([data-part='exclude'])` says the same thing
|
|
18
|
+
* in one line and resolves lower still, and it was refused on a date rather
|
|
19
|
+
* than on a principle: it reached Baseline in March 2026, and an `@scope`
|
|
20
|
+
* block a browser cannot parse drops this whole stylesheet instead of one
|
|
21
|
+
* declaration. The attribute is the same either way, so the move costs no
|
|
22
|
+
* contract change. `specs/components/prose/contract.json` holds the note.
|
|
23
|
+
*/
|
|
24
|
+
[data-ui='prose'] {
|
|
25
|
+
/**
|
|
26
|
+
* The space between blocks. Typeset proves one number governs a whole page
|
|
27
|
+
* — heading gaps, list gaps and the room around a rule all derive from it —
|
|
28
|
+
* and the `flow` axis names the three values worth having instead of asking
|
|
29
|
+
* every consumer to pick a length.
|
|
30
|
+
*
|
|
31
|
+
* It is stated in `em`, like the heading ladder below, because block rhythm
|
|
32
|
+
* that did not follow the type would tighten as the reader enlarged it.
|
|
33
|
+
*/
|
|
34
|
+
--_flow: var(--slotted-prose-flow-normal, 1.15em);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The heading ladder, and the family's only other literals. A heading in
|
|
38
|
+
* editorial content is sized relative to the text it introduces; the
|
|
39
|
+
* foundation type scale is a scale of absolute control steps that stops at
|
|
40
|
+
* 1.5rem because no control needs more. An `h1` taken from it would be 1.33
|
|
41
|
+
* times its own body text at `size="xl"`, and adding a fourth step to the
|
|
42
|
+
* scale would change ground thirty-four families stand on to serve this
|
|
43
|
+
* one. `docs/history/designs/2026-09-04-prose-design.md` section 5 holds
|
|
44
|
+
* the trade; `docs/token-foundation.md` rule 1 grants the exemption to a
|
|
45
|
+
* family token whose reason is recorded, which is what `container` already
|
|
46
|
+
* used for its three page widths.
|
|
47
|
+
*/
|
|
48
|
+
--_h1: var(--slotted-prose-h1-size, 2em);
|
|
49
|
+
--_h2: var(--slotted-prose-h2-size, 1.5em);
|
|
50
|
+
--_h3: var(--slotted-prose-h3-size, 1.25em);
|
|
51
|
+
--_h4: var(--slotted-prose-h4-size, 1.125em);
|
|
52
|
+
--_h5: var(--slotted-prose-h5-size, 1em);
|
|
53
|
+
--_h6: var(--slotted-prose-h6-size, 0.875em);
|
|
54
|
+
|
|
55
|
+
--_muted: var(--slotted-text-muted, GrayText);
|
|
56
|
+
--_rule: var(--slotted-border-subtle, ButtonBorder);
|
|
57
|
+
--_ground: var(--slotted-surface-sunken, Canvas);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The query container. It restyles descendants only and never the root's
|
|
61
|
+
* own tracks, so the family needs one element rather than the two
|
|
62
|
+
* `docs/build-briefing.md` requires of a family that resizes its own grid.
|
|
63
|
+
*/
|
|
64
|
+
container-name: slotted-prose;
|
|
65
|
+
container-type: inline-size;
|
|
66
|
+
|
|
67
|
+
font-family: var(
|
|
68
|
+
--slotted-prose-font-family,
|
|
69
|
+
var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif)
|
|
70
|
+
);
|
|
71
|
+
/* One rem, not the control step: a paragraph is not a button label. */
|
|
72
|
+
font-size: var(--slotted-font-size-lg, 1rem);
|
|
73
|
+
/**
|
|
74
|
+
* The reading leading, stated once rather than taken per step. The scale
|
|
75
|
+
* pairs 1.45 with its two largest steps because a control's label is one
|
|
76
|
+
* line; SC 1.4.8 asks a paragraph for at least 1.5, and a paragraph is
|
|
77
|
+
* what this family sets.
|
|
78
|
+
*/
|
|
79
|
+
line-height: var(--slotted-prose-line-height, var(--slotted-line-height-md, 1.5));
|
|
80
|
+
/**
|
|
81
|
+
* A word longer than its column breaks rather than escaping it, in the
|
|
82
|
+
* form that also lowers the element's min-content size — so a pasted URL
|
|
83
|
+
* cannot widen the table cell or the grid track that holds it. The
|
|
84
|
+
* property inherits, which is the point: it reaches every tag below
|
|
85
|
+
* without a rule of its own.
|
|
86
|
+
*/
|
|
87
|
+
overflow-wrap: anywhere;
|
|
88
|
+
/* No colour is painted: text on a tonal surface reads that surface. */
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* ── The two axes ───────────────────────────────────────────────────── */
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Named for the type-scale step the body takes, as `typography`'s own axis
|
|
95
|
+
* is, so the offset from control type is a fact a reader can check rather
|
|
96
|
+
* than one hidden behind `sm`.
|
|
97
|
+
*/
|
|
98
|
+
[data-ui='prose'][data-size='md'] {
|
|
99
|
+
font-size: var(--slotted-font-size-md, 0.875rem);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[data-ui='prose'][data-size='lg'] {
|
|
103
|
+
font-size: var(--slotted-font-size-lg, 1rem);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[data-ui='prose'][data-size='xl'] {
|
|
107
|
+
font-size: var(--slotted-font-size-xl, 1.125rem);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[data-ui='prose'][data-flow='tight'] {
|
|
111
|
+
--_flow: var(--slotted-prose-flow-tight, 0.75em);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-ui='prose'][data-flow='normal'] {
|
|
115
|
+
--_flow: var(--slotted-prose-flow-normal, 1.15em);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
[data-ui='prose'][data-flow='loose'] {
|
|
119
|
+
--_flow: var(--slotted-prose-flow-loose, 1.6em);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* ── Flow ───────────────────────────────────────────────────────────────
|
|
123
|
+
*
|
|
124
|
+
* Every gap in this family is a `margin-block-start` on the block that
|
|
125
|
+
* arrives, and nothing selects forward. `:last-child`, `:has()` and `:empty`
|
|
126
|
+
* each change their own match when a block is appended, which restyles text
|
|
127
|
+
* already under the reader's eye — the failure a page streaming a language
|
|
128
|
+
* model's answer hits on every token. `:first-child` and `+` look backwards
|
|
129
|
+
* and are stable, and `prose.styles.test.mjs` holds the distinction.
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
[data-ui='prose']
|
|
133
|
+
:where(
|
|
134
|
+
p,
|
|
135
|
+
ul,
|
|
136
|
+
ol,
|
|
137
|
+
dl,
|
|
138
|
+
pre,
|
|
139
|
+
table,
|
|
140
|
+
figure,
|
|
141
|
+
blockquote,
|
|
142
|
+
details,
|
|
143
|
+
hr,
|
|
144
|
+
h1,
|
|
145
|
+
h2,
|
|
146
|
+
h3,
|
|
147
|
+
h4,
|
|
148
|
+
h5,
|
|
149
|
+
h6,
|
|
150
|
+
[data-part='scroll'],
|
|
151
|
+
[data-part='footnotes']
|
|
152
|
+
):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
153
|
+
margin-block-end: 0;
|
|
154
|
+
margin-block-start: var(--_flow);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Nothing opens with space it did not ask for. The rule reaches the first
|
|
159
|
+
* child of the region and of every box that holds blocks of its own, so a
|
|
160
|
+
* quotation, a list item and a table cell each start flush.
|
|
161
|
+
*/
|
|
162
|
+
[data-ui='prose'] > :first-child,
|
|
163
|
+
[data-ui='prose']
|
|
164
|
+
:where(li, blockquote, td, th, details, figure, [data-part='scroll'])
|
|
165
|
+
> :first-child {
|
|
166
|
+
margin-block-start: 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* A heading binds to the text it introduces: more room above it than below.
|
|
171
|
+
* The pair is what keeps two consecutive headings — a section title followed
|
|
172
|
+
* by its first subsection — from collapsing into one block.
|
|
173
|
+
*/
|
|
174
|
+
[data-ui='prose']
|
|
175
|
+
:where(h1, h2, h3, h4, h5, h6):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
176
|
+
color: var(--slotted-text, CanvasText);
|
|
177
|
+
font-family: var(
|
|
178
|
+
--slotted-font-family-heading,
|
|
179
|
+
var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif)
|
|
180
|
+
);
|
|
181
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
182
|
+
line-height: var(--slotted-line-height-2xl, 1.4);
|
|
183
|
+
/**
|
|
184
|
+
* The gap above a heading is stated in the heading's own `em`, so it grows
|
|
185
|
+
* with the level: 48 pixels above an `h2` and 40 above an `h3` at the
|
|
186
|
+
* default step. That compounding is the decision, not a side effect —
|
|
187
|
+
* Tailwind reaches the same two distances by tuning six per-level values
|
|
188
|
+
* by hand, and one ratio that follows the ladder is the same result
|
|
189
|
+
* without the table.
|
|
190
|
+
*/
|
|
191
|
+
margin-block-start: calc(var(--_flow) * 1.75);
|
|
192
|
+
/**
|
|
193
|
+
* A heading reached by its own anchor stops below the top edge rather than
|
|
194
|
+
* against it, so the reader lands on a title with its text under it.
|
|
195
|
+
*/
|
|
196
|
+
scroll-margin-block-start: var(--_flow);
|
|
197
|
+
text-wrap: balance;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
[data-ui='prose']
|
|
201
|
+
:where(h1, h2, h3, h4, h5, h6)
|
|
202
|
+
+ :not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
203
|
+
margin-block-start: calc(var(--_flow) * 0.5);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
[data-ui='prose'] :where(h1):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
207
|
+
font-size: var(--_h1);
|
|
208
|
+
line-height: var(--slotted-line-height-3xl, 1.3);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
[data-ui='prose'] :where(h2):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
212
|
+
font-size: var(--_h2);
|
|
213
|
+
line-height: var(--slotted-line-height-3xl, 1.3);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
[data-ui='prose'] :where(h3):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
217
|
+
font-size: var(--_h3);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
[data-ui='prose'] :where(h4):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
221
|
+
font-size: var(--_h4);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
[data-ui='prose'] :where(h5):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
225
|
+
font-size: var(--_h5);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The sixth level is smaller than the body text, so weight and case are what
|
|
230
|
+
* carry it. A heading that is merely smaller reads as a caption.
|
|
231
|
+
*/
|
|
232
|
+
[data-ui='prose'] :where(h6):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
233
|
+
color: var(--_muted);
|
|
234
|
+
font-size: var(--_h6);
|
|
235
|
+
letter-spacing: var(--slotted-prose-h6-tracking, 0.06em);
|
|
236
|
+
text-transform: uppercase;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The Arabic script joins its letters and has no case at all, so the
|
|
241
|
+
* tracking that pays back what capitals took would sever the joins. The list
|
|
242
|
+
* is the common languages written in it, which is the correction
|
|
243
|
+
* `typography` already carries.
|
|
244
|
+
*/
|
|
245
|
+
[data-ui='prose']
|
|
246
|
+
:where(h6):not(:where([data-part='exclude'], [data-part='exclude'] *)):where(
|
|
247
|
+
:lang(ar),
|
|
248
|
+
:lang(fa),
|
|
249
|
+
:lang(ur)
|
|
250
|
+
) {
|
|
251
|
+
letter-spacing: normal;
|
|
252
|
+
text-transform: none;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* ── Running text ───────────────────────────────────────────────────── */
|
|
256
|
+
|
|
257
|
+
[data-ui='prose'] :where(p):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
258
|
+
/**
|
|
259
|
+
* `pretty` asks the browser for the last few lines rather than the whole
|
|
260
|
+
* paragraph, which is what keeps a widow off the end without the cost
|
|
261
|
+
* `balance` carries over a long block.
|
|
262
|
+
*/
|
|
263
|
+
text-wrap: pretty;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
[data-ui='prose'] :where(strong, b):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
267
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
[data-ui='prose'] :where(small):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
271
|
+
font-size: var(--slotted-prose-small-size, 0.875em);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* The one element in editorial text that carries a colour of its own. The
|
|
276
|
+
* warning tone is the highlighter a reader recognises, and the pair is the
|
|
277
|
+
* theme's own — a mark painted with a background and no matching ink is a
|
|
278
|
+
* mark that vanishes in the dark scheme.
|
|
279
|
+
*/
|
|
280
|
+
[data-ui='prose'] :where(mark):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
281
|
+
background-color: var(--slotted-tone-warning-surface, Mark);
|
|
282
|
+
border-radius: var(--slotted-radius-xs, 2px);
|
|
283
|
+
color: var(--slotted-tone-warning-text, MarkText);
|
|
284
|
+
padding-inline: var(--slotted-prose-mark-inset, 0.2em);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
[data-ui='prose']
|
|
288
|
+
:where(abbr[title]):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
289
|
+
text-decoration-line: underline;
|
|
290
|
+
text-decoration-style: dotted;
|
|
291
|
+
text-underline-offset: 0.2em;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* A raised or lowered figure takes its own line height out of the line it
|
|
296
|
+
* sits in; zero is what keeps the paragraph's rhythm even where a footnote
|
|
297
|
+
* reference lands.
|
|
298
|
+
*/
|
|
299
|
+
[data-ui='prose'] :where(sub, sup):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
300
|
+
font-size: 0.75em;
|
|
301
|
+
line-height: 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* ── Links ──────────────────────────────────────────────────────────────
|
|
305
|
+
*
|
|
306
|
+
* A bare `a` from a Markdown renderer reads the `link` family's own tokens,
|
|
307
|
+
* so a rendered link and a `<Link>` on the same page are one theme decision
|
|
308
|
+
* rather than two colours for one idea. No selector here names another
|
|
309
|
+
* family's `data-ui`, which is the rule `accordion` and `collapsible` set: a
|
|
310
|
+
* `<Link>` placed inside this region wins on specificity and is left alone.
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
[data-ui='prose'] :where(a[href]):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
314
|
+
color: var(--slotted-link-color, var(--slotted-tone-accent-text, LinkText));
|
|
315
|
+
text-decoration-color: var(
|
|
316
|
+
--slotted-link-underline-color,
|
|
317
|
+
color-mix(in oklab, currentColor 55%, transparent)
|
|
318
|
+
);
|
|
319
|
+
text-decoration-line: underline;
|
|
320
|
+
text-decoration-thickness: var(--slotted-link-underline-thickness, from-font);
|
|
321
|
+
text-underline-offset: var(--slotted-link-underline-offset, 0.15em);
|
|
322
|
+
transition:
|
|
323
|
+
color var(--slotted-duration-short) var(--slotted-easing-standard),
|
|
324
|
+
text-decoration-color var(--slotted-duration-short) var(--slotted-easing-standard);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
[data-ui='prose']
|
|
328
|
+
:where(a[href]):not(:where([data-part='exclude'], [data-part='exclude'] *)):not(
|
|
329
|
+
[data-disabled]
|
|
330
|
+
):hover {
|
|
331
|
+
color: var(
|
|
332
|
+
--slotted-link-color-hover,
|
|
333
|
+
color-mix(
|
|
334
|
+
in oklab,
|
|
335
|
+
var(--slotted-link-color, var(--slotted-tone-accent-text, LinkText)) 65%,
|
|
336
|
+
var(--slotted-text, CanvasText)
|
|
337
|
+
)
|
|
338
|
+
);
|
|
339
|
+
text-decoration-color: currentColor;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
[data-ui='prose']
|
|
343
|
+
:where(a[href]):not(:where([data-part='exclude'], [data-part='exclude'] *)):focus-visible {
|
|
344
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
345
|
+
outline-offset: var(--slotted-link-focus-ring-offset, 1px);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* ── Lists ──────────────────────────────────────────────────────────────
|
|
349
|
+
*
|
|
350
|
+
* The native `::marker` is kept. `list-item` draws its own because its row is
|
|
351
|
+
* a grid and CSS has no `display` that is both `list-item` and a grid; an
|
|
352
|
+
* editorial `li` is not a grid, and removing the marker costs the list
|
|
353
|
+
* semantics Safari drops with it —
|
|
354
|
+
* <https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html>.
|
|
355
|
+
*/
|
|
356
|
+
|
|
357
|
+
[data-ui='prose'] :where(ul, ol):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
358
|
+
padding-inline-start: var(--slotted-prose-list-inset, 1.5em);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
[data-ui='prose'] :where(li):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
362
|
+
margin-block-start: calc(var(--_flow) * 0.35);
|
|
363
|
+
padding-inline-start: var(--slotted-prose-marker-gap, 0.25em);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
[data-ui='prose'] :where(li):not(:where([data-part='exclude'], [data-part='exclude'] *))::marker {
|
|
367
|
+
color: var(--_muted);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* A nested list belongs to the row above it, so it takes the gap between two
|
|
372
|
+
* rows rather than the gap between two blocks.
|
|
373
|
+
*/
|
|
374
|
+
[data-ui='prose']
|
|
375
|
+
:where(li)
|
|
376
|
+
> :where(ul, ol):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
377
|
+
margin-block-start: calc(var(--_flow) * 0.35);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
[data-ui='prose'] :where(dt):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
381
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
382
|
+
margin-block-start: calc(var(--_flow) * 0.5);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
[data-ui='prose'] :where(dd):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
386
|
+
margin-inline-start: var(--slotted-prose-list-inset, 1.5em);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* ── Quotation ──────────────────────────────────────────────────────── */
|
|
390
|
+
|
|
391
|
+
[data-ui='prose'] :where(blockquote):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
392
|
+
border-inline-start: var(
|
|
393
|
+
--slotted-prose-quote-thickness,
|
|
394
|
+
var(--slotted-border-width-thick, 2px)
|
|
395
|
+
)
|
|
396
|
+
solid var(--slotted-prose-quote-color, var(--slotted-border-strong, ButtonBorder));
|
|
397
|
+
color: var(--_muted);
|
|
398
|
+
/**
|
|
399
|
+
* The user agent indents a quotation by forty physical pixels of its own,
|
|
400
|
+
* and a family that never says otherwise inherits them: the rule of this
|
|
401
|
+
* blockquote stood a whole indent to the right of the paragraph above it
|
|
402
|
+
* in the first capture of this family, and the figure below it did the
|
|
403
|
+
* same. The reset is what makes the quotation's rule land on the text's
|
|
404
|
+
* own margin.
|
|
405
|
+
*/
|
|
406
|
+
margin-inline: 0;
|
|
407
|
+
padding-inline-start: var(--slotted-prose-quote-inset, 1em);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** The same forty pixels, on the element that holds an image. */
|
|
411
|
+
[data-ui='prose'] :where(figure):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
412
|
+
margin-inline: 0;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
[data-ui='prose']
|
|
416
|
+
:where(figcaption, caption):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
417
|
+
color: var(--_muted);
|
|
418
|
+
font-size: var(--slotted-prose-caption-size, 0.875em);
|
|
419
|
+
margin-block-start: calc(var(--_flow) * 0.5);
|
|
420
|
+
text-align: start;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* ── Media ──────────────────────────────────────────────────────────── */
|
|
424
|
+
|
|
425
|
+
[data-ui='prose']
|
|
426
|
+
:where(img, svg, video, canvas):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
427
|
+
block-size: auto;
|
|
428
|
+
border-radius: var(--slotted-prose-media-radius, var(--slotted-radius-md, 6px));
|
|
429
|
+
display: block;
|
|
430
|
+
max-inline-size: 100%;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* ── Code ───────────────────────────────────────────────────────────────
|
|
434
|
+
*
|
|
435
|
+
* The type, the ground and the inset. Highlighting, a copy control, line
|
|
436
|
+
* numbers and a wrap toggle belong to `code-block` in wave 8; a Markdown
|
|
437
|
+
* fence needs none of them to be readable.
|
|
438
|
+
*
|
|
439
|
+
* The ground is a fill and the edge is a border, deliberately: under forced
|
|
440
|
+
* colours the fill is replaced and the border is what still says where the
|
|
441
|
+
* code starts.
|
|
442
|
+
*/
|
|
443
|
+
|
|
444
|
+
[data-ui='prose']
|
|
445
|
+
:where(code, kbd, samp, var):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
446
|
+
font-family: var(--slotted-font-family-mono, ui-monospace, monospace);
|
|
447
|
+
/* The mono face runs large beside the sans at the same size. */
|
|
448
|
+
font-size: var(--slotted-prose-code-size, 0.9em);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
[data-ui='prose'] :where(code):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
452
|
+
background-color: var(--_ground);
|
|
453
|
+
border: var(--slotted-border-width-thin, 1px) solid var(--_rule);
|
|
454
|
+
border-radius: var(--slotted-radius-sm, 4px);
|
|
455
|
+
padding-inline: var(--slotted-prose-code-inset, 0.3em);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
[data-ui='prose'] :where(kbd):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
459
|
+
background-color: var(--slotted-surface-raised, Canvas);
|
|
460
|
+
border: var(--slotted-kbd-border-width, var(--slotted-border-width-thin, 1px)) solid
|
|
461
|
+
var(--slotted-border-strong, ButtonBorder);
|
|
462
|
+
border-radius: var(--slotted-kbd-radius, var(--slotted-radius-sm, 4px));
|
|
463
|
+
font-family: var(--slotted-kbd-font-family, var(--slotted-font-family-mono, monospace));
|
|
464
|
+
font-weight: var(--slotted-kbd-font-weight, var(--slotted-font-weight-medium, 500));
|
|
465
|
+
padding-inline: var(--slotted-prose-code-inset, 0.3em);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
[data-ui='prose'] :where(pre):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
469
|
+
background-color: var(--_ground);
|
|
470
|
+
/**
|
|
471
|
+
* A fence reads left to right in a right-to-left document: the order of
|
|
472
|
+
* the characters is what the code means, and the first RTL capture of this
|
|
473
|
+
* family showed the block aligned to the right with its check marks
|
|
474
|
+
* trailing. The rule is the block's own, so an Arabic sentence around it
|
|
475
|
+
* is untouched.
|
|
476
|
+
*/
|
|
477
|
+
direction: ltr;
|
|
478
|
+
border: var(--slotted-border-width-thin, 1px) solid var(--_rule);
|
|
479
|
+
border-radius: var(--slotted-prose-media-radius, var(--slotted-radius-md, 6px));
|
|
480
|
+
overflow-x: auto;
|
|
481
|
+
padding-block: var(--slotted-space-5, 0.75rem);
|
|
482
|
+
padding-inline: var(--slotted-space-6, 1rem);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/** A fence is one box, not a box of boxes. */
|
|
486
|
+
[data-ui='prose']
|
|
487
|
+
:where(pre)
|
|
488
|
+
> :where(code):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
489
|
+
background-color: transparent;
|
|
490
|
+
border: 0;
|
|
491
|
+
border-radius: 0;
|
|
492
|
+
font-size: inherit;
|
|
493
|
+
padding-inline: 0;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* ── Tables ─────────────────────────────────────────────────────────────
|
|
497
|
+
*
|
|
498
|
+
* The rhythm and the rules. Sorting, sticky headers, column resize and row
|
|
499
|
+
* selection are the `table` family of wave 6.
|
|
500
|
+
*
|
|
501
|
+
* Every separator sits on the row that arrives — `tr + tr` looks backwards —
|
|
502
|
+
* so a row appended to a streaming answer restyles nothing above it.
|
|
503
|
+
*/
|
|
504
|
+
|
|
505
|
+
[data-ui='prose'] :where(table):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
506
|
+
border-collapse: collapse;
|
|
507
|
+
inline-size: 100%;
|
|
508
|
+
text-align: start;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* A table the consumer handed to the `scroll` part takes the width its
|
|
513
|
+
* content asks for and overflows the wrapper, which is the whole point of
|
|
514
|
+
* having wrapped it. Left at `100%` it would instead be squeezed into the
|
|
515
|
+
* column, and the inherited `overflow-wrap: anywhere` would then break
|
|
516
|
+
* "Environment" across two lines rather than let one column be wide — the
|
|
517
|
+
* defect the first capture of this family showed.
|
|
518
|
+
*
|
|
519
|
+
* `min-inline-size` keeps a narrow table filling the column, so wrapping a
|
|
520
|
+
* table that happens to fit costs nothing.
|
|
521
|
+
*/
|
|
522
|
+
[data-ui='prose']
|
|
523
|
+
[data-part='scroll']
|
|
524
|
+
> :where(table):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
525
|
+
inline-size: max-content;
|
|
526
|
+
min-inline-size: 100%;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
[data-ui='prose'] :where(th, td):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
530
|
+
padding-block: var(--slotted-space-4, 0.5rem);
|
|
531
|
+
padding-inline: var(--slotted-space-5, 0.75rem);
|
|
532
|
+
text-align: start;
|
|
533
|
+
vertical-align: baseline;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
[data-ui='prose'] :where(th):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
537
|
+
font-weight: var(--slotted-font-weight-semibold, 600);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
[data-ui='prose']
|
|
541
|
+
:where(thead)
|
|
542
|
+
> :where(tr)
|
|
543
|
+
> :where(th, td):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
544
|
+
border-block-end: var(--slotted-border-width-thin, 1px) solid
|
|
545
|
+
var(--slotted-border-strong, ButtonBorder);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
[data-ui='prose']
|
|
549
|
+
:where(tr + tr)
|
|
550
|
+
> :where(th, td):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
551
|
+
border-block-start: var(--slotted-border-width-thin, 1px) solid var(--_rule);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/* ── Rules and disclosure ───────────────────────────────────────────── */
|
|
555
|
+
|
|
556
|
+
[data-ui='prose'] :where(hr):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
557
|
+
border: 0;
|
|
558
|
+
border-block-start: var(--slotted-divider-thickness, var(--slotted-border-width-thin, 1px))
|
|
559
|
+
solid var(--slotted-divider-color, var(--slotted-border-subtle, ButtonBorder));
|
|
560
|
+
margin-block-start: calc(var(--_flow) * 1.75);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
[data-ui='prose'] :where(summary):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
564
|
+
cursor: pointer;
|
|
565
|
+
font-weight: var(--slotted-font-weight-medium, 500);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
[data-ui='prose']
|
|
569
|
+
:where(summary):not(:where([data-part='exclude'], [data-part='exclude'] *)):focus-visible {
|
|
570
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
571
|
+
outline-offset: var(--slotted-prose-focus-ring-offset, 2px);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/* ── The lead ───────────────────────────────────────────────────────── */
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* The block after the lead takes a wider gap than two ordinary paragraphs
|
|
578
|
+
* do, so the opening reads as its own movement rather than as the first of
|
|
579
|
+
* several. The selector looks backwards, which is what keeps it stable while
|
|
580
|
+
* the rest of the document is still arriving.
|
|
581
|
+
*/
|
|
582
|
+
[data-ui='prose']
|
|
583
|
+
[data-part='lead']
|
|
584
|
+
+ :not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
585
|
+
margin-block-start: calc(var(--_flow) * 1.35);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
[data-ui='prose'] [data-part='lead'] {
|
|
589
|
+
color: var(--_muted);
|
|
590
|
+
font-size: var(--slotted-prose-lead-size, 1.125em);
|
|
591
|
+
line-height: var(--slotted-prose-lead-line-height, var(--slotted-line-height-lg, 1.5));
|
|
592
|
+
text-wrap: pretty;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/* ── The heading anchor ─────────────────────────────────────────────────
|
|
596
|
+
*
|
|
597
|
+
* Hidden with opacity, never with `display: none` or `visibility: hidden`:
|
|
598
|
+
* the anchor stays in the tab sequence and in the accessible tree, so a
|
|
599
|
+
* keyboard reader reaches it and a screen reader announces it. It appears
|
|
600
|
+
* when its heading is hovered *or* when it is itself focus-visible — a
|
|
601
|
+
* control that answered hover alone would be a control a keyboard cannot
|
|
602
|
+
* find.
|
|
603
|
+
*
|
|
604
|
+
* It sits on the heading's own line, after the words, and it holds its box
|
|
605
|
+
* whether it is visible or not — so revealing it moves nothing. Hanging it
|
|
606
|
+
* in a start gutter is the arrangement Primer uses and the first capture of
|
|
607
|
+
* this family showed why it cannot be borrowed: a region flush with its
|
|
608
|
+
* container has no gutter, and the focused anchor was clipped by the edge.
|
|
609
|
+
* A family cannot assume the page indented it.
|
|
610
|
+
*
|
|
611
|
+
* The binding writes a word joiner — U+2060, the zero-width character whose
|
|
612
|
+
* whole purpose is to forbid a break without occupying anything — before the
|
|
613
|
+
* anchor, so the last word of a title and its permalink wrap together. It is
|
|
614
|
+
* the same device `link` uses for its external marker, and for the same
|
|
615
|
+
* reason: a no-break space would carry a full word space with it.
|
|
616
|
+
*/
|
|
617
|
+
|
|
618
|
+
[data-ui='prose'] [data-part='anchor'] {
|
|
619
|
+
align-items: center;
|
|
620
|
+
color: var(--_muted);
|
|
621
|
+
display: inline-flex;
|
|
622
|
+
justify-content: center;
|
|
623
|
+
margin-inline-start: var(--slotted-prose-anchor-gap, 0.35em);
|
|
624
|
+
/* WCAG 2.2 SC 2.5.8: a target is at least 24 by 24 CSS pixels. */
|
|
625
|
+
min-block-size: var(--slotted-target-min, 24px);
|
|
626
|
+
min-inline-size: var(--slotted-target-min, 24px);
|
|
627
|
+
opacity: 0;
|
|
628
|
+
text-decoration-line: none;
|
|
629
|
+
transition: opacity var(--slotted-duration-short) var(--slotted-easing-standard);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
[data-ui='prose']
|
|
633
|
+
:where(h1, h2, h3, h4, h5, h6):hover
|
|
634
|
+
> [data-part='anchor']:not([data-disabled]),
|
|
635
|
+
[data-ui='prose'] [data-part='anchor']:focus-visible {
|
|
636
|
+
opacity: 1;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
[data-ui='prose'] [data-part='anchor']:focus-visible {
|
|
640
|
+
border-radius: var(--slotted-radius-sm, 4px);
|
|
641
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
642
|
+
outline-offset: var(--slotted-prose-focus-ring-offset, 2px);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* ── The scrolling block ────────────────────────────────────────────────
|
|
646
|
+
*
|
|
647
|
+
* A `pre` or a `table` wider than the column has to scroll sideways, and a
|
|
648
|
+
* reader with no pointer must be able to do it: the axe rule
|
|
649
|
+
* `scrollable-region-focusable` is WCAG 2.2 level A. Anything that takes
|
|
650
|
+
* focus then needs a name and a role, or it fails SC 4.1.2 — which is why
|
|
651
|
+
* the binding writes `role="region"`, `tabindex="0"` and a name *only while
|
|
652
|
+
* the block actually overflows*. A tab stop on a block that fits is a tab
|
|
653
|
+
* stop that does nothing.
|
|
654
|
+
*
|
|
655
|
+
* The ring is therefore painted on `[tabindex]` rather than on the part:
|
|
656
|
+
* Adrian Roselli's own trick, where the selector will not match markup that
|
|
657
|
+
* is not operable. He writes `:focus`; this writes `:focus-visible`, because
|
|
658
|
+
* a pointer press inside a `tabindex` box focuses it and a ring drawn then is
|
|
659
|
+
* a ring nobody asked for.
|
|
660
|
+
*/
|
|
661
|
+
|
|
662
|
+
[data-ui='prose'] [data-part='scroll'] {
|
|
663
|
+
max-inline-size: 100%;
|
|
664
|
+
/* The page does not scroll sideways because one table is wide. */
|
|
665
|
+
overflow-x: auto;
|
|
666
|
+
/* A swipe that reaches the end of the table does not then move the page. */
|
|
667
|
+
overscroll-behavior-inline: contain;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
[data-ui='prose'] [data-part='scroll'][tabindex]:focus-visible {
|
|
671
|
+
outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
|
|
672
|
+
outline-offset: var(--slotted-prose-focus-ring-offset, 2px);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/* ── Footnotes ──────────────────────────────────────────────────────── */
|
|
676
|
+
|
|
677
|
+
[data-ui='prose'] [data-part='footnotes'] {
|
|
678
|
+
border-block-start: var(--slotted-border-width-thin, 1px) solid var(--_rule);
|
|
679
|
+
color: var(--_muted);
|
|
680
|
+
font-size: var(--slotted-prose-footnotes-size, 0.875em);
|
|
681
|
+
margin-block-start: calc(var(--_flow) * 2);
|
|
682
|
+
padding-block-start: var(--_flow);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/* ── The opt-out ────────────────────────────────────────────────────────
|
|
686
|
+
*
|
|
687
|
+
* The donut on every rule above stops this family reaching inside, and these
|
|
688
|
+
* four declarations undo what inheritance carried in on its own. Tailwind's
|
|
689
|
+
* `not-prose` and Typeset's `not-typeset` both leave the inherited type in
|
|
690
|
+
* place, so a card inside a `prose-xl` region is set in 20-pixel text.
|
|
691
|
+
* Returning the subtree to control type is what a component placed here
|
|
692
|
+
* expects, and a component that sets its own overrides all four.
|
|
693
|
+
*/
|
|
694
|
+
|
|
695
|
+
[data-ui='prose'] [data-part='exclude'] {
|
|
696
|
+
font-family: var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif);
|
|
697
|
+
font-size: var(--slotted-font-size-md, 0.875rem);
|
|
698
|
+
line-height: var(--slotted-line-height-md, 1.5);
|
|
699
|
+
overflow-wrap: normal;
|
|
700
|
+
text-wrap: wrap;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/* ── Reflow ─────────────────────────────────────────────────────────────
|
|
704
|
+
*
|
|
705
|
+
* A container query cannot restyle its own container — `container-type`
|
|
706
|
+
* makes an element a query container for its descendants — and every rule
|
|
707
|
+
* here targets a descendant, which is why the family needs one element
|
|
708
|
+
* rather than two. The breakpoints are literals because CSS forbids a custom
|
|
709
|
+
* property in a container condition.
|
|
710
|
+
*/
|
|
711
|
+
|
|
712
|
+
@container slotted-prose (max-width: 34rem) {
|
|
713
|
+
/* A 2em heading in a 400-pixel column is six words a line. */
|
|
714
|
+
[data-ui='prose'] :where(h1):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
715
|
+
font-size: var(--slotted-prose-h1-size-narrow, 1.6em);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
[data-ui='prose'] :where(h2):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
719
|
+
font-size: var(--slotted-prose-h2-size-narrow, 1.35em);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
@container slotted-prose (max-width: 22rem) {
|
|
724
|
+
[data-ui='prose']
|
|
725
|
+
:where(blockquote):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
726
|
+
padding-inline-start: calc(var(--slotted-prose-quote-inset, 1em) / 2);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
[data-ui='prose'] :where(ul, ol):not(:where([data-part='exclude'], [data-part='exclude'] *)) {
|
|
730
|
+
padding-inline-start: var(--slotted-prose-list-inset-narrow, 1.15em);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|