@eduardoalvarez/arrecife 0.5.0 → 0.6.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 +52 -0
- package/README.md +706 -470
- package/dist/brand/index.cjs +112 -95
- package/dist/brand/index.d.cts +40 -39
- package/dist/brand/index.d.ts +40 -39
- package/dist/brand/index.js +5 -4
- package/dist/catalog-D13txprv.d.cts +78 -0
- package/dist/catalog-D13txprv.d.ts +78 -0
- package/dist/chart/index.cjs +100 -83
- package/dist/chart/index.d.cts +66 -66
- package/dist/chart/index.d.ts +66 -66
- package/dist/chart/index.js +14 -12
- package/dist/chunk-25YNFCIF.js +141 -0
- package/dist/{chunk-YZ2SDOVZ.js → chunk-6O3KWB6P.js} +30 -30
- package/dist/chunk-CKRSQPTX.js +36 -0
- package/dist/{chunk-ZEOQKRQ7.js → chunk-DKCN7BAL.js} +1 -1
- package/dist/chunk-GCRII2KQ.js +86 -0
- package/dist/chunk-JMOOFZ3B.js +42 -0
- package/dist/chunk-O4TAH7YJ.js +276 -0
- package/dist/chunk-ODBFN44D.js +45 -0
- package/dist/{chunk-VPT32GPG.js → chunk-PMN7NR3G.js} +2 -2
- package/dist/chunk-XKYHTOUJ.js +27 -0
- package/dist/form/index.cjs +109 -92
- package/dist/form/index.d.cts +43 -42
- package/dist/form/index.d.ts +43 -42
- package/dist/form/index.js +25 -23
- package/dist/index.cjs +1068 -929
- package/dist/index.d.cts +770 -773
- package/dist/index.d.ts +770 -773
- package/dist/index.js +629 -675
- package/dist/{label-DuTvJGxD.d.ts → label-MgHFKnFy.d.cts} +3 -3
- package/dist/{label-DuTvJGxD.d.cts → label-MgHFKnFy.d.ts} +3 -3
- package/dist/og/index.cjs +130 -130
- package/dist/og/index.d.cts +93 -89
- package/dist/og/index.d.ts +93 -89
- package/dist/og/index.js +106 -106
- package/dist/shiki/index.cjs +28 -30
- package/dist/shiki/index.d.cts +4 -4
- package/dist/shiki/index.d.ts +4 -4
- package/dist/shiki/index.js +12 -12
- package/dist/theme/index.cjs +97 -0
- package/dist/theme/index.d.cts +144 -0
- package/dist/theme/index.d.ts +144 -0
- package/dist/theme/index.js +2 -0
- package/dist/tokens/index.cjs +133 -86
- package/dist/tokens/index.d.cts +246 -161
- package/dist/tokens/index.d.ts +246 -161
- package/dist/tokens/index.js +2 -2
- package/dist/tokens/theme.css +133 -98
- package/dist/variants/index.cjs +192 -0
- package/dist/variants/index.d.cts +192 -0
- package/dist/variants/index.d.ts +192 -0
- package/dist/variants/index.js +3 -0
- package/llms.txt +810 -744
- package/package.json +20 -11
- package/dist/catalogo-Du5ID-Hi.d.cts +0 -77
- package/dist/catalogo-Du5ID-Hi.d.ts +0 -77
- package/dist/chunk-E3OMP2DL.js +0 -36
- package/dist/chunk-KPZNNMV5.js +0 -83
- package/dist/chunk-NHS7ETKJ.js +0 -27
- package/dist/chunk-TSPJOM6K.js +0 -229
- package/dist/chunk-UOWIDFCB.js +0 -81
- package/dist/tema/index.cjs +0 -94
- package/dist/tema/index.d.cts +0 -110
- package/dist/tema/index.d.ts +0 -110
- package/dist/tema/index.js +0 -2
package/llms.txt
CHANGED
|
@@ -1,166 +1,211 @@
|
|
|
1
1
|
# @eduardoalvarez/arrecife
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
> `docs/llms.
|
|
5
|
-
> `pnpm check:llms`
|
|
3
|
+
> GENERATED by `scripts/build-llms.mjs`. Do not edit by hand: the prose lives in
|
|
4
|
+
> `docs/llms.template.md` and the inventory comes out of the types on every
|
|
5
|
+
> build. `pnpm check:llms` fails if this file and the code stop saying the same
|
|
6
|
+
> thing.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
TypeScript, Tailwind v4, shadcn/ui
|
|
8
|
+
The component library of Eduardo Álvarez's visual identity. React 19,
|
|
9
|
+
TypeScript, Tailwind v4, shadcn/ui on top of Radix.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
`AGENTS.md`,
|
|
11
|
+
This document is for an agent writing code in a project that consumes the
|
|
12
|
+
library. If you are working **inside** the Arrecife repo, the document is
|
|
13
|
+
`AGENTS.md`, not this one.
|
|
13
14
|
|
|
14
|
-
##
|
|
15
|
+
## First things first: do not reimplement what is already here
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
Before writing a card, a button, a header or a footer, look through the inventory
|
|
18
|
+
below. The library exists because five projects were each writing the same pieces
|
|
19
|
+
on their own and drifting apart. A new component hand-written in the consuming
|
|
20
|
+
project reintroduces exactly that problem.
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
Do not hand-write colors, sizes or spacing either. Every value in the system has
|
|
23
|
+
a token and a Tailwind utility; a `#hex` or a `p-[13px]` in the consuming project
|
|
24
|
+
is the sign that the wrong path was taken.
|
|
24
25
|
|
|
25
|
-
##
|
|
26
|
+
## Installation
|
|
26
27
|
|
|
27
28
|
```bash
|
|
28
29
|
pnpm add @eduardoalvarez/arrecife
|
|
29
30
|
```
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
Requirements, and they are not optional:
|
|
32
33
|
|
|
33
34
|
| | |
|
|
34
35
|
| --- | --- |
|
|
35
|
-
| React | `^19.0.0`
|
|
36
|
-
| Tailwind | v4. **
|
|
37
|
-
| Node | `>=22.18.0`
|
|
36
|
+
| React | `^19.0.0` and `react-dom` `^19.0.0`, as peer dependencies |
|
|
37
|
+
| Tailwind | v4. **There is no v3 preset**: the output is `@theme`, which v3 does not understand |
|
|
38
|
+
| Node | `>=22.18.0` for the subpaths that run at build time (`./og`, `./tokens`) |
|
|
38
39
|
|
|
39
|
-
Radix, `clsx`, `tailwind-merge`, `class-variance-authority`, `date-fns`
|
|
40
|
-
`react-day-picker`
|
|
41
|
-
|
|
40
|
+
Radix, `clsx`, `tailwind-merge`, `class-variance-authority`, `date-fns` and
|
|
41
|
+
`react-day-picker` come as dependencies of the library. You do not need to
|
|
42
|
+
install or declare them.
|
|
42
43
|
|
|
43
|
-
**
|
|
44
|
-
|
|
44
|
+
**It ships no `lucide-react` and no icon library.** The glyphs the components
|
|
45
|
+
need are inline, inherit `currentColor` and measure 1em.
|
|
45
46
|
|
|
46
|
-
##
|
|
47
|
+
## Tailwind configuration
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
Two lines, in this order, in the project's entry stylesheet:
|
|
49
50
|
|
|
50
51
|
```css
|
|
51
52
|
@import "tailwindcss";
|
|
52
53
|
@import "@eduardoalvarez/arrecife/tokens/theme.css";
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Tailwind
|
|
56
|
+
Without the second one, the components mount with none of the system's styles:
|
|
57
|
+
the classes they use (`bg-surface-raised`, `text-h1`, `rounded-card`) do not
|
|
58
|
+
exist in a bare Tailwind.
|
|
58
59
|
|
|
59
|
-
Tailwind
|
|
60
|
-
|
|
60
|
+
Tailwind has to scan the library so it does not purge those classes. If the
|
|
61
|
+
project declares `@source`, include the package:
|
|
61
62
|
|
|
62
63
|
```css
|
|
63
64
|
@source "../node_modules/@eduardoalvarez/arrecife/dist";
|
|
64
65
|
```
|
|
65
66
|
|
|
66
|
-
###
|
|
67
|
+
### Light mode and dark mode
|
|
67
68
|
|
|
68
|
-
**
|
|
69
|
-
|
|
69
|
+
**Dark mode is primary and it is the default.** A dark project declares nothing.
|
|
70
|
+
A project in light mode declares the attribute on `<html>`:
|
|
70
71
|
|
|
71
72
|
```html
|
|
72
73
|
<html data-theme="light">
|
|
73
74
|
```
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
There is no `dark:` class. The variant available is `light:`, for the cases of
|
|
77
|
+
inverted light mode, and it is almost never needed: the tokens already switch on
|
|
78
|
+
their own.
|
|
77
79
|
|
|
78
|
-
###
|
|
80
|
+
### Fonts
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
Bricolage Grotesque (`font-display`), Geist (`font-sans`)
|
|
82
|
-
(`font-mono`)
|
|
83
|
-
|
|
82
|
+
The library declares the families **by name** and does not load them. The project
|
|
83
|
+
loads Bricolage Grotesque (`font-display`), Geist (`font-sans`) and JetBrains
|
|
84
|
+
Mono (`font-mono`) however it prefers — `next/font`, `@fontsource`, a `<link>`.
|
|
85
|
+
If it does not load them, the browser falls back and the typography looks wrong.
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
The name has to match **exactly** the one the tokens declare, and this has
|
|
88
|
+
already failed in two projects:
|
|
87
89
|
|
|
88
|
-
|
|
|
90
|
+
| Utility | Name the token asks for |
|
|
89
91
|
| --- | --- |
|
|
90
92
|
| `font-display` | `"Bricolage Grotesque"` |
|
|
91
93
|
| `font-sans` | `"Geist"` |
|
|
92
94
|
| `font-mono` | `"JetBrains Mono"` |
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
`"Geist Variable"`.
|
|
96
|
-
tokens
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
Several font packages publish them as `"Bricolage Grotesque Variable"` or
|
|
97
|
+
`"Geist Variable"`. Registering the `@font-face` under that name does NOT load
|
|
98
|
+
what the tokens ask for: the family falls back to the system silently, with no
|
|
99
|
+
console error. The `@font-face`'s `font-family` is an alias the project chooses,
|
|
100
|
+
so write it with the name from the table.
|
|
99
101
|
|
|
100
|
-
##
|
|
102
|
+
## What to import from where
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
script
|
|
105
|
-
|
|
104
|
+
The choice matters, for two different reasons. Four subpaths **do not drag React
|
|
105
|
+
in**, so they can be consumed from a worker, an `astro.config.mjs`, a build
|
|
106
|
+
script or a Satori generator. Another two ask for an **optional peer dependency**
|
|
107
|
+
that only whoever uses them installs.
|
|
106
108
|
|
|
107
|
-
|
|
|
109
|
+
| Subpath | Drags React | Also requires | What for |
|
|
108
110
|
| --- | --- | --- | --- |
|
|
109
|
-
| `@eduardoalvarez/arrecife` |
|
|
110
|
-
| `@eduardoalvarez/arrecife/tokens` | **no** | — |
|
|
111
|
-
| `@eduardoalvarez/arrecife/tokens/theme.css` | — | — |
|
|
112
|
-
| `@eduardoalvarez/arrecife/
|
|
113
|
-
| `@eduardoalvarez/arrecife/
|
|
114
|
-
| `@eduardoalvarez/arrecife/
|
|
115
|
-
| `@eduardoalvarez/arrecife/
|
|
116
|
-
| `@eduardoalvarez/arrecife/
|
|
117
|
-
| `@eduardoalvarez/arrecife/
|
|
118
|
-
| `@eduardoalvarez/arrecife/
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
111
|
+
| `@eduardoalvarez/arrecife` | yes | — | Components, primitives, brand, `cn`. Re-exports tokens and theme |
|
|
112
|
+
| `@eduardoalvarez/arrecife/tokens` | **no** | — | The `tokens` object in JS. Satori, Astro, scripts |
|
|
113
|
+
| `@eduardoalvarez/arrecife/tokens/theme.css` | — | — | Tailwind v4's `@theme` |
|
|
114
|
+
| `@eduardoalvarez/arrecife/theme` | **no** | — | `themeScript` for the `<head>`, and reading or changing the mode |
|
|
115
|
+
| `@eduardoalvarez/arrecife/variants` | **no** | — | The class vocabulary: `buttonVariants`, `badgeVariants`, `CARD_SURFACE` |
|
|
116
|
+
| `@eduardoalvarez/arrecife/og` | **no** | — | The Open Graph templates for Satori |
|
|
117
|
+
| `@eduardoalvarez/arrecife/shiki` | **no** | — | The syntax highlighting theme |
|
|
118
|
+
| `@eduardoalvarez/arrecife/brand` | yes | — | Logo, isotype and mascot as components |
|
|
119
|
+
| `@eduardoalvarez/arrecife/form` | yes | `react-hook-form` | The form layer: labels, errors and `aria-*` |
|
|
120
|
+
| `@eduardoalvarez/arrecife/chart` | yes | `recharts` | The chart chassis and the series palette |
|
|
121
|
+
| `@eduardoalvarez/arrecife/assets/*` | — | — | The brand PNGs |
|
|
122
|
+
|
|
123
|
+
Importing the root from a build script to get one token is the mistake the
|
|
124
|
+
subpaths exist to prevent: it drags all of React into a worker that never mounts
|
|
125
|
+
it.
|
|
126
|
+
|
|
127
|
+
`./form` and `./chart` sit outside the root for the symmetric reason: if they
|
|
128
|
+
hung off the main index, the projects that draw no charts and use no React Hook
|
|
129
|
+
Form would have to install those dependencies anyway so their bundler could
|
|
130
|
+
resolve an import they never execute.
|
|
131
|
+
|
|
132
|
+
### Next, Server Components and `"use client"`
|
|
133
|
+
|
|
134
|
+
The root, `./brand`, `./form` and `./chart` ship `"use client"` in the published
|
|
135
|
+
`dist/`. They render React and their Radix primitives call `createContext` at
|
|
136
|
+
module scope, so without the directive a Next project with the App Router cannot
|
|
137
|
+
import them at all: it fails at build time with
|
|
138
|
+
`TypeError: (0 , r.createContext) is not a function`.
|
|
139
|
+
|
|
140
|
+
You do not add anything: importing `Button` from a Server Component works, and
|
|
141
|
+
the boundary is already where it belongs. What you should NOT do is wrap the
|
|
142
|
+
import in an adapter of your own marked `"use client"` — that was the workaround
|
|
143
|
+
before 0.6.0 and it pulled 272 KB of client chunk in for components that never
|
|
144
|
+
needed it.
|
|
145
|
+
|
|
146
|
+
The five portable subpaths do NOT carry the directive, and that is the half that
|
|
147
|
+
matters in a Server Component: `./tokens`, `./theme`, `./variants`, `./og` and
|
|
148
|
+
`./shiki` stay on the server. If all you need are classes — for a `<div>`, an
|
|
149
|
+
`<a>` or an Astro island you do not want to hydrate — import them from
|
|
150
|
+
`./variants` and nothing crosses to the client:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
// A Server Component, or an .astro frontmatter. No React reaches the browser.
|
|
154
|
+
import { buttonVariants, CARD_SURFACE } from '@eduardoalvarez/arrecife/variants';
|
|
155
|
+
|
|
156
|
+
<a className={buttonVariants({ variant: 'tertiary' })} href="/cursos">./ver_cursos →</a>
|
|
157
|
+
<div className={CARD_SURFACE}>…</div>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
In Astro and in plain Vite the directive is inert — a string literal at the top
|
|
161
|
+
of a module. Rollup may warn `Module level directives cause errors when bundled`
|
|
162
|
+
and nothing else happens: one `dist` serves the Next projects and the Astro ones.
|
|
127
163
|
|
|
128
164
|
```ts
|
|
129
|
-
//
|
|
165
|
+
// Good, in an OG generator or in astro.config.mjs
|
|
130
166
|
import { tokens } from '@eduardoalvarez/arrecife/tokens';
|
|
131
167
|
import { arrecife } from '@eduardoalvarez/arrecife/shiki';
|
|
132
168
|
|
|
133
|
-
//
|
|
134
|
-
import {
|
|
169
|
+
// Good, in the <head> of an Astro that mounts no React
|
|
170
|
+
import { themeScript } from '@eduardoalvarez/arrecife/theme';
|
|
135
171
|
|
|
136
|
-
//
|
|
172
|
+
// Bad: mounts React where it is not needed
|
|
137
173
|
import { tokens } from '@eduardoalvarez/arrecife';
|
|
138
174
|
```
|
|
139
175
|
|
|
140
|
-
###
|
|
176
|
+
### The theme, and the first-paint flash
|
|
141
177
|
|
|
142
|
-
`ThemeToggle`
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
178
|
+
`ThemeToggle` is the button; the hard part is in `./theme`. Without `themeScript`
|
|
179
|
+
inline in the `<head>`, the first paint comes out in the default mode and the
|
|
180
|
+
chosen one arrives a frame later: on a dark site the user left in light mode,
|
|
181
|
+
that is a white flash on every load.
|
|
146
182
|
|
|
147
183
|
```astro
|
|
148
184
|
---
|
|
149
|
-
import {
|
|
185
|
+
import { themeScript } from '@eduardoalvarez/arrecife/theme';
|
|
150
186
|
---
|
|
151
187
|
<head>
|
|
152
|
-
|
|
188
|
+
<!-- The site follows the reader's OS, falling back to dark. -->
|
|
189
|
+
<script is:inline set:html={themeScript()} />
|
|
190
|
+
|
|
191
|
+
<!-- Or: this site IS dark, and the OS is not consulted. -->
|
|
192
|
+
<script is:inline set:html={themeScript({ base: 'dark' })} />
|
|
153
193
|
</head>
|
|
154
194
|
```
|
|
155
195
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
196
|
+
`base` is not «the fallback», it is «this site IS this mode». A stored choice
|
|
197
|
+
still wins over it, so the toggle keeps working — it sets what happens when
|
|
198
|
+
nobody has chosen yet. Use it when the project has already decided its mode and
|
|
199
|
+
does not want the OS overruling that.
|
|
159
200
|
|
|
160
|
-
|
|
201
|
+
It has to go INLINE. A `<script src>`, even a synchronous one, gets downloaded,
|
|
202
|
+
and the flash comes back. The script re-attaches on `astro:after-swap` because
|
|
203
|
+
view transitions replace the whole `<html>`.
|
|
204
|
+
|
|
205
|
+
### The social icons are namespaced
|
|
161
206
|
|
|
162
207
|
```tsx
|
|
163
|
-
// ❌
|
|
208
|
+
// ❌ does not exist
|
|
164
209
|
import { GitHub } from '@eduardoalvarez/arrecife';
|
|
165
210
|
|
|
166
211
|
// ✅
|
|
@@ -168,95 +213,107 @@ import { social } from '@eduardoalvarez/arrecife';
|
|
|
168
213
|
<social.GitHub />
|
|
169
214
|
```
|
|
170
215
|
|
|
171
|
-
|
|
172
|
-
`
|
|
216
|
+
All nine: `GitHub`, `LinkedIn`, `X`, `Instagram`, `Discord`, `YouTube`, `Rss`,
|
|
217
|
+
`Email`, `Newsletter`. They live under a namespace because one of them is called
|
|
218
|
+
`X`, and loose it collides. `Newsletter` is the bell: a way to follow, like
|
|
219
|
+
`Rss`, named for what it means.
|
|
173
220
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
221
|
+
The internal glyphs — `Close`, `ChevronDown`, `Sun` — are **not exported** and
|
|
222
|
+
are not going to be: they are the primitives' minimum set. A component that needs
|
|
223
|
+
an icon receives it as a prop (`Stat` has `icon`, each `SocialLink` in `Footer`
|
|
224
|
+
has its own). Do not ask for them to be published: pass your own.
|
|
178
225
|
|
|
179
226
|
## Tokens
|
|
180
227
|
|
|
181
|
-
|
|
182
|
-
|
|
228
|
+
The source is a TypeScript object and the CSS output is generated from it, so the
|
|
229
|
+
same value is available in both places and they cannot disagree.
|
|
183
230
|
|
|
184
|
-
| Token | Custom property |
|
|
231
|
+
| Token | Custom property | Tailwind utility |
|
|
185
232
|
| --- | --- | --- |
|
|
186
|
-
| `colors[
|
|
233
|
+
| `colors[mode].surfaceRaised` | `--color-surface-raised` | `bg-surface-raised` |
|
|
187
234
|
| `brand.hull` | `--color-brand-hull` | `bg-brand-hull` |
|
|
188
|
-
| `typeScale.h1` | `--text-h1` | `text-h1` (
|
|
235
|
+
| `typeScale.h1` | `--text-h1` | `text-h1` (brings line-height, weight and tracking) |
|
|
189
236
|
| `fonts.display` | `--font-display` | `font-display` |
|
|
190
237
|
| `radius.card` | `--radius-card` | `rounded-card` |
|
|
191
238
|
| `spacing.stepLg` | `--spacing-step-lg` | `p-step-lg`, `gap-step-lg`, `mb-step-lg` |
|
|
192
239
|
| `spacing.section` | `--spacing-section` | `py-section`, `mb-section` |
|
|
193
240
|
| `control.md` | `--spacing-control-md` | `px-control-md` |
|
|
194
241
|
| `control.icon` | `--spacing-control-icon` | `size-control-icon` (42×42) |
|
|
195
|
-
| `gradient[
|
|
242
|
+
| `gradient[mode].hero` | `--gradient-hero` | `gradient-hero` |
|
|
196
243
|
| `size.nav` | `--spacing-nav` | `h-nav` |
|
|
197
244
|
| `size.content` / `size.wide` | `--container-content` / `--container-wide` | `max-w-content` / `max-w-wide` |
|
|
198
245
|
| `limits.measure` | `--container-measure` | `max-w-measure` |
|
|
199
246
|
| `shadow.standard` | `--shadow-standard` | `shadow-standard` |
|
|
200
247
|
| `motion` | `--duration-standard`, `--ease-standard` | `duration-standard`, `ease-standard` |
|
|
201
248
|
|
|
202
|
-
`transition-standard`
|
|
203
|
-
color
|
|
204
|
-
|
|
205
|
-
**
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<https://github.com/Proskynete/arrecife/blob/main/docs/
|
|
213
|
-
|
|
214
|
-
##
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
1. **
|
|
220
|
-
2. **`Button variant="conversion"`
|
|
221
|
-
runtime;
|
|
222
|
-
3.
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
249
|
+
`transition-standard` is the system's only transition and it can only animate
|
|
250
|
+
color and border: that is how the utility is written.
|
|
251
|
+
|
|
252
|
+
**The spacing steps carry `step` in the name and it is not optional.** `p-md` is
|
|
253
|
+
not an Arrecife class: in a Tailwind v4 project it lands on the numeric scale and
|
|
254
|
+
does nothing visible. Page rhythm is `p-step-md`, `gap-step-sm`, `py-step-xl`.
|
|
255
|
+
They carry a prefix because `xs, sm, md, lg, xl` are the names of Tailwind's
|
|
256
|
+
`--container-*` scale, and a `--spacing-md` of our own was swallowing `max-w-md`
|
|
257
|
+
across the whole project with nothing warning about it. `max-w-*`, `w-*` and
|
|
258
|
+
`h-*` belong to Tailwind and are used as they are. Migration guide from 0.2.0:
|
|
259
|
+
<https://github.com/Proskynete/arrecife/blob/main/docs/migration-0.3.md>.
|
|
260
|
+
|
|
261
|
+
## System rules the consuming code must not break
|
|
262
|
+
|
|
263
|
+
These are identity decisions, already measured. Breaking them produces code that
|
|
264
|
+
compiles and looks wrong, or that fails the project's accessibility audit.
|
|
265
|
+
|
|
266
|
+
1. **Zero literal hexes.** Every color comes from a token or its custom property.
|
|
267
|
+
2. **`Button variant="conversion"` appears once per screen.** It is not enforced
|
|
268
|
+
at runtime; two on the same page are a design error.
|
|
269
|
+
3. **`Button variant="destructive"` is for the irreversible only.** Never for
|
|
270
|
+
«cancel» on a form, and not inside an `AlertDialog` — there the confirm button
|
|
271
|
+
stays `primary`, because the title, the focus on cancel and the no-click-outside
|
|
272
|
+
already carry the weight. See `docs/decisions.md` § 21.
|
|
273
|
+
4. **`secondary` is never filled.** It is border and text.
|
|
274
|
+
5. **No entrance animations.** Modals, menus, tooltips and toasts appear where
|
|
275
|
+
they will stay. The only exception is the `Button loading` spinner.
|
|
276
|
+
6. **Semantics and scale are independent.** An `h2` that has to look small is
|
|
277
|
+
`<Text as="h2" variant="h3">`, never an `h3` that lies about the hierarchy.
|
|
278
|
+
7. **`textMuted` never goes over `surfaceRaised`**: it gives 4.07 in dark. Over a
|
|
279
|
+
raised surface — menus, active tabs — the token is `textSecondary`.
|
|
280
|
+
8. **A background tinted with a semantic color carries text from a text token**,
|
|
281
|
+
not from the semantic color. The color stays on the border and on the glyph.
|
|
282
|
+
Putting `accent` over its own tint at 8 % gives 4.12 and does not reach AA.
|
|
283
|
+
9. **`Progress` requires `label`.** A bar with no accessible name does not say
|
|
284
|
+
what it is about.
|
|
285
|
+
10. **`Button size="icon"` and `size="icon-sm"` require `aria-label`.** They carry
|
|
286
|
+
no text. `icon` is 42×42 and is a page action; `icon-sm` is 32×32 and is for a
|
|
287
|
+
dense table row.
|
|
288
|
+
11. **The mascot's faces only appear** in empty states, confirmations, errors,
|
|
289
|
+
course progress and celebration. Never in a hero, pricing, services, contact
|
|
290
|
+
or the CV.
|
|
291
|
+
12. **The fin is not a free parameter**: `foam` on a dark background, `color` on a
|
|
292
|
+
light one. The components already choose it from the background.
|
|
293
|
+
|
|
294
|
+
## What the library does NOT do, on purpose
|
|
295
|
+
|
|
296
|
+
These are the confusions people run into most often when consuming it.
|
|
297
|
+
|
|
298
|
+
- **It does not ship Shiki.** It publishes the *theme*, not the highlighter.
|
|
299
|
+
`CodeBlock` receives the code **already highlighted** by the project's tool.
|
|
300
|
+
- **It does not format dates.** `ArticleCard`, `TalkCard` and company receive the
|
|
301
|
+
date already formatted by the project: the library imposes no locale.
|
|
302
|
+
`dateTime` is separate, in ISO, for the `<time>` attribute.
|
|
303
|
+
- **`NewsletterForm` does not do the POST.** It is presentational: it takes
|
|
304
|
+
`state` and emits `onSubmitEmail`. The call is made by the project with its own
|
|
305
|
+
provider.
|
|
306
|
+
- **It ships no router.** The components with links accept `asChild` to wrap the
|
|
307
|
+
framework's `Link`.
|
|
308
|
+
- **It ships no `data-testid`.** A composed part your test suite has to reach is
|
|
309
|
+
reached with a slot: `ArticleCard`'s `tagAsChild`, `Breadcrumb`'s and
|
|
310
|
+
`TableOfContents`'s `linkAsChild`. They hand you the element and its
|
|
311
|
+
attributes and keep the classes. Do NOT select by structure or by a style
|
|
312
|
+
class — a style class is not a contract and it changes when the style does.
|
|
313
|
+
- **It does not load fonts.** It declares them by name.
|
|
314
|
+
- **There is no Tailwind v3 preset.**
|
|
315
|
+
|
|
316
|
+
## Usage patterns
|
|
260
317
|
|
|
261
318
|
```tsx
|
|
262
319
|
import { Text, Button, Badge, cn } from '@eduardoalvarez/arrecife';
|
|
@@ -264,12 +321,12 @@ import type { TextProps } from '@eduardoalvarez/arrecife';
|
|
|
264
321
|
|
|
265
322
|
<Text variant="eyebrow" tone="muted">charlas</Text>
|
|
266
323
|
<Text as="h2" variant="h1">Escalar con criterio</Text>
|
|
267
|
-
<Text variant="body">
|
|
268
|
-
<Text variant="ui" measure={false}>
|
|
324
|
+
<Text variant="body">Clamps itself to 68ch.</Text>
|
|
325
|
+
<Text variant="ui" measure={false}>No clamp, for a narrow cell.</Text>
|
|
269
326
|
```
|
|
270
327
|
|
|
271
|
-
`asChild`
|
|
272
|
-
|
|
328
|
+
`asChild` renders the child instead of the component's own element. It is how the
|
|
329
|
+
framework's link gets wrapped without losing the styles:
|
|
273
330
|
|
|
274
331
|
```tsx
|
|
275
332
|
<Button asChild>
|
|
@@ -277,23 +334,23 @@ enlace del framework sin perder los estilos:
|
|
|
277
334
|
</Button>
|
|
278
335
|
```
|
|
279
336
|
|
|
280
|
-
`cn`
|
|
281
|
-
|
|
337
|
+
`cn` is `clsx` + `tailwind-merge`. It is used to compose `className` without two
|
|
338
|
+
utilities from the same group fighting each other.
|
|
282
339
|
|
|
283
|
-
Open Graph,
|
|
340
|
+
Open Graph, without React:
|
|
284
341
|
|
|
285
342
|
```ts
|
|
286
343
|
import satori from 'satori';
|
|
287
|
-
import {
|
|
344
|
+
import { articleTemplate, OG } from '@eduardoalvarez/arrecife/og';
|
|
288
345
|
|
|
289
|
-
const svg = await satori(
|
|
346
|
+
const svg = await satori(articleTemplate({ title, date, readingMinutes }), {
|
|
290
347
|
width: OG.width, // 1200
|
|
291
348
|
height: OG.height, // 630
|
|
292
349
|
fonts: [...],
|
|
293
350
|
});
|
|
294
351
|
```
|
|
295
352
|
|
|
296
|
-
|
|
353
|
+
Syntax highlighting, from the site's configuration:
|
|
297
354
|
|
|
298
355
|
```ts
|
|
299
356
|
import { arrecife } from '@eduardoalvarez/arrecife/shiki';
|
|
@@ -303,1253 +360,1262 @@ export default defineConfig({
|
|
|
303
360
|
});
|
|
304
361
|
```
|
|
305
362
|
|
|
306
|
-
#
|
|
363
|
+
# Inventory
|
|
307
364
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
365
|
+
What follows comes out of the TypeScript compiler on every build. Only the props
|
|
366
|
+
**declared by the library** are listed: the ones inherited from an HTML element
|
|
367
|
+
or from a Radix primitive are summarised in the `Extends` line, and they are the
|
|
368
|
+
usual ones.
|
|
311
369
|
|
|
312
|
-
##
|
|
370
|
+
## Primitives
|
|
313
371
|
|
|
314
|
-
|
|
372
|
+
Imported from `@eduardoalvarez/arrecife`. 110 exports.
|
|
315
373
|
|
|
316
374
|
### Accordion, AccordionItem, AccordionTrigger, AccordionContent
|
|
317
375
|
|
|
318
|
-
|
|
376
|
+
Source: `src/primitives/accordion.tsx`
|
|
319
377
|
|
|
320
378
|
**Accordion**
|
|
321
|
-
|
|
379
|
+
The disclosure. Two projects asked for it: the portfolio FAQ and the course syllabus, which is literally a list of sections that open.
|
|
322
380
|
|
|
323
|
-
-
|
|
324
|
-
-
|
|
381
|
+
- Extends: `ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>`
|
|
382
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
325
383
|
|
|
326
384
|
**AccordionItem**
|
|
327
|
-
-
|
|
385
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
328
386
|
|
|
329
387
|
**AccordionTrigger**
|
|
330
|
-
|
|
388
|
+
The trigger IS the heading, so it goes INSIDE an `<h3>`: Radix wraps the button in `AccordionPrimitive.Header`, which renders whichever element you ask of it. Without that, a screen reader sees a list of loose buttons and loses the page structure, which is precisely what a FAQ needs to keep.
|
|
331
389
|
|
|
332
|
-
-
|
|
390
|
+
- Extends: `ComponentPropsWithoutRef< typeof AccordionPrimitive.Trigger >`
|
|
333
391
|
|
|
334
|
-
| prop |
|
|
392
|
+
| prop | type | req. | default | what it does |
|
|
335
393
|
| --- | --- | --- | --- | --- |
|
|
336
|
-
| `headingLevel` | `4 \| 2 \| 3` | | `3` |
|
|
394
|
+
| `headingLevel` | `4 \| 2 \| 3` | | `3` | The level of the heading wrapping the trigger. |
|
|
337
395
|
|
|
338
396
|
**AccordionContent**
|
|
339
|
-
-
|
|
397
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
340
398
|
|
|
341
399
|
### AlertDialogOverlay, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogCancel, AlertDialogAction, AlertDialog, AlertDialogTrigger
|
|
342
400
|
|
|
343
|
-
|
|
401
|
+
Source: `src/primitives/alert-dialog.tsx`
|
|
344
402
|
|
|
345
403
|
**AlertDialogOverlay**
|
|
346
|
-
-
|
|
404
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
347
405
|
|
|
348
406
|
**AlertDialogContent**
|
|
349
|
-
|
|
407
|
+
No entrance animation, same as `Dialog`: it appears where it will stay.
|
|
350
408
|
|
|
351
|
-
-
|
|
409
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
352
410
|
|
|
353
411
|
**AlertDialogHeader**
|
|
354
|
-
-
|
|
412
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
355
413
|
|
|
356
414
|
**AlertDialogFooter**
|
|
357
|
-
|
|
415
|
+
Cancel to the LEFT of confirm on desktop and BELOW it on mobile, which is what `flex-col-reverse` gives: the DOM order puts cancel first — that is where focus goes — and in a column the thumb finds it where it should be without changing the tab order.
|
|
358
416
|
|
|
359
|
-
-
|
|
417
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
360
418
|
|
|
361
419
|
**AlertDialogTitle**
|
|
362
|
-
-
|
|
420
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
363
421
|
|
|
364
422
|
**AlertDialogDescription**
|
|
365
|
-
|
|
423
|
+
What is lost, spelled out. The `alertdialog` role makes this get announced up front, so «this action cannot be undone» does not go here on its own: what goes here is what gets deleted and what it takes down with it.
|
|
366
424
|
|
|
367
|
-
-
|
|
425
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
368
426
|
|
|
369
427
|
**AlertDialogCancel**
|
|
370
|
-
|
|
428
|
+
The one that takes focus on open.
|
|
371
429
|
|
|
372
|
-
-
|
|
430
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
373
431
|
|
|
374
432
|
**AlertDialogAction**
|
|
375
|
-
-
|
|
433
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
376
434
|
|
|
377
435
|
**AlertDialog**
|
|
378
|
-
|
|
436
|
+
The destructive confirmation. It is NOT a `Dialog` with different text, which is why it lives in its own file and on its own Radix primitive.
|
|
379
437
|
|
|
380
|
-
-
|
|
438
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
381
439
|
|
|
382
440
|
**AlertDialogTrigger**
|
|
383
|
-
-
|
|
441
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
384
442
|
|
|
385
443
|
### Alert
|
|
386
444
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
El aviso lleva el color en el fondo, no solo en el borde.
|
|
445
|
+
Source: `src/primitives/alert.tsx`
|
|
390
446
|
|
|
391
|
-
-
|
|
447
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'div'>, 'title'> & VariantProps<typeof alert>`
|
|
392
448
|
|
|
393
|
-
| prop |
|
|
449
|
+
| prop | type | req. | default | what it does |
|
|
394
450
|
| --- | --- | --- | --- | --- |
|
|
395
|
-
| `
|
|
396
|
-
| `icon` | `ReactNode` | | |
|
|
451
|
+
| `emphasis` | `"subtle" \| "strong"` | | | |
|
|
452
|
+
| `icon` | `ReactNode` | | | Replaces the variant's mono glyph. Never an emoji: if you need something else, it is an SVG from `glyphs`. |
|
|
397
453
|
| `title` | `ReactNode` | | | |
|
|
398
|
-
| `variant` | `"accent" \| "success" \| "warning" \| "error"` | |
|
|
454
|
+
| `variant` | `"accent" \| "success" \| "warning" \| "error"` | | | |
|
|
399
455
|
|
|
400
456
|
### Avatar, AvatarImage, AvatarFallback, AvatarUpload
|
|
401
457
|
|
|
402
|
-
|
|
458
|
+
Source: `src/primitives/avatar.tsx`
|
|
403
459
|
|
|
404
460
|
**Avatar**
|
|
405
|
-
|
|
461
|
+
One for everything: the author's photo and anyone else's in the system. There is no separate `brand/Avatar` — a profile photo wearing the brand's skin is exactly this with a different `src`.
|
|
406
462
|
|
|
407
|
-
-
|
|
463
|
+
- Extends: `ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & VariantProps<typeof avatar>`
|
|
408
464
|
|
|
409
|
-
| prop |
|
|
465
|
+
| prop | type | req. | default | what it does |
|
|
410
466
|
| --- | --- | --- | --- | --- |
|
|
411
|
-
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | |
|
|
467
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | | | |
|
|
412
468
|
|
|
413
469
|
**AvatarImage**
|
|
414
|
-
-
|
|
470
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
415
471
|
|
|
416
472
|
**AvatarFallback**
|
|
417
|
-
|
|
473
|
+
Initials while the image loads, or when there is no image.
|
|
418
474
|
|
|
419
|
-
-
|
|
475
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
420
476
|
|
|
421
477
|
**AvatarUpload**
|
|
422
|
-
|
|
478
|
+
The avatar you can change. `Avatar` displays; this one also lets you pick.
|
|
423
479
|
|
|
424
|
-
-
|
|
480
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'div'>, 'onSelect' \| 'children'> & VariantProps<typeof avatar>`
|
|
425
481
|
|
|
426
|
-
| prop |
|
|
482
|
+
| prop | type | req. | default | what it does |
|
|
427
483
|
| --- | --- | --- | --- | --- |
|
|
428
|
-
| `accept` | `string` | | `image/*` |
|
|
484
|
+
| `accept` | `string` | | `image/*` | What the system dialog accepts. |
|
|
429
485
|
| `disabled` | `boolean \| undefined` | | `false` | |
|
|
430
|
-
| `fallback` | `ReactNode` | | |
|
|
431
|
-
| `label` | `string` | | `Cambiar la foto` |
|
|
432
|
-
| `onSelectFile` | `(
|
|
486
|
+
| `fallback` | `ReactNode` | | | Initials while there is no image. |
|
|
487
|
+
| `label` | `string` | | `Cambiar la foto` | The control's accessible name. It is the only thing naming it: there is no visible text. |
|
|
488
|
+
| `onSelectFile` | `(file: File) => void` | | | Fires with the chosen file. The upload is the project's job. |
|
|
433
489
|
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | | | |
|
|
434
|
-
| `src` | `string` | | |
|
|
490
|
+
| `src` | `string` | | | The current image, already uploaded. The local preview beats it while it lasts. |
|
|
435
491
|
|
|
436
492
|
### Badge, CategoryBadge, MetricBadge
|
|
437
493
|
|
|
438
|
-
|
|
494
|
+
Source: `src/primitives/badge.tsx`
|
|
439
495
|
|
|
440
496
|
**Badge**
|
|
441
|
-
|
|
497
|
+
Three badge families, three components. Why there are three shapes and not one is in `variants/badge.ts`, next to the classes that make them.
|
|
442
498
|
|
|
443
|
-
-
|
|
499
|
+
- Extends: `ComponentPropsWithoutRef<'span'> & VariantProps<typeof badge>`
|
|
444
500
|
|
|
445
|
-
| prop |
|
|
501
|
+
| prop | type | req. | default | what it does |
|
|
446
502
|
| --- | --- | --- | --- | --- |
|
|
447
|
-
| `variant` | `"
|
|
503
|
+
| `variant` | `"neutral" \| "accent" \| "warm" \| "success" \| "warning" \| "error"` | | | |
|
|
448
504
|
|
|
449
505
|
**CategoryBadge**
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
- Extiende: `ComponentPropsWithoutRef<'span'>`
|
|
506
|
+
- Extends: `ComponentPropsWithoutRef<'span'>`
|
|
453
507
|
|
|
454
|
-
| prop |
|
|
508
|
+
| prop | type | req. | default | what it does |
|
|
455
509
|
| --- | --- | --- | --- | --- |
|
|
456
|
-
| `active` | `boolean \| undefined` | | `false` |
|
|
510
|
+
| `active` | `boolean \| undefined` | | `false` | Selected filter: solid sand with ink on top. |
|
|
457
511
|
|
|
458
512
|
**MetricBadge**
|
|
459
|
-
-
|
|
513
|
+
- Extends: `ComponentPropsWithoutRef<'span'>`
|
|
460
514
|
|
|
461
|
-
| prop |
|
|
515
|
+
| prop | type | req. | default | what it does |
|
|
462
516
|
| --- | --- | --- | --- | --- |
|
|
463
|
-
| `boxed` | `boolean \| undefined` | | `false` |
|
|
517
|
+
| `boxed` | `boolean \| undefined` | | `false` | Adds the hairline ring. By default a metric carries no box. |
|
|
464
518
|
|
|
465
519
|
### Button
|
|
466
520
|
|
|
467
|
-
|
|
521
|
+
Source: `src/primitives/button.tsx`
|
|
468
522
|
|
|
469
|
-
|
|
523
|
+
- Extends: `ComponentPropsWithoutRef<'button'> & VariantProps<typeof button>`
|
|
470
524
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
| prop | tipo | req. | defecto | qué hace |
|
|
525
|
+
| prop | type | req. | default | what it does |
|
|
474
526
|
| --- | --- | --- | --- | --- |
|
|
475
|
-
| `asChild` | `boolean` | | `false` |
|
|
476
|
-
| `icon` | `ReactNode` | | |
|
|
477
|
-
| `loading` | `boolean` | | `false` |
|
|
478
|
-
| `size` | `"sm" \| "md" \| "lg" \| "icon"` | |
|
|
479
|
-
| `variant` | `"primary" \| "conversion" \| "secondary" \| "tertiary"` | |
|
|
527
|
+
| `asChild` | `boolean` | | `false` | Renders the child instead of a `<button>`, to wrap a link. |
|
|
528
|
+
| `icon` | `ReactNode` | | | SVG glyph before the text. Hidden while loading. |
|
|
529
|
+
| `loading` | `boolean` | | `false` | Disables and announces `aria-busy`. Incompatible with `asChild`. |
|
|
530
|
+
| `size` | `"sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | | | |
|
|
531
|
+
| `variant` | `"primary" \| "conversion" \| "secondary" \| "tertiary" \| "destructive" \| "destructiveOutline"` | | | |
|
|
480
532
|
|
|
481
533
|
### Calendar
|
|
482
534
|
|
|
483
|
-
|
|
535
|
+
Source: `src/primitives/calendar.tsx`
|
|
484
536
|
|
|
485
|
-
|
|
537
|
+
A navigable month calendar, on top of `react-day-picker`.
|
|
486
538
|
|
|
487
|
-
-
|
|
539
|
+
- Extends: `ComponentProps<typeof DayPicker>`
|
|
488
540
|
|
|
489
|
-
| prop |
|
|
541
|
+
| prop | type | req. | default | what it does |
|
|
490
542
|
| --- | --- | --- | --- | --- |
|
|
491
|
-
| `fullWidth` | `boolean \| undefined` | | `false` |
|
|
543
|
+
| `fullWidth` | `boolean \| undefined` | | `false` | Stretches the calendar to fill its container's whole width, with the cells splitting it evenly. |
|
|
492
544
|
|
|
493
545
|
### Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter
|
|
494
546
|
|
|
495
|
-
|
|
547
|
+
Source: `src/primitives/card.tsx`
|
|
496
548
|
|
|
497
549
|
**Card**
|
|
498
|
-
-
|
|
550
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
499
551
|
|
|
500
552
|
**CardHeader**
|
|
501
|
-
-
|
|
553
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
502
554
|
|
|
503
555
|
**CardTitle**
|
|
504
|
-
-
|
|
556
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
505
557
|
|
|
506
558
|
**CardDescription**
|
|
507
|
-
-
|
|
559
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
508
560
|
|
|
509
561
|
**CardContent**
|
|
510
|
-
-
|
|
562
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
511
563
|
|
|
512
564
|
**CardFooter**
|
|
513
|
-
-
|
|
565
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
514
566
|
|
|
515
567
|
### Checkbox
|
|
516
568
|
|
|
517
|
-
|
|
569
|
+
Source: `src/primitives/checkbox.tsx`
|
|
518
570
|
|
|
519
|
-
-
|
|
520
|
-
-
|
|
571
|
+
- Extends: `ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>`
|
|
572
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
521
573
|
|
|
522
574
|
### Code
|
|
523
575
|
|
|
524
|
-
|
|
576
|
+
Source: `src/primitives/code.tsx`
|
|
525
577
|
|
|
526
|
-
|
|
578
|
+
Inline code, inside prose.
|
|
527
579
|
|
|
528
|
-
-
|
|
529
|
-
-
|
|
580
|
+
- Extends: `ComponentPropsWithoutRef<'code'>`
|
|
581
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
530
582
|
|
|
531
583
|
### DateField
|
|
532
584
|
|
|
533
|
-
|
|
585
|
+
Source: `src/primitives/date-field.tsx`
|
|
534
586
|
|
|
535
|
-
|
|
587
|
+
A date field on the native control, not on a calendar of our own.
|
|
536
588
|
|
|
537
|
-
-
|
|
589
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'input'>, 'type'>`
|
|
538
590
|
|
|
539
|
-
| prop |
|
|
591
|
+
| prop | type | req. | default | what it does |
|
|
540
592
|
| --- | --- | --- | --- | --- |
|
|
541
593
|
| `invalid` | `boolean \| undefined` | | `false` | |
|
|
542
|
-
| `withTime` | `boolean \| undefined` | | `false` |
|
|
594
|
+
| `withTime` | `boolean \| undefined` | | `false` | Adds the time to the field. It is the native `datetime-local`. |
|
|
543
595
|
|
|
544
596
|
### DialogOverlay, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, Dialog, DialogTrigger, DialogClose
|
|
545
597
|
|
|
546
|
-
|
|
598
|
+
Source: `src/primitives/dialog.tsx`
|
|
547
599
|
|
|
548
600
|
**DialogOverlay**
|
|
549
|
-
-
|
|
601
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
550
602
|
|
|
551
603
|
**DialogContent**
|
|
552
|
-
|
|
604
|
+
No entrance animation: there is no scale or displacement in the system.
|
|
553
605
|
|
|
554
|
-
-
|
|
606
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
555
607
|
|
|
556
608
|
**DialogHeader**
|
|
557
|
-
-
|
|
609
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
558
610
|
|
|
559
611
|
**DialogFooter**
|
|
560
|
-
-
|
|
612
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
561
613
|
|
|
562
614
|
**DialogTitle**
|
|
563
|
-
-
|
|
615
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
564
616
|
|
|
565
617
|
**DialogDescription**
|
|
566
|
-
-
|
|
618
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
567
619
|
|
|
568
620
|
**Dialog**
|
|
569
|
-
-
|
|
621
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
570
622
|
|
|
571
623
|
**DialogTrigger**
|
|
572
|
-
-
|
|
624
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
573
625
|
|
|
574
626
|
**DialogClose**
|
|
575
|
-
-
|
|
627
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
576
628
|
|
|
577
629
|
### DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenu, DropdownMenuTrigger, DropdownMenuGroup, DropdownMenuRadioGroup, DropdownMenuSub
|
|
578
630
|
|
|
579
|
-
|
|
631
|
+
Source: `src/primitives/dropdown-menu.tsx`
|
|
580
632
|
|
|
581
633
|
**DropdownMenuContent**
|
|
582
|
-
|
|
634
|
+
No entrance animation: the menu appears, it does not unfold.
|
|
583
635
|
|
|
584
|
-
-
|
|
636
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
585
637
|
|
|
586
638
|
**DropdownMenuItem**
|
|
587
|
-
-
|
|
639
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
588
640
|
|
|
589
641
|
**DropdownMenuCheckboxItem**
|
|
590
|
-
-
|
|
642
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
591
643
|
|
|
592
644
|
**DropdownMenuRadioItem**
|
|
593
|
-
-
|
|
645
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
594
646
|
|
|
595
647
|
**DropdownMenuLabel**
|
|
596
|
-
-
|
|
648
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
597
649
|
|
|
598
650
|
**DropdownMenuSeparator**
|
|
599
|
-
-
|
|
651
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
600
652
|
|
|
601
653
|
**DropdownMenuSubTrigger**
|
|
602
|
-
-
|
|
654
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
603
655
|
|
|
604
656
|
**DropdownMenuSubContent**
|
|
605
|
-
-
|
|
657
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
606
658
|
|
|
607
659
|
**DropdownMenu**
|
|
608
|
-
-
|
|
660
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
609
661
|
|
|
610
662
|
**DropdownMenuTrigger**
|
|
611
|
-
-
|
|
663
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
612
664
|
|
|
613
665
|
**DropdownMenuGroup**
|
|
614
|
-
-
|
|
666
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
615
667
|
|
|
616
668
|
**DropdownMenuRadioGroup**
|
|
617
|
-
-
|
|
669
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
618
670
|
|
|
619
671
|
**DropdownMenuSub**
|
|
620
|
-
-
|
|
672
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
621
673
|
|
|
622
674
|
### Input
|
|
623
675
|
|
|
624
|
-
|
|
676
|
+
Source: `src/primitives/input.tsx`
|
|
625
677
|
|
|
626
|
-
-
|
|
678
|
+
- Extends: `ComponentPropsWithoutRef<'input'>`
|
|
627
679
|
|
|
628
|
-
| prop |
|
|
680
|
+
| prop | type | req. | default | what it does |
|
|
629
681
|
| --- | --- | --- | --- | --- |
|
|
630
|
-
| `invalid` | `boolean` | | `false` |
|
|
682
|
+
| `invalid` | `boolean` | | `false` | Marks the control as invalid and tints the border. |
|
|
631
683
|
|
|
632
684
|
### Label
|
|
633
685
|
|
|
634
|
-
|
|
686
|
+
Source: `src/primitives/label.tsx`
|
|
635
687
|
|
|
636
|
-
|
|
688
|
+
The `label` scale: 13px, which is the system's absolute minimum on screen.
|
|
637
689
|
|
|
638
|
-
-
|
|
639
|
-
-
|
|
690
|
+
- Extends: `ComponentPropsWithoutRef<typeof LabelPrimitive.Root>`
|
|
691
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
640
692
|
|
|
641
693
|
### Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis
|
|
642
694
|
|
|
643
|
-
|
|
695
|
+
Source: `src/primitives/pagination.tsx`
|
|
644
696
|
|
|
645
697
|
**Pagination**
|
|
646
|
-
-
|
|
698
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
647
699
|
|
|
648
700
|
**PaginationContent**
|
|
649
|
-
-
|
|
701
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
650
702
|
|
|
651
703
|
**PaginationItem**
|
|
652
|
-
-
|
|
704
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
653
705
|
|
|
654
706
|
**PaginationLink**
|
|
655
|
-
-
|
|
707
|
+
- Extends: `ComponentPropsWithoutRef<'a'>`
|
|
656
708
|
|
|
657
|
-
| prop |
|
|
709
|
+
| prop | type | req. | default | what it does |
|
|
658
710
|
| --- | --- | --- | --- | --- |
|
|
659
711
|
| `isActive` | `boolean` | | `false` | |
|
|
660
712
|
|
|
661
713
|
**PaginationPrevious**
|
|
662
714
|
|
|
663
|
-
| prop |
|
|
715
|
+
| prop | type | req. | default | what it does |
|
|
664
716
|
| --- | --- | --- | --- | --- |
|
|
665
717
|
| `isActive` | `boolean` | | | |
|
|
666
718
|
|
|
667
719
|
**PaginationNext**
|
|
668
720
|
|
|
669
|
-
| prop |
|
|
721
|
+
| prop | type | req. | default | what it does |
|
|
670
722
|
| --- | --- | --- | --- | --- |
|
|
671
723
|
| `isActive` | `boolean` | | | |
|
|
672
724
|
|
|
673
725
|
**PaginationEllipsis**
|
|
674
|
-
-
|
|
726
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
675
727
|
|
|
676
728
|
### PopoverContent, Popover, PopoverTrigger, PopoverAnchor
|
|
677
729
|
|
|
678
|
-
|
|
730
|
+
Source: `src/primitives/popover.tsx`
|
|
679
731
|
|
|
680
732
|
**PopoverContent**
|
|
681
|
-
|
|
733
|
+
No entrance animation: it appears where it will stay, like the rest.
|
|
682
734
|
|
|
683
|
-
-
|
|
684
|
-
-
|
|
735
|
+
- Extends: `ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & Labelled`
|
|
736
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
685
737
|
|
|
686
738
|
**Popover**
|
|
687
|
-
-
|
|
739
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
688
740
|
|
|
689
741
|
**PopoverTrigger**
|
|
690
|
-
-
|
|
742
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
691
743
|
|
|
692
744
|
**PopoverAnchor**
|
|
693
|
-
-
|
|
745
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
694
746
|
|
|
695
747
|
### Progress
|
|
696
748
|
|
|
697
|
-
|
|
749
|
+
Source: `src/primitives/progress.tsx`
|
|
698
750
|
|
|
699
|
-
|
|
751
|
+
The indicator's width changes, it is not animated: the system animates neither scale nor displacement. `transition-standard` only covers color and border, so the width jump is immediate even with the class in place.
|
|
700
752
|
|
|
701
|
-
-
|
|
753
|
+
- Extends: `ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>`
|
|
702
754
|
|
|
703
|
-
| prop |
|
|
755
|
+
| prop | type | req. | default | what it does |
|
|
704
756
|
| --- | --- | --- | --- | --- |
|
|
705
|
-
| `label` | `string` |
|
|
706
|
-
| `tone` | `"accent" \| "warm"` | | `accent` |
|
|
757
|
+
| `label` | `string` | yes | | The bar's accessible name. It is mandatory on purpose: a progress bar with no name does not say what the progress is about, and no other part of the component can deduce it. |
|
|
758
|
+
| `tone` | `"accent" \| "warm"` | | `accent` | Sand instead of biolume, for course progress. |
|
|
707
759
|
|
|
708
760
|
### RadioGroup, RadioGroupItem
|
|
709
761
|
|
|
710
|
-
|
|
762
|
+
Source: `src/primitives/radio-group.tsx`
|
|
711
763
|
|
|
712
764
|
**RadioGroup**
|
|
713
|
-
-
|
|
714
|
-
-
|
|
765
|
+
- Extends: `ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>`
|
|
766
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
715
767
|
|
|
716
768
|
**RadioGroupItem**
|
|
717
|
-
-
|
|
718
|
-
-
|
|
769
|
+
- Extends: `ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>`
|
|
770
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
719
771
|
|
|
720
772
|
### SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, Select, SelectGroup, SelectValue
|
|
721
773
|
|
|
722
|
-
|
|
774
|
+
Source: `src/primitives/select.tsx`
|
|
723
775
|
|
|
724
776
|
**SelectTrigger**
|
|
725
|
-
-
|
|
777
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
726
778
|
|
|
727
779
|
**SelectContent**
|
|
728
|
-
|
|
780
|
+
No entrance animation: the menu appears, it does not unfold.
|
|
729
781
|
|
|
730
|
-
-
|
|
782
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
731
783
|
|
|
732
784
|
**SelectLabel**
|
|
733
|
-
-
|
|
785
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
734
786
|
|
|
735
787
|
**SelectItem**
|
|
736
|
-
-
|
|
788
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
737
789
|
|
|
738
790
|
**SelectSeparator**
|
|
739
|
-
-
|
|
791
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
740
792
|
|
|
741
793
|
**Select**
|
|
742
|
-
-
|
|
794
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
743
795
|
|
|
744
796
|
**SelectGroup**
|
|
745
|
-
-
|
|
797
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
746
798
|
|
|
747
799
|
**SelectValue**
|
|
748
|
-
-
|
|
800
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
749
801
|
|
|
750
802
|
### Separator
|
|
751
803
|
|
|
752
|
-
|
|
804
|
+
Source: `src/primitives/separator.tsx`
|
|
753
805
|
|
|
754
|
-
`hairline`,
|
|
806
|
+
`hairline`, not `border`: a division between pieces of content is subtle by definition. To delimit a control there is `border`, which is another token.
|
|
755
807
|
|
|
756
|
-
-
|
|
757
|
-
-
|
|
808
|
+
- Extends: `ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>`
|
|
809
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
758
810
|
|
|
759
811
|
### SheetContent, SheetHeader, SheetBody, SheetFooter, SheetTitle, SheetDescription, Sheet, SheetTrigger, SheetClose
|
|
760
812
|
|
|
761
|
-
|
|
813
|
+
Source: `src/primitives/sheet.tsx`
|
|
762
814
|
|
|
763
815
|
**SheetContent**
|
|
764
|
-
|
|
816
|
+
The second and last exception to «no displacement», approved knowingly: a panel entering from an edge slides by definition, and held still it would be an off-centre modal.
|
|
765
817
|
|
|
766
|
-
-
|
|
818
|
+
- Extends: `ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & VariantProps<typeof panel>`
|
|
767
819
|
|
|
768
|
-
| prop |
|
|
820
|
+
| prop | type | req. | default | what it does |
|
|
769
821
|
| --- | --- | --- | --- | --- |
|
|
770
822
|
| `side` | `"right" \| "left" \| "top" \| "bottom"` | | `right` | |
|
|
771
823
|
|
|
772
824
|
**SheetHeader**
|
|
773
|
-
-
|
|
825
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
774
826
|
|
|
775
827
|
**SheetBody**
|
|
776
|
-
-
|
|
828
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
777
829
|
|
|
778
830
|
**SheetFooter**
|
|
779
|
-
-
|
|
831
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
780
832
|
|
|
781
833
|
**SheetTitle**
|
|
782
|
-
-
|
|
834
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
783
835
|
|
|
784
836
|
**SheetDescription**
|
|
785
|
-
-
|
|
837
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
786
838
|
|
|
787
839
|
**Sheet**
|
|
788
|
-
-
|
|
840
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
789
841
|
|
|
790
842
|
**SheetTrigger**
|
|
791
|
-
-
|
|
843
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
792
844
|
|
|
793
845
|
**SheetClose**
|
|
794
|
-
-
|
|
846
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
795
847
|
|
|
796
848
|
### Skeleton
|
|
797
849
|
|
|
798
|
-
|
|
850
|
+
Source: `src/primitives/skeleton.tsx`
|
|
799
851
|
|
|
800
|
-
|
|
852
|
+
A 1.4s linear sweep, from the document.
|
|
801
853
|
|
|
802
|
-
-
|
|
854
|
+
- Extends: `ComponentPropsWithoutRef<'div'>`
|
|
803
855
|
|
|
804
|
-
| prop |
|
|
856
|
+
| prop | type | req. | default | what it does |
|
|
805
857
|
| --- | --- | --- | --- | --- |
|
|
806
|
-
| `still` | `boolean \| undefined` | | `false` |
|
|
858
|
+
| `still` | `boolean \| undefined` | | `false` | Turns the sweep off. For long lists, where many at once are dizzying. |
|
|
807
859
|
|
|
808
860
|
### Switch
|
|
809
861
|
|
|
810
|
-
|
|
862
|
+
Source: `src/primitives/switch.tsx`
|
|
811
863
|
|
|
812
|
-
|
|
864
|
+
The knob changes position, but is not animated while doing so: the position IS the state, not a transition. The only thing that transitions is the track's color.
|
|
813
865
|
|
|
814
|
-
-
|
|
815
|
-
-
|
|
866
|
+
- Extends: `ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>`
|
|
867
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
816
868
|
|
|
817
869
|
### Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption
|
|
818
870
|
|
|
819
|
-
|
|
871
|
+
Source: `src/primitives/table.tsx`
|
|
820
872
|
|
|
821
873
|
**Table**
|
|
822
|
-
|
|
874
|
+
The container scrolls horizontally: the page never does.
|
|
823
875
|
|
|
824
|
-
-
|
|
876
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
825
877
|
|
|
826
878
|
**TableHeader**
|
|
827
|
-
-
|
|
879
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
828
880
|
|
|
829
881
|
**TableBody**
|
|
830
|
-
-
|
|
882
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
831
883
|
|
|
832
884
|
**TableFooter**
|
|
833
|
-
-
|
|
885
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
834
886
|
|
|
835
887
|
**TableRow**
|
|
836
|
-
-
|
|
888
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
837
889
|
|
|
838
890
|
**TableHead**
|
|
839
|
-
-
|
|
891
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
840
892
|
|
|
841
893
|
**TableCell**
|
|
842
|
-
-
|
|
894
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
843
895
|
|
|
844
896
|
**TableCaption**
|
|
845
|
-
-
|
|
897
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
846
898
|
|
|
847
899
|
### Tabs, TabsList, TabsTrigger, TabsContent
|
|
848
900
|
|
|
849
|
-
|
|
901
|
+
Source: `src/primitives/tabs.tsx`
|
|
850
902
|
|
|
851
903
|
**Tabs**
|
|
852
|
-
-
|
|
853
|
-
-
|
|
904
|
+
- Extends: `ComponentPropsWithoutRef<typeof TabsPrimitive.Root>`
|
|
905
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
854
906
|
|
|
855
907
|
**TabsList**
|
|
856
|
-
-
|
|
908
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
857
909
|
|
|
858
910
|
**TabsTrigger**
|
|
859
|
-
-
|
|
911
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
860
912
|
|
|
861
913
|
**TabsContent**
|
|
862
|
-
-
|
|
914
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
863
915
|
|
|
864
916
|
### Textarea
|
|
865
917
|
|
|
866
|
-
|
|
918
|
+
Source: `src/primitives/textarea.tsx`
|
|
867
919
|
|
|
868
|
-
-
|
|
920
|
+
- Extends: `ComponentPropsWithoutRef<'textarea'>`
|
|
869
921
|
|
|
870
|
-
| prop |
|
|
922
|
+
| prop | type | req. | default | what it does |
|
|
871
923
|
| --- | --- | --- | --- | --- |
|
|
872
924
|
| `invalid` | `boolean` | | `false` | |
|
|
873
925
|
|
|
874
926
|
### Toaster
|
|
875
927
|
|
|
876
|
-
|
|
928
|
+
Source: `src/primitives/toaster.tsx`
|
|
877
929
|
|
|
878
|
-
|
|
930
|
+
Mount it ONCE, as high in the tree as possible. Two mounted `Toaster`s paint every notice twice: the list belongs to the module, not to the instance.
|
|
879
931
|
|
|
880
|
-
-
|
|
932
|
+
- Extends: `{ /** How long a notice lasts when it does not say otherwise. */ duration?: number; /** * The name of the landmark Radix creates for the notices region. It is * translated because a screen reader reads it, and Radix's default is in * English. */ label?: string; }`
|
|
881
933
|
|
|
882
|
-
| prop |
|
|
934
|
+
| prop | type | req. | default | what it does |
|
|
883
935
|
| --- | --- | --- | --- | --- |
|
|
884
|
-
| `duration` | `number` | | `5000` |
|
|
885
|
-
| `label` | `string` | | `Avisos` |
|
|
936
|
+
| `duration` | `number` | | `5000` | How long a notice lasts when it does not say otherwise. |
|
|
937
|
+
| `label` | `string` | | `Avisos` | The name of the landmark Radix creates for the notices region. It is translated because a screen reader reads it, and Radix's default is in English. |
|
|
886
938
|
|
|
887
939
|
### TooltipContent, TooltipProvider, Tooltip, TooltipTrigger
|
|
888
940
|
|
|
889
|
-
|
|
941
|
+
Source: `src/primitives/tooltip.tsx`
|
|
890
942
|
|
|
891
943
|
**TooltipContent**
|
|
892
|
-
-
|
|
944
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
893
945
|
|
|
894
946
|
**TooltipProvider**
|
|
895
|
-
-
|
|
947
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
896
948
|
|
|
897
949
|
**Tooltip**
|
|
898
|
-
-
|
|
950
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
899
951
|
|
|
900
952
|
**TooltipTrigger**
|
|
901
|
-
-
|
|
953
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
902
954
|
|
|
903
955
|
### Text
|
|
904
956
|
|
|
905
|
-
|
|
957
|
+
Source: `src/primitives/typography.tsx`
|
|
906
958
|
|
|
907
|
-
-
|
|
959
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'p'>, 'color'> & VariantProps<typeof text>`
|
|
908
960
|
|
|
909
|
-
| prop |
|
|
961
|
+
| prop | type | req. | default | what it does |
|
|
910
962
|
| --- | --- | --- | --- | --- |
|
|
911
|
-
| `as` | `"h2" \| "h3" \| "li" \| "p" \| "span" \| "caption" \| "dd" \| "dt" \| "em" \| "figcaption" \| "
|
|
912
|
-
| `asChild` | `boolean` | | `false` |
|
|
913
|
-
| `measure` | `boolean` | | |
|
|
914
|
-
| `tone` | `"
|
|
915
|
-
| `variant` | `"display" \| "
|
|
963
|
+
| `as` | `"h1" \| "h2" \| "h3" \| "strong" \| "li" \| "p" \| "span" \| "caption" \| "dd" \| "dt" \| "em" \| "figcaption" \| "h4" \| "legend"` | | | HTML tag. Defaults to whichever one matches the scale. |
|
|
964
|
+
| `asChild` | `boolean` | | `false` | Renders the child instead of creating an element, to wrap a link. |
|
|
965
|
+
| `measure` | `boolean` | | | Clamps the line to 68ch. On by default for `body`, the only scale meant to be read in long paragraphs. |
|
|
966
|
+
| `tone` | `"primary" \| "secondary" \| "accent" \| "warm" \| "success" \| "warning" \| "error" \| "muted"` | | | |
|
|
967
|
+
| `variant` | `"display" \| "stat" \| "h1" \| "h2" \| "h3" \| "body" \| "lead" \| "ui" \| "label" \| "tag" \| "chip" \| "meta" \| "eyebrow"` | | | |
|
|
916
968
|
|
|
917
|
-
##
|
|
969
|
+
## Components
|
|
918
970
|
|
|
919
|
-
|
|
971
|
+
Imported from `@eduardoalvarez/arrecife`. 24 exports.
|
|
920
972
|
|
|
921
973
|
### ArticleCard
|
|
922
974
|
|
|
923
|
-
|
|
975
|
+
Source: `src/components/article-card/index.tsx`
|
|
924
976
|
|
|
925
|
-
|
|
977
|
+
The metadata line uses `meta` and not `eyebrow`: `18 ago 2026 · 8 min de lectura` is a datum, not an overline, and in small caps it was neither.
|
|
926
978
|
|
|
927
|
-
-
|
|
979
|
+
- Extends: `Omit<CardShellProps, 'children' \| 'title'>`
|
|
928
980
|
|
|
929
|
-
| prop |
|
|
981
|
+
| prop | type | req. | default | what it does |
|
|
930
982
|
| --- | --- | --- | --- | --- |
|
|
931
|
-
| `asChild` | `boolean \| undefined` | | |
|
|
932
|
-
| `date` | `ReactNode` | | |
|
|
933
|
-
| `dateTime` | `string` | | |
|
|
934
|
-
| `excerpt` | `ReactNode` | | |
|
|
935
|
-
| `headingLevel` | `2 \| 3` | | `3` |
|
|
983
|
+
| `asChild` | `boolean \| undefined` | | | Renders the child instead of an `<a>`. It is how Next's or Astro's `Link` plugs in without the library depending on any router. |
|
|
984
|
+
| `date` | `ReactNode` | | | Date already formatted by the project: the library imposes no locale. |
|
|
985
|
+
| `dateTime` | `string` | | | The `<time>` element's `datetime` value, in ISO. |
|
|
986
|
+
| `excerpt` | `ReactNode` | | | Standfirst. Clamped to two lines so the grid does not fall out of line. |
|
|
987
|
+
| `headingLevel` | `2 \| 3` | | `3` | The headline level. `h3` by default: a lone card in a grid does not earn a level its position does not give it. |
|
|
936
988
|
| `readingMinutes` | `number` | | | |
|
|
989
|
+
| `tagAsChild` | `(props: { tag: string; children: ReactNode; }) => ReactNode` | | | Renders each tag through the child, so an E2E suite can reach it. |
|
|
937
990
|
| `tags` | `readonly string[]` | | | |
|
|
938
|
-
| `title` | `ReactNode` |
|
|
991
|
+
| `title` | `ReactNode` | yes | | |
|
|
939
992
|
|
|
940
993
|
### AudioPlayer
|
|
941
994
|
|
|
942
|
-
|
|
995
|
+
Source: `src/components/audio-player/index.tsx`
|
|
943
996
|
|
|
944
|
-
-
|
|
997
|
+
- Extends: `{ src: string; title?: string; /** * `full` for podcast pages, `compact` for sidebars and `banner` for articles * with narration. `compact` and `banner` also bring the floating player when * the static one leaves the viewport. */ mode?: AudioPlayerMode \| undefined; /** * Called once per load, the first time the audio starts. * This is where the project hooks up its analytics; the library ships none. */ onFirstPlay?: ((title?: string) => void) \| undefined; }`
|
|
945
998
|
|
|
946
|
-
| prop |
|
|
999
|
+
| prop | type | req. | default | what it does |
|
|
947
1000
|
| --- | --- | --- | --- | --- |
|
|
948
|
-
| `mode` | `"banner" \| "full" \| "compact"` | | `full` | `full`
|
|
949
|
-
| `onFirstPlay` | `(title?: string \| undefined) => void` | | |
|
|
950
|
-
| `src` | `string` |
|
|
1001
|
+
| `mode` | `"banner" \| "full" \| "compact"` | | `full` | `full` for podcast pages, `compact` for sidebars and `banner` for articles with narration. `compact` and `banner` also bring the floating player when the static one leaves the viewport. |
|
|
1002
|
+
| `onFirstPlay` | `(title?: string \| undefined) => void` | | | Called once per load, the first time the audio starts. This is where the project hooks up its analytics; the library ships none. |
|
|
1003
|
+
| `src` | `string` | yes | | |
|
|
951
1004
|
| `title` | `string` | | | |
|
|
952
1005
|
|
|
953
1006
|
### AuthorCard
|
|
954
1007
|
|
|
955
|
-
|
|
1008
|
+
Source: `src/components/author-card/index.tsx`
|
|
956
1009
|
|
|
957
|
-
|
|
1010
|
+
The byline at the foot of the article: 52px avatar, 15/500 name and the role in muted mono. Three data points, not one more.
|
|
958
1011
|
|
|
959
|
-
-
|
|
1012
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'div'>, 'role'>`
|
|
960
1013
|
|
|
961
|
-
| prop |
|
|
1014
|
+
| prop | type | req. | default | what it does |
|
|
962
1015
|
| --- | --- | --- | --- | --- |
|
|
963
|
-
| `action` | `ReactNode` | | |
|
|
964
|
-
| `bio` | `ReactNode` | | |
|
|
965
|
-
| `name` | `string` |
|
|
966
|
-
| `role` | `ReactNode` | | |
|
|
967
|
-
| `src` | `string` | | |
|
|
1016
|
+
| `action` | `ReactNode` | | | Links or a contact button. |
|
|
1017
|
+
| `bio` | `ReactNode` | | | One or two sentences. It clamps itself to 68ch. |
|
|
1018
|
+
| `name` | `string` | yes | | |
|
|
1019
|
+
| `role` | `ReactNode` | | | The role. It goes in mono: it is a datum, not a sentence. |
|
|
1020
|
+
| `src` | `string` | | | The avatar's URL. Without it the initials are shown. |
|
|
968
1021
|
|
|
969
1022
|
### Blockquote
|
|
970
1023
|
|
|
971
|
-
|
|
1024
|
+
Source: `src/components/blockquote/index.tsx`
|
|
972
1025
|
|
|
973
|
-
|
|
1026
|
+
The side bar is `accent`, the interactive color, because a quotation is somebody else's voice entering the text. It carries no decorative quote marks: the system's glyphs are SVG, and an ornamental quote adds nothing the border and the indent do not already say.
|
|
974
1027
|
|
|
975
|
-
-
|
|
1028
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'blockquote'>, 'cite'>`
|
|
976
1029
|
|
|
977
|
-
| prop |
|
|
1030
|
+
| prop | type | req. | default | what it does |
|
|
978
1031
|
| --- | --- | --- | --- | --- |
|
|
979
|
-
| `author` | `ReactNode` | | |
|
|
980
|
-
| `source` | `ReactNode` | | |
|
|
1032
|
+
| `author` | `ReactNode` | | | Who said it. Marked up as `<cite>`. |
|
|
1033
|
+
| `source` | `ReactNode` | | | Where they said it: a talk, an article, a conversation. |
|
|
981
1034
|
|
|
982
1035
|
### Breadcrumb
|
|
983
1036
|
|
|
984
|
-
|
|
1037
|
+
Source: `src/components/breadcrumb/index.tsx`
|
|
985
1038
|
|
|
986
|
-
-
|
|
1039
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'nav'>, 'children'>`
|
|
987
1040
|
|
|
988
|
-
| prop |
|
|
1041
|
+
| prop | type | req. | default | what it does |
|
|
989
1042
|
| --- | --- | --- | --- | --- |
|
|
990
|
-
| `homeHref` | `string` | | `/` |
|
|
991
|
-
| `homeLabel` | `string` | | `Inicio` |
|
|
992
|
-
| `items` | `readonly
|
|
993
|
-
| `linkAsChild` | `(props: { href: string; children: ReactNode; }) => ReactNode` | | |
|
|
1043
|
+
| `homeHref` | `string` | | `/` | Where the `~` goes. The site root by default. |
|
|
1044
|
+
| `homeLabel` | `string` | | `Inicio` | Accessible label for the `~`, which otherwise reads as a stray tilde. |
|
|
1045
|
+
| `items` | `readonly Crumb[]` | yes | | |
|
|
1046
|
+
| `linkAsChild` | `(props: { href: string; children: ReactNode; }) => ReactNode` | | | Renders the links through the child, to plug in Next's or Astro's `Link`. It receives each `href` in the Slot's `props`. |
|
|
994
1047
|
|
|
995
1048
|
### CodeBlock
|
|
996
1049
|
|
|
997
|
-
|
|
1050
|
+
Source: `src/components/code-block/index.tsx`
|
|
998
1051
|
|
|
999
|
-
`brand.hull`
|
|
1052
|
+
`brand.hull` is «hull · outline and the background of code blocks», so a code block is dark in light mode too. That is why the root declares `data-theme="dark"`: everything inside — ink, hairline, accent — switches to the dark palette regardless of the page's theme. It is the system's only island of inverted theme, and it is deliberate.
|
|
1000
1053
|
|
|
1001
|
-
-
|
|
1054
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'div'>, 'children'>`
|
|
1002
1055
|
|
|
1003
|
-
| prop |
|
|
1056
|
+
| prop | type | req. | default | what it does |
|
|
1004
1057
|
| --- | --- | --- | --- | --- |
|
|
1005
|
-
| `children` | `ReactNode` |
|
|
1006
|
-
| `copyText` | `string` | | |
|
|
1007
|
-
| `language` | `string` | | |
|
|
1058
|
+
| `children` | `ReactNode` | yes | | The already-highlighted code, or flat text. |
|
|
1059
|
+
| `copyText` | `string` | | | The text copied to the clipboard. Without it, the button is not shown. |
|
|
1060
|
+
| `language` | `string` | | | The language label. Shown in the top bar. |
|
|
1008
1061
|
|
|
1009
1062
|
### CourseCard
|
|
1010
1063
|
|
|
1011
|
-
|
|
1064
|
+
Source: `src/components/course-card/index.tsx`
|
|
1012
1065
|
|
|
1013
|
-
-
|
|
1066
|
+
- Extends: `Omit<CardShellProps, 'children' \| 'title'>`
|
|
1014
1067
|
|
|
1015
|
-
| prop |
|
|
1068
|
+
| prop | type | req. | default | what it does |
|
|
1016
1069
|
| --- | --- | --- | --- | --- |
|
|
1017
|
-
| `asChild` | `boolean \| undefined` | | |
|
|
1018
|
-
| `meta` | `readonly ReactNode[]` | | |
|
|
1019
|
-
| `progress` | `number` | | |
|
|
1020
|
-
| `status` | `ReactNode` | | |
|
|
1070
|
+
| `asChild` | `boolean \| undefined` | | | Renders the child instead of an `<a>`. It is how Next's or Astro's `Link` plugs in without the library depending on any router. |
|
|
1071
|
+
| `meta` | `readonly ReactNode[]` | | | Level, duration, number of lessons: whatever the project wants to list. |
|
|
1072
|
+
| `progress` | `number` | | | Percentage completed. It only makes sense for someone already enrolled; when passed, the bar goes in sand, which is the color of course progress. |
|
|
1073
|
+
| `status` | `ReactNode` | | | Status label: «próximamente», «gratis», «nuevo». |
|
|
1021
1074
|
| `summary` | `ReactNode` | | | |
|
|
1022
|
-
| `title` | `ReactNode` |
|
|
1075
|
+
| `title` | `ReactNode` | yes | | |
|
|
1023
1076
|
|
|
1024
1077
|
### EmptyState
|
|
1025
1078
|
|
|
1026
|
-
|
|
1079
|
+
Source: `src/components/empty-state/index.tsx`
|
|
1027
1080
|
|
|
1028
|
-
|
|
1081
|
+
The mascot's most important rule, finally as code.
|
|
1029
1082
|
|
|
1030
|
-
-
|
|
1083
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'div'>, 'title'>`
|
|
1031
1084
|
|
|
1032
|
-
| prop |
|
|
1085
|
+
| prop | type | req. | default | what it does |
|
|
1033
1086
|
| --- | --- | --- | --- | --- |
|
|
1034
|
-
| `action` | `ReactNode` | | |
|
|
1035
|
-
| `basePath` | `string` | | |
|
|
1036
|
-
| `description` | `ReactNode` | | |
|
|
1037
|
-
| `
|
|
1038
|
-
| `title` | `ReactNode` |
|
|
1087
|
+
| `action` | `ReactNode` | | | The action that gets you out of the empty state. Usually a tertiary button. |
|
|
1088
|
+
| `basePath` | `string` | | | Where the brand PNGs are served from. |
|
|
1089
|
+
| `description` | `ReactNode` | | | One line explaining what is missing or what to do. |
|
|
1090
|
+
| `expression` | `"annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink"` | yes | | The face. Mandatory: without it this is a centred paragraph. |
|
|
1091
|
+
| `title` | `ReactNode` | yes | | |
|
|
1039
1092
|
|
|
1040
1093
|
### EventCalendar
|
|
1041
1094
|
|
|
1042
|
-
|
|
1095
|
+
Source: `src/components/event-calendar/index.tsx`
|
|
1043
1096
|
|
|
1044
|
-
-
|
|
1097
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'div'>, 'onSelect' \| 'children'>`
|
|
1045
1098
|
|
|
1046
|
-
| prop |
|
|
1099
|
+
| prop | type | req. | default | what it does |
|
|
1047
1100
|
| --- | --- | --- | --- | --- |
|
|
1048
|
-
| `emptyMessage` | `ReactNode` | | `Nada en este día.` |
|
|
1049
|
-
| `events` | `readonly
|
|
1050
|
-
| `formatDay` | `(
|
|
1051
|
-
| `formatTime` | `(
|
|
1052
|
-
| `onCreateEvent` | `(
|
|
1101
|
+
| `emptyMessage` | `ReactNode` | | `Nada en este día.` | The panel's text when the chosen day has nothing on it. |
|
|
1102
|
+
| `events` | `readonly CalendarEvent[]` | yes | | |
|
|
1103
|
+
| `formatDay` | `(day: Date) => string` | | `(day) => format(day, "EEEE d de MMMM", { locale: es })` | The panel's heading. Defaults to date-fns' `es`, like `Calendar`. |
|
|
1104
|
+
| `formatTime` | `(date: Date) => string` | | `(date) => format(date, HH:mm, { locale: es })` | |
|
|
1105
|
+
| `onCreateEvent` | `(event: Omit<CalendarEvent, "id">) => void` | | | Without it, the schedule is read-only and the form is not painted. |
|
|
1053
1106
|
| `onDeleteEvent` | `(id: string) => void` | | | |
|
|
1054
|
-
| `onSelectDay` | `(
|
|
1055
|
-
| `onUpdateEvent` | `(
|
|
1056
|
-
| `selected` | `Date` | | |
|
|
1107
|
+
| `onSelectDay` | `(day: Date) => void` | | | |
|
|
1108
|
+
| `onUpdateEvent` | `(event: CalendarEvent) => void` | | | |
|
|
1109
|
+
| `selected` | `Date` | | | Selected day, if the project controls it. Without it, it starts on today. |
|
|
1057
1110
|
|
|
1058
1111
|
### Footer, FooterLink
|
|
1059
1112
|
|
|
1060
|
-
|
|
1113
|
+
Source: `src/components/footer/index.tsx`
|
|
1061
1114
|
|
|
1062
1115
|
**Footer**
|
|
1063
|
-
-
|
|
1116
|
+
- Extends: `ComponentPropsWithoutRef<'footer'>`
|
|
1064
1117
|
|
|
1065
|
-
| prop |
|
|
1118
|
+
| prop | type | req. | default | what it does |
|
|
1066
1119
|
| --- | --- | --- | --- | --- |
|
|
1067
|
-
| `brand` | `ReactNode` | | |
|
|
1068
|
-
| `social` | `readonly
|
|
1069
|
-
| `year` | `number` | | `new Date().getFullYear()` |
|
|
1120
|
+
| `brand` | `ReactNode` | | | The brand row: the fin and the wordmark, at the very top. |
|
|
1121
|
+
| `social` | `readonly SocialLink[]` | | | |
|
|
1122
|
+
| `year` | `number` | | `new Date().getFullYear()` | The signature's year. |
|
|
1070
1123
|
|
|
1071
1124
|
**FooterLink**
|
|
1072
|
-
-
|
|
1125
|
+
- Extends: `ComponentPropsWithoutRef<'a'>`
|
|
1073
1126
|
|
|
1074
|
-
| prop |
|
|
1127
|
+
| prop | type | req. | default | what it does |
|
|
1075
1128
|
| --- | --- | --- | --- | --- |
|
|
1076
1129
|
| `asChild` | `boolean \| undefined` | | `false` | |
|
|
1077
1130
|
|
|
1078
1131
|
### Hero
|
|
1079
1132
|
|
|
1080
|
-
|
|
1133
|
+
Source: `src/components/hero/index.tsx`
|
|
1081
1134
|
|
|
1082
|
-
|
|
1135
|
+
ONE per site. It is the only piece in the system that is spent like the conversion button, and for the same reason: if there are two, there are none.
|
|
1083
1136
|
|
|
1084
|
-
-
|
|
1137
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'section'>, 'title'>`
|
|
1085
1138
|
|
|
1086
|
-
| prop |
|
|
1139
|
+
| prop | type | req. | default | what it does |
|
|
1087
1140
|
| --- | --- | --- | --- | --- |
|
|
1088
|
-
| `action` | `ReactNode` | | |
|
|
1141
|
+
| `action` | `ReactNode` | | | The buttons. The screen's only `conversion` goes here. |
|
|
1089
1142
|
| `basePath` | `string` | | | |
|
|
1090
1143
|
| `description` | `ReactNode` | | | |
|
|
1091
|
-
| `eyebrow` | `ReactNode` | | | Mono,
|
|
1092
|
-
| `pose` | `"desk" \| "laptop-coffee" \| "peek" \| "surf"` | | |
|
|
1093
|
-
| `title` | `ReactNode` |
|
|
1094
|
-
| `variant` | `"
|
|
1144
|
+
| `eyebrow` | `ReactNode` | | | Mono, small caps, in accent. |
|
|
1145
|
+
| `pose` | `"desk" \| "laptop-coffee" \| "peek" \| "surf"` | | | Tiburoncín's pose. Without it the hero is a panel with text. |
|
|
1146
|
+
| `title` | `ReactNode` | yes | | |
|
|
1147
|
+
| `variant` | `"header" \| "centered"` | | `header` | `header` bleeds the pose off the corner; `centered` puts it on top and centres the text, for a page that is only this. |
|
|
1095
1148
|
|
|
1096
1149
|
### LinkRow
|
|
1097
1150
|
|
|
1098
|
-
|
|
1151
|
+
Source: `src/components/link-row/index.tsx`
|
|
1099
1152
|
|
|
1100
|
-
|
|
1153
|
+
Migrated from `links/src/components/Card.astro`. The original scaled the card to 102 %, lifted the title by a pixel and rotated and enlarged the icon on hover — four movements the system does not allow. Here the hover changes the border and the icon's color, and nothing else.
|
|
1101
1154
|
|
|
1102
|
-
-
|
|
1155
|
+
- Extends: `Omit<CardShellProps, 'children'>`
|
|
1103
1156
|
|
|
1104
|
-
| prop |
|
|
1157
|
+
| prop | type | req. | default | what it does |
|
|
1105
1158
|
| --- | --- | --- | --- | --- |
|
|
1106
|
-
| `asChild` | `boolean \| undefined` | | |
|
|
1159
|
+
| `asChild` | `boolean \| undefined` | | | Renders the child instead of an `<a>`. It is how Next's or Astro's `Link` plugs in without the library depending on any router. |
|
|
1107
1160
|
| `description` | `ReactNode` | | | |
|
|
1108
|
-
| `external` | `boolean \| undefined` | | `false` |
|
|
1109
|
-
| `icon` | `ReactNode` | | |
|
|
1110
|
-
| `name` | `ReactNode` |
|
|
1161
|
+
| `external` | `boolean \| undefined` | | `false` | Marks the link as external: adds the arrow and the safe `rel`. |
|
|
1162
|
+
| `icon` | `ReactNode` | | | The target's SVG glyph. Never an emoji. |
|
|
1163
|
+
| `name` | `ReactNode` | yes | | |
|
|
1111
1164
|
|
|
1112
1165
|
### Nav, NavItem
|
|
1113
1166
|
|
|
1114
|
-
|
|
1167
|
+
Source: `src/components/nav/index.tsx`
|
|
1115
1168
|
|
|
1116
1169
|
**Nav**
|
|
1117
|
-
|
|
1170
|
+
The site bar: 64px, abyss at 86 % and a 14px blur behind it.
|
|
1118
1171
|
|
|
1119
|
-
-
|
|
1172
|
+
- Extends: `ComponentPropsWithoutRef<'header'>`
|
|
1120
1173
|
|
|
1121
|
-
| prop |
|
|
1174
|
+
| prop | type | req. | default | what it does |
|
|
1122
1175
|
| --- | --- | --- | --- | --- |
|
|
1123
|
-
| `actions` | `ReactNode` | | |
|
|
1124
|
-
| `brand` | `ReactNode` | | |
|
|
1176
|
+
| `actions` | `ReactNode` | | | Actions on the right: conversion, theme switch, search. |
|
|
1177
|
+
| `brand` | `ReactNode` | | | The logo, on the left. |
|
|
1125
1178
|
|
|
1126
1179
|
**NavItem**
|
|
1127
|
-
|
|
1180
|
+
The `./` is put there by the component, not by whoever uses it.
|
|
1128
1181
|
|
|
1129
|
-
-
|
|
1182
|
+
- Extends: `ComponentPropsWithoutRef<'a'>`
|
|
1130
1183
|
|
|
1131
|
-
| prop |
|
|
1184
|
+
| prop | type | req. | default | what it does |
|
|
1132
1185
|
| --- | --- | --- | --- | --- |
|
|
1133
|
-
| `active` | `boolean \| undefined` | | `false` |
|
|
1134
|
-
| `asChild` | `boolean \| undefined` | | `false` |
|
|
1186
|
+
| `active` | `boolean \| undefined` | | `false` | Current section: biolume with a 1px underline. |
|
|
1187
|
+
| `asChild` | `boolean \| undefined` | | `false` | Renders the child instead of an `<a>`, for the router's `Link`. |
|
|
1135
1188
|
|
|
1136
1189
|
### NewsletterForm
|
|
1137
1190
|
|
|
1138
|
-
|
|
1191
|
+
Source: `src/components/newsletter-form/index.tsx`
|
|
1139
1192
|
|
|
1140
|
-
-
|
|
1193
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'section'>, 'title' \| 'onSubmit'>`
|
|
1141
1194
|
|
|
1142
|
-
| prop |
|
|
1195
|
+
| prop | type | req. | default | what it does |
|
|
1143
1196
|
| --- | --- | --- | --- | --- |
|
|
1197
|
+
| `aside` | `ReactNode` | | | The illustration, as a second column inside the panel. |
|
|
1144
1198
|
| `basePath` | `string` | | | |
|
|
1145
1199
|
| `description` | `ReactNode` | | | |
|
|
1146
|
-
| `disclaimer` | `ReactNode` | | |
|
|
1147
|
-
| `errorMessage` | `ReactNode` | | `No se pudo suscribir ese
|
|
1148
|
-
| `
|
|
1149
|
-
| `
|
|
1150
|
-
| `
|
|
1151
|
-
| `
|
|
1200
|
+
| `disclaimer` | `ReactNode` | | | The small print. It is the «sin spam», which is why it accepts a face. |
|
|
1201
|
+
| `errorMessage` | `ReactNode` | | `No se pudo suscribir ese email. Revísalo y vuelve a intentar.` | |
|
|
1202
|
+
| `expression` | `"annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink"` | | | |
|
|
1203
|
+
| `fieldErrors` | `{ name?: ReactNode; email?: ReactNode; }` | | | A message under one specific field, instead of the single alert. |
|
|
1204
|
+
| `fieldLabel` | `string` | | `Email electrónico` | |
|
|
1205
|
+
| `nameField` | `boolean` | | `false` | Adds the name field ahead of the email one. |
|
|
1206
|
+
| `nameInputProps` | `Omit<InputProps, "id" \| "disabled" \| "name">` | | | Whatever the project needs to hang off the name field: `minLength`, `maxLength`, `pattern`. The library imposes none of the three. |
|
|
1152
1207
|
| `nameLabel` | `string` | | `Nombre` | |
|
|
1153
1208
|
| `namePlaceholder` | `string` | | `Cómo te llamas` | |
|
|
1154
|
-
| `
|
|
1155
|
-
| `
|
|
1156
|
-
| `
|
|
1209
|
+
| `onFieldChange` | `(field: "email" \| "name", value: string) => void` | | | Fires when either field changes. It is where the project clears its error. |
|
|
1210
|
+
| `onSubmitEmail` | `(email: string, name?: string \| undefined) => void` | | | Fires with the email already read from the field, and with the name if that field is enabled. |
|
|
1211
|
+
| `placeholder` | `string` | | `tu@email.dev` | |
|
|
1212
|
+
| `resetOnSuccess` | `boolean` | | `true` | Empties the fields after a successful subscription. On by default. |
|
|
1213
|
+
| `state` | `"success" \| "error" \| "idle" \| "sending"` | | `idle` | |
|
|
1157
1214
|
| `submitLabel` | `string` | | `Suscribirme` | |
|
|
1158
|
-
| `successMessage` | `ReactNode` | | `Ya estás dentro. Te llega un
|
|
1159
|
-
| `title` | `ReactNode` |
|
|
1215
|
+
| `successMessage` | `ReactNode` | | `Ya estás dentro. Te llega un email cada dos semanas, y nada más.` | |
|
|
1216
|
+
| `title` | `ReactNode` | yes | | |
|
|
1160
1217
|
|
|
1161
1218
|
### PageHeader
|
|
1162
1219
|
|
|
1163
|
-
|
|
1220
|
+
Source: `src/components/page-header/index.tsx`
|
|
1164
1221
|
|
|
1165
|
-
|
|
1222
|
+
One header at two scales, not two components.
|
|
1166
1223
|
|
|
1167
|
-
-
|
|
1224
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'header'>, 'title'> & VariantProps<typeof header>`
|
|
1168
1225
|
|
|
1169
|
-
| prop |
|
|
1226
|
+
| prop | type | req. | default | what it does |
|
|
1170
1227
|
| --- | --- | --- | --- | --- |
|
|
1171
|
-
| `action` | `ReactNode` | | |
|
|
1172
|
-
| `as` | `"
|
|
1228
|
+
| `action` | `ReactNode` | | | Slot for the calls to action. If a conversion button goes here, it is the only one on the screen. |
|
|
1229
|
+
| `as` | `"h1" \| "h2"` | | `h1` | The headline's level. `h1` unless the page already has one. |
|
|
1173
1230
|
| `description` | `ReactNode` | | | |
|
|
1174
|
-
| `eyebrow` | `ReactNode` | | | Mono,
|
|
1231
|
+
| `eyebrow` | `ReactNode` | | | Mono, small caps, in accent. It is the section the page belongs to. |
|
|
1175
1232
|
| `size` | `"display" \| "page"` | | `page` | |
|
|
1176
|
-
| `title` | `ReactNode` |
|
|
1233
|
+
| `title` | `ReactNode` | yes | | |
|
|
1177
1234
|
|
|
1178
1235
|
### ScrollingProgressBar
|
|
1179
1236
|
|
|
1180
|
-
|
|
1237
|
+
Source: `src/components/scrolling-progress-bar/index.tsx`
|
|
1181
1238
|
|
|
1182
|
-
|
|
1239
|
+
How much you have read. It is NOT `Progress` under another name.
|
|
1183
1240
|
|
|
1184
|
-
-
|
|
1241
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'div'>, 'children'>`
|
|
1185
1242
|
|
|
1186
|
-
| prop |
|
|
1243
|
+
| prop | type | req. | default | what it does |
|
|
1187
1244
|
| --- | --- | --- | --- | --- |
|
|
1188
|
-
| `sticky` | `boolean` | | `true` |
|
|
1189
|
-
| `target` | `RefObject<HTMLElement \| null>` | | |
|
|
1190
|
-
| `tone` | `"accent" \| "warm"` | | `accent` |
|
|
1245
|
+
| `sticky` | `boolean` | | `true` | Pins the bar to the top edge of the window. |
|
|
1246
|
+
| `target` | `RefObject<HTMLElement \| null>` | | | The element being measured. Without it, the whole document. |
|
|
1247
|
+
| `tone` | `"accent" \| "warm"` | | `accent` | Sand instead of biolume, to match course progress. |
|
|
1191
1248
|
|
|
1192
1249
|
### SidebarItem, SidebarNav
|
|
1193
1250
|
|
|
1194
|
-
|
|
1251
|
+
Source: `src/components/sidebar-nav/index.tsx`
|
|
1195
1252
|
|
|
1196
1253
|
**SidebarItem**
|
|
1197
|
-
|
|
1254
|
+
The blog admin's sidebar.
|
|
1198
1255
|
|
|
1199
|
-
-
|
|
1256
|
+
- Extends: `ComponentPropsWithoutRef<'a'>`
|
|
1200
1257
|
|
|
1201
|
-
| prop |
|
|
1258
|
+
| prop | type | req. | default | what it does |
|
|
1202
1259
|
| --- | --- | --- | --- | --- |
|
|
1203
1260
|
| `active` | `boolean \| undefined` | | `false` | |
|
|
1204
1261
|
| `asChild` | `boolean \| undefined` | | `false` | |
|
|
1205
|
-
| `badge` | `ReactNode` | | |
|
|
1262
|
+
| `badge` | `ReactNode` | | | Counter on the right: pending drafts, unused media. |
|
|
1206
1263
|
|
|
1207
1264
|
**SidebarNav**
|
|
1208
|
-
-
|
|
1265
|
+
- Extends: `ComponentPropsWithoutRef<'nav'>`
|
|
1209
1266
|
|
|
1210
|
-
| prop |
|
|
1267
|
+
| prop | type | req. | default | what it does |
|
|
1211
1268
|
| --- | --- | --- | --- | --- |
|
|
1212
1269
|
| `branch` | `ReactNode` | | | |
|
|
1213
|
-
| `version` | `ReactNode` | | |
|
|
1270
|
+
| `version` | `ReactNode` | | | Version and branch, at the bottom. |
|
|
1214
1271
|
|
|
1215
1272
|
### Stat
|
|
1216
1273
|
|
|
1217
|
-
|
|
1274
|
+
Source: `src/components/stat/index.tsx`
|
|
1218
1275
|
|
|
1219
|
-
|
|
1276
|
+
A large metric: the number in the `stat` scale and its name underneath.
|
|
1220
1277
|
|
|
1221
|
-
-
|
|
1278
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'div'>, 'title'>`
|
|
1222
1279
|
|
|
1223
|
-
| prop |
|
|
1280
|
+
| prop | type | req. | default | what it does |
|
|
1224
1281
|
| --- | --- | --- | --- | --- |
|
|
1225
|
-
| `description` | `ReactNode` | | |
|
|
1226
|
-
| `icon` | `ReactNode` | | |
|
|
1227
|
-
| `label` | `ReactNode` |
|
|
1228
|
-
| `progress` | `number` | | |
|
|
1229
|
-
| `tone` | `"neutral" \| "alerta"` | | `neutral` | `alerta`
|
|
1230
|
-
| `value` | `ReactNode` |
|
|
1282
|
+
| `description` | `ReactNode` | | | The standfirst: the nuance the number alone does not give. «12 aplicaciones» does not say whether that is a lot, and this is where that gets said. |
|
|
1283
|
+
| `icon` | `ReactNode` | | | Glyph beside the title, at 1em. It inherits `currentColor`, so it follows the title's tone and does not have to be tinted separately. |
|
|
1284
|
+
| `label` | `ReactNode` | yes | | What is being counted. It goes in mono small caps. |
|
|
1285
|
+
| `progress` | `number` | | | With `progress`, the metric reads as progress and adds the bar. |
|
|
1286
|
+
| `tone` | `"neutral" \| "alerta"` | | `neutral` | `alerta` only when the number IS the problem. |
|
|
1287
|
+
| `value` | `ReactNode` | yes | | The number, already formatted. The library imposes no locale. |
|
|
1231
1288
|
|
|
1232
1289
|
### TalkCard
|
|
1233
1290
|
|
|
1234
|
-
|
|
1291
|
+
Source: `src/components/talk-card/index.tsx`
|
|
1292
|
+
|
|
1293
|
+
A talk has more than one destination, and that is what shapes this type.
|
|
1235
1294
|
|
|
1236
|
-
-
|
|
1295
|
+
- Extends: `\| (Omit<CardShellProps, 'children' \| 'title'> & TalkContent & { /** The card is the link. Do not pass resources with this. */ resources?: never; }) \| (Omit<ComponentPropsWithoutRef<'article'>, 'title'> & TalkContent & { /** * Slides, repo, recording. They are the links, so the card stops being * one. */ resources: ReactNode; })`
|
|
1237
1296
|
|
|
1238
|
-
| prop |
|
|
1297
|
+
| prop | type | req. | default | what it does |
|
|
1239
1298
|
| --- | --- | --- | --- | --- |
|
|
1240
|
-
| `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. |
|
|
1241
1299
|
| `date` | `ReactNode` | | | |
|
|
1242
1300
|
| `dateTime` | `string` | | | |
|
|
1243
|
-
| `description` | `ReactNode` | | |
|
|
1244
|
-
| `event` | `ReactNode` |
|
|
1301
|
+
| `description` | `ReactNode` | | | What the talk was about. Clamped to two lines, same as `ArticleCard`'s `excerpt`, so the grid does not fall out of line. |
|
|
1302
|
+
| `event` | `ReactNode` | yes | | Where it was given: the conference, the meetup, the team. |
|
|
1245
1303
|
| `location` | `ReactNode` | | | |
|
|
1246
|
-
| `
|
|
1247
|
-
| `
|
|
1304
|
+
| `resources` | `ReactNode` | | | The card is the link. Do not pass resources with this. Slides, repo, recording. They are the links, so the card stops being one. |
|
|
1305
|
+
| `status` | `ReactNode` | | | Short status label: «con vídeo», «próxima», «solo audio». |
|
|
1306
|
+
| `title` | `ReactNode` | yes | | |
|
|
1248
1307
|
|
|
1249
1308
|
### ThemeToggle
|
|
1250
1309
|
|
|
1251
|
-
|
|
1310
|
+
Source: `src/components/theme-toggle/index.tsx`
|
|
1252
1311
|
|
|
1253
|
-
|
|
1312
|
+
The control that was missing. The library defined the whole theming system and exposed nothing that changes it, so two projects were reimplementing it.
|
|
1254
1313
|
|
|
1255
|
-
-
|
|
1314
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'button'>, 'onClick'>`
|
|
1256
1315
|
|
|
1257
|
-
| prop |
|
|
1316
|
+
| prop | type | req. | default | what it does |
|
|
1258
1317
|
| --- | --- | --- | --- | --- |
|
|
1259
|
-
| `label` | `string` | | `Cambiar de
|
|
1260
|
-
| `onThemeChange` | `(
|
|
1261
|
-
| `size` | `"sm" \| "md" \| "lg" \| "icon"` | | `icon` | |
|
|
1262
|
-
| `variant` | `"primary" \| "conversion" \| "secondary" \| "tertiary"` | | `secondary` | |
|
|
1318
|
+
| `label` | `string` | | `Cambiar de theme` | Accessible name. The button has no visible text, so it is the only thing naming it. |
|
|
1319
|
+
| `onThemeChange` | `(theme: Theme) => void` | | | Fires with whichever theme ended up set, in case the project wants to record it. |
|
|
1320
|
+
| `size` | `"sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | | `icon` | |
|
|
1321
|
+
| `variant` | `"primary" \| "conversion" \| "secondary" \| "tertiary" \| "destructive" \| "destructiveOutline"` | | `secondary` | |
|
|
1263
1322
|
|
|
1264
1323
|
### TableOfContents
|
|
1265
1324
|
|
|
1266
|
-
|
|
1325
|
+
Source: `src/components/toc/index.tsx`
|
|
1267
1326
|
|
|
1268
|
-
-
|
|
1327
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'nav'>, 'children'>`
|
|
1269
1328
|
|
|
1270
|
-
| prop |
|
|
1329
|
+
| prop | type | req. | default | what it does |
|
|
1271
1330
|
| --- | --- | --- | --- | --- |
|
|
1272
|
-
| `activeHref` | `string` | | |
|
|
1273
|
-
| `items` | `readonly
|
|
1331
|
+
| `activeHref` | `string` | | | The anchor of the visible section. |
|
|
1332
|
+
| `items` | `readonly TocEntry[]` | yes | | |
|
|
1274
1333
|
| `linkAsChild` | `(props: { href: string; children: ReactNode; }) => ReactNode` | | | |
|
|
1275
1334
|
|
|
1276
|
-
##
|
|
1335
|
+
## Brand
|
|
1277
1336
|
|
|
1278
|
-
|
|
1337
|
+
Imported from `@eduardoalvarez/arrecife` or `@eduardoalvarez/arrecife/brand`. 4 exports.
|
|
1279
1338
|
|
|
1280
|
-
###
|
|
1339
|
+
### Isotype
|
|
1281
1340
|
|
|
1282
|
-
|
|
1341
|
+
Source: `src/brand/isotype.tsx`
|
|
1283
1342
|
|
|
1284
|
-
-
|
|
1343
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'img'>, 'src' \| 'alt'>`
|
|
1285
1344
|
|
|
1286
|
-
| prop |
|
|
1345
|
+
| prop | type | req. | default | what it does |
|
|
1287
1346
|
| --- | --- | --- | --- | --- |
|
|
1288
|
-
| `alt` | `string` | | |
|
|
1289
|
-
| `
|
|
1290
|
-
| `
|
|
1347
|
+
| `alt` | `string` | | | Alt text. Empty when the isotype accompanies text that already names it. |
|
|
1348
|
+
| `background` | `"dark" \| "light"` | | `dark` | Which background it sits on. Deciding is mandatory even though it has a default: the fin's body is nearly black, so the two-blue variant disappears over abyss. Being a prop, the rule stops being something to remember. |
|
|
1349
|
+
| `basePath` | `string` | | `ASSETS_PATH` | |
|
|
1291
1350
|
|
|
1292
1351
|
### Logo
|
|
1293
1352
|
|
|
1294
|
-
|
|
1353
|
+
Source: `src/brand/logo.tsx`
|
|
1295
1354
|
|
|
1296
|
-
|
|
1355
|
+
The wordmark comes from `naming.wordmark`, not from a hand-written string, and it always reads «Eduardo Álvarez». The mascot is called Tiburoncín and its name never appears inside the logo: there is no prop that changes the text.
|
|
1297
1356
|
|
|
1298
|
-
-
|
|
1357
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'span'>, 'children'>`
|
|
1299
1358
|
|
|
1300
|
-
| prop |
|
|
1359
|
+
| prop | type | req. | default | what it does |
|
|
1301
1360
|
| --- | --- | --- | --- | --- |
|
|
1302
|
-
| `
|
|
1303
|
-
| `
|
|
1304
|
-
| `
|
|
1305
|
-
| `
|
|
1361
|
+
| `background` | `"dark" \| "light"` | | `dark` | |
|
|
1362
|
+
| `basePath` | `string` | | `ASSETS_PATH` | |
|
|
1363
|
+
| `isotypeOnly` | `boolean \| undefined` | | `false` | Hides the wordmark and leaves only the fin, for very narrow bars. |
|
|
1364
|
+
| `withTagline` | `boolean \| undefined` | | `false` | Adds the tagline under the wordmark, separated from the fin by a divider. |
|
|
1306
1365
|
|
|
1307
|
-
###
|
|
1366
|
+
### Mascot, MascotFace
|
|
1308
1367
|
|
|
1309
|
-
|
|
1368
|
+
Source: `src/brand/mascot.tsx`
|
|
1310
1369
|
|
|
1311
|
-
**
|
|
1312
|
-
Tiburoncín
|
|
1370
|
+
**Mascot**
|
|
1371
|
+
Full-body Tiburoncín.
|
|
1313
1372
|
|
|
1314
|
-
-
|
|
1373
|
+
- Extends: `Base`
|
|
1315
1374
|
|
|
1316
|
-
| prop |
|
|
1375
|
+
| prop | type | req. | default | what it does |
|
|
1317
1376
|
| --- | --- | --- | --- | --- |
|
|
1318
|
-
| `alt` | `string` | | |
|
|
1319
|
-
| `basePath` | `string` | | `
|
|
1320
|
-
| `pose` | `"desk" \| "laptop-coffee" \| "peek" \| "surf"` |
|
|
1377
|
+
| `alt` | `string` | | | Alt text. Empty by default: the mascot is illustration and the text beside it already says what there is to know. Fill it in only when the image carries information that is not written next to it. |
|
|
1378
|
+
| `basePath` | `string` | | `ASSETS_PATH` | |
|
|
1379
|
+
| `pose` | `"desk" \| "laptop-coffee" \| "peek" \| "surf"` | yes | | |
|
|
1321
1380
|
|
|
1322
|
-
**
|
|
1323
|
-
|
|
1381
|
+
**MascotFace**
|
|
1382
|
+
Tiburoncín's head, with an expression.
|
|
1324
1383
|
|
|
1325
|
-
-
|
|
1384
|
+
- Extends: `Base`
|
|
1326
1385
|
|
|
1327
|
-
| prop |
|
|
1386
|
+
| prop | type | req. | default | what it does |
|
|
1328
1387
|
| --- | --- | --- | --- | --- |
|
|
1329
|
-
| `alt` | `string` | | |
|
|
1330
|
-
| `basePath` | `string` | | `
|
|
1331
|
-
| `
|
|
1388
|
+
| `alt` | `string` | | | Alt text. Empty by default: the mascot is illustration and the text beside it already says what there is to know. Fill it in only when the image carries information that is not written next to it. |
|
|
1389
|
+
| `basePath` | `string` | | `ASSETS_PATH` | |
|
|
1390
|
+
| `expression` | `"annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink"` | yes | | |
|
|
1332
1391
|
|
|
1333
|
-
##
|
|
1392
|
+
## Forms
|
|
1334
1393
|
|
|
1335
|
-
|
|
1394
|
+
Imported from `@eduardoalvarez/arrecife/form` · requires `react-hook-form`. 7 exports.
|
|
1336
1395
|
|
|
1337
1396
|
### FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage, Form
|
|
1338
1397
|
|
|
1339
|
-
|
|
1398
|
+
Source: `src/form/index.tsx`
|
|
1340
1399
|
|
|
1341
1400
|
**FormField**
|
|
1342
|
-
|
|
1401
|
+
A controlled field. It wraps RHF's `Controller` and also publishes the name into context, which is where the label and the message read it from without having to repeat it three times.
|
|
1343
1402
|
|
|
1344
|
-
-
|
|
1403
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1345
1404
|
|
|
1346
1405
|
**FormItem**
|
|
1347
|
-
|
|
1406
|
+
The field's box: label, control, help and message, in a column.
|
|
1348
1407
|
|
|
1349
|
-
-
|
|
1408
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1350
1409
|
|
|
1351
1410
|
**FormLabel**
|
|
1352
|
-
|
|
1411
|
+
The label is NOT tinted red when the field fails.
|
|
1353
1412
|
|
|
1354
|
-
-
|
|
1413
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1355
1414
|
|
|
1356
1415
|
**FormControl**
|
|
1357
|
-
|
|
1416
|
+
Wraps the control and wires its attributes: the `id` the label points at, the `aria-describedby` with the help and the message, and the `aria-invalid`.
|
|
1358
1417
|
|
|
1359
|
-
-
|
|
1418
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1360
1419
|
|
|
1361
1420
|
**FormDescription**
|
|
1362
|
-
|
|
1421
|
+
The field's help text. It is always announced, error or not.
|
|
1363
1422
|
|
|
1364
|
-
-
|
|
1423
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1365
1424
|
|
|
1366
1425
|
**FormMessage**
|
|
1367
|
-
|
|
1426
|
+
The validation message. With no error it renders nothing: a gap reserved for the failure shifts the rest of the form every time it appears.
|
|
1368
1427
|
|
|
1369
|
-
-
|
|
1428
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1370
1429
|
|
|
1371
1430
|
**Form**
|
|
1372
|
-
|
|
1431
|
+
The layer that ties the controls to a form with validation and messages.
|
|
1373
1432
|
|
|
1374
|
-
-
|
|
1433
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1375
1434
|
|
|
1376
|
-
##
|
|
1435
|
+
## Charts
|
|
1377
1436
|
|
|
1378
|
-
|
|
1437
|
+
Imported from `@eduardoalvarez/arrecife/chart` · requires `recharts`. 5 exports.
|
|
1379
1438
|
|
|
1380
1439
|
### ChartContainer, ChartTooltip, ChartLegend, ChartTooltipContent, ChartLegendContent
|
|
1381
1440
|
|
|
1382
|
-
|
|
1441
|
+
Source: `src/chart/index.tsx`
|
|
1383
1442
|
|
|
1384
1443
|
**ChartContainer**
|
|
1385
|
-
|
|
1444
|
+
Wraps the chart in a `<figure>` with an accessible name and gives Recharts the concrete height it needs to measure itself.
|
|
1386
1445
|
|
|
1387
|
-
-
|
|
1446
|
+
- Extends: `Omit<ComponentPropsWithoutRef<'figure'>, 'title'>`
|
|
1388
1447
|
|
|
1389
|
-
| prop |
|
|
1448
|
+
| prop | type | req. | default | what it does |
|
|
1390
1449
|
| --- | --- | --- | --- | --- |
|
|
1391
|
-
| `height` | `number` | | `320` |
|
|
1392
|
-
| `label` | `string` |
|
|
1393
|
-
| `summary` | `ReactNode` | | |
|
|
1450
|
+
| `height` | `number` | | `320` | Height in pixels. Recharts needs a concrete one to measure itself. |
|
|
1451
|
+
| `label` | `string` | yes | | What the chart shows, in one sentence. Mandatory, like `Progress`'s `label`: a bar `<svg>` with no accessible name is not «a chart without a label», it is an empty region. |
|
|
1452
|
+
| `summary` | `ReactNode` | | | What the chart says, in words. It goes in a visually hidden `figcaption`. |
|
|
1394
1453
|
|
|
1395
1454
|
**ChartTooltip**
|
|
1396
|
-
|
|
1455
|
+
Recharts' `Tooltip` with the system's defaults: no animation, and the cursor tinted `surfaceRaised`.
|
|
1397
1456
|
|
|
1398
|
-
-
|
|
1457
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1399
1458
|
|
|
1400
1459
|
**ChartLegend**
|
|
1401
|
-
-
|
|
1460
|
+
- No own props: it passes through those of the element or primitive it wraps.
|
|
1402
1461
|
|
|
1403
1462
|
**ChartTooltipContent**
|
|
1404
|
-
|
|
1463
|
+
The tooltip's box. It is a system card — `surface`, control border, standard shadow — and not Recharts' white box, which in dark mode is a white rectangle on top of a dark panel.
|
|
1405
1464
|
|
|
1406
|
-
-
|
|
1465
|
+
- Extends: `{ active?: boolean \| undefined; payload?: readonly ChartPayloadItem[] \| undefined; label?: ReactNode; /** Formats the value. Without it, it is printed as is: the library imposes no locale. */ formatter?: ((value: unknown, item: ChartPayloadItem) => ReactNode) \| undefined; /** Hides the header, for a single-category chart. */ hideLabel?: boolean; className?: string; }`
|
|
1407
1466
|
|
|
1408
|
-
| prop |
|
|
1467
|
+
| prop | type | req. | default | what it does |
|
|
1409
1468
|
| --- | --- | --- | --- | --- |
|
|
1410
1469
|
| `active` | `boolean \| undefined` | | | |
|
|
1411
1470
|
| `className` | `string` | | | |
|
|
1412
|
-
| `formatter` | `(
|
|
1413
|
-
| `hideLabel` | `boolean` | | `false` |
|
|
1471
|
+
| `formatter` | `(value: unknown, item: ChartPayloadItem) => ReactNode` | | | Formats the value. Without it, it is printed as is: the library imposes no locale. |
|
|
1472
|
+
| `hideLabel` | `boolean` | | `false` | Hides the header, for a single-category chart. |
|
|
1414
1473
|
| `label` | `ReactNode` | | | |
|
|
1415
1474
|
| `payload` | `readonly ChartPayloadItem[]` | | | |
|
|
1416
1475
|
|
|
1417
1476
|
**ChartLegendContent**
|
|
1418
|
-
|
|
1477
|
+
The legend, with the tooltip's same square swatch and the `label` scale.
|
|
1419
1478
|
|
|
1420
|
-
-
|
|
1479
|
+
- Extends: `{ payload?: readonly ChartPayloadItem[] \| undefined; className?: string; }`
|
|
1421
1480
|
|
|
1422
|
-
| prop |
|
|
1481
|
+
| prop | type | req. | default | what it does |
|
|
1423
1482
|
| --- | --- | --- | --- | --- |
|
|
1424
1483
|
| `className` | `string` | | | |
|
|
1425
1484
|
| `payload` | `readonly ChartPayloadItem[]` | | | |
|
|
1426
1485
|
|
|
1427
|
-
##
|
|
1486
|
+
## Exports that are not components
|
|
1428
1487
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1488
|
+
The root re-exports everything from `./tokens` and `./brand` for convenience.
|
|
1489
|
+
Each one appears exactly once, under the most specific subpath that publishes
|
|
1490
|
+
it: if the code does not mount React, that subpath is the one to import.
|
|
1491
|
+
|
|
1492
|
+
### `@eduardoalvarez/arrecife/variants`
|
|
1493
|
+
|
|
1494
|
+
| export | type | what it is |
|
|
1495
|
+
| --- | --- | --- |
|
|
1496
|
+
| `alertVariants` | `(props?: (ConfigVariants<{ variant: { accent: string; success: string; warning: string; error: string; }; emphasis: { subtle: string; strong: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1497
|
+
| `avatarVariants` | `(props?: (ConfigVariants<{ size: { sm: string; md: string; lg: string; xl: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1498
|
+
| `badgeVariants` | `(props?: (ConfigVariants<{ variant: { neutral: string; accent: string; warm: string; success: string; warning: string; error: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1499
|
+
| `buttonVariants` | `(props?: (ConfigVariants<{ variant: { primary: string[]; conversion: string; secondary: string[]; tertiary: string[]; destructive: string; destructiveOutline: string[]; }; size: { sm: string; md: string; lg: string; icon: string; 'icon-sm': string; }; }> & ClassProp) \| undefined): string` | |
|
|
1500
|
+
| `CARD` | `string[]` | |
|
|
1501
|
+
| `CARD_HOVER` | `"transition-standard hover:border-hairline-hover"` | |
|
|
1502
|
+
| `CARD_SURFACE` | `"rounded-card border-hairline bg-surface border"` | |
|
|
1503
|
+
| `categoryBadgeVariants` | `(props?: (ConfigVariants<{ active: { false: string; true: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1504
|
+
| `metricBadgeVariants` | `string[]` | |
|
|
1505
|
+
| `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` | |
|
|
1432
1506
|
|
|
1433
1507
|
### `@eduardoalvarez/arrecife/tokens`
|
|
1434
1508
|
|
|
1435
|
-
| export |
|
|
1509
|
+
| export | type | what it is |
|
|
1436
1510
|
| --- | --- | --- |
|
|
1437
|
-
| `brand` | `{ readonly body: "#3E7CB1"; readonly spots: "#C2D7E7"; readonly hull: "#0B1524"; }` |
|
|
1511
|
+
| `brand` | `{ readonly body: "#3E7CB1"; readonly spots: "#C2D7E7"; readonly hull: "#0B1524"; }` | Brand — identical in both modes. |
|
|
1438
1512
|
| `colors` | `{ dark, light }` | |
|
|
1439
|
-
| `control` | `{ readonly sm: 14; readonly md: 22; readonly lg: 30; readonly icon: 42; }` |
|
|
1440
|
-
| `dark` | `{ background, surface, surfaceRaised, border, hairline, hairlineHover, textPrimary, textSecondary, textMuted, accent, accentHover, accentOn, warm, warmHover, warmOn, success, warning, error }` |
|
|
1513
|
+
| `control` | `{ readonly sm: 14; readonly md: 22; readonly lg: 30; readonly icon: 42; readonly iconSm: 32; }` | Controls, from the document: `sm 8/14 · md 12/22 · lg 15/30 · icon 42×42`. |
|
|
1514
|
+
| `dark` | `{ background, surface, surfaceRaised, border, hairline, hairlineHover, textPrimary, textSecondary, textMuted, accent, accentHover, accentOn, warm, warmHover, warmOn, success, warning, error, danger, dangerHover, dangerOn }` | Dark mode (primary). Contrast measured against `background` #091319. |
|
|
1441
1515
|
| `fonts` | `{ display, sans, mono }` | |
|
|
1442
1516
|
| `gradient` | `{ dark, light }` | |
|
|
1443
|
-
| `light` | `{ background, surface, surfaceRaised, border, hairline, hairlineHover, textPrimary, textSecondary, textMuted, accent, accentHover, accentOn, warm, warmHover, warmOn, success, warning, error }` |
|
|
1444
|
-
| `limits` | `{ readonly minScreenPx: 13; readonly minPrintPt: 12; readonly measure: "68ch"; }` |
|
|
1445
|
-
| `motion` | `{ readonly duration: "150ms"; readonly easing: "ease-out"; readonly properties: "color, background-color, border-color, fill, stroke"; }` | 150ms ease-out —
|
|
1446
|
-
| `naming` | `{ readonly wordmark: "Eduardo Álvarez"; readonly mascot: "Tiburoncín"; readonly domain: "eduardoalvarez.dev"; }` |
|
|
1517
|
+
| `light` | `{ background, surface, surfaceRaised, border, hairline, hairlineHover, textPrimary, textSecondary, textMuted, accent, accentHover, accentOn, warm, warmHover, warmOn, success, warning, error, danger, dangerHover, dangerOn }` | Light mode. Contrast measured against `background` #F6F2EA. `background` is WARM white: never #FFF as the page background. |
|
|
1518
|
+
| `limits` | `{ readonly minScreenPx: 13; readonly minPrintPt: 12; readonly measure: "68ch"; }` | Hard legibility limits. |
|
|
1519
|
+
| `motion` | `{ readonly duration: "150ms"; readonly easing: "ease-out"; readonly properties: "color, background-color, border-color, fill, stroke"; }` | 150ms ease-out — color and border only. The system animates neither position nor scale: states are communicated with border and color, not with movement. |
|
|
1520
|
+
| `naming` | `{ readonly wordmark: "Eduardo Álvarez"; readonly mascot: "Tiburoncín"; readonly domain: "eduardoalvarez.dev"; }` | The wordmark always reads «Eduardo Álvarez». The mascot is called Tiburoncín and its name never appears inside the logo. |
|
|
1447
1521
|
| `radius` | `{ readonly chip: 6; readonly control: 10; readonly card: 14; readonly panel: 16; readonly pill: 999; }` | |
|
|
1448
|
-
| `series` | `{ readonly dark: readonly ["#35D6C0", "#F2A65A", "#3E7CB1", "#71919C"]; readonly light: readonly ["#0D7C6F", "#A65B27", "#3E7CB1", "#626A75"]; }` |
|
|
1449
|
-
| `shadow` | `{ readonly standard: "0 1px 2px rgba(0, 0, 0, 0.35)"; }` |
|
|
1450
|
-
| `sintaxis` | `{ fondo, identificador, literal, palabraClave, comentario, invalido }` | La paleta del resaltado de sintaxis. |
|
|
1522
|
+
| `series` | `{ readonly dark: readonly ["#35D6C0", "#F2A65A", "#3E7CB1", "#71919C"]; readonly light: readonly ["#0D7C6F", "#A65B27", "#3E7CB1", "#626A75"]; }` | The chart series palette. FOUR, for the same reason as the syntax palette: the system communicates with color and border, not with chromatic noise. |
|
|
1523
|
+
| `shadow` | `{ readonly standard: "0 1px 2px rgba(0, 0, 0, 0.35)"; }` | A single level. There is no elevation scale. |
|
|
1451
1524
|
| `size` | `{ readonly nav: 64; readonly content: 760; readonly wide: 1180; }` | |
|
|
1452
|
-
| `spacing` | `{ readonly stepXs: 8; readonly stepSm: 12; readonly stepMd: 16; readonly stepLg: 26; readonly stepXl: 40; readonly section: 96; }` |
|
|
1453
|
-
| `
|
|
1454
|
-
| `
|
|
1525
|
+
| `spacing` | `{ readonly stepXs: 8; readonly stepSm: 12; readonly stepMd: 16; readonly stepLg: 26; readonly stepXl: 40; readonly section: 96; }` | Page rhythm. All five steps carry `step` in the name, and that is not decoration: it is the fix for a bug that never surfaced anywhere. |
|
|
1526
|
+
| `syntax` | `{ background, identifier, literal, keyword, comment, invalid }` | The syntax highlighting palette. |
|
|
1527
|
+
| `tagline` | `{ long, short, en }` | |
|
|
1528
|
+
| `tokens` | `{ colors, brand, fonts, typeScale, limits, radius, control, spacing, size, gradient, syntax, series, shadow, motion, tagline, naming }` | Every token in a single object, for Satori templates and generators. |
|
|
1455
1529
|
| `typeScale` | `{ display, stat, h1, h2, h3, body, lead, ui, label, tag, chip, meta, eyebrow }` | |
|
|
1456
1530
|
|
|
1457
|
-
|
|
1531
|
+
Types (13): `BrandToken`, `ColorMode`, `ColorToken`, `ControlToken`, `FontToken`, `GradientToken`, `RadiusToken`, `SeriesToken`, `SizeToken`, `SpacingToken`, `SyntaxToken`, `Tokens`, `TypeScaleToken`.
|
|
1532
|
+
|
|
1533
|
+
### `@eduardoalvarez/arrecife/theme`
|
|
1534
|
+
|
|
1535
|
+
| export | type | what it is |
|
|
1536
|
+
| --- | --- | --- |
|
|
1537
|
+
| `applyTheme` | `(theme: Theme, persist?: boolean): void` | Sets the theme on `<html>` and persists it. |
|
|
1538
|
+
| `currentTheme` | `(): Theme` | The theme currently in place, read from the DOM. |
|
|
1539
|
+
| `preferredTheme` | `({ base }?: ThemeOptions): Theme` | The theme that applies: whatever was chosen, and with no choice, `base` if the site declared one, else whatever the system asks for. With no `prefers-color-scheme` declared, dark, which is primary. |
|
|
1540
|
+
| `storedTheme` | `(): Theme \| null` | The stored preference, if any. `null` means «nobody has chosen», which is not the same as «chose dark»: with no choice, the system decides. |
|
|
1541
|
+
| `THEME_ATTRIBUTE` | `"data-theme"` | The attribute the `[data-theme]` blocks in `theme.css` read. |
|
|
1542
|
+
| `THEME_EVENT` | `"arrecife:theme"` | The event emitted when the theme changes. |
|
|
1543
|
+
| `THEME_KEY` | `"arrecife-theme"` | The `localStorage` key. |
|
|
1544
|
+
| `themeScript` | `({ base }?: ThemeOptions): string` | The script that goes INLINE in the `<head>`, before any stylesheet. |
|
|
1545
|
+
| `toggleTheme` | `(): Theme` | Switches to the opposite one and returns whichever stuck. |
|
|
1546
|
+
| `watchTheme` | `(onChange: (theme: Theme) => void, options?: ThemeOptions): () => void` | Subscribes to theme changes and returns the function that cancels it. |
|
|
1547
|
+
|
|
1548
|
+
Types (2): `Theme`, `ThemeOptions`.
|
|
1458
1549
|
|
|
1459
1550
|
### `@eduardoalvarez/arrecife/brand`
|
|
1460
1551
|
|
|
1461
|
-
| export |
|
|
1552
|
+
| export | type | what it is |
|
|
1462
1553
|
| --- | --- | --- |
|
|
1463
|
-
| `
|
|
1464
|
-
| `
|
|
1465
|
-
| `
|
|
1466
|
-
| `
|
|
1467
|
-
| `
|
|
1468
|
-
| `
|
|
1469
|
-
| `
|
|
1554
|
+
| `ASSETS_PATH` | `"/brand"` | Where the PNGs are served from. `/brand` by default, which is where they already live in all five projects (`public/brand/`), so there is nothing to configure. |
|
|
1555
|
+
| `faceList` | `readonly ("annoyed" \| "confused" \| "hearts" \| "laughing" \| "shades" \| "waiting" \| "wink")[]` | |
|
|
1556
|
+
| `faces` | `{ annoyed, confused, hearts, laughing, shades, waiting, wink }` | The faces. They are used only in empty states, confirmations, errors, course progress and celebration — never in a hero, pricing, services, contact or CV. That is why `EmptyState` takes a face and `PageHeader` does not. |
|
|
1557
|
+
| `faceUsage` | `{ wink, waiting, laughing, shades, hearts, confused, annoyed }` | The assigned use of each face, from the manual's inventory. |
|
|
1558
|
+
| `fins` | `{ readonly color: "fin.png"; readonly foam: "fin-foam.png"; }` | The fin, in its two variants. |
|
|
1559
|
+
| `poseList` | `readonly ("desk" \| "laptop-coffee" \| "peek" \| "surf")[]` | |
|
|
1560
|
+
| `poses` | `{ readonly desk: "pose-desk.png"; readonly 'laptop-coffee': "pose-laptop-coffee.png"; readonly peek: "pose-peek.png"; readonly surf: "pose-surf.png"; }` | Full-body poses. |
|
|
1470
1561
|
|
|
1471
|
-
|
|
1562
|
+
Types (8): `Background`, `Face`, `Fin`, `IsotypeProps`, `LogoProps`, `MascotFaceProps`, `MascotProps`, `Pose`.
|
|
1472
1563
|
|
|
1473
1564
|
### `@eduardoalvarez/arrecife/shiki`
|
|
1474
1565
|
|
|
1475
|
-
| export |
|
|
1566
|
+
| export | type | what it is |
|
|
1476
1567
|
| --- | --- | --- |
|
|
1477
|
-
| `arrecife` | `
|
|
1568
|
+
| `arrecife` | `ShikiTheme` | |
|
|
1478
1569
|
|
|
1479
|
-
|
|
1570
|
+
Types (1): `ShikiTheme`.
|
|
1480
1571
|
|
|
1481
1572
|
### `@eduardoalvarez/arrecife/chart`
|
|
1482
1573
|
|
|
1483
|
-
| export |
|
|
1574
|
+
| export | type | what it is |
|
|
1484
1575
|
| --- | --- | --- |
|
|
1485
|
-
| `
|
|
1486
|
-
| `
|
|
1576
|
+
| `SERIES_COLORS` | `string[]` | All four, in order, to hand to a `Pie` with `Cell` in one go. |
|
|
1577
|
+
| `seriesColor` | `(index: number): string` | The color of series `index`, as a custom property. |
|
|
1487
1578
|
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
### `@eduardoalvarez/arrecife/tema`
|
|
1491
|
-
|
|
1492
|
-
| export | tipo | qué es |
|
|
1493
|
-
| --- | --- | --- |
|
|
1494
|
-
| `alternarTema` | `(): Tema` | Cambia al contrario y devuelve el que quedó. |
|
|
1495
|
-
| `aplicarTema` | `(tema: Tema, persistir?: boolean): void` | Pone el tema en el `<html>` y lo persiste. |
|
|
1496
|
-
| `escucharTema` | `(alCambiar: (tema: Tema) => void): () => void` | Se suscribe a los cambios de tema y devuelve la función que cancela. |
|
|
1497
|
-
| `scriptTema` | `string` | El script que va INLINE en el `<head>`, antes de cualquier hoja de estilo. |
|
|
1498
|
-
| `TEMA_ATRIBUTO` | `"data-theme"` | El atributo que leen los bloques `[data-theme]` de `theme.css`. |
|
|
1499
|
-
| `TEMA_CLAVE` | `"arrecife-tema"` | La clave de `localStorage`. |
|
|
1500
|
-
| `TEMA_EVENTO` | `"arrecife:tema"` | El evento que se emite cuando el tema cambia. |
|
|
1501
|
-
| `temaActual` | `(): Tema` | El tema que hay puesto ahora mismo, leído del DOM. |
|
|
1502
|
-
| `temaGuardado` | `(): Tema \| null` | La preferencia guardada, si la hay. `null` significa «nadie ha elegido», que no es lo mismo que «eligió oscuro»: sin elección manda el sistema. |
|
|
1503
|
-
| `temaPreferido` | `(): Tema` | El tema que corresponde: lo elegido, y si no hay elección, lo que pida el sistema. Sin `prefers-color-scheme` declarado, oscuro, que es el primario. |
|
|
1504
|
-
|
|
1505
|
-
Tipos (1): `Tema`.
|
|
1579
|
+
Types (4): `ChartContainerProps`, `ChartLegendContentProps`, `ChartPayloadItem`, `ChartTooltipContentProps`.
|
|
1506
1580
|
|
|
1507
1581
|
### `@eduardoalvarez/arrecife/form`
|
|
1508
1582
|
|
|
1509
|
-
| export |
|
|
1583
|
+
| export | type | what it is |
|
|
1510
1584
|
| --- | --- | --- |
|
|
1511
|
-
| `useFormField` | `(): { invalid: boolean; isDirty: boolean; isTouched: boolean; isValidating: boolean; error?: FieldError; name: string; id: string;
|
|
1585
|
+
| `useFormField` | `(): { invalid: boolean; isDirty: boolean; isTouched: boolean; isValidating: boolean; error?: FieldError; name: string; id: string; descriptionId: string; messageId: string; }` | What any piece of the field needs: the name, the three ids and the validation state. |
|
|
1512
1586
|
|
|
1513
1587
|
### `@eduardoalvarez/arrecife/og`
|
|
1514
1588
|
|
|
1515
|
-
| export |
|
|
1589
|
+
| export | type | what it is |
|
|
1516
1590
|
| --- | --- | --- |
|
|
1517
|
-
| `
|
|
1518
|
-
| `
|
|
1519
|
-
| `
|
|
1520
|
-
| `
|
|
1521
|
-
| `
|
|
1522
|
-
| `
|
|
1591
|
+
| `articleTemplate` | `(data: ArticleData): SatoriNode` | Article · 145° gradient over abyss, category and reading time in sand. |
|
|
1592
|
+
| `courseTemplate` | `(data: CourseData): SatoriNode` | Course · THE ONLY LIGHT TEMPLATE. |
|
|
1593
|
+
| `defaultTemplate` | `(data?: DefaultData): SatoriNode` | Default · the document's declared exception. |
|
|
1594
|
+
| `OG` | `{ readonly width: 1200; readonly height: 630; readonly margin: 64; readonly mascot: 430; readonly signatureFin: 34; readonly mascotReserve: 560; }` | The canvas and the grid. These are the production measurements. |
|
|
1595
|
+
| `plantillaBase` | `(options: { mode: "dark" \| "light"; background: string; eyebrow: { text: string; color: string; }; title: string; bajada?: string \| undefined; signature: string; firmaColor: string; base: string; mascot?: SatoriNode \| ... 1 more ... \| undefined; mascotaIzquierda?: boolean \| undefined; tinta: string; tintaSecundaria: string; }): SatoriNode` | |
|
|
1596
|
+
| `talkTemplate` | `(data: TalkData): SatoriNode` | Talk · eyebrow in biolume with the event and year, pose bleeding off the corner. |
|
|
1523
1597
|
|
|
1524
|
-
|
|
1598
|
+
Types (6): `ArticleData`, `BaseData`, `CourseData`, `DefaultData`, `SatoriNode`, `TalkData`.
|
|
1525
1599
|
|
|
1526
1600
|
### `@eduardoalvarez/arrecife`
|
|
1527
1601
|
|
|
1528
|
-
| export |
|
|
1602
|
+
| export | type | what it is |
|
|
1529
1603
|
| --- | --- | --- |
|
|
1530
|
-
| `alertVariants` | `(props?: (ConfigVariants<{ variant: { accent: string; success: string; warning: string; error: string; }; enfasis: { sutil: string; fuerte: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1531
|
-
| `avatarVariants` | `(props?: (ConfigVariants<{ size: { sm: string; md: string; lg: string; xl: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1532
|
-
| `badgeVariants` | `(props?: (ConfigVariants<{ variant: { neutral: string; accent: string; warm: string; success: string; warning: string; error: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1533
|
-
| `buttonVariants` | `(props?: (ConfigVariants<{ variant: { primary: string[]; conversion: string; secondary: string[]; tertiary: string[]; }; size: { sm: string; md: string; lg: string; icon: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1534
|
-
| `categoryBadgeVariants` | `(props?: (ConfigVariants<{ active: { false: string; true: string; }; }> & ClassProp) \| undefined): string` | |
|
|
1535
1604
|
| `cn` | `(...inputs: ClassValue[]): string` | |
|
|
1536
|
-
| `HOVER_TARJETA` | `"transition-standard hover:border-hairline-hover"` | El hover de la regla 6: solo el borde. Se aplica donde la tarjeta es pulsable. |
|
|
1537
1605
|
| `social` | `typeof import("src/lib/social")` | |
|
|
1538
|
-
| `
|
|
1539
|
-
| `
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
-
|
|
1548
|
-
|
|
1549
|
-
- `
|
|
1550
|
-
|
|
1551
|
-
- `docs/
|
|
1552
|
-
|
|
1553
|
-
- `
|
|
1554
|
-
decían lo mismo, con la resolución de cada uno.
|
|
1555
|
-
- `AGENTS.md`: para trabajar dentro del repo de la librería.
|
|
1606
|
+
| `toast` | `(message: ReactNode, options?: ToastOptions \| undefined): string` | Fires a notice. It returns its id, which is what you keep in order to close it by hand — the «guardando…» case that gets replaced when the request finishes. |
|
|
1607
|
+
| `useTheme` | `(): Theme` | The theme set right now, for a project that needs to branch in React — a different logo per mode, an image with no light version. |
|
|
1608
|
+
|
|
1609
|
+
Types (60): `AccordionProps`, `AccordionTriggerProps`, `AlertProps`, `ArticleCardProps`, `AudioPlayerMode`, `AudioPlayerProps`, `AuthorCardProps`, `AvatarProps`, `AvatarUploadProps`, `BadgeProps`, `BlockquoteProps`, `BreadcrumbProps`, `ButtonProps`, `CalendarEvent`, `CalendarProps`, `CategoryBadgeProps`, `CheckboxProps`, `CodeBlockProps`, `CodeProps`, `CourseCardProps`, `Crumb`, `DateFieldProps`, `EmptyStateProps`, `EventCalendarProps`, `FooterLinkProps`, `FooterProps`, `HeroProps`, `InputProps`, `LabelProps`, `LinkRowProps`, `MetricBadgeProps`, `NavItemProps`, `NavProps`, `NewsletterFormProps`, `NewsletterState`, `PageHeaderProps`, `PaginationLinkProps`, `PopoverContentProps`, `ProgressProps`, `RadioGroupItemProps`, `RadioGroupProps`, `ScrollingProgressBarProps`, `SeparatorProps`, `SheetContentProps`, `SidebarItemProps`, `SidebarNavProps`, `SkeletonProps`, `SocialLink`, `StatProps`, `SwitchProps`, `TableOfContentsProps`, `TabsProps`, `TalkCardProps`, `TextProps`, `TextareaProps`, `ThemeToggleProps`, `ToastOptions`, `ToastVariant`, `ToasterProps`, `TocEntry`.
|
|
1610
|
+
|
|
1611
|
+
# Where to look if this is not enough
|
|
1612
|
+
|
|
1613
|
+
- Storybook publishes every component with its stories and the props table
|
|
1614
|
+
generated from the types.
|
|
1615
|
+
- The repo's `README.md`: the reasoning behind each decision, the contrast
|
|
1616
|
+
correction table and the release cycle.
|
|
1617
|
+
- `docs/design-system.md` and `docs/brand-manual.md`: the identity documents,
|
|
1618
|
+
greppable.
|
|
1619
|
+
- `docs/decisions.md`: the points where the code and the document did not say the
|
|
1620
|
+
same thing, each with its resolution.
|
|
1621
|
+
- `AGENTS.md`: for working inside the library's repo.
|