@eduardoalvarez/arrecife 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/llms.txt CHANGED
@@ -99,32 +99,64 @@ escribe con el nombre de la tabla.
99
99
 
100
100
  ## Qué importar de dónde
101
101
 
102
- `exports` tiene cinco subrutas y la elección importa: tres de ellas **no
103
- arrastran React**, y por eso pueden consumirse desde un worker, un
104
- `astro.config.mjs`, un script de build o un generador con Satori.
105
-
106
- | Subruta | Arrastra React | Para qué |
107
- | --- | --- | --- |
108
- | `@eduardoalvarez/arrecife` | sí | Componentes, primitivos, marca, `cn`. Reexporta tokens |
109
- | `@eduardoalvarez/arrecife/tokens` | **no** | El objeto `tokens` en JS. Satori, Astro, scripts |
110
- | `@eduardoalvarez/arrecife/tokens/theme.css` | — | El `@theme` de Tailwind v4 |
111
- | `@eduardoalvarez/arrecife/og` | **no** | Las plantillas de Open Graph para Satori |
112
- | `@eduardoalvarez/arrecife/shiki` | **no** | El tema de resaltado de sintaxis |
113
- | `@eduardoalvarez/arrecife/brand` | sí | Logo, isotipo y mascota como componentes |
114
- | `@eduardoalvarez/arrecife/assets/*` | — | Los PNG de la marca |
102
+ La elección importa, y por dos motivos distintos. Cuatro subrutas **no arrastran
103
+ React**, así que pueden consumirse desde un worker, un `astro.config.mjs`, un
104
+ script de build o un generador con Satori. Otras dos piden una **dependencia de
105
+ pares opcional** que solo instala quien las use.
106
+
107
+ | Subruta | Arrastra React | Pide además | Para qué |
108
+ | --- | --- | --- | --- |
109
+ | `@eduardoalvarez/arrecife` | sí | — | Componentes, primitivos, marca, `cn`. Reexporta tokens y tema |
110
+ | `@eduardoalvarez/arrecife/tokens` | **no** | — | El objeto `tokens` en JS. Satori, Astro, scripts |
111
+ | `@eduardoalvarez/arrecife/tokens/theme.css` | — | — | El `@theme` de Tailwind v4 |
112
+ | `@eduardoalvarez/arrecife/tema` | **no** | — | `scriptTema` para el `<head>`, y leer o cambiar el modo |
113
+ | `@eduardoalvarez/arrecife/og` | **no** | — | Las plantillas de Open Graph para Satori |
114
+ | `@eduardoalvarez/arrecife/shiki` | **no** | — | El tema de resaltado de sintaxis |
115
+ | `@eduardoalvarez/arrecife/brand` | sí | — | Logo, isotipo y mascota como componentes |
116
+ | `@eduardoalvarez/arrecife/form` | sí | `react-hook-form` | La capa de formulario: etiquetas, errores y `aria-*` |
117
+ | `@eduardoalvarez/arrecife/chart` | sí | `recharts` | El chasis de las gráficas y la paleta de series |
118
+ | `@eduardoalvarez/arrecife/assets/*` | — | — | Los PNG de la marca |
115
119
 
116
120
  Importar la raíz desde un script de build para sacar un token es el error que las
117
121
  subrutas existen para evitar: arrastra React entero a un worker que no lo monta.
118
122
 
123
+ `./form` y `./chart` están fuera de la raíz por el motivo simétrico: si colgaran
124
+ del índice principal, los proyectos que no dibujan gráficas ni usan React Hook
125
+ Form tendrían que instalar esas dependencias igualmente para que su bundler
126
+ resolviera un import que nunca ejecutan.
127
+
119
128
  ```ts
120
129
  // Bien, en un generador de OG o en astro.config.mjs
121
130
  import { tokens } from '@eduardoalvarez/arrecife/tokens';
122
131
  import { arrecife } from '@eduardoalvarez/arrecife/shiki';
123
132
 
133
+ // Bien, en el <head> de un Astro que no monta React
134
+ import { scriptTema } from '@eduardoalvarez/arrecife/tema';
135
+
124
136
  // Mal: monta React donde no hace falta
125
137
  import { tokens } from '@eduardoalvarez/arrecife';
126
138
  ```
127
139
 
140
+ ### El tema, y el parpadeo de la primera pintura
141
+
142
+ `ThemeToggle` es el botón; lo difícil está en `./tema`. Sin `scriptTema` inline en
143
+ el `<head>`, la primera pintura sale con el modo por defecto y el elegido entra
144
+ un frame después: en un sitio oscuro que el usuario dejó en claro, eso es un
145
+ fogonazo blanco en cada carga.
146
+
147
+ ```astro
148
+ ---
149
+ import { scriptTema } from '@eduardoalvarez/arrecife/tema';
150
+ ---
151
+ <head>
152
+ <script is:inline set:html={scriptTema} />
153
+ </head>
154
+ ```
155
+
156
+ Tiene que ir INLINE. Un `<script src>`, aunque sea síncrono, se descarga, y el
157
+ parpadeo vuelve. El script reengancha en `astro:after-swap` porque las
158
+ transiciones de vista reemplazan el `<html>` entero.
159
+
128
160
  ## Tokens
