First commit

This commit is contained in:
2026-08-10 04:29:24 -06:00
commit 26851fa750
71 changed files with 8333 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
# Accesibilidad
Objetivo: WCAG 2.2 nivel AA para los contratos entregados.
## Formularios
- Labels vinculados con `for`/`id` generado o explícito.
- Descripciones y errores unidos mediante `aria-describedby`.
- Los errores establecen `aria-invalid`; la aplicación decide cuándo anunciarlos dinámicamente.
- Checkbox, radio y select conservan semántica HTML nativa.
- `required` y `disabled` se transmiten al control real.
- El switch expone `role="switch"` y `aria-checked` porque representa encendido/apagado, no selección de formulario genérica.
## Teclado y foco
- El foco usa un contorno de 2 px con separación de 2 px.
- Tabs: Tab entra en el tab activo; flechas cambian a la pestaña anterior/siguiente; Home y End van a extremos; tabs deshabilitadas se omiten.
- Dialog: `showModal()` proporciona top layer, fondo inerte, contención y Escape nativos. Al cerrar se intenta restaurar el foco previo.
- Todos los botones tienen una altura mínima razonable; `DsIconButton` exige un `label` accesible.
## Estados
- Loading en botones conserva el texto, usa `aria-busy` y deshabilita interacciones duplicadas.
- `DsProgressBar` publica valor, mínimo y máximo.
- Alertas estáticas no usan live regions por defecto para evitar anuncios al cargar una página. `live` debe activarse solo cuando el mensaje aparece como resultado de una acción.
- Los iconos decorativos se ocultan con `aria-hidden`.
- Skeleton es decorativo; el contenedor que lo usa debe proporcionar el estado textual cuando sea necesario.
## Movimiento y contraste
`prefers-reduced-motion: reduce` reduce animaciones y transiciones. La paleta usa texto slate oscuro sobre fondos claros, blanco sobre acciones sky oscuras y variantes de estado con texto oscuro. Los indicadores de foco no dependen de cambios de color internos.
## Pruebas incluidas
- Asociación label/control, descripción, error y `v-model`.
- Disabled y loading.
- Navegación de Tabs con teclado.
- Apertura, nombre y cancelación de Dialog.
- Presencia de exportaciones públicas.
Limitación: jsdom permite verificar contratos DOM, pero no sustituye pruebas manuales con lector de pantalla, zoom al 200 %, alto contraste ni navegación real en Safari/iOS. Estas comprobaciones forman parte de la revisión previa a una publicación estable.
+83
View File
@@ -0,0 +1,83 @@
# Catálogo de componentes
Todos los nombres comienzan con `Ds`. A menos que se indique lo contrario, los atributos HTML adicionales llegan al control raíz correspondiente.
## Layout
| Componente | Props principales | Slots |
| ----------------- | ----------------------------------------------- | -------------------------------------- |
| `DsAppShell` | `maxWidth` | default |
| `DsPageHeader` | `title`, `description`, `eyebrow`, `standalone` | default implícito en textos, `actions` |
| `DsPanel` | `as`, `padding: sm\|md\|lg`, `tone`, `flat` | default |
| `DsSectionHeader` | `title`, `description`, `eyebrow` | `actions` |
| `DsStack` | `as`, `gap`, `align` | default |
| `DsInline` | `as`, `gap`, `align`, `justify`, `wrap` | default |
| `DsDivider` | — | — |
## Acciones
| Componente | Props y eventos | Slots |
| --------------- | -------------------------------------------------------------------------------- | ------------- |
| `DsButton` | `variant`, `size`, `type`, `disabled`, `loading`, `block`, evento nativo `click` | default |
| `DsIconButton` | `label` obligatorio, más variantes y estados de `DsButton` | icono default |
| `DsButtonGroup` | `label` | default |
| `DsFormActions` | — | default |
`loading` conserva el texto como nombre accesible, añade `aria-busy` y deshabilita el botón.
## Formularios
| Componente | Modelo | Props principales |
| ------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |
| `DsFormField` | — | `id`, `label`, `description`, `error`, `required`; slot scoped con `controlId`, `describedBy`, `invalid` |
| `DsTextInput` | `string` | `id`, `name`, `label`, `description`, `error`, `type`, `autocomplete`, `disabled`, `required`; slots `leading`, `trailing` |
| `DsTextarea` | `string` | contrato común más `rows` |
| `DsPasswordInput` | `string` | contrato común y botón mostrar/ocultar |
| `DsSearchInput` | `string` | contrato de texto con icono decorativo de búsqueda |
| `DsSelect` | `string` | contrato común y `options: DsSelectOption[]` |
| `DsCheckbox` | `boolean` | `label`, `description`, `error`, estados HTML |
| `DsRadioGroup` | `string` | `legend`, `name`, `options: DsRadioOption[]`, descripción y error |
| `DsSwitch` | `boolean` | `label`, `description`, `disabled`, `name` |
| `DsSettingsSection` | — | `title`, `description`; slots default y `actions` |
Ejemplo:
```vue
<DsTextInput
v-model="email"
name="email"
type="email"
label="Correo electrónico"
description="Usaremos tu correo de trabajo."
:error="emailError"
required
/>
```
## Retroalimentación
| Componente | Props principales | Comportamiento |
| ----------------- | ------------------------------------ | -------------------------------- |
| `DsSpinner` | `size`, `label` | `role=status`, nombre oculto |
| `DsSkeleton` | `width`, `height`, `rounded` | decorativo y sin gradiente |
| `DsProgressBar` | `value`, `max`, `label`, `showValue` | semántica `progressbar` |
| `DsAlert` | `variant`, `title`, `live` | `live` activa `status` o `alert` |
| `DsInlineMessage` | `variant`, `live` | ayuda o error breve |
| `DsEmptyState` | `title`, `description` | slots `icon`, `actions` |
| `DsLoadingState` | `title`, `description` | región de estado anunciable |
| `DsErrorState` | `title`, `description` | alerta y slot `actions` |
## Navegación y overlays
`DsTabs` recibe `items: DsTabItem[]`, `label` y `v-model<string>`. Cada `id` define un slot del mismo nombre. Admite flechas, Home y End.
```vue
<DsTabs v-model="tab" :items="tabs" label="Preferencias">
<template #profile></template>
<template #security></template>
</DsTabs>
```
`DsDialog` recibe `v-model<boolean>`, `title`, `description`, `closeLabel` y `closeOnBackdrop`. Emite `close` y `cancel`; ofrece slots default y `footer`.
`DsConfirmDialog` añade `confirmLabel`, `cancelLabel`, `danger` y `loading`; emite `confirm` y `cancel`.
+50
View File
@@ -0,0 +1,50 @@
# Fundamentos visuales
## Principios
1. Sobriedad: superficies claras, jerarquía tipográfica fuerte y sombras discretas.
2. Semántica antes que paleta: los componentes consumen intención (`primary`, `danger`, `surface`) y no tonos de producto directos.
3. Accesibilidad visible: foco, errores y estados no dependen únicamente del color.
4. Composición: `DsStack`, `DsInline`, `DsPanel` y encabezados resuelven layouts repetidos sin acoplarse a páginas.
5. Movimiento funcional: transiciones breves, anuladas con `prefers-reduced-motion`.
## Tokens
Los valores predeterminados viven en `src/tokens/`.
| Familia | Variables principales |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Fondos | `--ds-color-background`, `--ds-color-surface`, `--ds-color-surface-muted`, `--ds-color-surface-inverse` |
| Texto | `--ds-color-text`, `--ds-color-text-muted`, `--ds-color-text-inverse`, `--ds-color-text-danger` |
| Bordes y foco | `--ds-color-border`, `--ds-color-border-hover`, `--ds-color-border-danger`, `--ds-color-focus-ring` |
| Acciones | `--ds-color-primary`, `--ds-color-primary-hover`, `--ds-color-primary-active`, `--ds-color-primary-disabled`, `--ds-color-danger` |
| Estado | variables `info-*`, `success-*`, `warning-*` y `error-*` |
| Espacio | `--ds-space-1` a `--ds-space-12` |
| Forma | `--ds-radius-sm` a `--ds-radius-xl`, `--ds-shadow-sm`, `--ds-shadow-md` |
| Controles | `--ds-control-height-sm`, `--ds-control-height-md`, `--ds-control-height-lg` |
| Movimiento | `--ds-duration-fast`, `--ds-duration-normal` |
## Tema predeterminado
- `slate-50` como fondo.
- Blanco para superficies.
- `slate-900` para texto y superficies inversas.
- `sky-600` para acciones principales y `sky-400` para foco.
- Radios de 1018 px y sombras de baja opacidad.
El sistema no usa gradientes, transparencias de tipo glass, sombras decorativas ni movimiento ornamental.
## Personalización
Sobrescribe variables después de importar la hoja del paquete:
```css
:root {
--ds-color-primary: #2563eb;
--ds-color-primary-hover: #1d4ed8;
--ds-radius-md: 0.5rem;
--ds-page-width: 80rem;
}
```
No sobrescribas selectores internos salvo que estés corrigiendo una limitación documentada. La API estable de theming son las variables `--ds-*`.
+87
View File
@@ -0,0 +1,87 @@
# Instalación, consumo y theming
## Instalar localmente
Construye la librería:
```bash
npm install
npm run build:lib
```
Desde otra aplicación Vue:
```bash
npm install ../design_system
```
El paquete está configurado para publicarse en el registro npm de Gitea de `omararch48`. La publicación sigue siendo manual y requiere `GITEA_NPM_TOKEN`.
## Publicar en Gitea
Define un token personal con permiso de paquetes sin guardarlo en el repositorio:
```bash
export GITEA_NPM_TOKEN="tu-token"
npm publish
```
El hook `prepublishOnly` ejecuta type-check, pruebas y build de la librería antes de subir el paquete. Gitea no permite reemplazar una combinación existente de nombre y versión; incrementa la versión antes de una publicación posterior:
```bash
npm version patch
npm publish
```
El scope `@omaresquivel` apunta a:
```text
https://gitea.autobyte.dev/api/packages/omararch48/npm/
```
## Importar
```ts
import { DsButton, DsPanel, DsTextInput } from "@omaresquivel/design-system";
import "@omaresquivel/design-system/style.css";
```
Vue `^3.5.0` es peer dependency. Tailwind, Vite y TypeScript no son dependencias runtime del consumidor.
## Registro global opcional
```ts
import { DesignSystem } from "@omaresquivel/design-system";
import "@omaresquivel/design-system/style.css";
app.use(DesignSystem);
```
## Personalizar el tema
Carga las personalizaciones después del CSS del paquete:
```css
:root {
--ds-color-primary: #2563eb;
--ds-color-primary-hover: #1d4ed8;
--ds-color-focus-ring: #60a5fa;
--ds-radius-lg: 1rem;
}
```
No es necesario configurar `@source`, copiar `@apply` ni exponer Tailwind al consumidor. Si una aplicación ya usa Tailwind, ambos pueden coexistir porque los selectores del sistema llevan el prefijo `ds-` y los tokens llevan `--ds-`.
## Enlace durante desarrollo
Puede usarse una dependencia local en `package.json`:
```json
{
"dependencies": {
"@omaresquivel/design-system": "file:../design_system"
}
}
```
Después de modificar la librería ejecuta `npm run build:lib` y actualiza la instalación de la aplicación consumidora según el flujo de su gestor de paquetes.
+49
View File
@@ -0,0 +1,49 @@
# Investigación y decisiones
Investigación realizada el 10 de agosto de 2026. Se priorizaron fuentes oficiales y especificaciones.
## Fuentes consultadas
- [Vue: TypeScript con Composition API](https://vuejs.org/guide/typescript/composition-api): props y emits tipados con `<script setup>`.
- [Vue: descripción general de TypeScript](https://vuejs.org/guide/typescript/overview): Vite transpila y `vue-tsc` realiza la comprobación estática.
- [Vite: modo librería](https://vite.dev/guide/build.html#library-mode): entrada `build.lib`, externalización de dependencias y distribución de CSS.
- [Tailwind CSS: variables de tema](https://tailwindcss.com/docs/theme): configuración CSS-first y tokens disponibles como variables CSS.
- [Tailwind CSS: detección de clases](https://tailwindcss.com/docs/detecting-classes-in-source-files): una librería fuente externa exige `@source`; las clases deben existir de forma completa.
- [Tailwind CSS: funciones y directivas](https://tailwindcss.com/docs/functions-and-directives): `@reference` es necesario para usar `@apply` en bloques de estilo procesados por separado.
- [Tailwind CSS: compatibilidad](https://tailwindcss.com/docs/compatibility): Tailwind v4 está diseñado para navegadores modernos.
- [WAI-ARIA Authoring Practices: patrones](https://www.w3.org/WAI/ARIA/apg/patterns/): comportamiento esperado para botones, tabs, diálogos, radio groups y switches.
- [WAI-ARIA APG: Dialog modal](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/): nombre accesible, foco contenido, Escape y retorno de foco.
- [WAI-ARIA APG: Tabs](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/): flechas, Home, End, roving tabindex y relación tab/panel.
- [WCAG 2.2](https://www.w3.org/TR/WCAG22/), [Focus Appearance](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html) y [Status Messages](https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html): foco perceptible y anuncios sin cambios de contexto.
- [MDN: elemento dialog](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog) y [`showModal()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal): top layer, fondo inerte, Escape y colocación inicial del foco.
## Decisiones derivadas
### Distribución
Vite construye una entrada ES y externaliza Vue. El paquete publica declaraciones TypeScript, `index.js` y `design-system.css`. `vue` es `peerDependency` para evitar dos runtimes en una aplicación.
El CSS se importa desde la entrada durante el build para garantizar su extracción, pero se expone como `@omaresquivel/design-system/style.css` y se documenta su importación explícita. `sideEffects` conserva los archivos CSS durante tree-shaking.
### Tailwind y estilos
Se distribuye CSS compilado. Esta decisión evita que cada consumidor instale Tailwind, configure `@source` para `node_modules` o replique bloques `@apply`. Tailwind queda como herramienta interna y el resultado se controla mediante variables CSS semánticas.
Los componentes usan clases globales con namespace `ds-` en una única capa `components`. No se ejecuta Tailwind por cada `<style scoped>`, siguiendo su advertencia sobre el coste y el contexto aislado de los estilos SFC. Los nombres prefijados evitan colisiones y las variables conservan la capacidad de theming.
### Accesibilidad
- Controles de formulario nativos antes que widgets ARIA recreados.
- `<dialog>.showModal()` para obtener top layer e inertización nativas en los navegadores objetivo.
- Tabs implementadas con el patrón APG y activación automática porque sus paneles son locales e inmediatos.
- Estados dinámicos anuncian `status` o `alert` solo mediante la prop `live`; contenido estático no se anuncia al montar.
- Foco visible global de 2 px y controles de altura mínima entre 36 y 48 px.
## Alternativas descartadas
- **Distribuir solamente fuentes Tailwind:** obligaría al consumidor a escanear la librería y lo acoplaría a Tailwind.
- **Incluir Vue en el bundle:** produciría runtimes duplicados y rompería expectativas de plugins e inyección.
- **Adoptar una librería visual completa:** sustituiría el lenguaje propio en lugar de estandarizarlo.
- **Implementar todos los widgets con ARIA:** aumenta el riesgo frente a controles HTML nativos.
- **Storybook en la primera versión:** el playground separado ya valida la API pública y los casos de producto con menos infraestructura.
- **Modo oscuro inicial:** requiere una revisión visual y de contraste independiente; queda en el roadmap.
+22
View File
@@ -0,0 +1,22 @@
# Roadmap
## Próxima fase
- Auditoría manual con VoiceOver, NVDA y navegación al 200 % de zoom.
- Pruebas visuales automatizadas en anchos móvil, tablet y escritorio.
- Tema oscuro con revisión de contraste independiente.
- Iconografía mantenida como paquete o API propia.
- Mejoras de formularios: grupos de campos, contador de caracteres y estados asíncronos.
- Versionado semántico, changelog y política de deprecación antes de compartir el paquete ampliamente.
## Deliberadamente pospuesto
- Tabla de datos interactiva.
- Date/time pickers.
- Combobox y autocomplete avanzados.
- Editor rich text.
- Toast manager global.
- Menús, tooltips y popovers complejos.
- Integraciones con router, Pinia, autenticación, backend o persistencia.
Estos componentes requieren investigación y contratos de accesibilidad propios. No deben añadirse como simples variaciones visuales.