@eduardoalvarez/arrecife 0.1.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 +55 -0
- package/LICENSE +21 -0
- package/README.md +520 -0
- package/assets/brand/face-annoyed.png +0 -0
- package/assets/brand/face-confused.png +0 -0
- package/assets/brand/face-hearts.png +0 -0
- package/assets/brand/face-laughing.png +0 -0
- package/assets/brand/face-shades.png +0 -0
- package/assets/brand/face-waiting.png +0 -0
- package/assets/brand/face-wink.png +0 -0
- package/assets/brand/fin-foam.png +0 -0
- package/assets/brand/fin.png +0 -0
- package/assets/brand/pose-desk.png +0 -0
- package/assets/brand/pose-laptop-coffee.png +0 -0
- package/assets/brand/pose-peek.png +0 -0
- package/assets/brand/pose-surf.png +0 -0
- package/dist/brand/index.cjs +228 -0
- package/dist/brand/index.d.cts +63 -0
- package/dist/brand/index.d.ts +63 -0
- package/dist/brand/index.js +3 -0
- package/dist/catalogo-Du5ID-Hi.d.cts +77 -0
- package/dist/catalogo-Du5ID-Hi.d.ts +77 -0
- package/dist/chunk-22OXRTAL.js +22 -0
- package/dist/chunk-DHF63QNM.js +231 -0
- package/dist/chunk-E3OMP2DL.js +36 -0
- package/dist/chunk-ZL5X6XO7.js +102 -0
- package/dist/index.cjs +3191 -0
- package/dist/index.d.cts +1024 -0
- package/dist/index.d.ts +1024 -0
- package/dist/index.js +2624 -0
- package/dist/og/index.cjs +322 -0
- package/dist/og/index.d.cts +164 -0
- package/dist/og/index.d.ts +164 -0
- package/dist/og/index.js +242 -0
- package/dist/shiki/index.cjs +127 -0
- package/dist/shiki/index.d.cts +16 -0
- package/dist/shiki/index.d.ts +16 -0
- package/dist/shiki/index.js +95 -0
- package/dist/tokens/index.cjs +263 -0
- package/dist/tokens/index.d.cts +623 -0
- package/dist/tokens/index.d.ts +623 -0
- package/dist/tokens/index.js +2 -0
- package/dist/tokens/theme.css +266 -0
- package/package.json +128 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-08-27)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### 🚀 Novedades
|
|
7
|
+
|
|
8
|
+
* **brand:** catálogo de Tiburoncín, Logo, Isotipo, Mascota y galería ([c0d616b](https://github.com/Proskynete/arrecife/commit/c0d616b0478005105e2fa6df474ee20eb1b14bb8))
|
|
9
|
+
* **ci:** comprobar la superficie publicada ([6c8e715](https://github.com/Proskynete/arrecife/commit/6c8e7154c734e8cc64f107d8ee3d3d878b77eb46))
|
|
10
|
+
* **components:** AudioPlayer migrado desde eduardoalvarez.dev ([b4c5345](https://github.com/Proskynete/arrecife/commit/b4c53459ad17bc97674f8a9108f9abf0f982407c))
|
|
11
|
+
* **components:** AuthorCard, Stat, TableOfContents y SidebarNav ([15c64c7](https://github.com/Proskynete/arrecife/commit/15c64c731e3da10356602799e7ea4f8bf508071f))
|
|
12
|
+
* **components:** EmptyState, Breadcrumb, Nav, Footer, Hero y NewsletterForm ([9ef81dd](https://github.com/Proskynete/arrecife/commit/9ef81dddd0a1368313a4f596ecff52a336c75f02))
|
|
13
|
+
* **components:** tarjetas, bloque de código, cita y cabecera de página ([6743361](https://github.com/Proskynete/arrecife/commit/6743361f349e46dbf7ab1e07942cf100c92b12fd))
|
|
14
|
+
* **og:** las cuatro plantillas con la retícula del documento ([68b9ebd](https://github.com/Proskynete/arrecife/commit/68b9ebdead8c69b788d29e5e5ab430389b600f0e))
|
|
15
|
+
* **primitives:** capa base sobre shadcn y Radix ([81855ee](https://github.com/Proskynete/arrecife/commit/81855eea62e36f4b07b0dc170dad577313eb35a5))
|
|
16
|
+
* **primitives:** Code en línea, skeleton con barrido y las escalas nuevas en Text ([9f742a3](https://github.com/Proskynete/arrecife/commit/9f742a30fae68ba149dc165030dffd59d96ec68c))
|
|
17
|
+
* **shiki:** el tema de resaltado generado desde tokens ([af5e795](https://github.com/Proskynete/arrecife/commit/af5e7956ca914376f265b80c225e24b21d8d653c))
|
|
18
|
+
* **tokens:** escalas nuevas, controles, degradados y paleta de sintaxis ([a17a508](https://github.com/Proskynete/arrecife/commit/a17a5081d4d112516e16f6961db502f5444a294c))
|
|
19
|
+
* **tokens:** fuente única, generador de Tailwind y guarda de pureza ([f6eb94b](https://github.com/Proskynete/arrecife/commit/f6eb94bb6ad47548fce9d5f39a47ef86130d9ae4))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### 🐛 Correcciones
|
|
23
|
+
|
|
24
|
+
* **a11y:** cursor-pointer en todo lo que se pulsa ([70b0784](https://github.com/Proskynete/arrecife/commit/70b07849311024874e6127e617db71c3833de2b8))
|
|
25
|
+
* **alert:** tinte al 8%, glifos mono y la segunda receta ([b90affc](https://github.com/Proskynete/arrecife/commit/b90affc5106c802ef864056aaeeca29dd942cd23))
|
|
26
|
+
* **audio-player:** los tamaños del documento ([4e68362](https://github.com/Proskynete/arrecife/commit/4e6836247e3e811194522fdaa99d91c8a7a231d9))
|
|
27
|
+
* **badge:** separar categoría, estado y métrica ([1c0239a](https://github.com/Proskynete/arrecife/commit/1c0239a472f4703c0e5859db923f2b12467cf463))
|
|
28
|
+
* **button:** cuatro variantes, tamaños del documento y fuera el de peligro ([07d209f](https://github.com/Proskynete/arrecife/commit/07d209fa5b969982b9e2a642e0d6ed8bd75af822))
|
|
29
|
+
* **cards:** padding de 26, categoría en los tags y la superficie que no entra ([8f30fda](https://github.com/Proskynete/arrecife/commit/8f30fdad3109a836176980629204657af43a39bd))
|
|
30
|
+
* **ci:** la primera versión es 0.1.0 y feat sube la minor ([2ed3859](https://github.com/Proskynete/arrecife/commit/2ed385956dda5485ce23ff7e65818d175bc46607))
|
|
31
|
+
* **cn:** derivar de tokens las escalas que tailwind-merge debe conocer ([2d6c4e5](https://github.com/Proskynete/arrecife/commit/2d6c4e503346413d6d2789a768698ff0beeab836))
|
|
32
|
+
* **storybook:** .storybook entraba al tsconfig pero no se compilaba ([2885830](https://github.com/Proskynete/arrecife/commit/2885830b2fb0982cf657903cc20da998efba7273))
|
|
33
|
+
* **storybook:** el contenedor de docs también toma el fondo del token ([beeb434](https://github.com/Proskynete/arrecife/commit/beeb434631d49571554420c98150143811f1bd40))
|
|
34
|
+
* **storybook:** el wrapper interno de docs, que era el que pintaba blanco ([edda8c6](https://github.com/Proskynete/arrecife/commit/edda8c65ff05d1e3ca815d52d0be4cbcad91ad1f))
|
|
35
|
+
* **storybook:** forzar el fondo del contenedor de docs ([11fcfc7](https://github.com/Proskynete/arrecife/commit/11fcfc730f595926f04e69f171feb948b683d34a))
|
|
36
|
+
* **storybook:** tema de docs por la API y no a base de CSS a la contra ([86f80cf](https://github.com/Proskynete/arrecife/commit/86f80cfe7e26822a96169559824691f7164634da))
|
|
37
|
+
* **storybook:** tematizar el manager, que es lo que quedaba blanco ([d1643a9](https://github.com/Proskynete/arrecife/commit/d1643a9f3e648503a20fe9e016dcf3481d77c159))
|
|
38
|
+
* **tsconfig:** incluir .storybook por include y no por files ([ca8a8d6](https://github.com/Proskynete/arrecife/commit/ca8a8d6e26eba6e30e4bdd4019cc6673ef197542))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### 📚 Documentación
|
|
42
|
+
|
|
43
|
+
* cómo se consume, qué se decidió y por qué ([0f9654f](https://github.com/Proskynete/arrecife/commit/0f9654ff8ae25ba9d9df0a2dbb422fc8abf05e55))
|
|
44
|
+
* incorporar al repo los documentos de identidad y las decisiones ([ca9273f](https://github.com/Proskynete/arrecife/commit/ca9273fa821d9314f54d8d821b4ee3dfeb9446df))
|
|
45
|
+
* **readme:** cómo publicar una versión ([df9702b](https://github.com/Proskynete/arrecife/commit/df9702b71ba22879afcfe5a5e5513abe59af38eb))
|
|
46
|
+
* **readme:** la fase 5, las subrutas nuevas y la tercera corrección de contraste ([871cee9](https://github.com/Proskynete/arrecife/commit/871cee92a8fef7ea33264b869140d45cd6f4d3b2))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### 🚀 CI/CD
|
|
50
|
+
|
|
51
|
+
* no correr CI y lint dos veces por PR ([010ca3c](https://github.com/Proskynete/arrecife/commit/010ca3c249bcbaa3558ec30ff173fb053f56b56a))
|
|
52
|
+
* pipeline de publicación en npm y las comprobaciones de PR ([930da3d](https://github.com/Proskynete/arrecife/commit/930da3d91199eff38135e2a07456b02cd935c8a1))
|
|
53
|
+
* release-please y publicación de confianza con OIDC ([f005b8c](https://github.com/Proskynete/arrecife/commit/f005b8c3a0dfc0c42015140f52ebe4c37e8507e5))
|
|
54
|
+
|
|
55
|
+
## Changelog
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eduardo Esteban Álvarez Castañeda
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
# Arrecife
|
|
2
|
+
|
|
3
|
+
Librería de componentes de la identidad visual de Eduardo Álvarez.
|
|
4
|
+
`@eduardoalvarez/arrecife` · React 19 · TypeScript · shadcn/ui · Storybook · tsup.
|
|
5
|
+
|
|
6
|
+
## Los documentos de identidad
|
|
7
|
+
|
|
8
|
+
`docs/design-system.md` y `docs/manual-de-marca.md` son la extracción de los dos
|
|
9
|
+
canvas de Claude Design, en el repo para poder hacer `grep` y para versionarlos.
|
|
10
|
+
El canvas sigue siendo la fuente; esto es la copia consultable.
|
|
11
|
+
|
|
12
|
+
Está aquí por un motivo concreto: la paleta de resaltado vivía escrita a mano en
|
|
13
|
+
un proyecto con un `#E05252` que este README declara incorrecto desde hace
|
|
14
|
+
meses, y nadie lo vio porque el documento no era `grep`-able desde el código.
|
|
15
|
+
|
|
16
|
+
`docs/decisiones.md` es la otra mitad: los quince puntos donde el código y el
|
|
17
|
+
documento no dicen lo mismo, con la resolución y el motivo de cada uno.
|
|
18
|
+
|
|
19
|
+
## La restricción que manda sobre todo lo demás
|
|
20
|
+
|
|
21
|
+
`src/tokens/` no importa nada: ni React, ni componentes, ni CSS de terceros. Es el
|
|
22
|
+
único subpaquete que pueden consumir los cinco proyectos, incluido un generador
|
|
23
|
+
de OG con Satori y un sitio Astro que no monta React. Si un token termina
|
|
24
|
+
dependiendo de un componente, la librería dejó de ser portable.
|
|
25
|
+
|
|
26
|
+
No es documentación: `pnpm check:tokens` lo verifica en cada build y ESLint lo
|
|
27
|
+
dice en el editor.
|
|
28
|
+
|
|
29
|
+
## Salida de Tailwind
|
|
30
|
+
|
|
31
|
+
Una sola fuente, `src/tokens/tokens.ts`. Una salida generada,
|
|
32
|
+
`dist/tokens/theme.css`, con `@theme` para Tailwind v4. La genera
|
|
33
|
+
`scripts/build-tokens.mjs`; no se edita a mano y se regenera en cada build.
|
|
34
|
+
|
|
35
|
+
Decisión de la Fase 0: **solo v4**. El portfolio (`eduardoalvarez.dev`) migra de
|
|
36
|
+
Tailwind v3 a v4 antes de consumir Arrecife. Si esa migración se atrasa, volver a
|
|
37
|
+
publicar el preset de v3 es añadir un emisor más a `build-tokens.mjs` que lea el
|
|
38
|
+
mismo objeto `tokens`: la fuente no cambia.
|
|
39
|
+
|
|
40
|
+
### Consumo
|
|
41
|
+
|
|
42
|
+
```css
|
|
43
|
+
@import "tailwindcss";
|
|
44
|
+
@import "@eduardoalvarez/arrecife/tokens/theme.css";
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
El modo oscuro es el primario y es el default. Un proyecto en modo claro declara
|
|
48
|
+
`data-theme="light"` en `<html>`; uno oscuro no necesita declarar nada.
|
|
49
|
+
|
|
50
|
+
Las familias tipográficas se declaran por nombre. Cada proyecto carga Bricolage
|
|
51
|
+
Grotesque, Geist y JetBrains Mono como prefiera: la librería no impone cómo.
|
|
52
|
+
|
|
53
|
+
### Mapa de tokens a utilidades
|
|
54
|
+
|
|
55
|
+
| Token | Custom property | Utilidad |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| `colors[modo].surfaceRaised` | `--color-surface-raised` | `bg-surface-raised` |
|
|
58
|
+
| `brand.hull` | `--color-brand-hull` | `bg-brand-hull` |
|
|
59
|
+
| `typeScale.h1` | `--text-h1` | `text-h1` (arrastra line-height, weight y tracking) |
|
|
60
|
+
| `fonts.display` | `--font-display` | `font-display` |
|
|
61
|
+
| `radius.card` | `--radius-card` | `rounded-card` |
|
|
62
|
+
| `spacing.lg` | `--spacing-lg` | `p-lg`, `gap-lg`, `mb-lg` |
|
|
63
|
+
| `control.md` | `--spacing-control-md` | `px-control-md` (padding de botón) |
|
|
64
|
+
| `control.icon` | `--spacing-control-icon` | `size-control-icon` (botón de icono 42×42) |
|
|
65
|
+
| `gradient[modo].hero` | `--gradient-hero` | `degradado-hero` (utilidad, sigue el modo) |
|
|
66
|
+
| `size.nav` | `--spacing-nav` | `h-nav` |
|
|
67
|
+
| `size.content` / `size.wide` | `--container-content` / `--container-wide` | `max-w-content` / `max-w-wide` |
|
|
68
|
+
| `limits.measure` | `--container-measure` | `max-w-measure` |
|
|
69
|
+
| `shadow.standard` | `--shadow-standard` | `shadow-standard` |
|
|
70
|
+
| `motion` | `--duration-standard`, `--ease-standard` | `duration-standard`, `ease-standard` |
|
|
71
|
+
|
|
72
|
+
El variante `light:` está disponible para los casos del modo claro invertido.
|
|
73
|
+
|
|
74
|
+
También se puede consumir el objeto en JS, sin CSS y sin React — es lo que usan
|
|
75
|
+
las plantillas de OG:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { tokens } from '@eduardoalvarez/arrecife/tokens';
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
El tema de resaltado va en otra subruta por la misma razón — se consume desde
|
|
82
|
+
`astro.config.mjs`, no desde un componente:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { arrecife } from '@eduardoalvarez/arrecife/shiki';
|
|
86
|
+
|
|
87
|
+
export default defineConfig({
|
|
88
|
+
markdown: { syntaxHighlight: 'shiki', shikiConfig: { theme: arrecife } },
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**La librería no trae Shiki.** Los proyectos ya resaltan en build con su propia
|
|
93
|
+
herramienta; lo que les faltaba no era un resaltador, era el tema. `CodeBlock`
|
|
94
|
+
sigue recibiendo el código ya resaltado, que es para lo que está escrito.
|
|
95
|
+
|
|
96
|
+
Las plantillas de OG se publican en su propia subruta por la misma razón: un
|
|
97
|
+
generador corre en un worker o en un script de build y no debe arrastrar React ni
|
|
98
|
+
un solo componente.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import satori from 'satori';
|
|
102
|
+
import { plantillaArticulo, OG } from '@eduardoalvarez/arrecife/og';
|
|
103
|
+
|
|
104
|
+
const svg = await satori(plantillaArticulo({ title, date, readingMinutes }), {
|
|
105
|
+
width: OG.width, // 1200
|
|
106
|
+
height: OG.height, // 630
|
|
107
|
+
fonts: [...],
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Son funciones puras que devuelven el árbol que Satori pinta, construido solo con
|
|
112
|
+
tokens. `dist/og/index.js` no menciona React en ninguna línea, y eso es
|
|
113
|
+
comprobable con un `grep`.
|
|
114
|
+
|
|
115
|
+
## Cómo se usa desde un proyecto
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { Text, Button, Badge, cn } from '@eduardoalvarez/arrecife';
|
|
119
|
+
import type { TextProps } from '@eduardoalvarez/arrecife';
|
|
120
|
+
|
|
121
|
+
<Text variant="eyebrow" tone="muted">charlas</Text>
|
|
122
|
+
<Text as="h2" variant="h1">Escalar con criterio</Text>
|
|
123
|
+
<Text variant="body">Se corta solo a 68ch.</Text>
|
|
124
|
+
<Text variant="ui" measure={false}>Sin corte, para una celda estrecha.</Text>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Cada componente publica su página de documentación en Storybook con la tabla de
|
|
128
|
+
props generada desde los tipos. Las de `Text`:
|
|
129
|
+
|
|
130
|
+
| prop | tipo | por defecto |
|
|
131
|
+
| --- | --- | --- |
|
|
132
|
+
| `variant` | `display · stat · h1 · h2 · h3 · body · lead · ui · label · tag · meta · chip · eyebrow` | `body` |
|
|
133
|
+
| `tone` | `primary · secondary · muted · accent · warm · success · warning · error` | `primary` |
|
|
134
|
+
| `as` | `h1 · h2 · h3 · h4 · p · span · strong · em · figcaption · caption · legend · dt · dd · li` | según `variant` |
|
|
135
|
+
| `measure` | `boolean` — corta a 68ch | `true` en `body` |
|
|
136
|
+
| `asChild` | `boolean` — renderiza el hijo, para envolver un enlace | `false` |
|
|
137
|
+
|
|
138
|
+
Comprobado empaquetando la librería con `pnpm pack` e instalándola en un proyecto
|
|
139
|
+
aparte: los tipos resuelven desde `dist/`, `./tokens` carga sin arrastrar React y
|
|
140
|
+
`./tokens/theme.css` se resuelve por subruta.
|
|
141
|
+
|
|
142
|
+
## Scripts
|
|
143
|
+
|
|
144
|
+
| | |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `pnpm build` | verifica la pureza de tokens, compila con tsup y genera `theme.css` |
|
|
147
|
+
| `pnpm typecheck` | `tsc --noEmit` |
|
|
148
|
+
| `pnpm lint` | ESLint, incluido el veto a hex literales fuera de `tokens.ts` |
|
|
149
|
+
| `pnpm check:tokens` | falla si `src/tokens/` importa algo de fuera |
|
|
150
|
+
| `pnpm test` | corre axe sobre las 161 stories, en los dos modos |
|
|
151
|
+
| `pnpm check:exports` | verifica que `dist/` tiene lo que `exports` promete |
|
|
152
|
+
| `pnpm storybook` | genera los tokens y levanta Storybook en el 6006 |
|
|
153
|
+
|
|
154
|
+
## El contraste como test, no como panel
|
|
155
|
+
|
|
156
|
+
`pnpm test` monta cada story en un Chromium real y le pasa axe con
|
|
157
|
+
`a11y: { test: 'error' }`. Corre dos veces, una por modo: un color solo falla en
|
|
158
|
+
uno de los dos, así que pasar en oscuro no prueba nada sobre el claro.
|
|
159
|
+
|
|
160
|
+
Está comprobado que no es decorativo: devolver `textMuted` claro a su valor
|
|
161
|
+
anterior tira ocho stories con «insufficient color contrast of 4.24».
|
|
162
|
+
|
|
163
|
+
Hay una sola regla desactivada, en dos stories concretas y con el motivo escrito
|
|
164
|
+
al lado: `aria-hidden-focus` en `Select`/`DropdownMenu` abiertos. Radix marca
|
|
165
|
+
`aria-hidden` todo lo que queda fuera del portal y deja el disparador dentro
|
|
166
|
+
siendo focusable; el foco está atrapado por su `FocusScope`, así que no se puede
|
|
167
|
+
tabular hasta él. Es un desacuerdo conocido entre axe y Radix.
|
|
168
|
+
|
|
169
|
+
## Correcciones de contraste
|
|
170
|
+
|
|
171
|
+
El documento de identidad medía todo contra `background`. Pero `surfaceRaised`
|
|
172
|
+
es el peor caso en los **dos** modos: en claro es más oscuro que el fondo de
|
|
173
|
+
página, en oscuro es más claro. Es donde viven menús y tabs activos.
|
|
174
|
+
|
|
175
|
+
| token | antes | ahora | motivo |
|
|
176
|
+
| --- | --- | --- | --- |
|
|
177
|
+
| `light.textMuted` | `#6B7480` | `#626A75` | 4.24 no llegaba a AA sobre papel |
|
|
178
|
+
| `light.warning` | `#9A6A12` | `#8D6111` | 4.23 no llegaba a AA sobre papel |
|
|
179
|
+
| `dark.error` | `#E05252` | `#E15757` | 4.35 sobre `surface`, que es donde va un error de formulario |
|
|
180
|
+
|
|
181
|
+
Los tres conservan tono y saturación exactos: solo cambia la luminosidad entre
|
|
182
|
+
uno y cuatro puntos. `accent` y `warm` claros no se tocan.
|
|
183
|
+
|
|
184
|
+
Token nuevo: `hairlineHover` — `#2C4D5D` en oscuro (el valor de la regla 6) y
|
|
185
|
+
`#D3C8B2` en claro, derivado de igualar el salto perceptual (ΔL\* 10.5) en vez
|
|
186
|
+
de la razón de contraste, que cerca del blanco se pasa de frenada.
|
|
187
|
+
|
|
188
|
+
`textMuted` no va nunca sobre `surfaceRaised`: en oscuro da 4.07. Los menús usan
|
|
189
|
+
`textSecondary`, que da 6.96.
|
|
190
|
+
|
|
191
|
+
### La tercera corrección: el color semántico no es color de texto sobre su tinte
|
|
192
|
+
|
|
193
|
+
Salió al implementar la receta de avisos del documento — fondo al 8 % del color
|
|
194
|
+
semántico — y la tiró la suite en modo claro, en cinco stories.
|
|
195
|
+
|
|
196
|
+
Los semánticos claros están calibrados para pasar **justo** sobre papel. Teñir el
|
|
197
|
+
fondo con ellos los hunde por debajo de AA:
|
|
198
|
+
|
|
199
|
+
| tono | sobre papel | sobre su propio tinte 8 % | `textPrimary` sobre el tinte |
|
|
200
|
+
| --- | --- | --- | --- |
|
|
201
|
+
| `accent` | 4.55 | **4.12** | 14.82 |
|
|
202
|
+
| `warm` | 4.54 | **4.11** | 14.85 |
|
|
203
|
+
| `success` | 5.80 | 5.17 | 14.63 |
|
|
204
|
+
| `warning` | 4.88 | **4.40** | 14.78 |
|
|
205
|
+
| `error` | 4.87 | **4.35** | 14.64 |
|
|
206
|
+
|
|
207
|
+
No hay alfa que lo arregle: el problema es poner el color encima de sí mismo. La
|
|
208
|
+
resolución no toca la paleta — el tinte es una **superficie**, así que el texto
|
|
209
|
+
que lleva encima es un token de texto. El color semántico se queda donde no es
|
|
210
|
+
texto: el borde y el glifo.
|
|
211
|
+
|
|
212
|
+
El 8 %, por cierto, aguanta igual o mejor sobre papel que sobre abismo (1.106 vs.
|
|
213
|
+
1.149 en acento). La sospecha de que el modo claro necesitaba una segunda tabla
|
|
214
|
+
iba al revés: el punto flojo del sistema es `error` sobre abismo, 1.067.
|
|
215
|
+
|
|
216
|
+
## Publicar una versión
|
|
217
|
+
|
|
218
|
+
**No hay pasos manuales.** El tag, el CHANGELOG y el bump de versión los hace
|
|
219
|
+
release-please a partir de los commits convencionales que ya se escriben —y que
|
|
220
|
+
`lint-pr-title` ya obliga a escribir bien.
|
|
221
|
+
|
|
222
|
+
El ciclo, entero, está en `.github/workflows/release.yml`:
|
|
223
|
+
|
|
224
|
+
1. Mergeas un PR a `main` con un título tipo `feat(badge): …`.
|
|
225
|
+
2. release-please abre —o actualiza— un PR llamado `chore: versión X.Y.Z` con el
|
|
226
|
+
bump en `package.json` y la entrada nueva del `CHANGELOG.md`. Ese PR se queda
|
|
227
|
+
abierto y se va acumulando con cada merge, así que puedes juntar varios
|
|
228
|
+
cambios en una versión.
|
|
229
|
+
3. Cuando lo mergeas, corta el tag, crea el release y dispara la publicación.
|
|
230
|
+
4. Antes de subir nada, el workflow comprueba que el tag y `package.json`
|
|
231
|
+
coinciden, y corre lint, tipos, build, la verificación de `exports` y la
|
|
232
|
+
suite completa en los dos modos.
|
|
233
|
+
|
|
234
|
+
`feat:` sube la minor y `fix:` la patch. Mientras la versión sea `0.x`, un
|
|
235
|
+
cambio que rompe sube la **minor** y no la major: eso es lo que significa el
|
|
236
|
+
`0.` — que la API todavía se puede mover sin gastar la 1.0. Está en
|
|
237
|
+
`release-please-config.json`, y ahí también está `initial-version` con el `0.1.0`
|
|
238
|
+
de la primera versión.
|
|
239
|
+
|
|
240
|
+
Cuando la API se estabilice, se sube a `1.0.0` a mano una vez y a partir de ahí
|
|
241
|
+
un `BREAKING CHANGE:` sube la major como en cualquier paquete.
|
|
242
|
+
|
|
243
|
+
### La publicación de confianza
|
|
244
|
+
|
|
245
|
+
El workflow publica con **OIDC**: GitHub emite un token que prueba «este build
|
|
246
|
+
salió de este repo y de este workflow», y npm lo cambia por permiso de
|
|
247
|
+
publicación. No hay ningún secreto de larga vida que robar, ni que rotar. De
|
|
248
|
+
paso genera la **procedencia**, que firma el paquete con un enlace verificable a
|
|
249
|
+
ese commit exacto.
|
|
250
|
+
|
|
251
|
+
Se configura una vez, en npmjs.com → el paquete → *Settings* → *Trusted
|
|
252
|
+
publisher*:
|
|
253
|
+
|
|
254
|
+
| Campo | Valor |
|
|
255
|
+
| --- | --- |
|
|
256
|
+
| Publisher | GitHub Actions |
|
|
257
|
+
| Organization or user | `Proskynete` |
|
|
258
|
+
| Repository | `arrecife` |
|
|
259
|
+
| Workflow filename | `release.yml` |
|
|
260
|
+
| Environment | `npm` |
|
|
261
|
+
|
|
262
|
+
Ese *Workflow filename* es la razón de que el release y la publicación vivan en
|
|
263
|
+
un solo archivo en vez de en un workflow reutilizable: npm casa el token contra
|
|
264
|
+
un nombre, y con `workflow_call` hay dos candidatos.
|
|
265
|
+
|
|
266
|
+
**El huevo y la gallina.** No se puede configurar un publicador de confianza en
|
|
267
|
+
un paquete que todavía no existe, así que la primera versión necesita token:
|
|
268
|
+
|
|
269
|
+
1. Crea el entorno `npm` en la configuración del repo con el secreto
|
|
270
|
+
`NPM_TOKEN` (un token de tipo *automation*).
|
|
271
|
+
2. Sube la versión y publica la primera vez. El workflow avisa en el log de que
|
|
272
|
+
está usando token.
|
|
273
|
+
3. Configura la publicación de confianza con la tabla de arriba.
|
|
274
|
+
4. **Borra el secreto `NPM_TOKEN`.** El paso que lo usa se salta solo cuando no
|
|
275
|
+
está, y OIDC toma el relevo sin tocar ni una línea del workflow.
|
|
276
|
+
|
|
277
|
+
Para probar sin gastar una versión: *Actions → Release y publicación → Run
|
|
278
|
+
workflow* con el ensayo activado. Hace todo menos publicar, no necesita token, y
|
|
279
|
+
el resumen del run lista qué archivos viajarían y cuánto pesa el tarball.
|
|
280
|
+
|
|
281
|
+
## Estado
|
|
282
|
+
|
|
283
|
+
- **Fase 1** · andamiaje, tokens y Storybook con el switch de tema. Completa.
|
|
284
|
+
- **Fase 2** · `brand/`. Completa con los PNG que ya existían.
|
|
285
|
+
- **Fase 3** · los 18 primitivos sobre shadcn/Radix, más `Text` y ocho añadidos
|
|
286
|
+
después de medir el uso real en los cinco proyectos. Completa.
|
|
287
|
+
- **Fase 4** · `AudioPlayer`, migrado. Completa.
|
|
288
|
+
- **Fase 5** · completa. `ArticleCard`, `AuthorCard`, `TalkCard`, `CourseCard`,
|
|
289
|
+
`LinkRow`, `CodeBlock`, `Blockquote`, `PageHeader`, `EmptyState`, `Breadcrumb`,
|
|
290
|
+
`Nav`, `SidebarNav`, `TableOfContents`, `Stat`, `Footer`, `Hero`,
|
|
291
|
+
`NewsletterForm`, `og/` y `shiki/`.
|
|
292
|
+
|
|
293
|
+
El criterio para decidir qué entra sigue siendo el mismo: **codifica una regla
|
|
294
|
+
de identidad, tiene dos o más consumidores, y no arrastra infraestructura del
|
|
295
|
+
proyecto.**
|
|
296
|
+
|
|
297
|
+
### `Hero` y `NewsletterForm` volvieron a entrar
|
|
298
|
+
|
|
299
|
+
Estaban fuera de la lista con un argumento escrito, y el argumento se revisó.
|
|
300
|
+
|
|
301
|
+
**`Hero`.** Se había descartado porque «el hero del portafolio y el de cursos son
|
|
302
|
+
el mismo esqueleto que una cabecera de sección, y `PageHeader` los cubre con una
|
|
303
|
+
prop de escala». Eso vale para el texto y solo para el texto. El hero del
|
|
304
|
+
documento tiene además degradado, radio de panel, texto acotado al 62 % del ancho
|
|
305
|
+
y la pose sangrando por la esquina inferior derecha — nada de lo cual cabe en una
|
|
306
|
+
prop de escala de `PageHeader`, y todo lo cual son reglas de identidad que si no
|
|
307
|
+
viven aquí se reimplementan cinco veces. Son dos piezas distintas: `PageHeader`
|
|
308
|
+
sigue siendo la cabecera de sección y va dentro de `<main>`; `Hero` es la portada
|
|
309
|
+
y va uno por sitio.
|
|
310
|
+
|
|
311
|
+
**`NewsletterForm`.** Se había descartado porque «la mitad de su código es un
|
|
312
|
+
`POST` a un endpoint que solo vive ahí: eso es infraestructura». Correcto, y por
|
|
313
|
+
eso el `POST` no está aquí. El componente es presentacional: recibe `state` y
|
|
314
|
+
emite `onSubmitEmail`, y el proyecto hace la llamada con su proveedor. Lo que sí
|
|
315
|
+
es identidad son los cuatro estados y, sobre todo, que el aviso vaya **debajo**
|
|
316
|
+
del formulario en vez de reemplazarlo — reemplazarlo es lo que rompe el caso real
|
|
317
|
+
de quien se suscribe con el correo equivocado.
|
|
318
|
+
|
|
319
|
+
`Nav`, `Footer`, `Breadcrumb` y `Hero` son composición de página y se pueden
|
|
320
|
+
discutir como piezas de librería. Entran igual: la estética CLI —el `./sección`
|
|
321
|
+
de la barra, el `~ / artículos / slug` de la ruta, la firma `$ cd ~/…` del pie—
|
|
322
|
+
es lo primero que se desincroniza cuando cinco proyectos la escriben cada uno por
|
|
323
|
+
su cuenta.
|
|
324
|
+
|
|
325
|
+
### Decisiones de la Fase 3
|
|
326
|
+
|
|
327
|
+
- **Sin `lucide-react`.** Los ocho glifos que los primitivos necesitan están
|
|
328
|
+
inline en `src/lib/glyphs.tsx`, heredan `currentColor` y miden 1em. Una
|
|
329
|
+
librería de iconos como dependencia se la come cada uno de los cinco proyectos.
|
|
330
|
+
- **Sin animaciones de entrada.** Modales, menús, tooltips y toasts aparecen
|
|
331
|
+
donde van a quedarse. La perilla del `Switch` cambia de posición sin deslizarse.
|
|
332
|
+
La única transición del sistema es `transition-standard`, que solo puede animar
|
|
333
|
+
color y borde porque así está escrita la utilidad.
|
|
334
|
+
- **Una excepción, documentada:** el spinner de `Button loading` gira. Un botón
|
|
335
|
+
cargando sin movimiento es indistinguible de uno deshabilitado; es
|
|
336
|
+
realimentación de progreso, no de estado, y va envuelto en `motion-safe`.
|
|
337
|
+
- **`Progress` exige `label`.** Una barra sin nombre accesible no dice de qué es
|
|
338
|
+
el progreso, y ninguna otra parte del componente puede deducirlo.
|
|
339
|
+
- **Cero hex literales**, incluido `Button`. La regla 2 sale con
|
|
340
|
+
`light:bg-brand-hull`, porque el casco ya era un token.
|
|
341
|
+
- **`cursor-pointer` explícito** en todo lo que se pulsa. Tailwind v4 quitó del
|
|
342
|
+
preflight el `cursor: pointer` de `button`, así que un botón sin la clase se
|
|
343
|
+
queda con la flecha del sistema. Lo llevan `Button`, `Checkbox`,
|
|
344
|
+
`RadioGroupItem`, `Switch`, `TabsTrigger`, el disparador de `Select`, los
|
|
345
|
+
cerrar de `Dialog`/`Sheet`/`Toast`, `PaginationLink`, el casco de las tarjetas
|
|
346
|
+
pulsables y los enlaces de `Nav`, `Footer` y `Breadcrumb` — que renderizan
|
|
347
|
+
`<a>` sin `href` cuando se les enchufa el `Link` de un enrutador.
|
|
348
|
+
|
|
349
|
+
Dos excepciones deliberadas. `Label` apunta a un control pero no es el control.
|
|
350
|
+
Y los **ítems de menú** de `Select` y `DropdownMenu` se quedan en
|
|
351
|
+
`cursor-default`: un menú nativo no muestra la manito, y el highlight de la
|
|
352
|
+
fila ya dice que la fila responde.
|
|
353
|
+
|
|
354
|
+
### La paleta de sintaxis
|
|
355
|
+
|
|
356
|
+
Del documento, literal: «keywords arena, strings bioluz, comments plancton,
|
|
357
|
+
identifiers espuma», sobre casco. Cuatro colores a propósito — funciones,
|
|
358
|
+
variables y tipos caen los tres en espuma, porque el sistema se comunica con
|
|
359
|
+
color y borde y no con ruido cromático. Los números y booleanos van con las
|
|
360
|
+
cadenas: el documento no los asigna, y agruparlos por «son literales» es más
|
|
361
|
+
coherente que estrenar un quinto color.
|
|
362
|
+
|
|
363
|
+
Medido sobre `brand.hull` #0B1524, todo AA:
|
|
364
|
+
|
|
365
|
+
| rol | token | contraste |
|
|
366
|
+
| --- | --- | --- |
|
|
367
|
+
| identificador | `textPrimary` | 16.42:1 |
|
|
368
|
+
| literal | `accent` | 10.05:1 |
|
|
369
|
+
| palabra clave | `warm` | 9.05:1 |
|
|
370
|
+
| comentario | `textMuted` | 5.43:1 |
|
|
371
|
+
| invalidez | `error` | 4.97:1 |
|
|
372
|
+
|
|
373
|
+
`brand.body` (#3E7CB1) no entra: el sistema lo restringe a relleno y aquí mide
|
|
374
|
+
4.2:1.
|
|
375
|
+
|
|
376
|
+
Vivía escrita a mano en `eduardoalvarez.dev/src/settings/shiki-reef.ts`, y ahí
|
|
377
|
+
dentro se había quedado un `#E05252` — justo el hex que este README dice que está
|
|
378
|
+
mal. Es el caso de libro de por qué la paleta no puede vivir dentro de un
|
|
379
|
+
proyecto: el tema se genera desde `tokens.sintaxis` y el rojo sale corregido solo.
|
|
380
|
+
|
|
381
|
+
### Temas anidables
|
|
382
|
+
|
|
383
|
+
`theme.css` emite un bloque por modo, no solo el claro. Así un subárbol puede
|
|
384
|
+
declarar el modo contrario al de la página y todo lo de dentro lo respeta.
|
|
385
|
+
|
|
386
|
+
Lo usa `CodeBlock`: `brand.hull` es «fondo de bloques de código», así que un
|
|
387
|
+
bloque es oscuro también en modo claro — y ahí `textPrimary` es casi negro. La
|
|
388
|
+
raíz del bloque declara `data-theme="dark"` y la tinta se resuelve sola. Es la
|
|
389
|
+
única isla de tema invertido del sistema, y es deliberada.
|
|
390
|
+
|
|
391
|
+
### Las tarjetas y la regla 6
|
|
392
|
+
|
|
393
|
+
`ArticleCard`, `TalkCard`, `CourseCard` y `LinkRow` comparten un casco interno
|
|
394
|
+
que no se publica, para que la regla 6 viva en un solo sitio: el hover cambia el
|
|
395
|
+
borde de `hairline` a `hairlineHover` y tiñe el título de acento. Nada más.
|
|
396
|
+
|
|
397
|
+
`LinkRow` viene de `links/src/components/Card.astro`, que escalaba la tarjeta al
|
|
398
|
+
102 %, subía el título un píxel y giraba y agrandaba el icono — cuatro
|
|
399
|
+
movimientos que el sistema no permite.
|
|
400
|
+
|
|
401
|
+
Ninguna tarjeta depende de un enrutador: por defecto renderizan un `<a href>`, y
|
|
402
|
+
`asChild` deja enchufar el `Link` de Next o de Astro.
|
|
403
|
+
|
|
404
|
+
### `AudioPlayer` — qué cambió al migrarlo
|
|
405
|
+
|
|
406
|
+
La lógica no se reescribió. Los tres modos, el reproductor flotante, los saltos
|
|
407
|
+
de ±15s, el ciclo de velocidad 1 → 1.25 → 1.5 → 1.75 → 2 y el volumen con mute
|
|
408
|
+
son los del portafolio. Lo que cambió:
|
|
409
|
+
|
|
410
|
+
**Dos dependencias que un paquete no puede tener.** `Icon` del portafolio pasó a
|
|
411
|
+
`src/lib/glyphs.tsx` con los trazados idénticos; `trackEvent` pasó a la prop
|
|
412
|
+
`onFirstPlay`, que sigue disparándose una sola vez por carga.
|
|
413
|
+
|
|
414
|
+
**Un cambio de API.** `compact`/`banner` como dos booleanos pasaron a
|
|
415
|
+
`mode="full" | "compact" | "banner"`, que es el vocabulario con el que ya se
|
|
416
|
+
describían los tres modos. Hay que tocar las llamadas del portafolio en la Fase 6.
|
|
417
|
+
|
|
418
|
+
**Tres animaciones que el sistema no permite.** La onda ya no anima `scaleY` — las
|
|
419
|
+
barras siguen distinguiendo reproducción de pausa por opacidad. El flotante
|
|
420
|
+
aparece y desaparece en vez de deslizarse. La barra de progreso ya no interpola
|
|
421
|
+
el ancho, que además la hacía ir por detrás del audio. El giro del spinner de
|
|
422
|
+
carga se queda, con la misma justificación que en `Button`.
|
|
423
|
+
|
|
424
|
+
**Un fallo de contraste heredado.** El botón de velocidad ponía `textMuted` sobre
|
|
425
|
+
`surfaceRaised`: 4.07:1 en oscuro. Pasó a `textSecondary`. El original arrastra
|
|
426
|
+
ese fallo.
|
|
427
|
+
|
|
428
|
+
**Un `bug` latente.** Las piezas del reproductor viven a nivel de módulo, no
|
|
429
|
+
dentro del componente. Declaradas dentro, cambian de identidad en cada render y
|
|
430
|
+
React las remonta: con `timeupdate` disparando cuatro veces por segundo, el
|
|
431
|
+
arrastre de la barra perdía el pointer capture.
|
|
432
|
+
|
|
433
|
+
### La marca
|
|
434
|
+
|
|
435
|
+
Las trece piezas de Tiburoncín estaban repartidas por los cinco proyectos, byte
|
|
436
|
+
a byte idénticas. Se consolidaron en `assets/brand/` y se publican en el
|
|
437
|
+
paquete; se sirven en `/brand`, que es la misma ruta que todos usan ya desde su
|
|
438
|
+
`public/`, así que el valor por defecto de `basePath` funciona sin configurar
|
|
439
|
+
nada.
|
|
440
|
+
|
|
441
|
+
```tsx
|
|
442
|
+
import { Logo, Mascota, CaraDeMascota, listaCaras } from '@eduardoalvarez/arrecife/brand';
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
| | |
|
|
446
|
+
| --- | --- |
|
|
447
|
+
| aletas | `fin.png` (dos azules) y `fin-foam.png` (silueta espuma) |
|
|
448
|
+
| caras | annoyed · confused · hearts · laughing · shades · waiting · wink |
|
|
449
|
+
| poses | desk · laptop-coffee · peek · surf |
|
|
450
|
+
|
|
451
|
+
Los nombres son un tipo: una cara que no existe no compila, y el autocompletado
|
|
452
|
+
ofrece las que hay. Añadir una es soltar el PNG y añadir una línea al catálogo.
|
|
453
|
+
|
|
454
|
+
**Regla 1 como API.** `sobre="oscuro"` usa la silueta a una tinta y
|
|
455
|
+
`sobre="claro"` la de dos azules. No es una nota en una guía: es una prop. El
|
|
456
|
+
análisis de píxeles lo confirma — el 94 % de `fin-foam.png` es `#EDF4F3`, o sea
|
|
457
|
+
el token espuma.
|
|
458
|
+
|
|
459
|
+
**Regla 5 como API.** El wordmark sale de `naming.wordmark` y siempre dice
|
|
460
|
+
«Eduardo Álvarez». No hay ninguna prop que permita cambiar ese texto, y
|
|
461
|
+
Tiburoncín no aparece escrito dentro del logo.
|
|
462
|
+
|
|
463
|
+
**Regla 4 como API.** Las caras van solo en estados vacíos, confirmaciones,
|
|
464
|
+
errores, progreso de curso y celebración. La regla vive en qué componentes
|
|
465
|
+
aceptan una cara, no en la documentación.
|
|
466
|
+
|
|
467
|
+
El formato es un detalle de implementación: cuando lleguen los SVG, se
|
|
468
|
+
reemplazan los archivos y no cambia una línea de código.
|
|
469
|
+
|
|
470
|
+
### Los ocho que se añadieron después
|
|
471
|
+
|
|
472
|
+
No estaban en la lista original. Entraron midiendo en cuántos de los cinco
|
|
473
|
+
proyectos se usa cada uno, con el mismo criterio que sacó a `Hero` y
|
|
474
|
+
`NewsletterSection`.
|
|
475
|
+
|
|
476
|
+
| | archivos que lo usan | por qué |
|
|
477
|
+
| --- | --- | --- |
|
|
478
|
+
| `Card` | 34, en 4 proyectos | Es la única definición de qué es una superficie de tarjeta. Las cuatro tarjetas con dominio reutilizan sus clases. |
|
|
479
|
+
| `Label` | 21, en 2 | Había siete controles de formulario y ninguna etiqueta. |
|
|
480
|
+
| `Avatar` | 19, en 3 | Uno solo para todo: no hay un `brand/Avatar` aparte, porque una foto de perfil es esto con otro `src`. |
|
|
481
|
+
| `Sheet` | 6, en 3 | Es `Dialog` con variante de lado. |
|
|
482
|
+
| `Separator` | 8, en 2 | `hairline` era un token sin componente. |
|
|
483
|
+
| `Popover` | 5, en 3 | Base de cualquier selector desplegable. |
|
|
484
|
+
| `DateField` | — | El control nativo, sin dependencias, para elegir fecha en un formulario. |
|
|
485
|
+
| `Calendar` | 6, en 3 | Calendario mensual navegable, para el planificador de contenido. `fullWidth` lo estira al ancho del contenedor. |
|
|
486
|
+
|
|
487
|
+
No hay `DatePicker`: son `Popover` más `Calendar` y son cinco líneas. Un tercer
|
|
488
|
+
componente que solo pega dos que ya existen es superficie de API que mantener sin
|
|
489
|
+
ganar nada.
|
|
490
|
+
|
|
491
|
+
`Popover` exige `aria-label` o `aria-labelledby` en el tipo. Radix le pone
|
|
492
|
+
`role="dialog"` al contenido, y un diálogo sin nombre accesible no le dice nada a
|
|
493
|
+
un lector de pantalla: ahora no se puede olvidar porque no compila.
|
|
494
|
+
|
|
495
|
+
### La segunda excepción de movimiento
|
|
496
|
+
|
|
497
|
+
`Sheet` se desliza. Es la segunda y última excepción a «nada de desplazamiento»,
|
|
498
|
+
aprobada a sabiendas: un panel que entra desde un borde quieto sería un modal
|
|
499
|
+
descentrado. Dura `--duration-standard` con `--ease-standard` —lo mismo y con la
|
|
500
|
+
misma curva que cualquier cambio de color— así que no introduce un tiempo nuevo,
|
|
501
|
+
y va detrás de `motion-safe`.
|
|
502
|
+
|
|
503
|
+
`Calendar` **no** anima el cambio de mes: `animate` de react-day-picker se queda
|
|
504
|
+
en su valor por defecto, que es apagado.
|
|
505
|
+
|
|
506
|
+
### `Text` — la escala como API
|
|
507
|
+
|
|
508
|
+
`Text` no estaba en la lista original y se añadió después, porque sin él la
|
|
509
|
+
escala solo existía como clases sueltas y nada impedía poner `text-display` en un
|
|
510
|
+
párrafo. Tres reglas del sistema viven dentro del componente:
|
|
511
|
+
|
|
512
|
+
| regla | cómo se aplica |
|
|
513
|
+
| --- | --- |
|
|
514
|
+
| display solo para titulares, nunca cuerpo | la familia va atada a la escala; no existe una prop `font` |
|
|
515
|
+
| el peso y el tracking son de la escala | vienen del token `--text-*` y no se exponen |
|
|
516
|
+
| medida máxima de cuerpo 68ch | `body` la aplica solo; `measure={false}` la quita |
|
|
517
|
+
|
|
518
|
+
`as` y `variant` son independientes a propósito: un encabezado de segundo nivel
|
|
519
|
+
que tiene que verse más pequeño es `<Text as="h2" variant="h3">`, no un `h3` que
|
|
520
|
+
miente sobre la jerarquía de la página.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|