@eduardoalvarez/arrecife 0.2.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/dist/index.d.ts CHANGED
@@ -1,14 +1,18 @@
1
- export { BrandToken, ColorMode, ColorToken, ControlToken, FontToken, GradientToken, RadiusToken, SintaxisToken, SizeToken, SpacingToken, Tokens, TypeScaleToken, brand, colors, control, dark, fonts, gradient, light, limits, motion, naming, radius, shadow, sintaxis, size, spacing, tagline, tokens, typeScale } from './tokens/index.js';
1
+ export { BrandToken, ColorMode, ColorToken, ControlToken, FontToken, GradientToken, RadiusToken, SeriesToken, SintaxisToken, SizeToken, SpacingToken, Tokens, TypeScaleToken, brand, colors, control, dark, fonts, gradient, light, limits, motion, naming, radius, series, shadow, sintaxis, size, spacing, tagline, tokens, typeScale } from './tokens/index.js';
2
+ import { Tema } from './tema/index.js';
3
+ export { TEMA_ATRIBUTO, TEMA_CLAVE, TEMA_EVENTO, alternarTema, aplicarTema, escucharTema, scriptTema, temaActual, temaGuardado, temaPreferido } from './tema/index.js';
2
4
  import * as react from 'react';
3
- import { ComponentPropsWithoutRef, ReactNode, ComponentProps, SVGProps } from 'react';
5
+ import { ComponentPropsWithoutRef, ReactNode, ComponentProps, RefObject, SVGProps } from 'react';
6
+ import * as AccordionPrimitive from '@radix-ui/react-accordion';
4
7
  import * as class_variance_authority_types from 'class-variance-authority/types';
5
8
  import { VariantProps } from 'class-variance-authority';
9
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
6
10
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
7
11
  import { DayPicker } from 'react-day-picker';
8
12
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
9
13
  import * as DialogPrimitive from '@radix-ui/react-dialog';
10
14
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
11
- import * as LabelPrimitive from '@radix-ui/react-label';
15
+ export { L as Label, a as LabelProps } from './label-DuTvJGxD.js';
12
16
  import * as PopoverPrimitive from '@radix-ui/react-popover';
13
17
  import * as ProgressPrimitive from '@radix-ui/react-progress';
14
18
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
@@ -22,6 +26,45 @@ import { C as Cara, P as Pose } from './catalogo-Du5ID-Hi.js';
22
26
  export { A as Aleta, F as Fondo, R as RUTA_ASSETS, a as aletas, c as caras, l as listaCaras, b as listaPoses, p as poses, u as usoDeCara } from './catalogo-Du5ID-Hi.js';
23
27
  export { CaraDeMascota, CaraDeMascotaProps, Isotipo, IsotipoProps, Logo, LogoProps, Mascota, MascotaProps } from './brand/index.js';
24
28
  import { ClassValue } from 'clsx';
29
+ import '@radix-ui/react-label';
30
+
31
+ /**
32
+ * El plegable. Lo pedían dos proyectos: el FAQ del portafolio y el temario de
33
+ * cursos, que es literalmente una lista de secciones que se abren.
34
+ *
35
+ * NO se anima la altura, que es lo primero que hace todo el mundo con este
36
+ * componente. Radix publica `--radix-accordion-content-height` justo para eso y
37
+ * aquí no se usa: el sistema no anima posición ni tamaño, y un panel que crece
38
+ * a 200ms es exactamente el desplazamiento que las tarjetas, los menús y los
39
+ * modales ya evitan. El panel aparece donde va a quedarse.
40
+ *
41
+ * Lo único que se mueve es el galón, y se mueve porque `rotate` bajo
42
+ * `transition-standard` NO transiciona: la utilidad solo cubre color y borde,
43
+ * así que el giro es instantáneo aunque la clase esté puesta. Es el mismo trato
44
+ * que recibe el ancho de `Progress`.
45
+ *
46
+ * La división entre items es `hairline`, no `border`: es una separación de
47
+ * lectura, no el borde de un control.
48
+ */
49
+ type AccordionProps = ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>;
50
+ declare function Accordion({ className, ...props }: AccordionProps): react.JSX.Element;
51
+ declare function AccordionItem({ className, ...props }: ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>): react.JSX.Element;
52
+ /**
53
+ * El disparador es el encabezado, así que va DENTRO de un `<h3>`: Radix envuelve
54
+ * el botón en `AccordionPrimitive.Header`, que renderiza el elemento que se le
55
+ * pida. Sin eso, un lector de pantalla ve una lista de botones sueltos y pierde
56
+ * la estructura de la página, que es justo lo que un FAQ necesita conservar.
57
+ *
58
+ * `headingLevel` existe porque el nivel correcto depende de dónde se monte: en
59
+ * una página de FAQ el bloque cuelga de un `<h2>` de sección, y en un temario
60
+ * puede colgar de un `<h3>`. Fijarlo aquí sería adivinar.
61
+ */
62
+ type AccordionTriggerProps = ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> & {
63
+ /** Nivel del encabezado que envuelve al disparador. */
64
+ headingLevel?: 2 | 3 | 4;
65
+ };
66
+ declare function AccordionTrigger({ className, children, headingLevel, ...props }: AccordionTriggerProps): react.JSX.Element;
67
+ declare function AccordionContent({ className, children, ...props }: ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>): react.JSX.Element;
25
68
 
