@ultimat3/ui 23.0.0 → 25.0.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/CATALOG.md +119 -15
- package/CLAUDE.md +17 -8
- package/README.md +119 -33
- package/package.json +6 -6
- package/src/a11y.ts +27 -13
- package/src/catalog/build-catalog.ts +2 -2
- package/src/components/Accordion.tsx +8 -2
- package/src/components/AppShell.module.scss +105 -4
- package/src/components/AppShell.tsx +48 -10
- package/src/components/BarChart.module.scss +47 -9
- package/src/components/BarChart.tsx +152 -72
- package/src/components/Card.tsx +52 -16
- package/src/components/ChartFrame.module.scss +188 -0
- package/src/components/ChartFrame.tsx +155 -0
- package/src/components/CommandPalette.module.scss +8 -5
- package/src/components/Container.tsx +46 -9
- package/src/components/DataTable.module.scss +190 -0
- package/src/components/DataTable.tsx +161 -51
- package/src/components/Divider.tsx +0 -2
- package/src/components/DonutChart.module.scss +73 -0
- package/src/components/DonutChart.tsx +154 -0
- package/src/components/Dropzone.module.scss +3 -1
- package/src/components/Dropzone.tsx +17 -8
- package/src/components/ErrorState.tsx +39 -12
- package/src/components/FileInput.module.scss +3 -1
- package/src/components/FileInput.tsx +6 -2
- package/src/components/Form.tsx +17 -4
- package/src/components/Gauge.module.scss +48 -0
- package/src/components/Gauge.tsx +67 -0
- package/src/components/Grid.tsx +34 -13
- package/src/components/Image.tsx +2 -2
- package/src/components/InlineBar.module.scss +23 -0
- package/src/components/InlineBar.tsx +31 -0
- package/src/components/LineChart.module.scss +147 -0
- package/src/components/LineChart.tsx +169 -0
- package/src/components/Menu.tsx +9 -5
- package/src/components/Meter.tsx +3 -3
- package/src/components/PageHeader.tsx +5 -2
- package/src/components/Popover.module.scss +17 -20
- package/src/components/Popover.tsx +14 -7
- package/src/components/Section.tsx +45 -26
- package/src/components/Stack.tsx +59 -18
- package/src/components/Table.module.scss +30 -0
- package/src/components/Table.tsx +14 -3
- package/src/components/Tabs.module.scss +6 -0
- package/src/components/Tabs.tsx +2 -2
- package/src/components/Text.tsx +41 -15
- package/src/components/Textarea.tsx +7 -4
- package/src/components/Toaster.tsx +1 -1
- package/src/components/Toolbar.tsx +7 -6
- package/src/components/Tooltip.module.scss +8 -3
- package/src/components/app-shell-view.ts +6 -4
- package/src/components/bar-chart-view.ts +11 -6
- package/src/components/chart-frame-view.ts +219 -0
- package/src/components/chart-ticks-view.ts +173 -0
- package/src/components/data-table-view.ts +68 -0
- package/src/components/donut-chart-view.ts +120 -0
- package/src/components/file-input-view.ts +32 -13
- package/src/components/gauge-view.ts +43 -0
- package/src/components/heading-node.tsx +62 -0
- package/src/components/image-source.ts +3 -3
- package/src/components/line-chart-view.ts +153 -0
- package/src/components/link-target.ts +21 -1
- package/src/components/meter-view.ts +13 -0
- package/src/components/money-view.ts +2 -7
- package/src/components/qr-matrix.ts +16 -23
- package/src/components/relative-time-view.ts +11 -4
- package/src/form/form-binding.ts +32 -14
- package/src/form/use-form.ts +3 -3
- package/src/global.scss +9 -0
- package/src/icons/build-icons.ts +109 -24
- package/src/index.ts +47 -15
- package/src/jsx-probe.ts +5 -4
- package/src/theme/brand-declarations.ts +113 -0
- package/src/theme/brand.ts +104 -96
- package/src/theme/context.ts +1 -1
- package/src/theme/preset-scifi.ts +99 -0
- package/src/theme/preset-shape.ts +18 -0
- package/src/theme/presets.ts +15 -0
- package/src/theme/shadow-value.ts +39 -0
- package/src/theme/theme.ts +36 -11
- package/src/toast/toast-state.ts +0 -0
- package/src/toast/toast-store.ts +1 -1
- package/src/toast/use-toasts.ts +1 -1
- package/src/tokens/_colors.scss +20 -0
- package/src/tokens/_effects.scss +59 -0
- package/src/tokens/_index.scss +2 -0
- package/src/tokens/_mixins.scss +6 -4
- package/src/tokens/_shadow.scss +16 -0
- package/src/tokens/_touch.scss +39 -0
- package/src/tokens/_typography.scss +14 -0
- package/src/tokens/color-tokens.ts +133 -0
- package/src/tokens/colour-vision.ts +106 -0
- package/src/tokens/contrast-pairs.ts +6 -1
- package/src/tokens/contrast.ts +1 -1
- package/src/tokens/theme.scss +2 -0
- package/src/tokens/tokens.ts +41 -87
- package/src/fake-dom.ts +0 -206
- package/src/sass-probe.ts +0 -55
- package/src/theme/inline-script.ts +0 -49
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// The frame every chart stands in: a `<figure>` named by its `<figcaption>`, the plot, a legend
|
|
2
|
+
// that wraps under the plot on a narrow container and stands beside it on a wide one, and a
|
|
3
|
+
// visually-hidden `<table>` of the same numbers — what a screen reader and a reader who cannot
|
|
4
|
+
// tell the colours apart get instead of the picture. Server-rendered HTML: no island, no JS.
|
|
5
|
+
|
|
6
|
+
import type { JSX } from 'solid-js';
|
|
7
|
+
import { cx } from '../cx';
|
|
8
|
+
import styles from './ChartFrame.module.scss';
|
|
9
|
+
import type { ChartTable, SeriesStyle } from './chart-frame-view';
|
|
10
|
+
import { markerPath } from './chart-frame-view';
|
|
11
|
+
|
|
12
|
+
export interface ChartLegendItem {
|
|
13
|
+
/** The series' name, already translated. */
|
|
14
|
+
readonly label: string;
|
|
15
|
+
/** A figure beside the name — a donut segment's value. Already formatted. */
|
|
16
|
+
readonly value?: string | undefined;
|
|
17
|
+
/** A second, quieter figure — the segment's share. Already formatted. */
|
|
18
|
+
readonly detail?: string | undefined;
|
|
19
|
+
/** The key: the same colour slot, dash and marker the plot draws this series with. */
|
|
20
|
+
readonly swatch: JSX.Element;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** One focusable point over the plot: where it sits, and what it reads. */
|
|
24
|
+
export interface ChartHit {
|
|
25
|
+
/** CSS percentages of the plot box (`cssPercent`). */
|
|
26
|
+
readonly x: string;
|
|
27
|
+
readonly y: string;
|
|
28
|
+
/** The accessible name AND the visible readout — one string, so the two never disagree. */
|
|
29
|
+
readonly label: string;
|
|
30
|
+
/** Which way the readout opens, so a point at the edge keeps its readout inside the box. */
|
|
31
|
+
readonly edge: 'start' | 'middle' | 'end';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ChartFrameProps {
|
|
35
|
+
/** The chart's name, already translated: the `<figcaption>`, and the data table's caption. */
|
|
36
|
+
label: string;
|
|
37
|
+
/** The plot — the chart's own svg and HTML axis labels. */
|
|
38
|
+
children: JSX.Element;
|
|
39
|
+
/**
|
|
40
|
+
* Show the caption above the plot. Off by default: a chart usually sits under a Card's own
|
|
41
|
+
* heading, and the caption names the figure for assistive tech either way.
|
|
42
|
+
*/
|
|
43
|
+
showCaption?: boolean | undefined;
|
|
44
|
+
/** One entry per series. Omitted or empty renders no legend. */
|
|
45
|
+
legend?: readonly ChartLegendItem[] | undefined;
|
|
46
|
+
/** The same data as rows, rendered visually hidden (`chartTable`). */
|
|
47
|
+
table?: ChartTable | undefined;
|
|
48
|
+
/** Lands on the root `<figure>`. */
|
|
49
|
+
class?: string | undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function ChartFrame(props: ChartFrameProps): JSX.Element {
|
|
53
|
+
const legend = (): readonly ChartLegendItem[] => props.legend ?? [];
|
|
54
|
+
return (
|
|
55
|
+
<figure class={cx(styles['frame'], props.class)}>
|
|
56
|
+
<figcaption class={props.showCaption === true ? styles['caption'] : styles['hidden']}>
|
|
57
|
+
{props.label}
|
|
58
|
+
</figcaption>
|
|
59
|
+
<div class={cx(styles['layout'], legend().length > 0 && styles['withLegend'])}>
|
|
60
|
+
<div class={styles['plot']}>{props.children}</div>
|
|
61
|
+
{legend().length === 0 ? null : (
|
|
62
|
+
<ul class={styles['legend']}>
|
|
63
|
+
{legend().map((item) => (
|
|
64
|
+
<li class={styles['item']}>
|
|
65
|
+
<span class={styles['swatch']} aria-hidden="true">
|
|
66
|
+
{item.swatch}
|
|
67
|
+
</span>
|
|
68
|
+
<span class={styles['name']} data-legend="name">
|
|
69
|
+
{item.label}
|
|
70
|
+
</span>
|
|
71
|
+
{item.value === undefined ? null : (
|
|
72
|
+
<span class={styles['value']}>{item.value}</span>
|
|
73
|
+
)}
|
|
74
|
+
{item.detail === undefined ? null : (
|
|
75
|
+
<span class={styles['detail']}>{item.detail}</span>
|
|
76
|
+
)}
|
|
77
|
+
</li>
|
|
78
|
+
))}
|
|
79
|
+
</ul>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
{props.table === undefined ? null : chartTableNode(props.table)}
|
|
83
|
+
</figure>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function chartTableNode(table: ChartTable): JSX.Element {
|
|
88
|
+
const [corner = '', ...heads] = table.head;
|
|
89
|
+
return (
|
|
90
|
+
<table class={styles['hidden']}>
|
|
91
|
+
<caption>{table.caption}</caption>
|
|
92
|
+
<thead>
|
|
93
|
+
<tr>
|
|
94
|
+
{/* An empty corner is a <td>: a header cell with no text names nothing. */}
|
|
95
|
+
{corner === '' ? <td /> : <th scope="col">{corner}</th>}
|
|
96
|
+
{heads.map((head) => (
|
|
97
|
+
<th scope="col">{head}</th>
|
|
98
|
+
))}
|
|
99
|
+
</tr>
|
|
100
|
+
</thead>
|
|
101
|
+
<tbody>
|
|
102
|
+
{table.rows.map((row) => (
|
|
103
|
+
<tr>
|
|
104
|
+
<th scope="row">{row.key}</th>
|
|
105
|
+
{row.cells.map((cell) => (
|
|
106
|
+
<td>{cell}</td>
|
|
107
|
+
))}
|
|
108
|
+
</tr>
|
|
109
|
+
))}
|
|
110
|
+
</tbody>
|
|
111
|
+
</table>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** A legend key for a line series: its dash, its marker, its colour slot. */
|
|
116
|
+
export function seriesSwatch(style: SeriesStyle): JSX.Element {
|
|
117
|
+
return (
|
|
118
|
+
<svg class={styles['key']} viewBox="0 0 24 12" data-series={style.slot} aria-hidden="true">
|
|
119
|
+
<path class={styles['keyLine']} d="M1,6 L23,6" stroke-dasharray={style.dash} />
|
|
120
|
+
<path class={styles['keyMarker']} d={markerPath(style.marker, 12, 6, 4)} />
|
|
121
|
+
</svg>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The keyboard layer: one focusable point per datum, over the plot, each reading its own value on
|
|
127
|
+
* `:hover` and `:focus-visible` — CSS only, so a chart that asks for it still ships no JS. DOM
|
|
128
|
+
* order is the tab order, and the caller passes them in reading order.
|
|
129
|
+
*/
|
|
130
|
+
export function chartHits(hits: readonly ChartHit[]): JSX.Element {
|
|
131
|
+
return (
|
|
132
|
+
<div class={styles['hits']}>
|
|
133
|
+
{hits.map((hit) => (
|
|
134
|
+
// A datum is an image of one value: `img` is the role that names it and nothing more.
|
|
135
|
+
<span
|
|
136
|
+
class={styles['hit']}
|
|
137
|
+
role="img"
|
|
138
|
+
tabindex={0}
|
|
139
|
+
aria-label={hit.label}
|
|
140
|
+
data-hit="true"
|
|
141
|
+
data-edge={hit.edge}
|
|
142
|
+
style={{ '--x': hit.x, '--y': hit.y }}
|
|
143
|
+
>
|
|
144
|
+
{/* A zero-width anchor that centres the bubble over the point with flex, not a
|
|
145
|
+
percentage translate: that one lands a whole width off under `dir="rtl"`. */}
|
|
146
|
+
<span class={styles['readout']} aria-hidden="true">
|
|
147
|
+
<span class={styles['bubble']} data-readout="true">
|
|
148
|
+
{hit.label}
|
|
149
|
+
</span>
|
|
150
|
+
</span>
|
|
151
|
+
</span>
|
|
152
|
+
))}
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
.palette {
|
|
4
4
|
position: fixed;
|
|
5
5
|
inset-block-start: 12vh;
|
|
6
|
-
inset-
|
|
6
|
+
// Centred by auto margins between two zero insets, never by a 50% inset and a -50% translate:
|
|
7
|
+
// the inset is logical and the translate physical, so under `dir="rtl"` that pair puts the
|
|
8
|
+
// palette a whole width off-centre (`rtl-sheets.test.ts`).
|
|
9
|
+
inset-inline: 0;
|
|
7
10
|
inline-size: min(32rem, calc(100vw - #{t.space(8)}));
|
|
8
11
|
max-block-size: 60vh;
|
|
9
|
-
margin: 0;
|
|
12
|
+
margin-block: 0;
|
|
13
|
+
margin-inline: auto;
|
|
10
14
|
padding: 0;
|
|
11
15
|
border: 1px solid t.role('line');
|
|
12
16
|
border-radius: t.radius(lg);
|
|
@@ -20,8 +24,7 @@
|
|
|
20
24
|
overflow: hidden;
|
|
21
25
|
z-index: t.z(dialog);
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
transform: translateX(-50%) translateY(calc(-1 * #{t.space(1)})) scale(0.98);
|
|
27
|
+
transform: translateY(calc(-1 * #{t.space(1)})) scale(0.98);
|
|
25
28
|
opacity: 0;
|
|
26
29
|
visibility: hidden;
|
|
27
30
|
transition:
|
|
@@ -30,7 +33,7 @@
|
|
|
30
33
|
visibility t.duration(fast) t.easing(out);
|
|
31
34
|
|
|
32
35
|
&[open] {
|
|
33
|
-
transform:
|
|
36
|
+
transform: translateY(0) scale(1);
|
|
34
37
|
opacity: 1;
|
|
35
38
|
visibility: visible;
|
|
36
39
|
transition-duration: t.duration(base);
|
|
@@ -17,13 +17,50 @@ export interface ContainerProps {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function Container(props: ContainerProps): JSX.Element {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
20
|
+
const cls = (): string =>
|
|
21
|
+
cx(styles['container'], styles[`size-${props.size ?? 'lg'}`], props.class);
|
|
22
|
+
const style = (): JSX.CSSProperties => ({
|
|
23
|
+
'--container-gutter': `var(--space-${props.gutter ?? 4})`,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// One intrinsic per case, never `const Tag = props.as; <Tag>`: the island build compiles a
|
|
27
|
+
// capitalised tag to `createComponent(Tag)` and calls the string (#488, `intrinsic-root.test.ts`).
|
|
28
|
+
switch (props.as ?? 'div') {
|
|
29
|
+
case 'main':
|
|
30
|
+
return (
|
|
31
|
+
<main class={cls()} style={style()}>
|
|
32
|
+
{props.children}
|
|
33
|
+
</main>
|
|
34
|
+
);
|
|
35
|
+
case 'section':
|
|
36
|
+
return (
|
|
37
|
+
<section class={cls()} style={style()}>
|
|
38
|
+
{props.children}
|
|
39
|
+
</section>
|
|
40
|
+
);
|
|
41
|
+
case 'article':
|
|
42
|
+
return (
|
|
43
|
+
<article class={cls()} style={style()}>
|
|
44
|
+
{props.children}
|
|
45
|
+
</article>
|
|
46
|
+
);
|
|
47
|
+
case 'header':
|
|
48
|
+
return (
|
|
49
|
+
<header class={cls()} style={style()}>
|
|
50
|
+
{props.children}
|
|
51
|
+
</header>
|
|
52
|
+
);
|
|
53
|
+
case 'footer':
|
|
54
|
+
return (
|
|
55
|
+
<footer class={cls()} style={style()}>
|
|
56
|
+
{props.children}
|
|
57
|
+
</footer>
|
|
58
|
+
);
|
|
59
|
+
case 'div':
|
|
60
|
+
return (
|
|
61
|
+
<div class={cls()} style={style()}>
|
|
62
|
+
{props.children}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
29
66
|
}
|
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
@use 'sass:map';
|
|
1
2
|
@use '../tokens' as t;
|
|
2
3
|
|
|
4
|
+
// The table's OWN width decides its layout, never the viewport's: the same table is a card list
|
|
5
|
+
// in a sidebar and a full table in a wide main column. The rungs are the one breakpoint ladder,
|
|
6
|
+
// read as container widths.
|
|
7
|
+
$sm: map.get(t.$breakpoints, 'sm');
|
|
8
|
+
$md: map.get(t.$breakpoints, 'md');
|
|
9
|
+
$lg: map.get(t.$breakpoints, 'lg');
|
|
10
|
+
|
|
3
11
|
.wrap {
|
|
12
|
+
@include t.container(datatable);
|
|
4
13
|
display: flex;
|
|
5
14
|
flex-direction: column;
|
|
6
15
|
gap: t.space(3);
|
|
16
|
+
// Inline-size containment sizes the box without its content, so it must take its width from
|
|
17
|
+
// the parent: a shrink-to-fit container would otherwise collapse to nothing.
|
|
18
|
+
inline-size: 100%;
|
|
19
|
+
min-inline-size: 0;
|
|
7
20
|
}
|
|
8
21
|
|
|
9
22
|
// Numbers align to the trailing edge in both writing directions.
|
|
@@ -40,3 +53,180 @@
|
|
|
40
53
|
opacity: 0.6;
|
|
41
54
|
transition: opacity t.duration(fast) t.easing(out);
|
|
42
55
|
}
|
|
56
|
+
|
|
57
|
+
// --- What hides at which width -------------------------------------------------------------------
|
|
58
|
+
// A lower-priority cell hides while the table is narrow, and the row's disclosure shows exactly
|
|
59
|
+
// the entries whose cells are hidden: at every width each value is on screen once.
|
|
60
|
+
|
|
61
|
+
.cell > [data-label],
|
|
62
|
+
.more,
|
|
63
|
+
.entry {
|
|
64
|
+
display: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@container datatable (width < #{$lg}) {
|
|
68
|
+
.wrap .cell[data-priority='3'],
|
|
69
|
+
.wrap .heading[data-priority='3'] {
|
|
70
|
+
display: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.wrap .more[data-more='lg'] {
|
|
74
|
+
display: table-cell;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.wrap .entry[data-priority='3'] {
|
|
78
|
+
display: grid;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@container datatable (width < #{$md}) {
|
|
83
|
+
.wrap .cell[data-priority],
|
|
84
|
+
.wrap .heading[data-priority] {
|
|
85
|
+
display: none;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.wrap .more {
|
|
89
|
+
display: table-cell;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.wrap .entry {
|
|
93
|
+
display: grid;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.disclosure {
|
|
98
|
+
white-space: normal;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.summary {
|
|
102
|
+
@include t.focus-ring;
|
|
103
|
+
border-radius: t.radius(sm);
|
|
104
|
+
color: t.role('accent');
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.entries {
|
|
109
|
+
display: grid;
|
|
110
|
+
gap: t.space(1);
|
|
111
|
+
margin: 0;
|
|
112
|
+
margin-block-start: t.space(2);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.entry {
|
|
116
|
+
grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
|
|
117
|
+
gap: t.space(3);
|
|
118
|
+
|
|
119
|
+
:global(dt) {
|
|
120
|
+
color: t.role('fg-muted');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
:global(dd) {
|
|
124
|
+
margin: 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// --- Cards ---------------------------------------------------------------------------------------
|
|
129
|
+
// Below `sm` a row is a card and each cell a labelled line. Every element still carries its table
|
|
130
|
+
// role (the markup declares them), because WebKit drops table semantics on a `display` change. The
|
|
131
|
+
// header row stays in the accessibility tree — it is what names each cell — and shows only the
|
|
132
|
+
// headers that are sort controls, as a row of chips above the cards.
|
|
133
|
+
|
|
134
|
+
@container datatable (width < #{$sm}) {
|
|
135
|
+
.wrap[data-narrow='cards'] {
|
|
136
|
+
:global(table),
|
|
137
|
+
:global(caption),
|
|
138
|
+
.body {
|
|
139
|
+
display: block;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.head {
|
|
143
|
+
display: block;
|
|
144
|
+
border-block-end: 1px solid t.role('line');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.head:not([data-sortable]) {
|
|
148
|
+
@include t.visually-hidden;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.head :global(tr) {
|
|
152
|
+
display: flex;
|
|
153
|
+
flex-wrap: wrap;
|
|
154
|
+
gap: t.space(2);
|
|
155
|
+
padding-block: t.space(3);
|
|
156
|
+
padding-inline: t.space(4);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.heading {
|
|
160
|
+
display: block;
|
|
161
|
+
position: static;
|
|
162
|
+
padding: 0;
|
|
163
|
+
border: 0;
|
|
164
|
+
background: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.heading:not([data-sort-control]) {
|
|
168
|
+
@include t.visually-hidden;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.heading[data-sort-control] {
|
|
172
|
+
padding-block: t.space(1);
|
|
173
|
+
padding-inline: t.space(3);
|
|
174
|
+
border: 1px solid t.role('line');
|
|
175
|
+
border-radius: t.radius(pill);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.row {
|
|
179
|
+
display: grid;
|
|
180
|
+
gap: t.space(2);
|
|
181
|
+
padding-block: t.space(3);
|
|
182
|
+
padding-inline: t.space(4);
|
|
183
|
+
border-block-end: 1px solid t.role('line');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.body .row:last-child {
|
|
187
|
+
border-block-end: 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// `:first-child` matches the specificity the sticky-column rule pins it with, so a card is
|
|
191
|
+
// never a sticky cell.
|
|
192
|
+
.cell,
|
|
193
|
+
.cell:first-child {
|
|
194
|
+
display: grid;
|
|
195
|
+
grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
|
|
196
|
+
align-items: baseline;
|
|
197
|
+
gap: t.space(3);
|
|
198
|
+
position: static;
|
|
199
|
+
padding: 0;
|
|
200
|
+
border: 0;
|
|
201
|
+
background: none;
|
|
202
|
+
white-space: normal;
|
|
203
|
+
text-align: start;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.cell.numeric {
|
|
207
|
+
text-align: end;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.cell > [data-label] {
|
|
211
|
+
display: block;
|
|
212
|
+
text-align: start;
|
|
213
|
+
color: t.role('fg-muted');
|
|
214
|
+
font-size: t.text(xs);
|
|
215
|
+
font-weight: t.weight(medium);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Every value is a line of the card already: the disclosure has nothing left to hold, and no
|
|
219
|
+
// priority hides anything.
|
|
220
|
+
.more {
|
|
221
|
+
display: none;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.cell[data-priority] {
|
|
225
|
+
display: grid;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.heading[data-priority] {
|
|
229
|
+
display: block;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|