Saltar al contenido
EspañolEnglish

Primeros pasos

Hay tres caminos, de menos a más trabajo: una aplicación nueva con create, un proyecto que ya existe con init, o montarlo a mano. Los dos primeros hacen lo mismo que el tercero, y doctor comprueba el resultado en cualquiera de los tres.

Qué hace falta

QuéVersión
React y React DOM^18.3.0 || ^19.0.0
Node.js>=20.19
Next (opcional)^14.0.0 || ^15.0.0 || ^16.0.0
react-hook-form (opcional)^7.54.0

Y nada más: el paquete tiene 0 dependencias al ejecutarse. react-hook-form solo hace falta para @amset/ui/form, y next para @amset/ui/next.

Se instala del registro de paquetes del GitLab de AMSET: la versión pública en npm está prevista y todavía no está publicada. Una línea en el .npmrc del proyecto dice de dónde sale el paquete, y el token de lectura va en el tuyo:

.npmrc
# .npmrc del repositorio (se commitea)@amset:registry=https://gitlab.amset.tech/api/v4/packages/npm/ # ~/.npmrc, el tuyo, fuera del repositorio (nunca se commitea)//gitlab.amset.tech/api/v4/packages/npm/:_authToken=<token con read_package_registry>
  • Un 401 no quiere decir «ese paquete no existe»: quiere decir que el token no vale. El registro contesta 401 a todo lo que llega sin credenciales.
  • Un nombre mal escrito no da error: lo que este registro no tiene lo reenvía a npm público, así que @amset/iu instalaría el paquete de un desconocido.

Una aplicación nueva

Código
npx amset-ui create mi-app                       # Next con App Routernpx amset-ui create mi-panel --template vite      # Vite

Sale con TypeScript estricto, ESLint, el armazón con su menú, una página por fichero, tu tema y el proveedor ya montados. Sin Tailwind.

Sus opciones y lo que escribe, con la salida real.
Pantallas enteras para añadirle: panel, clientes, factura, ajustes, entrar.

Un proyecto que ya existe

Código
npx amset-ui init --brand "#2F9E44" --installnpx amset-ui doctor

init pone la línea del registro, la hoja base en el orden de capas correcto, tu tema y el proveedor con el enrutador de Next, sin pisar nada tuyo. doctor comprueba que todo está en su sitio y sale con 1 si algo falla.

Qué toca, fichero a fichero, con el antes y el después.
Lo que diagnostica y cómo usarlo en la CI.

A mano

Código
npm install @amset/ui

No hay que importar ninguna hoja de estilos: cada componente trae la suya, y el empaquetador se lleva solo la de lo que usas. Las de la biblioteca van en la capa @layer amset-ui, así que cualquier CSS tuyo sin capa les gana sin !important. Si además importas la hoja entera (@amset/ui/styles.css), pagas las reglas dos veces: es una cosa o la otra.

El tema va en data-theme="light" o "dark" sobre <html>; sin el atributo manda el sistema operativo, y al imprimir siempre sale el claro. La densidad, en data-density. Los dos van sin prefijo: si tu proyecto ya usa data-theme con otros valores, la biblioteca no aplica ningún tema.

Sin proveedor, la biblioteca habla en castellano (es-ES). En una aplicación en otro idioma, AmsetProvider con su locale: textos, fechas, números y moneda.

Next con App Router

app/layout.tsx
// app/layout.tsx (componente de servidor)import { AmsetProvider, INITIAL_THEME_SCRIPT } from '@amset/ui'import { NextRouterProvider } from '@amset/ui/next' export default function Layout({ children }: { readonly children: React.ReactNode }) {  return (    <html lang="es" suppressHydrationWarning>      <head>        <script dangerouslySetInnerHTML={{ __html: INITIAL_THEME_SCRIPT }} />      </head>      <body>        <AmsetProvider locale="es-ES">          <NextRouterProvider>{children}</NextRouterProvider>        </AmsetProvider>      </body>    </html>  )}
  • Los enlaces. Sin un enrutador, Link y ButtonLink navegan recargando la página entera, sin ningún error. NextRouterProvider los lleva por router.push y los precarga al pasar el ratón o el foco, como next/link.
  • Servidor y cliente. Los componentes sin interacción no llevan 'use client'; los demás, lo más abajo posible. Ninguno lee «hoy» ni «ahora» mientras se pinta, así que el HTML del servidor y el del navegador coinciden al hidratar.
  • El tema sin parpadeo: INITIAL_THEME_SCRIPT en el <head> aplica el tema guardado antes del primer pintado.
  • SEO: @amset/ui/seo es una subruta de servidor, sin dependencias: pageMetadata (el Metadata de Next), JsonLd y checkSeo.

