@apliteni/apliteni-ui 0.11.4 → 0.23.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/package.json +1 -1
- package/react/dist/index.css +19 -0
- package/react/dist/index.d.ts +43 -2
- package/react/dist/index.js +98 -6
- package/src/assets/icons.js +51 -9
- package/src/components/account-nav.js +1 -1
- package/src/components/index.js +16 -2
- package/src/components/loading.js +126 -0
- package/src/components/shell.js +10 -9
- package/src/index.css +6 -1
- package/src/index.js +1 -0
- package/src/inline.js +4 -0
- package/src/styles/badge.css +1 -1
- package/src/styles/base.css +1 -1
- package/src/styles/button.css +18 -5
- package/src/styles/callout.css +37 -18
- package/src/styles/card.css +1 -1
- package/src/styles/code.css +7 -1
- package/src/styles/confirm.css +1 -1
- package/src/styles/drawer.css +9 -6
- package/src/styles/dropdown.css +14 -7
- package/src/styles/empty.css +1 -1
- package/src/styles/feedback.css +25 -16
- package/src/styles/footer.css +15 -6
- package/src/styles/input.css +28 -2
- package/src/styles/layout.css +14 -10
- package/src/styles/loading.css +144 -0
- package/src/styles/motion.css +14 -24
- package/src/styles/nav.css +14 -12
- package/src/styles/reduced-motion.css +32 -0
- package/src/styles/success.css +8 -8
- package/src/styles/table.css +21 -12
- package/src/styles/topbar.css +27 -12
- package/src/tokens/accents.css +7 -7
- package/src/tokens/tokens.css +91 -10
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Loading and permission-denied — the screen-scale states.
|
|
3
|
+
*
|
|
4
|
+
* Markup comes from components/loading.js:
|
|
5
|
+
*
|
|
6
|
+
* <div class="ui-busy" role="status" aria-live="polite" aria-busy="true">
|
|
7
|
+
* <span class="ui-sr">Loading your report…</span>
|
|
8
|
+
* <div class="ui-busy__body">
|
|
9
|
+
* <div class="ui-skel" aria-hidden="true">
|
|
10
|
+
* <span class="ui-skel__bar m-skeleton"></span>…
|
|
11
|
+
*
|
|
12
|
+
* <div class="ui-denied">
|
|
13
|
+
* <div class="ui-denied__seal">…lock…</div>
|
|
14
|
+
* <div class="ui-denied__title">You don’t have access</div>
|
|
15
|
+
* <div class="ui-denied__sub">…</div>
|
|
16
|
+
* <div class="ui-denied__need">Needs <code class="ui-code">reports.read</code></div>
|
|
17
|
+
* <div class="ui-denied__actions">…</div>
|
|
18
|
+
*
|
|
19
|
+
* The shimmer is .m-skeleton from styles/motion.css — this file owns layout and
|
|
20
|
+
* nothing else about the animation, so reduced motion is already handled by the
|
|
21
|
+
* global block at the end of that file and there is one shimmer in the kit.
|
|
22
|
+
*
|
|
23
|
+
* .ui-denied deliberately borrows every colour .ui-empty uses. To a reader the
|
|
24
|
+
* two are the same event — what you came for is not here — and giving denial
|
|
25
|
+
* its own red made it read as a fault the reader had committed. The lock says
|
|
26
|
+
* which of the two it is.
|
|
27
|
+
* ========================================================================== */
|
|
28
|
+
|
|
29
|
+
/* Visually hidden, still read aloud. The kit had no such utility, and the live
|
|
30
|
+
region needs one: its message is for assistive tech only — the sighted
|
|
31
|
+
reader is already looking at the skeleton. */
|
|
32
|
+
.ui-sr {
|
|
33
|
+
position: absolute;
|
|
34
|
+
width: 1px;
|
|
35
|
+
height: 1px;
|
|
36
|
+
margin: -1px;
|
|
37
|
+
padding: 0;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
clip: rect(0 0 0 0);
|
|
40
|
+
clip-path: inset(50%);
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
border: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* -- The live region ------------------------------------------------------- */
|
|
46
|
+
.ui-busy {
|
|
47
|
+
display: block;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* -- Skeleton -------------------------------------------------------------- */
|
|
51
|
+
.ui-skel {
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: 12px;
|
|
55
|
+
}
|
|
56
|
+
.ui-skel__bar {
|
|
57
|
+
display: block;
|
|
58
|
+
height: 13px;
|
|
59
|
+
width: 100%;
|
|
60
|
+
border-radius: var(--radius-xs);
|
|
61
|
+
}
|
|
62
|
+
/* Last line of a paragraph run stops short, the way real prose does — a block
|
|
63
|
+
of four identical full-width bars reads as a table, not as text. Any explicit
|
|
64
|
+
width from skeleton({ lines: [...] }) is inline and still wins. */
|
|
65
|
+
.ui-skel > .ui-skel__bar:last-child:not(:only-child) {
|
|
66
|
+
width: 62%;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Table shape: the column rhythm the real table will have, so the placeholder
|
|
70
|
+
does not resize the page out from under the reader when the rows land. */
|
|
71
|
+
.ui-skel--table {
|
|
72
|
+
gap: 0;
|
|
73
|
+
}
|
|
74
|
+
.ui-skel--table .ui-skel__row {
|
|
75
|
+
display: grid;
|
|
76
|
+
grid-template-columns: 1.6fr repeat(calc(var(--skel-cols) - 1), 1fr);
|
|
77
|
+
gap: 18px;
|
|
78
|
+
align-items: center;
|
|
79
|
+
padding: 14px 0;
|
|
80
|
+
border-bottom: 1px solid var(--border);
|
|
81
|
+
}
|
|
82
|
+
.ui-skel--table .ui-skel__row:last-child {
|
|
83
|
+
border-bottom: 0;
|
|
84
|
+
}
|
|
85
|
+
.ui-skel--table .ui-skel__bar {
|
|
86
|
+
height: 11px;
|
|
87
|
+
}
|
|
88
|
+
.ui-skel--table .ui-skel__row--head .ui-skel__bar {
|
|
89
|
+
height: 9px;
|
|
90
|
+
width: 58%;
|
|
91
|
+
opacity: 0.7;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* -- Permission denied ----------------------------------------------------- */
|
|
95
|
+
.ui-denied {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
align-items: center;
|
|
99
|
+
text-align: center;
|
|
100
|
+
gap: 8px;
|
|
101
|
+
padding: 48px 24px;
|
|
102
|
+
color: var(--muted);
|
|
103
|
+
}
|
|
104
|
+
.ui-denied__seal {
|
|
105
|
+
width: 44px;
|
|
106
|
+
height: 44px;
|
|
107
|
+
margin-bottom: 6px;
|
|
108
|
+
border-radius: 50%;
|
|
109
|
+
display: grid;
|
|
110
|
+
place-items: center;
|
|
111
|
+
color: var(--dim);
|
|
112
|
+
background: color-mix(in srgb, var(--text) 7%, transparent);
|
|
113
|
+
}
|
|
114
|
+
.ui-denied__seal svg {
|
|
115
|
+
width: 20px;
|
|
116
|
+
height: 20px;
|
|
117
|
+
stroke: currentColor;
|
|
118
|
+
fill: none;
|
|
119
|
+
stroke-width: 1.9;
|
|
120
|
+
}
|
|
121
|
+
.ui-denied__title {
|
|
122
|
+
color: var(--text);
|
|
123
|
+
font-size: var(--text-md);
|
|
124
|
+
font-weight: var(--weight-medium);
|
|
125
|
+
margin: 2px 0 0;
|
|
126
|
+
}
|
|
127
|
+
.ui-denied__sub {
|
|
128
|
+
font-size: var(--text-sm);
|
|
129
|
+
max-width: var(--prose-caption);
|
|
130
|
+
margin: 0;
|
|
131
|
+
line-height: 1.55;
|
|
132
|
+
}
|
|
133
|
+
/* The missing scope, verbatim. A reader who can name what they lack can ask
|
|
134
|
+
for it; "insufficient permissions" sends them to a ticket to find out. */
|
|
135
|
+
.ui-denied__need {
|
|
136
|
+
font-size: var(--text-sm);
|
|
137
|
+
margin-top: 4px;
|
|
138
|
+
}
|
|
139
|
+
.ui-denied__actions {
|
|
140
|
+
display: flex;
|
|
141
|
+
gap: 10px;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
margin-top: 12px;
|
|
144
|
+
}
|
package/src/styles/motion.css
CHANGED
|
@@ -16,8 +16,11 @@
|
|
|
16
16
|
* apliteni-ui/motion. Without JS, [data-reveal] content stays fully visible
|
|
17
17
|
* (progressive enhancement, never hidden behind a script that didn't run).
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* The global prefers-reduced-motion net that neutralises ALL of the above — plus
|
|
20
|
+
* any component animation in the kit — lives in reduced-motion.css, because the
|
|
21
|
+
* React bundle needs the same net and two copies would drift. index.css imports
|
|
22
|
+
* it beside this file; it is NOT @imported from here, because the icon gates read
|
|
23
|
+
* each stylesheet on its own and refuse a sheet that pulls in another.
|
|
21
24
|
* ========================================================================== */
|
|
22
25
|
|
|
23
26
|
/* -- Entrances -------------------------------------------------------------
|
|
@@ -31,7 +34,7 @@
|
|
|
31
34
|
.m-scale-in,
|
|
32
35
|
.m-blur-in {
|
|
33
36
|
animation-duration: var(--m-dur, var(--dur-slow));
|
|
34
|
-
animation-timing-function: var(--m-ease, var(--
|
|
37
|
+
animation-timing-function: var(--m-ease, var(--ease-out));
|
|
35
38
|
animation-delay: var(--m-delay, 0s);
|
|
36
39
|
animation-fill-mode: both;
|
|
37
40
|
}
|
|
@@ -78,7 +81,7 @@
|
|
|
78
81
|
box-shadow: var(--shadow-md);
|
|
79
82
|
}
|
|
80
83
|
.m-press {
|
|
81
|
-
transition: transform var(--
|
|
84
|
+
transition: transform var(--dur-instant) var(--ease-sharp);
|
|
82
85
|
}
|
|
83
86
|
.m-press:active { transform: scale(0.97); }
|
|
84
87
|
|
|
@@ -100,12 +103,12 @@
|
|
|
100
103
|
color-mix(in srgb, var(--text) 12%, transparent),
|
|
101
104
|
transparent
|
|
102
105
|
);
|
|
103
|
-
animation: m-shimmer var(--duration-crawl, 1s)
|
|
106
|
+
animation: m-shimmer var(--duration-crawl, 1s) linear infinite; /* motion: ambient — a skeleton sweeps while the data is away */
|
|
104
107
|
}
|
|
105
108
|
@keyframes m-shimmer { to { transform: translateX(100%); } }
|
|
106
109
|
|
|
107
110
|
/* -- Attention ------------------------------------------------------------- */
|
|
108
|
-
.m-pulse { animation: m-pulse 2s var(--ease) infinite; }
|
|
111
|
+
.m-pulse { animation: m-pulse 2s var(--ease) infinite; } /* motion: ambient — attention loop with no interaction origin */
|
|
109
112
|
@keyframes m-pulse {
|
|
110
113
|
0%, 100% { transform: scale(1); opacity: 1; }
|
|
111
114
|
50% { transform: scale(1.04); opacity: 0.86; }
|
|
@@ -126,7 +129,7 @@
|
|
|
126
129
|
.m-draw [data-draw] {
|
|
127
130
|
stroke-dasharray: var(--m-len, 48);
|
|
128
131
|
stroke-dashoffset: var(--m-len, 48);
|
|
129
|
-
animation: m-draw var(--dur-slow) var(--
|
|
132
|
+
animation: m-draw var(--dur-slow) var(--ease-out) forwards;
|
|
130
133
|
animation-delay: var(--m-delay, 0s);
|
|
131
134
|
}
|
|
132
135
|
@keyframes m-draw { to { stroke-dashoffset: 0; } }
|
|
@@ -140,8 +143,8 @@
|
|
|
140
143
|
opacity: 0;
|
|
141
144
|
transform: translateY(18px);
|
|
142
145
|
transition:
|
|
143
|
-
opacity var(--dur-slow) var(--
|
|
144
|
-
transform var(--dur-slow) var(--
|
|
146
|
+
opacity var(--dur-slow) var(--ease-out),
|
|
147
|
+
transform var(--dur-slow) var(--ease-out);
|
|
145
148
|
transition-delay: calc(var(--reveal-i, 0) * var(--delay-2));
|
|
146
149
|
}
|
|
147
150
|
.js-reveal [data-reveal].is-revealed {
|
|
@@ -149,18 +152,5 @@
|
|
|
149
152
|
transform: none;
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
/*
|
|
153
|
-
*
|
|
154
|
-
* component motion (button loader, badge pulse, feedback check), and smooth
|
|
155
|
-
* scroll. iteration-count:1 lets one-shot animations settle on their final
|
|
156
|
-
* frame (e.g. a checkmark stays drawn) instead of snapping to the start. */
|
|
157
|
-
@media (prefers-reduced-motion: reduce) {
|
|
158
|
-
html { scroll-behavior: auto !important; }
|
|
159
|
-
*, ::before, ::after {
|
|
160
|
-
animation-duration: 0.01ms !important;
|
|
161
|
-
animation-iteration-count: 1 !important;
|
|
162
|
-
transition-duration: 0.01ms !important;
|
|
163
|
-
scroll-behavior: auto !important;
|
|
164
|
-
}
|
|
165
|
-
[data-reveal] { opacity: 1 !important; transform: none !important; }
|
|
166
|
-
}
|
|
155
|
+
/* The global reduced-motion net lives in reduced-motion.css, imported beside this
|
|
156
|
+
* file from src/index.css — it has to reach the React bundle as well. */
|
package/src/styles/nav.css
CHANGED
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
cursor: pointer;
|
|
45
45
|
text-align: left;
|
|
46
46
|
font-family: inherit;
|
|
47
|
-
transition: background
|
|
47
|
+
transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
|
|
48
48
|
}
|
|
49
|
-
.ui-nav .ui-nav__item { color: var(--text); } /* (0,2,0): a host sheet's a:link is (0,1,1) — why: docs/
|
|
49
|
+
.ui-nav .ui-nav__item { color: var(--text); } /* (0,2,0): a host sheet's a:link is (0,1,1) — why: docs/specification.md#the-page-shell */
|
|
50
50
|
.ui-nav__item:hover { background: var(--surface-2); color: var(--strong); }
|
|
51
51
|
.ui-nav__item.is-active {
|
|
52
52
|
background: var(--surface-3);
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
.ui-nav__item.is-active:focus-visible { box-shadow: var(--ring); }
|
|
71
71
|
|
|
72
72
|
.ui-nav__ic { display: inline-flex; flex: none; }
|
|
73
|
-
.ui-nav__ic svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width:
|
|
73
|
+
.ui-nav__ic svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2.2; opacity: 0.72; }
|
|
74
74
|
.ui-nav__item.is-active .ui-nav__ic svg,
|
|
75
75
|
.ui-nav__item.is-current .ui-nav__ic svg { opacity: 1; stroke: var(--accent); }
|
|
76
76
|
.ui-nav__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
@@ -82,7 +82,9 @@
|
|
|
82
82
|
/* Tabbing onto it is the same "about to click this" the pointer means. The ring
|
|
83
83
|
says where you are; only this says what the row does. */
|
|
84
84
|
.ui-nav__item.is-danger:focus-visible { background: var(--glow-pink); color: var(--pink); }
|
|
85
|
-
|
|
85
|
+
/* A row that is off. No opacity: the label was already --muted and the fade
|
|
86
|
+
only pulled it toward the rail behind it (#220). */
|
|
87
|
+
.ui-nav__item.is-disabled { color: var(--disabled-ink); cursor: default; pointer-events: none; }
|
|
86
88
|
|
|
87
89
|
/* The current row under the pointer. It rests on --surface-3, which is also what
|
|
88
90
|
a resting row hovers to, so the one row with no hover response was the row the
|
|
@@ -123,7 +125,7 @@
|
|
|
123
125
|
4.08 in dark Nebula, where the flat surface reads 4.92 — and no alpha reaches
|
|
124
126
|
it, the largest that clears being 0.06 in dark Nebula and 0.02 in light
|
|
125
127
|
Emerald. So the accent wash is painted on a base surface, never a raised one.
|
|
126
|
-
See #157 and docs/
|
|
128
|
+
See #157 and docs/specification.md#colour-and-contrast. */
|
|
127
129
|
.ui-nav__item.is-active .ui-nav__badge.is-accent { background: var(--surface-3); color: var(--accent); }
|
|
128
130
|
|
|
129
131
|
/* Collapsible group */
|
|
@@ -134,7 +136,7 @@
|
|
|
134
136
|
border-right: 1.6px solid var(--muted);
|
|
135
137
|
border-bottom: 1.6px solid var(--muted);
|
|
136
138
|
transform: rotate(-45deg);
|
|
137
|
-
transition: transform
|
|
139
|
+
transition: transform var(--dur-fast) var(--ease);
|
|
138
140
|
}
|
|
139
141
|
.ui-nav__group.is-open > .ui-nav__toggle .ui-nav__caret { transform: rotate(45deg); }
|
|
140
142
|
.ui-nav__sub {
|
|
@@ -165,7 +167,7 @@
|
|
|
165
167
|
.ui-nav--side.is-collapsed .ui-nav__caret,
|
|
166
168
|
.ui-nav--side.is-collapsed .ui-nav__sub { display: none; }
|
|
167
169
|
.ui-nav--side.is-collapsed .ui-nav__item { justify-content: center; padding: 11px 0; gap: 0; }
|
|
168
|
-
.ui-nav--side.is-collapsed .ui-nav__ic svg { width: 19px; height: 19px; }
|
|
170
|
+
.ui-nav--side.is-collapsed .ui-nav__ic svg { width: 19px; height: 19px; stroke-width: 2; }
|
|
169
171
|
.ui-nav--side.is-collapsed .ui-nav__item.is-active::before { left: 3px; }
|
|
170
172
|
.ui-nav--side.is-collapsed .ui-nav__foot { text-align: center; }
|
|
171
173
|
|
|
@@ -188,7 +190,7 @@
|
|
|
188
190
|
font-size: 14px;
|
|
189
191
|
font-weight: 500;
|
|
190
192
|
border-radius: 10px 10px 0 0;
|
|
191
|
-
transition: color
|
|
193
|
+
transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
|
|
192
194
|
}
|
|
193
195
|
.ui-nav__tab:hover { color: var(--strong); }
|
|
194
196
|
.ui-nav__tab:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 8px; }
|
|
@@ -204,7 +206,7 @@
|
|
|
204
206
|
background: var(--accent);
|
|
205
207
|
opacity: 0;
|
|
206
208
|
transform: scaleX(0.4);
|
|
207
|
-
transition: opacity
|
|
209
|
+
transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
|
|
208
210
|
}
|
|
209
211
|
.ui-nav--tabs.is-underline .ui-nav__tab.is-active::after { opacity: 1; transform: scaleX(1); }
|
|
210
212
|
|
|
@@ -216,7 +218,7 @@
|
|
|
216
218
|
color: var(--accent);
|
|
217
219
|
}
|
|
218
220
|
|
|
219
|
-
.ui-nav__tab.is-disabled { color: var(--
|
|
221
|
+
.ui-nav__tab.is-disabled { color: var(--disabled-ink); cursor: default; }
|
|
220
222
|
.ui-nav__tab .ui-nav__badge { height: 18px; }
|
|
221
223
|
|
|
222
224
|
/* -- Breadcrumbs --------------------------------------------------------- */
|
|
@@ -239,7 +241,7 @@
|
|
|
239
241
|
border-radius: 7px;
|
|
240
242
|
text-decoration: none;
|
|
241
243
|
font-size: 13px;
|
|
242
|
-
transition: color
|
|
244
|
+
transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
|
|
243
245
|
}
|
|
244
246
|
/* Carries the block's class as well as its own, for the reason .ui-nav__item
|
|
245
247
|
does above: a linked crumb is an <a>, and a host stylesheet's `a:link` is
|
|
@@ -248,4 +250,4 @@
|
|
|
248
250
|
a.ui-nav__crumb:hover { color: var(--strong); background: var(--surface-2); }
|
|
249
251
|
a.ui-nav__crumb:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
250
252
|
.ui-nav__crumb.is-current { color: var(--strong); font-weight: 500; }
|
|
251
|
-
.ui-nav__crumb .ui-nav__ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width:
|
|
253
|
+
.ui-nav__crumb .ui-nav__ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.5; }
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* apliteni-ui — REDUCED-MOTION NET
|
|
3
|
+
*
|
|
4
|
+
* One file, because it has to reach every bundle the kit publishes and a second
|
|
5
|
+
* copy of it would drift. `src/styles/motion.css` imports it, so `apliteni-ui/css`
|
|
6
|
+
* carries it; `react/src/index.ts` imports it, so `apliteni-ui/react/css` carries
|
|
7
|
+
* it too. A consumer who imports both gets it twice, which costs nothing: every
|
|
8
|
+
* rule here is idempotent and `!important`.
|
|
9
|
+
*
|
|
10
|
+
* It neutralises every animation and transition in whatever bundle it lands in —
|
|
11
|
+
* the motion library's effects, component motion (button loader, badge pulse,
|
|
12
|
+
* feedback check), and smooth scroll. `iteration-count: 1` lets one-shot
|
|
13
|
+
* animations settle on their final frame (a checkmark stays drawn) instead of
|
|
14
|
+
* snapping back to the start.
|
|
15
|
+
*
|
|
16
|
+
* `0.01ms` is the kill-switch idiom, not a duration: it is short enough that
|
|
17
|
+
* nothing is perceptible and non-zero so `transitionend`/`animationend` still
|
|
18
|
+
* fire, which is what scripts that wait for a close animation depend on. It is
|
|
19
|
+
* the one time literal in the kit that is not a timing — see
|
|
20
|
+
* docs/specification.md#motion.
|
|
21
|
+
* ========================================================================== */
|
|
22
|
+
|
|
23
|
+
@media (prefers-reduced-motion: reduce) {
|
|
24
|
+
html { scroll-behavior: auto !important; }
|
|
25
|
+
*, ::before, ::after {
|
|
26
|
+
animation-duration: 0.01ms !important;
|
|
27
|
+
animation-iteration-count: 1 !important;
|
|
28
|
+
transition-duration: 0.01ms !important;
|
|
29
|
+
scroll-behavior: auto !important;
|
|
30
|
+
}
|
|
31
|
+
[data-reveal] { opacity: 1 !important; transform: none !important; }
|
|
32
|
+
}
|
package/src/styles/success.css
CHANGED
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
.ui-sx__glow--a {
|
|
36
36
|
width: 58%; top: -14%; left: 50%; translate: -50% 0;
|
|
37
37
|
background: radial-gradient(circle at center, var(--sx-tint), transparent 68%);
|
|
38
|
-
animation: ui-sx-float 14s ease-in-out infinite alternate;
|
|
38
|
+
animation: ui-sx-float 14s ease-in-out infinite alternate; /* motion: ambient — a background glow that drifts on its own clock; `alternate` needs a symmetric curve, which var(--ease) is not */
|
|
39
39
|
}
|
|
40
40
|
.ui-sx__glow--b {
|
|
41
41
|
width: 52%; bottom: -20%; left: 12%;
|
|
42
42
|
background: radial-gradient(circle at center,
|
|
43
43
|
color-mix(in srgb, var(--accent) 30%, transparent), transparent 68%);
|
|
44
|
-
animation: ui-sx-float 18s ease-in-out -6s infinite alternate;
|
|
44
|
+
animation: ui-sx-float 18s ease-in-out -6s infinite alternate; /* motion: ambient — the second glow, offset in period and phase so the pair never lines up */
|
|
45
45
|
}
|
|
46
46
|
:root[data-theme="light"] .ui-sx__glow { opacity: 0.7; }
|
|
47
47
|
@keyframes ui-sx-float {
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
letter-spacing: -.02em; line-height: 1.2; margin: 0;
|
|
64
64
|
}
|
|
65
65
|
.ui-sx__body {
|
|
66
|
-
color: var(--dim); font-size: 14.5px; line-height: 1.6; margin: 9px 0 0; max-width:
|
|
66
|
+
color: var(--dim); font-size: 14.5px; line-height: 1.6; margin: 9px 0 0; max-width: var(--prose-caption);
|
|
67
67
|
}
|
|
68
68
|
.ui-sx__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }
|
|
69
69
|
|
|
@@ -104,21 +104,21 @@
|
|
|
104
104
|
/* Soft tinted disc that springs in behind the tick. */
|
|
105
105
|
.ui-sx__disc {
|
|
106
106
|
fill: var(--sx-tint); stroke: var(--sx-track); stroke-width: 1.5;
|
|
107
|
-
transform-origin: center; animation: ui-sx-pop .5s cubic-bezier(.22,1,.3,1) both;
|
|
107
|
+
transform-origin: center; animation: ui-sx-pop .5s cubic-bezier(.22,1,.3,1) both; /* motion: choreographed — first beat of the check: disc, then tick at .28s, then ring at .2s */
|
|
108
108
|
}
|
|
109
109
|
/* The tick, stroked on via dash-offset (path length ~34). */
|
|
110
110
|
.ui-sx__tick {
|
|
111
111
|
fill: none; stroke: var(--sx-green); stroke-width: 5;
|
|
112
112
|
stroke-linecap: round; stroke-linejoin: round;
|
|
113
113
|
stroke-dasharray: 34; stroke-dashoffset: 34;
|
|
114
|
-
animation: ui-sx-draw .5s cubic-bezier(.6,0,.35,1) .28s forwards;
|
|
114
|
+
animation: ui-sx-draw .5s cubic-bezier(.6,0,.35,1) .28s forwards; /* motion: choreographed — the tick draws while the disc is still settling */
|
|
115
115
|
filter: drop-shadow(0 0 5px color-mix(in srgb, var(--green) 55%, transparent));
|
|
116
116
|
}
|
|
117
117
|
/* Burst ring that expands and fades once, framing the moment. */
|
|
118
118
|
.ui-sx__ring {
|
|
119
119
|
fill: none; stroke: var(--sx-green); stroke-width: 2;
|
|
120
120
|
transform-origin: center; opacity: 0;
|
|
121
|
-
animation: ui-sx-burst .7s cubic-bezier(.3,.7,.4,1) .2s forwards;
|
|
121
|
+
animation: ui-sx-burst .7s cubic-bezier(.3,.7,.4,1) .2s forwards; /* motion: choreographed — the ring outlives the tick so the moment ends on the frame */
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
@keyframes ui-sx-pop {
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
.ui-sx__piece {
|
|
138
138
|
position: absolute; top: -8%; width: 8px; height: 8px; border-radius: 2px;
|
|
139
139
|
opacity: 0; transform-origin: center;
|
|
140
|
-
animation: ui-sx-fall 2.4s cubic-bezier(.2,.6,.35,1) var(--sx-d, 0s) forwards;
|
|
140
|
+
animation: ui-sx-fall 2.4s cubic-bezier(.2,.6,.35,1) var(--sx-d, 0s) forwards; /* motion: ambient — fall time is set by the height of the panel, not by an interaction */
|
|
141
141
|
scale: var(--sx-s, 1);
|
|
142
142
|
}
|
|
143
143
|
.ui-sx__piece--a { background: var(--accent); }
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
background: conic-gradient(var(--green) var(--sx-a, 360deg), var(--surface-2) 0);
|
|
163
163
|
mask: radial-gradient(circle, transparent 5px, #000 6px);
|
|
164
164
|
-webkit-mask: radial-gradient(circle, transparent 5px, #000 6px);
|
|
165
|
-
animation: ui-sx-sweep var(--sx-secs, 5s) linear forwards;
|
|
165
|
+
animation: ui-sx-sweep var(--sx-secs, 5s) linear forwards; /* motion: ambient — the caller sets the countdown; the ring just spends it */
|
|
166
166
|
}
|
|
167
167
|
@keyframes ui-sx-sweep { from { --sx-a: 360deg; } to { --sx-a: 0deg; } }
|
|
168
168
|
.ui-sx__count-text { color: var(--muted); font-size: 13px; }
|
package/src/styles/table.css
CHANGED
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
text-transform: uppercase;
|
|
16
16
|
color: var(--muted);
|
|
17
17
|
font-weight: var(--weight-semibold);
|
|
18
|
-
padding: 0
|
|
18
|
+
padding: 0 var(--space-4) var(--space-3) 0;
|
|
19
19
|
border-bottom: 1px solid var(--border);
|
|
20
20
|
}
|
|
21
21
|
.ui-table td {
|
|
22
|
-
padding:
|
|
22
|
+
padding: var(--space-4) var(--space-4) var(--space-4) 0;
|
|
23
23
|
border-bottom: 1px solid var(--border);
|
|
24
24
|
color: var(--dim);
|
|
25
25
|
vertical-align: middle;
|
|
@@ -39,11 +39,13 @@
|
|
|
39
39
|
* continuous while letting the row ends round; table padding (which only applies
|
|
40
40
|
* in the separate model) insets the rows so the fill clears the container edge.
|
|
41
41
|
* Zebra tables keep the original full-bleed tint (finance ledger), so they're
|
|
42
|
-
* excluded here — their hover rule lives further down.
|
|
42
|
+
* excluded here — their hover rule lives further down.
|
|
43
|
+
* The inset was 6px, which is a tie between --space-1 and --space-2; it rounds
|
|
44
|
+
* up, because what it buys is clearance and clearance rounds away from the edge. */
|
|
43
45
|
.ui-table--hover:not(.ui-table--zebra) {
|
|
44
46
|
border-collapse: separate;
|
|
45
47
|
border-spacing: 0;
|
|
46
|
-
padding: 0
|
|
48
|
+
padding: 0 var(--space-2);
|
|
47
49
|
}
|
|
48
50
|
.ui-table--hover:not(.ui-table--zebra) tbody tr:hover td { background: var(--surface-2); }
|
|
49
51
|
.ui-table--hover:not(.ui-table--zebra) tbody tr:hover td:first-child {
|
|
@@ -53,17 +55,24 @@
|
|
|
53
55
|
|
|
54
56
|
/* ── Data-table modifiers (composable) ──────────────────────────────────────
|
|
55
57
|
* Compose for a dense financial ledger: .ui-table--dense --zebra --hover.
|
|
56
|
-
* --dense compact row rhythm for many-row / many-column data
|
|
58
|
+
* --dense compact row rhythm for many-row / many-column data: --space-3
|
|
59
|
+
* across and --space-2 down, one step in from the base table's
|
|
60
|
+
* --space-4 across and --space-4 down. Both of its old numbers sat
|
|
61
|
+
* exactly between two steps — 14px between 12 and 16, 10px between 8
|
|
62
|
+
* and 12 — and both round DOWN, because the modifier exists to fit
|
|
63
|
+
* more rows and rounding up spends the distinction it is for.
|
|
57
64
|
* --zebra row striping instead of per-row rules; 2px header rule; the
|
|
58
65
|
* (accent) hover tint out-ranks the stripe (defined after it)
|
|
59
66
|
* Cell helpers: .ui-table__num (right, tabular), .ui-table__code (plain
|
|
60
|
-
* monospace id — never a boxed code chip inside a table).
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
* monospace id — never a boxed code chip inside a table).
|
|
68
|
+
* why: docs/specification.md#spacing-and-rhythm
|
|
69
|
+
* held by: stories/table-rhythm.test.js */
|
|
70
|
+
.ui-table--dense th { padding: 0 var(--space-3) var(--space-2) 0; }
|
|
71
|
+
.ui-table--dense td { padding: var(--space-2) var(--space-3); font-size: var(--text-sm); white-space: nowrap; }
|
|
63
72
|
.ui-table--dense th:first-child,
|
|
64
|
-
.ui-table--dense td:first-child { padding-left:
|
|
73
|
+
.ui-table--dense td:first-child { padding-left: var(--space-3); }
|
|
65
74
|
.ui-table--dense th:last-child,
|
|
66
|
-
.ui-table--dense td:last-child { padding-right:
|
|
75
|
+
.ui-table--dense td:last-child { padding-right: var(--space-3); }
|
|
67
76
|
|
|
68
77
|
.ui-table--zebra th { border-bottom-width: 2px; }
|
|
69
78
|
.ui-table--zebra td { border-bottom: 0; }
|
|
@@ -78,9 +87,9 @@
|
|
|
78
87
|
* checkboxes sat flush against the tint with a measured 0px gap.
|
|
79
88
|
* Header and body are inset together, or the columns stop lining up. */
|
|
80
89
|
.ui-table--zebra th:first-child,
|
|
81
|
-
.ui-table--zebra td:first-child { padding-left:
|
|
90
|
+
.ui-table--zebra td:first-child { padding-left: var(--space-3); }
|
|
82
91
|
.ui-table--zebra th:last-child,
|
|
83
|
-
.ui-table--zebra td:last-child { padding-right:
|
|
92
|
+
.ui-table--zebra td:last-child { padding-right: var(--space-3); }
|
|
84
93
|
.ui-table--zebra.ui-table--hover tbody tr:hover td,
|
|
85
94
|
.ui-table--zebra tbody tr:hover td { background: color-mix(in srgb, var(--accent) 8%, transparent); }
|
|
86
95
|
|
package/src/styles/topbar.css
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
}
|
|
15
15
|
.topbar__in {
|
|
16
16
|
width: 100%;
|
|
17
|
-
max-width:
|
|
17
|
+
max-width: var(--container);
|
|
18
18
|
margin: 0 auto;
|
|
19
19
|
padding: 0 clamp(14px, 2.4vw, 26px);
|
|
20
20
|
display: flex;
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
color: var(--muted);
|
|
60
60
|
padding: 4px 13px;
|
|
61
61
|
border-radius: 24px;
|
|
62
|
-
transition: color
|
|
62
|
+
transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
|
|
63
63
|
}
|
|
64
64
|
.dtsw a:hover { color: var(--text); }
|
|
65
65
|
.dtsw a.cur { color: var(--strong); background: var(--surface); box-shadow: var(--shadow-sm); }
|
|
@@ -75,11 +75,14 @@
|
|
|
75
75
|
color: var(--text);
|
|
76
76
|
border: 1px solid var(--border);
|
|
77
77
|
border-radius: 10px;
|
|
78
|
-
transition:
|
|
78
|
+
transition:
|
|
79
|
+
transform var(--dur-fast) var(--ease),
|
|
80
|
+
background var(--dur-fast) var(--ease),
|
|
81
|
+
border-color var(--dur-fast) var(--ease);
|
|
79
82
|
}
|
|
80
83
|
.toggle:hover { transform: translateY(-1px); border-color: var(--border-strong); }
|
|
81
84
|
.toggle .ic { width: 17px; height: 17px; display: inline-block; color: var(--accent); }
|
|
82
|
-
.toggle .ic svg { display: block; width: 100%; height: 100%; }
|
|
85
|
+
.toggle .ic svg { display: block; width: 100%; height: 100%; stroke-width: 2.2; }
|
|
83
86
|
|
|
84
87
|
/* Version switcher */
|
|
85
88
|
.vsw { position: relative; }
|
|
@@ -96,7 +99,7 @@
|
|
|
96
99
|
border: 1px solid var(--border);
|
|
97
100
|
border-radius: 24px;
|
|
98
101
|
padding: 7px 11px 7px 13px;
|
|
99
|
-
transition: border-color
|
|
102
|
+
transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
|
|
100
103
|
}
|
|
101
104
|
.vsw__btn:hover { border-color: var(--accent); }
|
|
102
105
|
.vsw__btn .lbl { color: var(--muted); }
|
|
@@ -106,7 +109,7 @@
|
|
|
106
109
|
border-right: 1.6px solid var(--muted);
|
|
107
110
|
border-bottom: 1.6px solid var(--muted);
|
|
108
111
|
transform: rotate(45deg) translateY(-1px);
|
|
109
|
-
transition: transform
|
|
112
|
+
transition: transform var(--dur-fast) var(--ease);
|
|
110
113
|
}
|
|
111
114
|
.vsw.open .vsw__btn .car { transform: rotate(-135deg) translateY(2px); }
|
|
112
115
|
.vsw__menu {
|
|
@@ -122,7 +125,13 @@
|
|
|
122
125
|
opacity: 0;
|
|
123
126
|
visibility: hidden;
|
|
124
127
|
transform: translateY(-6px);
|
|
125
|
-
transition: 0.18s
|
|
128
|
+
/* Named properties, not the bare shorthand: `transition: 0.18s` is `all`, and `all`
|
|
129
|
+
includes visibility — held at the OLD value for the whole duration, so the menu is
|
|
130
|
+
still `hidden` in the frame it opens. */
|
|
131
|
+
transition:
|
|
132
|
+
opacity var(--dur-med) var(--ease),
|
|
133
|
+
transform var(--dur-med) var(--ease),
|
|
134
|
+
visibility var(--dur-med) linear;
|
|
126
135
|
z-index: var(--z-dropdown);
|
|
127
136
|
}
|
|
128
137
|
.vsw.open .vsw__menu { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
@@ -133,7 +142,7 @@
|
|
|
133
142
|
padding: 10px 13px;
|
|
134
143
|
border-radius: 10px;
|
|
135
144
|
cursor: pointer;
|
|
136
|
-
transition: background
|
|
145
|
+
transition: background var(--dur-fast) var(--ease);
|
|
137
146
|
}
|
|
138
147
|
.vopt:hover { background: var(--surface); }
|
|
139
148
|
.vopt .vname { font-family: var(--font-sans); font-size: 12.5px; color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
|
|
@@ -151,7 +160,9 @@
|
|
|
151
160
|
}
|
|
152
161
|
.vbadge.live { color: var(--chip-success-ink); background: var(--chip-success-fill); }
|
|
153
162
|
.vbadge.arch { color: var(--muted); background: var(--surface); }
|
|
154
|
-
|
|
163
|
+
/* The switcher keeps its glyph and drops its label where the shell folds — the
|
|
164
|
+
same step the rail folds at: docs/specification.md#breakpoints. */
|
|
165
|
+
@media (max-width: 720px) { .vsw__btn .lbl { display: none; } }
|
|
155
166
|
|
|
156
167
|
/* Account — avatar + dropdown. Gated: hidden until the app confirms a session
|
|
157
168
|
and adds `.on` (mirrors the portal's /auth/me reveal). */
|
|
@@ -169,7 +180,7 @@
|
|
|
169
180
|
place-items: center;
|
|
170
181
|
cursor: pointer;
|
|
171
182
|
letter-spacing: 0.02em;
|
|
172
|
-
transition: border-color
|
|
183
|
+
transition: border-color var(--dur-fast) var(--ease);
|
|
173
184
|
}
|
|
174
185
|
.acct .avatar:hover { border-color: var(--accent); }
|
|
175
186
|
.amenu {
|
|
@@ -184,7 +195,11 @@
|
|
|
184
195
|
opacity: 0;
|
|
185
196
|
visibility: hidden;
|
|
186
197
|
transform: translateY(-6px);
|
|
187
|
-
|
|
198
|
+
/* Named properties — see .vsw__menu above. */
|
|
199
|
+
transition:
|
|
200
|
+
opacity var(--dur-med) var(--ease),
|
|
201
|
+
transform var(--dur-med) var(--ease),
|
|
202
|
+
visibility var(--dur-med) linear;
|
|
188
203
|
z-index: var(--z-dropdown);
|
|
189
204
|
}
|
|
190
205
|
.acct.open .amenu { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
@@ -196,7 +211,7 @@
|
|
|
196
211
|
.amenu a { display: flex; align-items: center; gap: 11px; padding: 10px 15px; color: var(--text); text-decoration: none; font-family: var(--font-sans); font-size: 13px; cursor: pointer; }
|
|
197
212
|
.amenu a:hover { background: var(--surface); }
|
|
198
213
|
.amenu a.cur { color: var(--accent); }
|
|
199
|
-
.amenu a svg { width: 19px; height: 19px; flex: none; stroke: currentColor; fill: none; stroke-width:
|
|
214
|
+
.amenu a svg { width: 19px; height: 19px; flex: none; stroke: currentColor; fill: none; stroke-width: 2; opacity: 0.85; }
|
|
200
215
|
.amenu .asep { height: 1px; background: var(--border); margin: 5px 0; }
|
|
201
216
|
.amenu a.aout { color: var(--muted); }
|
|
202
217
|
.amenu a.aout:hover { color: var(--pink); }
|