< script lang = "ts" >
/**
* Depth demo — the Eidos depth channel, made visceral.
*
* Not abstract tiles: real UI that RESPONDS. Three beats —
* 1. Reacciona — cards lift on hover (state moment: depth responds to interaction).
* 2. Asciende — a dialog RISES over a scrim on open (event moment: the `present-rise`
* firma — shadow grows + lift — fired by stamping `data-event-*`, as sema would).
* 3. Cada plano, un rol — the 5 planes shown as the real components they're for.
*
* Self-contained: eidos foundation (planes + firma) + `data-theme`.
*/
import '@/uix/eidos/index.css'
let theme = $state< 'light' | 'dark'>('dark')
// ── Ascenso: a dialog that rises over a scrim (the event moment) ────────────
let open = $state(false)
let evt = $state< { event? : string ; phase? : string } >({} )
function summon(): void {
open = true
evt = {}
requestAnimationFrame(() => {
evt = { event : 'present' , phase : 'active' }
})
}
function dismiss(): void {
open = false
evt = {}
}
< / script >
< svelte:head > < title > Profundidad · Eidos< / title > < / svelte:head >
< div class = "root" data-theme = { theme } >
< header class = "masthead" >
< div class = "mast-row" >
< p class = "kicker" > Eidos · canal de profundidad< / p >
< button class = "theme-toggle" onclick = {() => ( theme = theme === 'light' ? 'dark' : 'light' )} >
{ theme === 'light' ? '◐ claro' : '◑ oscuro' }
< / button >
< / div >
< h1 class = "mast-title" > La profundidad < em > ocurre< / em > < / h1 >
< p class = "mast-lede" >
No es una propiedad que un elemento < i > tiene< / i > — es algo que < i > sucede< / i > . Mira: la
profundidad < strong > responde< / strong > al cursor y < strong > asciende< / strong > cuando algo emerge.
< / p >
< / header >
<!-- ── 1 · Reacciona (estado · hover) ─────────────────────────────────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Reacciona< / h2 >
< p class = "sec-note" > Pasa el cursor — cada tarjeta sube de plano (raised → overlay): la sombra crece y se eleva.< / p >
< / div >
< div class = "lift-row" >
{ #each [{ t : 'Ingresos' , v : '€48.2k' , d : '+12% este mes' , c : 'fulfill' }, { t : 'Sesiones' , v : '9 312' , d : '+4% vs ayer' , c : 'primary' }, { t : 'Alertas' , v : '3' , d : 'requieren acción' , c : 'risk' }] as card }
< article class = "lift-card" data-depth = "raised" >
< span class = "lift-tag" data-c = { card . c } > { card . t } </span >
< span class = "lift-value" > { card . v } </ span >
< span class = "lift-delta" > { card . d } </ span >
< / article >
{ /each }
< / div >
< / section >
<!-- ── 2 · Asciende (evento · la firma) ───────────────────────────────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Asciende< / h2 >
< p class = "sec-note" >
Abre el panel: < em > asciende< / em > sobre el contenido (que recede tras el scrim). La sombra
crece desde plano hasta su elevación — la < em > firma< / em > del evento, no un estilo fijo.
< / p >
< / div >
< div class = "stage" >
< div class = "stage-app" class:dimmed = { open } >
< div class = "app-bar" >
< span class = "app-dot" > < / span > < span class = "app-dot" > < / span > < span class = "app-dot" > < / span >
< span class = "app-title" > panel · proyecto< / span >
< / div >
{ #each [ 'Revisar borrador de diseño' , 'Publicar el sprint de profundidad' , 'Cerrar incidencias abiertas' ] as row , i }
< div class = "app-row" >< span class = "app-check" data-on = { i === 1 } > </span > { row } </ div >
{ /each }
< / div >
{ #if open }
< button class = "scrim" aria-label = "cerrar" onclick = { dismiss } > </button >
< div
class="risen"
data-depth="modal"
data-event={ evt . event }
data-event-phase={ evt . phase }
>
< h3 class = "risen-title" > Acabo de ascender< / h3 >
< p class = "risen-body" >
Mi sombra creció desde la superficie hasta el plano < code > modal< / code > mientras subía — y
el contenido de detrás recedió. Eso es la profundidad < em > ocurriendo< / em > .
< / p >
< div class = "risen-actions" >
< button class = "btn btn-primary" onclick = { dismiss } > Entendido</button >
< button class = "btn btn-ghost" onclick = { dismiss } > Cerrar</button >
< / div >
< / div >
{ /if }
< button class = "stage-trigger" onclick = {() => ( open ? dismiss () : summon ())} >
{ open ? '↓ cerrar' : '↑ abrir panel' }
< / button >
< / div >
< / section >
<!-- ── 3 · Cada plano, un rol ─────────────────────────────────────────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Cada plano, un rol< / h2 >
< p class = "sec-note" >
Los 5 planos son los componentes que los necesitan. < code > data-depth< / code > cohere
superficie + sombra + z; en dark, "más arriba" = más claro (mode-aware).
< / p >
< / div >
< div class = "roles" >
<!-- flush -->
< figure class = "role" >
< div class = "role-stage" >
< div class = "chips" >
< span class = "chip" data-depth = "flush" > diseño< / span >
< span class = "chip" data-depth = "flush" > activo< / span >
< span class = "chip" data-depth = "flush" > v2< / span >
< / div >
< / div >
< figcaption > < b > flush< / b > · chips, a ras de superficie< / figcaption >
< / figure >
<!-- raised -->
< figure class = "role" >
< div class = "role-stage" >
< div class = "mini-card" data-depth = "raised" >
< span class = "mini-avatar" > A< / span >
< div > < span class = "mini-name" > Tarjeta< / span > < span class = "mini-sub" > contenido en reposo< / span > < / div >
< / div >
< / div >
< figcaption > < b > raised< / b > · tarjetas, paneles< / figcaption >
< / figure >
<!-- overlay -->
< figure class = "role" >
< div class = "role-stage" >
< div class = "mini-menu" data-depth = "overlay" >
< div class = "menu-item" > Duplicar< / div >
< div class = "menu-item" data-on > Mover a…< / div >
< div class = "menu-item" > Eliminar< / div >
< / div >
< / div >
< figcaption > < b > overlay< / b > · menús, popovers< / figcaption >
< / figure >
<!-- modal -->
< figure class = "role" >
< div class = "role-stage" >
< div class = "mini-dialog" data-depth = "modal" >
< span class = "mini-name" > ¿Eliminar?< / span >
< span class = "mini-sub" > No se puede deshacer.< / span >
< span class = "mini-btn" > Eliminar< / span >
< / div >
< / div >
< figcaption > < b > modal< / b > · diálogos, cima< / figcaption >
< / figure >
<!-- recessed -->
< figure class = "role" >
< div class = "role-stage" >
< div class = "mini-field" >
< span class = "field-label" > Buscar< / span >
< span class = "field-input" data-depth = "recessed" > escribe aquí…< / span >
< / div >
< / div >
< figcaption > < b > recessed< / b > · inputs, pozos< / figcaption >
< / figure >
< / div >
< / section >
< footer class = "footnote" >
< p >
< strong > Default fuerte, jaula abierta.< / strong > Planos config-driven, primitivos
(< code > --shadow-*< / code > , < code > z-index< / code > ) a un paso, firma anulable; la capa eventful
es aditiva y degrada con < code > prefers-reduced-motion< / code > . Un solo evento mueve
profundidad + posición + sound + haptic, coordinados.
< / p >
< / footer >
< / div >
< style >
.root {
min-block-size: 100vh;
background:
radial-gradient(130% 90% at 50% -15%, var(--color-surface-raised), transparent 55%),
var(--color-surface-default);
color: var(--color-content-primary);
font-family: var(--font-family-primary);
padding: clamp(1.5rem, 5vw, 5rem) clamp(1.25rem, 5vw, 6rem) 6rem;
--rule: var(--color-border-default);
}
.masthead { max - inline - size : 56rem ; margin - block - end : clamp ( 3 rem , 8 vw , 6 rem ); }
.mast-row { display : flex ; align - items : center ; justify - content : space - between ; gap : 1rem ; margin - block - end : 2.5rem ; }
.kicker { margin : 0 ; font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); letter - spacing : 0.18em ; text - transform : uppercase ; color : var ( -- color - content - muted ); }
.theme-toggle { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); color : var ( -- color - content - secondary ); background : var ( -- color - surface - raised ); border : 1px solid var ( -- rule ); border - radius : 999px ; padding : 0.5rem 1 rem ; cursor : pointer ; }
.theme-toggle:hover { color : var ( -- color - content - primary ); }
.mast-title { font - family : var ( -- font - family - display ); font - size : var ( -- font - size - xxxl ); font - weight : 600 ; line - height : 1 ; letter - spacing : - 0.03 em ; margin : 0 0 1.25 rem ; text - wrap : balance ; }
.mast-title em { font - style : italic ; color : var ( -- color - primary - text , var ( -- color - content - primary )); }
.mast-lede { max - inline - size : 48ch ; font - size : var ( -- font - size - lg ); line - height : 1.5 ; color : var ( -- color - content - secondary ); margin : 0 ; text - wrap : pretty ; }
.mast-lede i { font - style : italic ; }
.mast-lede strong, .sec-note em { color : var ( -- color - content - primary ); font - weight : 600 ; font - style : normal ; }
.section { margin - block - end : clamp ( 3.5 rem , 9 vw , 7 rem ); }
.sec-head { margin - block - end : 2.5rem ; }
.sec-title { font - family : var ( -- font - family - display ); font - size : var ( -- font - size - xl ); font - weight : 600 ; letter - spacing : - 0.01 em ; margin : 0 0 0.5 rem ; }
.sec-note { margin : 0 ; max - inline - size : 62ch ; font - size : var ( -- font - size - sm ); line - height : 1.55 ; color : var ( -- color - content - muted ); }
code { font - family : var ( -- font - family - mono ); font - size : 0.9em ; color : var ( -- color - content - secondary ); background : var ( -- color - surface - muted ); padding : 0.1em 0.4 em ; border - radius : 4px ; }
/* 1 · Reacciona (hover lift) */
.lift-row { display : grid ; grid - template - columns : repeat ( auto - fit , minmax ( min ( 100 % , 14 rem ), 1 fr )); gap : 1.5rem ; }
.lift-card {
background: var(--color-surface-raised);
border: 1px solid var(--rule);
border-radius: var(--radius-lg);
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
cursor: default;
transition: box-shadow 240ms var(--ease-out, cubic-bezier(0.2, 0, 0, 1)), translate 240ms var(--ease-out, cubic-bezier(0.2, 0, 0, 1)), border-color 240ms;
}
.lift-card:hover {
box-shadow: var(--depth-overlay-shadow);
translate: 0 -6px;
border-color: color-mix(in srgb, var(--color-primary-solid) 40%, var(--rule));
}
.lift-tag { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); letter - spacing : 0.04em ; text - transform : uppercase ; color : var ( -- color - content - muted ); }
.lift-tag[data-c='fulfill'] { color : var ( -- color - fulfill - text , var ( -- color - content - secondary )); }
.lift-tag[data-c='primary'] { color : var ( -- color - primary - text , var ( -- color - content - secondary )); }
.lift-tag[data-c='risk'] { color : var ( -- color - risk - text , var ( -- color - content - secondary )); }
.lift-value { font - family : var ( -- font - family - display ); font - size : var ( -- font - size - xxl ); line - height : 1 ; letter - spacing : - 0.02 em ; color : var ( -- color - content - primary ); }
.lift-delta { font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
@media (prefers-reduced-motion: reduce) { . lift - card { transition : box - shadow 240 ms ; } . lift - card :hover { translate : none ; } }
/* 2 · Asciende (the stage + rising dialog + scrim) */
.stage {
position: relative;
overflow: hidden;
border-radius: var(--radius-lg);
border: 1px solid var(--rule);
background: var(--color-surface-default);
min-block-size: 22rem;
padding: clamp(1.25rem, 3vw, 2rem);
}
.stage-app { transition : filter 280 ms var ( -- ease - out , ease ), opacity 280 ms ; max - inline - size : 30rem ; }
.stage-app.dimmed { filter : saturate ( 0.7 ); opacity : 0.5 ; }
.app-bar { display : flex ; align - items : center ; gap : 0.4rem ; padding - block - end : 1rem ; margin - block - end : 1rem ; border - block - end : 1px solid var ( -- rule ); }
.app-dot { inline - size : 0.6rem ; block - size : 0.6rem ; border - radius : 50 % ; background : var ( -- color - surface - muted ); }
.app-title { margin - inline - start : 0.5rem ; font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
.app-row { display : flex ; align - items : center ; gap : 0.75rem ; padding - block : 0.6rem ; font - size : var ( -- font - size - sm ); color : var ( -- color - content - secondary ); border - block - end : 1px solid color - mix ( in srgb , var ( -- rule ) 50 % , transparent ); }
.app-check { inline - size : 1.05rem ; block - size : 1.05rem ; border - radius : 5px ; border : 1.5px solid var ( -- rule ); flex : none ; }
.app-check[data-on='true'] { background : var ( -- color - primary - solid ); border - color : var ( -- color - primary - solid ); }
.scrim {
position: absolute;
inset: 0;
border: 0;
padding: 0;
background: color-mix(in srgb, var(--color-neutral-contrast, #000) 55%, transparent);
cursor: pointer;
animation: depth-scrim-in 240ms var(--ease-out, ease) both;
}
@keyframes depth-scrim-in { from { opacity : 0 ; } to { opacity : 1 ; } }
.risen {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 50%;
translate: -50% -50%;
inline-size: min(24rem, calc(100% - 3rem));
background: var(--depth-modal-surface, var(--color-surface-overlay));
border: 1px solid var(--rule);
border-radius: var(--radius-xl);
padding: 1.75rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.risen-title { font - family : var ( -- font - family - display ); font - size : var ( -- font - size - xl ); margin : 0 ; color : var ( -- color - content - primary ); }
.risen-body { margin : 0 ; font - size : var ( -- font - size - sm ); line - height : 1.55 ; color : var ( -- color - content - secondary ); }
.risen-actions { display : flex ; gap : 0.6rem ; margin - block - start : 0.75rem ; }
.btn { font - family : var ( -- font - family - primary ); font - size : var ( -- font - size - sm ); border - radius : var ( -- radius - md ); padding : 0.55rem 1.1 rem ; cursor : pointer ; border : 1px solid transparent ; }
.btn-primary { background : var ( -- color - primary - solid ); color : var ( -- color - primary - on - solid , # fff ); }
.btn-ghost { background : transparent ; color : var ( -- color - content - secondary ); border - color : var ( -- rule ); }
.stage-trigger {
position: absolute;
inset-block-end: 1.25rem;
inset-inline-end: 1.25rem;
font-family: var(--font-family-mono);
font-size: var(--font-size-sm);
color: var(--color-primary-on-solid, #fff);
background: var(--color-primary-solid);
border: 0;
border-radius: var(--radius-md);
padding: 0.65rem 1.1rem;
cursor: pointer;
z-index: 1;
box-shadow: var(--depth-raised-shadow);
}
/* 3 · Cada plano, un rol */
.roles { display : grid ; grid - template - columns : repeat ( auto - fit , minmax ( min ( 100 % , 13 rem ), 1 fr )); gap : clamp ( 1.25 rem , 3 vw , 2.5 rem ); }
.role { margin : 0 ; display : flex ; flex - direction : column ; gap : 1rem ; }
.role-stage { min - block - size : 8rem ; display : grid ; place - items : center ; padding : 1rem ; border - radius : var ( -- radius - lg ); background : var ( -- color - surface - default ); border : 1px solid color - mix ( in srgb , var ( -- rule ) 60 % , transparent ); }
.role figcaption { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
.role figcaption b { color : var ( -- color - content - primary ); font - weight : 600 ; }
.chips { display : flex ; flex - wrap : wrap ; gap : 0.5rem ; justify - content : center ; }
.chip { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); padding : 0.3rem 0.7 rem ; border - radius : 999px ; background : var ( -- color - surface - muted ); color : var ( -- color - content - secondary ); border : 1px solid color - mix ( in srgb , var ( -- rule ) 60 % , transparent ); }
.mini-card { display : flex ; align - items : center ; gap : 0.75rem ; padding : 0.9rem 1.1 rem ; border - radius : var ( -- radius - md ); background : var ( -- color - surface - raised ); }
.mini-avatar { inline - size : 2rem ; block - size : 2rem ; border - radius : 50 % ; display : grid ; place - items : center ; background : var ( -- color - primary - solid ); color : var ( -- color - primary - on - solid , # fff ); font - size : var ( -- font - size - sm ); font - weight : 600 ; }
.mini-name { display : block ; font - size : var ( -- font - size - sm ); font - weight : 600 ; color : var ( -- color - content - primary ); }
.mini-sub { display : block ; font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
.mini-menu { inline - size : 9rem ; padding : 0.4rem ; border - radius : var ( -- radius - md ); background : var ( -- color - surface - overlay ); display : flex ; flex - direction : column ; }
.menu-item { padding : 0.45rem 0.65 rem ; border - radius : var ( -- radius - sm ); font - size : var ( -- font - size - sm ); color : var ( -- color - content - secondary ); }
.menu-item[data-on] { background : var ( -- color - primary - solid ); color : var ( -- color - primary - on - solid , # fff ); }
.mini-dialog { inline - size : 11rem ; padding : 1rem ; border - radius : var ( -- radius - md ); background : var ( -- color - surface - overlay ); display : flex ; flex - direction : column ; gap : 0.35rem ; }
.mini-btn { margin - block - start : 0.5rem ; align - self : flex - start ; font - size : var ( -- font - size - xs ); padding : 0.35rem 0.7 rem ; border - radius : var ( -- radius - sm ); background : var ( -- color - threat - solid , var ( -- color - primary - solid )); color : # fff ; }
.mini-field { display : flex ; flex - direction : column ; gap : 0.4rem ; inline - size : 100 % ; max - inline - size : 11rem ; }
.field-label { font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
.field-input { padding : 0.6rem 0.8 rem ; border - radius : var ( -- radius - md ); background : var ( -- color - surface - muted ); color : var ( -- color - content - muted ); font - size : var ( -- font - size - sm ); }
.footnote { max - inline - size : 60rem ; padding - block - start : 2rem ; border - block - start : 1px solid var ( -- rule ); }
.footnote p { margin : 0 ; font - size : var ( -- font - size - sm ); line - height : 1.6 ; color : var ( -- color - content - muted ); text - wrap : pretty ; }
< / style >