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.
svelte-kit-vice/web/routes/temas/forma/+page.svelte

443 lines
22 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script lang="ts">
/**
* Shape demo — the Eidos shape channel, made tangible on real UI.
*
* 1. Taller — a real "now playing" card + a control panel (family · smoothing · radius ·
* concentric) that retunes the WHOLE card live (the jaula-abierta centerpiece).
* 2. De cerca — round vs squircle, big, so the seam difference reads.
* 3. Familias — the same chip in all 4 families + each on a fitting component.
* 4. Ocurre — press real buttons → the corner firms (the press-squeeze firma).
* 5. Vs el resto— the field is arc+static; the strip shows what only Eidos does.
*
* Self-contained: eidos foundation (shape tokens + families + firma) + `data-theme`.
* Continuity needs `corner-shape` (Chromium 2025+); degrades to the plain arc elsewhere.
*/
import '@/uix/eidos/index.css'
let theme = $state<'light' | 'dark'>('dark')
$effect(() => {
const el = document.documentElement
const prev = el.getAttribute('data-theme')
el.setAttribute('data-theme', theme)
return () => {
if (prev === null) el.removeAttribute('data-theme')
else el.setAttribute('data-theme', prev)
}
})
const FAMILIES = [
{ id: 'rounded', label: 'rounded' },
{ id: 'continuous', label: 'continuous' },
{ id: 'cut', label: 'cut' },
{ id: 'scoop', label: 'scoop' }
] as const
type Family = (typeof FAMILIES)[number]['id']
// ── 1 · Taller: the live control panel drives the whole sample card ──────────
let family = $state<Family>('continuous')
let smoothing = $state(2)
let radius = $state('xl')
let nest = $state(true)
let playing = $state(true)
const RADII = ['sm', 'md', 'lg', 'xl'] as const
// ── 4 · eventful press ───────────────────────────────────────────────────────
let pressed = $state<string | null>(null)
function firePress(id: string): void {
pressed = null
requestAnimationFrame(() => (pressed = id))
}
</script>
<svelte:head><title>Forma · Eidos</title></svelte:head>
<div class="root" data-theme={theme}>
<header class="masthead">
<div class="mast-row">
<p class="kicker">Eidos · canal de forma</p>
<button class="theme-toggle" onclick={() => (theme = theme === 'light' ? 'dark' : 'light')}>
{theme === 'light' ? '◐ claro' : '◑ oscuro'}
</button>
</div>
<h1 class="mast-title">La esquina, <em>repensada</em></h1>
<p class="mast-lede">
Toda la web reduce la forma a un número: <code>border-radius</code>, un arco. La esquina
tiene más ejes — <strong>continuidad</strong>, <strong>familia</strong>,
<strong>armonía</strong> y <strong>tensión</strong>. Aquí la forma es un canal: tócala y mira
la misma UI cambiar de carácter.
</p>
</header>
<!-- ── 1 · Taller (centerpiece) ─────────────────────────────────────────── -->
<section class="section">
<div class="sec-head">
<h2 class="sec-title">El taller</h2>
<p class="sec-note">
Una tarjeta real. Cambia familia, suavizado, magnitud o el anidado concéntrico y
<em>todo</em> se reentona a la vez — un solo sistema, no estilos sueltos. (El avatar es un
círculo de verdad: las familias no lo deforman, por diseño.)
</p>
</div>
<div class="lab">
<!-- the sample UI -->
<div
class="player"
data-shape={family}
style="--sample-radius: var(--radius-{radius}); --shape-outer-radius: var(--radius-{radius}); --shape-smoothing: {smoothing}"
>
<div
class="player-cover"
data-shape={family}
data-shape-nest={nest ? '' : undefined}
class:cover-naive={!nest}
>
<button class="player-play" data-shape={family} onclick={() => (playing = !playing)}>
{playing ? '❚❚' : '▶'}
</button>
<span class="player-eq" data-on={playing}><i></i><i></i><i></i><i></i></span>
</div>
<div class="player-body">
<div class="player-head">
<span class="player-avatar">N</span>
<div class="player-meta">
<span class="player-title">Continuum</span>
<span class="player-artist">Nocturne Ensemble</span>
</div>
<span class="player-tag" data-shape={family}>FLAC</span>
</div>
<div class="player-track"><span class="player-fill"></span></div>
<div class="player-row">
<input class="player-input" data-shape={family} placeholder="añade a una lista…" />
<button class="player-add" data-shape={family}>+</button>
</div>
</div>
</div>
<!-- the control panel -->
<div class="panel">
<div class="ctl">
<span class="ctl-label">familia</span>
<div class="chips">
{#each FAMILIES as f}
<button class="chip" data-active={family === f.id} onclick={() => (family = f.id)}>
{f.label}
</button>
{/each}
</div>
</div>
<div class="ctl" class:ctl-dim={family !== 'continuous'}>
<span class="ctl-label">suavizado</span>
<input type="range" min="1" max="3.2" step="0.05" bind:value={smoothing} disabled={family !== 'continuous'} />
<span class="ctl-val">{smoothing.toFixed(2)}</span>
</div>
<div class="ctl">
<span class="ctl-label">magnitud</span>
<div class="chips">
{#each RADII as r}
<button class="chip" data-active={radius === r} onclick={() => (radius = r)}>{r}</button>
{/each}
</div>
</div>
<label class="ctl ctl-check">
<input type="checkbox" bind:checked={nest} />
<span class="ctl-label">radios concéntricos {nest ? '' : '· off (la carátula desencaja)'}</span>
</label>
<code class="panel-code"
>data-shape="{family}"{family === 'continuous' ? ` · --shape-smoothing:${smoothing.toFixed(2)}` : ''}</code
>
</div>
</div>
</section>
<!-- ── 2 · De cerca ─────────────────────────────────────────────────────── -->
<section class="section">
<div class="sec-head">
<h2 class="sec-title">De cerca</h2>
<p class="sec-note">
El arco circular tiene un <em>quiebre</em> donde el lado encuentra la curva. El squircle
(superelipse) fluye sin costura — lo que hace premium a iOS. Misma magnitud, distinta
continuidad.
</p>
</div>
<div class="closeup">
<figure class="cu">
<div class="cu-box" data-shape="rounded"></div>
<figcaption><b>rounded</b> · <code>superellipse(1)</code> — arco</figcaption>
</figure>
<figure class="cu">
<div class="cu-box" data-shape="continuous"></div>
<figcaption><b>continuous</b> · <code>superellipse(2)</code> — squircle</figcaption>
</figure>
</div>
</section>
<!-- ── 3 · Familias ─────────────────────────────────────────────────────── -->
<section class="section">
<div class="sec-head">
<h2 class="sec-title">Familias</h2>
<p class="sec-note">
Una sola mecánica (<code>corner-shape</code> vía <code>data-shape</code>) cubre cuatro
familias. Material solo tiene <code>cut</code>; Apple solo <code>continuous</code>. Aquí,
la misma etiqueta en las cuatro:
</p>
</div>
<div class="fam-row">
{#each FAMILIES as f}
<figure class="fam">
<span class="fam-chip" data-shape={f.id}>etiqueta</span>
<figcaption>{f.label}</figcaption>
</figure>
{/each}
</div>
</section>
<!-- ── 4 · Ocurre ───────────────────────────────────────────────────────── -->
<section class="section">
<div class="sec-head">
<h2 class="sec-title">La forma <em>ocurre</em></h2>
<p class="sec-note">
Púlsalos: la esquina <em>se afirma</em> bajo el dedo (la continuidad se cuadra un instante)
a la vez que baja de escala y se aplana la sombra — un evento, tres canales: la firma
cross-modal. El botón <code>rounded</code> es el control: no morfea.
</p>
</div>
<div class="press-row">
<button
class="press-btn"
data-shape="continuous"
data-event-family={pressed === 'a' ? 'contact' : undefined}
data-event-phase={pressed === 'a' ? 'active' : undefined}
onanimationend={() => (pressed = null)}
onpointerdown={() => firePress('a')}
>continuous</button>
<button class="press-btn press-ctrl" data-shape="rounded">rounded</button>
</div>
</section>
<!-- ── 5 · Vs el resto ──────────────────────────────────────────────────── -->
<section class="section">
<div class="sec-head">
<h2 class="sec-title">Lo que solo hace Eidos</h2>
<p class="sec-note">
El campo entero (Tailwind · shadcn · Chakra · Mantine · Ant · Radix · Carbon · Fluent ·
Spectrum · Polaris) se queda en la primera columna.
</p>
</div>
<div class="vs">
<div class="vs-col vs-them">
<span class="vs-h">todos los demás</span>
<div class="vs-sample" data-shape="rounded"></div>
<span class="vs-cap">escala + arco + estático</span>
</div>
<div class="vs-col">
<span class="vs-h">+ continuidad</span>
<div class="vs-sample" data-shape="continuous"></div>
<span class="vs-cap">squircle tokenizado</span>
</div>
<div class="vs-col">
<span class="vs-h">+ familias</span>
<div class="vs-sample" data-shape="scoop"></div>
<span class="vs-cap">cut · scoop · …</span>
</div>
<div class="vs-col">
<span class="vs-h">+ anidado</span>
<div class="vs-sample vs-nest" data-shape="continuous" style="--shape-outer-radius: 28px">
<span data-shape="continuous" data-shape-nest></span>
</div>
<span class="vs-cap">concéntrico</span>
</div>
<div class="vs-col">
<span class="vs-h">+ eventful</span>
<button
class="vs-sample vs-press"
data-shape="continuous"
data-event-family={pressed === 'v' ? 'contact' : undefined}
data-event-phase={pressed === 'v' ? 'active' : undefined}
onanimationend={() => (pressed = null)}
onpointerdown={() => firePress('v')}
aria-label="pulsa"
></button>
<span class="vs-cap">pulsa · morfea</span>
</div>
</div>
</section>
<footer class="footnote">
<p>
<strong>El primer squircle-como-token de la web.</strong> Continuidad + familias + anidado +
eventful como tokens abiertos — magnitud intacta en <code>--radius-*</code>, degradando al
arco donde no hay <code>corner-shape</code>. Config-driven + runtime
<code>applyShape()</code>. La continuidad solo existía en Apple (plataforma); el morph solo
en Material (ad-hoc). Aquí se reúnen, y son tuyos.
</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(3rem, 8vw, 6rem); }
.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 1rem; 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.03em; margin: 0 0 1.25rem; text-wrap: balance; }
.mast-title em { font-style: italic; color: var(--color-primary-text, var(--color-content-primary)); }
.mast-lede { max-inline-size: 52ch; font-size: var(--font-size-lg); line-height: 1.5; color: var(--color-content-secondary); margin: 0; text-wrap: pretty; }
.mast-lede strong { color: var(--color-content-primary); font-weight: 600; }
.section { margin-block-end: clamp(3.5rem, 9vw, 7rem); }
.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.01em; margin: 0 0 0.5rem; }
.sec-title em { font-style: italic; color: var(--color-primary-text, var(--color-content-primary)); }
.sec-note { margin: 0; max-inline-size: 64ch; font-size: var(--font-size-sm); line-height: 1.55; color: var(--color-content-muted); }
.sec-note em { color: var(--color-content-primary); font-style: normal; font-weight: 600; }
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.4em; border-radius: 4px; }
/* 1 · Taller */
.lab { display: grid; grid-template-columns: 1fr; gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
@media (min-width: 52rem) { .lab { grid-template-columns: 22rem 1fr; } }
.player {
inline-size: 100%;
max-inline-size: 22rem;
border-radius: var(--sample-radius);
background: var(--color-surface-raised);
border: 1px solid var(--rule);
box-shadow: var(--depth-overlay-shadow), var(--depth-overlay-halo);
overflow: hidden;
}
.player-cover {
position: relative;
aspect-ratio: 16 / 10;
margin: var(--space-2);
/* radius comes from [data-shape-nest] (concentric) or .cover-naive — never set here, or the
scoped rule would beat the foundation's nest rule and the inset would stop being concentric */
background:
radial-gradient(60% 80% at 30% 20%, color-mix(in oklab, var(--color-primary-solid) 90%, white), transparent 70%),
radial-gradient(70% 90% at 80% 90%, #12a594, transparent 70%),
linear-gradient(145deg, var(--color-primary-solid), #2a1a4a);
display: grid;
place-items: center;
overflow: hidden;
}
.cover-naive { border-radius: var(--radius-xl); }
.player-play {
inline-size: 3.25rem;
aspect-ratio: 1;
border-radius: var(--sample-radius);
border: 0;
background: color-mix(in srgb, #000 28%, transparent);
backdrop-filter: blur(6px);
color: #fff;
font-size: var(--font-size-sm);
cursor: pointer;
display: grid;
place-items: center;
}
.player-play:hover { background: color-mix(in srgb, #000 40%, transparent); }
.player-eq { position: absolute; inset-block-end: 0.6rem; inset-inline-start: 0.7rem; display: flex; gap: 3px; align-items: flex-end; block-size: 1rem; opacity: 0; transition: opacity 200ms; }
.player-eq[data-on='true'] { opacity: 0.9; }
.player-eq i { inline-size: 3px; background: #fff; border-radius: 2px; animation: eq 900ms ease-in-out infinite; }
.player-eq i:nth-child(1) { block-size: 40%; animation-delay: -200ms; }
.player-eq i:nth-child(2) { block-size: 90%; animation-delay: -500ms; }
.player-eq i:nth-child(3) { block-size: 60%; animation-delay: -100ms; }
.player-eq i:nth-child(4) { block-size: 100%; animation-delay: -350ms; }
@keyframes eq { 0%, 100% { block-size: 25%; } 50% { block-size: 100%; } }
@media (prefers-reduced-motion: reduce) { .player-eq i { animation: none; } }
.player-body { padding: var(--space-1) var(--space-3) var(--space-3); display: flex; flex-direction: column; gap: var(--space-2-5); }
.player-head { display: flex; align-items: center; gap: 0.75rem; }
.player-avatar { inline-size: 2.25rem; aspect-ratio: 1; border-radius: 50%; display: grid; place-items: center; background: var(--color-primary-solid); color: var(--color-primary-on-solid, #fff); font-weight: 600; font-size: var(--font-size-sm); flex: none; }
.player-meta { display: flex; flex-direction: column; min-inline-size: 0; flex: 1; }
.player-title { font-family: var(--font-family-display); font-weight: 600; color: var(--color-content-primary); }
.player-artist { font-size: var(--font-size-xs); color: var(--color-content-muted); }
.player-tag { font-family: var(--font-family-mono); font-size: 0.62rem; letter-spacing: 0.06em; padding: 0.2rem 0.5rem; border-radius: var(--radius-sm); background: var(--color-surface-overlay); color: var(--color-content-secondary); border: 1px solid var(--rule); }
.player-track { block-size: 5px; border-radius: 999px; background: var(--color-surface-muted); overflow: hidden; }
.player-fill { display: block; inline-size: 42%; block-size: 100%; background: var(--color-primary-solid); }
.player-row { display: flex; gap: 0.5rem; }
.player-input { flex: 1; min-inline-size: 0; padding: 0.5rem 0.75rem; border-radius: var(--sample-radius); border: 1px solid var(--rule); background: var(--color-surface-default); color: var(--color-content-primary); font: inherit; font-size: var(--font-size-xs); outline: none; }
.player-input:focus { border-color: color-mix(in srgb, var(--color-primary-solid) 55%, var(--rule)); }
.player-add { inline-size: 2.1rem; border-radius: var(--sample-radius); border: 1px solid var(--rule); background: var(--color-surface-overlay); color: var(--color-content-primary); cursor: pointer; font-size: var(--font-size-md); }
.player-add:hover { background: var(--color-surface-muted); }
.panel { display: flex; flex-direction: column; gap: 1.5rem; padding: clamp(1.25rem, 3vw, 2rem); border-radius: var(--radius-lg); background: var(--color-surface-default); border: 1px solid var(--rule); }
.ctl { display: flex; flex-direction: column; gap: 0.65rem; }
.ctl-dim { opacity: 0.45; }
.ctl-check { flex-direction: row; align-items: center; gap: 0.6rem; cursor: pointer; }
.ctl-check input { accent-color: var(--color-primary-solid); }
.ctl-label { font-family: var(--font-family-mono); font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-content-muted); }
.ctl input[type='range'] { accent-color: var(--color-primary-solid); }
.ctl-val { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-content-primary); }
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chip { font-family: var(--font-family-mono); font-size: var(--font-size-xs); padding: 0.35rem 0.75rem; border-radius: 999px; cursor: pointer; background: var(--color-surface-raised); color: var(--color-content-secondary); border: 1px solid var(--rule); }
.chip:hover { color: var(--color-content-primary); }
.chip[data-active='true'] { background: var(--color-primary-solid); color: var(--color-primary-on-solid, #fff); border-color: var(--color-primary-solid); }
.panel-code { display: block; font-family: var(--font-family-mono); font-size: var(--font-size-xs); padding: 0.7rem 0.9rem; border-radius: var(--radius-md); background: var(--color-surface-muted); color: var(--color-content-secondary); word-break: break-all; }
/* 2 · De cerca */
.closeup { display: flex; flex-wrap: wrap; gap: clamp(1.5rem, 5vw, 4rem); justify-content: center; }
.cu { margin: 0; display: flex; flex-direction: column; gap: 1.25rem; align-items: center; }
.cu-box { inline-size: clamp(9rem, 26vw, 14rem); aspect-ratio: 1; border-radius: clamp(2.5rem, 7vw, 4.5rem); background: linear-gradient(145deg, var(--color-primary-solid), #12a594); }
.cu figcaption { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-content-muted); }
.cu figcaption b { color: var(--color-content-primary); }
/* 3 · Familias */
.fam-row { display: flex; flex-wrap: wrap; gap: clamp(1rem, 4vw, 2.5rem); justify-content: center; }
.fam { margin: 0; display: flex; flex-direction: column; gap: 0.9rem; align-items: center; }
/* filled chips (no border / drop-shadow): a concave `scoop` casts shadow INTO its notches and
a 1px border seams at the concave corners — both read as broken. Flat fills render every
family cleanly and are the right home for cut/scoop anyway (chips/badges). */
.fam-chip { display: grid; place-items: center; min-inline-size: 6.5rem; padding: 0.9rem 1.6rem; border-radius: 16px; background: color-mix(in oklab, var(--color-primary-solid) 26%, var(--color-surface-overlay)); font-family: var(--font-family-mono); font-size: var(--font-size-sm); color: var(--color-content-primary); }
.fam figcaption { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-content-muted); }
/* 4 · Ocurre */
.press-row { display: flex; flex-wrap: wrap; gap: 1.5rem; }
.press-btn {
font-family: var(--font-family-primary);
font-size: var(--font-size-md);
font-weight: 600;
padding: 1rem 2.25rem;
border-radius: 26px;
border: 1px solid color-mix(in srgb, var(--color-primary-solid) 50%, var(--rule));
background: linear-gradient(145deg, var(--color-surface-raised), var(--color-surface-overlay));
color: var(--color-content-primary);
box-shadow: var(--depth-raised-shadow);
cursor: pointer;
will-change: transform, border-radius;
}
.press-ctrl { opacity: 0.85; }
@media (prefers-reduced-motion: reduce) { .press-btn, .vs-press { animation: none !important; } }
/* 5 · Vs el resto */
.vs { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr)); gap: clamp(1rem, 3vw, 2rem); }
.vs-col { display: flex; flex-direction: column; gap: 0.75rem; align-items: center; text-align: center; }
.vs-h { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-content-primary); font-weight: 600; }
.vs-them .vs-h { color: var(--color-content-muted); }
.vs-sample { inline-size: 100%; aspect-ratio: 1; border-radius: 28px; background: linear-gradient(145deg, var(--color-primary-solid), #12a594); border: 0; padding: 0; }
.vs-them .vs-sample { background: linear-gradient(145deg, var(--color-surface-overlay), var(--color-surface-muted)); }
.vs-nest { background: var(--color-surface-overlay); display: grid; place-items: center; }
.vs-nest span { inline-size: 64%; aspect-ratio: 1; background: linear-gradient(145deg, var(--color-primary-solid), #12a594); }
.vs-press { cursor: pointer; will-change: transform, border-radius; }
.vs-cap { font-size: var(--font-size-xs); color: var(--color-content-muted); }
.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; }
.footnote strong { color: var(--color-content-primary); font-weight: 600; }
</style>

Powered by TurnKey Linux.