129
161
 
130
162
  La fuente es un objeto de TypeScript y la salida CSS se genera de él, así que el
@@ -260,7 +292,76 @@ primitiva de Radix se resumen en la línea `Extiende`, y son los de siempre.
260
292
 
261
293
  ## Primitivos
262
294
 
263
- Se importan de `@eduardoalvarez/arrecife`. 100 exportaciones.
295
+ Se importan de `@eduardoalvarez/arrecife`. 115 exportaciones.
296
+
297
+ ### Accordion, AccordionItem, AccordionTrigger, AccordionContent
298
+
299
+ Fuente: `src/primitives/accordion.tsx`
300
+
301
+ **Accordion**
302
+ El plegable. Lo pedían dos proyectos: el FAQ del portafolio y el temario de cursos, que es literalmente una lista de secciones que se abren.
303
+
304
+ - Extiende: `ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>`
305
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
306
+
307
+ **AccordionItem**
308
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
309
+
310
+ **AccordionTrigger**
311
+ El disparador es el encabezado, así que va DENTRO de un `<h3>`: Radix envuelve el botón en `AccordionPrimitive.Header`, que renderiza el elemento que se le pida. Sin eso, un lector de pantalla ve una lista de botones sueltos y pierde la estructura de la página, que es justo lo que un FAQ necesita conservar.
312
+
313
+ - Extiende: `ComponentPropsWithoutRef< typeof AccordionPrimitive.Trigger >`
314
+
315
+ | prop | tipo | req. | defecto | qué hace |
316
+ | --- | --- | --- | --- | --- |
317
+ | `headingLevel` | `4 \| 2 \| 3` | | `3` | Nivel del encabezado que envuelve al disparador. |
318
+
319
+ **AccordionContent**
320
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
321
+
322
+ ### AlertDialogOverlay, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogCancel, AlertDialogAction, AlertDialog, AlertDialogTrigger
323
+
324
+ Fuente: `src/primitives/alert-dialog.tsx`
325
+
326
+ **AlertDialogOverlay**
327
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
328
+
329
+ **AlertDialogContent**
330
+ Sin entrada animada, igual que `Dialog`: aparece donde va a quedarse.
331
+
332
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
333
+
334
+ **AlertDialogHeader**
335
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
336
+
337
+ **AlertDialogFooter**
338
+ Cancelar a la IZQUIERDA de confirmar en escritorio y ABAJO en móvil, que es lo que da `flex-col-reverse`: el orden del DOM pone cancelar primero —es donde va el foco— y en columna el dedo lo encuentra donde toca sin cambiar la tabulación.
339
+
340
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
341
+
342
+ **AlertDialogTitle**
343
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
344
+
345
+ **AlertDialogDescription**
346
+ Lo que se pierde, dicho entero. Es lo que el rol `alertdialog` hace que se anuncie de entrada, así que aquí no va «esta acción no se puede deshacer» suelto: va qué se borra y qué se lleva por delante.
347
+
348
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
349
+
350
+ **AlertDialogCancel**
351
+ El que se lleva el foco al abrir.
352
+
353
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
354
+
355
+ **AlertDialogAction**
356
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
357
+
358
+ **AlertDialog**
359
+ La confirmación destructiva. NO es un `Dialog` con otro texto, y por eso está en su propio archivo y sobre su propia primitiva de Radix.
360
+
361
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
362
+
363
+ **AlertDialogTrigger**
364
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
264
365
 
265
366
  ### Alert
266
367
 
@@ -764,6 +865,19 @@ Sin deslizamiento de entrada: el aviso aparece donde va a quedarse.
764
865
  **ToastAction**
765
866
  - Sin props propios: pasa los del elemento o la primitiva que envuelve.
766
867
 
868
+ ### Toaster
869
+
870
+ Fuente: `src/primitives/toaster.tsx`
871
+
872
+ Va UNA vez, lo más arriba posible del árbol. Dos `Toaster` montados pintan cada aviso dos veces: la lista es del módulo, no de la instancia.
873
+
874
+ - Extiende: `{ /** Cuánto dura un aviso que no dice lo contrario. */ duration?: number; /** * Nombre del landmark que Radix crea para la región de avisos. Se traduce * porque lo lee un lector de pantalla, y el default de Radix está en inglés. */ label?: string; }`
875
+
876
+ | prop | tipo | req. | defecto | qué hace |
877
+ | --- | --- | --- | --- | --- |
878
+ | `duration` | `number` | | `5000` | Cuánto dura un aviso que no dice lo contrario. |
879
+ | `label` | `string` | | `Avisos` | Nombre del landmark que Radix crea para la región de avisos. Se traduce porque lo lee un lector de pantalla, y el default de Radix está en inglés. |
880
+
767
881
  ### TooltipContent, TooltipProvider, Tooltip, TooltipTrigger