Next con Pages Router

Código
// next.config.tsexport default { transpilePackages: ['@amset/ui'] } // pages/_app.tsximport { AmsetProvider } from '@amset/ui'import { NextPagesRouterProvider } from '@amset/ui/next/pages' export default function App({ Component, pageProps }) {  return (    <AmsetProvider locale="es-ES">      <NextPagesRouterProvider>        <Component {...pageProps} />      </NextPagesRouterProvider>    </AmsetProvider>  )}

Con el Pages Router hace falta transpilePackages: ['@amset/ui']: sin él, Next carga el paquete con Node, que no sabe qué hacer con los .css que importa cada componente, y el build falla. El App Router, Vite y webpack no necesitan nada.

Vite (y cualquier otro enrutador)

Código
// src/main.tsx, con React Routerimport { AmsetProvider, RouterProvider } from '@amset/ui'import { useNavigate } from 'react-router' function Enrutado({ children }: { readonly children: React.ReactNode }) {  const navigate = useNavigate()  return <RouterProvider navigate={(href) => { void navigate(href) }}>{children}</RouterProvider>}

RouterProvider recibe la función con la que navega tu enrutador. Con React Router es navigate; con otro, la que use. Sin él, los enlaces recargan la página.

React 18 y 19

Funciona con React ^18.3.0 || ^19.0.0, y las dos versiones se prueban: además de la batería con React 19, la CI pinta cada ficha con React 18 de verdad y falla con cualquier excepción o aviso de React.

La única diferencia que se nota es el ref: en React 18 un componente de función no lo recibe como prop. Los tres que lo exponen en su tipo (Button, IconButton y FloatingButton) van envueltos para que llegue igual en las dos; en el resto, el elemento de dentro se pide con inputRef o similar.

Con Tailwind

Con Tailwind 4, o sin Tailwind, no hay que hacer nada. Tailwind 3 no emite capas nativas: su preflight y sus utilidades le ganan a cualquier capa, sea cual sea el orden de los imports, y el botón se queda sin fondo y el campo con el borde de @tailwindcss/forms. Se arregla poniendo el preflight por debajo de la biblioteca y las utilidades por encima, compilando Tailwind en dos ficheros:

globals.css
/* globals.css, con Tailwind 3 */@layer tw-base, amset-ui, tw-utils; @import './tailwind-base.css' layer(tw-base);   /* solo @tailwind base */@import './tailwind-utils.css' layer(tw-utils); /* @tailwind components y utilities */

Idiomas

Los textos de la biblioteca (etiquetas accesibles, mensajes, avisos del lector de pantalla) vienen en castellano y en inglés. Cualquier otro idioma se carga desde el proveedor como un JSON de "Componente.clave": "texto"; la plantilla con todas las claves es @amset/ui/locales/en.json.

Código
import { AmsetProvider } from '@amset/ui' const catalogs = {  fr: () => import('./i18n/fr.json'),   // solo se descarga si el idioma es «fr»  'pt-BR': () => import('./i18n/pt-BR.json'),} <AmsetProvider locale={locale} catalogs={catalogs}>{children}</AmsetProvider>

Se cargan bajo demanda: con siete idiomas configurados, el navegador descarga uno. Para pintarlo traducido en el servidor, loadMessages(locale, catalogs) lo resuelve antes y se pasa como messages.

Pruebas con Jest

Jest necesita un moduleNameMapper que convierta los .css en un módulo vacío, porque cada componente importa su hoja. Vitest ya lo hace solo.

jest.config.js
// jest.config.jsexport default {  moduleNameMapper: { '\\.css$': '<rootDir>/test/css-vacio.js' },}