@tensor_1/tokens 2.47.0 → 2.49.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/build/css/components.css +1 -1
- package/build/css/styles.css +1 -1
- package/build/css/tokens.css +4 -0
- package/build/ts/tokens.d.ts +1 -1
- package/build/ts/tokens.js +8 -0
- package/package.json +1 -1
- package/styles/components/alert.css +6 -4
- package/styles/components/app-shell.css +40 -0
- package/styles/components/arrival.css +7 -1
- package/styles/components/attachment.css +4 -1
- package/styles/components/audit-log.css +29 -0
- package/styles/components/breadcrumbs.css +2 -0
- package/styles/components/busy-region.css +10 -2
- package/styles/components/button.css +7 -0
- package/styles/components/card.css +22 -0
- package/styles/components/change-highlight.css +58 -0
- package/styles/components/checkbox.css +18 -0
- package/styles/components/code-display.css +43 -0
- package/styles/components/combobox.css +4 -1
- package/styles/components/command-palette.css +16 -2
- package/styles/components/contact-card.css +17 -6
- package/styles/components/count.css +23 -0
- package/styles/components/data-workspace.css +40 -0
- package/styles/components/date-picker.css +14 -1
- package/styles/components/detail-layout.css +9 -0
- package/styles/components/diff-viewer.css +5 -0
- package/styles/components/display-preferences.css +38 -0
- package/styles/components/drag.css +63 -0
- package/styles/components/drawer.css +6 -5
- package/styles/components/editorial-home.css +24 -2
- package/styles/components/flow.css +5 -0
- package/styles/components/heartbeat.css +8 -0
- package/styles/components/icon.css +14 -0
- package/styles/components/inline-edit.css +19 -3
- package/styles/components/input.css +62 -0
- package/styles/components/kanban.css +10 -1
- package/styles/components/kbd.css +25 -0
- package/styles/components/lightbox.css +13 -0
- package/styles/components/list-motion.css +27 -0
- package/styles/components/list.css +2 -0
- package/styles/components/match-review.css +40 -0
- package/styles/components/media.css +4 -2
- package/styles/components/metric-input.css +3 -1
- package/styles/components/modal.css +10 -5
- package/styles/components/page-header.css +12 -0
- package/styles/components/pagination.css +2 -0
- package/styles/components/peek.css +6 -5
- package/styles/components/popover.css +14 -10
- package/styles/components/print-layout.css +177 -0
- package/styles/components/progress.css +7 -0
- package/styles/components/qr-code.css +30 -0
- package/styles/components/qr-scanner.css +61 -0
- package/styles/components/relationship-graph.css +71 -15
- package/styles/components/rule-builder.css +2 -1
- package/styles/components/save-status.css +5 -0
- package/styles/components/scheduling-calendar.css +82 -1
- package/styles/components/search-suggest.css +8 -6
- package/styles/components/segmented.css +31 -1
- package/styles/components/select.css +20 -2
- package/styles/components/sidebar.css +51 -1
- package/styles/components/sortable-list.css +42 -0
- package/styles/components/spinner.css +5 -0
- package/styles/components/stat-tile.css +36 -12
- package/styles/components/states.css +50 -0
- package/styles/components/status-pill.css +28 -6
- package/styles/components/steps.css +2 -0
- package/styles/components/table-toolbar.css +1 -1
- package/styles/components/table.css +48 -28
- package/styles/components/tabs.css +32 -3
- package/styles/components/tag-input.css +21 -0
- package/styles/components/textarea.css +3 -0
- package/styles/components/toast.css +23 -8
- package/styles/components/tooltip.css +7 -3
- package/styles/components/tree.css +3 -1
- package/styles/components/view-menu.css +3 -0
- package/styles/motion.css +23 -4
- package/tokens/base/effect.json +6 -0
|
@@ -16,9 +16,14 @@
|
|
|
16
16
|
}
|
|
17
17
|
.uix-table th[data-sort] { cursor: pointer; user-select: none; }
|
|
18
18
|
.uix-table th[data-sort]:hover { color: var(--uix-text); }
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
.uix-table th[
|
|
19
|
+
/* sort arrows: Lucide arrow-up-down / arrow-up / arrow-down through a mask in currentColor, so they
|
|
20
|
+
match the icon set and follow the theme (HAR-1576; scripts/glyph-mask.mjs prints the urls) */
|
|
21
|
+
.uix-table th[data-sort]::after {
|
|
22
|
+
content: ""; display: inline-block; width: 1em; height: 1em; margin-inline-start: var(--uix-space-1); vertical-align: -.15em;
|
|
23
|
+
background: currentColor; opacity: .35; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21 16-4 4-4-4'/%3E%3Cpath d='M17 20V4'/%3E%3Cpath d='m3 8 4-4 4 4'/%3E%3Cpath d='M7 4v16'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21 16-4 4-4-4'/%3E%3Cpath d='M17 20V4'/%3E%3Cpath d='m3 8 4-4 4 4'/%3E%3Cpath d='M7 4v16'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
24
|
+
}
|
|
25
|
+
.uix-table th[aria-sort="ascending"]::after { opacity: 1; color: var(--uix-accent); -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12 7-7 7 7'/%3E%3Cpath d='M12 19V5'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12 7-7 7 7'/%3E%3Cpath d='M12 19V5'/%3E%3C/svg%3E"); }
|
|
26
|
+
.uix-table th[aria-sort="descending"]::after { opacity: 1; color: var(--uix-accent); -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14'/%3E%3Cpath d='m19 12-7 7-7-7'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14'/%3E%3Cpath d='m19 12-7 7-7-7'/%3E%3C/svg%3E"); }
|
|
22
27
|
/* multi-column sort ordinal (⇧+click adds a secondary key) */
|
|
23
28
|
.uix-table th[data-sort-order]::before { content: attr(data-sort-order); display: inline-block; margin-right: var(--uix-space-1); font-size: var(--uix-text-eyebrow); font-variant-numeric: tabular-nums; color: var(--uix-accent); }
|
|
24
29
|
|
|
@@ -32,14 +37,19 @@
|
|
|
32
37
|
.uix-th__sortbtn::before { content: ""; position: absolute; inset: 0; }
|
|
33
38
|
|
|
34
39
|
.uix-table td { padding: 0 var(--uix-space-4); height: var(--uix-row-h); border-bottom: 1px solid var(--uix-border); color: var(--uix-text); vertical-align: middle; }
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
/* The row's current state is one value, --uix-row-bg: the row paints it, and a frozen cell paints
|
|
41
|
+
it too (below), so a frozen cell can never keep the zebra stripe on a selected row (operator
|
|
42
|
+
review 2026-10-23). Every state rule has the same specificity (:where), so the order decides:
|
|
43
|
+
hover > selected > pinned > zebra > plain. --uix-row-bg is a registered colour (@property at
|
|
44
|
+
the end of this file), so the row eases the value itself and its frozen cells ease with it.
|
|
45
|
+
[data-selected] carries row-selection styling; aria-selected is invalid on a native
|
|
40
46
|
table row — AT state comes from the row checkbox (UIX-A11Y-2). */
|
|
41
|
-
.uix-table tbody tr
|
|
42
|
-
.uix-table tbody tr:
|
|
47
|
+
.uix-table tbody tr { --uix-row-bg: var(--uix-row); background: var(--uix-row-bg); transition: --uix-row-bg var(--uix-dur-fast) var(--uix-ease-out); }
|
|
48
|
+
.uix-table tbody tr:where(:nth-child(even)) { --uix-row-bg: var(--uix-row-alt); }
|
|
49
|
+
.uix-table--no-zebra tbody tr:where(:nth-child(even)) { --uix-row-bg: var(--uix-row); }
|
|
50
|
+
.uix-table tbody tr:where([data-pinned]) { --uix-row-bg: var(--uix-row-pinned-bg); }
|
|
51
|
+
.uix-table tbody tr:where([data-selected]) { --uix-row-bg: var(--uix-row-selected-bg); }
|
|
52
|
+
@media (hover: hover) { .uix-table tbody tr:where(:hover) { --uix-row-bg: var(--uix-bg-hover); } }
|
|
43
53
|
/* Row links stay calm on hover/focus: the row tint is the affordance, so neither the base
|
|
44
54
|
layer's `a:hover` underline nor link.css reaches them. `:not(.uix-btn)` keeps buttons
|
|
45
55
|
styled; the global :focus-visible ring still marks keyboard focus. */
|
|
@@ -67,22 +77,21 @@
|
|
|
67
77
|
.uix-table--pinned-col th:first-child, .uix-table--pinned-col td:first-child { position: sticky; left: 0; z-index: var(--uix-z-raised); box-shadow: 1px 0 0 var(--uix-border); }
|
|
68
78
|
.uix-table--pinned-actions th:last-child, .uix-table--pinned-actions td:last-child { position: sticky; right: 0; z-index: var(--uix-z-raised); box-shadow: -1px 0 0 var(--uix-border); }
|
|
69
79
|
.uix-table--pinned-col thead th:first-child, .uix-table--pinned-actions thead th:last-child { z-index: var(--uix-z-sticky); background: var(--uix-surface); }
|
|
80
|
+
/* The dividers mark where rows scroll under a frozen column: a React TableWrap that measured no
|
|
81
|
+
sideways overflow (data-overflow="false") draws none. Without the measurement they stay. */
|
|
82
|
+
.uix-table-wrap[data-overflow="false"] :is(.uix-table--pinned-col :is(th, td):first-child, .uix-table--pinned-actions :is(th, td):last-child) { box-shadow: none; }
|
|
70
83
|
/* A pinned cell paints its row's state itself and must be opaque, or the cells scrolling
|
|
71
|
-
under it show through. The dark
|
|
72
|
-
over the surface
|
|
73
|
-
.uix-table--pinned-col tbody tr td:first-child, .uix-table--pinned-actions tbody tr td:last-child { background: var(--uix-row)
|
|
74
|
-
.uix-table--pinned-col tbody tr:nth-child(even) td:first-child, .uix-table--pinned-actions tbody tr:nth-child(even) td:last-child { background: var(--uix-row-alt); }
|
|
75
|
-
/* same specificity as the even-row rule above and the state rules below, so the order decides:
|
|
76
|
-
an :nth-child(even) here outranked hover, pinned and selected on every even row */
|
|
77
|
-
.uix-table--pinned-col.uix-table--no-zebra tbody tr td:first-child,
|
|
78
|
-
.uix-table--pinned-actions.uix-table--no-zebra tbody tr td:last-child { background: var(--uix-row); }
|
|
79
|
-
.uix-table--pinned-col tbody tr[data-pinned] td:first-child, .uix-table--pinned-actions tbody tr[data-pinned] td:last-child { background: linear-gradient(var(--uix-row-pinned-bg), var(--uix-row-pinned-bg)) var(--uix-surface); }
|
|
80
|
-
.uix-table--pinned-col tbody tr[data-selected] td:first-child, .uix-table--pinned-actions tbody tr[data-selected] td:last-child { background: linear-gradient(var(--uix-row-selected-bg), var(--uix-row-selected-bg)) var(--uix-surface); }
|
|
81
|
-
.uix-table--pinned-col tbody tr:hover td:first-child, .uix-table--pinned-actions tbody tr:hover td:last-child { background: var(--uix-bg-hover); }
|
|
84
|
+
under it show through. The dark tints are translucent, so the row's --uix-row-bg is layered
|
|
85
|
+
over the surface rather than painted bare. */
|
|
86
|
+
.uix-table--pinned-col tbody tr td:first-child, .uix-table--pinned-actions tbody tr td:last-child { background: linear-gradient(var(--uix-row-bg), var(--uix-row-bg)), var(--uix-surface); }
|
|
82
87
|
/* an expanded row's full-width detail cell is both first and last child: keep its own tint */
|
|
83
88
|
.uix-table--pinned-col tbody tr.uix-table__detail > td, .uix-table--pinned-actions tbody tr.uix-table__detail > td { background: var(--uix-bg-subtle); }
|
|
84
89
|
@media (prefers-reduced-motion: reduce) {
|
|
85
|
-
.uix-table tbody tr
|
|
90
|
+
.uix-table tbody tr { transition: none; }
|
|
91
|
+
}
|
|
92
|
+
/* forced colours keep a gradient image: a frozen cell is plain Canvas there, like its row */
|
|
93
|
+
@media (forced-colors: active) {
|
|
94
|
+
.uix-table--pinned-col tbody tr td:first-child, .uix-table--pinned-actions tbody tr td:last-child { background: Canvas; }
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
/* ── column sizing + cell behaviour (a shared vocabulary, not per-app px) ──────── */
|
|
@@ -102,6 +111,8 @@
|
|
|
102
111
|
/* ── cell content vocabulary — a fixed set of renderers keeps tables consistent ── */
|
|
103
112
|
.uix-cell-strong { font-weight: 600; }
|
|
104
113
|
.uix-cell-sub { display: block; font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
|
|
114
|
+
/* read-back codes, references and times: digits line up down the column (HAR-1815) */
|
|
115
|
+
.uix-cell-sub--tabular { font-variant-numeric: tabular-nums; }
|
|
105
116
|
.uix-cell-mono { font-family: var(--uix-font-mono); font-size: var(--uix-text-meta); }
|
|
106
117
|
|
|
107
118
|
/* ── id cell — the canonical row click-through (row → peek, ↗ → full record) ───── */
|
|
@@ -127,21 +138,26 @@
|
|
|
127
138
|
.uix-table tbody tr:focus-within .uix-id-cell__arrow { opacity: 1; }
|
|
128
139
|
|
|
129
140
|
/* ── selection column + select-all ────────────────────────────────────────────── */
|
|
130
|
-
|
|
141
|
+
/* --uix-selcol-w: the selection column's width, for a column frozen right after it. */
|
|
142
|
+
.uix-table { --uix-selcol-w: 44px; }
|
|
143
|
+
.uix-table th.uix-table__selcol, .uix-table td.uix-table__selcell { width: var(--uix-selcol-w); padding-left: var(--uix-space-3); padding-right: 0; text-align: center; }
|
|
131
144
|
|
|
132
145
|
/* contextual bulk-action bar — appears when >=1 row is selected */
|
|
146
|
+
/* It wraps: on a phone the actions take a second row rather than run off the bar's edge, and
|
|
147
|
+
the count stays on one line (operator review 2026-10-23). */
|
|
133
148
|
.uix-bulkbar {
|
|
134
|
-
display: flex; align-items: center; gap: var(--uix-space-3);
|
|
149
|
+
display: flex; flex-wrap: wrap; align-items: center; gap: var(--uix-space-2) var(--uix-space-3); min-width: 0;
|
|
135
150
|
padding: var(--uix-space-2) var(--uix-space-3); margin-bottom: var(--uix-space-3);
|
|
136
151
|
border: 1px solid var(--uix-accent); border-radius: var(--uix-radius-md);
|
|
137
152
|
background: var(--uix-brand-muted); color: var(--uix-text);
|
|
138
|
-
|
|
153
|
+
/* enters from @starting-style on a transition (interruptible), not a keyframe (HAR-2578) */
|
|
154
|
+
transition: opacity var(--uix-dur) var(--uix-ease-out), transform var(--uix-dur) var(--uix-ease-out);
|
|
139
155
|
}
|
|
156
|
+
@starting-style { .uix-bulkbar:not([hidden]) { opacity: 0; transform: translateY(calc(-1 * var(--uix-lift))); } }
|
|
140
157
|
.uix-bulkbar[hidden] { display: none; }
|
|
141
|
-
|
|
158
|
+
/* --uix-accent-text, not --uix-accent: the plain accent on the accent tint is 3.4:1 in dark (HAR-1388 specimen) */
|
|
159
|
+
.uix-bulkbar__count { font-size: var(--uix-text-meta); font-weight: 600; color: var(--uix-accent-text); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
142
160
|
.uix-bulkbar__spacer { flex: 1; }
|
|
143
|
-
@keyframes uix-bulkbar-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
|
|
144
|
-
@media (prefers-reduced-motion: reduce) { .uix-bulkbar { animation: none; } }
|
|
145
161
|
|
|
146
162
|
/* ── row hover actions + kebab ─────────────────────────────────────────────────── */
|
|
147
163
|
.uix-table__actions { width: 84px; text-align: right; white-space: nowrap; }
|
|
@@ -214,3 +230,7 @@
|
|
|
214
230
|
.uix-compare__head > div + div, .uix-compare__cell + .uix-compare__cell { border-left: 1px solid var(--uix-border); }
|
|
215
231
|
.uix-compare__attr { grid-column: 1 / -1; background: var(--uix-bg-subtle); padding: var(--uix-space-1) var(--uix-space-3); border-top: 1px solid var(--uix-border); font-size: var(--uix-text-eyebrow); letter-spacing: var(--uix-tracking-eyebrow); text-transform: uppercase; color: var(--uix-text-muted); }
|
|
216
232
|
}
|
|
233
|
+
|
|
234
|
+
/* The row's state colour (above) is registered so it can be eased: the row and its frozen cells,
|
|
235
|
+
which paint the same value, change together. Outside the layer: @property is global. */
|
|
236
|
+
@property --uix-row-bg { syntax: '<color>'; inherits: true; initial-value: transparent; }
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/* uix tabs — line / enclosed / pill. Depends only on --uix-* tokens. */
|
|
2
2
|
@layer uix.components {
|
|
3
|
-
|
|
3
|
+
/* tabs that do not fit wrap onto another row (overflow="wrap", the default; HAR-1754) rather
|
|
4
|
+
than squeezing every label; no tab is wider than its row, so one long label wraps inside it */
|
|
5
|
+
.uix-tabs { display: flex; flex-wrap: wrap; gap: var(--uix-space-1); }
|
|
4
6
|
.uix-tab {
|
|
5
7
|
border: 0; background: transparent; font: inherit; font-size: var(--uix-text-body); font-weight: 500;
|
|
6
8
|
color: var(--uix-text-hushed); padding: var(--uix-space-3) var(--uix-space-3); cursor: pointer; position: relative;
|
|
7
9
|
/* long or translated labels wrap (two lines at the 16rem cap) instead of truncating */
|
|
8
|
-
white-space: normal; max-width: 16rem; text-align: start;
|
|
10
|
+
white-space: normal; max-width: min(16rem, 100%); overflow-wrap: anywhere; text-align: start;
|
|
9
11
|
transition: color var(--uix-dur-fast) var(--uix-ease-out), background-color var(--uix-dur-fast) var(--uix-ease-out);
|
|
10
12
|
}
|
|
11
|
-
.uix-tab:hover { color: var(--uix-text); }
|
|
13
|
+
@media (hover: hover) { .uix-tab:hover { color: var(--uix-text); } }
|
|
12
14
|
.uix-tab[aria-selected="true"] { color: var(--uix-text); }
|
|
13
15
|
.uix-tab:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: -2px; border-radius: var(--uix-radius-sm); }
|
|
14
16
|
|
|
@@ -20,6 +22,9 @@
|
|
|
20
22
|
transform: scaleX(0); transition: transform var(--uix-dur) var(--uix-ease-out);
|
|
21
23
|
}
|
|
22
24
|
.uix-tabs--line .uix-tab[aria-selected="true"]::after { transform: scaleX(1); }
|
|
25
|
+
/* arrow keys switch tabs many times a minute: the underline moves at once (no slide) while the
|
|
26
|
+
keyboard is in the row; a pointer choice keeps the short slide */
|
|
27
|
+
.uix-tabs--line:has(.uix-tab:focus-visible) .uix-tab::after { transition: none; }
|
|
23
28
|
|
|
24
29
|
/* enclosed */
|
|
25
30
|
.uix-tabs--enclosed { gap: 0; border-bottom: 1px solid var(--uix-border); }
|
|
@@ -63,5 +68,29 @@
|
|
|
63
68
|
.uix-tabs-scroller__prev svg, .uix-tabs-scroller__next svg { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
|
|
64
69
|
.uix-tabs-scroller__prev:dir(rtl) svg, .uix-tabs-scroller__next:dir(rtl) svg { transform: scaleX(-1); }
|
|
65
70
|
|
|
71
|
+
/* Sliding indicator (React Tabs, HAR-2589). At rest each tab shows its own mark. When a click
|
|
72
|
+
changes the tab, one element carries the line's underline or the pill's fill from the old
|
|
73
|
+
tab to the new one (transform only, about 160 ms); while it slides ([data-uix-indicator])
|
|
74
|
+
the new tab's own mark steps aside, then it hides again. Keyboard changes jump. */
|
|
75
|
+
/* the indicator is the last child: isolate + z-index -1 paint it above the row's own
|
|
76
|
+
background and beneath the tabs */
|
|
77
|
+
.uix-tabs { position: relative; isolation: isolate; }
|
|
78
|
+
/* clipped while it slides (a resize must not widen the page); a scrolling row contains it */
|
|
79
|
+
.uix-tabs[data-uix-indicator]:not(.uix-tabs--scroll) { overflow: clip; overflow-clip-margin: var(--uix-space-1); }
|
|
80
|
+
.uix-tabs__indicator { position: absolute; z-index: calc(-1 * var(--uix-z-raised)); pointer-events: none; transform-origin: 0 0; }
|
|
81
|
+
.uix-tabs__indicator[data-moving] { transition: transform var(--uix-dur) var(--uix-ease-in-out); }
|
|
82
|
+
.uix-tabs--line > .uix-tabs__indicator::after {
|
|
83
|
+
content: ""; position: absolute; left: 8px; right: 8px; bottom: -1px; height: 2px;
|
|
84
|
+
background: var(--uix-accent); border-radius: var(--uix-radius-pill);
|
|
85
|
+
}
|
|
86
|
+
.uix-tabs--scroll.uix-tabs--line > .uix-tabs__indicator::after { bottom: 0; }
|
|
87
|
+
.uix-tabs--pill > .uix-tabs__indicator { background: var(--uix-surface); box-shadow: var(--uix-shadow-sm); border-radius: calc(var(--uix-radius-md) - 3px); }
|
|
88
|
+
@media (forced-colors: none) {
|
|
89
|
+
.uix-tabs--line[data-uix-indicator] .uix-tab::after { visibility: hidden; }
|
|
90
|
+
.uix-tabs--pill[data-uix-indicator] .uix-tab[aria-selected="true"] { background: transparent; box-shadow: none; }
|
|
91
|
+
}
|
|
92
|
+
/* forced colours: the tabs' own marks are clearer than a painted box */
|
|
93
|
+
@media (forced-colors: active) { .uix-tabs__indicator { display: none; } }
|
|
94
|
+
|
|
66
95
|
@media (prefers-reduced-motion: reduce) { .uix-tabs--scroll { scroll-behavior: auto; } }
|
|
67
96
|
}
|
|
@@ -25,3 +25,24 @@
|
|
|
25
25
|
.uix-tag__remove:active { background: color-mix(in srgb, var(--uix-text) 26%, transparent); }
|
|
26
26
|
.uix-tag__remove:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
/* TagInput (React, HAR-1377): the field, its refusal line and the suggestion list (a top-layer
|
|
30
|
+
popup styled as Select's). */
|
|
31
|
+
@layer uix.components {
|
|
32
|
+
.uix-taginput-field { display: grid; gap: var(--uix-space-1); min-width: 0; }
|
|
33
|
+
/* the chips' list wrapper is not a box of its own: the chips sit in the field's wrapping row */
|
|
34
|
+
.uix-taginput__tags { display: contents; }
|
|
35
|
+
.uix-taginput[data-invalid] { border-color: var(--uix-danger); }
|
|
36
|
+
.uix-taginput[data-invalid]:focus-within { box-shadow: 0 0 0 3px var(--uix-danger-bg); }
|
|
37
|
+
/* disabled: the subtle surface and no × buttons; the tags stay readable (no opacity on the text) */
|
|
38
|
+
.uix-taginput[data-disabled] { background: var(--uix-bg-subtle); cursor: not-allowed; }
|
|
39
|
+
.uix-taginput__field::placeholder { color: var(--uix-text-muted); }
|
|
40
|
+
.uix-taginput__field:disabled { cursor: not-allowed; }
|
|
41
|
+
/* a long tag wraps inside its chip instead of widening the field */
|
|
42
|
+
.uix-taginput .uix-tag { max-width: 100%; min-width: 0; overflow-wrap: anywhere; }
|
|
43
|
+
.uix-taginput__message { min-width: 0; overflow-wrap: anywhere; }
|
|
44
|
+
.uix-taginput__popup { max-width: min(28rem, calc(100vw - 2 * var(--uix-space-4))); }
|
|
45
|
+
/* a suggestion is "selected" while it is the active one (selection follows the keys): the row tint
|
|
46
|
+
shows that; the chosen-value look of a select (accent, bold) does not apply */
|
|
47
|
+
.uix-taginput__popup .uix-listbox__option[aria-selected="true"] { color: var(--uix-text); font-weight: 400; }
|
|
48
|
+
}
|
|
@@ -12,4 +12,7 @@
|
|
|
12
12
|
.uix-textarea:focus { outline: none; border-color: var(--uix-ring); box-shadow: 0 0 0 3px var(--uix-brand-muted); }
|
|
13
13
|
.uix-textarea:disabled { opacity: .55; background: var(--uix-bg-subtle); }
|
|
14
14
|
.uix-textarea[aria-invalid="true"] { border-color: var(--uix-danger); }
|
|
15
|
+
/* lg: the public-page size beside Input lg (HAR-1812): its type and side padding; the 84 px
|
|
16
|
+
floor already clears the 44 px target, and it still grows with rows and the handle */
|
|
17
|
+
.uix-textarea--lg { padding: var(--uix-space-3) var(--uix-space-4); font-size: var(--uix-text-h3); }
|
|
15
18
|
}
|
|
@@ -4,21 +4,27 @@
|
|
|
4
4
|
.uix-toast {
|
|
5
5
|
display: flex; gap: var(--uix-space-3); align-items: flex-start; padding: var(--uix-space-3) var(--uix-space-4); border-radius: var(--uix-radius-md);
|
|
6
6
|
background: var(--uix-surface-2); border: 1px solid var(--uix-border); box-shadow: var(--uix-shadow-popover);
|
|
7
|
-
|
|
7
|
+
/* Transitions, not keyframes, so a toast that is updated or re-shown mid-exit turns around
|
|
8
|
+
instead of restarting (HAR-2578). Enters from the toaster's edge at --uix-dur-slow; leaves the
|
|
9
|
+
same way at --uix-dur-exit (faster, still ease-out). `translate` is the swipe offset the React
|
|
10
|
+
Toast writes while a finger drags it; the Toaster glides the neighbours when one leaves. */
|
|
11
|
+
transition: opacity var(--uix-dur-slow) var(--uix-ease-out), transform var(--uix-dur-slow) var(--uix-ease-out),
|
|
12
|
+
translate var(--uix-dur) var(--uix-ease-out);
|
|
13
|
+
touch-action: pan-y;
|
|
8
14
|
}
|
|
9
|
-
.uix-toast
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
@keyframes uix-toast-fade-out { to { opacity: 0; } }
|
|
15
|
-
@keyframes uix-toast-out { to { opacity: 0; transform: translateX(16px); } }
|
|
15
|
+
@starting-style { .uix-toast { opacity: 0; transform: var(--uix-toast-from, translateX(var(--uix-space-4))); } }
|
|
16
|
+
.uix-toast[data-leaving] { opacity: 0; transform: var(--uix-toast-from, translateX(var(--uix-space-4))); pointer-events: none; transition-duration: var(--uix-dur-exit); }
|
|
17
|
+
/* while dragged the toast follows the finger; on release it snaps back or flies out */
|
|
18
|
+
.uix-toast[data-swiping] { transition: none; user-select: none; }
|
|
16
19
|
.uix-toast__icon { flex: none; margin-top: 1px; }
|
|
17
20
|
.uix-toast__icon svg { width: var(--uix-icon-md); height: var(--uix-icon-md); }
|
|
18
21
|
.uix-toast__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--uix-space-1); }
|
|
19
22
|
.uix-toast__title { font-weight: 600; font-size: var(--uix-text-body); }
|
|
20
23
|
.uix-toast__msg { font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
|
|
21
24
|
.uix-toast__close { border: 0; background: transparent; color: var(--uix-text-muted); cursor: pointer; padding: var(--uix-space-1) var(--uix-space-2); border-radius: var(--uix-radius-xs); }
|
|
25
|
+
/* the × is the icon set's X (HAR-1576), centred in a 24 px target */
|
|
26
|
+
.uix-toast__close { display: inline-grid; place-items: center; min-width: 24px; min-height: 24px; padding: var(--uix-space-1); }
|
|
27
|
+
.uix-toast__close > .uix-icon { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
|
|
22
28
|
.uix-toast__close:hover { background: var(--uix-bg-hover); color: var(--uix-text); }
|
|
23
29
|
.uix-toast--success .uix-toast__icon { color: var(--uix-success); }
|
|
24
30
|
.uix-toast--danger .uix-toast__icon { color: var(--uix-danger); }
|
|
@@ -37,6 +43,15 @@
|
|
|
37
43
|
.uix-toast__action:focus-visible, .uix-toast__close:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
|
|
38
44
|
.uix-toast__spinner { width: var(--uix-icon-md); height: var(--uix-icon-md); }
|
|
39
45
|
.uix-toaster[data-position^="top"] { top: var(--uix-space-4); bottom: auto; }
|
|
46
|
+
/* a toast enters from, and leaves toward, the toaster's own edge */
|
|
47
|
+
.uix-toaster[data-position$="start"] { --uix-toast-from: translateX(calc(-1 * var(--uix-space-4))); }
|
|
48
|
+
.uix-toaster[data-position="bottom-center"] { --uix-toast-from: translateY(var(--uix-space-4)); }
|
|
49
|
+
.uix-toaster[data-position="top-center"] { --uix-toast-from: translateY(calc(-1 * var(--uix-space-4))); }
|
|
40
50
|
.uix-toaster[data-position$="start"] { left: var(--uix-space-4); right: auto; }
|
|
41
51
|
.uix-toaster[data-position$="center"] { left: 50%; right: auto; transform: translateX(-50%); }
|
|
42
52
|
}
|
|
53
|
+
|
|
54
|
+
/* The in-app reduced-motion preference (HAR-1372, <html data-motion="reduce">) needs no rule here: toasts
|
|
55
|
+
move on transitions (HAR-2578), which the motion.css policy narrows to fades for both the OS setting
|
|
56
|
+
and data-motion="reduce". */
|
|
57
|
+
|
|
@@ -15,12 +15,16 @@
|
|
|
15
15
|
padding: var(--uix-space-2) var(--uix-space-3); border-radius: var(--uix-radius-sm);
|
|
16
16
|
/* no pointer-events:none — the bubble must be hoverable (WCAG 1.4.13, UIX-A11Y-1) */
|
|
17
17
|
box-shadow: var(--uix-shadow-popover); white-space: normal;
|
|
18
|
-
|
|
18
|
+
/* grows out of the trigger (--uix-anchor-origin); the closed state carries the faster exit. The
|
|
19
|
+
React Tooltip waits --uix-delay-tooltip before it opens on hover, and a neighbour hovered within
|
|
20
|
+
--uix-delay-tooltip-skip of one closing opens at once with [data-uix-instant] (no fade). */
|
|
21
|
+
opacity: 0; transform: scale(.97); transform-origin: var(--uix-anchor-origin, bottom center);
|
|
22
|
+
transition: opacity var(--uix-dur-exit-fast) var(--uix-ease-out), transform var(--uix-dur-exit-fast) var(--uix-ease-out);
|
|
19
23
|
}
|
|
20
24
|
/* rich content (HAR-1349): a short list or a few lines, never interactive */
|
|
21
25
|
.uix-tooltip__bubble :is(ul, ol) { margin: 0; padding-left: 1.1em; }
|
|
22
26
|
.uix-tooltip__bubble :is(p, ul, ol) + :is(p, ul, ol) { margin-top: var(--uix-space-1); }
|
|
23
|
-
.uix-tooltip__bubble:popover-open { opacity: 1; }
|
|
27
|
+
.uix-tooltip__bubble:popover-open { opacity: 1; transform: none; transition-duration: var(--uix-dur-fast); }
|
|
24
28
|
/* A shortcut in the bubble (HAR-2031: Tooltip `shortcut`, Button `shortcut`): after the text,
|
|
25
29
|
the keys drawn as outlines in the bubble's own colour, so they keep its contrast in both
|
|
26
30
|
themes. A Button's hint bubble is one line: the name of an icon button, then the keys. */
|
|
@@ -28,7 +32,7 @@
|
|
|
28
32
|
.uix-tooltip__shortcut:first-child { margin-inline-start: 0; }
|
|
29
33
|
.uix-tooltip__shortcut .uix-kbd { color: inherit; background: transparent; border-color: color-mix(in srgb, currentColor 45%, transparent); }
|
|
30
34
|
.uix-tooltip__bubble--hint { white-space: nowrap; }
|
|
31
|
-
@starting-style { .uix-tooltip__bubble:popover-open { opacity: 0; } }
|
|
35
|
+
@starting-style { .uix-tooltip__bubble:popover-open { opacity: 0; transform: scale(.97); } }
|
|
32
36
|
|
|
33
37
|
/* legacy CSS-only tooltip — clipped by overflow ancestors; superseded by .uix-tooltip__bubble */
|
|
34
38
|
[data-uix-tip] { position: relative; }
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
selection, and collapse styling all key off the item, not the row (UIX-FIX-04). */
|
|
4
4
|
@layer uix.components {
|
|
5
5
|
.uix-tree, .uix-tree ul { list-style: none; margin: 0; padding: 0; }
|
|
6
|
-
.uix-
|
|
6
|
+
/* scoped under .uix-tree so it beats the `.uix-tree ul` reset above: unscoped, that reset won and
|
|
7
|
+
no level was indented (seen while checking HAR-1828's org chart) */
|
|
8
|
+
.uix-tree .uix-tree__group { padding-left: var(--uix-tree-indent-step, var(--uix-space-5)); }
|
|
7
9
|
/* TENSOR RX-125 (UIX-08): the indent stops growing past level 4 — a guide line
|
|
8
10
|
marks the depth instead, so deep nodes keep room for their names. */
|
|
9
11
|
.uix-tree__group .uix-tree__group .uix-tree__group .uix-tree__group .uix-tree__group {
|
|
@@ -75,5 +75,8 @@
|
|
|
75
75
|
.uix-saved-views__actions:has([aria-expanded="true"]) { opacity: 1; }
|
|
76
76
|
.uix-saved-views__pin { display: grid; place-items: center; width: 28px; height: 28px; border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted); cursor: pointer; }
|
|
77
77
|
.uix-saved-views__pin:is(:hover, :focus-visible), .uix-saved-views__pin[data-on] { color: var(--uix-warning-text); background: var(--uix-bg-hover); }
|
|
78
|
+
/* the pin is the icon set's Star (HAR-1576): an outline, filled while pinned, so the state is a shape too */
|
|
79
|
+
.uix-saved-views__pin > .uix-icon { width: var(--uix-icon-sm); height: var(--uix-icon-sm); }
|
|
80
|
+
.uix-saved-views__pin[data-on] > .uix-icon { fill: currentColor; }
|
|
78
81
|
.uix-saved-views__footer { border-top: 1px solid var(--uix-border); margin-top: var(--uix-space-1); padding: var(--uix-space-2); }
|
|
79
82
|
}
|
package/styles/motion.css
CHANGED
|
@@ -22,6 +22,21 @@
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/* The in-app preference (HAR-1372): <html data-motion="reduce">, written by useDisplayPreferences and
|
|
26
|
+
displayPreferencesScript, applies the same policy for a person who has not set it in the OS. With the OS
|
|
27
|
+
setting on, the block above already applies. Components that swap a loop for a pulse mirror their
|
|
28
|
+
reduced rule the same way. */
|
|
29
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
30
|
+
:root[data-motion="reduce"] *, :root[data-motion="reduce"] *::before, :root[data-motion="reduce"] *::after {
|
|
31
|
+
transition-property: opacity, color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, visibility, overlay, display !important;
|
|
32
|
+
transition-behavior: allow-discrete !important;
|
|
33
|
+
/* motion-allow: ends product keyframes at once while still firing animationend (the same policy as above) */
|
|
34
|
+
animation-duration: .001ms !important;
|
|
35
|
+
animation-iteration-count: 1 !important;
|
|
36
|
+
scroll-behavior: auto !important;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
/* Keyframes (cascade-neutral). */
|
|
26
41
|
@keyframes uix-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
27
42
|
@keyframes uix-pop-in {
|
|
@@ -43,6 +58,9 @@
|
|
|
43
58
|
/* the entrance helpers keep their fade and lose their travel */
|
|
44
59
|
.uix-anim-fade, .uix-anim-pop, .uix-anim-up { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) !important; }
|
|
45
60
|
}
|
|
61
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
62
|
+
:root[data-motion="reduce"] :is(.uix-anim-fade, .uix-anim-pop, .uix-anim-up) { animation: uix-fade-in var(--uix-dur) var(--uix-ease-out) !important; }
|
|
63
|
+
}
|
|
46
64
|
|
|
47
65
|
/* Hover-lift affordance for interactive cards/tiles. Gated so touch devices don't get a stuck hover.
|
|
48
66
|
The shadow steps; only the lift animates (box-shadow is never animated). */
|
|
@@ -51,8 +69,9 @@
|
|
|
51
69
|
.uix-lift-hover:hover { transform: translateY(calc(-1 * var(--uix-lift))); box-shadow: var(--uix-shadow-popover); }
|
|
52
70
|
}
|
|
53
71
|
|
|
54
|
-
/* Instant surfaces. Keyboard-initiated and 100×-a-day surfaces
|
|
55
|
-
|
|
56
|
-
[data-uix-
|
|
57
|
-
|
|
72
|
+
/* Instant surfaces. Keyboard-initiated and 100×-a-day surfaces open and close with no animation — the
|
|
73
|
+
frequency budget is "none". [data-uix-instant] is a standing choice (Modal `instant`, CommandPalette,
|
|
74
|
+
a tooltip opened inside its delay group); [data-uix-key-opened] is set per open or close by the React
|
|
75
|
+
overlays (Modal, Drawer, Peek, Popover and Menu, Select) when a key press led to it. */
|
|
76
|
+
:is([data-uix-instant], [data-uix-key-opened]), :is([data-uix-instant], [data-uix-key-opened])::backdrop { transition: none !important; animation: none !important; }
|
|
58
77
|
}
|
package/tokens/base/effect.json
CHANGED
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
"dur-exit-slow": { "$type": "duration", "$value": "160ms", "$description": "Exit paired with --uix-dur-slow (modal, drawer, side peek)." },
|
|
22
22
|
"dur-highlight": { "$type": "duration", "$value": "2400ms", "$description": "How long an arrival or change tint takes to fade back to the row colour. Not a transition budget: the tint is a held signal, not movement." },
|
|
23
23
|
|
|
24
|
+
"dur-change": { "$type": "duration", "$value": "1200ms", "$description": "How long a change tint (ChangeHighlight, .uix-changed) takes to fade on the row or field that changed. Shorter than --uix-dur-highlight: a change in place, not a landing." },
|
|
25
|
+
|
|
26
|
+
"delay-pending": { "$type": "duration", "$value": "300ms", "$description": "How long a pending action waits before it shows a pending indicator: a spinner that would flash for less is noise." },
|
|
27
|
+
"hold-pending": { "$type": "duration", "$value": "500ms", "$description": "Once a pending indicator shows, the least time it stays, so it never flickers." },
|
|
28
|
+
"hold-result": { "$type": "duration", "$value": "1500ms", "$description": "How long an in-place result (a button's success or error state, a copied check) holds before it returns to rest." },
|
|
29
|
+
|
|
24
30
|
"delay-tooltip": { "$type": "duration", "$value": "400ms", "$description": "Hover delay before a tooltip opens. Keyboard focus opens it at once." },
|
|
25
31
|
"delay-tooltip-skip": { "$type": "duration", "$value": "300ms", "$description": "Delay-group window: a neighbouring tooltip hovered within this long after one closed opens at once." },
|
|
26
32
|
|