768
882
 
769
883
  Fuente: `src/primitives/tooltip.tsx`
@@ -788,15 +902,15 @@ Fuente: `src/primitives/typography.tsx`
788
902
 
789
903
  | prop | tipo | req. | defecto | qué hace |
790
904
  | --- | --- | --- | --- | --- |
791
- | `as` | `"caption" \| "dd" \| "dt" \| "em" \| "figcaption" \| "h1" \| "h2" \| "h3" \| "h4" \| "legend" \| "li" \| "p" \| "span" \| "strong"` | | | Etiqueta HTML. Por defecto, la que corresponde a la escala. |
905
+ | `as` | `"h2" \| "h3" \| "li" \| "p" \| "span" \| "caption" \| "dd" \| "dt" \| "em" \| "figcaption" \| "h1" \| "h4" \| "legend" \| "strong"` | | | Etiqueta HTML. Por defecto, la que corresponde a la escala. |
792
906
  | `asChild` | `boolean` | | `false` | Renderiza el hijo en vez de crear un elemento, para envolver un enlace. |
793
907
  | `measure` | `boolean` | | | Corta la línea a 68ch. Activo por defecto en `body`, que es la única escala pensada para leerse en párrafos largos. |
794
908
  | `tone` | `"accent" \| "success" \| "warning" \| "error" \| "warm" \| "primary" \| "secondary" \| "muted"` | | `primary` | |
795
- | `variant` | `"display" \| "body" \| "h1" \| "h2" \| "h3" \| "label" \| "meta" \| "stat" \| "lead" \| "ui" \| "tag" \| "chip" \| "eyebrow"` | | `body` | |
909
+ | `variant` | `"display" \| "h2" \| "h3" \| "label" \| "body" \| "h1" \| "meta" \| "stat" \| "lead" \| "ui" \| "tag" \| "chip" \| "eyebrow"` | | `body` | |
796
910
 
797
911
  ## Componentes
798
912
 
799
- Se importan de `@eduardoalvarez/arrecife`. 21 exportaciones.
913
+ Se importan de `@eduardoalvarez/arrecife`. 23 exportaciones.
800
914
 
801
915
  ### ArticleCard
802
916
 