26
69
  /**
27
70
  * El aviso lleva el color en el fondo, no solo en el borde.
@@ -70,7 +113,7 @@ import { ClassValue } from 'clsx';
70
113
  * `docs/decisiones.md`.
71
114
  */
72
115
  declare const alert: (props?: ({
73
- variant?: "accent" | "success" | "warning" | "error" | null | undefined;
116
+ variant?: "error" | "accent" | "success" | "warning" | null | undefined;
74
117
  enfasis?: "sutil" | "fuerte" | null | undefined;
75
118
  } & class_variance_authority_types.ClassProp) | undefined) => string;
76
119
  type AlertProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & VariantProps<typeof alert> & {
@@ -83,6 +126,50 @@ type AlertProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & VariantProps<
83
126
  };
84
127
  declare function Alert({ className, variant, enfasis, title, icon, children, ...props }: AlertProps): react.JSX.Element;
85
128
 
129
+ /**
130
+ * La confirmación destructiva. NO es un `Dialog` con otro texto, y por eso está
131
+ * en su propio archivo y sobre su propia primitiva de Radix.
132
+ *
133
+ * Tres diferencias, y las tres importan en el momento en que alguien va a
134
+ * borrar un artículo:
135
+ *
136
+ * 1. El rol es `alertdialog`, no `dialog`. Un lector de pantalla lo anuncia
137
+ * con la descripción incluida, sin esperar a que se navegue hasta ella.
138
+ * 2. El foco inicial va al CANCELAR, no al primer elemento. Quien pulsa Enter
139
+ * por inercia no borra nada. Radix lo hace solo si el cancelar existe, y
140
+ * por eso `AlertDialogCancel` no es opcional en la práctica.
141
+ * 3. NO se cierra al pulsar fuera ni tiene aspa. Salir de una confirmación es
142
+ * una decisión, no un descuido: hay que decir que no.
143
+ *
144
+ * El botón de confirmar NO es rojo. El sistema no tiene variante de peligro
145
+ * —`Button` lo dice explícito— y el error vive en los avisos y en la validación
146
+ * de campo, no en un botón. Lo que comunica la gravedad es el texto: «Borrar el
147
+ * artículo», no «Aceptar».
148
+ */
149
+ declare const AlertDialog: react.FC<AlertDialogPrimitive.AlertDialogProps>;
150
+ declare const AlertDialogTrigger: react.ForwardRefExoticComponent<AlertDialogPrimitive.AlertDialogTriggerProps & react.RefAttributes<HTMLButtonElement>>;
151
+ declare function AlertDialogOverlay({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>): react.JSX.Element;
152
+ /** Sin entrada animada, igual que `Dialog`: aparece donde va a quedarse. */
153
+ declare function AlertDialogContent({ className, children, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>): react.JSX.Element;
154
+ declare function AlertDialogHeader({ className, ...props }: ComponentPropsWithoutRef<'div'>): react.JSX.Element;
155
+ /**
156
+ * Cancelar a la IZQUIERDA de confirmar en escritorio y ABAJO en móvil, que es lo
157
+ * que da `flex-col-reverse`: el orden del DOM pone cancelar primero —es donde va
158
+ * el foco— y en columna el dedo lo encuentra donde toca sin cambiar la
159
+ * tabulación.
160
+ */
161
+ declare function AlertDialogFooter({ className, ...props }: ComponentPropsWithoutRef<'div'>): react.JSX.Element;
162
+ declare function AlertDialogTitle({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>): react.JSX.Element;
163
+ /**
164
+ * Lo que se pierde, dicho entero. Es lo que el rol `alertdialog` hace que se
165
+ * anuncie de entrada, así que aquí no va «esta acción no se puede deshacer»
166
+ * suelto: va qué se borra y qué se lleva por delante.
167
+ */
168
+ declare function AlertDialogDescription({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>): react.JSX.Element;
169
+ /** El que se lleva el foco al abrir. */
170
+ declare function AlertDialogCancel({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>): react.JSX.Element;
171
+ declare function AlertDialogAction({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>): react.JSX.Element;
172
+
86
173
  declare const avatar: (props?: ({
87
174
  size?: "sm" | "md" | "lg" | "xl" | null | undefined;
88
175
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -130,7 +217,7 @@ declare function AvatarFallback({ className, ...props }: ComponentPropsWithoutRe
130
217
  * «Publicado» o «Borrador» con todas sus letras.
131
218
  */
132
219
  declare const badge: (props?: ({
133
- variant?: "accent" | "success" | "warning" | "error" | "neutral" | "warm" | null | undefined;
220
+ variant?: "error" | "accent" | "success" | "warning" | "neutral" | "warm" | null | undefined;
134
221
  } & class_variance_authority_types.ClassProp) | undefined) => string;
135
222
  type BadgeProps = ComponentPropsWithoutRef<'span'> & VariantProps<typeof badge>;
136
223
  declare function Badge({ className, variant, ...props }: BadgeProps): react.JSX.Element;
@@ -329,15 +416,6 @@ type InputProps = ComponentPropsWithoutRef<'input'> & {
329
416
  };
330
417
  declare function Input({ className, invalid, ...props }: InputProps): react.JSX.Element;
331
418
 
332
- type LabelProps = ComponentPropsWithoutRef<typeof LabelPrimitive.Root>;
333
- /**
334
- * La escala `label`: 13px, que es el mínimo absoluto en pantalla del sistema.
335
- *
336
- * Se atenúa junto al control cuando este está deshabilitado, para que la
337
- * pareja etiqueta-control se lea siempre como una sola unidad.
338
- */
339
- declare function Label({ className, ...props }: LabelProps): react.JSX.Element;
340
-
341
419
  declare function Pagination({ className, ...props }: ComponentPropsWithoutRef<'nav'>): react.JSX.Element;
342
420
  declare function PaginationContent({ className, ...props }: ComponentPropsWithoutRef<'ul'>): react.JSX.Element;
343
421
  declare function PaginationItem(props: ComponentPropsWithoutRef<'li'>): react.JSX.Element;
@@ -485,23 +563,85 @@ declare function Textarea({ className, invalid, ...props }: TextareaProps): reac
485
563
  declare const ToastProvider: react.FC<ToastPrimitive.ToastProviderProps>;
486
564
  declare const ToastAction: react.ForwardRefExoticComponent<ToastPrimitive.ToastActionProps & react.RefAttributes<HTMLButtonElement>>;
487
565
  declare function ToastViewport({ className, ...props }: ComponentPropsWithoutRef<typeof ToastPrimitive.Viewport>): react.JSX.Element;
488
- declare const toast: (props?: ({
489
- variant?: "success" | "error" | "neutral" | null | undefined;
566
+ declare const toast$1: (props?: ({
567
+ variant?: "error" | "success" | "neutral" | null | undefined;
490
568
  } & class_variance_authority_types.ClassProp) | undefined) => string;
491
- type ToastProps = ComponentPropsWithoutRef<typeof ToastPrimitive.Root> & VariantProps<typeof toast>;
569
+ type ToastProps = ComponentPropsWithoutRef<typeof ToastPrimitive.Root> & VariantProps<typeof toast$1>;
492
570
  /** Sin deslizamiento de entrada: el aviso aparece donde va a quedarse. */
493
571
  declare function Toast({ className, variant, children, ...props }: ToastProps): react.JSX.Element;
494
572
  declare function ToastTitle({ className, ...props }: ComponentPropsWithoutRef<typeof ToastPrimitive.Title>): react.JSX.Element;
495
573
  declare function ToastDescription({ className, ...props }: ComponentPropsWithoutRef<typeof ToastPrimitive.Description>): react.JSX.Element;
496
574
 
575
+ /**
576
+ * La cara imperativa del `Toast` de Radix: `toast('Guardado')` desde cualquier
577
+ * sitio, sin pasar el aviso por props hasta el componente que lo dispara.
578
+ *
579
+ * Existe porque dos proyectos traían `sonner` para esto. `Toast` cubre el mismo
580
+ * rol y tiene otra forma: Radix es declarativo con proveedor, y para mostrar un
581
+ * aviso desde el `catch` de un `fetch` hay que subir estado hasta donde vive el
582
+ * proveedor. Eso es exactamente lo que `sonner` evita, y es una necesidad real,
583
+ * no una preferencia de API.
584
+ *
585
+ * La alternativa era que los dos proyectos se adaptaran. Se descartó: el aviso
586
+ * lo dispara la capa de datos, que no tiene —ni debería tener— un componente
587
+ * cerca al que subirle un `useState`.
588
+ *
589
+ * Lo que NO se copió de `sonner` es el catálogo entero. No hay `toast.promise`,
590
+ * ni `toast.custom`, ni posiciones configurables, ni apilado con perspectiva:
591
+ * son cuatro variantes de lo mismo y cada una es superficie pública que hay que
592
+ * mantener. Están las tres formas que los proyectos usan de verdad —neutral,
593
+ * éxito, error—, `dismiss` y nada más.
594
+ *
595
+ * El estado vive en un módulo, no en un contexto, porque el punto es que se
596
+ * pueda llamar desde fuera del árbol. `Toaster` se suscribe con
597
+ * `useSyncExternalStore`, que es la forma que React 19 tiene de leer un estado
598
+ * externo sin efectos ni renders en cascada.
599
+ */
600
+ type ToastVariant = NonNullable<ToastProps['variant']>;
601
+ type ToastOptions = {
602
+ /** La primera línea, en negrita. Sin ella el aviso es una sola frase. */
603
+ title?: ReactNode;
604
+ description?: ReactNode;
605
+ variant?: ToastVariant;
606
+ /** Milisegundos en pantalla. `Infinity` lo deja hasta que se cierre a mano. */
607
+ duration?: number;
608
+ /** Un `ToastAction`, si el aviso ofrece deshacer. */
609
+ action?: ReactNode;
610
+ };
611
+ type Lanzador = {
612
+ (mensaje: ReactNode, opciones?: ToastOptions): string;
613
+ success: (mensaje: ReactNode, opciones?: ToastOptions) => string;
614
+ error: (mensaje: ReactNode, opciones?: ToastOptions) => string;
615
+ dismiss: (id?: string) => void;
616
+ };
617
+ /**
618
+ * Lanza un aviso. Devuelve su id, que es lo que hay que guardar para cerrarlo a
619
+ * mano —el caso de «guardando…» que se reemplaza cuando termina la petición.
620
+ */
621
+ declare const toast: Lanzador;
622
+ type ToasterProps = {
623
+ /** Cuánto dura un aviso que no dice lo contrario. */
624
+ duration?: number;
625
+ /**
626
+ * Nombre del landmark que Radix crea para la región de avisos. Se traduce
627
+ * porque lo lee un lector de pantalla, y el default de Radix está en inglés.
628
+ */
629
+ label?: string;
630
+ };
631
+ /**
632
+ * Va UNA vez, lo más arriba posible del árbol. Dos `Toaster` montados pintan
633
+ * cada aviso dos veces: la lista es del módulo, no de la instancia.
634
+ */
635
+ declare function Toaster({ duration, label }: ToasterProps): react.JSX.Element;
636
+
497
637
  declare const TooltipProvider: react.FC<TooltipPrimitive.TooltipProviderProps>;
498
638
  declare const Tooltip: react.FC<TooltipPrimitive.TooltipProps>;
499
639
  declare const TooltipTrigger: react.ForwardRefExoticComponent<TooltipPrimitive.TooltipTriggerProps & react.RefAttributes<HTMLButtonElement>>;
500
640
  declare function TooltipContent({ className, sideOffset, ...props }: ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>): react.JSX.Element;
501
641
 
502
642
  declare const texto: (props?: ({
503
- variant?: "display" | "body" | "h1" | "h2" | "h3" | "label" | "meta" | "stat" | "lead" | "ui" | "tag" | "chip" | "eyebrow" | null | undefined;
504
- tone?: "accent" | "success" | "warning" | "error" | "warm" | "primary" | "secondary" | "muted" | null | undefined;
643
+ variant?: "display" | "h2" | "h3" | "label" | "body" | "h1" | "meta" | "stat" | "lead" | "ui" | "tag" | "chip" | "eyebrow" | null | undefined;
644
+ tone?: "error" | "accent" | "success" | "warning" | "primary" | "secondary" | "warm" | "muted" | null | undefined;
505
645
  } & class_variance_authority_types.ClassProp) | undefined) => string;
506
646
  /** Etiquetas admitidas. La lista es corta a propósito: no es un `div` con estilo. */
507
647
  type Etiqueta = 'h1' | 'h2' | 'h3' | 'h4' | 'p' | 'span' | 'strong' | 'em' | 'figcaption' | 'caption' | 'legend' | 'dt' | 'dd' | 'li';
@@ -537,6 +677,16 @@ type ArticleCardProps = Omit<TarjetaProps, 'children' | 'title'> & {
537
677
  dateTime?: string | undefined;
538
678
  readingMinutes?: number | undefined;
539
679
  tags?: readonly string[] | undefined;
680
+ /**
681
+ * Nivel del titular. `h3` por defecto: una tarjeta suelta en una rejilla no
682
+ * gana el nivel que su posición no le da.
683
+ *
684
+ * Acotado a dos valores a propósito. La página de listado —donde las tarjetas
685
+ * SÍ son el encabezado principal de la sección— necesita `h2`, y esa
686
+ * intención se perdía con la constante; abrirlo hasta `h4` o `h5`, en cambio,
687
+ * es invitar a saltarse niveles, que es el fallo que la constante evitaba.
688
+ */
689
+ headingLevel?: 2 | 3;
540
690
  };
541
691
  /**
542
692
  * La línea de metadatos va en `meta` y no en `eyebrow`: `18 ago 2026 · 8 min de
@@ -546,7 +696,7 @@ type ArticleCardProps = Omit<TarjetaProps, 'children' | 'title'> & {
546
696
  * Los tags son la familia CATEGORÍA — píldora de arena en minúscula —, no la de
547
697
  * estado. Un slug es lo que se lee `engineering-culture`.
548
698
  */
549
- declare function ArticleCard({ title, excerpt, date, dateTime, readingMinutes, tags, className, ...props }: ArticleCardProps): react.JSX.Element;
699
+ declare function ArticleCard({ title, excerpt, date, dateTime, readingMinutes, tags, headingLevel, className, ...props }: ArticleCardProps): react.JSX.Element;
550
700
 
551
701
  /**
552
702
  * La firma al pie del artículo: avatar 52px, nombre 15/500 y el rol en mono
@@ -747,8 +897,16 @@ type FooterProps = ComponentPropsWithoutRef<'footer'> & {
747
897
  year?: number;
748
898
  /** Enlaces de texto: aviso legal, RSS, mapa del sitio. */
749
899
  children?: ReactNode;
900
+ /**
901
+ * La fila de marca: la aleta y el wordmark, arriba del todo.
902
+ *
903
+ * Existe porque sin ella acababa metida en `children` con un `w-full` para
904
+ * que se llevara su propia línea. Funcionaba y era un apaño: la marca no es
905
+ * un enlace de texto más, y una ranura propia lo dice en el tipo.
906
+ */
907
+ brand?: ReactNode;
750
908
  };
751
- declare function Footer({ social, year, children, className, ...props }: FooterProps): react.JSX.Element;
909
+ declare function Footer({ social, year, children, brand, className, ...props }: FooterProps): react.JSX.Element;
752
910
  type FooterLinkProps = ComponentPropsWithoutRef<'a'> & {
753
911
  asChild?: boolean | undefined;
754
912
  };
@@ -759,13 +917,22 @@ declare function FooterLink({ asChild, className, ...props }: FooterLinkProps):
759
917
  * conversión, y por la misma razón: si hay dos, no hay ninguno.
760
918
  *
761
919
  * Degradado, radio de panel, texto al 62 % del ancho y la pose sangrando por el
762
- * borde inferior derecho. NUNCA centrada: una mascota centrada bajo un titular
763
- * es una ilustración de portada, y esto es una cabecera.
920
+ * borde inferior derecho. Eso es la variante `cabecera`, que es el defecto.
764
921
  *
765
922
  * En móvil no hay borde por el que sangrar, así que la pose baja al flujo, bajo
766
923
  * los botones. No es un `hidden` en pantalla pequeña: la pose es el 40 % de la
767
924
  * personalidad del hero.
768
925
  *
926
+ * La otra variante es `centrado`, y existe porque la regla de arriba tiene un
927
+ * caso donde no aplica. «Nunca centrada» se escribió contra el hero de una
928
+ * página con más contenido debajo: ahí una mascota centrada bajo el titular es
929
+ * una ilustración de portada, no una cabecera. Pero una página de enlaces es
930
+ * centrada de extremo a extremo y la mascota es el protagonista, no el remate.
931
+ * Ese proyecto se saltaba `Hero` entero por esto, que es peor: una regla
932
+ * declarada y con nombre se discute; una copia del degradado en otro repo se
933
+ * desincroniza. La pose va ARRIBA del titular, no debajo, para que siga sin
934
+ * leerse como la ilustración que cierra un bloque de texto.
935
+ *
769
936
  * El degradado viene de `--gradient-hero`, así que sigue el modo. No hay ángulo
770
937
  * escrito a mano en ningún proyecto.
771
938
  */
@@ -779,8 +946,13 @@ type HeroProps = Omit<ComponentPropsWithoutRef<'section'>, 'title'> & {
779
946
  /** La pose de Tiburoncín. Sin ella el hero es un panel con texto. */
780
947
  pose?: Pose | undefined;
781
948
  basePath?: string | undefined;
949
+ /**
950
+ * `cabecera` sangra la pose por la esquina; `centrado` la pone arriba y
951
+ * centra el texto, para una página que es solo esto.
952
+ */
953
+ variant?: 'cabecera' | 'centrado';
782
954
  };
783
- declare function Hero({ title, eyebrow, description, action, pose, basePath, className, ...props }: HeroProps): react.JSX.Element;
955
+ declare function Hero({ title, eyebrow, description, action, pose, basePath, variant, className, ...props }: HeroProps): react.JSX.Element;
784
956
 
785
957
  type LinkRowProps = Omit<TarjetaProps, 'children'> & {
786
958
  name: ReactNode;
@@ -851,14 +1023,30 @@ declare function NavItem({ active, asChild, className, children, ...props }: Nav
851
1023
  * receta sutil, las dos líneas se leen como una sola caja.
852
1024
  *
853
1025
  * Es uno de los sitios donde la mascota puede aparecer: el «sin spam».
1026
+ *
1027
+ * El campo de nombre es OPCIONAL y está apagado por defecto. No es una prop de
1028
+ * estilo: el endpoint de uno de los proyectos valida nombre y correo y responde
1029
+ * 400 si falta el primero, así que un formulario de un solo campo ahí no es un
1030
+ * formulario más pobre — es uno que envía algo que el servidor rechaza. El
1031
+ * nombre viaja como SEGUNDO argumento de `onSubmitEmail`, para que las llamadas
1032
+ * que ya existen —las que solo declaran `(email)`— sigan compilando.
1033
+ *
1034
+ * Lo que la librería NO hace es validar el nombre. El proyecto que lo pide lo
1035
+ * acota entre 2 y 50 caracteres y solo letras y acentos; esa regla es suya y del
1036
+ * servidor que la comprueba de verdad, y copiarla aquí sería tener dos fuentes
1037
+ * que se desincronizan en silencio. `nameInputProps` está para que el proyecto
1038
+ * ponga la suya.
854
1039
  */
855
1040
  type NewsletterState = 'reposo' | 'enviando' | 'exito' | 'error';
856
1041
  type NewsletterFormProps = Omit<ComponentPropsWithoutRef<'section'>, 'title' | 'onSubmit'> & {
857
1042
  title: ReactNode;
858
1043
  description?: ReactNode;
859
1044
  state?: NewsletterState;
860
- /** Se dispara con el correo ya leído del campo. */
861
- onSubmitEmail?: ((email: string) => void) | undefined;
1045
+ /**
1046
+ * Se dispara con el correo ya leído del campo, y con el nombre si el campo
1047
+ * está puesto.
1048
+ */
1049
+ onSubmitEmail?: ((email: string, name?: string) => void) | undefined;
862
1050
  successMessage?: ReactNode;
863
1051
  errorMessage?: ReactNode;
864
1052
  /** La letra pequeña. Es el «sin spam», y por eso admite cara. */
@@ -868,8 +1056,17 @@ type NewsletterFormProps = Omit<ComponentPropsWithoutRef<'section'>, 'title' | '
868
1056
  submitLabel?: string;
869
1057
  placeholder?: string;
870
1058
  fieldLabel?: string;
1059
+ /** Añade el campo de nombre delante del correo. */
1060
+ nameField?: boolean;
1061
+ nameLabel?: string;
1062
+ namePlaceholder?: string;
1063
+ /**
1064
+ * Lo que el proyecto necesite colgar del campo de nombre: `minLength`,
1065
+ * `maxLength`, `pattern`. La librería no impone ninguna de las tres.
1066
+ */
1067
+ nameInputProps?: Omit<InputProps, 'id' | 'name' | 'disabled'> | undefined;
871
1068
  };
872
- declare function NewsletterForm({ title, description, state, onSubmitEmail, successMessage, errorMessage, disclaimer, expresion, basePath, submitLabel, placeholder, fieldLabel, className, ...props }: NewsletterFormProps): react.JSX.Element;
1069
+ declare function NewsletterForm({ title, description, state, onSubmitEmail, successMessage, errorMessage, disclaimer, expresion, basePath, submitLabel, placeholder, fieldLabel, nameField, nameLabel, namePlaceholder, nameInputProps, className, ...props }: NewsletterFormProps): react.JSX.Element;
873
1070
 
874
1071
  /**
875
1072
  * Una sola cabecera en dos escalas, no dos componentes.
@@ -908,6 +1105,43 @@ type PageHeaderProps = Omit<ComponentPropsWithoutRef<'header'>, 'title'> & Varia
908
1105
  };
909
1106
  declare function PageHeader({ title, eyebrow, description, action, size, as, className, ...props }: PageHeaderProps): react.JSX.Element;
910
1107
 
1108
+ /**
1109
+ * Cuánto llevas leído. NO es `Progress` con otro nombre.
1110
+ *
1111
+ * `Progress` mide una tarea: hay un total conocido, alguien la empezó y va a
1112
+ * terminar. Esto mide una POSICIÓN en un documento, que se puede recorrer en
1113
+ * los dos sentidos y de la que no hay nada que completar. Por eso no lleva
1114
+ * `role="progressbar"` ni valor accesible: va `aria-hidden`.
1115
+ *
1116
+ * Eso último es deliberado y es la decisión que hay que defender. Un lector de
1117
+ * pantalla ya sabe dónde está en el documento y anunciarle «37 %» cada vez que
1118
+ * se mueve es ruido, no información. La barra es orientación visual, y lo que
1119
+ * es solo visual se declara como tal.
1120
+ *
1121
+ * El ancho se escribe directamente, sin transición: `transition-standard` solo
1122
+ * cubre color y borde, así que la barra sigue al scroll en vez de perseguirlo.
1123
+ *
1124
+ * La medición va dentro de `requestAnimationFrame`. Leer `scrollTop` en el
1125
+ * manejador de scroll fuerza un reflujo síncrono en cada evento, y en un
1126
+ * artículo largo eso se nota en el propio scroll — el efecto contrario al que
1127
+ * busca la pieza.
1128
+ */
1129
+ type ScrollingProgressBarProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {
1130
+ /**
1131
+ * El elemento que se mide. Sin él, el documento entero.
1132
+ *
1133
+ * Se pasa cuando la barra debe seguir SOLO al artículo: si la página tiene
1134
+ * una cabecera alta y un pie con enlaces, medir el documento marca el 100 %
1135
+ * cuando todavía quedan dos párrafos.
1136
+ */
1137
+ target?: RefObject<HTMLElement | null> | undefined;
1138
+ /** Arena en vez de bioluz, para igualar el progreso de curso. */
1139
+ tone?: 'accent' | 'warm';
1140
+ /** Pega la barra al borde superior de la ventana. */
1141
+ sticky?: boolean;
1142
+ };
1143
+ declare function ScrollingProgressBar({ target, tone, sticky, className, ...props }: ScrollingProgressBarProps): react.JSX.Element;
1144
+
911
1145
  /**
912
1146
  * La barra lateral del admin del blog.
913
1147
  *
@@ -964,8 +1198,63 @@ type TalkCardProps = Omit<TarjetaProps, 'children' | 'title'> & {
964
1198
  location?: ReactNode;
965
1199
  /** Etiqueta corta de estado: «con vídeo», «próxima», «solo audio». */
966
1200
  status?: ReactNode;
1201
+ /**
1202
+ * De qué iba la charla. Se corta a dos líneas, igual que el `excerpt` de
1203
+ * `ArticleCard`, para que la rejilla no se desalinee.
1204
+ */
1205
+ description?: ReactNode;
967
1206
  };
968
- declare function TalkCard({ title, event, date, dateTime, location, status, className, ...props }: TalkCardProps): react.JSX.Element;
1207
+ declare function TalkCard({ title, event, date, dateTime, location, status, description, className, ...props }: TalkCardProps): react.JSX.Element;
1208
+
1209
+ /**
1210
+ * El control que faltaba. La librería definía todo el sistema de temas y no
1211
+ * exponía lo que lo cambia, así que dos proyectos lo reimplementaban.
1212
+ *
1213
+ * Lo difícil no es el botón: es que la primera pintura no parpadee y que la
1214
+ * elección sobreviva a la navegación. Eso vive en `@eduardoalvarez/arrecife/tema`,
1215
+ * que no importa React —lo consume un Astro que no monta ninguno— y de ahí sale
1216
+ * `scriptTema`, que va inline en el `<head>`. Sin ese script, este botón
1217
+ * funciona y aun así se ve el fogonazo en cada carga.
1218
+ *
1219
+ * Los DOS iconos se renderizan siempre y el que sobra lo esconde el CSS con la
1220
+ * variante `light:`. No es una optimización: es lo que evita que el servidor y
1221
+ * el cliente discrepen. El servidor no sabe qué tema eligió quien va a leer, así
1222
+ * que cualquier icono que elija en el HTML tiene la mitad de probabilidades de
1223
+ * ser el equivocado, y corregirlo al hidratar es el parpadeo otra vez.
1224
+ *
1225
+ * El nombre accesible NO dice a qué modo se va. Sería más informativo y sería
1226
+ * una mentira la mitad del tiempo por lo mismo de arriba: el HTML del servidor
1227
+ * lo fija antes de saber el tema. «Cambiar de tema» es cierto siempre.
1228
+ */
1229
+ type ThemeToggleProps = Omit<ComponentPropsWithoutRef<'button'>, 'onClick'> & {
1230
+ /** Nombre accesible. El botón no tiene texto visible, así que es lo único que lo nombra. */
1231
+ label?: string;
1232
+ /** Se dispara con el tema que quedó puesto, por si el proyecto quiere anotarlo. */
1233
+ onThemeChange?: ((tema: Tema) => void) | undefined;
1234
+ variant?: ButtonProps['variant'];
1235
+ size?: ButtonProps['size'];
1236
+ };
1237
+ declare function ThemeToggle({ label, onThemeChange, variant, size, className, ...props }: ThemeToggleProps): react.JSX.Element;
1238
+ /**
1239
+ * El tema puesto ahora mismo, para un proyecto que necesite ramificar en React
1240
+ * —un logo distinto por modo, una imagen que no tiene versión clara—.
1241
+ *
1242
+ * Es `useSyncExternalStore` y no un `useState` con un efecto detrás porque el
1243
+ * tema es exactamente eso: un estado que vive fuera de React, en un atributo del
1244
+ * `<html>` que puede cambiar sin que React se entere. Escribirlo con un efecto
1245
+ * que llama a `setState` en el montaje es el patrón que dispara un render en
1246
+ * cascada y que la regla `set-state-in-effect` señala con razón.
1247
+ *
1248
+ * `getServerSnapshot` devuelve `'dark'` porque en el servidor no hay `document`.
1249
+ * El primer render del cliente coincide con el del servidor y el valor real
1250
+ * entra después, que es la misma discrepancia de hidratación que `ThemeToggle`
1251
+ * evita renderizando los dos iconos.
1252
+ *
1253
+ * De ahí la regla de uso: si lo que ramifica es SOLO estilo, esto no hace falta
1254
+ * y la variante `light:` es mejor — no re-renderiza nada. Esto es para cuando
1255
+ * cambia el contenido.
1256
+ */
1257
+ declare function useTema(): Tema;
969
1258
 
970
1259
  /**
971
1260
  * «En esta página». El índice del artículo largo.
@@ -976,6 +1265,20 @@ declare function TalkCard({ title, event, date, dateTime, location, status, clas
976
1265
  *
977
1266
  * El activo se marca con `aria-current`, no solo con color — la sección en la
978
1267
  * que estás no puede comunicarse únicamente con bioluz.
1268
+ *
1269
+ * Y ese atributo es además EL GANCHO: las clases del activo se aplican con la
1270
+ * variante `aria-[current]:`, no con un ternario en el render. La diferencia es
1271
+ * la que hay entre servir solo controlado y servir también sin controlar.
1272
+ *
1273
+ * Un sitio Astro resuelve el scroll-spy con quince líneas de script que ponen
1274
+ * `aria-current` en el enlace visible y quitan el del anterior. Con el estado
1275
+ * calculado en el render, ese script no podía hacer nada: había que hidratar el
1276
+ * índice como isla de React en cada artículo para algo que cuesta cero
1277
+ * JavaScript de framework. Ahora el CSS reacciona al atributo y las dos formas
1278
+ * de usarlo dan el mismo resultado.
1279
+ *
1280
+ * El gancho es la PRESENCIA del atributo, así que se quita para desmarcar; no
1281
+ * se pone `aria-current="false"`.
979
1282
  */
980
1283
  type Entrada = {
981
1284
  /** El ancla, con `#`. */
@@ -1021,4 +1324,4 @@ declare namespace social {
1021
1324
  export { social_Correo as Correo, social_Discord as Discord, social_GitHub as GitHub, social_Instagram as Instagram, social_LinkedIn as LinkedIn, social_Rss as Rss, social_X as X, social_YouTube as YouTube };
1022
1325
  }
1023
1326
 
1024
- export { Alert, type AlertProps, ArticleCard, type ArticleCardProps, AudioPlayer, type AudioPlayerMode, type AudioPlayerProps, AuthorCard, type AuthorCardProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Blockquote, type BlockquoteProps, Breadcrumb, type BreadcrumbProps, Button, type ButtonProps, Calendar, type CalendarProps, Cara, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryBadge, type CategoryBadgeProps, Checkbox, type CheckboxProps, Code, CodeBlock, type CodeBlockProps, type CodeProps, CourseCard, type CourseCardProps, DateField, type DateFieldProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, type Entrada, Footer, FooterLink, type FooterLinkProps, type FooterProps, HOVER_TARJETA, Hero, type HeroProps, Input, type InputProps, Label, type LabelProps, LinkRow, type LinkRowProps, MetricBadge, type MetricBadgeProps, type Migaja, Nav, NavItem, type NavItemProps, type NavProps, NewsletterForm, type NewsletterFormProps, type NewsletterState, PageHeader, type PageHeaderProps, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, type PaginationLinkProps, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, Pose, Progress, type ProgressProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, type Red, SUPERFICIE_TARJETA, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, Sheet, SheetBody, SheetClose, SheetContent, type SheetContentProps, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SidebarItem, type SidebarItemProps, SidebarNav, type SidebarNavProps, Skeleton, type SkeletonProps, Stat, type StatProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableOfContents, type TableOfContentsProps, TableRow, Tabs, TabsContent, TabsList, type TabsProps, TabsTrigger, TalkCard, type TalkCardProps, Text, type TextProps, Textarea, type TextareaProps, Toast, ToastAction, ToastDescription, type ToastProps, ToastProvider, ToastTitle, ToastViewport, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alert as alertVariants, avatar as avatarVariants, badge as badgeVariants, button as buttonVariants, categoria as categoryBadgeVariants, cn, social, texto as textVariants };
1327
+ export { Accordion, AccordionContent, AccordionItem, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, Alert, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogTitle, AlertDialogTrigger, type AlertProps, ArticleCard, type ArticleCardProps, AudioPlayer, type AudioPlayerMode, type AudioPlayerProps, AuthorCard, type AuthorCardProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Blockquote, type BlockquoteProps, Breadcrumb, type BreadcrumbProps, Button, type ButtonProps, Calendar, type CalendarProps, Cara, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryBadge, type CategoryBadgeProps, Checkbox, type CheckboxProps, Code, CodeBlock, type CodeBlockProps, type CodeProps, CourseCard, type CourseCardProps, DateField, type DateFieldProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, type Entrada, Footer, FooterLink, type FooterLinkProps, type FooterProps, HOVER_TARJETA, Hero, type HeroProps, Input, type InputProps, LinkRow, type LinkRowProps, MetricBadge, type MetricBadgeProps, type Migaja, Nav, NavItem, type NavItemProps, type NavProps, NewsletterForm, type NewsletterFormProps, type NewsletterState, PageHeader, type PageHeaderProps, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, type PaginationLinkProps, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, Pose, Progress, type ProgressProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, type Red, SUPERFICIE_TARJETA, ScrollingProgressBar, type ScrollingProgressBarProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, Sheet, SheetBody, SheetClose, SheetContent, type SheetContentProps, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SidebarItem, type SidebarItemProps, SidebarNav, type SidebarNavProps, Skeleton, type SkeletonProps, Stat, type StatProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableOfContents, type TableOfContentsProps, TableRow, Tabs, TabsContent, TabsList, type TabsProps, TabsTrigger, TalkCard, type TalkCardProps, Tema, Text, type TextProps, Textarea, type TextareaProps, ThemeToggle, type ThemeToggleProps, Toast, ToastAction, ToastDescription, type ToastOptions, type ToastProps, ToastProvider, ToastTitle, type ToastVariant, ToastViewport, Toaster, type ToasterProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alert as alertVariants, avatar as avatarVariants, badge as badgeVariants, button as buttonVariants, categoria as categoryBadgeVariants, cn, social, texto as textVariants, toast, useTema };