Communitygithub.com

karero/website-design-system

Gestiona la capa visual y de rendimiento: gráficos móviles, modo oscuro y tokens de tema.

¿Qué es website-design-system?

Este skill es responsable de la apariencia y el rendimiento visual de un sitio web, abarcando gráficos para dispositivos móviles, modo oscuro y los tokens del tema. Define y mantiene el archivo `BRAND.md` junto con los bloques de tokens de color en `global.css`, asegurando consistencia entre ambos. También impone reglas estrictas para imágenes (formatos modernos, dimensionado correcto, lazy loading, alt text), tablas responsivas (con estilos `stack` y `figures`), y una capa de modo oscuro de primera clase que cumple con WCAG AA en ambos temas. El sistema está pensado para trabajar con Astro y Astro Assets, con tests automatizados que verifican contraste, layout responsivo, y que no haya direcciones de email visibles en el HTML (usando un componente de ofuscación). Es ideal para equipos que buscan un diseño sistemático, accesible y optimizado para rendimiento.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/karero/croftweaver/tree/HEAD/skills/website-design-system

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Sistema de diseño web

Gestiona la capa visual y de rendimiento: gráficos móviles, modo oscuro y los tokens del tema. Genera/mantiene BRAND.md (plantilla: el templates/brand.md del skill new-website) y el bloque de tokens en el templates/astro/src/styles/global.css de ese skill. Las reglas de imágenes están a continuación (WebP/AVIF mediante astro:assets de Astro); el test images.spec.ts las aplica. Para generar assets de imagen, usa el skill image.

Modo oscuro (primera clase)

  • Tokens, no colores fijos. Todo el color fluye a través de propiedades personalizadas CSS; el modo claro es el valor por defecto en :root, las anulaciones para modo oscuro van bajo :root[data-theme="dark"], además de un bloque @media (prefers-color-scheme: dark) para visitantes sin JavaScript. El starter ya incluye esta estructura exacta.
  • Script sin FOUC en <head> que establece data-theme desde localStorage.theme o la preferencia del sistema antes del primer pintado (ya integrado en Base.astro). El interruptor escribe localStorage.theme (el starter no incluye ninguno; si añades uno, menciónalo en la sección 3 de la página de privacidad).
  • Contraste en ambos temas: cada par texto/fondo cumple WCAG AA (4.5:1 en cuerpo, 3:1 en grande/UI). Un color que funciona en modo claro a menudo falla en oscuro; el test de accesibilidad ejecuta la matriz completa claro+oscuro, así que verifica ambos.

Gráficos móviles y responsivos

  • Mobile first: diseña desde 360px hacia arriba; objetivos táctiles ≥ 44px; sin desplazamiento horizontal. Verifica en 360 / 768 / 1280 (preview_resize).
  • Formatos modernos: WebP (o AVIF) en lugar de JPEG/PNG para fotos.
  • Tamaño acorde a la visualización, no al origen. Nunca envíes una imagen de 2000px en un espacio de 80px; genera variantes de ancho y usa srcset/sizes (o <picture>).
  • Sin desplazamiento del layout: establece los atributos explícitos width/height para que el espacio se reserve antes de que la imagen cargue (protege CLS / LCP). El componente <Image> de Astro los establece automáticamente; images.spec.ts los exige (un aspect-ratio solo con CSS no cumple el test).
  • Lazy vs eager: loading="lazy" debajo del pliegue; la imagen LCP/hero carga con eager y fetchpriority="high" (NO le pongas lazy).
  • Texto alternativo en cada imagen (alt="" solo si es puramente decorativa).
  • Fuentes: autoalojadas en woff2 con font-display: swap; precarga solo las 1–2 que se usan sobre el pliegue. Evita cambios de layout por intercambios tardíos de fuentes.

Capacidad de texto según idioma (texto traducido)

  • El texto traducido suele ser más largo. El alemán (y muchos otros idiomas) suele ser un 20–35% más largo que el texto equivalente en inglés. Un botón, enlace de navegación o tarjeta ajustado al texto en inglés se desbordará silenciosamente, se romperá de forma antiestética o se truncará en cuanto se reemplace por la copia traducida real.
  • Prefiere anchos flexibles. Usa min-width / flex / grid con tamaños auto/fr para cualquier elemento que contenga texto traducido, no un width fijo ajustado a la cadena en inglés.
  • Desbordamiento ≠ ancho: el tamaño flexible no evita que una sola palabra compuesta larga (Datenschutzerklärung) reviente una columna estrecha; para eso necesitas hyphens: auto (correcto por idioma gracias al atributo lang que Base.astro ya establece) además de overflow-wrap: break-word como fallback; el global.css del starter incluye ambos en body. Las celdas de tabla desactivan la separación silábica (ver Tablas).
  • No te bases únicamente en white-space: nowrap + text-overflow: ellipsis para enlaces de navegación o etiquetas de botones: truncar un enlace de navegación suele ser una mala experiencia de usuario (el visitante ya no puede saber a dónde lleva). Está bien para contenido donde el truncamiento es una concesión aceptada (por ejemplo, una celda de tabla), pero no para navegación.
  • Verifica con etiquetas traducidas reales, no con placeholders en inglés. Cuando un sitio incluye LanguageSwitcher.astro de astro-i18n-setup o cualquier navegación bilingüe/multilingüe, comprueba el layout en 360/768/1280 (preview_resize) usando la copia real enviada en cada idioma, no solo el placeholder en inglés.

Tablas

Un único estilo para todas las tablas; una página no lleva CSS de layout para una tabla. Vive en src/styles/tables.css del starter (importado después de global.css por Base.astro) y tests/tables.spec.ts lo verifica. Un sitio creado antes de esta sección copia tables.css, añade la línea de importación a su layout y primero elimina el CSS de tabla propio de cada página.