@@ -812,6 +926,7 @@ La línea de metadatos va en `meta` y no en `eyebrow`: `18 ago 2026 · 8 min de
812
926
  | `date` | `ReactNode` | | | Fecha ya formateada por el proyecto: la librería no impone locale. |
813
927
  | `dateTime` | `string` | | | Valor de `datetime` del `<time>`, en ISO. |
814
928
  | `excerpt` | `ReactNode` | | | Entradilla. Se corta a dos líneas para que la rejilla no se desalinee. |
929
+ | `headingLevel` | `2 \| 3` | | `3` | Nivel del titular. `h3` por defecto: una tarjeta suelta en una rejilla no gana el nivel que su posición no le da. |
815
930
  | `readingMinutes` | `number` | | | |
816
931
  | `tags` | `readonly string[]` | | | |
817
932
  | `title` | `ReactNode` | sí | | |
@@ -925,6 +1040,7 @@ Fuente: `src/components/footer/index.tsx`
925
1040
 
926
1041
  | prop | tipo | req. | defecto | qué hace |
927
1042
  | --- | --- | --- | --- | --- |
1043
+ | `brand` | `ReactNode` | | | La fila de marca: la aleta y el wordmark, arriba del todo. |
928
1044
  | `social` | `readonly Red[]` | | | |
929
1045
  | `year` | `number` | | `new Date().getFullYear()` | Año de la firma. |
930
1046
 
@@ -951,6 +1067,7 @@ UNO por sitio. Es la única pieza del sistema que se gasta como el botón de con
951
1067
  | `eyebrow` | `ReactNode` | | | Mono, versalitas, en acento. |
952
1068
  | `pose` | `"desk" \| "laptop-coffee" \| "peek" \| "surf"` | | | La pose de Tiburoncín. Sin ella el hero es un panel con texto. |
953
1069
  | `title` | `ReactNode` | sí | | |
1070
+ | `variant` | `"cabecera" \| "centrado"` | | `cabecera` | `cabecera` sangra la pose por la esquina; `centrado` la pone arriba y centra el texto, para una página que es solo esto. |
954
1071
 
955
1072
  ### LinkRow
956
1073
 
@@ -1006,7 +1123,11 @@ Fuente: `src/components/newsletter-form/index.tsx`
1006
1123
  | `errorMessage` | `ReactNode` | | `No se pudo suscribir ese correo. Revísalo y vuelve a intentar.` | |
1007
1124
  | `expresion` | `"annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink"` | | | |
1008
1125
  | `fieldLabel` | `string` | | `Correo electrónico` | |
1009
- | `onSubmitEmail` | `(email: string) => void` | | | Se dispara con el correo ya leído del campo. |
1126
+ | `nameField` | `boolean` | | `false` | Añade el campo de nombre delante del correo. |
1127
+ | `nameInputProps` | `Omit<InputProps, "id" \| "disabled" \| "name">` | | | Lo que el proyecto necesite colgar del campo de nombre: `minLength`, `maxLength`, `pattern`. La librería no impone ninguna de las tres. |
1128
+ | `nameLabel` | `string` | | `Nombre` | |
1129
+ | `namePlaceholder` | `string` | | `Cómo te llamas` | |
1130
+ | `onSubmitEmail` | `(email: string, name?: string \| undefined) => void` | | | Se dispara con el correo ya leído del campo, y con el nombre si el campo está puesto. |
1010
1131
  | `placeholder` | `string` | | `tu@correo.dev` | |
1011
1132
  | `state` | `"error" \| "reposo" \| "enviando" \| "exito"` | | `reposo` | |
1012
1133
  | `submitLabel` | `string` | | `Suscribirme` | |
@@ -1024,12 +1145,26 @@ Una sola cabecera en dos escalas, no dos componentes.
1024
1145
  | prop | tipo | req. | defecto | qué hace |
1025
1146
  | --- | --- | --- | --- | --- |
1026
1147
  | `action` | `ReactNode` | | | Ranura para las llamadas a la acción. Si aquí va un botón de conversión, es el único de la pantalla. |
1027
- | `as` | `"h1" \| "h2"` | | `h1` | Nivel del titular. `h1` salvo que la página ya tenga uno. |
1148
+ | `as` | `"h2" \| "h1"` | | `h1` | Nivel del titular. `h1` salvo que la página ya tenga uno. |
1028
1149
  | `description` | `ReactNode` | | | |
1029
1150
  | `eyebrow` | `ReactNode` | | | Mono, versalitas, en acento. Es la sección a la que pertenece la página. |
1030
1151
  | `size` | `"display" \| "page"` | | `page` | |
1031
1152
  | `title` | `ReactNode` | sí | | |
1032
1153
 
1154
+ ### ScrollingProgressBar
1155
+
1156
+ Fuente: `src/components/scrolling-progress-bar/index.tsx`
1157
+
1158
+ Cuánto llevas leído. NO es `Progress` con otro nombre.
1159
+
1160
+ - Extiende: `Omit<ComponentPropsWithoutRef<'div'>, 'children'>`
1161
+
1162
+ | prop | tipo | req. | defecto | qué hace |
1163
+ | --- | --- | --- | --- | --- |
1164
+ | `sticky` | `boolean` | | `true` | Pega la barra al borde superior de la ventana. |
1165
+ | `target` | `RefObject<HTMLElement \| null>` | | | El elemento que se mide. Sin él, el documento entero. |
1166
+ | `tone` | `"accent" \| "warm"` | | `accent` | Arena en vez de bioluz, para igualar el progreso de curso. |
1167
+
1033
1168
  ### SidebarItem, SidebarNav
1034
1169
 
1035
1170
  Fuente: `src/components/sidebar-nav/index.tsx`
@@ -1079,11 +1214,27 @@ Fuente: `src/components/talk-card/index.tsx`
1079
1214
  | `asChild` | `boolean \| undefined` | | | Renderiza el hijo en vez de un `<a>`. Es como se enchufa el `Link` de Next o de Astro sin que la librería dependa de ningún enrutador. |
1080
1215
  | `date` | `ReactNode` | | | |
1081
1216
  | `dateTime` | `string` | | | |
1217
+ | `description` | `ReactNode` | | | De qué iba la charla. Se corta a dos líneas, igual que el `excerpt` de `ArticleCard`, para que la rejilla no se desalinee. |
1082
1218
  | `event` | `ReactNode` | sí | | Dónde se dio: la conferencia, el meetup, el equipo. |
1083
1219
  | `location` | `ReactNode` | | | |
1084
1220
  | `status` | `ReactNode` | | | Etiqueta corta de estado: «con vídeo», «próxima», «solo audio». |
1085
1221
  | `title` | `ReactNode` | sí | | |
1086
1222
 
1223
+ ### ThemeToggle
1224
+
1225
+ Fuente: `src/components/theme-toggle/index.tsx`
1226
+
1227
+ El control que faltaba. La librería definía todo el sistema de temas y no exponía lo que lo cambia, así que dos proyectos lo reimplementaban.
1228
+
1229
+ - Extiende: `Omit<ComponentPropsWithoutRef<'button'>, 'onClick'>`
1230
+
1231
+ | prop | tipo | req. | defecto | qué hace |
1232
+ | --- | --- | --- | --- | --- |
1233
+ | `label` | `string` | | `Cambiar de tema` | Nombre accesible. El botón no tiene texto visible, así que es lo único que lo nombra. |
1234
+ | `onThemeChange` | `(tema: Tema) => void` | | | Se dispara con el tema que quedó puesto, por si el proyecto quiere anotarlo. |
1235
+ | `size` | `"sm" \| "md" \| "lg" \| "icon"` | | `icon` | |
1236
+ | `variant` | `"primary" \| "conversion" \| "secondary" \| "tertiary"` | | `secondary` | |
1237
+
1087
1238
  ### TableOfContents
1088
1239
 
1089
1240
  Fuente: `src/components/toc/index.tsx`
@@ -1152,6 +1303,100 @@ La cabeza de Tiburoncín, con expresión.
1152
1303
  | `basePath` | `string` | | `RUTA_ASSETS` | |
1153
1304
  | `expresion` | `"annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink"` | sí | | |
1154
1305
 
1306
+ ## Formularios
1307
+
1308
+ Se importan de `@eduardoalvarez/arrecife/form` · pide `react-hook-form`. 7 exportaciones.
1309
+
1310
+ ### FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage, Form
1311
+
1312
+ Fuente: `src/form/index.tsx`
1313
+
1314
+ **FormField**
1315
+ Un campo controlado. Envuelve el `Controller` de RHF y además publica el nombre en contexto, que es de donde lo leen la etiqueta y el mensaje sin que haya que repetirlo tres veces.
1316
+
1317
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1318
+
1319
+ **FormItem**
1320
+ La caja del campo: etiqueta, control, ayuda y mensaje, en columna.
1321
+
1322
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1323
+
1324
+ **FormLabel**
1325
+ La etiqueta NO se tiñe de rojo cuando el campo falla.
1326
+
1327
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1328
+
1329
+ **FormControl**
1330
+ Envuelve al control y le cablea los atributos: el `id` que la etiqueta apunta, el `aria-describedby` con la ayuda y el mensaje, y el `aria-invalid`.
1331
+
1332
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1333
+
1334
+ **FormDescription**
1335
+ La ayuda del campo. Se anuncia siempre, haya error o no.
1336
+
1337
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1338
+
1339
+ **FormMessage**
1340
+ El mensaje de validación. Sin error no renderiza nada: un hueco reservado para el fallo desplaza el resto del formulario cada vez que aparece.
1341
+
1342
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1343
+
1344
+ **Form**
1345
+ La capa que ata los controles a un formulario con validación y mensajes.
1346
+
1347
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1348
+
1349
+ ## Gráficas
1350
+
1351
+ Se importan de `@eduardoalvarez/arrecife/chart` · pide `recharts`. 5 exportaciones.
1352
+
1353
+ ### ChartContainer, ChartTooltip, ChartLegend, ChartTooltipContent, ChartLegendContent
1354
+
1355
+ Fuente: `src/chart/index.tsx`
1356
+
1357
+ **ChartContainer**
1358
+ Envuelve la gráfica en un `<figure>` con nombre accesible y le da a Recharts el alto concreto que necesita para medirse.
1359
+
1360
+ - Extiende: `Omit<ComponentPropsWithoutRef<'figure'>, 'title'>`
1361
+
1362
+ | prop | tipo | req. | defecto | qué hace |
1363
+ | --- | --- | --- | --- | --- |
1364
+ | `height` | `number` | | `320` | Alto en píxeles. Recharts necesita uno concreto para medir. |
1365
+ | `label` | `string` | sí | | Qué muestra la gráfica, en una frase. Obligatorio, como el `label` de `Progress`: un `<svg>` de barras sin nombre accesible no es «una gráfica sin etiqueta», es una región vacía. |
1366
+ | `summary` | `ReactNode` | | | Lo que la gráfica dice, en palabras. Va en un `figcaption` oculto visualmente. |
1367
+
1368
+ **ChartTooltip**
1369
+ El `Tooltip` de Recharts con los defectos del sistema: sin animación y con el cursor teñido de `surfaceRaised`.
1370
+
1371
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1372
+
1373
+ **ChartLegend**
1374
+ - Sin props propios: pasa los del elemento o la primitiva que envuelve.
1375
+
1376
+ **ChartTooltipContent**
1377
+ La caja del tooltip. Es una tarjeta del sistema —`surface`, borde de control, sombra estándar— y no la caja blanca de Recharts, que en modo oscuro es un rectángulo blanco encima de un panel oscuro.
1378
+
1379
+ - Extiende: `{ active?: boolean \| undefined; payload?: readonly ChartPayloadItem[] \| undefined; label?: ReactNode; /** Formatea el valor. Sin ella se imprime tal cual: la librería no impone locale. */ formatter?: ((valor: unknown, item: ChartPayloadItem) => ReactNode) \| undefined; /** Oculta el encabezado, para una gráfica de una sola categoría. */ hideLabel?: boolean; className?: string; }`
1380
+
1381
+ | prop | tipo | req. | defecto | qué hace |
1382
+ | --- | --- | --- | --- | --- |
1383
+ | `active` | `boolean \| undefined` | | | |
1384
+ | `className` | `string` | | | |
1385
+ | `formatter` | `(valor: unknown, item: ChartPayloadItem) => ReactNode` | | | Formatea el valor. Sin ella se imprime tal cual: la librería no impone locale. |
1386
+ | `hideLabel` | `boolean` | | `false` | Oculta el encabezado, para una gráfica de una sola categoría. |
1387
+ | `label` | `ReactNode` | | | |
1388
+ | `payload` | `readonly ChartPayloadItem[]` | | | |
1389
+
1390
+ **ChartLegendContent**
1391
+ La leyenda con la misma marca cuadrada del tooltip y la escala `label`.
1392
+
1393
+ - Extiende: `{ payload?: readonly ChartPayloadItem[] \| undefined; className?: string; }`
1394
+
1395
+ | prop | tipo | req. | defecto | qué hace |
1396
+ | --- | --- | --- | --- | --- |
1397
+ | `className` | `string` | | | |
1398
+ | `payload` | `readonly ChartPayloadItem[]` | | | |
1399
+
1155
1400
  ## Exportaciones que no son componentes
1156
1401
 
1157
1402
  La raíz reexporta todo lo de `./tokens` y `./brand` por conveniencia. Cada uno
@@ -1173,15 +1418,16 @@ no monta React, esa subruta es la que hay que importar.
1173
1418
  | `motion` | `{ readonly duration: "150ms"; readonly easing: "ease-out"; readonly properties: "color, background-color, border-color, fill, stroke"; }` | 150ms ease-out — solo color y borde. El sistema no anima posición ni escala: los estados se comunican con borde y color, no con movimiento. |
1174
1419
  | `naming` | `{ readonly wordmark: "Eduardo Álvarez"; readonly mascot: "Tiburoncín"; readonly domain: "eduardoalvarez.dev"; }` | El wordmark siempre dice «Eduardo Álvarez». La mascota se llama Tiburoncín y nunca aparece escrita dentro del logo. |
1175
1420
  | `radius` | `{ readonly chip: 6; readonly control: 10; readonly card: 14; readonly panel: 16; readonly pill: 999; }` | |
1421
+ | `series` | `{ readonly dark: readonly ["#35D6C0", "#F2A65A", "#3E7CB1", "#71919C"]; readonly light: readonly ["#0D7C6F", "#A65B27", "#3E7CB1", "#626A75"]; }` | La paleta de series de las gráficas. CUATRO, por el mismo motivo que la de sintaxis: el sistema se comunica con color y borde, no con ruido cromático. |
1176
1422
  | `shadow` | `{ readonly standard: "0 1px 2px rgba(0, 0, 0, 0.35)"; }` | Un solo nivel. No hay escala de elevación. |
1177
1423
  | `sintaxis` | `{ fondo, identificador, literal, palabraClave, comentario, invalido }` | La paleta del resaltado de sintaxis. |
1178
1424
  | `size` | `{ readonly nav: 64; readonly content: 760; readonly wide: 1180; }` | |
1179
1425
  | `spacing` | `{ readonly stepXs: 8; readonly stepSm: 12; readonly stepMd: 16; readonly stepLg: 26; readonly stepXl: 40; readonly section: 96; }` | El ritmo de página. Los cinco escalones llevan `step` en el nombre, y no es decoración: es la corrección de un bug que no dio la cara en ningún sitio. |
1180
1426
  | `tagline` | `{ largo, corto, en }` | |
1181
- | `tokens` | `{ colors, brand, fonts, typeScale, limits, radius, control, spacing, size, gradient, sintaxis, shadow, motion, tagline, naming }` | Todos los tokens en un solo objeto, para plantillas Satori y generadores. |
1427
+ | `tokens` | `{ colors, brand, fonts, typeScale, limits, radius, control, spacing, size, gradient, sintaxis, series, shadow, motion, tagline, naming }` | Todos los tokens en un solo objeto, para plantillas Satori y generadores. |
1182
1428
  | `typeScale` | `{ display, stat, h1, h2, h3, body, lead, ui, label, tag, chip, meta, eyebrow }` | |
1183
1429
 
1184
- Tipos (12): `BrandToken`, `ColorMode`, `ColorToken`, `ControlToken`, `FontToken`, `GradientToken`, `RadiusToken`, `SintaxisToken`, `SizeToken`, `SpacingToken`, `Tokens`, `TypeScaleToken`.
1430
+ Tipos (13): `BrandToken`, `ColorMode`, `ColorToken`, `ControlToken`, `FontToken`, `GradientToken`, `RadiusToken`, `SeriesToken`, `SintaxisToken`, `SizeToken`, `SpacingToken`, `Tokens`, `TypeScaleToken`.
1185
1431
 
1186
1432
  ### `@eduardoalvarez/arrecife/brand`
1187
1433
 
@@ -1205,6 +1451,38 @@ Tipos (8): `Aleta`, `Cara`, `CaraDeMascotaProps`, `Fondo`, `IsotipoProps`, `Logo
1205
1451
 
