The command line
amset-ui is the command line that ships inside @amset/ui: it is not installed separately, you call it with npx amset-ui. It creates an application, sets the library up in an existing project, diagnoses it, brings code from an earlier version up to date, generates your theme and copies components and whole screens into your repository.
Everything on this page is real output: each command runs when the site is built, in a clean folder, with the CLI from @amset/ui 0.17.1, on two sample projects (a freshly created Next application and a dashboard coming from 0.14). If the CLI changes a message, this page changes with it.
The commands
| Command | What for |
|---|---|
create | A new application, Next or Vite, with the library already in place. |
init | Set the library up in an existing project. |
doctor | Diagnose a project that already uses it. Exits with 1 if there are errors. |
migrate | Rewrite code that uses names the library has removed. |
theme | Generate your theme CSS from amset-ui.config.js. |
knobs | List what can be adjusted on each component. |
add and list | Copy the code of a component or a whole screen into your project. |
amset-ui - el tema de @amset/ui npx amset-ui create <carpeta> [--template next|vite] [--brand <#hex>] [--install] [--dry-run] Una aplicacion nueva con la biblioteca ya puesta: TypeScript estricto, ESLint, sin Tailwind, el armazon con su menu y cada pagina en su propio fichero. Next (App Router) por defecto. No escribe en una carpeta que ya tenga algo dentro. npx amset-ui add <componente o bloque>... [--dest <ruta>] [--overwrite] [--dry-run] npx amset-ui list [<texto>] [--blocks] Copia a tu proyecto el codigo de un componente, o un bloque: una pantalla entera (panel, alta de cliente, factura, ajustes...) hecha solo con componentes de la biblioteca. --blocks lista solo los bloques. npx amset-ui init [--brand <#hex>] [--install] [--dry-run] Deja el proyecto con la biblioteca puesta como toca: el .npmrc, las hojas en el orden de capas correcto, tu tema, el proveedor y el enrutador de Next. Idempotente: lo que ya esta bien no se toca. --dry-run cuenta que haria sin escribir nada. npx amset-ui migrate [<carpeta o fichero>...] [--dry-run] [--check] Pone al dia el codigo que usa nombres que @amset/ui retiro en la 0.14 (props y columnas de Table, sentido del orden, Formulario y los campos *Form, PropsCalendarRange...). Lo reescribe con el arbol de TypeScript de tu proyecto, no con expresiones regulares, y deja como aviso, con fichero y linea, lo que no puede decidir solo. --check no escribe y sale con 1 si queda algo que migrar (sirve en la CI). npx amset-ui doctor [--json] Diagnostica un proyecto que ya usa la biblioteca: capas CSS, Tailwind 3, data-theme, tema sin generar, enlaces sin enrutador, Pages Router, Jest. Sale con codigo 1 si hay algun error (sirve en la CI). npx amset-ui theme [--config <ruta>] [--out <ruta>] Genera tu CSS de tema desde amset-ui.config.js (o .mjs, o .json). npx amset-ui knobs [<componente>] Lista lo que puedes tocar de cada componente. El fichero de configuracion: export default { palette: { brand, secondary, tertiary, error, success, warning, info, neutral }, // en hex; DERIVA respetando la luminosidad colors: { dark: { ink: '#E6FAFF' } }, // un rol EXACTO, sin derivar base: { radius, 'border-width', font, ... }, // tokens globales, NO colores components: { button: { radius: '999px' } }, // ver "amset-ui knobs" selector: ':root', // la raiz del tema; otra cosa = una marca por seccion out: 'amset-theme.css', } El CSS generado se importa DESPUES de @amset/ui/styles.css: va sin @layera proposito, asi gana sobre la capa de la biblioteca sin !important.npx amset-ui --help prints it.create: a new application
Writes an application from the templates that ship in the package (starters/) and then runs init on it, so it comes out with the .npmrc, your theme and the provider in place, and doctor has nothing to report. Next with the App Router by default; --template vite for Vite. It does not write into a folder that already has something in it.
| Option | What it does |
|---|---|
--template next or vite | The template. Without it, Next with the App Router. |
--brand <#hex> | Your brand colour: the theme is generated from it. |
--install | Installs the dependencies with your package manager. |
--dry-run | Says what it would write, without writing anything. |
- Strict TypeScript and ESLint out of the box, and no Tailwind.
- The frame of an application:
AppShell,TopBar,ThemeButtonandSideNav, which marks the page you are on. - Each page in its own file. In Next, server pages with their
loading.tsx,error.tsxandnot-found.tsx; in Vite,React.lazyand the page preloaded when you hover its link.
Creado: mi-app, starter de next con 13 ficheros, en mi-app Para arrancarlo: cd mi-app npm install npm run dev El token del registro de @amset va en tu ~/.npmrc; el .npmrc del proyecto solo dice de donde sale el paquete. nuevo .gitignore nuevo eslint.config.js nuevo index.html nuevo package.json nuevo src/App.tsx nuevo src/PageErrorBoundary.tsx nuevo src/index.css nuevo src/main.tsx nuevo src/pages/CustomersPage.tsx nuevo src/pages/HomePage.tsx nuevo src/pages/NotFoundPage.tsx nuevo src/router.ts nuevo src/routes.ts nuevo src/vite-env.d.ts nuevo tsconfig.json nuevo vite.config.ts Escribiria: mi-panel, starter de vite con 16 ficheros, en mi-panel Para arrancarlo: cd mi-panel npm install npm run dev El token del registro de @amset va en tu ~/.npmrc; el .npmrc del proyecto solo dice de donde sale el paquete.init: the library in an existing project
Leaves a Next (App Router or Pages Router) or Vite project with the library set up properly: the @amset:registry line in the project's .npmrc (never a token), @amset/ui/styles/base.css at the top of the global CSS (which fixes the layer order without paying for the CSS twice), your generated theme, and AmsetProvider with the Next router mounted in the layout, in _app or around <App />.
It is idempotent: what is already right is left alone. It does not overwrite a file of yours, it only adds lines to it, and if it does not know where something goes it tells you the exact line. With Tailwind 3 it does not reorder the CSS, because that cannot be fixed with an import: doctor explains it.
| Option | What it does |
|---|---|
--brand <#hex> | Your brand colour for the theme. |
--install | Installs @amset/ui with your package manager. The registry token still goes in your ~/.npmrc. |
--dry-run | Reports what it would do, without writing anything. |
nuevo .npmrc nuevo amset-ui.config.js modificado app/globals.css modificado app/layout.tsx modificado package.json Cambiaria: 5 fichero(s). - Falta instalar el paquete: npm install @amset/ui (el token del registro va en tu ~/.npmrc; con --install lo hago yo). - Generaria el tema con: npx amset-ui theme --config amset-ui.config.js - Para que el tema no parpadee al cargar en el servidor: <script dangerouslySetInnerHTML={{ __html: INITIAL_THEME_SCRIPT }} /> en el <head> (INITIAL_THEME_SCRIPT sale de '@amset/ui'). nuevo .npmrc nuevo amset-ui.config.js modificado app/globals.css modificado app/layout.tsx modificado package.json Cambiado: 5 fichero(s). - Falta instalar el paquete: npm install @amset/ui (el token del registro va en tu ~/.npmrc; con --install lo hago yo). - Tema generado desde amset-ui.config.js. - Para que el tema no parpadee al cargar en el servidor: <script dangerouslySetInnerHTML={{ __html: INITIAL_THEME_SCRIPT }} /> en el <head> (INITIAL_THEME_SCRIPT sale de '@amset/ui'). Comprueba el resultado con: npx amset-ui doctorimport type { ReactNode } from 'react' import './globals.css' export default function RootLayout({ children }: { readonly children: ReactNode }) { return ( <html lang="es"> <body>{children}</body> </html> )}import type { ReactNode } from 'react' import './globals.css'import { AmsetProvider } from '@amset/ui'import { NextRouterProvider } from '@amset/ui/next' export default function RootLayout({ children }: { readonly children: ReactNode }) { return ( <html lang="es"> <body><AmsetProvider><NextRouterProvider>{children}</NextRouterProvider></AmsetProvider></body> </html> )}@import '@amset/ui/styles/base.css';@import './amset-theme.css';body { margin: 0;}doctor: what fails without warning
Diagnoses a project that already uses the library and looks for what breaks without raising any error. Each warning is a trap already found in a real project:
- Tailwind 3 without a layer order: its preflight and its utilities beat the components, and the button loses its background.
- A project
data-themewith other values (claro,oscuro): the library applies no theme and follows the operating system. - A token written as
${VARIABLE}in the repository's.npmrc. - The Pages Router without
transpilePackages. - The theme not generated, out of date with
amset-ui.config.js, or not imported. - Links that reload the whole page because the router is missing.
- Jest without a
moduleNameMapperfor.cssfiles. - Names the library has already removed, with the file they are in.
It exits with code 1 if there is any error, so it works as a gate in CI. With --json it writes the same for a machine: each finding with its id, its level and its title.
ok @amset/ui ^0.17.1 esta en package.jsonok React ^19.3.0ok El .npmrc del repo apunta @amset al registro de GitLabok El CSS global importa @amset/ui/styles/base.cssok app/amset-theme.css esta al dia con amset-ui.config.jsok No queda ningun nombre retirado en la 0.14 ni en la 0.16ok AmsetProvider esta montadook Los enlaces usan el enrutador de Next Todo en orden.init and installing the package.ok @amset/ui ^0.14.0 esta en package.jsonok React ^19.3.0ok El .npmrc del repo apunta @amset al registro de GitLaberror Tailwind 3 no emite capas nativas y gana a los componentes Su preflight, @tailwindcss/forms y sus utilidades salen sin @layer, y eso gana sobre cualquier capa: el boton se queda sin fondo ni borde y el input coge el borde de forms. Medido en un navegador. Arreglo: Declara '@layer tw-base, amset-ui, tw-utils;' y compila Tailwind en dos ficheros (@tailwind base por un lado; components y utilities por otro). Los pasos exactos estan en el README, apartado 'With Tailwind 3'.aviso app/globals.css no importa ninguna hoja de @amset/ui Los componentes llevan su CSS, pero nada fija el orden de las capas ni carga los tokens de forma explicita. Arreglo: npx amset-ui initerror 1 fichero(s) usan nombres que @amset/ui retiro en la 0.14 o en la 0.16 Ya no existen y no dan aviso: la compilacion falla o la prop se ignora. app/pedidos.tsx (@amset/ui/estilos/boton.css, numerica, ordenable, paginacion, para). Arreglo: npx amset-ui migrate (los reescribe; --dry-run cuenta antes lo que haria)ok AmsetProvider esta montadoaviso Los enlaces de la biblioteca no estan conectados al enrutador de Next Sin NextRouterProvider, Link y ButtonLink navegan recargando la pagina entera, sin ningun error. Arreglo: <NextRouterProvider> de '@amset/ui/next' alrededor de la aplicacion. npx amset-ui init lo pone. 2 error(es), 2 aviso(s).{ "findings": [ { "id": "installed", "level": "ok", "title": "@amset/ui ^0.17.1 esta en package.json" }, { "id": "react", "level": "ok", "title": "React ^19.3.0" }, { "id": "registry", "level": "ok", "title": "El .npmrc del repo apunta @amset al registro de GitLab" }, { "id": "styles", "level": "ok", "title": "El CSS global importa @amset/ui/styles/base.css" }, { "id": "theme", "level": "ok", "title": "app/amset-theme.css esta al dia con amset-ui.config.js" }, { "id": "legacy-names", "level": "ok", "title": "No queda ningun nombre retirado en la 0.14 ni en la 0.16" }, { "id": "provider", "level": "ok", "title": "AmsetProvider esta montado" }, { "id": "router", "level": "ok", "title": "Los enlaces usan el enrutador de Next" } ], "errors": 0, "warnings": 0}--json.It also reads the CSS: a 0.14 stylesheet imported from a stylesheet (@import '@amset/ui/estilos.css') comes out as an error, because since 0.17 it no longer exists, and migrate rewrites it to @amset/ui/styles.css. And a data-tema on the <html>, which the library no longer reads, is flagged with its line: the attribute is data-theme.
migrate: code from an earlier version, brought up to date
Rewrites code that uses names the library has removed: those from 0.14 (the props and column keys of Table, the sort direction, Formulario and the *Form fields...) and what 0.17 moved to English (the per-component stylesheets, the para prop of Tooltip, now trigger, or nueva of PasswordField, now newPassword...).
It does so with the syntax tree from your project's TypeScript, not with regular expressions: it tells a prop apart from the same word in a comment, a string or another component, respects your indentation and your comments, and follows aliased imports. Whatever it cannot decide on its own it leaves as a warning, with file and line, instead of guessing.
| Option | What it does |
|---|---|
<folder or file>… | Where to look. With nothing, the whole project. |
--dry-run | Reports the changes, without writing. |
--check | Does not write and exits with 1 if anything is left to migrate: for CI. |
app/pedidos.tsx 6 cambio(s) Cambiaria: 6 cambio(s) en 1 fichero(s). app/pedidos.tsx 6 cambio(s) Cambiado: 6 cambio(s) en 1 fichero(s).import { Button, Table, Tooltip } from '@amset/ui'import '@amset/ui/estilos/boton.css' const columnas = [ { key: 'cliente', title: 'Cliente', ordenable: true }, { key: 'total', title: 'Total', numerica: true },] export function Pedidos({ filas }: { readonly filas: readonly { id: string; cliente: string; total: number }[] }) { return ( <> <Tooltip content="Exportar" para={<Button>Exportar</Button>} /> <Table aria-label="Pedidos" columns={columnas} rows={filas} enServidor paginacion={{ pageSize: 20 }} /> </> )}import { Button, Table, Tooltip } from '@amset/ui'import '@amset/ui/styles/button.css' const columnas = [ { key: 'cliente', title: 'Cliente', sortable: true }, { key: 'total', title: 'Total', numeric: true },] export function Pedidos({ filas }: { readonly filas: readonly { id: string; cliente: string; total: number }[] }) { return ( <> <Tooltip content="Exportar" trigger={<Button>Exportar</Button>} /> <Table aria-label="Pedidos" columns={columnas} rows={filas} serverSide pagination={{ pageSize: 20 }} /> </> )} Nada que migrar: no queda ningun nombre retirado en lo que he podido leer.theme: your theme, generated
Generates your theme CSS from amset-ui.config.js (or .mjs, or .json). It derives each colour of the palette while respecting the lightness of each role, and checks the contrast; if a colour asks for more saturation than a screen can show, it lowers it and says so.
| Option | What it does |
|---|---|
--config <path> | A different configuration file. |
--out <path> | Where to write the CSS. It can also be set in the configuration, with out. |
export default { palette: { brand: '#7c3aed', secondary: '#0f766e' }, colors: { dark: { ink: '#f5f3ff' } }, base: { radius: '10px' }, components: { button: { radius: '999px' } }, out: 'app/amset-theme.css',}Aviso: accent-active (light) pedia una saturacion de 0.2336 y la pantalla solo llega a 0.2222. Se ha bajado para que el color sea el mismo en todas partes.Aviso: accent-soft (light) pedia una saturacion de 0.0273 y la pantalla solo llega a 0.0234. Se ha bajado para que el color sea el mismo en todas partes.Aviso: secondary-hover (light) pedia una saturacion de 0.0861 y la pantalla solo llega a 0.0787. Se ha bajado para que el color sea el mismo en todas partes.Aviso: secondary-active (light) pedia una saturacion de 0.081 y la pantalla solo llega a 0.07. Se ha bajado para que el color sea el mismo en todas partes.Aviso: secondary-text (light) pedia una saturacion de 0.0861 y la pantalla solo llega a 0.0823. Se ha bajado para que el color sea el mismo en todas partes.Aviso: accent-text (dark) pedia una saturacion de 0.1363 y la pantalla solo llega a 0.117. Se ha bajado para que el color sea el mismo en todas partes.Aviso: focus (dark) pedia una saturacion de 0.1363 y la pantalla solo llega a 0.117. Se ha bajado para que el color sea el mismo en todas partes.Aviso: secondary-hover (dark) pedia una saturacion de 0.0861 y la pantalla solo llega a 0.0823. Se ha bajado para que el color sea el mismo en todas partes.Aviso: secondary-active (dark) pedia una saturacion de 0.081 y la pantalla solo llega a 0.0735. Se ha bajado para que el color sea el mismo en todas partes.Escrito app/amset-theme.css (3.1 kB).Importalo despues de @amset/ui/styles.css./* Generado por "npx amset-ui theme" - no editar a mano. * Va sin @layer a proposito: asi gana sobre la capa amset-ui sin !important. * Importalo DESPUES de @amset/ui/styles.css. */:root { --amset-accent: oklch(0.5 0.2466 293.01); --amset-accent-hover: oklch(0.45 0.2466 293.01); --amset-accent-active: oklch(0.4 0.2222 293.01); --amset-accent-text: oklch(0.47 0.2466 293.01); --amset-accent-soft: oklch(0.955 0.0234 293.01); --amset-accent-ink: oklch(0.985 0.0077 293.01); --amset-secondary: oklch(0.5 0.0861 186.39); --amset-secondary-hover: oklch(0.45 0.0787 186.39); --amset-secondary-active: oklch(0.4 0.07 186.39); --amset-secondary-text: oklch(0.47 0.0823 186.39); --amset-secondary-soft: oklch(0.955 0.0101 186.39); --amset-secondary-ink: oklch(0.985 0.003 186.39); --amset-focus: oklch(0.5 0.2466 293.01); --amset-radius: 10px;} [data-theme='dark'] { --amset-accent: oklch(0.52 0.2466 293.01); --amset-accent-hover: oklch(0.47 0.2466 293.01); --amset-accent-active: oklch(0.42 0.2334 293.01); --amset-accent-text: oklch(0.79 0.117 293.01); --amset-accent-soft: oklch(0.32 0.0908 293.01); --amset-accent-ink: oklch(0.985 0.0077 293.01); --amset-secondary: oklch(0.52 0.0861 186.39); --amset-secondary-hover: oklch(0.47 0.0823 186.39); --amset-secondary-active: oklch(0.42 0.0735 186.39); --amset-secondary-text: oklch(0.79 0.0506 186.39); --amset-secondary-soft: oklch(0.32 0.0329 186.39); --amset-secondary-ink: oklch(0.985 0.003 186.39); --amset-focus: oklch(0.79 0.117 293.01); --amset-ink: #f5f3ff;} @media (prefers-color-scheme: dark) { :root:not([data-theme='light']) { --amset-accent: oklch(0.52 0.2466 293.01); --amset-accent-hover: oklch(0.47 0.2466 293.01); --amset-accent-active: oklch(0.42 0.2334 293.01); --amset-accent-text: oklch(0.79 0.117 293.01); --amset-accent-soft: oklch(0.32 0.0908 293.01); --amset-accent-ink: oklch(0.985 0.0077 293.01); --amset-secondary: oklch(0.52 0.0861 186.39); --amset-secondary-hover: oklch(0.47 0.0823 186.39); --amset-secondary-active: oklch(0.42 0.0735 186.39); --amset-secondary-text: oklch(0.79 0.0506 186.39); --amset-secondary-soft: oklch(0.32 0.0329 186.39); --amset-secondary-ink: oklch(0.985 0.003 186.39); --amset-focus: oklch(0.79 0.117 293.01); --amset-ink: #f5f3ff; }} @media print { :root, [data-theme='dark'], :root:not([data-theme='light']) { --amset-accent: oklch(0.5 0.2466 293.01); --amset-accent-hover: oklch(0.45 0.2466 293.01); --amset-accent-active: oklch(0.4 0.2222 293.01); --amset-accent-text: oklch(0.47 0.2466 293.01); --amset-accent-soft: oklch(0.955 0.0234 293.01); --amset-accent-ink: oklch(0.985 0.0077 293.01); --amset-secondary: oklch(0.5 0.0861 186.39); --amset-secondary-hover: oklch(0.45 0.0787 186.39); --amset-secondary-active: oklch(0.4 0.07 186.39); --amset-secondary-text: oklch(0.47 0.0823 186.39); --amset-secondary-soft: oklch(0.955 0.0101 186.39); --amset-secondary-ink: oklch(0.985 0.003 186.39); --amset-focus: oklch(0.5 0.2466 293.01); }} .amset-button { --amset-button-radius: 999px;}@layer on purpose: that way it beats the library's layer without !important.knobs: what can be adjusted
Lists each component's --amset-<componente>-* variables with their default value: they are the keys accepted by components in amset-ui.config.js. Without a name, you get the 678 of the 108 components that have them; with a name, those of the components that match it.
button .amset-button color var(--amset-ink) ink var(--amset-surface) soft var(--amset-divider) text var(--amset-ink) text-active var(--amset-button-text) border var(--amset-border-bold) height var(--amset-height-control) padding var(--amset-space-3) font var(--amset-text-base) icon 1rem radius var(--amset-radius) weight var(--amset-weight-bold) family var(--amset-font) transform none spacing normal line 1 gap var(--amset-space-2) shadow none background transparent background-hover var(--amset-button-background) background-active var(--amset-button-background) textbutton .amset-textbutton color var(--amset-accent-text) offset 0.15em togglebutton .amset-togglebutton selected-background var(--amset-accent-soft) selected-ink var(--amset-accent-text) mark calc(var(--amset-border-width) * 2) 3 componentes, 26 mandos.add and list: the code in your repository
For those who prefer to have the code in their repository rather than depend on a published version: add copies a component's code with everything it needs, its stylesheets and a text catalogue with only what was copied; list shows what is available: 216 components and 7 blocks. It strips the .js extension from imports, does not overwrite a file you have already changed, and records what was copied in amset-ui.json.
A block is a whole screen: the dashboard, the new customer form, an invoice... It imports @amset/ui just like your application, so it copies no component code: only its own.
| Option | What it does |
|---|---|
--all | All components. |
--dest <path> | Where to copy. By default, amset-ui/. |
--overwrite | Also overwrites what you had changed. |
--keep-js | Keeps the .js extension in imports. |
--dry-run | Lists what it would copy. |
list --blocks | Only the blocks. |
nuevo base/attributes.ts nuevo components/button-group-context.ts nuevo components/button-parts.ts nuevo components/button.tsx nuevo components/choice-parts.tsx nuevo components/link.tsx nuevo messages/bundle.ts nuevo messages/texts/button.ts nuevo utils/cn.ts nuevo utils/links.ts nuevo utils/locale.tsx nuevo utils/messages.ts nuevo utils/reference.ts nuevo utils/routing.tsx nuevo utils/variants.ts nuevo messages/es.ts nuevo messages/en.ts nuevo styles/base.css nuevo styles/button-group.css nuevo styles/button.css nuevo styles/floating-button.css nuevo styles/print.css nuevo styles/text.css nuevo styles/theme-button.css nuevo styles.css Copiaria: 1 componente(s) en amset-ui (25 nuevos, 0 pisados, 0 iguales, 0 conservados). import { Button } from './amset-ui/components/button' Importa los estilos una vez, arriba de la aplicacion: amset-ui/styles.cssBloques (pantallas enteras):dashboard Dashboard Panel de gestióncustomer-list CustomerList Listado de clientescustomer-form CustomerForm Alta de clientecustomer-detail CustomerDetail Ficha de clienteinvoice-form InvoiceForm Emisión de facturasettings Settings Ajustessign-in SignIn Inicio de sesión 0 componentes y 7 bloques. "npx amset-ui add <nombre>" copia el codigo. nuevo blocks/dashboard/dashboard.tsx nuevo blocks/shared/dates.ts nuevo blocks/shared/invoices.tsx nuevo blocks/shared/screen.ts Copiado: 1 bloque(s) en amset-ui (4 nuevos, 0 pisados, 0 iguales, 0 conservados). import { Dashboard } from './amset-ui/blocks/dashboard/dashboard'In CI
doctor and migrate --check exit with 1 when something fails, so they go into CI as one more step. In GitLab CI:
amset-ui: stage: test script: - npm ci - npx amset-ui doctor - npx amset-ui migrate src --check