Dos tipos, diferenciados por lo que contienen las celdas:

  • stack (oraciones): una tabla por encima de 40rem; por debajo cada fila se apila, el encabezado de fila como título y cada celda bajo el nombre de su columna. Cuatro columnas de prosa no pueden compartir 320px.
  • figures (números cortos, class="num" en una celda numérica): sigue siendo una tabla en un teléfono y debe caber en 320px. Si ya no cabe, conviértela en stack; una barra de desplazamiento no es la solución. stack figures juntos es el aspecto de las figuras con el layout apilado.

Marcado, para cada tabla:

<div class="table-wrap stack" role="region" aria-label="Lo que muestra la tabla" tabindex="0">
  <table role="table">
    <thead role="rowgroup"><tr role="row">
      <th scope="col" role="columnheader">{cols[0]}</th><th scope="col" role="columnheader">{cols[1]}</th>
    </tr></thead>
    <tbody role="rowgroup">{rows.map((r) => (
      <tr role="row"><th scope="row" role="rowheader">{r.item}</th><td role="cell" data-label={cols[1]}>{r.text}</td></tr>
    ))}</tbody>
  </table>
</div>
  • Escribe los nombres de las columnas una vez, en una lista, y úsalos para el encabezado y para cada data-label: la etiqueta de una celda apilada debe ser igual al encabezado de su columna, y el test compara ambos. Una tabla figures no necesita roles por celda ni data-label.
  • Una tabla apilada ya no tiene display: table. Los roles son un fallback para un navegador que pierde la tabla al cambiar el layout; no se verifica que funcionen allí. En Chromium, medido una vez en una tabla real, el árbol de accesibilidad mantuvo la tabla sin ellos.
  • class="tap" en un enlace que está solo en una celda lo convierte en un objetivo táctil de 44px en un teléfono. nowrap mantiene un valor corto (1, 3, 6) en una línea. long-head (con stack) ensancha la primera columna cuando los encabezados de fila son oraciones.
  • Las palabras no se separan con guiones en las celdas. El hyphens: auto del cuerpo comprime una primera columna estrecha en fragmentos ("An-thropic"), así que el bloque establece hyphens: manual allí.
  • Una página cuyas tablas provienen de fuera y no pueden llevar el marcado va a TABLE_EXEMPT en el spec, con la razón. PAGES se mantiene completo: el mapa del sitio y las comprobaciones de llms.txt necesitan cada ruta activa en él.

Lo que verifica tables.spec.ts: cada tabla está en un .table-wrap de un tipo con su región, etiqueta y tabindex, scope="col" en cada celda de encabezado y un <th scope="row"> como primera celda de cada fila, con <td> para el resto; una tabla apilada tiene sus roles y una etiqueta igual al encabezado en cada celda; a 360 y 320px ninguna página ni tabla se desplaza lateralmente; a 641px y más, una tabla apilada vuelve a ser tabla con su fila de encabezado visible y sin palabras rotas (esto detecta reglas como overflow-wrap: anywhere que permiten que una columna comprima una palabra); ninguna celda tiene hyphens: auto. También prueba el CSS en un fixture que añade a la página de inicio, con axe en ambos temas, por lo que un sitio sin tablas sigue probando el bloque. Chromium sin cabeza no separa con guiones, por lo que la protección de guiones lee el estilo calculado, no el layout. Safari y VoiceOver no se verifican.

Ofuscación de correos electrónicos (ocultar direcciones de los rastreadores)

Nunca pongas un correo electrónico en texto plano o un mailto:tu@direccion en el código HTML fuente — los raspadores lo cosechan para spam en cuestión de horas. Renderiza cada dirección a través del componente EmailLink.astro (src/components/EmailLink.astro en los "templates/astro/" del skill new-website, junto con su ayudante lib/obfuscate.ts): la dirección se invierte y codifica en base64 en tiempo de compilación y se reensambla en el navegador, por lo que el HTML servido no contiene ninguna dirección legible. Los visitantes sin JavaScript ven una pista aún utilizable "nombre [en] dominio [punto] com".

<EmailLink address="[email protected]" subject="Consulta sobre el proyecto" text="Envíanos un correo" />

Regla: cero enlaces mailto: sin procesar y cero direcciones @ literales en el HTML compilado — website-qa lo verifica. La misma idea para cualquier número de teléfono que no quieras que sea cosechado.

Tokens de marca

Refleja la paleta de BRAND.md en el bloque de tokens y mantenlos sincronizados. Define: marca (fija) y, por tema: heading/ink/muted/accent/link/line/bg/bg-soft/surface. El color de acento y el color de enlace deben cumplir AA sobre el fondo en ambos temas, por eso cada tema tiene el suyo propio. El opcional website-brand-kit hace el reflejo con una verificación: su entrada lee las tablas de BRAND.md, mantiene cada color de texto a 4.5:1 sobre cada fondo en ambos temas, y escribe los valores en los tres bloques de color de global.css (claro, oscuro y el bloque oscuro para visitantes sin JavaScript). A mano, cambia los tres bloques juntos.

Hecho significa

BRAND.md relleno y consistente con el bloque de tokens; las imágenes son WebP, con el tamaño correcto, lazy/eager correcto, con alt; el test de accesibilidad (claro+oscuro) está en verde; el layout se mantiene en 360/768/1280 sin desplazamiento; cada tabla es stack o figures y tables.spec.ts está en verde. Las métricas de rendimiento (FCP/LCP) están verificadas en website-qa. El layout también se mantiene en 360/768/1280 con copia real (no placeholder) en cada idioma enviado, no solo la locale predeterminada.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Skills relacionados