@evinvest/uikit 0.23.0 → 0.25.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/README.md +151 -4
- package/dist/components/app-shell.js +85 -0
- package/dist/components/app-shell.js.map +1 -0
- package/dist/components/bottom-tab-bar.js +91 -0
- package/dist/components/bottom-tab-bar.js.map +1 -0
- package/dist/components/mobile-app-bar.js +51 -0
- package/dist/components/mobile-app-bar.js.map +1 -0
- package/dist/components/nav-badge.js +61 -0
- package/dist/components/nav-badge.js.map +1 -0
- package/dist/components/nav-link.js +50 -0
- package/dist/components/nav-link.js.map +1 -0
- package/dist/components/nav-model.js +55 -0
- package/dist/components/nav-model.js.map +1 -0
- package/dist/components/page-frame.js +92 -0
- package/dist/components/page-frame.js.map +1 -0
- package/dist/components/resource-error.js +47 -0
- package/dist/components/resource-error.js.map +1 -0
- package/dist/components/section-nav.js +86 -0
- package/dist/components/section-nav.js.map +1 -0
- package/dist/components/settled.js +19 -0
- package/dist/components/settled.js.map +1 -0
- package/dist/components/shell-icons.js +57 -0
- package/dist/components/shell-icons.js.map +1 -0
- package/dist/components/shell-nav.js +141 -0
- package/dist/components/shell-nav.js.map +1 -0
- package/dist/components/system-banner.js +53 -0
- package/dist/components/system-banner.js.map +1 -0
- package/dist/components/use-shell-nav.js +46 -0
- package/dist/components/use-shell-nav.js.map +1 -0
- package/dist/generated/button.js +4 -2
- package/dist/generated/button.js.map +1 -1
- package/dist/index.d.ts +421 -1
- package/dist/index.js +12 -0
- package/package.json +1 -1
- package/styles/theme.css +155 -1
- package/styles/tokens.css +157 -2
package/styles/theme.css
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* AUTO-GENERATED by `nix run .#gen` from the repo-root theme.css — do not edit. */
|
|
2
2
|
/*
|
|
3
|
-
* Toast (sonner) and Drawer enter/exit motion,
|
|
3
|
+
* Toast (sonner) and Drawer enter/exit motion, the entrances (rise / stagger / settled /
|
|
4
|
+
* skeleton) and the app-shell markers, shared by both ports. Imported by the token
|
|
4
5
|
* sheets so the lifecycle ships with the theme contract.
|
|
5
6
|
*/
|
|
6
7
|
|
|
@@ -306,6 +307,139 @@
|
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
|
|
310
|
+
/* ── Entrances: rise, stagger, settled, skeleton ─────────────────────────────
|
|
311
|
+
One keyframe for every "arriving" thing, driven by attributes so a consumer
|
|
312
|
+
without a motion library (and a Server Component) gets it for free:
|
|
313
|
+
|
|
314
|
+
data-enter="rise" the element fades in and rises --ev-rise, once, on
|
|
315
|
+
insertion; --enter-delay postpones it.
|
|
316
|
+
data-enter="stagger" each direct child does the same, one --enter-step
|
|
317
|
+
(default --ev-stagger) after the previous. The index
|
|
318
|
+
comes from nth-child; set --enter-index inline to
|
|
319
|
+
override it. Capped at 12 — past that the tail is
|
|
320
|
+
still arriving after the head has been read.
|
|
321
|
+
[data-slot="settled"][data-state="revealed"]
|
|
322
|
+
a skeleton → content handover (uikit <Settled>).
|
|
323
|
+
|
|
324
|
+
It animates the independent `translate` property, not `transform`, so it
|
|
325
|
+
composes with any transform the element already has. `backwards` holds the
|
|
326
|
+
start frame through the delay and then lets go, so nothing about the element
|
|
327
|
+
(stacking, containing block for `fixed` children) lingers after it lands.
|
|
328
|
+
Reduced motion keeps the fade and drops the travel. */
|
|
329
|
+
@keyframes ev-enter-rise {
|
|
330
|
+
from {
|
|
331
|
+
opacity: 0;
|
|
332
|
+
translate: 0 var(--ev-rise, 8px);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
[data-enter="rise"],
|
|
336
|
+
[data-enter="stagger"] > *,
|
|
337
|
+
[data-slot="settled"][data-state="revealed"] {
|
|
338
|
+
animation: ev-enter-rise var(--ev-dur-base, 280ms) var(--ev-ease-out, ease-out)
|
|
339
|
+
var(--enter-delay, 0ms) backwards;
|
|
340
|
+
}
|
|
341
|
+
[data-enter="stagger"] > * {
|
|
342
|
+
animation-delay: calc(
|
|
343
|
+
var(--enter-delay, 0ms) + var(--enter-index, 0) * var(--enter-step, var(--ev-stagger, 35ms))
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
[data-enter="stagger"] > :nth-child(2) { --enter-index: 1; }
|
|
347
|
+
[data-enter="stagger"] > :nth-child(3) { --enter-index: 2; }
|
|
348
|
+
[data-enter="stagger"] > :nth-child(4) { --enter-index: 3; }
|
|
349
|
+
[data-enter="stagger"] > :nth-child(5) { --enter-index: 4; }
|
|
350
|
+
[data-enter="stagger"] > :nth-child(6) { --enter-index: 5; }
|
|
351
|
+
[data-enter="stagger"] > :nth-child(7) { --enter-index: 6; }
|
|
352
|
+
[data-enter="stagger"] > :nth-child(8) { --enter-index: 7; }
|
|
353
|
+
[data-enter="stagger"] > :nth-child(9) { --enter-index: 8; }
|
|
354
|
+
[data-enter="stagger"] > :nth-child(10) { --enter-index: 9; }
|
|
355
|
+
[data-enter="stagger"] > :nth-child(11) { --enter-index: 10; }
|
|
356
|
+
[data-enter="stagger"] > :nth-child(n + 12) { --enter-index: 11; }
|
|
357
|
+
|
|
358
|
+
/* Every skeleton fades in before it starts to pulse. Keyed on the pulse class as
|
|
359
|
+
well as the slot: a running animation beats a transition on the same property,
|
|
360
|
+
so the entrance has to be the first of two animations rather than anything
|
|
361
|
+
layered beside `animate-pulse` — and a caller's `animate-none` (which `cn`
|
|
362
|
+
merges over the pulse) removes the class and with it this rule. Reduced motion
|
|
363
|
+
keeps the pulse, the signal that work is in flight, and drops the entrance. */
|
|
364
|
+
@keyframes ev-skeleton-in {
|
|
365
|
+
from {
|
|
366
|
+
opacity: 0;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
@keyframes ev-skeleton-pulse {
|
|
370
|
+
50% {
|
|
371
|
+
opacity: 0.5;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
[data-slot="skeleton"].animate-pulse {
|
|
375
|
+
animation:
|
|
376
|
+
ev-skeleton-in 200ms var(--ev-ease-out, ease-out),
|
|
377
|
+
ev-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) 200ms infinite;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* ── App shell: the rail's active-row marker and the tab bar's rule ──────────
|
|
381
|
+
ShellNav: one marker node per nav section, placed on the [aria-current] row by
|
|
382
|
+
measurement and moved by a `transform` transition — on the compositor, so it
|
|
383
|
+
keeps its frames while the main thread renders the page just navigated to (a
|
|
384
|
+
JS layout projection here dropped exactly those). Until the first placement
|
|
385
|
+
(SSR, every frame to hydration) the marker has no position: it stays hidden
|
|
386
|
+
and the marked row carries the fill itself, so the first paint is right.
|
|
387
|
+
`data-placed` flips both at once; `entered` is a marker that mounted into a
|
|
388
|
+
section already on screen (a move across sections — a fade), `first` one that
|
|
389
|
+
arrived with its section (a cut: fading under a row whose own fill fades out
|
|
390
|
+
would dip). */
|
|
391
|
+
@keyframes ev-shell-fade-in {
|
|
392
|
+
from {
|
|
393
|
+
opacity: 0;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
[data-slot="shell-nav-marker"] {
|
|
397
|
+
transition: transform var(--ev-dur-base, 280ms) var(--ev-ease-out, ease-out);
|
|
398
|
+
}
|
|
399
|
+
[data-slot="shell-nav-marker"]:not([data-placed]) {
|
|
400
|
+
visibility: hidden;
|
|
401
|
+
}
|
|
402
|
+
[data-slot="shell-nav-marker"][data-placed="entered"] {
|
|
403
|
+
animation: ev-shell-fade-in var(--ev-dur-fast, 180ms) var(--ev-ease-out, ease-out);
|
|
404
|
+
}
|
|
405
|
+
[data-slot="shell-nav-marker"]:not([data-placed]) ~ * [aria-current="page"] {
|
|
406
|
+
background-color: var(--primary);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* BottomTabBar: one rule for the whole bar, mounted once and translated by
|
|
410
|
+
whole tab widths (it is exactly one tab wide, so the two cannot drift). A
|
|
411
|
+
route no tab claims fades it out where it stands, and coming back fades it in
|
|
412
|
+
already in place. */
|
|
413
|
+
[data-slot="tab-bar-marker"] {
|
|
414
|
+
transition:
|
|
415
|
+
translate var(--ev-dur-base, 280ms) var(--ev-ease-out, ease-out),
|
|
416
|
+
opacity var(--ev-dur-base, 280ms) var(--ev-ease-out, ease-out);
|
|
417
|
+
}
|
|
418
|
+
[data-slot="tab-bar-marker"][data-state="idle"] {
|
|
419
|
+
opacity: 0;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
@keyframes ev-enter-fade {
|
|
423
|
+
from {
|
|
424
|
+
opacity: 0;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
@media (prefers-reduced-motion: reduce) {
|
|
428
|
+
[data-enter="rise"],
|
|
429
|
+
[data-enter="stagger"] > *,
|
|
430
|
+
[data-slot="settled"][data-state="revealed"] {
|
|
431
|
+
animation-name: ev-enter-fade;
|
|
432
|
+
}
|
|
433
|
+
[data-slot="skeleton"].animate-pulse {
|
|
434
|
+
animation: ev-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
435
|
+
}
|
|
436
|
+
[data-slot="shell-nav-marker"],
|
|
437
|
+
[data-slot="tab-bar-marker"] {
|
|
438
|
+
transition: none;
|
|
439
|
+
animation: none;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
309
443
|
/* ═══ CONTRACT ═══════════════════════════════════════════════════ */
|
|
310
444
|
|
|
311
445
|
@theme inline {
|
|
@@ -428,6 +562,26 @@
|
|
|
428
562
|
|
|
429
563
|
--sidebar-width: 14rem;
|
|
430
564
|
|
|
565
|
+
/* app shell (uikit <AppShell> / <ShellNav> / <BottomTabBar>) */
|
|
566
|
+
--shell-rail-w: 15.5rem; /* 248px */
|
|
567
|
+
--shell-tab-bar-h: 4rem; /* 64px, before the bottom safe area */
|
|
568
|
+
|
|
569
|
+
/* ── motion ──────────────────────────────────────────────────── *
|
|
570
|
+
* One place to retune the feel of every animation the kit ships (motion.css
|
|
571
|
+
* reads these) and every one a consumer writes against them:
|
|
572
|
+
* `ease-(--ev-ease-out) duration-(--ev-dur-base)`. Quiet on purpose — short,
|
|
573
|
+
* small travel: motion here says what changed, it never decorates. */
|
|
574
|
+
--ev-ease-out: cubic-bezier(0.22, 1, 0.36, 1); /* every entrance */
|
|
575
|
+
--ev-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* state that reverses */
|
|
576
|
+
--ev-dur-fast: 180ms;
|
|
577
|
+
--ev-dur-base: 280ms;
|
|
578
|
+
--ev-dur-slow: 400ms;
|
|
579
|
+
--ev-rise: 8px; /* how far an entrance travels */
|
|
580
|
+
--ev-stagger: 35ms; /* between rows of a list */
|
|
581
|
+
/* between the sections of a page: cards, not rows — at 35ms six of them read
|
|
582
|
+
as one block twitching; at 50ms the last lands ~0.55s in, still "opening" */
|
|
583
|
+
--ev-stagger-section: 50ms;
|
|
584
|
+
|
|
431
585
|
/* page container — responsive gutter + max width (uikit <Container>) */
|
|
432
586
|
--page-max: 90rem; /* 1440px */
|
|
433
587
|
--page-px: 1rem; /* 16px — mobile gutter, scales up below */
|
package/styles/tokens.css
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* accent-trace · accent-debug · accent-info · accent-warn · accent-error
|
|
22
22
|
* primary-ink — the primary role as ink on a surface, where the
|
|
23
23
|
* fill and the readable value are different colours
|
|
24
|
-
* SCALARS radius · control-* · display-scale · page-* · shadows · fonts
|
|
24
|
+
* SCALARS radius · control-* · display-scale · page-* · shell-* · shadows · fonts
|
|
25
|
+
* MOTION ev-ease-* · ev-dur-* · ev-rise · ev-stagger(-section)
|
|
25
26
|
* CHARTS chart-1 … chart-5
|
|
26
27
|
* MARK brand-mark · brand-aspect — supplied, never defined here
|
|
27
28
|
*
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
*/
|
|
41
42
|
|
|
42
43
|
/*
|
|
43
|
-
* Toast (sonner) and Drawer enter/exit motion,
|
|
44
|
+
* Toast (sonner) and Drawer enter/exit motion, the entrances (rise / stagger / settled /
|
|
45
|
+
* skeleton) and the app-shell markers, shared by both ports. Imported by the token
|
|
44
46
|
* sheets so the lifecycle ships with the theme contract.
|
|
45
47
|
*/
|
|
46
48
|
|
|
@@ -346,6 +348,139 @@
|
|
|
346
348
|
}
|
|
347
349
|
}
|
|
348
350
|
|
|
351
|
+
/* ── Entrances: rise, stagger, settled, skeleton ─────────────────────────────
|
|
352
|
+
One keyframe for every "arriving" thing, driven by attributes so a consumer
|
|
353
|
+
without a motion library (and a Server Component) gets it for free:
|
|
354
|
+
|
|
355
|
+
data-enter="rise" the element fades in and rises --ev-rise, once, on
|
|
356
|
+
insertion; --enter-delay postpones it.
|
|
357
|
+
data-enter="stagger" each direct child does the same, one --enter-step
|
|
358
|
+
(default --ev-stagger) after the previous. The index
|
|
359
|
+
comes from nth-child; set --enter-index inline to
|
|
360
|
+
override it. Capped at 12 — past that the tail is
|
|
361
|
+
still arriving after the head has been read.
|
|
362
|
+
[data-slot="settled"][data-state="revealed"]
|
|
363
|
+
a skeleton → content handover (uikit <Settled>).
|
|
364
|
+
|
|
365
|
+
It animates the independent `translate` property, not `transform`, so it
|
|
366
|
+
composes with any transform the element already has. `backwards` holds the
|
|
367
|
+
start frame through the delay and then lets go, so nothing about the element
|
|
368
|
+
(stacking, containing block for `fixed` children) lingers after it lands.
|
|
369
|
+
Reduced motion keeps the fade and drops the travel. */
|
|
370
|
+
@keyframes ev-enter-rise {
|
|
371
|
+
from {
|
|
372
|
+
opacity: 0;
|
|
373
|
+
translate: 0 var(--ev-rise, 8px);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
[data-enter="rise"],
|
|
377
|
+
[data-enter="stagger"] > *,
|
|
378
|
+
[data-slot="settled"][data-state="revealed"] {
|
|
379
|
+
animation: ev-enter-rise var(--ev-dur-base, 280ms) var(--ev-ease-out, ease-out)
|
|
380
|
+
var(--enter-delay, 0ms) backwards;
|
|
381
|
+
}
|
|
382
|
+
[data-enter="stagger"] > * {
|
|
383
|
+
animation-delay: calc(
|
|
384
|
+
var(--enter-delay, 0ms) + var(--enter-index, 0) * var(--enter-step, var(--ev-stagger, 35ms))
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
[data-enter="stagger"] > :nth-child(2) { --enter-index: 1; }
|
|
388
|
+
[data-enter="stagger"] > :nth-child(3) { --enter-index: 2; }
|
|
389
|
+
[data-enter="stagger"] > :nth-child(4) { --enter-index: 3; }
|
|
390
|
+
[data-enter="stagger"] > :nth-child(5) { --enter-index: 4; }
|
|
391
|
+
[data-enter="stagger"] > :nth-child(6) { --enter-index: 5; }
|
|
392
|
+
[data-enter="stagger"] > :nth-child(7) { --enter-index: 6; }
|
|
393
|
+
[data-enter="stagger"] > :nth-child(8) { --enter-index: 7; }
|
|
394
|
+
[data-enter="stagger"] > :nth-child(9) { --enter-index: 8; }
|
|
395
|
+
[data-enter="stagger"] > :nth-child(10) { --enter-index: 9; }
|
|
396
|
+
[data-enter="stagger"] > :nth-child(11) { --enter-index: 10; }
|
|
397
|
+
[data-enter="stagger"] > :nth-child(n + 12) { --enter-index: 11; }
|
|
398
|
+
|
|
399
|
+
/* Every skeleton fades in before it starts to pulse. Keyed on the pulse class as
|
|
400
|
+
well as the slot: a running animation beats a transition on the same property,
|
|
401
|
+
so the entrance has to be the first of two animations rather than anything
|
|
402
|
+
layered beside `animate-pulse` — and a caller's `animate-none` (which `cn`
|
|
403
|
+
merges over the pulse) removes the class and with it this rule. Reduced motion
|
|
404
|
+
keeps the pulse, the signal that work is in flight, and drops the entrance. */
|
|
405
|
+
@keyframes ev-skeleton-in {
|
|
406
|
+
from {
|
|
407
|
+
opacity: 0;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
@keyframes ev-skeleton-pulse {
|
|
411
|
+
50% {
|
|
412
|
+
opacity: 0.5;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
[data-slot="skeleton"].animate-pulse {
|
|
416
|
+
animation:
|
|
417
|
+
ev-skeleton-in 200ms var(--ev-ease-out, ease-out),
|
|
418
|
+
ev-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) 200ms infinite;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* ── App shell: the rail's active-row marker and the tab bar's rule ──────────
|
|
422
|
+
ShellNav: one marker node per nav section, placed on the [aria-current] row by
|
|
423
|
+
measurement and moved by a `transform` transition — on the compositor, so it
|
|
424
|
+
keeps its frames while the main thread renders the page just navigated to (a
|
|
425
|
+
JS layout projection here dropped exactly those). Until the first placement
|
|
426
|
+
(SSR, every frame to hydration) the marker has no position: it stays hidden
|
|
427
|
+
and the marked row carries the fill itself, so the first paint is right.
|
|
428
|
+
`data-placed` flips both at once; `entered` is a marker that mounted into a
|
|
429
|
+
section already on screen (a move across sections — a fade), `first` one that
|
|
430
|
+
arrived with its section (a cut: fading under a row whose own fill fades out
|
|
431
|
+
would dip). */
|
|
432
|
+
@keyframes ev-shell-fade-in {
|
|
433
|
+
from {
|
|
434
|
+
opacity: 0;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
[data-slot="shell-nav-marker"] {
|
|
438
|
+
transition: transform var(--ev-dur-base, 280ms) var(--ev-ease-out, ease-out);
|
|
439
|
+
}
|
|
440
|
+
[data-slot="shell-nav-marker"]:not([data-placed]) {
|
|
441
|
+
visibility: hidden;
|
|
442
|
+
}
|
|
443
|
+
[data-slot="shell-nav-marker"][data-placed="entered"] {
|
|
444
|
+
animation: ev-shell-fade-in var(--ev-dur-fast, 180ms) var(--ev-ease-out, ease-out);
|
|
445
|
+
}
|
|
446
|
+
[data-slot="shell-nav-marker"]:not([data-placed]) ~ * [aria-current="page"] {
|
|
447
|
+
background-color: var(--primary);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* BottomTabBar: one rule for the whole bar, mounted once and translated by
|
|
451
|
+
whole tab widths (it is exactly one tab wide, so the two cannot drift). A
|
|
452
|
+
route no tab claims fades it out where it stands, and coming back fades it in
|
|
453
|
+
already in place. */
|
|
454
|
+
[data-slot="tab-bar-marker"] {
|
|
455
|
+
transition:
|
|
456
|
+
translate var(--ev-dur-base, 280ms) var(--ev-ease-out, ease-out),
|
|
457
|
+
opacity var(--ev-dur-base, 280ms) var(--ev-ease-out, ease-out);
|
|
458
|
+
}
|
|
459
|
+
[data-slot="tab-bar-marker"][data-state="idle"] {
|
|
460
|
+
opacity: 0;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
@keyframes ev-enter-fade {
|
|
464
|
+
from {
|
|
465
|
+
opacity: 0;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
@media (prefers-reduced-motion: reduce) {
|
|
469
|
+
[data-enter="rise"],
|
|
470
|
+
[data-enter="stagger"] > *,
|
|
471
|
+
[data-slot="settled"][data-state="revealed"] {
|
|
472
|
+
animation-name: ev-enter-fade;
|
|
473
|
+
}
|
|
474
|
+
[data-slot="skeleton"].animate-pulse {
|
|
475
|
+
animation: ev-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
476
|
+
}
|
|
477
|
+
[data-slot="shell-nav-marker"],
|
|
478
|
+
[data-slot="tab-bar-marker"] {
|
|
479
|
+
transition: none;
|
|
480
|
+
animation: none;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
349
484
|
/* ═══ CONTRACT ═══════════════════════════════════════════════════ */
|
|
350
485
|
|
|
351
486
|
@theme inline {
|
|
@@ -468,6 +603,26 @@
|
|
|
468
603
|
|
|
469
604
|
--sidebar-width: 14rem;
|
|
470
605
|
|
|
606
|
+
/* app shell (uikit <AppShell> / <ShellNav> / <BottomTabBar>) */
|
|
607
|
+
--shell-rail-w: 15.5rem; /* 248px */
|
|
608
|
+
--shell-tab-bar-h: 4rem; /* 64px, before the bottom safe area */
|
|
609
|
+
|
|
610
|
+
/* ── motion ──────────────────────────────────────────────────── *
|
|
611
|
+
* One place to retune the feel of every animation the kit ships (motion.css
|
|
612
|
+
* reads these) and every one a consumer writes against them:
|
|
613
|
+
* `ease-(--ev-ease-out) duration-(--ev-dur-base)`. Quiet on purpose — short,
|
|
614
|
+
* small travel: motion here says what changed, it never decorates. */
|
|
615
|
+
--ev-ease-out: cubic-bezier(0.22, 1, 0.36, 1); /* every entrance */
|
|
616
|
+
--ev-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* state that reverses */
|
|
617
|
+
--ev-dur-fast: 180ms;
|
|
618
|
+
--ev-dur-base: 280ms;
|
|
619
|
+
--ev-dur-slow: 400ms;
|
|
620
|
+
--ev-rise: 8px; /* how far an entrance travels */
|
|
621
|
+
--ev-stagger: 35ms; /* between rows of a list */
|
|
622
|
+
/* between the sections of a page: cards, not rows — at 35ms six of them read
|
|
623
|
+
as one block twitching; at 50ms the last lands ~0.55s in, still "opening" */
|
|
624
|
+
--ev-stagger-section: 50ms;
|
|
625
|
+
|
|
471
626
|
/* page container — responsive gutter + max width (uikit <Container>) */
|
|
472
627
|
--page-max: 90rem; /* 1440px */
|
|
473
628
|
--page-px: 1rem; /* 16px — mobile gutter, scales up below */
|