@eduardoalvarez/arrecife 0.1.1 → 0.2.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/CHANGELOG.md +63 -43
- package/README.md +17 -0
- package/llms.txt +1223 -0
- package/package.json +10 -6
package/llms.txt
ADDED
|
@@ -0,0 +1,1223 @@
|
|
|
1
|
+
# @eduardoalvarez/arrecife
|
|
2
|
+
|
|
3
|
+
> GENERADO por `scripts/build-llms.mjs`. No lo edites a mano: la prosa está en
|
|
4
|
+
> `docs/llms.plantilla.md` y el inventario sale de los tipos en cada build.
|
|
5
|
+
> `pnpm check:llms` falla si este archivo y el código dejan de decir lo mismo.
|
|
6
|
+
|
|
7
|
+
Librería de componentes de la identidad visual de Eduardo Álvarez. React 19,
|
|
8
|
+
TypeScript, Tailwind v4, shadcn/ui sobre Radix.
|
|
9
|
+
|
|
10
|
+
Este documento es para un agente que escribe código en un proyecto que consume la
|
|
11
|
+
librería. Si estás trabajando **dentro** del repo de Arrecife, el documento es
|
|
12
|
+
`AGENTS.md`, no este.
|
|
13
|
+
|
|
14
|
+
## Lo primero: no reimplementes lo que ya está aquí
|
|
15
|
+
|
|
16
|
+
Antes de escribir una tarjeta, un botón, una cabecera o un pie, busca en el
|
|
17
|
+
inventario de más abajo. La librería existe porque cinco proyectos escribían las
|
|
18
|
+
mismas piezas cada uno por su cuenta y se desincronizaban. Un componente nuevo
|
|
19
|
+
escrito a mano en el proyecto consumidor reintroduce exactamente ese problema.
|
|
20
|
+
|
|
21
|
+
Tampoco escribas colores, tamaños ni espaciados a mano. Todo valor del sistema
|
|
22
|
+
tiene un token y una utilidad de Tailwind; un `#hex` o un `p-[13px]` en el
|
|
23
|
+
proyecto consumidor es la señal de que se eligió el camino equivocado.
|
|
24
|
+
|
|
25
|
+
## Instalación
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add @eduardoalvarez/arrecife
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Requisitos, y no son opcionales:
|
|
32
|
+
|
|
33
|
+
| | |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| React | `^19.0.0` y `react-dom` `^19.0.0`, como peer dependencies |
|
|
36
|
+
| Tailwind | v4. **No hay preset de v3**: la salida es `@theme`, que v3 no entiende |
|
|
37
|
+
| Node | `>=22.18.0` para las subrutas que corren en build (`./og`, `./tokens`) |
|
|
38
|
+
|
|
39
|
+
Radix, `clsx`, `tailwind-merge`, `class-variance-authority`, `date-fns` y
|
|
40
|
+
`react-day-picker` vienen como dependencias de la librería. No hace falta
|
|
41
|
+
instalarlos ni declararlos.
|
|
42
|
+
|
|
43
|
+
**No lleva `lucide-react` ni ninguna librería de iconos.** Los glifos que los
|
|
44
|
+
componentes necesitan van inline, heredan `currentColor` y miden 1em.
|
|
45
|
+
|
|
46
|
+
## Configuración de Tailwind
|
|
47
|
+
|
|
48
|
+
Dos líneas, en este orden, en la hoja de estilos de entrada del proyecto:
|
|
49
|
+
|
|
50
|
+
```css
|
|
51
|
+
@import "tailwindcss";
|
|
52
|
+
@import "@eduardoalvarez/arrecife/tokens/theme.css";
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Sin la segunda, los componentes se montan sin ningún estilo del sistema: las
|
|
56
|
+
clases que usan (`bg-surface-raised`, `text-h1`, `rounded-card`) no existen en un
|
|
57
|
+
Tailwind pelado.
|
|
58
|
+
|
|
59
|
+
Tailwind tiene que escanear la librería para no purgar esas clases. Si el
|
|
60
|
+
proyecto declara `@source`, incluye el paquete:
|
|
61
|
+
|
|
62
|
+
```css
|
|
63
|
+
@source "../node_modules/@eduardoalvarez/arrecife/dist";
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Modo claro y modo oscuro
|
|
67
|
+
|
|
68
|
+
**El modo oscuro es el primario y es el default.** Un proyecto oscuro no declara
|
|
69
|
+
nada. Un proyecto en modo claro declara el atributo en `<html>`:
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<html data-theme="light">
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
No hay clase `dark:`. La variante disponible es `light:`, para los casos del modo
|
|
76
|
+
claro invertido, y casi nunca hace falta: los tokens ya cambian solos.
|
|
77
|
+
|
|
78
|
+
### Fuentes
|
|
79
|
+
|
|
80
|
+
La librería declara las familias **por nombre** y no las carga. El proyecto carga
|
|
81
|
+
Bricolage Grotesque (`font-display`), Geist (`font-sans`) y JetBrains Mono
|
|
82
|
+
(`font-mono`) como prefiera —`next/font`, `@fontsource`, un `<link>`—. Si no las
|
|
83
|
+
carga, el navegador cae al fallback y la tipografía se ve mal.
|
|
84
|
+
|
|
85
|
+
## Qué importar de dónde
|
|
86
|
+
|
|
87
|
+
`exports` tiene cinco subrutas y la elección importa: tres de ellas **no
|
|
88
|
+
arrastran React**, y por eso pueden consumirse desde un worker, un
|
|
89
|
+
`astro.config.mjs`, un script de build o un generador con Satori.
|
|
90
|
+
|
|
91
|
+
| Subruta | Arrastra React | Para qué |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| `@eduardoalvarez/arrecife` | sí | Componentes, primitivos, marca, `cn`. Reexporta tokens |
|
|
94
|
+
| `@eduardoalvarez/arrecife/tokens` | **no** | El objeto `tokens` en JS. Satori, Astro, scripts |
|
|
95
|
+
| `@eduardoalvarez/arrecife/tokens/theme.css` | — | El `@theme` de Tailwind v4 |
|
|
96
|
+
| `@eduardoalvarez/arrecife/og` | **no** | Las plantillas de Open Graph para Satori |
|
|
97
|
+
| `@eduardoalvarez/arrecife/shiki` | **no** | El tema de resaltado de sintaxis |
|
|
98
|
+
| `@eduardoalvarez/arrecife/brand` | sí | Logo, isotipo y mascota como componentes |
|
|
99
|
+
| `@eduardoalvarez/arrecife/assets/*` | — | Los PNG de la marca |
|
|
100
|
+
|
|
101
|
+
Importar la raíz desde un script de build para sacar un token es el error que las
|
|
102
|
+
subrutas existen para evitar: arrastra React entero a un worker que no lo monta.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// Bien, en un generador de OG o en astro.config.mjs
|
|
106
|
+
import { tokens } from '@eduardoalvarez/arrecife/tokens';
|
|
107
|
+
import { arrecife } from '@eduardoalvarez/arrecife/shiki';
|
|
108
|
+
|
|
109
|
+
// Mal: monta React donde no hace falta
|
|
110
|
+
import { tokens } from '@eduardoalvarez/arrecife';
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Tokens
|
|
114
|
+
|
|
115
|
+
La fuente es un objeto de TypeScript y la salida CSS se genera de él, así que el
|
|
116
|
+
mismo valor está disponible en los dos sitios y no pueden discrepar.
|
|
117
|
+
|
|
118
|
+
| Token | Custom property | Utilidad de Tailwind |
|
|
119
|
+
| --- | --- | --- |
|
|
120
|
+
| `colors[modo].surfaceRaised` | `--color-surface-raised` | `bg-surface-raised` |
|
|
121
|
+
| `brand.hull` | `--color-brand-hull` | `bg-brand-hull` |
|
|
122
|
+
| `typeScale.h1` | `--text-h1` | `text-h1` (arrastra line-height, weight y tracking) |
|
|
123
|
+
| `fonts.display` | `--font-display` | `font-display` |
|
|
124
|
+
| `radius.card` | `--radius-card` | `rounded-card` |
|
|
125
|
+
| `spacing.lg` | `--spacing-lg` | `p-lg`, `gap-lg`, `mb-lg` |
|
|
126
|
+
| `control.md` | `--spacing-control-md` | `px-control-md` |
|
|
127
|
+
| `control.icon` | `--spacing-control-icon` | `size-control-icon` (42×42) |
|
|
128
|
+
| `gradient[modo].hero` | `--gradient-hero` | `degradado-hero` |
|
|
129
|
+
| `size.nav` | `--spacing-nav` | `h-nav` |
|
|
130
|
+
| `size.content` / `size.wide` | `--container-content` / `--container-wide` | `max-w-content` / `max-w-wide` |
|
|
131
|
+
| `limits.measure` | `--container-measure` | `max-w-measure` |
|
|
132
|
+
| `shadow.standard` | `--shadow-standard` | `shadow-standard` |
|
|
133
|
+
| `motion` | `--duration-standard`, `--ease-standard` | `duration-standard`, `ease-standard` |
|
|
134
|
+
|
|
135
|
+
`transition-standard` es la única transición del sistema y solo puede animar
|
|
136
|
+
color y borde: así está escrita la utilidad.
|
|
137
|
+
|
|
138
|
+
## Reglas del sistema que el código consumidor no debe romper
|
|
139
|
+
|
|
140
|
+
Son decisiones de identidad, ya medidas. Romperlas produce código que compila y
|
|
141
|
+
se ve mal, o que falla la auditoría de accesibilidad del proyecto.
|
|
142
|
+
|
|
143
|
+
1. **Cero hex literales.** Todo color sale de un token o de su custom property.
|
|
144
|
+
2. **`Button variant="conversion"` va una sola vez por pantalla.** No se fuerza en
|
|
145
|
+
runtime; dos en la misma página son un error de diseño.
|
|
146
|
+
3. **No hay variante de peligro en `Button`.** El error del sistema vive en los
|
|
147
|
+
avisos y en la validación de campo, no en un botón rojo.
|
|
148
|
+
4. **`secondary` nunca se rellena el fondo.** Es borde y texto.
|
|
149
|
+
5. **Sin animaciones de entrada.** Modales, menús, tooltips y toasts aparecen
|
|
150
|
+
donde van a quedarse. La única excepción es el spinner de `Button loading`.
|
|
151
|
+
6. **La semántica y la escala son independientes.** Un `h2` que debe verse
|
|
152
|
+
pequeño es `<Text as="h2" variant="h3">`, nunca un `h3` que miente sobre la
|
|
153
|
+
jerarquía.
|
|
154
|
+
7. **`textMuted` no va nunca sobre `surfaceRaised`**: da 4.07 en oscuro. Sobre
|
|
155
|
+
superficie elevada —menús, tabs activos— el token es `textSecondary`.
|
|
156
|
+
8. **Un fondo teñido con un color semántico lleva texto de token de texto**, no
|
|
157
|
+
del color semántico. El color se queda en el borde y en el glifo. Poner
|
|
158
|
+
`accent` sobre su propio tinte al 8 % da 4.12 y no llega a AA.
|
|
159
|
+
9. **`Progress` exige `label`.** Una barra sin nombre accesible no dice de qué es.
|
|
160
|
+
10. **`Button size="icon"` exige `aria-label`.** No lleva texto.
|
|
161
|
+
11. **Las caras de la mascota solo aparecen** en estados vacíos, confirmaciones,
|
|
162
|
+
errores, progreso de curso y celebración. Nunca en hero, precios, servicios,
|
|
163
|
+
contacto ni CV.
|
|
164
|
+
12. **La aleta no es un parámetro libre**: `espuma` sobre fondo oscuro, `color`
|
|
165
|
+
sobre fondo claro. Los componentes ya la eligen por el fondo.
|
|
166
|
+
|
|
167
|
+
## Lo que la librería NO hace, a propósito
|
|
168
|
+
|
|
169
|
+
Estas son las confusiones que más veces se cometen al consumirla.
|
|
170
|
+
|
|
171
|
+
- **No trae Shiki.** Publica el *tema*, no el resaltador. `CodeBlock` recibe el
|
|
172
|
+
código **ya resaltado** por la herramienta del proyecto.
|
|
173
|
+
- **No formatea fechas.** `ArticleCard`, `TalkCard` y compañía reciben la fecha ya
|
|
174
|
+
formateada por el proyecto: la librería no impone locale. `dateTime` es aparte,
|
|
175
|
+
en ISO, para el atributo del `<time>`.
|
|
176
|
+
- **`NewsletterForm` no hace el POST.** Es presentacional: recibe `state` y emite
|
|
177
|
+
`onSubmitEmail`. La llamada la hace el proyecto con su proveedor.
|
|
178
|
+
- **No trae enrutador.** Los componentes con enlaces aceptan `asChild` para
|
|
179
|
+
envolver el `Link` del framework.
|
|
180
|
+
- **No carga fuentes.** Las declara por nombre.
|
|
181
|
+
- **No hay preset de Tailwind v3.**
|
|
182
|
+
|
|
183
|
+
## Patrones de uso
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { Text, Button, Badge, cn } from '@eduardoalvarez/arrecife';
|
|
187
|
+
import type { TextProps } from '@eduardoalvarez/arrecife';
|
|
188
|
+
|
|
189
|
+
<Text variant="eyebrow" tone="muted">charlas</Text>
|
|
190
|
+
<Text as="h2" variant="h1">Escalar con criterio</Text>
|
|
191
|
+
<Text variant="body">Se corta solo a 68ch.</Text>
|
|
192
|
+
<Text variant="ui" measure={false}>Sin corte, para una celda estrecha.</Text>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`asChild` renderiza el hijo en vez del elemento propio. Es como se envuelve el
|
|
196
|
+
enlace del framework sin perder los estilos:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
<Button asChild>
|
|
200
|
+
<Link href="/cursos">Ver los cursos</Link>
|
|
201
|
+
</Button>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`cn` es `clsx` + `tailwind-merge`. Se usa para componer `className` sin que dos
|
|
205
|
+
utilidades del mismo grupo peleen.
|
|
206
|
+
|
|
207
|
+
Open Graph, sin React:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import satori from 'satori';
|
|
211
|
+
import { plantillaArticulo, OG } from '@eduardoalvarez/arrecife/og';
|
|
212
|
+
|
|
213
|
+
const svg = await satori(plantillaArticulo({ title, date, readingMinutes }), {
|
|
214
|
+
width: OG.width, // 1200
|
|
215
|
+
height: OG.height, // 630
|
|
216
|
+
fonts: [...],
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Resaltado de sintaxis, desde la configuración del sitio:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import { arrecife } from '@eduardoalvarez/arrecife/shiki';
|
|
224
|
+
|
|
225
|
+
export default defineConfig({
|
|
226
|
+
markdown: { syntaxHighlight: 'shiki', shikiConfig: { theme: arrecife } },
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
# Inventario
|
|
231
|
+
|
|
232
|
+
Lo que sigue sale del compilador de TypeScript en cada build. Solo se listan los
|
|
233
|
+
props **declarados por la librería**: los heredados de un elemento HTML o de una
|
|
234
|
+
primitiva de Radix se resumen en la línea `Extiende`, y son los de siempre.
|
|
235
|
+
|
|
236
|
+
## Primitivos
|
|
237
|
+
|
|
238
|
+
Se importan de `@eduardoalvarez/arrecife`. 100 exportaciones.
|
|
239
|
+
|
|
240
|
+
### Alert
|
|
241
|
+
|
|
242
|
+
Fuente: `src/primitives/alert.tsx`
|
|
243
|
+
|
|
244
|
+
El aviso lleva el color en el fondo, no solo en el borde.
|
|
245
|
+
|
|
246
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'div'>, 'title'> & VariantProps<typeof alert>`
|
|
247
|
+
|
|
248
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
249
|
+
| --- | --- | --- | --- | --- |
|
|
250
|
+
| `enfasis` | `"sutil" \| "fuerte"` | | `sutil` | |
|
|
251
|
+
| `icon` | `ReactNode` | | | Sustituye el glifo mono de la variante. Nunca un emoji: si necesitas otra cosa, es un SVG de `glyphs`. |
|
|
252
|
+
| `title` | `ReactNode` | | | |
|
|
253
|
+
| `variant` | `"accent" \| "success" \| "warning" \| "error"` | | `accent` | |
|
|
254
|
+
|
|
255
|
+
### Avatar, AvatarImage, AvatarFallback
|
|
256
|
+
|
|
257
|
+
Fuente: `src/primitives/avatar.tsx`
|
|
258
|
+
|
|
259
|
+
**Avatar**
|
|
260
|
+
Uno solo para todo: la foto del autor y la de cualquier persona del sistema. No hay un `brand/Avatar` aparte — una foto de perfil con la piel de la marca es exactamente esto con un `src` distinto.
|
|
261
|
+
|
|
262
|
+
- Extiende: `ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & VariantProps<typeof avatar>`
|
|
263
|
+
|
|
264
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
265
|
+
| --- | --- | --- | --- | --- |
|
|
266
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | | `md` | |
|
|
267
|
+
|
|
268
|
+
**AvatarImage**
|
|
269
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
270
|
+
|
|
271
|
+
**AvatarFallback**
|
|
272
|
+
Iniciales mientras la imagen carga, o cuando no hay imagen.
|
|
273
|
+
|
|
274
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
275
|
+
|
|
276
|
+
### Badge, CategoryBadge, MetricBadge
|
|
277
|
+
|
|
278
|
+
Fuente: `src/primitives/badge.tsx`
|
|
279
|
+
|
|
280
|
+
**Badge**
|
|
281
|
+
Semáforo. Cuadrada r6, sans 12.5/500 y fondo al 8 % del semántico — la receta del aviso en tamaño de palabra: un estado es un aviso de una sola palabra.
|
|
282
|
+
|
|
283
|
+
- Extiende: `ComponentPropsWithoutRef<'span'> & VariantProps<typeof badge>`
|
|
284
|
+
|
|
285
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
286
|
+
| --- | --- | --- | --- | --- |
|
|
287
|
+
| `variant` | `"accent" \| "success" \| "warning" \| "error" \| "neutral" \| "warm"` | | `neutral` | |
|
|
288
|
+
|
|
289
|
+
**CategoryBadge**
|
|
290
|
+
Un slug, en arena. Sin transformar: los slugs ya vienen en minúscula y forzarla sería el mismo error que forzaba el `uppercase`.
|
|
291
|
+
|
|
292
|
+
- Extiende: `ComponentPropsWithoutRef<'span'>`
|
|
293
|
+
|
|
294
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
295
|
+
| --- | --- | --- | --- | --- |
|
|
296
|
+
| `active` | `boolean \| undefined` | | `false` | Filtro seleccionado: arena sólido con tinta encima. |
|
|
297
|
+
|
|
298
|
+
**MetricBadge**
|
|
299
|
+
- Extiende: `ComponentPropsWithoutRef<'span'>`
|
|
300
|
+
|
|
301
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
302
|
+
| --- | --- | --- | --- | --- |
|
|
303
|
+
| `boxed` | `boolean \| undefined` | | `false` | Añade el aro de hairline. Por defecto la métrica va sin caja. |
|
|
304
|
+
|
|
305
|
+
### Button
|
|
306
|
+
|
|
307
|
+
Fuente: `src/primitives/button.tsx`
|
|
308
|
+
|
|
309
|
+
Las CUATRO variantes del sistema, y solo esas cuatro.
|
|
310
|
+
|
|
311
|
+
- Extiende: `ComponentPropsWithoutRef<'button'> & VariantProps<typeof button>`
|
|
312
|
+
|
|
313
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
314
|
+
| --- | --- | --- | --- | --- |
|
|
315
|
+
| `asChild` | `boolean` | | `false` | Renderiza el hijo en vez de un `<button>`, para envolver un enlace. |
|
|
316
|
+
| `icon` | `ReactNode` | | | Glifo SVG antes del texto. Se oculta mientras carga. |
|
|
317
|
+
| `loading` | `boolean` | | `false` | Deshabilita y anuncia `aria-busy`. Incompatible con `asChild`. |
|
|
318
|
+
| `size` | `"sm" \| "md" \| "lg" \| "icon"` | | `md` | |
|
|
319
|
+
| `variant` | `"primary" \| "conversion" \| "secondary" \| "tertiary"` | | `primary` | |
|
|
320
|
+
|
|
321
|
+
### Calendar
|
|
322
|
+
|
|
323
|
+
Fuente: `src/primitives/calendar.tsx`
|
|
324
|
+
|
|
325
|
+
Calendario mensual navegable, sobre `react-day-picker`.
|
|
326
|
+
|
|
327
|
+
- Extiende: `ComponentProps<typeof DayPicker>`
|
|
328
|
+
|
|
329
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
330
|
+
| --- | --- | --- | --- | --- |
|
|
331
|
+
| `fullWidth` | `boolean \| undefined` | | `false` | Estira el calendario hasta ocupar todo el ancho de su contenedor, con las celdas repartiéndoselo a partes iguales. |
|
|
332
|
+
|
|
333
|
+
### Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter
|
|
334
|
+
|
|
335
|
+
Fuente: `src/primitives/card.tsx`
|
|
336
|
+
|
|
337
|
+
**Card**
|
|
338
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
339
|
+
|
|
340
|
+
**CardHeader**
|
|
341
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
342
|
+
|
|
343
|
+
**CardTitle**
|
|
344
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
345
|
+
|
|
346
|
+
**CardDescription**
|
|
347
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
348
|
+
|
|
349
|
+
**CardContent**
|
|
350
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
351
|
+
|
|
352
|
+
**CardFooter**
|
|
353
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
354
|
+
|
|
355
|
+
### Checkbox
|
|
356
|
+
|
|
357
|
+
Fuente: `src/primitives/checkbox.tsx`
|
|
358
|
+
|
|
359
|
+
- Extiende: `ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>`
|
|
360
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
361
|
+
|
|
362
|
+
### Code
|
|
363
|
+
|
|
364
|
+
Fuente: `src/primitives/code.tsx`
|
|
365
|
+
|
|
366
|
+
Código en línea, dentro de prosa.
|
|
367
|
+
|
|
368
|
+
- Extiende: `ComponentPropsWithoutRef<'code'>`
|
|
369
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
370
|
+
|
|
371
|
+
### DateField
|
|
372
|
+
|
|
373
|
+
Fuente: `src/primitives/date-field.tsx`
|
|
374
|
+
|
|
375
|
+
Un campo de fecha sobre el control nativo, no sobre un calendario propio.
|
|
376
|
+
|
|
377
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'input'>, 'type'>`
|
|
378
|
+
|
|
379
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
380
|
+
| --- | --- | --- | --- | --- |
|
|
381
|
+
| `invalid` | `boolean \| undefined` | | `false` | |
|
|
382
|
+
| `withTime` | `boolean \| undefined` | | `false` | Añade la hora al campo. Es el `datetime-local` nativo. |
|
|
383
|
+
|
|
384
|
+
### DialogOverlay, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, Dialog, DialogTrigger, DialogClose
|
|
385
|
+
|
|
386
|
+
Fuente: `src/primitives/dialog.tsx`
|
|
387
|
+
|
|
388
|
+
**DialogOverlay**
|
|
389
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
390
|
+
|
|
391
|
+
**DialogContent**
|
|
392
|
+
Sin entrada animada: no hay escala ni desplazamiento en el sistema.
|
|
393
|
+
|
|
394
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
395
|
+
|
|
396
|
+
**DialogHeader**
|
|
397
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
398
|
+
|
|
399
|
+
**DialogFooter**
|
|
400
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
401
|
+
|
|
402
|
+
**DialogTitle**
|
|
403
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
404
|
+
|
|
405
|
+
**DialogDescription**
|
|
406
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
407
|
+
|
|
408
|
+
**Dialog**
|
|
409
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
410
|
+
|
|
411
|
+
**DialogTrigger**
|
|
412
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
413
|
+
|
|
414
|
+
**DialogClose**
|
|
415
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
416
|
+
|
|
417
|
+
### DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenu, DropdownMenuTrigger, DropdownMenuGroup, DropdownMenuRadioGroup, DropdownMenuSub
|
|
418
|
+
|
|
419
|
+
Fuente: `src/primitives/dropdown-menu.tsx`
|
|
420
|
+
|
|
421
|
+
**DropdownMenuContent**
|
|
422
|
+
Sin animación de entrada: el menú aparece, no se despliega.
|
|
423
|
+
|
|
424
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
425
|
+
|
|
426
|
+
**DropdownMenuItem**
|
|
427
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
428
|
+
|
|
429
|
+
**DropdownMenuCheckboxItem**
|
|
430
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
431
|
+
|
|
432
|
+
**DropdownMenuRadioItem**
|
|
433
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
434
|
+
|
|
435
|
+
**DropdownMenuLabel**
|
|
436
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
437
|
+
|
|
438
|
+
**DropdownMenuSeparator**
|
|
439
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
440
|
+
|
|
441
|
+
**DropdownMenuSubTrigger**
|
|
442
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
443
|
+
|
|
444
|
+
**DropdownMenuSubContent**
|
|
445
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
446
|
+
|
|
447
|
+
**DropdownMenu**
|
|
448
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
449
|
+
|
|
450
|
+
**DropdownMenuTrigger**
|
|
451
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
452
|
+
|
|
453
|
+
**DropdownMenuGroup**
|
|
454
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
455
|
+
|
|
456
|
+
**DropdownMenuRadioGroup**
|
|
457
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
458
|
+
|
|
459
|
+
**DropdownMenuSub**
|
|
460
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
461
|
+
|
|
462
|
+
### Input
|
|
463
|
+
|
|
464
|
+
Fuente: `src/primitives/input.tsx`
|
|
465
|
+
|
|
466
|
+
- Extiende: `ComponentPropsWithoutRef<'input'>`
|
|
467
|
+
|
|
468
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
469
|
+
| --- | --- | --- | --- | --- |
|
|
470
|
+
| `invalid` | `boolean` | | `false` | Marca el control como inválido y tiñe el borde. |
|
|
471
|
+
|
|
472
|
+
### Label
|
|
473
|
+
|
|
474
|
+
Fuente: `src/primitives/label.tsx`
|
|
475
|
+
|
|
476
|
+
La escala `label`: 13px, que es el mínimo absoluto en pantalla del sistema.
|
|
477
|
+
|
|
478
|
+
- Extiende: `ComponentPropsWithoutRef<typeof LabelPrimitive.Root>`
|
|
479
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
480
|
+
|
|
481
|
+
### Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis
|
|
482
|
+
|
|
483
|
+
Fuente: `src/primitives/pagination.tsx`
|
|
484
|
+
|
|
485
|
+
**Pagination**
|
|
486
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
487
|
+
|
|
488
|
+
**PaginationContent**
|
|
489
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
490
|
+
|
|
491
|
+
**PaginationItem**
|
|
492
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
493
|
+
|
|
494
|
+
**PaginationLink**
|
|
495
|
+
- Extiende: `ComponentPropsWithoutRef<'a'>`
|
|
496
|
+
|
|
497
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
498
|
+
| --- | --- | --- | --- | --- |
|
|
499
|
+
| `isActive` | `boolean` | | `false` | |
|
|
500
|
+
|
|
501
|
+
**PaginationPrevious**
|
|
502
|
+
|
|
503
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
504
|
+
| --- | --- | --- | --- | --- |
|
|
505
|
+
| `isActive` | `boolean` | | | |
|
|
506
|
+
|
|
507
|
+
**PaginationNext**
|
|
508
|
+
|
|
509
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
510
|
+
| --- | --- | --- | --- | --- |
|
|
511
|
+
| `isActive` | `boolean` | | | |
|
|
512
|
+
|
|
513
|
+
**PaginationEllipsis**
|
|
514
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
515
|
+
|
|
516
|
+
### PopoverContent, Popover, PopoverTrigger, PopoverAnchor
|
|
517
|
+
|
|
518
|
+
Fuente: `src/primitives/popover.tsx`
|
|
519
|
+
|
|
520
|
+
**PopoverContent**
|
|
521
|
+
Sin animación de entrada: aparece donde va a quedarse, como el resto.
|
|
522
|
+
|
|
523
|
+
- Extiende: `ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & Etiquetado`
|
|
524
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
525
|
+
|
|
526
|
+
**Popover**
|
|
527
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
528
|
+
|
|
529
|
+
**PopoverTrigger**
|
|
530
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
531
|
+
|
|
532
|
+
**PopoverAnchor**
|
|
533
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
534
|
+
|
|
535
|
+
### Progress
|
|
536
|
+
|
|
537
|
+
Fuente: `src/primitives/progress.tsx`
|
|
538
|
+
|
|
539
|
+
El ancho del indicador cambia, no se anima: el sistema no anima escala ni desplazamiento. `transition-standard` solo cubre color y borde, así que el salto de ancho es inmediato aunque la clase esté puesta.
|
|
540
|
+
|
|
541
|
+
- Extiende: `ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>`
|
|
542
|
+
|
|
543
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
544
|
+
| --- | --- | --- | --- | --- |
|
|
545
|
+
| `label` | `string` | sí | | Nombre accesible de la barra. Es obligatorio a propósito: una barra de progreso sin nombre no dice de qué es el progreso, y ninguna otra parte del componente puede deducirlo. |
|
|
546
|
+
| `tone` | `"accent" \| "warm"` | | `accent` | Arena en vez de bioluz, para progreso de curso. |
|
|
547
|
+
|
|
548
|
+
### RadioGroup, RadioGroupItem
|
|
549
|
+
|
|
550
|
+
Fuente: `src/primitives/radio-group.tsx`
|
|
551
|
+
|
|
552
|
+
**RadioGroup**
|
|
553
|
+
- Extiende: `ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>`
|
|
554
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
555
|
+
|
|
556
|
+
**RadioGroupItem**
|
|
557
|
+
- Extiende: `ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>`
|
|
558
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
559
|
+
|
|
560
|
+
### SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, Select, SelectGroup, SelectValue
|
|
561
|
+
|
|
562
|
+
Fuente: `src/primitives/select.tsx`
|
|
563
|
+
|
|
564
|
+
**SelectTrigger**
|
|
565
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
566
|
+
|
|
567
|
+
**SelectContent**
|
|
568
|
+
Sin animación de entrada: el menú aparece, no se despliega.
|
|
569
|
+
|
|
570
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
571
|
+
|
|
572
|
+
**SelectLabel**
|
|
573
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
574
|
+
|
|
575
|
+
**SelectItem**
|
|
576
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
577
|
+
|
|
578
|
+
**SelectSeparator**
|
|
579
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
580
|
+
|
|
581
|
+
**Select**
|
|
582
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
583
|
+
|
|
584
|
+
**SelectGroup**
|
|
585
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
586
|
+
|
|
587
|
+
**SelectValue**
|
|
588
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
589
|
+
|
|
590
|
+
### Separator
|
|
591
|
+
|
|
592
|
+
Fuente: `src/primitives/separator.tsx`
|
|
593
|
+
|
|
594
|
+
`hairline`, no `border`: una división entre contenidos es sutil por definición. Para delimitar un control existe `border`, que es otro token.
|
|
595
|
+
|
|
596
|
+
- Extiende: `ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>`
|
|
597
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
598
|
+
|
|
599
|
+
### SheetContent, SheetHeader, SheetBody, SheetFooter, SheetTitle, SheetDescription, Sheet, SheetTrigger, SheetClose
|
|
600
|
+
|
|
601
|
+
Fuente: `src/primitives/sheet.tsx`
|
|
602
|
+
|
|
603
|
+
**SheetContent**
|
|
604
|
+
La segunda y última excepción a «nada de desplazamiento», aprobada a sabiendas: un panel que entra desde un borde se desliza por definición, y quieto sería un modal descentrado.
|
|
605
|
+
|
|
606
|
+
- Extiende: `ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & VariantProps<typeof panel>`
|
|
607
|
+
|
|
608
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
609
|
+
| --- | --- | --- | --- | --- |
|
|
610
|
+
| `side` | `"right" \| "left" \| "top" \| "bottom"` | | `right` | |
|
|
611
|
+
|
|
612
|
+
**SheetHeader**
|
|
613
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
614
|
+
|
|
615
|
+
**SheetBody**
|
|
616
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
617
|
+
|
|
618
|
+
**SheetFooter**
|
|
619
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
620
|
+
|
|
621
|
+
**SheetTitle**
|
|
622
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
623
|
+
|
|
624
|
+
**SheetDescription**
|
|
625
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
626
|
+
|
|
627
|
+
**Sheet**
|
|
628
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
629
|
+
|
|
630
|
+
**SheetTrigger**
|
|
631
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
632
|
+
|
|
633
|
+
**SheetClose**
|
|
634
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
635
|
+
|
|
636
|
+
### Skeleton
|
|
637
|
+
|
|
638
|
+
Fuente: `src/primitives/skeleton.tsx`
|
|
639
|
+
|
|
640
|
+
Barrido de 1.4s lineal, del documento.
|
|
641
|
+
|
|
642
|
+
- Extiende: `ComponentPropsWithoutRef<'div'>`
|
|
643
|
+
|
|
644
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
645
|
+
| --- | --- | --- | --- | --- |
|
|
646
|
+
| `still` | `boolean \| undefined` | | `false` | Apaga el barrido. Para listas largas, donde muchas a la vez marean. |
|
|
647
|
+
|
|
648
|
+
### Switch
|
|
649
|
+
|
|
650
|
+
Fuente: `src/primitives/switch.tsx`
|
|
651
|
+
|
|
652
|
+
La perilla cambia de posición, pero no se anima al hacerlo: la posición es el estado, no una transición. Lo único que transiciona es el color de la vía.
|
|
653
|
+
|
|
654
|
+
- Extiende: `ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>`
|
|
655
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
656
|
+
|
|
657
|
+
### Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption
|
|
658
|
+
|
|
659
|
+
Fuente: `src/primitives/table.tsx`
|
|
660
|
+
|
|
661
|
+
**Table**
|
|
662
|
+
El contenedor scrollea en horizontal: la página nunca lo hace.
|
|
663
|
+
|
|
664
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
665
|
+
|
|
666
|
+
**TableHeader**
|
|
667
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
668
|
+
|
|
669
|
+
**TableBody**
|
|
670
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
671
|
+
|
|
672
|
+
**TableFooter**
|
|
673
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
674
|
+
|
|
675
|
+
**TableRow**
|
|
676
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
677
|
+
|
|
678
|
+
**TableHead**
|
|
679
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
680
|
+
|
|
681
|
+
**TableCell**
|
|
682
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
683
|
+
|
|
684
|
+
**TableCaption**
|
|
685
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
686
|
+
|
|
687
|
+
### Tabs, TabsList, TabsTrigger, TabsContent
|
|
688
|
+
|
|
689
|
+
Fuente: `src/primitives/tabs.tsx`
|
|
690
|
+
|
|
691
|
+
**Tabs**
|
|
692
|
+
- Extiende: `ComponentPropsWithoutRef<typeof TabsPrimitive.Root>`
|
|
693
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
694
|
+
|
|
695
|
+
**TabsList**
|
|
696
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
697
|
+
|
|
698
|
+
**TabsTrigger**
|
|
699
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
700
|
+
|
|
701
|
+
**TabsContent**
|
|
702
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
703
|
+
|
|
704
|
+
### Textarea
|
|
705
|
+
|
|
706
|
+
Fuente: `src/primitives/textarea.tsx`
|
|
707
|
+
|
|
708
|
+
- Extiende: `ComponentPropsWithoutRef<'textarea'>`
|
|
709
|
+
|
|
710
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
711
|
+
| --- | --- | --- | --- | --- |
|
|
712
|
+
| `invalid` | `boolean` | | `false` | |
|
|
713
|
+
|
|
714
|
+
### ToastViewport, Toast, ToastTitle, ToastDescription, ToastProvider, ToastAction
|
|
715
|
+
|
|
716
|
+
Fuente: `src/primitives/toast.tsx`
|
|
717
|
+
|
|
718
|
+
**ToastViewport**
|
|
719
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
720
|
+
|
|
721
|
+
**Toast**
|
|
722
|
+
Sin deslizamiento de entrada: el aviso aparece donde va a quedarse.
|
|
723
|
+
|
|
724
|
+
- Extiende: `ComponentPropsWithoutRef<typeof ToastPrimitive.Root> & VariantProps<typeof toast>`
|
|
725
|
+
|
|
726
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
727
|
+
| --- | --- | --- | --- | --- |
|
|
728
|
+
| `variant` | `"success" \| "error" \| "neutral"` | | `neutral` | |
|
|
729
|
+
|
|
730
|
+
**ToastTitle**
|
|
731
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
732
|
+
|
|
733
|
+
**ToastDescription**
|
|
734
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
735
|
+
|
|
736
|
+
**ToastProvider**
|
|
737
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
738
|
+
|
|
739
|
+
**ToastAction**
|
|
740
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
741
|
+
|
|
742
|
+
### TooltipContent, TooltipProvider, Tooltip, TooltipTrigger
|
|
743
|
+
|
|
744
|
+
Fuente: `src/primitives/tooltip.tsx`
|
|
745
|
+
|
|
746
|
+
**TooltipContent**
|
|
747
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
748
|
+
|
|
749
|
+
**TooltipProvider**
|
|
750
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
751
|
+
|
|
752
|
+
**Tooltip**
|
|
753
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
754
|
+
|
|
755
|
+
**TooltipTrigger**
|
|
756
|
+
- Sin props propios: pasa los del elemento o la primitiva que envuelve.
|
|
757
|
+
|
|
758
|
+
### Text
|
|
759
|
+
|
|
760
|
+
Fuente: `src/primitives/typography.tsx`
|
|
761
|
+
|
|
762
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'p'>, 'color'> & VariantProps<typeof texto>`
|
|
763
|
+
|
|
764
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
765
|
+
| --- | --- | --- | --- | --- |
|
|
766
|
+
| `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. |
|
|
767
|
+
| `asChild` | `boolean` | | `false` | Renderiza el hijo en vez de crear un elemento, para envolver un enlace. |
|
|
768
|
+
| `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. |
|
|
769
|
+
| `tone` | `"accent" \| "success" \| "warning" \| "error" \| "warm" \| "primary" \| "secondary" \| "muted"` | | `primary` | |
|
|
770
|
+
| `variant` | `"display" \| "body" \| "h1" \| "h2" \| "h3" \| "label" \| "meta" \| "stat" \| "lead" \| "ui" \| "tag" \| "chip" \| "eyebrow"` | | `body` | |
|
|
771
|
+
|
|
772
|
+
## Componentes
|
|
773
|
+
|
|
774
|
+
Se importan de `@eduardoalvarez/arrecife`. 21 exportaciones.
|
|
775
|
+
|
|
776
|
+
### ArticleCard
|
|
777
|
+
|
|
778
|
+
Fuente: `src/components/article-card/index.tsx`
|
|
779
|
+
|
|
780
|
+
La línea de metadatos va en `meta` y no en `eyebrow`: `18 ago 2026 · 8 min de lectura` es un dato, no un antetítulo, y en versalitas no era ninguna de las dos cosas.
|
|
781
|
+
|
|
782
|
+
- Extiende: `Omit<TarjetaProps, 'children' \| 'title'>`
|
|
783
|
+
|
|
784
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
785
|
+
| --- | --- | --- | --- | --- |
|
|
786
|
+
| `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. |
|
|
787
|
+
| `date` | `ReactNode` | | | Fecha ya formateada por el proyecto: la librería no impone locale. |
|
|
788
|
+
| `dateTime` | `string` | | | Valor de `datetime` del `<time>`, en ISO. |
|
|
789
|
+
| `excerpt` | `ReactNode` | | | Entradilla. Se corta a dos líneas para que la rejilla no se desalinee. |
|
|
790
|
+
| `readingMinutes` | `number` | | | |
|
|
791
|
+
| `tags` | `readonly string[]` | | | |
|
|
792
|
+
| `title` | `ReactNode` | sí | | |
|
|
793
|
+
|
|
794
|
+
### AudioPlayer
|
|
795
|
+
|
|
796
|
+
Fuente: `src/components/audio-player/index.tsx`
|
|
797
|
+
|
|
798
|
+
- Extiende: `{ src: string; title?: string; /** * `full` para páginas de podcast, `compact` para barras laterales y `banner` * para artículos con narración. `compact` y `banner` traen además el * reproductor flotante cuando el estático sale de vista. */ mode?: AudioPlayerMode \| undefined; /** * Se llama una sola vez por carga, la primera vez que el audio arranca. * Aquí es donde el proyecto engancha su analítica; la librería no la trae. */ onFirstPlay?: ((title?: string) => void) \| undefined; }`
|
|
799
|
+
|
|
800
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
801
|
+
| --- | --- | --- | --- | --- |
|
|
802
|
+
| `mode` | `"banner" \| "full" \| "compact"` | | `full` | `full` para páginas de podcast, `compact` para barras laterales y `banner` para artículos con narración. `compact` y `banner` traen además el reproductor flotante cuando el estático sale de vista. |
|
|
803
|
+
| `onFirstPlay` | `(title?: string \| undefined) => void` | | | Se llama una sola vez por carga, la primera vez que el audio arranca. Aquí es donde el proyecto engancha su analítica; la librería no la trae. |
|
|
804
|
+
| `src` | `string` | sí | | |
|
|
805
|
+
| `title` | `string` | | | |
|
|
806
|
+
|
|
807
|
+
### AuthorCard
|
|
808
|
+
|
|
809
|
+
Fuente: `src/components/author-card/index.tsx`
|
|
810
|
+
|
|
811
|
+
La firma al pie del artículo: avatar 52px, nombre 15/500 y el rol en mono muted. Tres datos, ni uno más.
|
|
812
|
+
|
|
813
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'div'>, 'role'>`
|
|
814
|
+
|
|
815
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
816
|
+
| --- | --- | --- | --- | --- |
|
|
817
|
+
| `action` | `ReactNode` | | | Enlaces o botón de contacto. |
|
|
818
|
+
| `bio` | `ReactNode` | | | Una o dos frases. Se corta a 68ch sola. |
|
|
819
|
+
| `name` | `string` | sí | | |
|
|
820
|
+
| `role` | `ReactNode` | | | El rol. Va en mono: es un dato, no una frase. |
|
|
821
|
+
| `src` | `string` | | | URL del avatar. Sin ella se muestran las iniciales. |
|
|
822
|
+
|
|
823
|
+
### Blockquote
|
|
824
|
+
|
|
825
|
+
Fuente: `src/components/blockquote/index.tsx`
|
|
826
|
+
|
|
827
|
+
La barra lateral es `accent`, que es el color interactivo, porque una cita es la voz de otro entrando en el texto. No lleva comillas decorativas: los glifos del sistema son SVG y una comilla de adorno no aporta nada que el borde y la sangría no digan ya.
|
|
828
|
+
|
|
829
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'blockquote'>, 'cite'>`
|
|
830
|
+
|
|
831
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
832
|
+
| --- | --- | --- | --- | --- |
|
|
833
|
+
| `author` | `ReactNode` | | | Quién lo dijo. Se marca como `<cite>`. |
|
|
834
|
+
| `source` | `ReactNode` | | | Dónde lo dijo: charla, artículo, conversación. |
|
|
835
|
+
|
|
836
|
+
### Breadcrumb
|
|
837
|
+
|
|
838
|
+
Fuente: `src/components/breadcrumb/index.tsx`
|
|
839
|
+
|
|
840
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'nav'>, 'children'>`
|
|
841
|
+
|
|
842
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
843
|
+
| --- | --- | --- | --- | --- |
|
|
844
|
+
| `homeHref` | `string` | | `/` | Destino del `~`. Por defecto, la raíz del sitio. |
|
|
845
|
+
| `homeLabel` | `string` | | `Inicio` | Etiqueta accesible del `~`, que si no se lee como una tilde suelta. |
|
|
846
|
+
| `items` | `readonly Migaja[]` | sí | | |
|
|
847
|
+
| `linkAsChild` | `(props: { href: string; children: ReactNode; }) => ReactNode` | | | Renderiza los enlaces con el hijo, para enchufar el `Link` de Next o Astro. Recibe cada `href` en el `props` del Slot. |
|
|
848
|
+
|
|
849
|
+
### CodeBlock
|
|
850
|
+
|
|
851
|
+
Fuente: `src/components/code-block/index.tsx`
|
|
852
|
+
|
|
853
|
+
`brand.hull` es «casco · contorno y fondo de bloques de código», así que un bloque de código es oscuro también en modo claro. Por eso la raíz declara `data-theme="dark"`: todo lo de dentro — tinta, hairline, acento — pasa a la paleta oscura sin importar el tema de la página. Es la única isla de tema invertido del sistema, y es deliberada.
|
|
854
|
+
|
|
855
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'div'>, 'children'>`
|
|
856
|
+
|
|
857
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
858
|
+
| --- | --- | --- | --- | --- |
|
|
859
|
+
| `children` | `ReactNode` | sí | | El código ya resaltado, o texto plano. |
|
|
860
|
+
| `copyText` | `string` | | | Texto que se copia al portapapeles. Sin esto, no se muestra el botón. |
|
|
861
|
+
| `language` | `string` | | | Etiqueta del lenguaje. Se muestra en la barra superior. |
|
|
862
|
+
|
|
863
|
+
### CourseCard
|
|
864
|
+
|
|
865
|
+
Fuente: `src/components/course-card/index.tsx`
|
|
866
|
+
|
|
867
|
+
- Extiende: `Omit<TarjetaProps, 'children' \| 'title'>`
|
|
868
|
+
|
|
869
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
870
|
+
| --- | --- | --- | --- | --- |
|
|
871
|
+
| `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. |
|
|
872
|
+
| `meta` | `readonly ReactNode[]` | | | Nivel, duración, número de lecciones: lo que el proyecto quiera listar. |
|
|
873
|
+
| `progress` | `number` | | | Porcentaje cursado. Solo tiene sentido para quien ya está inscrito; cuando se pasa, la barra va en arena, que es el color del progreso de curso. |
|
|
874
|
+
| `status` | `ReactNode` | | | Etiqueta de estado: «próximamente», «gratis», «nuevo». |
|
|
875
|
+
| `summary` | `ReactNode` | | | |
|
|
876
|
+
| `title` | `ReactNode` | sí | | |
|
|
877
|
+
|
|
878
|
+
### EmptyState
|
|
879
|
+
|
|
880
|
+
Fuente: `src/components/empty-state/index.tsx`
|
|
881
|
+
|
|
882
|
+
La regla más importante de la mascota, por fin como código.
|
|
883
|
+
|
|
884
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'div'>, 'title'>`
|
|
885
|
+
|
|
886
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
887
|
+
| --- | --- | --- | --- | --- |
|
|
888
|
+
| `action` | `ReactNode` | | | La acción que saca del estado vacío. Normalmente un botón terciario. |
|
|
889
|
+
| `basePath` | `string` | | | Dónde se sirven los PNG de la marca. |
|
|
890
|
+
| `description` | `ReactNode` | | | Una línea explicando qué falta o qué hacer. |
|
|
891
|
+
| `expresion` | `"annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink"` | sí | | La cara. Obligatoria: sin ella esto es un párrafo centrado. |
|
|
892
|
+
| `title` | `ReactNode` | sí | | |
|
|
893
|
+
|
|
894
|
+
### Footer, FooterLink
|
|
895
|
+
|
|
896
|
+
Fuente: `src/components/footer/index.tsx`
|
|
897
|
+
|
|
898
|
+
**Footer**
|
|
899
|
+
- Extiende: `ComponentPropsWithoutRef<'footer'>`
|
|
900
|
+
|
|
901
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
902
|
+
| --- | --- | --- | --- | --- |
|
|
903
|
+
| `social` | `readonly Red[]` | | | |
|
|
904
|
+
| `year` | `number` | | `new Date().getFullYear()` | Año de la firma. |
|
|
905
|
+
|
|
906
|
+
**FooterLink**
|
|
907
|
+
- Extiende: `ComponentPropsWithoutRef<'a'>`
|
|
908
|
+
|
|
909
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
910
|
+
| --- | --- | --- | --- | --- |
|
|
911
|
+
| `asChild` | `boolean \| undefined` | | `false` | |
|
|
912
|
+
|
|
913
|
+
### Hero
|
|
914
|
+
|
|
915
|
+
Fuente: `src/components/hero/index.tsx`
|
|
916
|
+
|
|
917
|
+
UNO por sitio. Es la única pieza del sistema que se gasta como el botón de conversión, y por la misma razón: si hay dos, no hay ninguno.
|
|
918
|
+
|
|
919
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'section'>, 'title'>`
|
|
920
|
+
|
|
921
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
922
|
+
| --- | --- | --- | --- | --- |
|
|
923
|
+
| `action` | `ReactNode` | | | Los botones. Aquí va el único `conversion` de la pantalla. |
|
|
924
|
+
| `basePath` | `string` | | | |
|
|
925
|
+
| `description` | `ReactNode` | | | |
|
|
926
|
+
| `eyebrow` | `ReactNode` | | | Mono, versalitas, en acento. |
|
|
927
|
+
| `pose` | `"desk" \| "laptop-coffee" \| "peek" \| "surf"` | | | La pose de Tiburoncín. Sin ella el hero es un panel con texto. |
|
|
928
|
+
| `title` | `ReactNode` | sí | | |
|
|
929
|
+
|
|
930
|
+
### LinkRow
|
|
931
|
+
|
|
932
|
+
Fuente: `src/components/link-row/index.tsx`
|
|
933
|
+
|
|
934
|
+
Migrado desde `links/src/components/Card.astro`. El original escalaba la tarjeta al 102 %, subía el título un píxel y giraba y agrandaba el icono en hover — cuatro movimientos que el sistema no permite. Aquí el hover cambia el borde y el color del icono, y nada más.
|
|
935
|
+
|
|
936
|
+
- Extiende: `Omit<TarjetaProps, 'children'>`
|
|
937
|
+
|
|
938
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
939
|
+
| --- | --- | --- | --- | --- |
|
|
940
|
+
| `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. |
|
|
941
|
+
| `description` | `ReactNode` | | | |
|
|
942
|
+
| `external` | `boolean \| undefined` | | `false` | Marca el enlace como externo: añade la flecha y el `rel` seguro. |
|
|
943
|
+
| `icon` | `ReactNode` | | | Glifo SVG del destino. Nunca un emoji. |
|
|
944
|
+
| `name` | `ReactNode` | sí | | |
|
|
945
|
+
|
|
946
|
+
### Nav, NavItem
|
|
947
|
+
|
|
948
|
+
Fuente: `src/components/nav/index.tsx`
|
|
949
|
+
|
|
950
|
+
**Nav**
|
|
951
|
+
La barra del sitio: 64px, abismo al 86 % y desenfoque de 14px detrás.
|
|
952
|
+
|
|
953
|
+
- Extiende: `ComponentPropsWithoutRef<'header'>`
|
|
954
|
+
|
|
955
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
956
|
+
| --- | --- | --- | --- | --- |
|
|
957
|
+
| `actions` | `ReactNode` | | | Acciones a la derecha: conversión, cambio de tema, buscar. |
|
|
958
|
+
| `brand` | `ReactNode` | | | El logo, a la izquierda. |
|
|
959
|
+
|
|
960
|
+
**NavItem**
|
|
961
|
+
El `./` lo pone el componente, no quien lo usa.
|
|
962
|
+
|
|
963
|
+
- Extiende: `ComponentPropsWithoutRef<'a'>`
|
|
964
|
+
|
|
965
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
966
|
+
| --- | --- | --- | --- | --- |
|
|
967
|
+
| `active` | `boolean \| undefined` | | `false` | Sección actual: bioluz con subrayado de 1px. |
|
|
968
|
+
| `asChild` | `boolean \| undefined` | | `false` | Renderiza el hijo en vez de un `<a>`, para el `Link` del enrutador. |
|
|
969
|
+
|
|
970
|
+
### NewsletterForm
|
|
971
|
+
|
|
972
|
+
Fuente: `src/components/newsletter-form/index.tsx`
|
|
973
|
+
|
|
974
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'section'>, 'title' \| 'onSubmit'>`
|
|
975
|
+
|
|
976
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
977
|
+
| --- | --- | --- | --- | --- |
|
|
978
|
+
| `basePath` | `string` | | | |
|
|
979
|
+
| `description` | `ReactNode` | | | |
|
|
980
|
+
| `disclaimer` | `ReactNode` | | | La letra pequeña. Es el «sin spam», y por eso admite cara. |
|
|
981
|
+
| `errorMessage` | `ReactNode` | | `No se pudo suscribir ese correo. Revísalo y vuelve a intentar.` | |
|
|
982
|
+
| `expresion` | `"annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink"` | | | |
|
|
983
|
+
| `fieldLabel` | `string` | | `Correo electrónico` | |
|
|
984
|
+
| `onSubmitEmail` | `(email: string) => void` | | | Se dispara con el correo ya leído del campo. |
|
|
985
|
+
| `placeholder` | `string` | | `tu@correo.dev` | |
|
|
986
|
+
| `state` | `"error" \| "reposo" \| "enviando" \| "exito"` | | `reposo` | |
|
|
987
|
+
| `submitLabel` | `string` | | `Suscribirme` | |
|
|
988
|
+
| `successMessage` | `ReactNode` | | `Ya estás dentro. Te llega un correo cada dos semanas, y nada más.` | |
|
|
989
|
+
| `title` | `ReactNode` | sí | | |
|
|
990
|
+
|
|
991
|
+
### PageHeader
|
|
992
|
+
|
|
993
|
+
Fuente: `src/components/page-header/index.tsx`
|
|
994
|
+
|
|
995
|
+
Una sola cabecera en dos escalas, no dos componentes.
|
|
996
|
+
|
|
997
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'header'>, 'title'> & VariantProps<typeof cabecera>`
|
|
998
|
+
|
|
999
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1000
|
+
| --- | --- | --- | --- | --- |
|
|
1001
|
+
| `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. |
|
|
1002
|
+
| `as` | `"h1" \| "h2"` | | `h1` | Nivel del titular. `h1` salvo que la página ya tenga uno. |
|
|
1003
|
+
| `description` | `ReactNode` | | | |
|
|
1004
|
+
| `eyebrow` | `ReactNode` | | | Mono, versalitas, en acento. Es la sección a la que pertenece la página. |
|
|
1005
|
+
| `size` | `"display" \| "page"` | | `page` | |
|
|
1006
|
+
| `title` | `ReactNode` | sí | | |
|
|
1007
|
+
|
|
1008
|
+
### SidebarItem, SidebarNav
|
|
1009
|
+
|
|
1010
|
+
Fuente: `src/components/sidebar-nav/index.tsx`
|
|
1011
|
+
|
|
1012
|
+
**SidebarItem**
|
|
1013
|
+
La barra lateral del admin del blog.
|
|
1014
|
+
|
|
1015
|
+
- Extiende: `ComponentPropsWithoutRef<'a'>`
|
|
1016
|
+
|
|
1017
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1018
|
+
| --- | --- | --- | --- | --- |
|
|
1019
|
+
| `active` | `boolean \| undefined` | | `false` | |
|
|
1020
|
+
| `asChild` | `boolean \| undefined` | | `false` | |
|
|
1021
|
+
| `badge` | `ReactNode` | | | Contador a la derecha: borradores pendientes, media sin usar. |
|
|
1022
|
+
|
|
1023
|
+
**SidebarNav**
|
|
1024
|
+
- Extiende: `ComponentPropsWithoutRef<'nav'>`
|
|
1025
|
+
|
|
1026
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1027
|
+
| --- | --- | --- | --- | --- |
|
|
1028
|
+
| `branch` | `ReactNode` | | | |
|
|
1029
|
+
| `version` | `ReactNode` | | | Versión y rama, al pie. |
|
|
1030
|
+
|
|
1031
|
+
### Stat
|
|
1032
|
+
|
|
1033
|
+
Fuente: `src/components/stat/index.tsx`
|
|
1034
|
+
|
|
1035
|
+
Una métrica grande: el número en la escala `stat` y su nombre debajo.
|
|
1036
|
+
|
|
1037
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'div'>, 'title'>`
|
|
1038
|
+
|
|
1039
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1040
|
+
| --- | --- | --- | --- | --- |
|
|
1041
|
+
| `label` | `ReactNode` | sí | | Qué se está contando. Va en mono versalitas. |
|
|
1042
|
+
| `progress` | `number` | | | Con `progress`, la métrica se lee como avance y añade la barra. |
|
|
1043
|
+
| `tone` | `"neutral" \| "alerta"` | | `neutral` | `alerta` solo cuando el número ES el problema. |
|
|
1044
|
+
| `value` | `ReactNode` | sí | | El número, ya formateado. La librería no impone locale. |
|
|
1045
|
+
|
|
1046
|
+
### TalkCard
|
|
1047
|
+
|
|
1048
|
+
Fuente: `src/components/talk-card/index.tsx`
|
|
1049
|
+
|
|
1050
|
+
- Extiende: `Omit<TarjetaProps, 'children' \| 'title'>`
|
|
1051
|
+
|
|
1052
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1053
|
+
| --- | --- | --- | --- | --- |
|
|
1054
|
+
| `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. |
|
|
1055
|
+
| `date` | `ReactNode` | | | |
|
|
1056
|
+
| `dateTime` | `string` | | | |
|
|
1057
|
+
| `event` | `ReactNode` | sí | | Dónde se dio: la conferencia, el meetup, el equipo. |
|
|
1058
|
+
| `location` | `ReactNode` | | | |
|
|
1059
|
+
| `status` | `ReactNode` | | | Etiqueta corta de estado: «con vídeo», «próxima», «solo audio». |
|
|
1060
|
+
| `title` | `ReactNode` | sí | | |
|
|
1061
|
+
|
|
1062
|
+
### TableOfContents
|
|
1063
|
+
|
|
1064
|
+
Fuente: `src/components/toc/index.tsx`
|
|
1065
|
+
|
|
1066
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'nav'>, 'children'>`
|
|
1067
|
+
|
|
1068
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1069
|
+
| --- | --- | --- | --- | --- |
|
|
1070
|
+
| `activeHref` | `string` | | | Ancla de la sección visible. |
|
|
1071
|
+
| `items` | `readonly Entrada[]` | sí | | |
|
|
1072
|
+
| `linkAsChild` | `(props: { href: string; children: ReactNode; }) => ReactNode` | | | |
|
|
1073
|
+
|
|
1074
|
+
## Marca
|
|
1075
|
+
|
|
1076
|
+
Se importan de `@eduardoalvarez/arrecife` o `@eduardoalvarez/arrecife/brand`. 4 exportaciones.
|
|
1077
|
+
|
|
1078
|
+
### Isotipo
|
|
1079
|
+
|
|
1080
|
+
Fuente: `src/brand/isotipo.tsx`
|
|
1081
|
+
|
|
1082
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'img'>, 'src' \| 'alt'>`
|
|
1083
|
+
|
|
1084
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1085
|
+
| --- | --- | --- | --- | --- |
|
|
1086
|
+
| `alt` | `string` | | | Texto alternativo. Vacío cuando el isotipo acompaña a un texto que ya lo nombra. |
|
|
1087
|
+
| `basePath` | `string` | | `RUTA_ASSETS` | |
|
|
1088
|
+
| `sobre` | `"oscuro" \| "claro"` | | `oscuro` | Sobre qué fondo se monta. Es obligatorio decidirlo, aunque tenga default: el cuerpo de la aleta es casi negro, así que la variante de dos azules desaparece sobre abismo. Al ser una prop, la regla deja de ser algo que recordar. |
|
|
1089
|
+
|
|
1090
|
+
### Logo
|
|
1091
|
+
|
|
1092
|
+
Fuente: `src/brand/logo.tsx`
|
|
1093
|
+
|
|
1094
|
+
El wordmark sale de `naming.wordmark`, no de una cadena escrita a mano, y siempre dice «Eduardo Álvarez». La mascota se llama Tiburoncín y no aparece escrita dentro del logo: no hay ninguna prop que permita cambiar el texto.
|
|
1095
|
+
|
|
1096
|
+
- Extiende: `Omit<ComponentPropsWithoutRef<'span'>, 'children'>`
|
|
1097
|
+
|
|
1098
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1099
|
+
| --- | --- | --- | --- | --- |
|
|
1100
|
+
| `basePath` | `string` | | `RUTA_ASSETS` | |
|
|
1101
|
+
| `sobre` | `"oscuro" \| "claro"` | | `oscuro` | |
|
|
1102
|
+
| `soloIsotipo` | `boolean \| undefined` | | `false` | Oculta el wordmark y deja solo la aleta, para barras muy estrechas. |
|
|
1103
|
+
|
|
1104
|
+
### Mascota, CaraDeMascota
|
|
1105
|
+
|
|
1106
|
+
Fuente: `src/brand/mascota.tsx`
|
|
1107
|
+
|
|
1108
|
+
**Mascota**
|
|
1109
|
+
Tiburoncín de cuerpo entero.
|
|
1110
|
+
|
|
1111
|
+
- Extiende: `Base`
|
|
1112
|
+
|
|
1113
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1114
|
+
| --- | --- | --- | --- | --- |
|
|
1115
|
+
| `alt` | `string` | | | Texto alternativo. Vacío por defecto: la mascota es ilustración y el texto que la acompaña ya dice lo que hay que saber. Se rellena solo cuando la imagen aporta información que no está escrita al lado. |
|
|
1116
|
+
| `basePath` | `string` | | `RUTA_ASSETS` | |
|
|
1117
|
+
| `pose` | `"desk" \| "laptop-coffee" \| "peek" \| "surf"` | sí | | |
|
|
1118
|
+
|
|
1119
|
+
**CaraDeMascota**
|
|
1120
|
+
La cabeza de Tiburoncín, con expresión.
|
|
1121
|
+
|
|
1122
|
+
- Extiende: `Base`
|
|
1123
|
+
|
|
1124
|
+
| prop | tipo | req. | defecto | qué hace |
|
|
1125
|
+
| --- | --- | --- | --- | --- |
|
|
1126
|
+
| `alt` | `string` | | | Texto alternativo. Vacío por defecto: la mascota es ilustración y el texto que la acompaña ya dice lo que hay que saber. Se rellena solo cuando la imagen aporta información que no está escrita al lado. |
|
|
1127
|
+
| `basePath` | `string` | | `RUTA_ASSETS` | |
|
|
1128
|
+
| `expresion` | `"annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink"` | sí | | |
|
|
1129
|
+
|
|
1130
|
+
## Exportaciones que no son componentes
|
|
1131
|
+
|
|
1132
|
+
La raíz reexporta todo lo de `./tokens` y `./brand` por conveniencia. Cada uno
|
|
1133
|
+
aparece una sola vez, en la subruta más específica que lo publica: si el código
|
|
1134
|
+
no monta React, esa subruta es la que hay que importar.
|
|
1135
|
+
|
|
1136
|
+
### `@eduardoalvarez/arrecife/tokens`
|
|
1137
|
+
|
|
1138
|
+
| export | tipo | qué es |
|
|
1139
|
+
| --- | --- | --- |
|
|
1140
|
+
| `brand` | `{ readonly body: "#3E7CB1"; readonly spots: "#C2D7E7"; readonly hull: "#0B1524"; }` | Marca — iguales en los dos modos. |
|
|
1141
|
+
| `colors` | `{ dark, light }` | |
|
|
1142
|
+
| `control` | `{ readonly sm: 14; readonly md: 22; readonly lg: 30; readonly icon: 42; }` | Controles, del documento: `sm 8/14 · md 12/22 · lg 15/30 · icono 42×42`. |
|
|
1143
|
+
| `dark` | `{ background, surface, surfaceRaised, border, hairline, hairlineHover, textPrimary, textSecondary, textMuted, accent, accentHover, accentOn, warm, warmHover, warmOn, success, warning, error }` | Modo oscuro (primario). Contrastes medidos sobre `background` #091319. |
|
|
1144
|
+
| `fonts` | `{ display, sans, mono }` | |
|
|
1145
|
+
| `gradient` | `{ dark, light }` | |
|
|
1146
|
+
| `light` | `{ background, surface, surfaceRaised, border, hairline, hairlineHover, textPrimary, textSecondary, textMuted, accent, accentHover, accentOn, warm, warmHover, warmOn, success, warning, error }` | Modo claro. Contrastes medidos sobre `background` #F6F2EA. `background` es blanco CÁLIDO: nunca #FFF como fondo de página. |
|
|
1147
|
+
| `limits` | `{ readonly minScreenPx: 13; readonly minPrintPt: 12; readonly measure: "68ch"; }` | Límites duros de legibilidad. |
|
|
1148
|
+
| `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. |
|
|
1149
|
+
| `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. |
|
|
1150
|
+
| `radius` | `{ readonly chip: 6; readonly control: 10; readonly card: 14; readonly panel: 16; readonly pill: 999; }` | |
|
|
1151
|
+
| `shadow` | `{ readonly standard: "0 1px 2px rgba(0, 0, 0, 0.35)"; }` | Un solo nivel. No hay escala de elevación. |
|
|
1152
|
+
| `sintaxis` | `{ fondo, identificador, literal, palabraClave, comentario, invalido }` | La paleta del resaltado de sintaxis. |
|
|
1153
|
+
| `size` | `{ readonly nav: 64; readonly content: 760; readonly wide: 1180; }` | |
|
|
1154
|
+
| `spacing` | `{ readonly xs: 8; readonly sm: 12; readonly md: 16; readonly lg: 26; readonly xl: 40; readonly section: 96; }` | |
|
|
1155
|
+
| `tagline` | `{ largo, corto, en }` | |
|
|
1156
|
+
| `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. |
|
|
1157
|
+
| `typeScale` | `{ display, stat, h1, h2, h3, body, lead, ui, label, tag, chip, meta, eyebrow }` | |
|
|
1158
|
+
|
|
1159
|
+
Tipos (12): `BrandToken`, `ColorMode`, `ColorToken`, `ControlToken`, `FontToken`, `GradientToken`, `RadiusToken`, `SintaxisToken`, `SizeToken`, `SpacingToken`, `Tokens`, `TypeScaleToken`.
|
|
1160
|
+
|
|
1161
|
+
### `@eduardoalvarez/arrecife/brand`
|
|
1162
|
+
|
|
1163
|
+
| export | tipo | qué es |
|
|
1164
|
+
| --- | --- | --- |
|
|
1165
|
+
| `aletas` | `{ readonly color: "fin.png"; readonly espuma: "fin-foam.png"; }` | La aleta, en sus dos variantes. |
|
|
1166
|
+
| `caras` | `{ annoyed, confused, hearts, laughing, shades, waiting, wink }` | Las caras. Solo se usan en estados vacíos, confirmaciones, errores, progreso de curso y celebración — nunca en hero, precios, servicios, contacto ni CV. Por eso `EmptyState` recibe una cara y `PageHeader` no. |
|
|
1167
|
+
| `listaCaras` | `readonly ("annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink")[]` | |
|
|
1168
|
+
| `listaPoses` | `readonly ("desk" \| "laptop-coffee" \| "peek" \| "surf")[]` | |
|
|
1169
|
+
| `poses` | `{ readonly desk: "pose-desk.png"; readonly 'laptop-coffee': "pose-laptop-coffee.png"; readonly peek: "pose-peek.png"; readonly surf: "pose-surf.png"; }` | Poses de cuerpo entero. |
|
|
1170
|
+
| `RUTA_ASSETS` | `"/brand"` | Dónde se sirven los PNG. Por defecto `/brand`, que es donde ya viven en los cinco proyectos (`public/brand/`), así que no hay nada que configurar. |
|
|
1171
|
+
| `usoDeCara` | `{ wink, waiting, laughing, shades, hearts, confused, annoyed }` | El uso asignado de cada cara, del inventario del manual. |
|
|
1172
|
+
|
|
1173
|
+
Tipos (8): `Aleta`, `Cara`, `CaraDeMascotaProps`, `Fondo`, `IsotipoProps`, `LogoProps`, `MascotaProps`, `Pose`.
|
|
1174
|
+
|
|
1175
|
+
### `@eduardoalvarez/arrecife/shiki`
|
|
1176
|
+
|
|
1177
|
+
| export | tipo | qué es |
|
|
1178
|
+
| --- | --- | --- |
|
|
1179
|
+
| `arrecife` | `TemaShiki` | |
|
|
1180
|
+
|
|
1181
|
+
Tipos (1): `TemaShiki`.
|
|
1182
|
+
|
|
1183
|
+
### `@eduardoalvarez/arrecife/og`
|
|
1184
|
+
|
|
1185
|
+
| export | tipo | qué es |
|
|
1186
|
+
| --- | --- | --- |
|
|
1187
|
+
| `OG` | `{ readonly width: 1200; readonly height: 630; readonly margen: 64; readonly mascota: 430; readonly aletaFirma: 34; readonly reservaMascota: 560; }` | El lienzo y la retícula. Son las medidas de producción. |
|
|
1188
|
+
| `plantillaArticulo` | `(datos: DatosArticulo): NodoSatori` | Artículo · degradado 145° sobre abismo, categoría y lectura en arena. |
|
|
1189
|
+
| `plantillaBase` | `(opciones: { modo: "oscuro" \| "claro"; fondo: string; eyebrow: { texto: string; color: string; }; title: string; bajada?: string \| undefined; firma: string; firmaColor: string; base: string; mascota?: NodoSatori \| null \| undefined; mascotaIzquierda?: boolean \| undefined; tinta: string; tintaSecundaria: string; }): NodoSatori` | |
|
|
1190
|
+
| `plantillaCharla` | `(datos: DatosCharla): NodoSatori` | Charla · eyebrow en bioluz con evento y año, pose sangrando por la esquina. |
|
|
1191
|
+
| `plantillaCurso` | `(datos: DatosCurso): NodoSatori` | Curso · LA ÚNICA PLANTILLA EN CLARO. |
|
|
1192
|
+
| `plantillaDefecto` | `(datos?: DatosDefecto): NodoSatori` | Por defecto · la excepción declarada del documento. |
|
|
1193
|
+
|
|
1194
|
+
Tipos (6): `DatosArticulo`, `DatosBase`, `DatosCharla`, `DatosCurso`, `DatosDefecto`, `NodoSatori`.
|
|
1195
|
+
|
|
1196
|
+
### `@eduardoalvarez/arrecife`
|
|
1197
|
+
|
|
1198
|
+
| export | tipo | qué es |
|
|
1199
|
+
| --- | --- | --- |
|
|
1200
|
+
| `alertVariants` | `(props?: (ConfigVariants<{ variant: { accent: string; success: string; warning: string; error: string; }; enfasis: { sutil: string; fuerte: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1201
|
+
| `avatarVariants` | `(props?: (ConfigVariants<{ size: { sm: string; md: string; lg: string; xl: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1202
|
+
| `badgeVariants` | `(props?: (ConfigVariants<{ variant: { neutral: string; accent: string; warm: string; success: string; warning: string; error: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1203
|
+
| `buttonVariants` | `(props?: (ConfigVariants<{ variant: { primary: string[]; conversion: string; secondary: string[]; tertiary: string[]; }; size: { sm: string; md: string; lg: string; icon: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1204
|
+
| `categoryBadgeVariants` | `(props?: (ConfigVariants<{ active: { false: string; true: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1205
|
+
| `cn` | `(...inputs: ClassValue[]): string` | |
|
|
1206
|
+
| `HOVER_TARJETA` | `"transition-standard hover:border-hairline-hover"` | El hover de la regla 6: solo el borde. Se aplica donde la tarjeta es pulsable. |
|
|
1207
|
+
| `social` | `typeof import("src/lib/social")` | |
|
|
1208
|
+
| `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. |
|
|
1209
|
+
| `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` | |
|
|
1210
|
+
|
|
1211
|
+
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`.
|
|
1212
|
+
|
|
1213
|
+
# Dónde mirar si esto no basta
|
|
1214
|
+
|
|
1215
|
+
- Storybook publica cada componente con sus stories y la tabla de props generada
|
|
1216
|
+
desde los tipos.
|
|
1217
|
+
- `README.md` del repo: el porqué de cada decisión, la tabla de correcciones de
|
|
1218
|
+
contraste y el ciclo de publicación.
|
|
1219
|
+
- `docs/design-system.md` y `docs/manual-de-marca.md`: los documentos de
|
|
1220
|
+
identidad, consultables con `grep`.
|
|
1221
|
+
- `docs/decisiones.md`: los quince puntos donde el código y el documento no
|
|
1222
|
+
decían lo mismo, con la resolución de cada uno.
|
|
1223
|
+
- `AGENTS.md`: para trabajar dentro del repo de la librería.
|