@apliteni/apliteni-ui 0.12.0 → 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 +5 -26
- package/src/components/account-nav.js +1 -1
- 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
package/src/styles/callout.css
CHANGED
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
color: var(--dim);
|
|
14
14
|
}
|
|
15
15
|
.ui-callout__icon { width: 18px; height: 18px; flex: none; margin-top: 1px; color: var(--muted); }
|
|
16
|
-
.ui-callout__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 1
|
|
16
|
+
.ui-callout__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 2.1; } /* 2.1 of a 24 box drawn at 18px = 1.58 CSS px, which is what buys the 3:1 graphic bar rather than the 4.5:1 text one. docs/specification.md#icons-and-glyphs */
|
|
17
17
|
.ui-callout b { color: var(--text); font-weight: var(--weight-semibold); }
|
|
18
18
|
.ui-callout code { font-family: var(--font-mono); font-size: 0.92em; color: var(--text); }
|
|
19
19
|
|
|
20
20
|
.ui-callout--info { background: var(--glow-cyan); }
|
|
21
|
-
.ui-callout--info .ui-callout__icon { color: var(--
|
|
21
|
+
.ui-callout--info .ui-callout__icon { color: var(--chip-info-ink); } /* the chip inks, not the raw signals: in light the raw signal cleared the graphic bar by a tenth on its own wash and read as a smudge, and a stroke is read the way text is. The two are the same value in dark by construction, so only light moves. #206 */
|
|
22
22
|
.ui-callout--success { background: var(--glow-green); }
|
|
23
|
-
.ui-callout--success .ui-callout__icon { color: var(--
|
|
23
|
+
.ui-callout--success .ui-callout__icon { color: var(--chip-success-ink); }
|
|
24
24
|
.ui-callout--warn { background: color-mix(in srgb, var(--amber) 13%, transparent); }
|
|
25
|
-
.ui-callout--warn .ui-callout__icon { color: var(--
|
|
25
|
+
.ui-callout--warn .ui-callout__icon { color: var(--chip-warn-ink); }
|
|
26
26
|
.ui-callout--danger { background: var(--glow-pink); }
|
|
27
|
-
.ui-callout--danger .ui-callout__icon { color: var(--
|
|
27
|
+
.ui-callout--danger .ui-callout__icon { color: var(--chip-danger-ink); }
|
|
28
28
|
|
|
29
29
|
/* ----------------------------------------------------------------------------
|
|
30
30
|
* Toast — a floating, dismissible notification.
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
color: var(--text);
|
|
43
43
|
font-size: var(--text-base);
|
|
44
44
|
width: 100%;
|
|
45
|
-
max-width:
|
|
45
|
+
max-width: var(--panel-md);
|
|
46
46
|
overflow: hidden; /* clips the timer bar + left marker to the rounded corners */
|
|
47
|
-
animation: ui-toast-in
|
|
47
|
+
animation: ui-toast-in var(--dur-med) var(--ease-out) both;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/* status → the paint tokens every style below consumes.
|
|
@@ -57,15 +57,15 @@
|
|
|
57
57
|
fill is: the accent is a graphic colour, sized for a 3px rule and a 22px
|
|
58
58
|
circle, and in the light theme it is not a colour text can be set in. The
|
|
59
59
|
trailing action is the one part of a toast that is BOTH the status colour and
|
|
60
|
-
a piece of text, so it takes the chip inks
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
a piece of text, so it takes the chip inks — the text-grade version of the
|
|
61
|
+
same five statuses, and the same value as the accent in dark by construction.
|
|
62
|
+
neutral's --toast-on is --signal-solid-ink because the neutral circle IS
|
|
63
|
+
--signal-solid-neutral: one fill, so one ink, chosen once. #206 */
|
|
64
64
|
.ui-toast--success { --toast-accent: var(--green); --toast-action-ink: var(--chip-success-ink); --toast-glow: var(--glow-green); --toast-on: var(--signal-contrast); --toast-solid: var(--signal-solid-success); }
|
|
65
65
|
.ui-toast--danger { --toast-accent: var(--pink); --toast-action-ink: var(--chip-danger-ink); --toast-glow: var(--glow-pink); --toast-on: var(--danger-contrast); --toast-solid: var(--signal-solid-danger); }
|
|
66
66
|
.ui-toast--warn { --toast-accent: var(--amber); --toast-action-ink: var(--chip-warn-ink); --toast-glow: color-mix(in srgb, var(--amber) 14%, transparent); --toast-on: var(--signal-contrast); --toast-solid: var(--signal-solid-warn); }
|
|
67
67
|
.ui-toast--info { --toast-accent: var(--cyan); --toast-action-ink: var(--chip-info-ink); --toast-glow: var(--glow-cyan); --toast-on: var(--signal-contrast); --toast-solid: var(--signal-solid-info); }
|
|
68
|
-
.ui-toast--neutral { --toast-accent: var(--muted); --toast-action-ink: var(--muted); --toast-glow: var(--surface-2); --toast-on: var(--
|
|
68
|
+
.ui-toast--neutral { --toast-accent: var(--muted); --toast-action-ink: var(--muted); --toast-glow: var(--surface-2); --toast-on: var(--signal-solid-ink); --toast-solid: var(--signal-solid-neutral); }
|
|
69
69
|
|
|
70
70
|
/* left status marker (soft + outline; solid is already a full fill) */
|
|
71
71
|
.ui-toast--soft::before,
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
.ui-toast__icon { width: 22px; height: 22px; flex: none; margin-top: 1px; display: grid; place-items: center; border-radius: 50%; background: var(--toast-accent); color: var(--toast-on); }
|
|
78
|
-
.ui-toast__icon svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2; }
|
|
78
|
+
.ui-toast__icon svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2.8; } /* 2.8 of a 24 box drawn at 13px = 1.52 CSS px. The circle has no room for a bigger glyph, so the weight comes out of the stroke. docs/specification.md#icons-and-glyphs */
|
|
79
79
|
.ui-toast__body { flex: 1; min-width: 0; }
|
|
80
80
|
.ui-toast__title { font-weight: var(--weight-medium); color: var(--strong); }
|
|
81
81
|
.ui-toast__text { color: var(--dim); }
|
|
@@ -91,13 +91,32 @@
|
|
|
91
91
|
.ui-toast__action { flex: none; align-self: center; background: none; border: 0; padding: 4px 9px; border-radius: var(--radius-xs); font: inherit; font-weight: var(--weight-medium); color: var(--toast-action-ink); cursor: pointer; }
|
|
92
92
|
.ui-toast__action:hover { background: color-mix(in srgb, var(--bg) 55%, transparent); }
|
|
93
93
|
|
|
94
|
-
.ui-toast__close { flex: none; align-self: flex-start; background: none; border: 0; color: var(--muted); cursor: pointer; padding: 2px; display: grid; place-items: center; }
|
|
95
|
-
.ui-toast__close svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width:
|
|
94
|
+
.ui-toast__close { flex: none; align-self: flex-start; background: none; border: 0; color: var(--muted); cursor: pointer; padding: 2px; display: grid; place-items: center; position: relative; }
|
|
95
|
+
.ui-toast__close svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.5; }
|
|
96
|
+
/* The target, not the ink. WCAG 2.5.8 measures what a pointer can land on, and
|
|
97
|
+
the drawn button is 19x19 — a 15px glyph in 2px of padding. Growing the box
|
|
98
|
+
to 24 would push the glyph 2.5px off the corner it is aligned to, so the
|
|
99
|
+
TARGET grows instead and the glyph does not move at all.
|
|
100
|
+
Centred, so the overhang is 2.5px on every side. It has 12px of flex gap to
|
|
101
|
+
.ui-toast__action beside it and 12px between stacked toasts, so it reaches no
|
|
102
|
+
neighbouring target; .ui-toast's overflow: hidden clips at 15px of padding,
|
|
103
|
+
well outside it. Measured, not asserted:
|
|
104
|
+
stories/guidelines/accessibility-floor.test.js reads this rule out of the
|
|
105
|
+
sheet and reports the target it makes. #219 */
|
|
106
|
+
.ui-toast__close::before {
|
|
107
|
+
content: '';
|
|
108
|
+
position: absolute;
|
|
109
|
+
left: 50%;
|
|
110
|
+
top: 50%;
|
|
111
|
+
width: 24px;
|
|
112
|
+
height: 24px;
|
|
113
|
+
transform: translate(-50%, -50%);
|
|
114
|
+
}
|
|
96
115
|
.ui-toast__close:hover { color: var(--text); }
|
|
97
116
|
|
|
98
117
|
/* auto-dismiss progress bar — .is-running is added by wireToastStack() */
|
|
99
118
|
.ui-toast__timer { position: absolute; left: 0; bottom: 0; height: 3px; width: 100%; background: var(--toast-accent); opacity: 0.85; transform-origin: left; }
|
|
100
|
-
.ui-toast__timer.is-running { animation: ui-toast-timer var(--toast-dur, 5s) linear forwards; }
|
|
119
|
+
.ui-toast__timer.is-running { animation: ui-toast-timer var(--toast-dur, 5s) linear forwards; /* motion: ambient — the bar spends the toast's dismiss timer, which the caller sets */ }
|
|
101
120
|
|
|
102
121
|
/* style: soft — tinted surface */
|
|
103
122
|
.ui-toast--soft { background: var(--toast-glow); box-shadow: var(--shadow-sm); }
|
|
@@ -142,7 +161,7 @@
|
|
|
142
161
|
@keyframes ui-toast-in { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }
|
|
143
162
|
@keyframes ui-toast-out { to { opacity: 0; transform: translateX(24px); } }
|
|
144
163
|
@keyframes ui-toast-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } }
|
|
145
|
-
.ui-toast.is-leaving { animation: ui-toast-out
|
|
164
|
+
.ui-toast.is-leaving { animation: ui-toast-out var(--dur-fast) var(--ease-sharp) forwards; }
|
|
146
165
|
|
|
147
166
|
@media (prefers-reduced-motion: reduce) {
|
|
148
167
|
.ui-toast, .ui-toast.is-leaving { animation: none; }
|
|
@@ -150,7 +169,7 @@
|
|
|
150
169
|
}
|
|
151
170
|
|
|
152
171
|
/* stack container for queued toasts */
|
|
153
|
-
.ui-toast-stack { display: flex; flex-direction: column; gap: 12px; width:
|
|
172
|
+
.ui-toast-stack { display: flex; flex-direction: column; gap: 12px; width: var(--panel-md); max-width: 100%; }
|
|
154
173
|
|
|
155
174
|
/* Success panel — centered confirmation, used after form submit */
|
|
156
175
|
.ui-success {
|
package/src/styles/card.css
CHANGED
package/src/styles/code.css
CHANGED
|
@@ -42,10 +42,16 @@
|
|
|
42
42
|
align-items: center;
|
|
43
43
|
gap: 6px;
|
|
44
44
|
padding: 2px 4px;
|
|
45
|
+
/* WCAG 2.5.8's 24 CSS px target floor, taken as a real box rather than as an
|
|
46
|
+
overlay: 2 + a 12px label at line-height 1.62 + 2 came to 23.44, so the
|
|
47
|
+
drawn button grows by 0.56px and the bar it sits in grows with it. Nothing
|
|
48
|
+
smaller than a rounding error moves, which is why this one is not a hit
|
|
49
|
+
area. Measured in stories/guidelines/accessibility-floor.test.js. #219 */
|
|
50
|
+
min-height: 24px;
|
|
45
51
|
transition: color var(--dur-fast) var(--ease);
|
|
46
52
|
}
|
|
47
53
|
.ui-snippet__copy:hover { color: var(--accent); }
|
|
48
|
-
.ui-snippet__copy svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width:
|
|
54
|
+
.ui-snippet__copy svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2.8; }
|
|
49
55
|
|
|
50
56
|
/* Shell token highlighting (matches viz/account.mjs classes) */
|
|
51
57
|
.ui-snippet pre .k { color: var(--purple-mid); font-weight: var(--weight-medium); }
|
package/src/styles/confirm.css
CHANGED
package/src/styles/drawer.css
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
--drawer-surface: var(--surface-2);
|
|
15
15
|
--drawer-shadow: var(--shadow-lg);
|
|
16
16
|
--drawer-scrim: var(--scrim);
|
|
17
|
-
--drawer-w:
|
|
17
|
+
--drawer-w: var(--panel-md); /* left|right panels: width */
|
|
18
18
|
--drawer-h: 45vh; /* top|bottom panels: height */
|
|
19
19
|
|
|
20
20
|
position: fixed;
|
|
@@ -102,9 +102,9 @@
|
|
|
102
102
|
|
|
103
103
|
/* -- Sizes ----------------------------------------------------------------- */
|
|
104
104
|
/* left|right → width; top|bottom → height. Same names, axis chosen by side. */
|
|
105
|
-
.ui-drawer--right.ui-drawer--sm, .ui-drawer--left.ui-drawer--sm { --drawer-w:
|
|
106
|
-
.ui-drawer--right.ui-drawer--md, .ui-drawer--left.ui-drawer--md { --drawer-w:
|
|
107
|
-
.ui-drawer--right.ui-drawer--lg, .ui-drawer--left.ui-drawer--lg { --drawer-w:
|
|
105
|
+
.ui-drawer--right.ui-drawer--sm, .ui-drawer--left.ui-drawer--sm { --drawer-w: var(--panel-sm); }
|
|
106
|
+
.ui-drawer--right.ui-drawer--md, .ui-drawer--left.ui-drawer--md { --drawer-w: var(--panel-md); }
|
|
107
|
+
.ui-drawer--right.ui-drawer--lg, .ui-drawer--left.ui-drawer--lg { --drawer-w: var(--panel-lg); }
|
|
108
108
|
.ui-drawer--top.ui-drawer--sm, .ui-drawer--bottom.ui-drawer--sm { --drawer-h: 30vh; }
|
|
109
109
|
.ui-drawer--top.ui-drawer--md, .ui-drawer--bottom.ui-drawer--md { --drawer-h: 45vh; }
|
|
110
110
|
.ui-drawer--top.ui-drawer--lg, .ui-drawer--bottom.ui-drawer--lg { --drawer-h: 72vh; }
|
|
@@ -138,11 +138,14 @@
|
|
|
138
138
|
background: transparent;
|
|
139
139
|
border: 1px solid transparent;
|
|
140
140
|
border-radius: var(--radius-sm);
|
|
141
|
-
transition:
|
|
141
|
+
transition:
|
|
142
|
+
color var(--dur-fast) var(--ease),
|
|
143
|
+
background var(--dur-fast) var(--ease),
|
|
144
|
+
border-color var(--dur-fast) var(--ease);
|
|
142
145
|
}
|
|
143
146
|
.ui-drawer__close:hover { color: var(--strong); background: var(--surface); }
|
|
144
147
|
.ui-drawer__close:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
|
|
145
|
-
.ui-drawer__close svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1
|
|
148
|
+
.ui-drawer__close svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.1; }
|
|
146
149
|
|
|
147
150
|
.ui-drawer__body {
|
|
148
151
|
flex: 1 1 auto;
|
package/src/styles/dropdown.css
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
border: 1px solid var(--border);
|
|
22
22
|
border-radius: var(--radius-pill);
|
|
23
23
|
padding: 7px 11px 7px 13px;
|
|
24
|
-
transition: border-color
|
|
24
|
+
transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
|
|
25
25
|
}
|
|
26
26
|
.ui-dropdown__trigger:hover { border-color: var(--accent); }
|
|
27
27
|
.ui-dropdown__trigger:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
border-right: 1.6px solid var(--muted);
|
|
35
35
|
border-bottom: 1.6px solid var(--muted);
|
|
36
36
|
transform: rotate(45deg) translateY(-1px);
|
|
37
|
-
transition: transform
|
|
37
|
+
transition: transform var(--dur-fast) var(--ease);
|
|
38
38
|
flex: none;
|
|
39
39
|
}
|
|
40
40
|
.ui-dropdown.open .ui-dropdown__chevron { transform: rotate(-135deg) translateY(2px); }
|
|
@@ -53,7 +53,12 @@
|
|
|
53
53
|
opacity: 0;
|
|
54
54
|
visibility: hidden;
|
|
55
55
|
transform: translateY(-6px);
|
|
56
|
-
transition:
|
|
56
|
+
transition:
|
|
57
|
+
opacity var(--dur-med) var(--ease),
|
|
58
|
+
transform var(--dur-med) var(--ease),
|
|
59
|
+
/* `linear`, not var(--ease): visibility is discrete, so easing it buys nothing and a
|
|
60
|
+
curve leaving [0, 1] flips it mid-fade. Same rule the drawer states at length. */
|
|
61
|
+
visibility var(--dur-med) linear;
|
|
57
62
|
z-index: var(--z-dropdown);
|
|
58
63
|
}
|
|
59
64
|
.ui-dropdown__panel.is-end { left: auto; right: 0; }
|
|
@@ -70,7 +75,7 @@
|
|
|
70
75
|
cursor: pointer;
|
|
71
76
|
text-decoration: none;
|
|
72
77
|
color: var(--text);
|
|
73
|
-
transition: background
|
|
78
|
+
transition: background var(--dur-fast) var(--ease);
|
|
74
79
|
}
|
|
75
80
|
.ui-dropdown__item:hover { background: var(--surface); }
|
|
76
81
|
.ui-dropdown__item:focus-visible { outline: none; background: var(--surface); box-shadow: var(--ring); }
|
|
@@ -78,11 +83,11 @@
|
|
|
78
83
|
.ui-dropdown__label { font-family: var(--font-sans); font-size: 12.5px; color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
|
|
79
84
|
.ui-dropdown__desc { font-size: 11px; color: var(--muted); line-height: 1.35; }
|
|
80
85
|
.ui-dropdown__ic { display: inline-flex; flex: none; color: var(--muted); }
|
|
81
|
-
.ui-dropdown__ic svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1
|
|
86
|
+
.ui-dropdown__ic svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.1; }
|
|
82
87
|
|
|
83
88
|
/* Trailing selected tick (listbox variant) */
|
|
84
89
|
.ui-dropdown__tick { margin-left: auto; flex: none; display: none; color: var(--accent); }
|
|
85
|
-
.ui-dropdown__tick svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2.
|
|
90
|
+
.ui-dropdown__tick svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2.4; }
|
|
86
91
|
.ui-dropdown__item.is-selected .ui-dropdown__tick { display: inline-flex; }
|
|
87
92
|
.ui-dropdown__item.is-selected .ui-dropdown__label { color: var(--accent); }
|
|
88
93
|
|
|
@@ -106,7 +111,9 @@
|
|
|
106
111
|
.ui-dropdown__badge.is-accent { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
|
|
107
112
|
|
|
108
113
|
/* Disabled + danger rows */
|
|
109
|
-
.ui-dropdown__item.is-disabled {
|
|
114
|
+
.ui-dropdown__item.is-disabled { color: var(--disabled-ink); cursor: not-allowed; pointer-events: none; }
|
|
115
|
+
.ui-dropdown__item.is-disabled .ui-dropdown__label,
|
|
116
|
+
.ui-dropdown__item.is-disabled .ui-dropdown__desc { color: inherit; }
|
|
110
117
|
.ui-dropdown__item.is-danger .ui-dropdown__label { color: var(--muted); }
|
|
111
118
|
.ui-dropdown__item.is-danger:hover .ui-dropdown__label { color: var(--pink); }
|
|
112
119
|
|
package/src/styles/empty.css
CHANGED
package/src/styles/feedback.css
CHANGED
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
box-shadow: 0 10px 26px var(--ui-fb-pill-glow),
|
|
28
28
|
0 2px 6px color-mix(in srgb, var(--shadow-ink) 28%, transparent),
|
|
29
29
|
inset 0 1px 0 color-mix(in srgb, var(--sheen) 28%, transparent);
|
|
30
|
-
transition:
|
|
30
|
+
transition:
|
|
31
|
+
transform var(--dur-fast) var(--ease-spring),
|
|
32
|
+
opacity var(--dur-fast) var(--ease),
|
|
33
|
+
box-shadow var(--dur-fast) var(--ease);
|
|
31
34
|
}
|
|
32
35
|
.ui-fbpill.show { opacity: 1; pointer-events: auto; transform: translate(-50%, -100%) translateY(-9px) scale(1); }
|
|
33
36
|
.ui-fbpill:hover {
|
|
@@ -37,19 +40,19 @@
|
|
|
37
40
|
}
|
|
38
41
|
.ui-fbpill.show:hover { transform: translate(-50%, -100%) translateY(-12px) scale(1.04); }
|
|
39
42
|
.ui-fbpill:active { transform: translate(-50%, -100%) translateY(-8px) scale(.98); }
|
|
40
|
-
.ui-fbpill svg { width: 16px; height: 16px; color: currentColor; filter: drop-shadow(0 1px 1px color-mix(in srgb, var(--shadow-ink) 25%, transparent)); }
|
|
43
|
+
.ui-fbpill svg { width: 16px; height: 16px; stroke-width: 2.4; color: currentColor; filter: drop-shadow(0 1px 1px color-mix(in srgb, var(--shadow-ink) 25%, transparent)); }
|
|
41
44
|
.ui-fbpill::before {
|
|
42
45
|
content: ""; position: absolute; top: 0; bottom: 0; left: -60%; width: 45%;
|
|
43
46
|
background: linear-gradient(100deg, transparent, color-mix(in srgb, var(--sheen) 55%, transparent), transparent); transform: skewX(-18deg);
|
|
44
47
|
}
|
|
45
|
-
.ui-fbpill.show::before { animation: uiFbSheen 1.15s ease .12s 1; }
|
|
46
|
-
.ui-fbpill:hover::before { animation: uiFbSheen .7s ease 1; }
|
|
48
|
+
.ui-fbpill.show::before { animation: uiFbSheen 1.15s ease .12s 1; } /* motion: ambient — a decorative sheen crossing the pill; its length is the width it travels, not a response */
|
|
49
|
+
.ui-fbpill:hover::before { animation: uiFbSheen .7s ease 1; } /* motion: ambient — the same sweep, quicker on hover so it finishes inside the gesture */
|
|
47
50
|
@keyframes uiFbSheen { 0% { left: -60%; } 100% { left: 130%; } }
|
|
48
51
|
|
|
49
52
|
/* Scrim behind the open composer */
|
|
50
53
|
.ui-fbscrim {
|
|
51
54
|
position: fixed; inset: 0; z-index: 70; background: var(--scrim);
|
|
52
|
-
opacity: 0; pointer-events: none; transition: opacity
|
|
55
|
+
opacity: 0; pointer-events: none; transition: opacity var(--dur-med) var(--ease);
|
|
53
56
|
}
|
|
54
57
|
.ui-fbscrim.show { opacity: 1; pointer-events: auto; }
|
|
55
58
|
|
|
@@ -58,47 +61,53 @@
|
|
|
58
61
|
position: fixed; z-index: 71; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(14px);
|
|
59
62
|
width: min(520px, calc(100vw - 32px)); background: var(--bg-elevated); border-radius: 18px;
|
|
60
63
|
box-shadow: var(--shadow-lg); opacity: 0; pointer-events: none;
|
|
61
|
-
transition: opacity
|
|
64
|
+
transition: opacity var(--dur-med) var(--ease), transform var(--dur-med) var(--ease); overflow: hidden;
|
|
62
65
|
}
|
|
63
66
|
.ui-fbcomposer.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
|
|
64
67
|
|
|
65
68
|
.ui-fbc__head { display: flex; align-items: center; gap: 12px; padding: 17px 20px 0; }
|
|
66
69
|
.ui-fbc__chip { display: inline-flex; align-items: center; gap: 8px; color: var(--accent); font-size: 13px; font-weight: var(--weight-semibold); letter-spacing: .01em; white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
67
|
-
.ui-fbc__chip svg { width: 14px; height: 14px; flex: none; opacity: .85; }
|
|
70
|
+
.ui-fbc__chip svg { width: 14px; height: 14px; flex: none; opacity: .85; stroke-width: 2.7; }
|
|
68
71
|
.ui-fbc__x { margin-left: auto; background: var(--surface-2); color: var(--dim); border: 0; width: 28px; height: 28px; border-radius: 8px; cursor: pointer; display: grid; place-items: center; flex: none; }
|
|
69
72
|
.ui-fbc__x:hover { color: var(--strong); }
|
|
70
|
-
.ui-fbc__x svg { width: 14px; height: 14px; }
|
|
73
|
+
.ui-fbc__x svg { width: 14px; height: 14px; stroke-width: 2.7; }
|
|
71
74
|
.ui-fbc__quote { margin: 14px 20px 0; background: var(--surface-2); border-radius: 12px; padding: 12px 15px; display: flex; gap: 11px; }
|
|
72
75
|
.ui-fbc__qm { color: var(--accent); font-size: 22px; font-weight: var(--weight-bold); line-height: .9; flex: none; opacity: .55; }
|
|
73
76
|
.ui-fbc__quote q { quotes: none; color: var(--dim); font-style: italic; font-size: 13.5px; line-height: 1.55; }
|
|
74
77
|
.ui-fbc__body { padding: 14px 20px 0; }
|
|
75
78
|
.ui-fbcomposer textarea {
|
|
76
79
|
width: 100%; min-height: 96px; resize: vertical; font-family: var(--font-sans); font-size: 14.5px; line-height: 1.55;
|
|
77
|
-
color: var(--strong); background: var(--surface-2); border: 0; border-radius: 12px; padding: 13px 15px; transition: box-shadow
|
|
80
|
+
color: var(--strong); background: var(--surface-2); border: 0; border-radius: 12px; padding: 13px 15px; transition: box-shadow var(--dur-fast) var(--ease);
|
|
78
81
|
}
|
|
79
82
|
.ui-fbcomposer textarea::placeholder { color: var(--muted); }
|
|
80
83
|
.ui-fbcomposer textarea:focus { outline: none; box-shadow: 0 0 0 2px var(--accent); }
|
|
81
84
|
.ui-fbc__foot { display: flex; align-items: center; justify-content: flex-end; gap: 12px; padding: 16px 20px 18px; }
|
|
82
85
|
|
|
83
|
-
.ui-fbbtn { font-family: var(--font-sans); font-size: 13.5px; font-weight: var(--weight-semibold); border-radius: 10px; padding: 9px 17px; cursor: pointer; border: 0; transition: filter
|
|
86
|
+
.ui-fbbtn { font-family: var(--font-sans); font-size: 13.5px; font-weight: var(--weight-semibold); border-radius: 10px; padding: 9px 17px; cursor: pointer; border: 0; transition: filter var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
|
|
84
87
|
.ui-fbbtn.ghost { background: transparent; color: var(--dim); }
|
|
85
88
|
.ui-fbbtn.ghost:hover { color: var(--strong); background: var(--surface-2); }
|
|
86
89
|
.ui-fbbtn.primary { background: var(--accent-strong); color: var(--accent-contrast); display: inline-flex; align-items: center; gap: 9px; }
|
|
87
90
|
.ui-fbbtn.primary:hover { filter: brightness(1.08); }
|
|
88
|
-
.ui-
|
|
91
|
+
/* Same flat pair as .ui-btn:disabled, for the same reason (#220). */
|
|
92
|
+
.ui-fbbtn.primary:disabled {
|
|
93
|
+
background: var(--disabled-surface);
|
|
94
|
+
color: var(--disabled-ink);
|
|
95
|
+
cursor: default;
|
|
96
|
+
filter: none;
|
|
97
|
+
}
|
|
89
98
|
.ui-fbc__send { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
|
|
90
|
-
.ui-fbspin { flex: none; width: 14px; height: 14px; box-sizing: border-box; border-radius: 50%; border: 2px solid color-mix(in srgb, currentColor 40%, transparent); border-top-color: currentColor; animation: uiFbSpin .7s linear infinite; }
|
|
99
|
+
.ui-fbspin { flex: none; width: 14px; height: 14px; box-sizing: border-box; border-radius: 50%; border: 2px solid color-mix(in srgb, currentColor 40%, transparent); border-top-color: currentColor; animation: uiFbSpin .7s linear infinite; } /* motion: ambient — a spinner turns until the request answers */
|
|
91
100
|
@keyframes uiFbSpin { to { transform: rotate(360deg); } }
|
|
92
101
|
|
|
93
102
|
/* Success state */
|
|
94
103
|
.ui-fbc__done { padding: 30px 26px 24px; text-align: center; background: radial-gradient(112% 60% at 50% 0%, var(--glow-green), transparent 62%); }
|
|
95
104
|
.ui-fbc__ck { width: 88px; height: 88px; margin: 0 auto 13px; display: grid; place-items: center; }
|
|
96
|
-
.ui-fbck { width: 88px; height: 88px; overflow: visible; display: block; transform-origin: center; animation: uiFbCkSpring 1s cubic-bezier(.22,1,.3,1) both; }
|
|
105
|
+
.ui-fbck { width: 88px; height: 88px; overflow: visible; display: block; transform-origin: center; animation: uiFbCkSpring 1s cubic-bezier(.22,1,.3,1) both; } /* motion: choreographed — the frame the mark and the comet are timed against */
|
|
97
106
|
.ui-fbck-t { fill: none; stroke: var(--surface-2); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; }
|
|
98
|
-
.ui-fbck-m { fill: none; stroke: var(--green); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 118; stroke-dashoffset: 118; animation: uiFbCkDraw .6s cubic-bezier(.5,0,.3,1) .1s forwards; }
|
|
99
|
-
.ui-fbck-s { fill: none; stroke: var(--sheen); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 15 400; stroke-dashoffset: 133; filter: drop-shadow(0 0 8px var(--green)) drop-shadow(0 0 15px var(--green)); opacity: 0; animation: uiFbCkComet .6s cubic-bezier(.5,0,.3,1) .1s forwards; }
|
|
107
|
+
.ui-fbck-m { fill: none; stroke: var(--green); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 118; stroke-dashoffset: 118; animation: uiFbCkDraw .6s cubic-bezier(.5,0,.3,1) .1s forwards; /* motion: choreographed — the mark draws while the spring is still settling */ }
|
|
108
|
+
.ui-fbck-s { fill: none; stroke: var(--sheen); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 15 400; stroke-dashoffset: 133; filter: drop-shadow(0 0 8px var(--green)) drop-shadow(0 0 15px var(--green)); opacity: 0; animation: uiFbCkComet .6s cubic-bezier(.5,0,.3,1) .1s forwards; /* motion: choreographed — the comet rides the mark, so it shares its duration and delay exactly */ }
|
|
100
109
|
.ui-fbc__done h4 { color: var(--strong); font-size: 19px; font-weight: var(--weight-semibold); letter-spacing: -.01em; margin-bottom: 7px; }
|
|
101
|
-
.ui-fbc__done p { color: var(--dim); font-size: 13.5px; line-height: 1.55; max-width:
|
|
110
|
+
.ui-fbc__done p { color: var(--dim); font-size: 13.5px; line-height: 1.55; max-width: var(--prose-caption); margin: 0 auto; }
|
|
102
111
|
.ui-fbc__done p b { color: var(--text); font-weight: var(--weight-semibold); }
|
|
103
112
|
.ui-fbc__done .row { margin-top: 18px; display: flex; gap: 10px; justify-content: center; }
|
|
104
113
|
@keyframes uiFbCkDraw { to { stroke-dashoffset: 0; } }
|
package/src/styles/footer.css
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
.ui-footer__in {
|
|
23
23
|
width: 100%;
|
|
24
|
-
max-width:
|
|
24
|
+
max-width: var(--container);
|
|
25
25
|
margin: 0 auto;
|
|
26
26
|
padding: 40px clamp(14px, 2.4vw, 26px) 30px;
|
|
27
27
|
}
|
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
flex-wrap: wrap;
|
|
35
35
|
padding-bottom: 30px;
|
|
36
36
|
}
|
|
37
|
+
/* Under --panel-sm, and deliberately: this is a flex TRACK in a wrapping
|
|
38
|
+
row, not a panel and not a paragraph — its width is what decides when the
|
|
39
|
+
footer breaks into columns, so it answers to the row rather than to a
|
|
40
|
+
scale. The one width below the floor the measure gate draws. See #208. */
|
|
37
41
|
.ui-footer__brand { max-width: 300px; }
|
|
38
42
|
.ui-footer .ui-footer__brand .brand { font-size: 15px; }
|
|
39
43
|
.ui-footer__tagline {
|
|
@@ -52,7 +56,10 @@
|
|
|
52
56
|
border: 1px solid var(--footer-border);
|
|
53
57
|
color: var(--footer-link);
|
|
54
58
|
background: var(--surface);
|
|
55
|
-
transition:
|
|
59
|
+
transition:
|
|
60
|
+
color var(--dur-fast) var(--ease),
|
|
61
|
+
border-color var(--dur-fast) var(--ease),
|
|
62
|
+
transform var(--dur-fast) var(--ease);
|
|
56
63
|
}
|
|
57
64
|
.ui-footer__social:hover { color: var(--accent); border-color: var(--accent); transform: translateY(-1px); }
|
|
58
65
|
.ui-footer__social svg { width: 17px; height: 17px; display: block; }
|
|
@@ -76,7 +83,7 @@
|
|
|
76
83
|
font-size: 13.5px;
|
|
77
84
|
color: var(--footer-link);
|
|
78
85
|
text-decoration: none;
|
|
79
|
-
transition: color
|
|
86
|
+
transition: color var(--dur-fast) var(--ease);
|
|
80
87
|
}
|
|
81
88
|
.ui-footer__links a:hover { color: var(--footer-link-hover); }
|
|
82
89
|
|
|
@@ -94,7 +101,7 @@
|
|
|
94
101
|
}
|
|
95
102
|
.ui-footer__legal { color: var(--footer-link); }
|
|
96
103
|
.ui-footer__legal-links { display: flex; gap: 22px; flex-wrap: wrap; }
|
|
97
|
-
.ui-footer__legal-links a { color: var(--footer-link); text-decoration: none; transition: color
|
|
104
|
+
.ui-footer__legal-links a { color: var(--footer-link); text-decoration: none; transition: color var(--dur-fast) var(--ease); }
|
|
98
105
|
.ui-footer__legal-links a:hover { color: var(--footer-link-hover); }
|
|
99
106
|
.ui-footer__switcher { display: inline-flex; align-items: center; gap: 12px; margin-left: auto; }
|
|
100
107
|
|
|
@@ -110,13 +117,15 @@
|
|
|
110
117
|
.ui-footer--app .ui-footer__in { max-width: none; padding: 12px clamp(14px, 2.4vw, 22px); }
|
|
111
118
|
.ui-footer--app .ui-footer__bar { padding-top: 0; border-top: 0; font-size: 12.5px; }
|
|
112
119
|
|
|
113
|
-
/* Responsive — columns collapse to a stacked layout on narrow screens.
|
|
120
|
+
/* Responsive — columns collapse to a stacked layout on narrow screens. Two of
|
|
121
|
+
the kit's three steps, and the second one is the phone step rather than a
|
|
122
|
+
fourth value of the footer's own: docs/specification.md#breakpoints. */
|
|
114
123
|
@media (max-width: 720px) {
|
|
115
124
|
.ui-footer__top { flex-direction: column; gap: 30px; }
|
|
116
125
|
.ui-footer__nav { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px 30px; }
|
|
117
126
|
.ui-footer__brand { max-width: none; }
|
|
118
127
|
}
|
|
119
|
-
@media (max-width:
|
|
128
|
+
@media (max-width: 560px) {
|
|
120
129
|
.ui-footer__nav { grid-template-columns: 1fr; }
|
|
121
130
|
.ui-footer__bar { flex-direction: column; align-items: flex-start; }
|
|
122
131
|
.ui-footer__switcher { margin-left: 0; }
|
package/src/styles/input.css
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
.ui-field__req { color: var(--pink); margin-left: 4px; }
|
|
16
16
|
.ui-field__hint { color: var(--muted); font-size: var(--text-sm); margin-top: 7px; line-height: 1.5; }
|
|
17
17
|
.ui-field__error { color: var(--pink); font-size: var(--text-sm); margin-top: 7px; display: flex; align-items: center; gap: 6px; }
|
|
18
|
+
.ui-field__error svg { width: 16px; height: 16px; flex: none; stroke-width: 2.4; }
|
|
18
19
|
|
|
19
20
|
.ui-input,
|
|
20
21
|
.ui-textarea,
|
|
@@ -46,8 +47,15 @@
|
|
|
46
47
|
.ui-input::placeholder,
|
|
47
48
|
.ui-textarea::placeholder { color: var(--muted); opacity: 0.9; }
|
|
48
49
|
|
|
50
|
+
/* The value a reader typed is still the value; it goes quiet, it does not fade
|
|
51
|
+
into the field under it the way an opacity would take both down together. #220 */
|
|
49
52
|
.ui-input:disabled,
|
|
50
|
-
.ui-textarea:disabled {
|
|
53
|
+
.ui-textarea:disabled {
|
|
54
|
+
background: var(--disabled-surface);
|
|
55
|
+
border-color: var(--disabled-border);
|
|
56
|
+
color: var(--disabled-ink);
|
|
57
|
+
cursor: not-allowed;
|
|
58
|
+
}
|
|
51
59
|
|
|
52
60
|
.ui-input.is-invalid,
|
|
53
61
|
.ui-textarea.is-invalid { border-color: var(--pink); }
|
|
@@ -64,7 +72,7 @@
|
|
|
64
72
|
color: var(--muted);
|
|
65
73
|
pointer-events: none;
|
|
66
74
|
}
|
|
67
|
-
.ui-input-group__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width:
|
|
75
|
+
.ui-input-group__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 2.2; }
|
|
68
76
|
|
|
69
77
|
.ui-select {
|
|
70
78
|
appearance: none;
|
|
@@ -98,6 +106,24 @@
|
|
|
98
106
|
transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
|
|
99
107
|
position: relative;
|
|
100
108
|
}
|
|
109
|
+
/* The target, not the ink. The box above is drawn at 19x19 because that is what
|
|
110
|
+
sits beside a 15px label without shouting; WCAG 2.5.8 wants 24 CSS px of
|
|
111
|
+
pointer target, which is a different question. This overlay answers it and
|
|
112
|
+
the checkbox keeps its size. ::after is the tick and the dot, so the target
|
|
113
|
+
is ::before — it paints first, under both, and is transparent anyway.
|
|
114
|
+
Centred, so the overhang is 2.5px on every side: 11px of gap to the label
|
|
115
|
+
inside the same <label> (which toggles the same input, so an overlap there
|
|
116
|
+
would cost nothing) and 14px between stacked rows in the specimen. Measured
|
|
117
|
+
in stories/guidelines/accessibility-floor.test.js. #219 */
|
|
118
|
+
.ui-check input::before {
|
|
119
|
+
content: '';
|
|
120
|
+
position: absolute;
|
|
121
|
+
left: 50%;
|
|
122
|
+
top: 50%;
|
|
123
|
+
width: 24px;
|
|
124
|
+
height: 24px;
|
|
125
|
+
transform: translate(-50%, -50%);
|
|
126
|
+
}
|
|
101
127
|
.ui-check input[type="radio"] { border-radius: 50%; }
|
|
102
128
|
.ui-check input:hover { border-color: var(--accent); }
|
|
103
129
|
.ui-check input:checked { background: var(--accent); border-color: var(--accent); }
|
package/src/styles/layout.css
CHANGED
|
@@ -114,12 +114,14 @@
|
|
|
114
114
|
The width is what keeps the auto margins honest: an auto inline margin takes
|
|
115
115
|
a grid item out of stretch, and Chrome then lays it out at its max-content
|
|
116
116
|
width, which is how the payout ledger pushed the page 529px sideways at 375.
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
--measure is the ONE source for this width now. shell.js used to carry its
|
|
118
|
+
own copy of the number and a test held the two in step; it now omits the
|
|
119
|
+
property rather than writing a default, so an absent or unparseable maxWidth
|
|
120
|
+
lands here, on the token, instead of on a second literal. See issue #198. */
|
|
119
121
|
.ui-app__main {
|
|
120
122
|
min-width: 0;
|
|
121
123
|
width: 100%;
|
|
122
|
-
max-width: var(--ui-app-main,
|
|
124
|
+
max-width: var(--ui-app-main, var(--measure));
|
|
123
125
|
margin-inline: auto;
|
|
124
126
|
padding: 40px clamp(24px, 4vw, 56px) 72px;
|
|
125
127
|
}
|
|
@@ -134,7 +136,7 @@
|
|
|
134
136
|
letter-spacing: var(--tracking-tight);
|
|
135
137
|
margin-bottom: 8px;
|
|
136
138
|
}
|
|
137
|
-
.ui-app__sub { color: var(--dim); font-size: 15px; line-height: 1.6; max-width:
|
|
139
|
+
.ui-app__sub { color: var(--dim); font-size: 15px; line-height: 1.6; max-width: var(--prose-body); margin-bottom: 32px; }
|
|
138
140
|
.ui-app__body { display: flex; flex-direction: column; gap: var(--space-5); }
|
|
139
141
|
/* A flex child's min-width is its content by default, so one wide block — a
|
|
140
142
|
ledger, a code sample — pushes the whole page sideways instead of scrolling
|
|
@@ -219,7 +221,7 @@
|
|
|
219
221
|
position: relative;
|
|
220
222
|
z-index: 1;
|
|
221
223
|
width: 100%;
|
|
222
|
-
max-width:
|
|
224
|
+
max-width: var(--panel-md);
|
|
223
225
|
background: var(--surface);
|
|
224
226
|
border-radius: 22px;
|
|
225
227
|
padding: 38px 36px 32px;
|
|
@@ -245,7 +247,7 @@
|
|
|
245
247
|
background: var(--glow-purple);
|
|
246
248
|
color: var(--accent);
|
|
247
249
|
}
|
|
248
|
-
.ui-scope__icon svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.
|
|
250
|
+
.ui-scope__icon svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.8; }
|
|
249
251
|
.ui-scope__t { color: var(--strong); font-size: 14px; font-weight: 500; }
|
|
250
252
|
.ui-scope__d { color: var(--dim); font-size: 13px; margin-top: 2px; line-height: 1.5; }
|
|
251
253
|
|
|
@@ -275,7 +277,7 @@
|
|
|
275
277
|
letter-spacing: -0.025em;
|
|
276
278
|
font-weight: 700;
|
|
277
279
|
color: var(--strong);
|
|
278
|
-
max-width:
|
|
280
|
+
max-width: var(--prose-display);
|
|
279
281
|
margin: 0 auto 22px;
|
|
280
282
|
}
|
|
281
283
|
.ui-hero__title .grad {
|
|
@@ -287,7 +289,7 @@
|
|
|
287
289
|
.ui-hero__sub {
|
|
288
290
|
font-size: clamp(16px, 2.1vw, 20px);
|
|
289
291
|
color: var(--dim);
|
|
290
|
-
max-width:
|
|
292
|
+
max-width: var(--prose-lede);
|
|
291
293
|
margin: 0 auto 34px;
|
|
292
294
|
line-height: 1.6;
|
|
293
295
|
}
|
|
@@ -298,7 +300,9 @@
|
|
|
298
300
|
display: grid;
|
|
299
301
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
300
302
|
gap: 20px;
|
|
301
|
-
|
|
303
|
+
/* Was 1080px — a third page-scale number with nothing arguing for it. The
|
|
304
|
+
feature row is a top-level band and lines up with the page it sits on. */
|
|
305
|
+
max-width: var(--container);
|
|
302
306
|
margin: 0 auto;
|
|
303
307
|
padding: 0 26px;
|
|
304
308
|
}
|
|
@@ -314,6 +318,6 @@
|
|
|
314
318
|
.ui-feature h3 { color: var(--strong); font-size: 17px; font-weight: 600; margin-bottom: 7px; }
|
|
315
319
|
.ui-feature p { color: var(--dim); font-size: 14.5px; line-height: 1.6; }
|
|
316
320
|
|
|
317
|
-
.ui-section-head { text-align: center; max-width:
|
|
321
|
+
.ui-section-head { text-align: center; max-width: var(--panel-lg); margin: 0 auto clamp(40px, 6vh, 64px); }
|
|
318
322
|
.ui-section-head h2 { color: var(--strong); font-size: clamp(28px, 4vw, 40px); font-weight: 700; letter-spacing: -0.02em; margin-bottom: 14px; }
|
|
319
323
|
.ui-section-head p { color: var(--dim); font-size: 17px; line-height: 1.6; }
|