Ejercicios resueltos — Capítulo 25¶
Volver al índice · Capítulo original
Ejercicio 1: Definir tokens¶
Define tus tokens: 10 colores, 8 tamaños de tipografía, 8 espaciados.
Solución¶
:root {
/* Colores primarios */
--color-primary-50: #faf5ff;
--color-primary-500: #a855f7;
--color-primary-700: #7e22ce;
--color-primary-900: #581c87;
/* Semánticos */
--color-bg: #ffffff;
--color-surface: #f8fafc;
--color-text: #1e293b;
--color-muted: #64748b;
--color-border: #e2e8f0;
--color-danger: #dc2626;
/* Tipografía */
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 1.875rem;
--text-4xl: 2.25rem;
/* Espaciado (múltiplos de 4) */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-12: 3rem;
--space-16: 4rem;
}
Ejercicio 2: Componente Button¶
Aplica tokens a un Button con 3 variantes y 3 tamaños.
Solución¶
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
border: none;
border-radius: 8px;
font-family: system-ui, sans-serif;
font-weight: 600;
cursor: pointer;
transition: background 150ms ease-out, transform 150ms ease-out;
}
.btn:active { transform: scale(0.98); }
.btn:focus-visible { outline: 3px solid var(--color-primary-500); outline-offset: 2px; }
/* Variantes */
.btn--primary {
background: var(--color-primary-500);
color: white;
}
.btn--primary:hover { background: var(--color-primary-700); }
.btn--secondary {
background: transparent;
color: var(--color-text);
border: 1px solid var(--color-border);
}
.btn--secondary:hover { background: var(--color-surface); }
.btn--danger {
background: var(--color-danger);
color: white;
}
/* Tamaños */
.btn--sm {
padding: var(--space-1) var(--space-3);
font-size: var(--text-sm);
}
.btn--md {
padding: var(--space-2) var(--space-4);
font-size: var(--text-base);
}
.btn--lg {
padding: var(--space-3) var(--space-6);
font-size: var(--text-lg);
}
<button class="btn btn--primary btn--md">Guardar</button>
<button class="btn btn--secondary btn--sm">Cancelar</button>
<button class="btn btn--danger btn--lg">Eliminar</button>
Ejercicio 3: Modo oscuro solo con tokens semánticos¶
Añade modo oscuro cambiando solo tokens.
Solución¶
:root {
--color-bg: #ffffff;
--color-surface: #f8fafc;
--color-text: #1e293b;
--color-muted: #64748b;
--color-border: #e2e8f0;
}
[data-theme="dark"] {
--color-bg: #0f172a;
--color-surface: #1e293b;
--color-text: #e2e8f0;
--color-muted: #94a3b8;
--color-border: #334155;
}
body {
background: var(--color-bg);
color: var(--color-text);
transition: background 300ms, color 300ms;
}
Todo el sistema se adapta sin tocar componentes. Solo cambia data-theme.
Ejercicio 4: Documentar con Storybook¶
Documenta un componente.
Solución¶
Instala:
npx storybook@latest init
Crea button.stories.js:
export default {
title: 'Componentes/Button',
argTypes: {
variant: { control: 'select', options: ['primary', 'secondary', 'danger'] },
size: { control: 'select', options: ['sm', 'md', 'lg'] },
},
};
const Template = ({ variant, size, label }) =>
`<button class="btn btn--${variant} btn--${size}">${label}</button>`;
export const Primary = Template.bind({});
Primary.args = { variant: 'primary', size: 'md', label: 'Guardar' };
export const Danger = Template.bind({});
Danger.args = { variant: 'danger', size: 'lg', label: 'Eliminar' };
Ejecuta:
npm run storybook
Ejercicio 5: Página /design-system¶
Crea una página con todos los componentes.
Solución¶
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Design System</title>
<link rel="stylesheet" href="tokens.css">
<link rel="stylesheet" href="componentes.css">
</head>
<body>
<h1>Design System</h1>
<section>
<h2>Colores</h2>
<div class="paleta">
<div class="swatch" style="background: var(--color-primary-500)">
<span>primary-500</span>
</div>
<div class="swatch" style="background: var(--color-danger)">
<span>danger</span>
</div>
</div>
</section>
<section>
<h2>Tipografía</h2>
<h1>text-4xl</h1>
<h2>text-3xl</h2>
<h3>text-2xl</h3>
<p>text-base: párrafo normal.</p>
</section>
<section>
<h2>Botones</h2>
<button class="btn btn--primary btn--md">Primary</button>
<button class="btn btn--secondary btn--md">Secondary</button>
<button class="btn btn--danger btn--md">Danger</button>
</section>
<section>
<h2>Espaciado</h2>
<div style="background:#e2e8f0; padding: var(--space-2)">space-2</div>
<div style="background:#e2e8f0; padding: var(--space-4)">space-4</div>
<div style="background:#e2e8f0; padding: var(--space-8)">space-8</div>
</section>
</body>
</html>
Ejercicio 6: Tokens con Style Dictionary¶
Publica tus tokens en CSS y SCSS.
Solución¶
npx style-dictionary init basic
tokens/color.json:
{
"color": {
"primary": {
"500": { "value": "#a855f7" },
"700": { "value": "#7e22ce" }
},
"danger": { "value": "#dc2626" }
}
}
config.json:
{
"source": ["tokens/**/*.json"],
"platforms": {
"css": {
"transformGroup": "css",
"buildPath": "build/css/",
"files": [{ "destination": "variables.css", "format": "css/variables" }]
},
"scss": {
"transformGroup": "scss",
"buildPath": "build/scss/",
"files": [{ "destination": "_variables.scss", "format": "scss/variables" }]
}
}
}
Ejecuta:
npx style-dictionary build
Ejercicio 7: Regla "todo color viene de un token"¶
Establece y aplica la regla.
Solución¶
Regla: en el código, ningún valor de color puede ser literal. Siempre var(--color-...).
Mal:
.button { background: #6a5acd; } /* ❌ */
.text { color: #1e293b; } /* ❌ */
Bien:
.button { background: var(--color-primary-500); } /* ✅ */
.text { color: var(--color-text); } /* ✅ */
Cómo hacerlo cumplir:
Añade un linter de estilo:
npm install -D stylelint stylelint-declaration-strict-value
.stylelintrc.json:
{
"plugins": ["stylelint-declaration-strict-value"],
"rules": {
"scale-unlimited/declaration-strict-value": [
["color", "background-color", "border-color", "fill", "stroke"],
{ "ignoreValues": ["transparent", "currentColor", "inherit"] }
]
}
}
Ejecuta npx stylelint "**/*.css" en CI para bloquear colores hardcodeados.
Ejercicio 8: Auditar un proyecto¶
Sustituye valores sueltos por tokens.
Solución¶
Checklist:
- [ ] Buscar
#[0-9a-f]{3,6}en tu CSS. - [ ] Para cada color encontrado, crear o reutilizar un token.
- [ ] Buscar valores de espaciado sueltos (
margin: 17px). - [ ] Ajustar a la escala (
--space-4: 1rem). - [ ] Buscar tamaños de fuente sueltos.
- [ ] Sustituir por
--text-*. - [ ] Verificar visualmente que nada se rompe.
Comando útil:
grep -rn "#[0-9a-fA-F]\{3,6\}" src/