Contribuir
iVOLT es una base de código pequeña con reglas estrictas. La mayoría de esas reglas existen porque un sistema de diseño se rompe despacio: una clase sin prefijo, un !important, un componente documentado con marcado que ninguna prueba llega a renderizar. Las pruebas de contrato que vienen más abajo codifican esas reglas para que una pull request falle pronto en lugar de ir derivando.
Puesta en marcha
git clone https://github.com/iNTERVOLUTIONS-Labs/iVOLT
cd iVOLT
npm ci # instala desde el único lockfile, nunca `npm install` en CI
npm run build # genera packages/ivolt/dist
npm run dev:docs # compila el paquete antes si falta dist/ # servidor de desarrollo de Astro para apps/docsEl repositorio es un workspace de npm con un solo lockfile en la raíz. dist/ se genera y no se versiona, así que compila antes de lanzar la suite de navegador, el informe de tamaños o la prueba de humo del empaquetado.
Comandos
| Comando | Qué hace |
|---|---|
npm run build | Compila el paquete: primero se generan tokens y utilidades, después se escriben el CSS y el JS en packages/ivolt/dist. |
npm test | Vitest: pruebas unitarias de los componentes JS y pruebas de contrato sobre las fuentes CSS y los fragmentos documentados. |
npm run test:browser | Playwright. Solo Chromium por defecto; IVOLT_ALL_BROWSERS=1 npm run test:browser ejecuta Chromium, Firefox y WebKit. Cubre comportamiento, axe, desbordamiento de 320 a 1920 px, RTL, temas, páginas de documentación y recetas. |
npm run sizes | Informa del tamaño de cada artefacto compilado. Un cambio que mueva esas cifras de forma apreciable debería explicar por qué. |
npm run pack-smoke | Empaqueta con npm pack, instala el tarball en un proyecto desechable y comprueba que los exports se resuelven y que los componentes sin usar se eliminan por tree-shaking. |
npm run check-examples | Carga las páginas de ejemplo y las recetas y verifica que siguen funcionando con la compilación actual. |
npm run build:docs | Compila el sitio estático de documentación. |
npm run verify | Todo lo anterior en orden. Es la puerta antes de cerrar una fase. |
Playwright necesita que instales sus navegadores una vez (npx playwright install); las comprobaciones de axe pasan por @axe-core/playwright.
Estructura del repositorio
iVOLT/
├─ package.json # workspaces + scripts de raíz
├─ package-lock.json # el único lockfile
├─ docs/ # contratos, sistema de diseño, listón de calidad, ADR, hoja de ruta
├─ packages/ivolt/
│ ├─ tokens/tokens.json # única fuente de verdad de los tokens
│ ├─ fixtures/<familia>/*.html # una fuente para documentación, pruebas y plantillas
│ ├─ src/css/ # reset, tokens (generado), base, layout/, components/,
│ │ # utilities (generado), core.css, ivolt.css, ivolt.flat.css
│ ├─ src/js/ # core/ (registro, opciones, eventos, foco, teclas),
│ │ # components/, theme.js, index.js, auto.js, iife.js
│ ├─ scripts/ # build-tokens, build-utilities, build-css, build-js, sizes
│ └─ dist/ # generado, no versionado
├─ apps/docs/ # sitio estático de Astro
├─ examples/ # plain-html, vite, recipes/
├─ tests/ # unit, browser, contracts, integration
└─ scripts/ # pack-smoke, check-examples, sync-examplesHay dos archivos generados que nunca deben editarse a mano: src/css/tokens.css (desde tokens/tokens.json) y src/css/utilities.css (desde la configuración de utilidades). Cambia la fuente y vuelve a compilar. Lo mismo vale para custom-media.css, reset.layer.css y core/breakpoints.js.
Reglas para un cambio
CSS
- Prefíjalo todo. Clases
iv-, utilidadesiv-u-, propiedades personalizadas--iv-, atributos de datosdata-iv-. - Nada de
!important. La única excepción es.iv-u-sr-only, y está documentada como tal. - Especificidad igual o menor que
(0,2,0)en los selectores de componente, para que una utilidad — emitida con el selector duplicado a exactamente(0,2,0)y cargada la última — gane siempre. Las alternativas documentadas con:targetpara diálogo y panel lateral son la única excepción admitida. - Selectores de elemento solo dentro de
.iv-root. Nada del paquete puede dar estilo a un elemento suelto de forma global. - Los módulos no declaran
@layerni importan nada. Las entradas asignan las capas en el momento de importar, y cada módulo se importa exactamente una vez por entrada. - Puntos de ruptura con nombres de
@custom-media, nuncavar()dentro de una media query, porque no funciona. - Los colores vienen de tokens semánticos. Un hexadecimal suelto en un archivo de componente es un error; no seguirá a los temas.
Las fixtures son la fuente única
Una fixture en packages/ivolt/fixtures/<familia>/<caso>.html es a la vez la vista previa de la página de documentación, el fragmento copiable y el marcado que renderizan las pruebas. No escribas marcado de ejemplo en línea dentro de una página de componente, ni marcado de prueba dentro de un archivo de spec. Si el fragmento documentado y el marcado probado pueden divergir, acabarán divergiendo.
Cuándo está terminada una familia
Contar variantes no ensancha el alcance. Una familia de componentes está terminada solo cuando tiene las cinco cosas:
- Estados: normal, hover, foco, activo, deshabilitado, y los de carga o error que necesite.
- Comportamiento de teclado, documentado tecla a tecla.
- Una alternativa para cuando JavaScript no se ejecuta.
- Documentación con fixtures.
- Pruebas: unitarias del comportamiento, de navegador del renderizado y axe en ambos temas.
JavaScript
- Sin acceso en el nivel superior a
document,window,navigatornimatchMedia; las importaciones en SSR no deben lanzar. - Sin
eval, sinnew Function, sininnerHTMLcon datos externos, sin atributoson*. El paquete tiene que funcionar bajoscript-src 'self'. - Sin dependencias, sin peticiones de red, sin telemetría.
init()es idempotente;destroy()devuelve el DOM a los atributos que encontró, y eso se prueba.- Los eventos llevan el prefijo
iv:y forman parte del contrato: añadir o renombrar uno es un cambio de contrato.
Los cambios de contrato necesitan un ADR
Todo lo que cambie una superficie pública — un nombre de clase, un nombre de token, una opción, un evento, un export, el orden de capas — necesita una entrada en docs/DECISIONS.md con la decisión, las alternativas consideradas y la evidencia. El ADR forma parte de la pull request, no es un seguimiento posterior.
Las pruebas de contrato
Se ejecutan con npm test y fallan de forma ruidosa si se rompe alguna regla de arriba:
| Comprobación | Falla cuando |
|---|---|
| Los módulos no declaran capa ni importan nada | Un módulo fuente añade @layer o un @import |
Nada de !important fuera de las utilidades sr-only | Cualquier otra declaración lo usa |
| Prefijos en clases y propiedades personalizadas | Aparece un nombre sin prefijo en las fuentes |
Selectores de elemento acotados a .iv-root | Un selector de elemento suelto se escapa del ámbito en base.css |
Techo de especificidad (0,2,0) | Un selector de componente lo supera fuera de las excepciones documentadas |
Puntos de ruptura vía @custom-media | Una media query lee una propiedad personalizada |
| Las utilidades coinciden exactamente con la matriz declarada | El archivo generado gana o pierde una clase respecto al contrato |
| Las entradas importan cada módulo una vez y declaran el orden de capas | Un import duplicado se come en silencio una asignación de capa |
| Los fragmentos coinciden con la distribución | El marcado documentado deja de coincidir con lo que el paquete distribuye de verdad |
Si una prueba de contrato bloquea un cambio que crees correcto, la solución es un ADR que mueva el contrato, no una excepción en la prueba.
Pull requests
- Ejecuta
npm run verifyantes de abrir una, y di en la descripción qué comprobaciones pasaste y cuáles no pudiste pasar. - Mantén el cambio centrado en una familia o en un asunto; las refactorizaciones mezcladas son difíciles de revisar contra un contrato.
- Informa de los errores de accesibilidad y de navegador con el nombre de la fixture, el motor y los pasos: la fixture hace que sea reproducible con un solo comando.
- Desde la 1.0 la superficie pública sigue SemVer. Un cambio correcto que rompa compatibilidad espera a una versión mayor y se registra antes; un cambio cómodo pero sin registrar no entra nunca.
Licencia
MIT, Copyright (c) 2026 iNTERVOLUTIONS. Mira LICENSE en la raíz del repositorio. Las contribuciones se aceptan bajo la misma licencia.