First commit
This commit is contained in:
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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 10–18 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-*`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user