You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
6.8 KiB
158 lines
6.8 KiB
/*
|
|
* Fase 1 prototype — the cascade, as a real eidos recipe would ship it: PLAIN global
|
|
* CSS in a .css file (no Svelte `<style>` scoping/pruning), selecting on data-attrs.
|
|
* EIDOS owns 100% of this. The page (soma role) only flips data-state + retains the
|
|
* leaving node (Svelte `out:`); nobody writes a per-card variable.
|
|
*/
|
|
|
|
[data-cascade-panel] {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
|
|
gap: 0.75rem;
|
|
padding: 1rem;
|
|
border: 1px solid var(--color-border-subtle, #e5e5e5);
|
|
border-radius: var(--radius-lg, 12px);
|
|
min-block-size: 8rem;
|
|
/* rhythm tokens, set once on the container (inherited). The demo overrides the
|
|
outer one inline. */
|
|
--card-stagger-each: 70ms;
|
|
--inner-stagger-each: 45ms;
|
|
/* inner content waits for its card to SETTLE, then cascades — so the nesting
|
|
reads as a distinct second phase instead of being masked by the card's fade. */
|
|
--inner-after: var(--duration-moderate, 240ms);
|
|
}
|
|
|
|
/* the wrapper IS the grid cell + the animated box; the Card fills it */
|
|
[data-cascade-item] {
|
|
display: flex;
|
|
}
|
|
[data-cascade-item] > [data-card] {
|
|
flex: 1;
|
|
}
|
|
|
|
@keyframes card-rise {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(14px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
}
|
|
@keyframes card-fall {
|
|
from {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
to {
|
|
opacity: 0;
|
|
transform: translateY(8px);
|
|
}
|
|
}
|
|
|
|
/* EIDOS computes the per-card index FROM STRUCTURE — nobody writes it.
|
|
nth-child → forward index (enter); nth-last-child → reverse (exit). */
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(1) { --row: 0; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(2) { --row: 1; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(3) { --row: 2; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(4) { --row: 3; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(5) { --row: 4; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(6) { --row: 5; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(7) { --row: 6; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(8) { --row: 7; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(9) { --row: 8; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(10) { --row: 9; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(11) { --row: 10; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(12) { --row: 11; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(13) { --row: 12; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(14) { --row: 13; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(15) { --row: 14; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-child(16) { --row: 15; }
|
|
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(1) { --row-rev: 0; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(2) { --row-rev: 1; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(3) { --row-rev: 2; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(4) { --row-rev: 3; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(5) { --row-rev: 4; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(6) { --row-rev: 5; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(7) { --row-rev: 6; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(8) { --row-rev: 7; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(9) { --row-rev: 8; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(10) { --row-rev: 9; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(11) { --row-rev: 10; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(12) { --row-rev: 11; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(13) { --row-rev: 12; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(14) { --row-rev: 13; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(15) { --row-rev: 14; }
|
|
[data-cascade-panel] > [data-cascade-item]:nth-last-child(16) { --row-rev: 15; }
|
|
|
|
/* ENTER: container open → each NON-leaving card rises, delayed by its structural index. */
|
|
[data-cascade-panel][data-state='open'] > [data-cascade-item]:not([data-leaving]) {
|
|
animation: card-rise var(--duration-moderate, 240ms) var(--ease-out, cubic-bezier(0.2, 0.8, 0.2, 1))
|
|
backwards;
|
|
animation-delay: calc(var(--row, 0) * var(--card-stagger-each));
|
|
}
|
|
|
|
/* EXIT (panel close): all cards reverse-cascade out, held forwards. */
|
|
[data-cascade-panel][data-state='closed'] > [data-cascade-item] {
|
|
animation: card-fall var(--duration-fast, 120ms) var(--ease-in, cubic-bezier(0.4, 0, 1, 1)) forwards;
|
|
animation-delay: calc(var(--row-rev, 0) * var(--card-stagger-each));
|
|
}
|
|
|
|
/* EXIT (cards removed, panel still open): each retained item plays card-fall, staggered
|
|
by its REVERSE structural index (--row-rev, from nth-last-child). A lone removal → the
|
|
last card has --row-rev 0 → delay 0 (immediate); a bulk removal → reverse cascade (the
|
|
last card leaves first). No `data-phase` needed — the structure gives both for free.
|
|
Soma's `out:` retains each node for delay + duration, then unmounts. */
|
|
[data-cascade-panel] > [data-cascade-item][data-leaving] {
|
|
animation: card-fall var(--duration-fast, 120ms) var(--ease-in, cubic-bezier(0.4, 0, 1, 1)) forwards;
|
|
animation-delay: calc(var(--row-rev, 0) * var(--card-stagger-each));
|
|
}
|
|
|
|
/* ── NESTED cascade: inner content inside each card ──────────────────────────
|
|
The nesting test. Each inner item composes TWO structural indices, both pure
|
|
CSS:
|
|
· `--row` — inherited from its ancestor [data-cascade-item] (the card's
|
|
OUTER position in the panel cascade);
|
|
· `--inner-row` — its OWN position among siblings (`:nth-child`, scoped to its
|
|
own [data-inner-list] parent — nesting is automatic).
|
|
delay = card_delay + inner_delay → the inner content enters AFTER its card.
|
|
Zero JS writes; inheritance + nth-child do the composition. */
|
|
|
|
[data-inner-list] {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.3rem;
|
|
margin-block-start: 0.5rem;
|
|
}
|
|
[data-inner-item] {
|
|
block-size: 0.55rem;
|
|
border-radius: 3px;
|
|
background: var(--color-content-muted, #8a8a8a);
|
|
}
|
|
[data-inner-list] > [data-inner-item]:nth-child(1) { --inner-row: 0; inline-size: 90%; }
|
|
[data-inner-list] > [data-inner-item]:nth-child(2) { --inner-row: 1; inline-size: 60%; }
|
|
[data-inner-list] > [data-inner-item]:nth-child(3) { --inner-row: 2; inline-size: 78%; }
|
|
|
|
@keyframes inner-rise {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateX(-18px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
}
|
|
|
|
[data-cascade-panel][data-state='open'] [data-inner-item] {
|
|
animation: inner-rise var(--duration-moderate, 240ms) var(--ease-out, cubic-bezier(0.2, 0.8, 0.2, 1))
|
|
backwards;
|
|
/* composed delay: card's outer index + the card's settle time + own inner index */
|
|
animation-delay: calc(
|
|
var(--row, 0) * var(--card-stagger-each) + var(--inner-after) + var(--inner-row, 0) *
|
|
var(--inner-stagger-each)
|
|
);
|
|
}
|