|
|
<script lang="ts">
|
|
|
/**
|
|
|
* Grafito — espécimen técnico-editorial del tema.
|
|
|
*
|
|
|
* No es una galería: es un *type specimen* de un sistema de diseño grafito.
|
|
|
* Monocromo + la paleta de 32 como único color. Fundamentos derivados de los
|
|
|
* sets de tokens canónicos del módulo (exhaustivo, sin drift). Componentes
|
|
|
* por familia. Estética en `theme.css` (chrome editorial); los componentes
|
|
|
* se muestran con la tipografía/tokens REALES de Grafito.
|
|
|
*/
|
|
|
import { onMount } from 'svelte'
|
|
|
// ── Componentes (representativos por familia) ─────────────────────
|
|
|
import Button from '$uix/eidos/components/button'
|
|
|
import Toggle from '$uix/eidos/components/toggle'
|
|
|
import ToggleGroup from '$uix/eidos/components/toggle-group'
|
|
|
import Toolbar from '$uix/eidos/components/toolbar'
|
|
|
import Badge from '$uix/eidos/components/badge'
|
|
|
import Avatar from '$uix/eidos/components/avatar'
|
|
|
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 Banner from '$uix/eidos/components/banner'
|
|
|
import Progress from '$uix/eidos/components/progress'
|
|
|
import Separator from '$uix/eidos/components/separator'
|
|
|
import Tabs from '$uix/eidos/components/tabs'
|
|
|
import Accordion from '$uix/eidos/components/accordion'
|
|
|
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'
|
|
|
// ── Token key-sets canónicos + metadatos del tema ────────────────
|
|
|
import {
|
|
|
TEXT_SIZES,
|
|
|
RADIUS_KEYS,
|
|
|
SHADOW_KEYS,
|
|
|
CONTAINER_WIDTH_KEYS,
|
|
|
CONTENT_WIDTH_KEYS,
|
|
|
ASPECT_RATIO_KEYS,
|
|
|
BORDER_WIDTH_KEYS,
|
|
|
BORDER_STYLE_KEYS,
|
|
|
DURATION_KEYS,
|
|
|
EASE_KEYS,
|
|
|
MOTION_DISTANCE_KEYS,
|
|
|
MOTION_SCALE_KEYS,
|
|
|
OPACITY_KEYS,
|
|
|
Z_INDEX_KEYS,
|
|
|
ICON_STROKE_WIDTH_KEYS,
|
|
|
SIZES
|
|
|
} from '$uix/eidos'
|
|
|
import { THEME_SCALES, THEME_HIERARCHY, THEME_INTENTS, COLOR_STEPS } from '../_lib/grafito'
|
|
|
|
|
|
// ── Token sets sin export canónico (espejo de los primitives) ────
|
|
|
const SPACE_KEYS = ['0', '0-5', '1', '1-5', '2', '2-5', '3', '3-5', '4', '4-5', '5', '5-5', '6', '7', '8', '10', '12', '16']
|
|
|
const ICON_SIZE_KEYS = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', 'xxl']
|
|
|
const FONT_FAMILIES = [
|
|
|
{ key: 'primary', label: 'Primary', note: 'Inter — UI / texto' },
|
|
|
{ key: 'secondary', label: 'Secondary', note: 'Inter' },
|
|
|
{ key: 'display', label: 'Display', note: 'Inter — titulares' },
|
|
|
{ key: 'mono', label: 'Mono', note: 'Roboto Mono — código' }
|
|
|
]
|
|
|
const FONT_WEIGHTS = [
|
|
|
{ key: 'regular', value: 400 },
|
|
|
{ key: 'medium', value: 500 },
|
|
|
{ key: 'semibold', value: 600 },
|
|
|
{ key: 'bold', value: 700 }
|
|
|
]
|
|
|
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: 'h5', label: 'Heading 5', sample: 'The quick brown fox jumps over' },
|
|
|
{ key: 'h6', label: 'Heading 6', sample: 'The quick brown fox jumps over' },
|
|
|
{ key: 'body', label: 'Body', sample: 'The quick brown fox jumps over the lazy dog.' },
|
|
|
{ key: 'prose', label: 'Prose', 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 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
|
|
|
// Override por componente: cualquier escala de la paleta.
|
|
|
// A component's color comes from two real axes (NOT arbitrary scale names):
|
|
|
// `color` = hierarchy override, `intent` = evaluative / semantic palette.
|
|
|
const HIERARCHY_OVERRIDES = ['primary', 'secondary', 'neutral'] as const
|
|
|
const INTENT_OVERRIDES = ['affirm', 'fulfill', 'risk', 'threat', 'loss'] as const
|
|
|
|
|
|
// ── Lectura de valores en runtime (precisión) ─────────────────────
|
|
|
let hex = $state<Record<string, string>>({})
|
|
|
let probe = $state<HTMLDivElement | null>(null)
|
|
|
// resolved px metrics keyed by css var name
|
|
|
let resolved = $state<Record<string, string>>({})
|
|
|
|
|
|
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 ? '#101114' : '#ffffff')
|
|
|
|
|
|
function read() {
|
|
|
if (typeof document === 'undefined') return
|
|
|
const cs = getComputedStyle(document.documentElement)
|
|
|
// scale hexes
|
|
|
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
|
|
|
// resolved px metrics via probe
|
|
|
if (!probe) return
|
|
|
const res: Record<string, string> = {}
|
|
|
const measure = (cssVar: string, prop: 'width' | 'height' | 'fontSize') => {
|
|
|
probe!.style.setProperty('--m', `var(${cssVar})`)
|
|
|
probe!.style.width = prop === 'width' ? 'var(--m)' : ''
|
|
|
probe!.style.height = prop === 'height' ? 'var(--m)' : ''
|
|
|
probe!.style.fontSize = prop === 'fontSize' ? 'var(--m)' : ''
|
|
|
const pc = getComputedStyle(probe!)
|
|
|
res[cssVar] = prop === 'width' ? pc.width : prop === 'height' ? pc.height : pc.fontSize
|
|
|
}
|
|
|
for (const k of SPACE_KEYS) measure(`--space-${k}`, 'width')
|
|
|
for (const k of SIZES) measure(`--control-height-${k}`, 'height')
|
|
|
for (const k of TEXT_SIZES) measure(`--font-size-${k}`, 'fontSize')
|
|
|
for (const k of ICON_SIZE_KEYS) measure(`--icon-size-${k}`, 'width')
|
|
|
resolved = res
|
|
|
}
|
|
|
const raw = (cssVar: string) => {
|
|
|
if (typeof document === 'undefined') return ''
|
|
|
return getComputedStyle(document.documentElement).getPropertyValue(cssVar).trim()
|
|
|
}
|
|
|
let rawState = $state<Record<string, string>>({})
|
|
|
function readRaw() {
|
|
|
const o: Record<string, string> = {}
|
|
|
for (const k of RADIUS_KEYS) o[`--radius-${k}`] = raw(`--radius-${k}`)
|
|
|
for (const k of BORDER_WIDTH_KEYS) o[`--border-width-${k}`] = raw(`--border-width-${k}`)
|
|
|
for (const k of SHADOW_KEYS) o[`--shadow-${k}`] = raw(`--shadow-${k}`)
|
|
|
for (const k of CONTAINER_WIDTH_KEYS) o[`--container-width-${k}`] = raw(`--container-width-${k}`)
|
|
|
for (const k of CONTENT_WIDTH_KEYS) o[`--content-width-${k}`] = raw(`--content-width-${k}`)
|
|
|
for (const k of ASPECT_RATIO_KEYS) o[`--aspect-ratio-${k}`] = raw(`--aspect-ratio-${k}`)
|
|
|
for (const k of OPACITY_KEYS) o[`--opacity-${k}`] = raw(`--opacity-${k}`)
|
|
|
for (const k of Z_INDEX_KEYS) o[`--z-index-${k}`] = raw(`--z-index-${k}`)
|
|
|
for (const k of DURATION_KEYS) o[`--duration-${k}`] = raw(`--duration-${k}`)
|
|
|
for (const k of MOTION_DISTANCE_KEYS) o[`--motion-distance-${k}`] = raw(`--motion-distance-${k}`)
|
|
|
for (const k of ICON_STROKE_WIDTH_KEYS) o[`--icon-stroke-width-${k}`] = raw(`--icon-stroke-width-${k}`)
|
|
|
rawState = o
|
|
|
}
|
|
|
|
|
|
// ── TOC + scrollspy ───────────────────────────────────────────────
|
|
|
const TOC = [
|
|
|
{
|
|
|
group: 'Color',
|
|
|
items: [{ id: 'color', n: '01', label: 'Color & paleta' }]
|
|
|
},
|
|
|
{
|
|
|
group: 'Fundamentos',
|
|
|
items: [
|
|
|
{ id: 'type', n: '02', label: 'Tipografía' },
|
|
|
{ id: 'space', n: '03', label: 'Espaciado & tamaño' },
|
|
|
{ id: 'shape', n: '04', label: 'Radio · borde · sombra' },
|
|
|
{ id: 'icon', n: '05', label: 'Iconos & focus' },
|
|
|
{ id: 'layout', n: '06', label: 'Layout' },
|
|
|
{ id: 'system', n: '07', label: 'Z-index · opacidad · motion' },
|
|
|
{ id: 'axes', n: '08', label: 'Densidad & scaling' }
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
group: 'Componentes',
|
|
|
items: [
|
|
|
{ id: 'actions', n: '09', label: 'Acciones' },
|
|
|
{ id: 'forms', n: '10', label: 'Formulario' },
|
|
|
{ id: 'feedback', n: '11', label: 'Feedback' },
|
|
|
{ id: 'nav', n: '12', label: 'Navegación & disclosure' },
|
|
|
{ id: 'overlays', n: '13', label: 'Overlays & menús' }
|
|
|
]
|
|
|
},
|
|
|
{ group: 'Sistema', items: [{ id: 'sema', n: '14', label: 'Semántica' }] }
|
|
|
]
|
|
|
let activeSection = $state('color')
|
|
|
|
|
|
// ── Estado de controles interactivos ─────────────────────────────
|
|
|
let email = $state('olivia@grafito.dev')
|
|
|
let fruit = $state<string[]>(['mango'])
|
|
|
let agree = $state(true)
|
|
|
let plan = $state('pro')
|
|
|
let wifi = $state(true)
|
|
|
let volume = $state([64])
|
|
|
let tab = $state('overview')
|
|
|
let faq = $state<string[]>(['what'])
|
|
|
let bold = $state(true)
|
|
|
let fmt = $state<string[]>(['bold'])
|
|
|
let toolbarFmt = $state<string[]>(['bold'])
|
|
|
let menuNotifications = $state(true)
|
|
|
let menuTheme = $state('system')
|
|
|
let menubarValue = $state('')
|
|
|
let axis = $state({ density: 'comfortable', scaling: '100' })
|
|
|
|
|
|
onMount(() => {
|
|
|
read()
|
|
|
readRaw()
|
|
|
const refresh = () => {
|
|
|
read()
|
|
|
readRaw()
|
|
|
axis = {
|
|
|
density: document.documentElement.getAttribute('data-density') ?? 'comfortable',
|
|
|
scaling: document.documentElement.getAttribute('data-scaling') ?? '100'
|
|
|
}
|
|
|
}
|
|
|
refresh()
|
|
|
const mo = new MutationObserver(refresh)
|
|
|
mo.observe(document.documentElement, {
|
|
|
attributes: true,
|
|
|
attributeFilter: ['data-mode', 'data-theme', 'data-density', 'data-scaling']
|
|
|
})
|
|
|
const sections = [...document.querySelectorAll('section[data-spec-section][id]')]
|
|
|
const io = new IntersectionObserver(
|
|
|
(entries) => {
|
|
|
for (const e of entries) if (e.isIntersecting) activeSection = (e.target as HTMLElement).id
|
|
|
},
|
|
|
{ rootMargin: '-12% 0px -70% 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}
|
|
|
|
|
|
<!-- sonda oculta para métricas resueltas -->
|
|
|
<div bind:this={probe} aria-hidden="true" data-spec-probe></div>
|
|
|
|
|
|
<!-- ═══ Hero ═══════════════════════════════════════════════════════ -->
|
|
|
<header data-spec-hero>
|
|
|
<p data-spec-eyebrow>activeUIX · Eidos · type specimen</p>
|
|
|
<h1 data-spec-display>Grafito</h1>
|
|
|
<p data-spec-lede>
|
|
|
Un sistema de diseño <em>grafito</em>: carbón monocromo, una paleta de <strong>32 escalas</strong>,
|
|
|
y la gramática de color del libro. La jerarquía aliasa la paleta; los intents auto-derivan.
|
|
|
</p>
|
|
|
<dl data-spec-stats>
|
|
|
<div><dt>Paleta</dt><dd>32 escalas</dd></div>
|
|
|
<div><dt>Roles</dt><dd>3 jerarquía · 6 intents</dd></div>
|
|
|
<div><dt>Tipografía</dt><dd>Inter · {TYPE_STYLES.length} estilos</dd></div>
|
|
|
<div><dt>Modos</dt><dd>light · dark · density · scaling</dd></div>
|
|
|
</dl>
|
|
|
</header>
|
|
|
|
|
|
<div data-spec-layout>
|
|
|
<aside data-spec-toc>
|
|
|
<nav aria-label="Índice del espécimen">
|
|
|
{#each TOC as grp}
|
|
|
<p data-spec-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)}>
|
|
|
<span data-spec-toc-n>{it.n}</span>{it.label}
|
|
|
</a>
|
|
|
</li>
|
|
|
{/each}
|
|
|
</ul>
|
|
|
{/each}
|
|
|
</nav>
|
|
|
</aside>
|
|
|
|
|
|
<main data-spec-content>
|
|
|
<!-- ═══ 01 · Color ═══════════════════════════════════════════ -->
|
|
|
<section data-spec-section id="color">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>01</span>
|
|
|
<div>
|
|
|
<h2 data-spec-display>Color & paleta</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 por la convención del libro (identidad step 9).</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<p data-spec-label>Paleta · 32 escalas · cada paso usable como <code>var(--scale-blue-10)</code></p>
|
|
|
<div data-spec-palette>
|
|
|
{#each THEME_SCALES as sc}
|
|
|
<div data-spec-scale>
|
|
|
<div data-spec-scale-head>
|
|
|
<strong>{sc.label}</strong>
|
|
|
<code>{hex[`${sc.name}-9`] ?? ''}</code>
|
|
|
</div>
|
|
|
<div data-spec-scale-ramp>
|
|
|
{#each COLOR_STEPS as step}
|
|
|
<i style="background: var(--scale-{sc.name}-{step})" title="{sc.name}-{step} · {hex[`${sc.name}-${step}`] ?? ''}"></i>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
<div data-spec-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 data-spec-cols2>
|
|
|
<div>
|
|
|
<p data-spec-label>Roles de jerarquía · alias a la paleta</p>
|
|
|
<div data-spec-roles>
|
|
|
{#each THEME_HIERARCHY as r}
|
|
|
<div data-spec-role>
|
|
|
<span data-spec-role-chip style="background: var(--color-{r.role}-solid)"></span>
|
|
|
<div>
|
|
|
<strong>{r.label}</strong>
|
|
|
<small>→ <code>{r.scale}</code></small>
|
|
|
</div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p data-spec-label>Intents · auto-derivados (step 9)</p>
|
|
|
<div data-spec-roles>
|
|
|
{#each THEME_INTENTS as it}
|
|
|
<div data-spec-role>
|
|
|
<span data-spec-role-chip style="background: var(--color-{it.role}-solid)"></span>
|
|
|
<div>
|
|
|
<strong>{it.label}</strong>
|
|
|
<small>auto → <code>{it.scale}</code></small>
|
|
|
</div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<p data-spec-label>Superficies · contenido · bordes</p>
|
|
|
<div data-spec-surfaces>
|
|
|
<div data-spec-surface style="background: var(--color-surface-default)"><span>surface</span><code>default</code></div>
|
|
|
<div data-spec-surface style="background: var(--color-surface-raised)"><span>surface</span><code>raised</code></div>
|
|
|
<div data-spec-surface style="background: var(--color-surface-muted)"><span>surface</span><code>muted</code></div>
|
|
|
<div data-spec-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-spec-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-spec-surface style="border-color: var(--color-border-strong); border-width: 2px"><span>border</span><code>strong</code></div>
|
|
|
</div>
|
|
|
|
|
|
<p data-spec-label>Override jerárquico · prop <code>color</code></p>
|
|
|
<div data-spec-cluster>
|
|
|
{#each HIERARCHY_OVERRIDES as c}
|
|
|
<Button color={c} variant="soft">{c}</Button>
|
|
|
{/each}
|
|
|
</div>
|
|
|
|
|
|
<p data-spec-label>Color semántico · prop <code>intent</code></p>
|
|
|
<div data-spec-cluster>
|
|
|
{#each INTENT_OVERRIDES as it}
|
|
|
<Button intent={it} variant="soft">{it}</Button>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 02 · Tipografía ══════════════════════════════════════ -->
|
|
|
<section data-spec-section id="type">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>02</span>
|
|
|
<div><h2 data-spec-display>Tipografía</h2><p>Familias, escala de tamaños, pesos y los estilos nombrados del sistema.</p></div>
|
|
|
</div>
|
|
|
|
|
|
<p data-spec-label>Familias</p>
|
|
|
<div data-spec-cols2>
|
|
|
{#each FONT_FAMILIES as f}
|
|
|
<div data-spec-type-fam>
|
|
|
<span style="font-family: var(--font-{f.key === 'mono' ? 'mono' : f.key === 'display' ? 'display' : f.key === 'secondary' ? 'secondary' : 'sans'})">Ag</span>
|
|
|
<div><strong>{f.label}</strong><small>{f.note}</small></div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
|
|
|
<p data-spec-label>Escala de tamaños</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each TEXT_SIZES as k}
|
|
|
<div data-spec-type-row>
|
|
|
<code>--font-size-{k}</code>
|
|
|
<span data-spec-type-val>{resolved[`--font-size-${k}`] ?? ''}</span>
|
|
|
<span data-spec-type-sample style="font-size: var(--font-size-{k})">Grafito</span>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
|
|
|
<div data-spec-cols2>
|
|
|
<div>
|
|
|
<p data-spec-label>Pesos</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each FONT_WEIGHTS as w}
|
|
|
<div data-spec-type-row>
|
|
|
<code>{w.key}</code>
|
|
|
<span data-spec-type-val>{w.value}</span>
|
|
|
<span data-spec-type-sample style="font-weight: var(--font-weight-{w.key}); font-size: 18px">Grafito</span>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p data-spec-label>Estilos nombrados</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each TYPE_STYLES as t}
|
|
|
<div data-spec-style-row>
|
|
|
<code>--style-{t.key}</code>
|
|
|
<span 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}</span>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 03 · Espaciado & tamaño ══════════════════════════════ -->
|
|
|
<section data-spec-section id="space">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>03</span>
|
|
|
<div><h2 data-spec-display>Espaciado & tamaño</h2><p>La escala de espacio (base 4px) y las alturas de control. Ambas escalan con densidad + scaling.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-cols2>
|
|
|
<div>
|
|
|
<p data-spec-label>Espaciado · {SPACE_KEYS.length} pasos</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each SPACE_KEYS as k}
|
|
|
<div data-spec-metric-row>
|
|
|
<code>--space-{k}</code>
|
|
|
<span data-spec-metric-val>{resolved[`--space-${k}`] ?? ''}</span>
|
|
|
<div data-spec-metric-bar><i style="inline-size: var(--space-{k})"></i></div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p data-spec-label>Tamaños · control-height ({SIZES.length})</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each SIZES as k}
|
|
|
<div data-spec-metric-row>
|
|
|
<code>--control-height-{k}</code>
|
|
|
<span data-spec-metric-val>{resolved[`--control-height-${k}`] ?? ''}</span>
|
|
|
<div data-spec-size-bar style="block-size: var(--control-height-{k})">{k}</div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 04 · Radio · borde · sombra ══════════════════════════ -->
|
|
|
<section data-spec-section id="shape">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>04</span>
|
|
|
<div><h2 data-spec-display>Radio · borde · sombra</h2><p>Geometría y elevación. No escalan con scaling (chrome nítido).</p></div>
|
|
|
</div>
|
|
|
<p data-spec-label>Radio · {RADIUS_KEYS.length}</p>
|
|
|
<div data-spec-tiles>
|
|
|
{#each RADIUS_KEYS as k}
|
|
|
<div data-spec-tile style="border-radius: var(--radius-{k})"><span>{k}</span><code>{rawState[`--radius-${k}`] ?? ''}</code></div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
<div data-spec-cols2>
|
|
|
<div>
|
|
|
<p data-spec-label>Borde · anchos ({BORDER_WIDTH_KEYS.length}) + estilos ({BORDER_STYLE_KEYS.length})</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each BORDER_WIDTH_KEYS as k}
|
|
|
<div data-spec-metric-row>
|
|
|
<code>--border-width-{k}</code>
|
|
|
<span data-spec-metric-val>{rawState[`--border-width-${k}`] ?? ''}</span>
|
|
|
<div data-spec-border-bar style="border-block-end: var(--border-width-{k}) solid var(--color-content-primary)"></div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
{#each BORDER_STYLE_KEYS as k}
|
|
|
<div data-spec-metric-row>
|
|
|
<code>--border-style-{k}</code>
|
|
|
<span data-spec-metric-val>{k}</span>
|
|
|
<div data-spec-border-bar style="border-block-end: 2px {k} var(--color-content-primary)"></div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p data-spec-label>Sombra · {SHADOW_KEYS.length}</p>
|
|
|
<div data-spec-tiles>
|
|
|
{#each SHADOW_KEYS as k}
|
|
|
<div data-spec-shadow-tile style="box-shadow: var(--shadow-{k})">{k}</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 05 · Iconos & focus ══════════════════════════════════ -->
|
|
|
<section data-spec-section id="icon">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>05</span>
|
|
|
<div><h2 data-spec-display>Iconos & focus</h2><p>Tamaños de icono, grosor de trazo y el anillo de foco.</p></div>
|
|
|
</div>
|
|
|
<p data-spec-label>Tamaños de icono · {ICON_SIZE_KEYS.length}</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each ICON_SIZE_KEYS as k}
|
|
|
<div data-spec-metric-row>
|
|
|
<code>--icon-size-{k}</code>
|
|
|
<span data-spec-metric-val>{resolved[`--icon-size-${k}`] ?? ''}</span>
|
|
|
<svg style="inline-size: var(--icon-size-{k}); block-size: var(--icon-size-{k})" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="9" /><path d="M12 7v10M7 12h10" /></svg>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
<p data-spec-label>Stroke-width ({ICON_STROKE_WIDTH_KEYS.length}) · focus ring</p>
|
|
|
<div data-spec-cluster>
|
|
|
{#each ICON_STROKE_WIDTH_KEYS as k}
|
|
|
<svg style="inline-size: 28px; block-size: 28px" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width={rawState[`--icon-stroke-width-${k}`] || 1.5} aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
|
|
|
{/each}
|
|
|
<button type="button" data-spec-focus-demo>focus ring</button>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 06 · Layout ══════════════════════════════════════════ -->
|
|
|
<section data-spec-section id="layout">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>06</span>
|
|
|
<div><h2 data-spec-display>Layout</h2><p>Anchos de contenedor y de contenido, y relaciones de aspecto.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-cols2>
|
|
|
<div>
|
|
|
<p data-spec-label>Container widths ({CONTAINER_WIDTH_KEYS.length}) · content widths ({CONTENT_WIDTH_KEYS.length})</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each CONTAINER_WIDTH_KEYS as k}
|
|
|
<div data-spec-metric-row><code>--container-width-{k}</code><span data-spec-metric-val>{rawState[`--container-width-${k}`] ?? ''}</span></div>
|
|
|
{/each}
|
|
|
{#each CONTENT_WIDTH_KEYS as k}
|
|
|
<div data-spec-metric-row><code>--content-width-{k}</code><span data-spec-metric-val>{rawState[`--content-width-${k}`] ?? ''}</span></div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p data-spec-label>Aspect ratios · {ASPECT_RATIO_KEYS.length}</p>
|
|
|
<div data-spec-tiles>
|
|
|
{#each ASPECT_RATIO_KEYS as k}
|
|
|
<div data-spec-aspect-tile style="aspect-ratio: var(--aspect-ratio-{k})"><span>{k}</span><code>{rawState[`--aspect-ratio-${k}`] ?? ''}</code></div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 07 · Z-index · opacidad · motion ═════════════════════ -->
|
|
|
<section data-spec-section id="system">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>07</span>
|
|
|
<div><h2 data-spec-display>Z-index · opacidad · motion</h2><p>Capas, transparencias y la gramática de movimiento.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-cols3>
|
|
|
<div>
|
|
|
<p data-spec-label>Z-index · {Z_INDEX_KEYS.length}</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each Z_INDEX_KEYS as k}
|
|
|
<div data-spec-metric-row><code>--z-index-{k}</code><span data-spec-metric-val>{rawState[`--z-index-${k}`] ?? ''}</span></div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p data-spec-label>Opacidad · {OPACITY_KEYS.length}</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each OPACITY_KEYS as k}
|
|
|
<div data-spec-metric-row>
|
|
|
<code>--opacity-{k}</code><span data-spec-metric-val>{rawState[`--opacity-${k}`] ?? ''}</span>
|
|
|
<div data-spec-opacity-chip style="opacity: var(--opacity-{k})"></div>
|
|
|
</div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p data-spec-label>Motion · {DURATION_KEYS.length} dur · {EASE_KEYS.length} ease · {MOTION_DISTANCE_KEYS.length} dist · {MOTION_SCALE_KEYS.length} scale</p>
|
|
|
<div data-spec-rows>
|
|
|
{#each DURATION_KEYS as k}
|
|
|
<div data-spec-metric-row><code>--duration-{k}</code><span data-spec-metric-val>{rawState[`--duration-${k}`] ?? ''}</span></div>
|
|
|
{/each}
|
|
|
{#each EASE_KEYS as k}
|
|
|
<div data-spec-metric-row><code>--ease-{k}</code></div>
|
|
|
{/each}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 08 · Densidad & scaling ══════════════════════════════ -->
|
|
|
<section data-spec-section id="axes">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>08</span>
|
|
|
<div><h2 data-spec-display>Densidad & scaling</h2><p>Dos ejes ortogonales (en la barra superior). Densidad = ritmo de layout; scaling = zoom global incluida la tipografía.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-cols2>
|
|
|
<div data-spec-axis-card>
|
|
|
<p data-spec-label>Densidad · {axis.density}</p>
|
|
|
<p data-spec-axis-note>Espacio + altura de control. La tipografía NO cambia.</p>
|
|
|
<div data-spec-axis-metrics>
|
|
|
<div><code>--space-4</code><strong>{resolved['--space-4'] ?? ''}</strong></div>
|
|
|
<div><code>--control-height-md</code><strong>{resolved['--control-height-md'] ?? ''}</strong></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div data-spec-axis-card>
|
|
|
<p data-spec-label>Scaling · {axis.scaling}%</p>
|
|
|
<p data-spec-axis-note>Zoom global — escala todo, incluida la tipografía.</p>
|
|
|
<div data-spec-axis-metrics>
|
|
|
<div><code>--font-size-md</code><strong>{resolved['--font-size-md'] ?? ''}</strong></div>
|
|
|
<div><code>--space-4</code><strong>{resolved['--space-4'] ?? ''}</strong></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 09 · Acciones ════════════════════════════════════════ -->
|
|
|
<section data-spec-section id="actions">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>09</span>
|
|
|
<div><h2 data-spec-display>Acciones</h2><p>Button · Toggle · ToggleGroup · Toolbar. El texto sobre sólidos claros (amber/orange) resuelve a oscuro por luminancia.</p></div>
|
|
|
</div>
|
|
|
<p data-spec-label>Variants · roles · tamaños · estados</p>
|
|
|
<div data-spec-stack>
|
|
|
<div data-spec-cluster>{#each BTN_VARIANTS as v}<Button variant={v}>{v}</Button>{/each}</div>
|
|
|
<div data-spec-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 data-spec-cluster>{#each BTN_SIZES as sz}<Button size={sz}>{sz}</Button>{/each}</div>
|
|
|
<div data-spec-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>
|
|
|
<p data-spec-label>Toggle · ToggleGroup · Toolbar</p>
|
|
|
<div data-spec-cluster>
|
|
|
<Toggle bind:pressed={bold}>Bold</Toggle>
|
|
|
<Toggle variant="outline">Outline</Toggle>
|
|
|
<ToggleGroup type="multiple" attached bind:value={fmt} aria-label="Formato">
|
|
|
<ToggleGroup.Item value="bold">B</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="italic">I</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="underline">U</ToggleGroup.Item>
|
|
|
</ToggleGroup>
|
|
|
</div>
|
|
|
<div data-spec-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>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 10 · Formulario ══════════════════════════════════════ -->
|
|
|
<section data-spec-section id="forms">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>10</span>
|
|
|
<div><h2 data-spec-display>Formulario</h2><p>Field · Select · Checkbox · Radio · Switch · Slider.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-cols2>
|
|
|
<div data-spec-panel data-spec-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.Viewport></Select.Content></Select.Portal>
|
|
|
</Select>
|
|
|
</Field>
|
|
|
</div>
|
|
|
<div data-spec-panel data-spec-stack>
|
|
|
<label data-spec-cluster style="cursor:pointer"><Checkbox bind:checked={agree}><Checkbox.Indicator /></Checkbox><span>Acepto los términos</span></label>
|
|
|
<Separator />
|
|
|
<RadioGroup bind:value={plan}>
|
|
|
<RadioGroup.Label data-spec-label>Plan</RadioGroup.Label>
|
|
|
<label data-spec-cluster style="cursor:pointer"><RadioGroup.Item value="starter"><RadioGroup.Indicator /></RadioGroup.Item><span>Starter</span></label>
|
|
|
<label data-spec-cluster style="cursor:pointer"><RadioGroup.Item value="pro"><RadioGroup.Indicator /></RadioGroup.Item><span>Pro</span></label>
|
|
|
</RadioGroup>
|
|
|
<Separator />
|
|
|
<label data-spec-cluster style="justify-content:space-between;cursor:pointer"><span>Wi-Fi</span><Switch bind:checked={wifi}><Switch.Thumb /></Switch></label>
|
|
|
<div data-spec-stack style="gap: var(--space-2)">
|
|
|
<span data-spec-label 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>
|
|
|
|
|
|
<!-- ═══ 11 · Feedback ════════════════════════════════════════ -->
|
|
|
<section data-spec-section id="feedback">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>11</span>
|
|
|
<div><h2 data-spec-display>Feedback & estado</h2><p>Banner · Progress · Avatar · Badge · Card.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-stack>
|
|
|
<Banner intent="primary" variant="soft">Tu prueba gratuita termina en 7 días.</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.</Banner>
|
|
|
</div>
|
|
|
<div data-spec-cols2>
|
|
|
<div data-spec-panel data-spec-stack>
|
|
|
<p data-spec-label>Progress · Badges</p>
|
|
|
<Progress value={64} max={100}><Progress.Label>Subiendo</Progress.Label><Progress.ValueText /><Progress.Indicator /></Progress>
|
|
|
<div data-spec-cluster>{#each BADGE_COLORS as c}<Badge color={c} dot>{c}</Badge>{/each}</div>
|
|
|
<div data-spec-cluster>{#each CHIP_VARIANTS as v}<Badge color="primary" variant={v}>{v}</Badge>{/each}</div>
|
|
|
</div>
|
|
|
<div data-spec-panel data-spec-stack>
|
|
|
<p data-spec-label>Avatares</p>
|
|
|
<div data-spec-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>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<Card variant="soft" color="primary">
|
|
|
<Card.Header><Card.Title>Plan Pro</Card.Title><Card.Description>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>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 12 · Navegación & disclosure ═════════════════════════ -->
|
|
|
<section data-spec-section id="nav">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>12</span>
|
|
|
<div><h2 data-spec-display>Navegación & disclosure</h2><p>Tabs · Accordion.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-panel data-spec-stack>
|
|
|
<Tabs bind:value={tab} variant="line">
|
|
|
<Tabs.List aria-label="Secciones"><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.</Tabs.Content>
|
|
|
<Tabs.Content value="activity">Actividad reciente.</Tabs.Content>
|
|
|
<Tabs.Content value="settings">Preferencias.</Tabs.Content>
|
|
|
</Tabs>
|
|
|
</div>
|
|
|
<div data-spec-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.</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</code>: paleta + roles; los intents auto-derivan.</Accordion.Content></Accordion.Item>
|
|
|
</Accordion>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 13 · Overlays & menús ════════════════════════════════ -->
|
|
|
<section data-spec-section id="overlays">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>13</span>
|
|
|
<div><h2 data-spec-display>Overlays & menús</h2><p>Popover · Dialog · Dropdown · Context · Menubar · Tooltip — portaleados con la superficie y sombra del tema.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-panel data-spec-cluster>
|
|
|
<Popover>
|
|
|
<Popover.Trigger>Popover</Popover.Trigger>
|
|
|
<Popover.Portal><Popover.Content side="bottom" align="start" sideOffset={8}><Popover.Arrow /><Popover.Title>Dimensiones</Popover.Title><Popover.Description>Ajusta el panel.</Popover.Description><div data-spec-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>Dialog</Dialog.Trigger>
|
|
|
<Dialog.Portal><Dialog.Overlay /><Dialog.Content><Dialog.Header><Dialog.Title>¿Eliminar?</Dialog.Title><Dialog.Description>Acción permanente.</Dialog.Description></Dialog.Header><Dialog.Footer><Dialog.Close>Cancelar</Dialog.Close><Button intent="threat">Eliminar</Button></Dialog.Footer></Dialog.Content></Dialog.Portal>
|
|
|
</Dialog>
|
|
|
<DropdownMenu>
|
|
|
<DropdownMenu.Trigger variant="outline">Dropdown</DropdownMenu.Trigger>
|
|
|
<DropdownMenu.Content side="bottom" align="start" sideOffset={6}>
|
|
|
<DropdownMenu.Item>Perfil</DropdownMenu.Item>
|
|
|
<DropdownMenu.Item>Facturación</DropdownMenu.Item>
|
|
|
<DropdownMenu.Separator />
|
|
|
<DropdownMenu.CheckboxItem bind:checked={menuNotifications}>Notificaciones</DropdownMenu.CheckboxItem>
|
|
|
<DropdownMenu.RadioGroup bind:value={menuTheme}><DropdownMenu.RadioItem value="light">Claro</DropdownMenu.RadioItem><DropdownMenu.RadioItem value="dark">Oscuro</DropdownMenu.RadioItem></DropdownMenu.RadioGroup>
|
|
|
</DropdownMenu.Content>
|
|
|
</DropdownMenu>
|
|
|
<Tooltip.Group delayDuration={250}><Tooltip><Tooltip.Trigger>Tooltip</Tooltip.Trigger><Tooltip.Portal><Tooltip.Content><Tooltip.Arrow />Con la sombra del tema</Tooltip.Content></Tooltip.Portal></Tooltip></Tooltip.Group>
|
|
|
<ContextMenu>
|
|
|
<ContextMenu.Trigger><div data-spec-ctx>Click derecho</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>
|
|
|
<div data-spec-panel>
|
|
|
<Menubar bind:value={menubarValue}>
|
|
|
<Menubar.Menu value="file"><Menubar.Trigger>Archivo</Menubar.Trigger><Menubar.Portal><Menubar.Content><Menubar.Item>Nuevo</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>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- ═══ 14 · Semántica ═══════════════════════════════════════ -->
|
|
|
<section data-spec-section id="sema">
|
|
|
<div data-spec-head>
|
|
|
<span data-spec-n>14</span>
|
|
|
<div><h2 data-spec-display>Semántica</h2><p>La capa perceptual: cada interacción emite una señal (familia + intent) → sonido, haptic y reacción visual <code>data-event-*</code>. Pulsa — con gesto de usuario suenan.</p></div>
|
|
|
</div>
|
|
|
<div data-spec-panel data-spec-cluster>
|
|
|
<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>
|
|
|
</section>
|
|
|
|
|
|
<footer data-spec-foot>
|
|
|
<span data-spec-display>Grafito</span>
|
|
|
<p>Tema sobre el sistema de theming de Eidos · paleta de 32 escalas · jerarquía aliasada, intents auto-derivados por la gramática del libro.</p>
|
|
|
</footer>
|
|
|
</main>
|
|
|
</div>
|