1206
1452
  Tipos (1): `TemaShiki`.
1207
1453
 
1454
+ ### `@eduardoalvarez/arrecife/chart`
1455
+
1456
+ | export | tipo | qué es |
1457
+ | --- | --- | --- |
1458
+ | `colorDeSerie` | `(indice: number): string` | El color de la serie `indice`, como custom property. |
1459
+ | `COLORES_DE_SERIE` | `string[]` | Las cuatro, en orden, para pasárselas de golpe a un `Pie` con `Cell`. |
1460
+
1461
+ Tipos (4): `ChartContainerProps`, `ChartLegendContentProps`, `ChartPayloadItem`, `ChartTooltipContentProps`.
1462
+
1463
+ ### `@eduardoalvarez/arrecife/tema`
1464
+
1465
+ | export | tipo | qué es |
1466
+ | --- | --- | --- |
1467
+ | `alternarTema` | `(): Tema` | Cambia al contrario y devuelve el que quedó. |
1468
+ | `aplicarTema` | `(tema: Tema, persistir?: boolean): void` | Pone el tema en el `<html>` y lo persiste. |
1469
+ | `escucharTema` | `(alCambiar: (tema: Tema) => void): () => void` | Se suscribe a los cambios de tema y devuelve la función que cancela. |
1470
+ | `scriptTema` | `string` | El script que va INLINE en el `<head>`, antes de cualquier hoja de estilo. |
1471
+ | `TEMA_ATRIBUTO` | `"data-theme"` | El atributo que leen los bloques `[data-theme]` de `theme.css`. |
1472
+ | `TEMA_CLAVE` | `"arrecife-tema"` | La clave de `localStorage`. |
1473
+ | `TEMA_EVENTO` | `"arrecife:tema"` | El evento que se emite cuando el tema cambia. |
1474
+ | `temaActual` | `(): Tema` | El tema que hay puesto ahora mismo, leído del DOM. |
1475
+ | `temaGuardado` | `(): Tema \| null` | La preferencia guardada, si la hay. `null` significa «nadie ha elegido», que no es lo mismo que «eligió oscuro»: sin elección manda el sistema. |
1476
+ | `temaPreferido` | `(): Tema` | El tema que corresponde: lo elegido, y si no hay elección, lo que pida el sistema. Sin `prefers-color-scheme` declarado, oscuro, que es el primario. |
1477
+
1478
+ Tipos (1): `Tema`.
1479
+
1480
+ ### `@eduardoalvarez/arrecife/form`
1481
+
1482
+ | export | tipo | qué es |
1483
+ | --- | --- | --- |
1484
+ | `useFormField` | `(): { invalid: boolean; isDirty: boolean; isTouched: boolean; isValidating: boolean; error?: FieldError; name: string; id: string; idDescripcion: string; idMensaje: string; }` | Lo que necesita cualquier pieza del campo: el nombre, los tres ids y el estado de validación. |
1485
+
1208
1486
  ### `@eduardoalvarez/arrecife/og`
