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/grafito/+page.svelte

1005 lines
40 KiB

<script lang="ts">
/**
* Showcase del tema Untitled UI — estructurado por secciones (estilo
* Radix Themes): un TOC lateral sticky + Fundamentos (color, tipografía,
* espaciado, densidad & scaling) + Componentes + Sistema (semántica).
* Todo se renderiza con componentes Eidos reales sobre el tema.
*/
import { onMount } from 'svelte'
import Button from '$uix/eidos/components/button'
import Badge from '$uix/eidos/components/badge'
import Card from '$uix/eidos/components/card'
import Field from '$uix/eidos/components/field'
import Select from '$uix/eidos/components/select'
import Checkbox from '$uix/eidos/components/checkbox'
import RadioGroup from '$uix/eidos/components/radio-group'
import Switch from '$uix/eidos/components/switch'
import Slider from '$uix/eidos/components/slider'
import Progress from '$uix/eidos/components/progress'
import Banner from '$uix/eidos/components/banner'
import Avatar from '$uix/eidos/components/avatar'
import Separator from '$uix/eidos/components/separator'
import Tabs from '$uix/eidos/components/tabs'
import Accordion from '$uix/eidos/components/accordion'
import Toggle from '$uix/eidos/components/toggle'
import ToggleGroup from '$uix/eidos/components/toggle-group'
import Toolbar from '$uix/eidos/components/toolbar'
import Popover from '$uix/eidos/components/popover'
import Dialog from '$uix/eidos/components/dialog'
import DropdownMenu from '$uix/eidos/components/dropdown-menu'
import ContextMenu from '$uix/eidos/components/context-menu'
import Menubar from '$uix/eidos/components/menubar'
import Tooltip from '$uix/eidos/components/tooltip'
import { THEME_SCALES, THEME_INTENTS, COLOR_STEPS } from '../_lib/grafito'
// ── Hex resolución (lee los literales de las escalas en runtime) ──
let hex = $state<Record<string, string>>({})
function readHex() {
if (typeof document === 'undefined') return
const cs = getComputedStyle(document.documentElement)
const out: Record<string, string> = {}
for (const sc of THEME_SCALES) {
for (const step of COLOR_STEPS) {
const v = cs.getPropertyValue(`--scale-${sc.name}-${step}`).trim()
if (v) out[`${sc.name}-${step}`] = v
}
}
hex = out
}
function luminance(value: string): number {
const m = value.replace('#', '')
if (m.length < 6) return 0.5
const r = parseInt(m.slice(0, 2), 16) / 255
const g = parseInt(m.slice(2, 4), 16) / 255
const b = parseInt(m.slice(4, 6), 16) / 255
return 0.2126 * r + 0.7152 * g + 0.0722 * b
}
const stepInk = (key: string) =>
luminance(hex[key] ?? '#000000') > 0.55 ? '#101828' : '#ffffff'
// ── Métricas en vivo de los ejes densidad + scaling ──────────────
// Se leen de una sonda oculta (los custom props devuelven el `calc()`
// sin resolver; un elemento real sí da el px computado).
let axisProbe = $state<HTMLDivElement | null>(null)
let axis = $state({ density: 'comfortable', scaling: '100', space4: '', fontMd: '', ctrlMd: '' })
function readAxis() {
if (typeof document === 'undefined') return
const root = document.documentElement
const cs = axisProbe ? getComputedStyle(axisProbe) : null
axis = {
density: root.getAttribute('data-density') ?? 'comfortable',
scaling: root.getAttribute('data-scaling') ?? '100',
space4: cs ? cs.width : '',
fontMd: cs ? cs.fontSize : '',
ctrlMd: cs ? cs.height : ''
}
}
// ── Datos de las secciones de tokens ──────────────────────────────
const TYPE_STYLES = [
{ key: 'hero', label: 'Hero', sample: 'Diseña lo que ocurre' },
{ key: 'h1', label: 'Heading 1', sample: 'The quick brown fox' },
{ key: 'h2', label: 'Heading 2', sample: 'The quick brown fox' },
{ key: 'h3', label: 'Heading 3', sample: 'The quick brown fox jumps' },
{ key: 'h4', label: 'Heading 4', sample: 'The quick brown fox jumps over' },
{ key: 'body', label: 'Body', sample: 'The quick brown fox jumps over the lazy dog.' },
{ key: 'label', label: 'Label', sample: 'Email address' },
{ key: 'caption', label: 'Caption', sample: 'Last updated 3 minutes ago' },
{ key: 'code', label: 'Code', sample: 'const theme = "grafito"' }
]
const SPACE = ['0', '0-5', '1', '1-5', '2', '2-5', '3', '3-5', '4', '5', '6', '8', '10', '12', '16']
const RADII = ['none', 'sm', 'md', 'lg', 'xl', 'xxl']
const SHADOWS = ['1', '2', '3', '4', '5', '6']
const SIZES = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', 'xxl']
const BTN_VARIANTS = ['solid', 'soft', 'surface', 'outline', 'ghost', 'plain'] as const
const BTN_SIZES = ['xs', 'sm', 'md', 'lg', 'xl'] as const
const CHIP_VARIANTS = ['soft', 'solid', 'outline', 'ghost'] as const
const BADGE_COLORS = ['primary', 'neutral', 'affirm', 'fulfill', 'risk', 'threat', 'loss'] as const
// ── Estado de los controles interactivos ──────────────────────────
let email = $state('olivia@untitledui.com')
let fruit = $state<string[]>(['mango'])
let agree = $state(true)
let notify = $state(false)
let plan = $state('pro')
let wifi = $state(true)
let analytics = $state(false)
let volume = $state([64])
let tab = $state('overview')
let tabsPills = $state('account')
let faq = $state<string[]>(['what'])
let bold = $state(true)
let fmt = $state<string[]>(['bold'])
let align = $state<string[]>(['left'])
let toolbarFmt = $state<string[]>(['bold'])
// ── Estado de los overlays / menús ────────────────────────────────
let menuNotifications = $state(true)
let menuTheme = $state('system')
let menubarValue = $state('')
// ── TOC + scrollspy ───────────────────────────────────────────────
const TOC = [
{
group: 'Fundamentos',
items: [
{ id: 'color', label: 'Color' },
{ id: 'typography', label: 'Tipografía' },
{ id: 'spacing', label: 'Espaciado & tamaño' },
{ id: 'axes', label: 'Densidad & Scaling' }
]
},
{
group: 'Componentes',
items: [
{ id: 'buttons', label: 'Botones' },
{ id: 'badges', label: 'Badges' },
{ id: 'forms', label: 'Formulario' },
{ id: 'feedback', label: 'Feedback' },
{ id: 'tabs', label: 'Tabs' },
{ id: 'disclosure', label: 'Disclosure' },
{ id: 'toggles', label: 'Toggles' },
{ id: 'toolbar', label: 'Toolbar' },
{ id: 'overlays', label: 'Overlays' },
{ id: 'menus', label: 'Menús' }
]
},
{ group: 'Sistema', items: [{ id: 'sema', label: 'Semántica' }] }
]
let activeSection = $state('color')
onMount(() => {
readHex()
readAxis()
const mo = new MutationObserver(() => {
readHex()
readAxis()
})
mo.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-mode', 'data-theme', 'data-density', 'data-scaling']
})
const sections = [...document.querySelectorAll('section[data-temas-section][id]')]
const io = new IntersectionObserver(
(entries) => {
for (const e of entries) {
if (e.isIntersecting) activeSection = (e.target as HTMLElement).id
}
},
{ rootMargin: '-100px 0px -65% 0px' }
)
for (const s of sections) io.observe(s)
return () => {
mo.disconnect()
io.disconnect()
}
})
</script>
{#snippet plusIcon()}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 5v14M5 12h14" />
</svg>
{/snippet}
{#snippet arrowIcon()}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M5 12h14M13 6l6 6-6 6" />
</svg>
{/snippet}
<!-- ═══ Hero ═══════════════════════════════════════════════════════ -->
<section data-temas-hero>
<h1>Grafito</h1>
<p>
Tema para activeUIX sobre el sistema de theming de Eidos. Primario <strong>carbón</strong>
monocromo, tipografía Inter, paleta derivada de Untitled UI (31 escalas), radios de 8px,
sombras en capas. Los 3 roles de jerarquía aliasan la paleta; los 6 intents auto-derivan por
la convención del libro — sin inventar variants ni roles nuevos.
</p>
<div data-temas-hero-meta>
<span data-temas-pill><i style="background: var(--color-primary-solid)"></i> Carbon · #1F2024</span>
<span data-temas-pill><i style="background: var(--color-content-primary)"></i> Inter</span>
<span data-temas-pill><i style="background: var(--color-fulfill-solid)"></i> 9 roles · 5 variants</span>
<span data-temas-pill><i style="background: var(--color-border-strong)"></i> Light · Dark · Density · Scaling</span>
</div>
</section>
<div data-temas-layout>
<aside data-temas-toc>
<nav aria-label="Secciones del tema">
{#each TOC as grp}
<p data-temas-toc-group>{grp.group}</p>
<ul>
{#each grp.items as it}
<li>
<a
href="#{it.id}"
data-active={activeSection === it.id ? 'true' : 'false'}
onclick={() => (activeSection = it.id)}
>
{it.label}
</a>
</li>
{/each}
</ul>
{/each}
</nav>
</aside>
<div data-temas-content>
<p data-temas-group-head>Fundamentos</p>
<!-- ═══ Color ══════════════════════════════════════════════════ -->
<section data-temas-section id="color">
<div data-temas-section-head>
<h2>Color</h2>
<p>
Tres capas: la <strong>Paleta</strong> (escalas, directamente usables) → los
<strong>Roles</strong> de jerarquía la aliasan → los <strong>Intents</strong> auto-derivan
de la paleta por la convención del libro (identidad = step 9). Los componentes pueden
override su color a cualquier escala. Los 9 roles son canon fijo; el tema solo trae la
paleta y mapea la jerarquía.
</p>
</div>
<div data-temas-stack>
<p data-temas-subhead>Paleta · escalas de 12 pasos</p>
<p data-temas-axis-note>Cada paso es directamente usable — p. ej. <code>var(--scale-teal-10)</code>.</p>
{#each THEME_SCALES as sc}
<div data-temas-ramp>
<div data-temas-ramp-head>
<strong>{sc.label}</strong>
<code>{hex[`${sc.name}-9`] ?? `--scale-${sc.name}-9`}</code>
</div>
<div data-temas-ramp-steps>
{#each COLOR_STEPS as step}
<div
data-temas-step
style="background: var(--scale-{sc.name}-{step}); color: {stepInk(`${sc.name}-${step}`)}"
title={hex[`${sc.name}-${step}`] ?? ''}
>
{step}
</div>
{/each}
</div>
</div>
{/each}
<div data-temas-step-legend>
<span>1–2 fondos</span>
<span>3–5 componente</span>
<span>6–8 bordes</span>
<span>9–10 sólido</span>
<span>11–12 texto</span>
</div>
</div>
<div data-temas-stack>
<p data-temas-subhead>Roles de jerarquía · alias explícito a una escala</p>
<div data-temas-roles>
{#each [{ role: 'primary', label: 'Primary', scale: 'carbon' }, { role: 'secondary', label: 'Secondary', scale: 'violet' }, { role: 'tertiary', label: 'Tertiary', scale: 'blue' }] as h}
<div data-temas-role>
<div data-temas-role-solid style="background: var(--color-{h.role}-solid)"></div>
<div data-temas-role-body>
<strong>{h.label}</strong>
<small>alias → <code>{h.scale}</code></small>
<div data-temas-role-tints>
<i style="background: var(--color-{h.role}-surface)" title="surface"></i>
<i style="background: var(--color-{h.role}-border)" title="border"></i>
<i style="background: var(--color-{h.role}-solid)" title="solid"></i>
<i style="background: var(--color-{h.role}-text)" title="text"></i>
</div>
</div>
</div>
{/each}
</div>
</div>
<div data-temas-stack>
<p data-temas-subhead>Intents · auto-derivados de la paleta (convención del libro)</p>
<div data-temas-roles>
{#each THEME_INTENTS as it}
<div data-temas-role>
<div data-temas-role-solid style="background: var(--color-{it.role}-solid)"></div>
<div data-temas-role-body>
<strong>{it.label}</strong>
<small>auto → <code>{it.scale}</code></small>
<div data-temas-role-tints>
<i style="background: var(--color-{it.role}-surface)" title="surface"></i>
<i style="background: var(--color-{it.role}-track)" title="track"></i>
<i style="background: var(--color-{it.role}-element)" title="element"></i>
<i style="background: var(--color-{it.role}-border)" title="border"></i>
<i style="background: var(--color-{it.role}-solid)" title="solid"></i>
<i style="background: var(--color-{it.role}-text)" title="text"></i>
</div>
</div>
</div>
{/each}
</div>
</div>
<div data-temas-stack>
<p data-temas-subhead>Superficies, contenido y bordes</p>
<div data-temas-surfaces>
<div data-temas-surface style="background: var(--color-surface-default)">
<span>surface</span><code>default</code>
</div>
<div data-temas-surface style="background: var(--color-surface-raised)">
<span>surface</span><code>raised</code>
</div>
<div data-temas-surface style="background: var(--color-surface-muted)">
<span>surface</span><code>muted</code>
</div>
<div data-temas-surface style="background: var(--color-content-primary); color: var(--color-surface-default)">
<span>content</span><code style="color: inherit; opacity: .7">primary</code>
</div>
<div data-temas-surface style="background: var(--color-content-secondary); color: var(--color-surface-default)">
<span>content</span><code style="color: inherit; opacity: .7">secondary</code>
</div>
<div data-temas-surface style="background: var(--color-surface-raised); border-color: var(--color-border-strong); border-width: 2px">
<span>border</span><code>strong</code>
</div>
</div>
</div>
</section>
<!-- ═══ Tipografía ═════════════════════════════════════════════ -->
<section data-temas-section id="typography">
<div data-temas-section-head>
<h2>Tipografía</h2>
<p>Inter para display y texto. Named styles del sistema (hero → caption + code).</p>
</div>
<div data-temas-panel>
{#each TYPE_STYLES as t}
<div data-temas-type-row>
<div data-temas-type-meta>
<strong>{t.label}</strong>
<code>--style-{t.key}</code>
</div>
<div
data-temas-type-sample
style="font-family: var(--style-{t.key}-font-family); font-size: var(--style-{t.key}-font-size); font-weight: var(--style-{t.key}-font-weight); line-height: var(--style-{t.key}-line-height); letter-spacing: var(--style-{t.key}-letter-spacing);"
>
{t.sample}
</div>
</div>
{/each}
</div>
</section>
<!-- ═══ Espaciado / Radio / Sombra / Tamaño ════════════════════ -->
<section data-temas-section id="spacing">
<div data-temas-section-head>
<h2>Espaciado, radio, sombra y tamaño</h2>
<p>Primitivas estables del tema. La escala base es de 4px; controles a 8px de radio.</p>
</div>
<div data-temas-fieldset>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Spacing</p>
{#each SPACE as k}
<div data-temas-space-row>
<code>--space-{k}</code>
<div data-temas-space-bar style="inline-size: var(--space-{k})"></div>
</div>
{/each}
</div>
<div data-temas-stack>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Radius</p>
<div data-temas-tiles>
{#each RADII as k}
<div data-temas-radius-tile style="border-radius: var(--radius-{k})">{k}</div>
{/each}
</div>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Sizes · control-height</p>
{#each SIZES as k}
<div data-temas-size-row>
<div data-temas-size-bar style="block-size: var(--size-{k}-control-height)">{k}</div>
<code style="color: var(--color-content-muted); font-family: var(--font-mono); font-size: var(--font-size-xs)">var(--size-{k}-control-height)</code>
</div>
{/each}
</div>
</div>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Shadows</p>
<div data-temas-tiles>
{#each SHADOWS as k}
<div data-temas-shadow-tile style="box-shadow: var(--shadow-{k})">shadow-{k}</div>
{/each}
</div>
</div>
</section>
<!-- ═══ Densidad & Scaling ═════════════════════════════════════ -->
<section data-temas-section id="axes">
<div data-temas-section-head>
<h2>Densidad &amp; Scaling</h2>
<p>Dos ejes ortogonales que se multiplican. Cámbialos en la barra superior y mira cómo reaccionan estas métricas y el specimen en vivo.</p>
</div>
<div data-temas-fieldset>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Densidad · {axis.density}</p>
<p data-temas-axis-note>Ritmo de layout (espacio) + altura de control. La tipografía NO cambia con la densidad.</p>
<div data-temas-axis-metrics>
<div><code>--space-4</code><strong>{axis.space4 || '—'}</strong></div>
<div><code>--control-height-md</code><strong>{axis.ctrlMd || '—'}</strong></div>
</div>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Scaling · {axis.scaling}%</p>
<p data-temas-axis-note>Zoom global (paridad Radix) — escala TODO proporcionalmente, incluida la tipografía. No toca radio, borde ni sombra.</p>
<div data-temas-axis-metrics>
<div><code>--font-size-md</code><strong>{axis.fontMd || '—'}</strong></div>
<div><code>--space-4 × scaling</code><strong>{axis.space4 || '—'}</strong></div>
</div>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Specimen en vivo</p>
<div data-temas-cluster>
<Button size="md">Acción</Button>
<Badge color="primary" variant="soft">v2.4</Badge>
</div>
<p style="margin: 0; color: var(--color-content-secondary)">
Texto de cuerpo: escala con <strong>scaling</strong>, estable bajo <strong>densidad</strong>.
</p>
</div>
</div>
<!-- sonda oculta para leer las métricas resueltas en px -->
<div
bind:this={axisProbe}
aria-hidden="true"
style="position: absolute; visibility: hidden; pointer-events: none; inline-size: var(--space-4); block-size: var(--control-height-md); font-size: var(--font-size-md)"
></div>
</section>
<p data-temas-group-head>Componentes</p>
<!-- ═══ Botones ════════════════════════════════════════════════ -->
<section data-temas-section id="buttons">
<div data-temas-section-head>
<h2>Botones</h2>
<p>6 variants · 9 roles · 5 tamaños · estados. El primario es carbón; el texto sobre sólidos claros resuelve a oscuro por luminancia.</p>
</div>
<div data-temas-demo>
<div data-temas-demo-block>
<p data-temas-subhead>Variants</p>
<div data-temas-cluster>
{#each BTN_VARIANTS as v}
<Button variant={v}>{v}</Button>
{/each}
</div>
</div>
<div data-temas-demo-block>
<p data-temas-subhead>Roles e intents</p>
<div data-temas-cluster>
<Button color="primary">Primary</Button>
<Button color="secondary" variant="surface">Secondary</Button>
<Button color="neutral" variant="outline">Neutral</Button>
<Button intent="affirm">Affirm</Button>
<Button intent="fulfill">Fulfill</Button>
<Button intent="risk">Risk</Button>
<Button intent="threat">Threat</Button>
<Button intent="loss">Loss</Button>
</div>
</div>
<div data-temas-demo-block>
<p data-temas-subhead>Tamaños</p>
<div data-temas-cluster>
{#each BTN_SIZES as sz}
<Button size={sz}>Button {sz}</Button>
{/each}
</div>
</div>
<div data-temas-demo-block>
<p data-temas-subhead>Con icono y estados</p>
<div data-temas-cluster>
<Button icon={plusIcon}>Nuevo</Button>
<Button variant="outline" endIcon={arrowIcon}>Continuar</Button>
<Button iconOnly icon={plusIcon} aria-label="Añadir" />
<Button loading>Guardando</Button>
<Button disabled>Desactivado</Button>
<Button intent="threat" variant="soft">Eliminar</Button>
</div>
</div>
</div>
</section>
<!-- ═══ Badges ═════════════════════════════════════════════════ -->
<section data-temas-section id="badges">
<div data-temas-section-head>
<h2>Badges</h2>
<p>Pills de estado en los 4 chip-variants y la paleta completa de roles.</p>
</div>
<div data-temas-demo>
<div data-temas-demo-block>
<p data-temas-subhead>Colores (soft)</p>
<div data-temas-cluster>
{#each BADGE_COLORS as c}
<Badge color={c} dot>{c}</Badge>
{/each}
</div>
</div>
<div data-temas-demo-block>
<p data-temas-subhead>Variants</p>
<div data-temas-cluster>
{#each CHIP_VARIANTS as v}
<Badge color="primary" variant={v}>{v}</Badge>
{/each}
<Badge color="fulfill" variant="soft">Activo</Badge>
<Badge color="risk" variant="soft" removable onRemove={() => {}}>Filtro</Badge>
</div>
</div>
</div>
</section>
<!-- ═══ Formulario ═════════════════════════════════════════════ -->
<section data-temas-section id="forms">
<div data-temas-section-head>
<h2>Controles de formulario</h2>
<p>Field, Select, Checkbox, Radio, Switch y Slider con el aspecto del tema.</p>
</div>
<div data-temas-fieldset>
<div data-temas-panel data-temas-stack>
<Field name="email">
<Field.Label>Email <Field.RequiredIndicator /></Field.Label>
<Field.Control>
<Field.Input type="email" bind:value={email} placeholder="tú@empresa.com" />
</Field.Control>
<Field.HelperText>Usaremos esto para iniciar sesión.</Field.HelperText>
</Field>
<Field name="fruit">
<Field.Label>Fruta favorita</Field.Label>
<Select bind:value={fruit}>
<Select.Trigger>
<Select.Value placeholder="Elige una fruta" />
<Select.Indicator />
</Select.Trigger>
<Select.Portal>
<Select.Content sideOffset={6}>
<Select.Viewport>
<Select.Item value="mango" label="Mango">Mango<Select.ItemIndicator /></Select.Item>
<Select.Item value="lychee" label="Lichi">Lichi<Select.ItemIndicator /></Select.Item>
<Select.Item value="guava" label="Guayaba">Guayaba<Select.ItemIndicator /></Select.Item>
<Select.Item value="papaya" label="Papaya">Papaya<Select.ItemIndicator /></Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select>
</Field>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Selección</p>
<label data-temas-cluster style="cursor: pointer">
<Checkbox bind:checked={agree}><Checkbox.Indicator /></Checkbox>
<span>Acepto los términos del servicio</span>
</label>
<label data-temas-cluster style="cursor: pointer">
<Checkbox bind:checked={notify} color="affirm"><Checkbox.Indicator /></Checkbox>
<span>Enviarme novedades del producto</span>
</label>
<Separator />
<RadioGroup bind:value={plan}>
<RadioGroup.Label data-temas-subhead>Plan</RadioGroup.Label>
<label data-temas-cluster style="cursor: pointer">
<RadioGroup.Item value="starter"><RadioGroup.Indicator /></RadioGroup.Item>
<span>Starter</span>
</label>
<label data-temas-cluster style="cursor: pointer">
<RadioGroup.Item value="pro"><RadioGroup.Indicator /></RadioGroup.Item>
<span>Pro</span>
</label>
<label data-temas-cluster style="cursor: pointer">
<RadioGroup.Item value="enterprise"><RadioGroup.Indicator /></RadioGroup.Item>
<span>Enterprise</span>
</label>
</RadioGroup>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Switch & Slider</p>
<label data-temas-cluster style="justify-content: space-between; cursor: pointer">
<span>Wi-Fi</span>
<Switch bind:checked={wifi}><Switch.Thumb /></Switch>
</label>
<label data-temas-cluster style="justify-content: space-between; cursor: pointer">
<span>Analítica</span>
<Switch bind:checked={analytics} intent="affirm"><Switch.Thumb /></Switch>
</label>
<Separator />
<div data-temas-stack style="gap: var(--space-2)">
<span data-temas-subhead style="margin: 0">Volumen · {volume[0]}%</span>
<Slider bind:value={volume} min={0} max={100} aria-label="Volumen">
<Slider.Range />
<Slider.Thumb index={0} />
</Slider>
</div>
</div>
</div>
</section>
<!-- ═══ Feedback ═══════════════════════════════════════════════ -->
<section data-temas-section id="feedback">
<div data-temas-section-head>
<h2>Feedback y estado</h2>
<p>Banners por intent, progreso, avatares y tarjetas.</p>
</div>
<div data-temas-stack>
<Banner intent="primary" variant="soft">Tu prueba gratuita termina en 7 días. Mejora tu plan cuando quieras.</Banner>
<Banner intent="fulfill" variant="soft">Cambios guardados correctamente.</Banner>
<Banner intent="risk" variant="soft">Tu método de pago caduca pronto.</Banner>
<Banner intent="threat" variant="soft">No se pudo procesar el pago. Revisa tus datos.</Banner>
</div>
<div data-temas-fieldset>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Progress</p>
<Progress value={64} max={100}>
<Progress.Label>Subiendo archivo</Progress.Label>
<Progress.ValueText />
<Progress.Indicator />
</Progress>
<div data-temas-cluster>
<Progress value={72} max={100} shape="circular"><Progress.Indicator /></Progress>
<Progress value={30} max={100} shape="circular"><Progress.Indicator /></Progress>
</div>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Avatares</p>
<div data-temas-cluster>
<Avatar><Avatar.Fallback>OV</Avatar.Fallback></Avatar>
<Avatar color="primary"><Avatar.Fallback>AM</Avatar.Fallback></Avatar>
<Avatar color="affirm" variant="soft">
<Avatar.Fallback>JV</Avatar.Fallback>
<Avatar.Badge color="fulfill" position="bottom-end" dot />
</Avatar>
<Avatar color="risk" ring="soft"><Avatar.Fallback>SK</Avatar.Fallback></Avatar>
</div>
</div>
<Card variant="soft" color="primary">
<Card.Header>
<Card.Title>Plan Pro</Card.Title>
<Card.Description>Todo lo del Starter, más colaboración ilimitada.</Card.Description>
</Card.Header>
<Card.Body>
<strong style="font-size: var(--font-size-xl)">$29</strong>
<span style="color: var(--color-content-muted)"> / mes</span>
</Card.Body>
<Card.Footer>
<Button block icon={arrowIcon}>Elegir Pro</Button>
</Card.Footer>
</Card>
</div>
</section>
<!-- ═══ Tabs ═══════════════════════════════════════════════════ -->
<section data-temas-section id="tabs">
<div data-temas-section-head>
<h2>Tabs</h2>
<p>Variantes line y pills. Indicador animado, foco con anillo carbón, activo en color primario.</p>
</div>
<div data-temas-fieldset>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Line</p>
<Tabs bind:value={tab} variant="line">
<Tabs.List aria-label="Secciones de ejemplo">
<Tabs.Trigger value="overview">Overview</Tabs.Trigger>
<Tabs.Trigger value="activity">Activity</Tabs.Trigger>
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
<Tabs.Indicator />
</Tabs.List>
<Tabs.Content value="overview">Resumen de la cuenta y métricas clave del mes.</Tabs.Content>
<Tabs.Content value="activity">Registro de actividad reciente del equipo.</Tabs.Content>
<Tabs.Content value="settings">Preferencias de notificación y seguridad.</Tabs.Content>
</Tabs>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Pills</p>
<Tabs bind:value={tabsPills} variant="pills">
<Tabs.List aria-label="Secciones de ejemplo">
<Tabs.Trigger value="account">Account</Tabs.Trigger>
<Tabs.Trigger value="team">Team</Tabs.Trigger>
<Tabs.Trigger value="billing">Billing</Tabs.Trigger>
<Tabs.Indicator />
</Tabs.List>
<Tabs.Content value="account">Datos de perfil y sesión.</Tabs.Content>
<Tabs.Content value="team">Miembros e invitaciones.</Tabs.Content>
<Tabs.Content value="billing">Plan, método de pago y facturas.</Tabs.Content>
</Tabs>
</div>
</div>
</section>
<!-- ═══ Disclosure ═════════════════════════════════════════════ -->
<section data-temas-section id="disclosure">
<div data-temas-section-head>
<h2>Disclosure</h2>
<p>Accordion (outline) — apertura única, chevron y foco coherentes con el tema.</p>
</div>
<div data-temas-panel>
<Accordion bind:value={faq} type="single" variant="outline">
<Accordion.Item value="what">
<Accordion.Header><Accordion.Trigger>¿Qué es activeUIX?</Accordion.Trigger></Accordion.Header>
<Accordion.Content>
Un sistema de UI en cuatro capas — morfo, soma, sema y eidos — donde el theming vive en
eidos y retinta los roles canónicos.
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="theme">
<Accordion.Header><Accordion.Trigger>¿Cómo se crea un tema?</Accordion.Trigger></Accordion.Header>
<Accordion.Content>
Con <code>ActiveEidos.create(&#123; config &#125;)</code>: se remapean los 9 roles a paletas
y se ajustan primitivas estables (tipografía, radio, sombra).
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="dark">
<Accordion.Header><Accordion.Trigger>¿Y el modo oscuro?</Accordion.Trigger></Accordion.Header>
<Accordion.Content>
El tema declara escalas light y dark; el primario carbón se invierte solo gracias al slot
<code>contrast</code>.
</Accordion.Content>
</Accordion.Item>
</Accordion>
</div>
</section>
<!-- ═══ Toggles ════════════════════════════════════════════════ -->
<section data-temas-section id="toggles">
<div data-temas-section-head>
<h2>Toggles</h2>
<p>Toggle suelto y toggle-group (segmentado, single + multiple). Activo en color primario.</p>
</div>
<div data-temas-demo>
<div data-temas-demo-block>
<p data-temas-subhead>Toggle</p>
<div data-temas-cluster>
<Toggle bind:pressed={bold}>Bold</Toggle>
<Toggle variant="outline">Outline</Toggle>
<Toggle variant="ghost">Ghost</Toggle>
<Toggle disabled>Disabled</Toggle>
</div>
</div>
<div data-temas-demo-block>
<p data-temas-subhead>Toggle group · segmentado</p>
<div data-temas-cluster>
<ToggleGroup type="multiple" attached bind:value={fmt} aria-label="Formato de texto">
<ToggleGroup.Item value="bold">B</ToggleGroup.Item>
<ToggleGroup.Item value="italic">I</ToggleGroup.Item>
<ToggleGroup.Item value="underline">U</ToggleGroup.Item>
</ToggleGroup>
<ToggleGroup type="single" attached bind:value={align} aria-label="Alineación">
<ToggleGroup.Item value="left">Izq.</ToggleGroup.Item>
<ToggleGroup.Item value="center">Centro</ToggleGroup.Item>
<ToggleGroup.Item value="right">Der.</ToggleGroup.Item>
</ToggleGroup>
</div>
</div>
</div>
</section>
<!-- ═══ Toolbar ════════════════════════════════════════════════ -->
<section data-temas-section id="toolbar">
<div data-temas-section-head>
<h2>Toolbar</h2>
<p>Barra de herramientas con botones, separador y grupo segmentado.</p>
</div>
<div data-temas-panel>
<Toolbar aria-label="Editor">
<Toolbar.Button>Deshacer</Toolbar.Button>
<Toolbar.Button>Rehacer</Toolbar.Button>
<Toolbar.Separator />
<Toolbar.Group type="multiple" bind:value={toolbarFmt} aria-label="Formato">
<Toolbar.GroupItem value="bold">B</Toolbar.GroupItem>
<Toolbar.GroupItem value="italic">I</Toolbar.GroupItem>
<Toolbar.GroupItem value="underline">U</Toolbar.GroupItem>
</Toolbar.Group>
<Toolbar.Separator />
<Toolbar.Button>Compartir</Toolbar.Button>
</Toolbar>
</div>
</section>
<!-- ═══ Overlays ═══════════════════════════════════════════════ -->
<section data-temas-section id="overlays">
<div data-temas-section-head>
<h2>Overlays</h2>
<p>Popover y dialog — paneles portaleados con la superficie, borde y sombra del tema.</p>
</div>
<div data-temas-panel data-temas-cluster>
<Popover>
<Popover.Trigger>Abrir popover</Popover.Trigger>
<Popover.Portal>
<Popover.Content side="bottom" align="start" sideOffset={8}>
<Popover.Arrow />
<Popover.Title>Dimensiones</Popover.Title>
<Popover.Description>Ajusta el ancho del panel lateral.</Popover.Description>
<div data-temas-cluster style="margin-block-start: var(--space-3)">
<Button size="sm">Aplicar</Button>
<Popover.Close>Cerrar</Popover.Close>
</div>
</Popover.Content>
</Popover.Portal>
</Popover>
<Dialog>
<Dialog.Trigger>Abrir dialog</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>¿Eliminar proyecto?</Dialog.Title>
<Dialog.Description>
Esta acción es permanente y no se puede deshacer.
</Dialog.Description>
</Dialog.Header>
<Dialog.Footer>
<Dialog.Close>Cancelar</Dialog.Close>
<Button intent="threat">Eliminar</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
</div>
</section>
<!-- ═══ Menús ══════════════════════════════════════════════════ -->
<section data-temas-section id="menus">
<div data-temas-section-head>
<h2>Menús</h2>
<p>Dropdown, menubar, context menu y tooltip — todos portaleados, con la sombra del tema.</p>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Menubar</p>
<Menubar bind:value={menubarValue}>
<Menubar.Menu value="file">
<Menubar.Trigger>Archivo</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item>Nuevo documento</Menubar.Item>
<Menubar.Item>Abrir…</Menubar.Item>
<Menubar.Separator />
<Menubar.Item>Guardar</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu value="edit">
<Menubar.Trigger>Editar</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item>Deshacer</Menubar.Item>
<Menubar.Item>Rehacer</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu value="view">
<Menubar.Trigger>Ver</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.CheckboxItem bind:checked={menuNotifications}>
Mostrar barra lateral
</Menubar.CheckboxItem>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar>
<div data-temas-cluster>
<DropdownMenu>
<DropdownMenu.Trigger variant="outline">Opciones</DropdownMenu.Trigger>
<DropdownMenu.Content side="bottom" align="start" sideOffset={6}>
<DropdownMenu.Group>
<DropdownMenu.GroupHeading>Cuenta</DropdownMenu.GroupHeading>
<DropdownMenu.Item>Perfil</DropdownMenu.Item>
<DropdownMenu.Item>Facturación</DropdownMenu.Item>
</DropdownMenu.Group>
<DropdownMenu.Separator />
<DropdownMenu.CheckboxItem bind:checked={menuNotifications}>
Notificaciones
</DropdownMenu.CheckboxItem>
<DropdownMenu.Separator />
<DropdownMenu.RadioGroup bind:value={menuTheme}>
<DropdownMenu.RadioItem value="light">Claro</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="dark">Oscuro</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="system">Sistema</DropdownMenu.RadioItem>
</DropdownMenu.RadioGroup>
</DropdownMenu.Content>
</DropdownMenu>
<Tooltip.Group delayDuration={250}>
<Tooltip>
<Tooltip.Trigger>Pasa el ratón</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content>
<Tooltip.Arrow />
Tooltip con la superficie y la sombra del tema
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip>
</Tooltip.Group>
</div>
<ContextMenu>
<ContextMenu.Trigger>
<div data-temas-ctx-target>Click derecho aquí</div>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item>Copiar</ContextMenu.Item>
<ContextMenu.Item>Pegar</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item>Duplicar</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu>
</div>
</section>
<p data-temas-group-head>Sistema</p>
<!-- ═══ Semántica ══════════════════════════════════════════════ -->
<section data-temas-section id="sema">
<div data-temas-section-head>
<h2>Semántica (sema)</h2>
<p>
La capa perceptual está activa: cada interacción emite una señal con familia + intent que se
materializa en <strong>sonido</strong> (earcon), <strong>haptic</strong> (Vibration API) y una
<strong>reacción visual</strong> <code>data-event-*</code> teñida por el intent del tema.
Pulsa los botones — en un navegador con gesto de usuario sonarán.
</p>
</div>
<div data-temas-panel data-temas-stack>
<p data-temas-subhead>Intents perceptuales</p>
<div data-temas-cluster>
<Button intent="affirm">Affirm</Button>
<Button intent="fulfill">Fulfill</Button>
<Button intent="risk" variant="soft">Risk</Button>
<Button intent="threat" variant="soft">Threat</Button>
<Button intent="loss" variant="soft">Loss</Button>
</div>
<p style="color: var(--color-content-muted); font-size: var(--font-size-sm)">
El motor (<code>EngineSemantic</code>) se cablea en el layout vía
<code>createActiveUix(&#123; events: &#123; sound, haptic, components &#125; &#125;)</code> con
los packs sema por componente. Sonido y haptic son opt-in; aquí están encendidos.
</p>
</div>
</section>
<footer data-temas-foot>
Tema <code>grafito</code> · construido con <code>ActiveEidos.create(&#123; config &#125;)</code> ·
primario carbón + paleta de 31 escalas; jerarquía aliasada, intents auto-derivados.
</footer>
</div>
</div>

Powered by TurnKey Linux.