feat(motion): M9 F1+F1b — modo children-DOM (2ª coordinación, esqueleto + demo)
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
<script lang="ts">
|
|
|
|
|
/**
|
|
|
|
|
* DomCascade — children-DOM stagger (RFC §M9), validated in ISOLATION before the
|
|
|
|
|
* menu. Unlike Reveal/Rail (each item is a registered child `Presence`), here the
|
|
|
|
|
* owner is ONE `Presence` (the container) and the items are STATIC DOM — exactly
|
|
|
|
|
* the shape a real menu/list has. The `DomCascade` mirrors the owner's
|
|
|
|
|
* `transitionAttrs` + a per-DOM-order `--motion-stagger-index` onto each item,
|
|
|
|
|
* reusing eidos's coordinated preset UNCHANGED (the items end up carrying the same
|
|
|
|
|
* attrs a child `Presence` would have produced).
|
|
|
|
|
*
|
|
|
|
|
* F1b validated the ENTER cascade (the items stagger in when the container opens).
|
|
|
|
|
* F1c closes the EXIT: the owner `Presence` cannot see the items' transitions on its
|
|
|
|
|
* own node (`getAnimations()`, no subtree), so the `DomCascade` exposes a `pending()`
|
|
|
|
|
* that the owner aggregates via `PresenceOptions.pending` — it now HOLDS the subtree
|
|
|
|
|
* until the cascade settles instead of dropping the items mid-stagger. Inherits the
|
feat(motion): M9 F1+F1b — modo children-DOM (2ª coordinación, esqueleto + demo)
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
* ActiveUix + Soma + Eidos scope from `../+layout.svelte`.
|
|
|
|
|
*/
|
|
|
|
|
import { ActiveEidos } from '$uix/eidos';
|
|
|
|
|
import { readableActive } from '$libs/reactive';
|
|
|
|
|
import { Presence } from '$soma/layers/presence.svelte';
|
|
|
|
|
import { DomCascade } from '$soma/layers/dom-cascade.svelte';
|
|
|
|
|
|
|
|
|
|
const eidos = ActiveEidos.require();
|
|
|
|
|
|
|
|
|
|
const ITEMS = ['Perfil', 'Ajustes', 'Notificaciones', 'Equipo', 'Facturación', 'Salir'];
|
|
|
|
|
const STYLES = [
|
|
|
|
|
{ value: 'cascade-slide', label: 'slide' },
|
|
|
|
|
{ value: 'cascade-fade', label: 'fade' },
|
|
|
|
|
{ value: 'cascade-scale', label: 'scale' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let open = $state(false);
|
|
|
|
|
let animStyle = $state('cascade-slide');
|
|
|
|
|
let containerEl = $state<HTMLElement | null>(null);
|
|
|
|
|
|
|
|
|
|
// ONE Presence for the owner (the container). It owns the lifecycle; the cascade
|
|
|
|
|
// only mirrors it onto the DOM items. F1c: on exit the owner awaits the items'
|
|
|
|
|
// cascade (`pending`) before unmounting — their transitions are invisible to the
|
|
|
|
|
// owner's `getAnimations()` (no subtree), so without this the container would drop
|
|
|
|
|
// mid-stagger on close. `cascade` is referenced lazily (only when `pending` fires).
|
|
|
|
|
// Explicit annotations break the inference cycle: `presence.pending` reads
|
|
|
|
|
// `cascade`, whose `ownerTransitionAttrs` reads `presence.transitionAttrs`.
|
|
|
|
|
const presence: Presence = new Presence({
|
feat(motion): M9 F1+F1b — modo children-DOM (2ª coordinación, esqueleto + demo)
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
dom: eidos.dom,
|
|
|
|
|
open: readableActive(() => open),
|
|
|
|
|
ref: readableActive(() => containerEl),
|
|
|
|
|
pending: () => cascade.pending()
|
feat(motion): M9 F1+F1b — modo children-DOM (2ª coordinación, esqueleto + demo)
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// The propagator: route `animStyle` to each DOM child, mirror the owner's
|
|
|
|
|
// transition attrs, number them by DOM order. `items()` is a plain selector read —
|
|
|
|
|
// the same shape a menu's `getItems` uses.
|
|
|
|
|
const cascade: DomCascade = new DomCascade({
|
feat(motion): M9 F1+F1b — modo children-DOM (2ª coordinación, esqueleto + demo)
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
dom: eidos.dom,
|
|
|
|
|
animationStyle: readableActive(() => animStyle),
|
|
|
|
|
ownerTransitionAttrs: readableActive(() => presence.transitionAttrs),
|
|
|
|
|
items: () =>
|
|
|
|
|
containerEl ? Array.from(containerEl.querySelectorAll<HTMLElement>('[data-dc-item]')) : []
|
|
|
|
|
});
|
|
|
|
|
cascade.watch();
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<svelte:head>
|
|
|
|
|
<title>DomCascade · children-DOM (M9 F1c)</title>
|
feat(motion): M9 F1+F1b — modo children-DOM (2ª coordinación, esqueleto + demo)
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
</svelte:head>
|
|
|
|
|
|
|
|
|
|
<div class="root">
|
|
|
|
|
<header>
|
|
|
|
|
<a class="back" href="/temas/animations">← Motion</a>
|
|
|
|
|
<h1>DomCascade <small>children-DOM · M9 F1c</small></h1>
|
feat(motion): M9 F1+F1b — modo children-DOM (2ª coordinación, esqueleto + demo)
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
<p class="lede">
|
|
|
|
|
La <strong>segunda coordinación</strong> del servicio (RFC §M9). A diferencia de
|
|
|
|
|
<a href="/temas/animations/reveal">Reveal</a> / <a href="/temas/animations/rail">Rail</a>
|
|
|
|
|
—donde cada ítem es un <code>Presence</code> registrado— aquí el owner es <strong>un solo</strong>
|
|
|
|
|
<code>Presence</code> (el contenedor) y los ítems son <strong>DOM estático</strong>: justo la
|
|
|
|
|
forma de un menú real. El propagador <code>DomCascade</code>
|
|
|
|
|
<strong>espeja</strong> el <code>data-starting/ending-style</code> del owner + un
|
|
|
|
|
<code>--motion-stagger-index</code> por orden DOM en cada ítem, <strong>reutilizando el preset
|
|
|
|
|
coordinado de eidos sin cambiarlo</strong>. Es la grieta que el menú destapó, ya pavimentada.
|
|
|
|
|
</p>
|
|
|
|
|
<p class="lede note">
|
|
|
|
|
F1b validó la <strong>entrada</strong>; <strong>F1c</strong> cierra la
|
|
|
|
|
<strong>salida</strong>: el owner ahora <strong>espera</strong> a los ítems antes de
|
|
|
|
|
desmontar. Su <code>getAnimations()</code> no ve las transiciones de los ítems —sin
|
|
|
|
|
<code>subtree</code>— así que el <code>DomCascade</code> expone un <code>pending()</code> que el
|
|
|
|
|
<code>Presence</code> agrega. Ciérrala: los ítems salen en cascada <strong>antes</strong> de que
|
|
|
|
|
el contenedor desaparezca.
|
feat(motion): M9 F1+F1b — modo children-DOM (2ª coordinación, esqueleto + demo)
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
</p>
|
|
|
|
|
<div class="actions">
|
|
|
|
|
<button class="btn primary" type="button" onclick={() => (open = !open)}>
|
|
|
|
|
{open ? 'Ocultar' : 'Mostrar'} lista
|
|
|
|
|
</button>
|
|
|
|
|
<div class="picker" role="group" aria-label="Estilo de animación">
|
|
|
|
|
{#each STYLES as s (s.value)}
|
|
|
|
|
<button
|
|
|
|
|
class="chip-btn"
|
|
|
|
|
class:on={animStyle === s.value}
|
|
|
|
|
type="button"
|
|
|
|
|
onclick={() => (animStyle = s.value)}
|
|
|
|
|
>
|
|
|
|
|
{s.label}
|
|
|
|
|
</button>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<!-- The stage feeds the cascade its rhythm; the items inherit it. The container is
|
|
|
|
|
the owner `Presence` (its transition attrs drive the cascade); the items are
|
|
|
|
|
plain DOM the propagator writes onto. -->
|
|
|
|
|
<div class="stage" style="--motion-cascade-duration: 320ms; --motion-stagger-each: 55ms">
|
|
|
|
|
{#if presence.isPresent}
|
|
|
|
|
<div class="container" bind:this={containerEl} {...presence.transitionAttrs}>
|
|
|
|
|
{#each ITEMS as label (label)}
|
|
|
|
|
<div class="dc-item" data-dc-item>{label}</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.root {
|
|
|
|
|
min-height: 100dvh;
|
|
|
|
|
background: var(--color-surface-default, #fff);
|
|
|
|
|
color: var(--color-content-primary, #111);
|
|
|
|
|
font-family: var(--font-family-primary, 'Inter', system-ui, sans-serif);
|
|
|
|
|
padding: 2.5rem 1.5rem 4rem;
|
|
|
|
|
}
|
|
|
|
|
header {
|
|
|
|
|
max-width: 880px;
|
|
|
|
|
margin-inline: auto;
|
|
|
|
|
margin-block-end: 2rem;
|
|
|
|
|
}
|
|
|
|
|
.back {
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
color: var(--color-content-muted, #666);
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
.back:hover {
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
h1 {
|
|
|
|
|
margin: 0.5rem 0 0.75rem;
|
|
|
|
|
font-size: 1.9rem;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
h1 small {
|
|
|
|
|
font-size: 0.95rem;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: var(--color-content-muted, #666);
|
|
|
|
|
}
|
|
|
|
|
.lede {
|
|
|
|
|
margin: 0 0 0.75rem;
|
|
|
|
|
max-width: 66ch;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
color: var(--color-content-secondary, #444);
|
|
|
|
|
}
|
|
|
|
|
.lede.note {
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
color: var(--color-content-muted, #777);
|
|
|
|
|
}
|
|
|
|
|
.lede a {
|
|
|
|
|
color: var(--color-primary-solid, #4f46e5);
|
|
|
|
|
}
|
|
|
|
|
.actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
margin-block-start: 1.25rem;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.btn {
|
|
|
|
|
appearance: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font: inherit;
|
|
|
|
|
font-size: 0.82rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
padding: 0.4rem 0.9rem;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
border: 1px solid var(--color-border-default, rgba(0, 0, 0, 0.15));
|
|
|
|
|
background: var(--color-surface-default, #fff);
|
|
|
|
|
color: var(--color-content-primary, #111);
|
|
|
|
|
}
|
|
|
|
|
.btn.primary {
|
|
|
|
|
background: var(--color-primary-solid, #4f46e5);
|
|
|
|
|
color: var(--color-primary-contrast, #fff);
|
|
|
|
|
border-color: transparent;
|
|
|
|
|
}
|
|
|
|
|
.picker {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
padding: 0.2rem;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
background: var(--color-surface-muted, rgba(0, 0, 0, 0.05));
|
|
|
|
|
}
|
|
|
|
|
.chip-btn {
|
|
|
|
|
appearance: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font: inherit;
|
|
|
|
|
font-size: 0.78rem;
|
|
|
|
|
padding: 0.3rem 0.7rem;
|
|
|
|
|
border-radius: 7px;
|
|
|
|
|
border: none;
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-content-secondary, #555);
|
|
|
|
|
}
|
|
|
|
|
.chip-btn.on {
|
|
|
|
|
background: var(--color-primary-solid, #4f46e5);
|
|
|
|
|
color: var(--color-primary-contrast, #fff);
|
|
|
|
|
}
|
|
|
|
|
.stage {
|
|
|
|
|
max-width: 880px;
|
|
|
|
|
margin-inline: auto;
|
|
|
|
|
padding: 1.5rem;
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
border: 1px dashed var(--color-border-subtle, rgba(0, 0, 0, 0.12));
|
|
|
|
|
background: var(--color-surface-raised, #f7f7f8);
|
|
|
|
|
min-height: 360px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.container {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.4rem;
|
|
|
|
|
width: min(280px, 100%);
|
|
|
|
|
padding: 0.5rem;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
background: var(--color-surface-overlay, rgba(99, 102, 241, 0.08));
|
|
|
|
|
border: 1px solid var(--color-border-subtle, rgba(0, 0, 0, 0.12));
|
|
|
|
|
}
|
|
|
|
|
/* The item's base look. The off-state + transition + stagger come from eidos's
|
|
|
|
|
coordinated preset (generated/base.css), applied via the attrs the DomCascade
|
|
|
|
|
propagates — NO animation CSS here. */
|
|
|
|
|
.dc-item {
|
|
|
|
|
padding: 0.5rem 0.75rem;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
background: var(--color-surface-default, #fff);
|
|
|
|
|
border: 1px solid var(--color-border-subtle, rgba(0, 0, 0, 0.1));
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
}
|
|
|
|
|
</style>
|