1209
1487
 
1210
1488
  | export | tipo | qué es |
@@ -1232,8 +1510,10 @@ Tipos (6): `DatosArticulo`, `DatosBase`, `DatosCharla`, `DatosCurso`, `DatosDefe
1232
1510
  | `social` | `typeof import("src/lib/social")` | |
1233
1511
  | `SUPERFICIE_TARJETA` | `"rounded-card border-hairline bg-surface border"` | El contenedor de superficie del sistema, y la única definición de lo que es una tarjeta: `surface`, borde `hairline`, radio de tarjeta. |
1234
1512
  | `textVariants` | `(props?: (ConfigVariants<{ variant: { display: string; stat: string; h1: string; h2: string; h3: string; body: string; lead: string; ui: string; label: string; tag: string; chip: string; meta: string; eyebrow: string; }; tone: { ...; }; }> & ClassProp) \| undefined): string` | |
1513
+ | `toast` | `(mensaje: ReactNode, opciones?: ToastOptions \| undefined): string` | Lanza un aviso. Devuelve su id, que es lo que hay que guardar para cerrarlo a mano —el caso de «guardando…» que se reemplaza cuando termina la petición. |
1514
+ | `useTema` | `(): Tema` | El tema puesto ahora mismo, para un proyecto que necesite ramificar en React —un logo distinto por modo, una imagen que no tiene versión clara—. |
1235
1515
 
1236
- Tipos (51): `AlertProps`, `ArticleCardProps`, `AudioPlayerMode`, `AudioPlayerProps`, `AuthorCardProps`, `AvatarProps`, `BadgeProps`, `BlockquoteProps`, `BreadcrumbProps`, `ButtonProps`, `CalendarProps`, `CategoryBadgeProps`, `CheckboxProps`, `CodeBlockProps`, `CodeProps`, `CourseCardProps`, `DateFieldProps`, `EmptyStateProps`, `Entrada`, `FooterLinkProps`, `FooterProps`, `HeroProps`, `InputProps`, `LabelProps`, `LinkRowProps`, `MetricBadgeProps`, `Migaja`, `NavItemProps`, `NavProps`, `NewsletterFormProps`, `NewsletterState`, `PageHeaderProps`, `PaginationLinkProps`, `PopoverContentProps`, `ProgressProps`, `RadioGroupItemProps`, `RadioGroupProps`, `Red`, `SeparatorProps`, `SheetContentProps`, `SidebarItemProps`, `SidebarNavProps`, `SkeletonProps`, `StatProps`, `SwitchProps`, `TableOfContentsProps`, `TabsProps`, `TalkCardProps`, `TextProps`, `TextareaProps`, `ToastProps`.
1516
+ Tipos (58): `AccordionProps`, `AccordionTriggerProps`, `AlertProps`, `ArticleCardProps`, `AudioPlayerMode`, `AudioPlayerProps`, `AuthorCardProps`, `AvatarProps`, `BadgeProps`, `BlockquoteProps`, `BreadcrumbProps`, `ButtonProps`, `CalendarProps`, `CategoryBadgeProps`, `CheckboxProps`, `CodeBlockProps`, `CodeProps`, `CourseCardProps`, `DateFieldProps`, `EmptyStateProps`, `Entrada`, `FooterLinkProps`, `FooterProps`, `HeroProps`, `InputProps`, `LabelProps`, `LinkRowProps`, `MetricBadgeProps`, `Migaja`, `NavItemProps`, `NavProps`, `NewsletterFormProps`, `NewsletterState`, `PageHeaderProps`, `PaginationLinkProps`, `PopoverContentProps`, `ProgressProps`, `RadioGroupItemProps`, `RadioGroupProps`, `Red`, `ScrollingProgressBarProps`, `SeparatorProps`, `SheetContentProps`, `SidebarItemProps`, `SidebarNavProps`, `SkeletonProps`, `StatProps`, `SwitchProps`, `TableOfContentsProps`, `TabsProps`, `TalkCardProps`, `TextProps`, `TextareaProps`, `ThemeToggleProps`, `ToastOptions`, `ToastProps`, `ToastVariant`, `ToasterProps`.
1237
1517
 
1238
1518
  # Dónde mirar si esto no basta
1239
1519
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eduardoalvarez/arrecife",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Librería de componentes de la identidad visual de Eduardo Álvarez",
5
5
  "license": "MIT",
6
6
  "author": "Eduardo Esteban Álvarez Castañeda <soy@eduardoalvarez.dev>",
@@ -30,6 +30,11 @@
30
30
  "require": "./dist/tokens/index.cjs"
31
31
  },
32
32
  "./tokens/theme.css": "./dist/tokens/theme.css",
33
+ "./tema": {
34
+ "types": "./dist/tema/index.d.ts",
35
+ "import": "./dist/tema/index.js",
36
+ "require": "./dist/tema/index.cjs"
37
+ },
33
38
  "./brand": {
34
39
  "types": "./dist/brand/index.d.ts",
35
40
  "import": "./dist/brand/index.js",
@@ -45,6 +50,16 @@
45
50
  "import": "./dist/shiki/index.js",
46
51
  "require": "./dist/shiki/index.cjs"
47
52
  },
53
+ "./form": {
54
+ "types": "./dist/form/index.d.ts",
55
+ "import": "./dist/form/index.js",
56
+ "require": "./dist/form/index.cjs"
57
+ },
58
+ "./chart": {
59
+ "types": "./dist/chart/index.d.ts",
60
+ "import": "./dist/chart/index.js",
61
+ "require": "./dist/chart/index.cjs"
62
+ },
48
63
  "./llms.txt": "./llms.txt",
49
64
  "./assets/*": "./assets/*",
50
65
  "./package.json": "./package.json"
@@ -74,7 +89,17 @@
74
89
  },
75
90
  "peerDependencies": {
76
91
  "react": "^19.0.0",
77
- "react-dom": "^19.0.0"
92
+ "react-dom": "^19.0.0",
93
+ "react-hook-form": "^7.0.0",
94
+ "recharts": "^3.0.0"
95
+ },
96
+ "peerDependenciesMeta": {
97
+ "react-hook-form": {
98
+ "optional": true
99
+ },
100
+ "recharts": {
101
+ "optional": true
102
+ }
78
103
  },
79
104
  "devDependencies": {
80
105
  "@eslint/js": "^10.0.1",
@@ -94,6 +119,8 @@
94
119
  "playwright": "^1.62.1",
95
120
  "react": "^19.2.0",
96
121
  "react-dom": "^19.2.0",
122
+ "react-hook-form": "^7.87.0",
123
+ "recharts": "^3.10.1",
97
124
  "storybook": "^10.5.10",
98
125
  "storybook-addon-pseudo-states": "^10.5.10",
99
126
  "tailwindcss": "^4.3.3",
@@ -111,6 +138,8 @@
111
138
  "access": "public"
112
139
  },
113
140
  "dependencies": {
141
+ "@radix-ui/react-accordion": "^1.2.20",
142
+ "@radix-ui/react-alert-dialog": "^1.1.23",
114
143
  "@radix-ui/react-avatar": "^1.2.6",
115
144
  "@radix-ui/react-checkbox": "^1.3.11",
116
145
  "@radix-ui/react-dialog": "^1.1.23",