|
|
|
|
@ -0,0 +1,596 @@
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
/**
|
|
|
|
|
* Color demo — the Eidos color system, same shape as the Motion demo.
|
|
|
|
|
*
|
|
|
|
|
* Shows every layer of the model, all rendered through the live CSS custom
|
|
|
|
|
* properties (so they reflect the active theme + light/dark, and will reflect
|
|
|
|
|
* the OKLCH/P3 output once it lands):
|
|
|
|
|
* - the PALETTE: 31 functional scales × 12 steps (`--scale-{name}-{step}`);
|
|
|
|
|
* - the 9 canonical ROLES (hierarchy = brand alias, intents = auto-derived by
|
|
|
|
|
* the book's convention) and their 9 slots (`--color-{role}-{slot}`);
|
|
|
|
|
* - the on-solid TEXT pick by APCA — computed live via the `uix.color` engine,
|
|
|
|
|
* the same metric `render-css` uses (white unless it fails the floor);
|
|
|
|
|
* - translucent ROLE SURFACES (compositing-inverse alpha) over a busy backdrop;
|
|
|
|
|
* - the neutral CHROME semantics (surface / content / border).
|
|
|
|
|
*
|
|
|
|
|
* The eidos foundation CSS (tokens + base theme blocks) is imported statically;
|
|
|
|
|
* `data-theme` on `.root` activates light/dark. No portals here, so no need to
|
|
|
|
|
* project to documentElement.
|
|
|
|
|
*/
|
|
|
|
|
import '@/uix/eidos/index.css'
|
|
|
|
|
import {
|
|
|
|
|
THEME_BASE_COLOR_ROLES,
|
|
|
|
|
THEME_BASE_LIGHT_COLOR_SCALES,
|
|
|
|
|
THEME_BASE_DARK_COLOR_SCALES
|
|
|
|
|
} from '$uix/eidos/lib/themes/base'
|
|
|
|
|
import {
|
|
|
|
|
COLOR_SCALE_STEPS,
|
|
|
|
|
COLOR_ROLE_SLOTS,
|
|
|
|
|
COLOR_ROLES,
|
|
|
|
|
HIERARCHY_COLOR_ROLES,
|
|
|
|
|
completeColorRoleMap,
|
|
|
|
|
type ColorRole
|
|
|
|
|
} from '$uix/eidos/lib/config-types'
|
|
|
|
|
import { INTENTS } from '$uix/intent'
|
|
|
|
|
import { apcaLc, oklchToGammaRgb, parseColor, wcagContrastRatio } from '$color'
|
|
|
|
|
|
|
|
|
|
let theme = $state<'light' | 'dark'>('light')
|
|
|
|
|
|
|
|
|
|
const SCALE_NAMES = Object.keys(THEME_BASE_LIGHT_COLOR_SCALES)
|
|
|
|
|
const ROLES = completeColorRoleMap(THEME_BASE_COLOR_ROLES)
|
|
|
|
|
const STEPS = COLOR_SCALE_STEPS
|
|
|
|
|
const SLOTS = COLOR_ROLE_SLOTS
|
|
|
|
|
|
|
|
|
|
const GROUPS: { label: string; note: string; roles: readonly ColorRole[] }[] = [
|
|
|
|
|
{ label: 'Jerarquía', note: 'alias de marca · el tema elige la escala', roles: HIERARCHY_COLOR_ROLES },
|
|
|
|
|
{ label: 'Intents', note: 'auto-derivados por convención del libro · identidad = paso 9', roles: INTENTS }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const roleScale = (role: ColorRole): string => {
|
|
|
|
|
const def = ROLES[role]
|
|
|
|
|
return typeof def === 'string' ? def : def.scale
|
|
|
|
|
}
|
|
|
|
|
const slotVar = (slot: string): string => slot.replace(/([A-Z])/g, '-$1').toLowerCase()
|
|
|
|
|
|
|
|
|
|
// Scales for the active mode — drives the APCA computation from real hex.
|
|
|
|
|
const activeScales = $derived(
|
|
|
|
|
theme === 'dark' ? THEME_BASE_DARK_COLOR_SCALES : THEME_BASE_LIGHT_COLOR_SCALES
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const ON_SOLID = '#ffffff'
|
|
|
|
|
const ON_SOLID_DARK = '#1c1917'
|
|
|
|
|
|
|
|
|
|
// Mirror render-css's policy: white stays unless it fails APCA |Lc| < 60 OR
|
|
|
|
|
// WCAG < 3 on this role's solid; then the slot flips to the dark on-solid.
|
|
|
|
|
function onSolidInfo(role: ColorRole): { picked: 'white' | 'dark'; lc: number; wcag: number } {
|
|
|
|
|
const hex = activeScales[roleScale(role)]?.['9'] ?? '#888888'
|
|
|
|
|
const solid = oklchToGammaRgb(parseColor(hex))
|
|
|
|
|
const white = oklchToGammaRgb(parseColor(ON_SOLID))
|
|
|
|
|
const lcWhite = apcaLc(white, solid)
|
|
|
|
|
const wcagWhite = wcagContrastRatio(white, solid)
|
|
|
|
|
const flips = Math.abs(lcWhite) < 60 || wcagWhite < 3
|
|
|
|
|
if (!flips) return { picked: 'white', lc: lcWhite, wcag: wcagWhite }
|
|
|
|
|
const dark = oklchToGammaRgb(parseColor(ON_SOLID_DARK))
|
|
|
|
|
return { picked: 'dark', lc: apcaLc(dark, solid), wcag: wcagContrastRatio(dark, solid) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SURFACES = ['default', 'raised', 'overlay', 'muted'] as const
|
|
|
|
|
const CONTENTS = ['primary', 'secondary', 'muted', 'disabled'] as const
|
|
|
|
|
const BORDERS = ['subtle', 'default', 'strong'] as const
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<svelte:head>
|
|
|
|
|
<title>Color · Eidos</title>
|
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
|
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
|
|
|
|
<link
|
|
|
|
|
rel="stylesheet"
|
|
|
|
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Roboto+Mono:wght@400;500&display=swap"
|
|
|
|
|
/>
|
|
|
|
|
</svelte:head>
|
|
|
|
|
|
|
|
|
|
<div class="root" data-theme={theme}>
|
|
|
|
|
<header class="topbar">
|
|
|
|
|
<div class="brand">
|
|
|
|
|
<strong>Color</strong>
|
|
|
|
|
<small>Eidos · paleta → roles → intents · motor uix.color</small>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="controls">
|
|
|
|
|
<div class="seg" role="group" aria-label="Tema">
|
|
|
|
|
<button type="button" data-active={theme === 'light'} onclick={() => (theme = 'light')}>
|
|
|
|
|
Light
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" data-active={theme === 'dark'} onclick={() => (theme = 'dark')}>
|
|
|
|
|
Dark
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<main class="canvas">
|
|
|
|
|
<p class="lede">
|
|
|
|
|
El color tiene <strong>tres capas</strong>: una <strong>paleta</strong> de 31 escalas
|
|
|
|
|
funcionales de 12 pasos (<code>--scale-*</code>), unos <strong>roles</strong> de jerarquía que
|
|
|
|
|
son alias de marca, y unos <strong>intents</strong> evaluativos que
|
|
|
|
|
<strong>auto-derivan</strong> de la paleta por convención del libro. Los componentes consumen los
|
|
|
|
|
<strong>9 slots</strong> (<code>--color-{'{role}'}-{'{slot}'}</code>). El motor
|
|
|
|
|
<code>uix.color</code> (OKLCH · APCA · alpha compositing-inverse) decide lo perceptual — abajo, el
|
|
|
|
|
texto on-solid lo elige <strong>APCA en vivo</strong>. Todo se renderiza vía las custom properties
|
|
|
|
|
activas, así que cambia con el tema.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<!-- La paleta — 31 escalas × 12 pasos -->
|
|
|
|
|
<section class="preset">
|
|
|
|
|
<div class="head">
|
|
|
|
|
<span class="eyebrow">--scale-{name}-{1..12}</span>
|
|
|
|
|
<h2>La paleta <small>(31 escalas · 12 pasos)</small></h2>
|
|
|
|
|
<p>
|
|
|
|
|
Escalas funcionales (1-2 fondos · 3-5 componente · 6-8 bordes · <strong>9 sólido</strong> ·
|
|
|
|
|
10 hover · 11-12 texto). El <strong>representativo</strong> de cada escala es el
|
|
|
|
|
<strong>paso 9</strong>. La paleta la trae el tema; el framework envía estas 31 (valores
|
|
|
|
|
Radix exactos, listos para autoría OKLCH).
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="palette">
|
|
|
|
|
<div class="scale-row step-head">
|
|
|
|
|
<span class="scale-name"></span>
|
|
|
|
|
<div class="swatches">
|
|
|
|
|
{#each STEPS as s}
|
|
|
|
|
<span class="step-num" class:nine={s === '9'}>{s}</span>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{#each SCALE_NAMES as name}
|
|
|
|
|
<div class="scale-row">
|
|
|
|
|
<span class="scale-name">{name}</span>
|
|
|
|
|
<div class="swatches">
|
|
|
|
|
{#each STEPS as s}
|
|
|
|
|
<span
|
|
|
|
|
class="sw"
|
|
|
|
|
class:nine={s === '9'}
|
|
|
|
|
style="background: var(--scale-{name}-{s})"
|
|
|
|
|
title="--scale-{name}-{s}"
|
|
|
|
|
></span>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<!-- Roles canónicos — 9 roles × 9 slots -->
|
|
|
|
|
<section class="preset">
|
|
|
|
|
<div class="head">
|
|
|
|
|
<span class="eyebrow">--color-{role}-{slot}</span>
|
|
|
|
|
<h2>Roles canónicos <small>(9 roles · 9 slots)</small></h2>
|
|
|
|
|
<p>
|
|
|
|
|
Los 3 roles de <strong>jerarquía</strong> (el tema los mapea a una escala) y los 6
|
|
|
|
|
<strong>intents</strong> (auto-derivados: <code>neutral→gray · affirm→teal · fulfill→green ·
|
|
|
|
|
risk→amber · threat→red · loss→plum</code>). Cada rol expone los 9 slots
|
|
|
|
|
(<code>track · element · hover · active · border · solid · solid-hover · text · contrast</code>).
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
{#each GROUPS as group}
|
|
|
|
|
<div class="role-group">
|
|
|
|
|
<h3>{group.label} <small>{group.note}</small></h3>
|
|
|
|
|
{#each group.roles as role}
|
|
|
|
|
<div class="role-row">
|
|
|
|
|
<span class="role-name">
|
|
|
|
|
<strong>{role}</strong>
|
|
|
|
|
<small>{roleScale(role)}</small>
|
|
|
|
|
</span>
|
|
|
|
|
<div class="swatches slots">
|
|
|
|
|
{#each SLOTS as slot}
|
|
|
|
|
<span
|
|
|
|
|
class="sw"
|
|
|
|
|
style="background: var(--color-{role}-{slotVar(slot)})"
|
|
|
|
|
title="--color-{role}-{slotVar(slot)}"
|
|
|
|
|
></span>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
<div class="slot-legend">
|
|
|
|
|
{#each SLOTS as slot}<span>{slotVar(slot)}</span>{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<!-- Texto on-solid — APCA pick, live via uix.color -->
|
|
|
|
|
<section class="preset">
|
|
|
|
|
<div class="head">
|
|
|
|
|
<span class="eyebrow">uix.color · pickOnSolid (APCA)</span>
|
|
|
|
|
<h2>Texto on-solid <small>(elegido por APCA · en vivo)</small></h2>
|
|
|
|
|
<p>
|
|
|
|
|
El color del texto SOBRE el sólido (<code>--color-{'{role}'}-contrast</code>). El motor
|
|
|
|
|
prefiere <strong>blanco</strong> salvo que falle el suelo APCA (|Lc| < 60) o WCAG (< 3)
|
|
|
|
|
— entonces voltea a <strong>oscuro</strong>. El chip usa los tokens REALES; el pie muestra
|
|
|
|
|
el cálculo APCA en vivo. En el tema base solo <strong>risk</strong> (naranja) voltea.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="onsolid-grid">
|
|
|
|
|
{#each COLOR_ROLES as role}
|
|
|
|
|
{@const info = onSolidInfo(role)}
|
|
|
|
|
<div
|
|
|
|
|
class="chip"
|
|
|
|
|
style="background: var(--color-{role}-solid); color: var(--color-{role}-contrast)"
|
|
|
|
|
>
|
|
|
|
|
<strong>{role}</strong>
|
|
|
|
|
<small>on-solid: {info.picked} · |Lc| {Math.abs(info.lc).toFixed(0)} · WCAG {info.wcag.toFixed(2)}</small>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<!-- Superficies translúcidas — compositing-inverse alpha -->
|
|
|
|
|
<section class="preset">
|
|
|
|
|
<div class="head">
|
|
|
|
|
<span class="eyebrow">--color-{role}-surface · alpha</span>
|
|
|
|
|
<h2>Superficies tintadas <small>(alpha compositing-inverse)</small></h2>
|
|
|
|
|
<p>
|
|
|
|
|
El tinte de la variante <code>soft</code> (<code>--color-{'{role}'}-surface</code>) es
|
|
|
|
|
<strong>translúcido</strong> (alpha derivado por inversión de composición), así que
|
|
|
|
|
<strong>compone</strong> sobre fondos no uniformes en vez de taparlos. Sobre las rayas se ve
|
|
|
|
|
el hue, no un bloque opaco.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="alpha-stage">
|
|
|
|
|
{#each COLOR_ROLES as role}
|
|
|
|
|
<span class="surface-chip" style="background: var(--color-{role}-surface)">
|
|
|
|
|
{role}
|
|
|
|
|
</span>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<!-- Chrome semántico — surface / content / border -->
|
|
|
|
|
<section class="preset">
|
|
|
|
|
<div class="head">
|
|
|
|
|
<span class="eyebrow">--color-surface/content/border-*</span>
|
|
|
|
|
<h2>Chrome semántico <small>(neutral · superficies / texto / bordes)</small></h2>
|
|
|
|
|
<p>
|
|
|
|
|
Los tokens neutrales que visten la UI: las <strong>superficies</strong> (fondos por
|
|
|
|
|
elevación), el <strong>texto</strong> por jerarquía y los <strong>bordes</strong> por fuerza.
|
|
|
|
|
Se invierten solos entre light y dark.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="chrome">
|
|
|
|
|
<div class="chrome-col">
|
|
|
|
|
<span class="chrome-label">surface</span>
|
|
|
|
|
{#each SURFACES as s}
|
|
|
|
|
<div class="surface-row" style="background: var(--color-surface-{s})">
|
|
|
|
|
<span>{s}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="chrome-col">
|
|
|
|
|
<span class="chrome-label">content</span>
|
|
|
|
|
{#each CONTENTS as c}
|
|
|
|
|
<div class="content-row" style="color: var(--color-content-{c})">Aa · {c}</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="chrome-col">
|
|
|
|
|
<span class="chrome-label">border</span>
|
|
|
|
|
{#each BORDERS as b}
|
|
|
|
|
<div class="border-row" style="border-color: var(--color-border-{b})">{b}</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.root {
|
|
|
|
|
min-height: 100dvh;
|
|
|
|
|
background: var(--color-surface-default);
|
|
|
|
|
color: var(--color-content-primary);
|
|
|
|
|
font-family: var(--font-family-primary, 'Inter', system-ui, sans-serif);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.topbar {
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
padding: var(--space-3) var(--space-6);
|
|
|
|
|
background: color-mix(in srgb, var(--color-surface-default) 86%, transparent);
|
|
|
|
|
backdrop-filter: blur(8px);
|
|
|
|
|
border-block-end: 1px solid var(--color-border-subtle);
|
|
|
|
|
}
|
|
|
|
|
.brand {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
}
|
|
|
|
|
.brand strong {
|
|
|
|
|
font-size: var(--font-size-lg);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.brand small {
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
}
|
|
|
|
|
.controls {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
}
|
|
|
|
|
.seg {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
padding: 2px;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
}
|
|
|
|
|
.seg button {
|
|
|
|
|
appearance: none;
|
|
|
|
|
border: 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: var(--space-1) var(--space-3);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-content-secondary);
|
|
|
|
|
font: inherit;
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
}
|
|
|
|
|
.seg button[data-active='true'] {
|
|
|
|
|
background: var(--color-primary-solid);
|
|
|
|
|
color: var(--color-primary-contrast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.canvas {
|
|
|
|
|
max-width: 920px;
|
|
|
|
|
margin-inline: auto;
|
|
|
|
|
padding: var(--space-8) var(--space-6) var(--space-12);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--space-8);
|
|
|
|
|
}
|
|
|
|
|
.lede {
|
|
|
|
|
color: var(--color-content-secondary);
|
|
|
|
|
font-size: var(--font-size-md);
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
max-width: 72ch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preset {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
padding: var(--space-6);
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-xl);
|
|
|
|
|
}
|
|
|
|
|
.head h2 {
|
|
|
|
|
margin: var(--space-1) 0 var(--space-2);
|
|
|
|
|
font-size: var(--font-size-xl);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.head h2 small {
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
}
|
|
|
|
|
.head p {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: var(--color-content-secondary);
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
max-width: 68ch;
|
|
|
|
|
}
|
|
|
|
|
.role-group h3 {
|
|
|
|
|
margin: 0 0 var(--space-2);
|
|
|
|
|
font-size: var(--font-size-md);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
.role-group h3 small {
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
}
|
|
|
|
|
.eyebrow {
|
|
|
|
|
font-family: var(--font-family-mono, 'Roboto Mono', ui-monospace, monospace);
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
letter-spacing: 0.02em;
|
|
|
|
|
color: var(--color-primary-text, var(--color-content-muted));
|
|
|
|
|
}
|
|
|
|
|
code {
|
|
|
|
|
font-family: var(--font-family-mono, 'Roboto Mono', ui-monospace, monospace);
|
|
|
|
|
font-size: 0.88em;
|
|
|
|
|
padding: 0.1em 0.35em;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
background: color-mix(in srgb, var(--color-content-muted) 12%, transparent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Palette grid */
|
|
|
|
|
.palette {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 3px;
|
|
|
|
|
}
|
|
|
|
|
.scale-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
}
|
|
|
|
|
.scale-name {
|
|
|
|
|
flex: none;
|
|
|
|
|
inline-size: 84px;
|
|
|
|
|
text-align: end;
|
|
|
|
|
font-family: var(--font-family-mono, 'Roboto Mono', ui-monospace, monospace);
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
color: var(--color-content-secondary);
|
|
|
|
|
}
|
|
|
|
|
.swatches {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex: 1;
|
|
|
|
|
gap: 0;
|
|
|
|
|
min-inline-size: 0;
|
|
|
|
|
}
|
|
|
|
|
.sw {
|
|
|
|
|
flex: 1;
|
|
|
|
|
block-size: 26px;
|
|
|
|
|
min-inline-size: 0;
|
|
|
|
|
}
|
|
|
|
|
.swatches .sw:first-child {
|
|
|
|
|
border-start-start-radius: var(--radius-sm);
|
|
|
|
|
border-end-start-radius: var(--radius-sm);
|
|
|
|
|
}
|
|
|
|
|
.swatches .sw:last-child {
|
|
|
|
|
border-start-end-radius: var(--radius-sm);
|
|
|
|
|
border-end-end-radius: var(--radius-sm);
|
|
|
|
|
}
|
|
|
|
|
.sw.nine {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
box-shadow: inset 0 0 0 2px var(--color-surface-default);
|
|
|
|
|
}
|
|
|
|
|
.step-head .step-num {
|
|
|
|
|
flex: 1;
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
font-family: var(--font-family-mono, 'Roboto Mono', ui-monospace, monospace);
|
|
|
|
|
}
|
|
|
|
|
.step-head .step-num.nine {
|
|
|
|
|
color: var(--color-content-primary);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Roles */
|
|
|
|
|
.role-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
}
|
|
|
|
|
.role-group + .role-group {
|
|
|
|
|
margin-block-start: var(--space-4);
|
|
|
|
|
}
|
|
|
|
|
.role-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
}
|
|
|
|
|
.role-name {
|
|
|
|
|
flex: none;
|
|
|
|
|
inline-size: 84px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
line-height: 1.15;
|
|
|
|
|
}
|
|
|
|
|
.role-name strong {
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
.role-name small {
|
|
|
|
|
font-family: var(--font-family-mono, 'Roboto Mono', ui-monospace, monospace);
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
}
|
|
|
|
|
.slots .sw {
|
|
|
|
|
block-size: 34px;
|
|
|
|
|
}
|
|
|
|
|
.slot-legend {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0;
|
|
|
|
|
margin-inline-start: calc(84px + var(--space-3));
|
|
|
|
|
}
|
|
|
|
|
.slot-legend span {
|
|
|
|
|
flex: 1;
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 9px;
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
font-family: var(--font-family-mono, 'Roboto Mono', ui-monospace, monospace);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* On-solid chips */
|
|
|
|
|
.onsolid-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
}
|
|
|
|
|
.chip {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
padding: var(--space-3) var(--space-4);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
box-shadow: var(--shadow-2);
|
|
|
|
|
}
|
|
|
|
|
.chip strong {
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.chip small {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
opacity: 0.85;
|
|
|
|
|
font-family: var(--font-family-mono, 'Roboto Mono', ui-monospace, monospace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Translucent surfaces over a busy backdrop */
|
|
|
|
|
.alpha-stage {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
padding: var(--space-4);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
background:
|
|
|
|
|
repeating-linear-gradient(
|
|
|
|
|
45deg,
|
|
|
|
|
color-mix(in srgb, var(--color-content-muted) 22%, transparent) 0 10px,
|
|
|
|
|
transparent 10px 20px
|
|
|
|
|
),
|
|
|
|
|
var(--color-surface-default);
|
|
|
|
|
}
|
|
|
|
|
.surface-chip {
|
|
|
|
|
padding: var(--space-2) var(--space-4);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
font-size: var(--font-size-sm);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: var(--color-content-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Chrome semantics */
|
|
|
|
|
.chrome {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
}
|
|
|
|
|
.chrome-col {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
}
|
|
|
|
|
.chrome-label {
|
|
|
|
|
font-family: var(--font-family-mono, 'Roboto Mono', ui-monospace, monospace);
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
color: var(--color-content-muted);
|
|
|
|
|
}
|
|
|
|
|
.surface-row {
|
|
|
|
|
padding: var(--space-3);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
color: var(--color-content-secondary);
|
|
|
|
|
}
|
|
|
|
|
.content-row {
|
|
|
|
|
font-size: var(--font-size-md);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
.border-row {
|
|
|
|
|
padding: var(--space-3);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
border: 2px solid var(--color-border-default);
|
|
|
|
|
font-size: var(--font-size-xs);
|
|
|
|
|
color: var(--color-content-secondary);
|
|
|
|
|
}
|
|
|
|
|
</style>
|