@eduardoalvarez/arrecife 0.6.0 → 0.8.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +288 -49
  3. package/dist/brand/index.cjs +2 -2
  4. package/dist/brand/index.js +3 -3
  5. package/dist/chart/index.cjs +129 -2
  6. package/dist/chart/index.d.cts +114 -4
  7. package/dist/chart/index.d.ts +114 -4
  8. package/dist/chart/index.js +129 -5
  9. package/dist/{chunk-PMN7NR3G.js → chunk-5A5GH2PF.js} +1 -1
  10. package/dist/{chunk-DKCN7BAL.js → chunk-6IGD5REB.js} +1 -1
  11. package/dist/{chunk-XKYHTOUJ.js → chunk-FAAGZG7A.js} +1 -1
  12. package/dist/{chunk-O4TAH7YJ.js → chunk-FGFNK72B.js} +29 -5
  13. package/dist/chunk-HOADZ6GS.js +72 -0
  14. package/dist/chunk-LXRGQKMG.js +145 -0
  15. package/dist/{chunk-6O3KWB6P.js → chunk-MPZBF2TZ.js} +2 -2
  16. package/dist/{chunk-JMOOFZ3B.js → chunk-TRPBID2W.js} +1 -1
  17. package/dist/{chunk-25YNFCIF.js → chunk-XXDATT3A.js} +6 -3
  18. package/dist/doctor.mjs +248 -0
  19. package/dist/form/index.cjs +2 -2
  20. package/dist/form/index.d.cts +1 -1
  21. package/dist/form/index.d.ts +1 -1
  22. package/dist/form/index.js +4 -4
  23. package/dist/icons/index.cjs +149 -0
  24. package/dist/icons/index.d.cts +94 -0
  25. package/dist/icons/index.d.ts +94 -0
  26. package/dist/icons/index.js +28 -0
  27. package/dist/index-BbRplw_B.d.cts +58 -0
  28. package/dist/index-BbRplw_B.d.ts +58 -0
  29. package/dist/index.cjs +425 -251
  30. package/dist/index.d.cts +303 -104
  31. package/dist/index.d.ts +303 -104
  32. package/dist/index.js +283 -285
  33. package/dist/{label-MgHFKnFy.d.ts → label-DJ4HuD-R.d.cts} +3 -2
  34. package/dist/{label-MgHFKnFy.d.cts → label-DJ4HuD-R.d.ts} +3 -2
  35. package/dist/og/index.cjs +3 -2
  36. package/dist/og/index.js +1 -1
  37. package/dist/shiki/index.js +1 -1
  38. package/dist/social/data.cjs +161 -0
  39. package/dist/social/data.d.cts +161 -0
  40. package/dist/social/data.d.ts +161 -0
  41. package/dist/social/data.js +2 -0
  42. package/dist/social/index.cjs +153 -0
  43. package/dist/social/index.d.cts +2 -0
  44. package/dist/social/index.d.ts +2 -0
  45. package/dist/social/index.js +3 -0
  46. package/dist/tokens/index.cjs +29 -5
  47. package/dist/tokens/index.d.cts +37 -10
  48. package/dist/tokens/index.d.ts +37 -10
  49. package/dist/tokens/index.js +2 -2
  50. package/dist/tokens/theme.css +79 -22
  51. package/dist/variants/index.cjs +6 -3
  52. package/dist/variants/index.d.cts +6 -3
  53. package/dist/variants/index.d.ts +6 -3
  54. package/dist/variants/index.js +1 -1
  55. package/llms.txt +530 -80
  56. package/package.json +29 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,117 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0](https://github.com/Proskynete/arrecife/compare/v0.7.0...v0.8.0) (2026-09-06)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **components:** `SidebarNav`, `SidebarGroup` and `SidebarItem` are removed. A project that wants a sidebar builds one from `Nav` and `Sheet`, which is what both admin projects already did. Migration in docs/migration-0.8.md.
9
+ * **components:** `Footer` no longer accepts `children`, and `FooterLink` is removed. A row of loose text links becomes `variant="full"` with `columns`. Migration in docs/migration-0.8.md.
10
+ * **tokens:** the `caret` utility is removed. If you wrote `motion-safe:caret` by hand, Tailwind now drops it silently and your mark goes still with no error: use `pulse-accent` on a thin shape. Migration in docs/migration-0.8.md.
11
+ * **primitives:** `Table` draws its own radius, border and clip, so the wrapper `div` around it has to lose its `rounded-*`, `border` and `overflow-hidden` or you get two borders. Nothing fails at compile time. Migration in docs/migration-0.8.md.
12
+
13
+ ### 🚀 Novedades
14
+
15
+ * **chart:** AreaChart, BarChart and LineChart on the chassis ([780c976](https://github.com/Proskynete/arrecife/commit/780c97633b1d245f9de42cc42a333e0b7baf679c))
16
+ * **components:** Footer gets a full shape, and its signature draws the halo ([88c5af3](https://github.com/Proskynete/arrecife/commit/88c5af362ff2ee44e50e534259014d1a3f19c253))
17
+ * **components:** SidebarNav goes, and so does the recipe built on it ([27f6a3e](https://github.com/Proskynete/arrecife/commit/27f6a3e65c4f67dfd1531992b436965bc591bdf6))
18
+ * **components:** the footer stops taking loose links, and the columns are the answer ([5d4495e](https://github.com/Proskynete/arrecife/commit/5d4495eb8f9928dfea0605026b8e99a91922ee27))
19
+ * **primitives:** Table draws its own surface, and its scroll region is reachable ([ca07f2c](https://github.com/Proskynete/arrecife/commit/ca07f2c5a04f9db45819a9d3f0d69e32e3d112c6))
20
+ * **social:** the glyphs come out as portable data, and Website joins them ([bb62292](https://github.com/Proskynete/arrecife/commit/bb62292c16d4a9c4953b4a83deab61db0f07afcf))
21
+ * **tokens:** pulse-accent, the halo both sites already drew ([28d1c49](https://github.com/Proskynete/arrecife/commit/28d1c49fa9c7f48077391e2462ace69053c1377e))
22
+ * **tokens:** the caret goes, and the motion rule is one criterion again ([46f03ff](https://github.com/Proskynete/arrecife/commit/46f03ff26c5568b38a1a0633f2ed1f1ac34bcaa7))
23
+
24
+
25
+ ### 🐛 Correcciones
26
+
27
+ * **components:** NavItem asChild reaches its child ([f0556d5](https://github.com/Proskynete/arrecife/commit/f0556d5a7e36af188e7c7cac8df82258310c5205))
28
+ * **primitives:** ref is in the type of the four form controls ([f219d32](https://github.com/Proskynete/arrecife/commit/f219d3253fbddf35f004cb5f88015ad25cedde90))
29
+ * the llms generator stops interpreting $ in the inventory ([e0f5309](https://github.com/Proskynete/arrecife/commit/e0f5309b6620c9b8e6ed07d77bc08a5164b977fd))
30
+ * **tokens:** the doctor stops asking a React-less project for [@source](https://github.com/source) ([7bf6e8f](https://github.com/Proskynete/arrecife/commit/7bf6e8fe97e444ed60547265cceda1bd960511c4))
31
+
32
+
33
+ ### 📚 Documentación
34
+
35
+ * §§ 47 and 48, and the two removals in the migration guide ([424933e](https://github.com/Proskynete/arrecife/commit/424933e7a419398e755ccd49019f00e54bcc8abf))
36
+ * docs/ splits into architecture, decisions and runbooks ([db5a9b4](https://github.com/Proskynete/arrecife/commit/db5a9b4621100bbf1b77b83894f43caea99c56c1))
37
+ * **readme:** the 0.8.0 callout, and SidebarNav comes off the Phase 5 list ([a5af2f0](https://github.com/Proskynete/arrecife/commit/a5af2f0c2f838432d4a9c815d2127c35146c88bd))
38
+ * **readme:** the table's surface, the halo, and the caret that goes with it ([a30b8b6](https://github.com/Proskynete/arrecife/commit/a30b8b601b30fb133cd1876c1139f88142c83d8a))
39
+ * the reference documents for 0.8.0 ([e4521e2](https://github.com/Proskynete/arrecife/commit/e4521e2b1492132baa3849d8b73e36425ff40f4c))
40
+
41
+
42
+ ### 🚀 CI/CD
43
+
44
+ * the templates and the size labeler follow docs/ into its folders ([ea037a1](https://github.com/Proskynete/arrecife/commit/ea037a16517e379ec7bf6544caa96239fae2a491))
45
+
46
+ ## [0.7.0](https://github.com/Proskynete/arrecife/compare/v0.6.0...v0.7.0) (2026-09-04)
47
+
48
+
49
+ ### ⚠ BREAKING CHANGES
50
+
51
+ * **icons:** `Icon` no longer accepts `weight`. Pass `tone` instead — `action` is the old default, `current` is `fill` and `quiet` is `light`. The version it changes in has not been published, so nothing on npm carries the old shape.
52
+ * **components:** `Stat`'s `tone="alerta"` becomes `tone="alert"`. Nothing else changes — same colour, same rule, same default. Migration in docs/migration-0.7.md.
53
+
54
+ ### 🚀 Novedades
55
+
56
+ * **components:** EmptyState gains the shape that carries no face ([7292ec0](https://github.com/Proskynete/arrecife/commit/7292ec07cc3332442335325c49bbc0f3473267fc))
57
+ * **components:** Nav gains the one thing of the three it was missing ([1d2ec5b](https://github.com/Proskynete/arrecife/commit/1d2ec5b0a39d46bf17ee91f2a756c3fa7e8e1cd8))
58
+ * **components:** Stat gains delta, spark and the achievement tone ([0cf4348](https://github.com/Proskynete/arrecife/commit/0cf4348f4efd16c49ab54a5c205bb06c4f04ffd6))
59
+ * **components:** Stat's tone stops being called alerta ([43ffa43](https://github.com/Proskynete/arrecife/commit/43ffa431ac6f675ab27be2a8096e0acbad758711))
60
+ * **components:** the sidebar collapses to a rail, and takes who is signed in ([6fae58f](https://github.com/Proskynete/arrecife/commit/6fae58f3e6bc9cf975a78ca1aeab484421386f51))
61
+ * **components:** the sidebar gets blocks, and the icon replaces the prompt ([1d910a3](https://github.com/Proskynete/arrecife/commit/1d910a38d56000d955bc317fc4566fb7038864f0))
62
+ * **components:** the Stat card as it goes in the panel ([50e47f1](https://github.com/Proskynete/arrecife/commit/50e47f1b898afe207011d00f983af260e623a970))
63
+ * **icons:** the system adopts Phosphor, and it still ships no icons ([8a89c4e](https://github.com/Proskynete/arrecife/commit/8a89c4ef3e2292b8cb5d3c0a287fa59ceb635679))
64
+ * **icons:** the weight is an axis with three roles, and none of them is bold ([a8a9bed](https://github.com/Proskynete/arrecife/commit/a8a9bed9df6dea6c48b63dfd5e4af8f44368befa))
65
+ * npx arrecife, for the two failures that produce no error ([1b2160f](https://github.com/Proskynete/arrecife/commit/1b2160f378a53d433851b4c7995d164f3b93edd1))
66
+ * **social:** the nine icons get a subpath that crosses the RSC boundary ([bfa239f](https://github.com/Proskynete/arrecife/commit/bfa239f6c75c8cd99b7c9fa0bad155fdc68b7d98))
67
+ * **tokens:** a second bar height, for a shell that also has a sidebar ([2277afb](https://github.com/Proskynete/arrecife/commit/2277afb6bc2a2674d7722d69a989259634b74cbd))
68
+ * **tokens:** the sidebar's two widths, because a collapsible one needs both ([8b04bc0](https://github.com/Proskynete/arrecife/commit/8b04bc0dcc065d408b40d2076cb61d5bbe19dbc6))
69
+
70
+
71
+ ### 🐛 Correcciones
72
+
73
+ * **a11y:** the focus ring is one utility, at the offset the document gives ([66519b9](https://github.com/Proskynete/arrecife/commit/66519b9ab9834608aae7098b127d1789ad932579))
74
+ * **a11y:** the two accessible names the English sweep took with it ([dea2b8c](https://github.com/Proskynete/arrecife/commit/dea2b8cf17b5ee4a449d92c4805e89c528aa4021))
75
+ * **components:** the default copy goes back to Spanish ([1b0121e](https://github.com/Proskynete/arrecife/commit/1b0121e284b3a5fd244ba63704adf35de06937d5))
76
+ * **components:** the story taught a face map that `faceUsage` denies ([98c46ff](https://github.com/Proskynete/arrecife/commit/98c46ff337e90eb4acb6e90b38a00b54be4805e3))
77
+ * **components:** thirteen more strings the sweep corrupted, in the demo copy ([d6dd7fc](https://github.com/Proskynete/arrecife/commit/d6dd7fcc84a205dba562c8e1f1559328bac73e76))
78
+ * **tokens:** no light gradient ends on surfaceRaised, where accent is 4.21 ([802fdd0](https://github.com/Proskynete/arrecife/commit/802fdd09e6841ea29c384e5f8f99866c1b30bb81))
79
+
80
+
81
+ ### 🔧 Mantenimiento
82
+
83
+ * **components:** the AudioPlayer's internals finish the move to English ([db566d7](https://github.com/Proskynete/arrecife/commit/db566d7223c04db928e925ef0fb4edc462c240f8))
84
+
85
+
86
+ ### 📚 Documentación
87
+
88
+ * § 35 and § 36, the weight axis and the two actions nobody carried over ([dc72b1d](https://github.com/Proskynete/arrecife/commit/dc72b1d70fe5344b34473fb1a6ae8bb22dca29e6))
89
+ * § 37, the focus ring, and the one half of it that is an interpretation ([6084787](https://github.com/Proskynete/arrecife/commit/6084787068bbd5882574aae323d42581d7ccdbd7))
90
+ * § 9 is ratified and § 38 answers the number nobody looked up ([cd301fc](https://github.com/Proskynete/arrecife/commit/cd301fc8bcba06a4df3928de83fa8eee573cc261))
91
+ * an icon is not illustration, and the version that says so is 0.7.0 ([5d5a88c](https://github.com/Proskynete/arrecife/commit/5d5a88c162fe8ec747602cff7c09476650cab2b8))
92
+ * **components:** the delta says direction, and the document says so first ([ad39c6f](https://github.com/Proskynete/arrecife/commit/ad39c6f9776817f745cca82b1c93fab6287eec31))
93
+ * the ban on icon libraries is lifted, and the set was picked by measurement ([b313b64](https://github.com/Proskynete/arrecife/commit/b313b64b69e0db6a75001a888d95be7c71d5b211))
94
+ * the contrast rule for surfaceRaised covers gradients too ([e5e92d5](https://github.com/Proskynete/arrecife/commit/e5e92d5efc7a0610d06b09b6a757def4fb88dce8))
95
+ * the focus ring is a utility now, and AGENTS said to write it out by hand ([1943567](https://github.com/Proskynete/arrecife/commit/1943567070e30aed733b08c1ca66fd18287221ff))
96
+ * the neutral number stops being biolume, and where the tone went ([9f3fffb](https://github.com/Proskynete/arrecife/commit/9f3fffbd1f6e65f8797a140f6be71268a2f62d38))
97
+ * the rail gets in, and the entry says which premise expired ([86d7b04](https://github.com/Proskynete/arrecife/commit/86d7b044432120d2b29eb3295c1958075a6109c1))
98
+ * the second shape of the empty state, and where it bends the document ([612e7a8](https://github.com/Proskynete/arrecife/commit/612e7a8c78b49a03a6db8f787b90ae13bc60a6eb))
99
+ * the sidebar's blocks, and why the label is not a heading ([dc030f1](https://github.com/Proskynete/arrecife/commit/dc030f1ae707e4ee8596b4b6659716c3debd0fa0))
100
+ * the third tone, and the fourth breaking change ([abb6dfc](https://github.com/Proskynete/arrecife/commit/abb6dfce7cf93f2167dbff29c32c8d6b399c440c))
101
+ * the two forms of the social icons, and which one crosses the boundary ([9b90a82](https://github.com/Proskynete/arrecife/commit/9b90a829201de857462c80cf846dde46d628597b))
102
+ * the two silent failures get a command, and the README stops being the guard ([4b40bb6](https://github.com/Proskynete/arrecife/commit/4b40bb6359e79100544da3ba8cb0586dfc82bd03))
103
+ * the weight is three values, and the README and AGENTS said it was one ([05f3351](https://github.com/Proskynete/arrecife/commit/05f33513c4332ab4651b0a256ad8cbecfe810e34))
104
+ * this is two releases, not one — the guide splits back ([16fdf94](https://github.com/Proskynete/arrecife/commit/16fdf9494092f0b77f7397d891637d130407555b))
105
+ * two of the three things Nav was missing were already there ([f77b712](https://github.com/Proskynete/arrecife/commit/f77b7125d500122b26bf22f79f5983be483e3df4))
106
+
107
+
108
+ ### 🚀 CI/CD
109
+
110
+ * a check for the copy, because «nothing catches this» was the wrong answer ([74169c3](https://github.com/Proskynete/arrecife/commit/74169c3803f753a61800de130184ec0a2043d821))
111
+ * icons joins the scope list ([783acc4](https://github.com/Proskynete/arrecife/commit/783acc432cea7f947e3dbd8fc3590be9617c5102))
112
+ * social joins the scope list, by the rule that was already written ([d805a37](https://github.com/Proskynete/arrecife/commit/d805a37e0206ff3f6f71718d69d8db93e2e70f75))
113
+ * the actions decisions.md owes a canvas get printed, because § 22's was not ([c5dc751](https://github.com/Proskynete/arrecife/commit/c5dc751aa01be82c3429239afb6372fa8471acc6))
114
+
3
115
  ## [0.6.0](https://github.com/Proskynete/arrecife/compare/v0.5.1...v0.6.0) (2026-09-02)
4
116
 
5
117
 
package/README.md CHANGED
@@ -7,7 +7,7 @@ Published Storybook: [arrecife.eduardoalvarez.dev](https://arrecife.eduardoalvar
7
7
 
8
8
  ## The identity documents
9
9
 
10
- `docs/design-system.md` and `docs/brand-manual.md` are the extraction of the two
10
+ `docs/architecture/design-system.md` and `docs/architecture/brand-manual.md` are the extraction of the two
11
11
  Claude Design canvases, kept in the repo so they can be grepped and versioned.
12
12
  The canvas is still the source; this is the consultable copy.
13
13
 
@@ -15,7 +15,7 @@ They are here for a concrete reason: the highlighting palette lived hand-written
15
15
  in a project with a `#E05252` that this README has declared wrong for months, and
16
16
  nobody saw it because the document was not greppable from the code.
17
17
 
18
- `docs/decisions.md` is the other half: the points where the code and the document
18
+ `docs/decisions/` is the other half: the points where the code and the document
19
19
  do not say the same thing, each with its resolution and its reason.
20
20
 
21
21
  ## The two documents for agents
@@ -86,6 +86,77 @@ the sheet in `src/styles/` it goes up two levels and not one. The blog's E2E
86
86
  tests caught it, not the build, and until 0.3.0 this was only written in
87
87
  `llms.txt` — the file an agent reads and a person does not.
88
88
 
89
+ ### `npx arrecife` — the two things that fail without saying so
90
+
91
+ ```
92
+ npx arrecife
93
+ ```
94
+
95
+ It reads your stylesheets and checks the two failures that produce no error, both
96
+ of which cost real hours in the migration:
97
+
98
+ **The missing `@source`**, above. It also works out the path for you, counted
99
+ from the sheet and not from the project root, which is the part that gets written
100
+ wrong.
101
+
102
+ **A token of yours redefining one of ours.** A project coming from shadcn brings
103
+ `@theme inline { --color-accent: var(--accent); }`, and the two are not the same
104
+ colour: shadcn's `--accent` is the hover **surface**, `#17303E`, and this
105
+ library's is the brand turquoise, `#35D6C0`. The result was **88 classes inside
106
+ the library's own components** painting grey — 28 `text-accent`, 26 focus rings,
107
+ 15 `bg-accent`, 12 `border-accent`. Buttons, focus rings and badges came out the
108
+ colour of a surface and it looked as though the migration had done nothing. (The
109
+ twenty-six are one `focus-ring` utility now, which changes the count and not the
110
+ failure: it reads `var(--color-accent)` like everything else here.)
111
+
112
+ ```
113
+ arrecife · 2 thing(s) that fail without saying so:
114
+
115
+ src/styles/globals.css
116
+ imports @eduardoalvarez/arrecife/tokens/theme.css and has no @source.
117
+ Every class the components emit is being purged — silently. Add:
118
+
119
+ @source "../../node_modules/@eduardoalvarez/arrecife/dist";
120
+
121
+ src/styles/globals.css
122
+ redefines --color-accent, which @eduardoalvarez/arrecife owns.
123
+ yours: var(--accent) ← points at another property, so it wins silently
124
+ arrecife: #35D6C0
125
+ ```
126
+
127
+ Five names collide with shadcn's — `background`, `border`, `warm`, `warm-hover`
128
+ and `accent`. Four are harmless because both sides happen to agree on the value,
129
+ so the command reports the value on each side and only fails on the ones that
130
+ differ. A collision that agrees is worth knowing about and is not worth failing
131
+ over.
132
+
133
+ > **Coming from 0.7.0.** Four breaks, and three of them are things the library
134
+ > takes back OUT because nothing in the four projects drew them: the `caret`
135
+ > utility, the footer's row of loose text links, and `SidebarNav`.
136
+ >
137
+ > Only one fails at compile time — `Footer` no longer takes `children` — and that
138
+ > is the one to look at first. The other three fail silently or not at all: the
139
+ > `Table` wrapper you no longer need gives you two borders, and a hand-written
140
+ > `motion-safe:caret` just stops animating.
141
+ >
142
+ > Everything else is additive: `Footer variant="full"` with columns, three chart
143
+ > shapes, `./social/data` for a project that mounts no React, and a `Website`
144
+ > glyph.
145
+ >
146
+ > Run `npx arrecife` first, then read
147
+ > [`docs/runbooks/migration-0.8.md`](docs/runbooks/migration-0.8.md).
148
+
149
+ > **Coming from 0.6.0.** One break, and it is a find and replace the type checker
150
+ > points at: `Stat`'s `tone="alerta"` is `tone="alert"`.
151
+ >
152
+ > Everything else is additive, and most of it lets a project delete something it
153
+ > was maintaining by hand: `./social` and `./icons` are two new subpaths,
154
+ > `EmptyState` has a shape with no face, `Stat` covers the KPI cards, and
155
+ > `npx arrecife` catches two failures that produce no error at all.
156
+ >
157
+ > Run `npx arrecife` first, then read
158
+ > [`docs/runbooks/migration-0.7.md`](docs/runbooks/migration-0.7.md).
159
+
89
160
  > **Coming from 0.5.x.** Two unrelated things landed in 0.6.0, and they ship
90
161
  > together because in `0.x` a breaking change bumps the minor.
91
162
  >
@@ -98,19 +169,19 @@ tests caught it, not the build, and until 0.3.0 this was only written in
98
169
  > the type checker catches every one at the call site.
99
170
  >
100
171
  > Both halves, in order, with the full rename table and what each project can now
101
- > **delete**: [`docs/migration-0.6.md`](docs/migration-0.6.md).
172
+ > **delete**: [`docs/runbooks/migration-0.6.md`](docs/runbooks/migration-0.6.md).
102
173
 
103
174
  > **Coming from 0.4.0 or earlier.** `Toast`, `ToastProvider`, `ToastViewport`,
104
175
  > `ToastTitle` and `ToastDescription` stopped being public API in 0.5.0: you use
105
176
  > `Toaster` and `toast()`. `ToastAction` stays. The migration, with the reasoning
106
- > and the examples, is in [`docs/migration-0.5.md`](docs/migration-0.5.md).
177
+ > and the examples, is in [`docs/runbooks/migration-0.5.md`](docs/runbooks/migration-0.5.md).
107
178
 
108
179
  > **Coming from 0.2.0 or earlier.** The five spacing steps were renamed: `p-md`
109
180
  > is now `p-step-md`, `gap-sm` is `gap-step-sm`. It is a breaking change, and if
110
181
  > your project uses `max-w-sm`, `max-w-md` or `max-w-lg`, those were also worth
111
182
  > 12, 16 and 26px with nothing saying so. The reasoning, the migration pattern
112
183
  > and what to check afterwards are in
113
- > [`docs/migration-0.3.md`](docs/migration-0.3.md).
184
+ > [`docs/runbooks/migration-0.3.md`](docs/runbooks/migration-0.3.md).
114
185
 
115
186
  The font families are declared by name. Each project loads Bricolage Grotesque,
116
187
  Geist and JetBrains Mono however it prefers: the library does not dictate how.
@@ -275,32 +346,53 @@ Verified by packing the library with `pnpm pack` and installing it in a separate
275
346
  project: the types resolve from `dist/`, `./tokens` loads without dragging React
276
347
  in and `./tokens/theme.css` resolves by subpath.
277
348
 
278
- ### The social icons are namespaced
279
-
280
- It is the first thing anyone consuming the library trips over, because the
281
- natural form does not work:
349
+ ### The social icons come from `./social`
282
350
 
283
351
  ```tsx
284
- // ❌ does not exist
352
+ // ❌ does not exist: the root publishes them grouped, not loose
285
353
  import { GitHub, LinkedIn } from '@eduardoalvarez/arrecife';
286
354
 
287
- // ✅
288
- import { social } from '@eduardoalvarez/arrecife';
355
+ // ✅ the normal form
356
+ import { GitHub, LinkedIn } from '@eduardoalvarez/arrecife/social';
289
357
 
358
+ // ✅ for iterating the catalogue
359
+ import { social } from '@eduardoalvarez/arrecife';
290
360
  <social.GitHub />
291
- <social.LinkedIn />
292
361
  ```
293
362
 
294
- All nine are `GitHub`, `LinkedIn`, `X`, `Instagram`, `Discord`, `YouTube`, `Rss`,
295
- `Email` and `Newsletter`. They live under a namespace for a concrete reason:
296
- **one of them is called `X`**.
363
+ All ten are `GitHub`, `LinkedIn`, `X`, `Instagram`, `Discord`, `YouTube`, `Rss`,
364
+ `Email`, `Newsletter` and `Website`. The last one is «my other site» — the
365
+ personal domain in a footer full of networks — and it exists so that footer stops
366
+ borrowing a globe from an icon set, which brings its own stroke weight and its
367
+ own margins with it.
368
+
369
+ **And the shapes are published a second time, without React.** `./social/data`
370
+ imports nothing: it holds every glyph as structured shapes plus `socialSvg`,
371
+ which returns a complete `<svg>` as a string. It is for the consumer that mounts
372
+ no React and used to paste the `<path>` into its own template — `links` had four
373
+ of them and `cursos` had six. The React components above are drawn from that same
374
+ file, so a `d` that changes changes in both or in neither. See
375
+ `docs/decisions/0.8.md` § 42.
376
+
377
+ **The two forms are not taste, and in Next they are not interchangeable.** The
378
+ root carries `"use client"`, and what crosses into a Server Component is a client
379
+ reference **per export** — the properties of a plain object are not exports. So
380
+ from a Server Component `social.LinkedIn` is `undefined`, and `undefined` as an
381
+ element type kills the build at prerender. `./social` carries no directive: the
382
+ icon renders on the server, ships no client JS, and pulls 5.6 KB instead of the
383
+ root's 116 KB. Reach for the subpath by default; reach for `social` when you are
384
+ mapping a list of link names onto icons.
385
+
386
+ The namespace stays because **one of them is called `X`**. An `export const X` at
387
+ the root of a component library collides with anything — a generic's type
388
+ variable, an `import { X }` from somewhere else — and the failure shows up far
389
+ from here. In the subpath you asked for icons, so the collision is yours to
390
+ resolve and it takes one word: `import { X as XIcon }`.
297
391
 
298
392
  `Newsletter` is the bell, and it is named for what it means and not for what it
299
393
  draws — same as everything else in the system. It plays `Rss`'s role: a way to
300
394
  follow, not a social network. That is what keeps it inside this catalogue and
301
- keeps the catalogue from turning into an icon library. An `export const X` at the root of a component library collides
302
- with anything — a generic's type variable, an `import { X }` from somewhere else
303
- — and the failure shows up far from here.
395
+ keeps the catalogue from turning into an icon library.
304
396
 
305
397
  **The internal glyphs are NOT exported.** `Close`, `ChevronDown`, `Copy`, `Sun`
306
398
  and company are the minimum set the primitives need and they stay inside.
@@ -309,6 +401,77 @@ decided not to have, and from there it grows on its own. A project that needs an
309
401
  icon passes its own: `Stat` receives `icon`, `Footer` receives each social link's
310
402
  `icon`.
311
403
 
404
+ ### The icons are yours, the way they are drawn is not
405
+
406
+ That last sentence used to end there, and «its own what, drawn how» had no
407
+ answer. The admin panel imports 89 distinct icons in 229 places — 77 of them
408
+ domain icons for a course admin, which no design system was going to ship — and
409
+ drew them at `size-4` twenty-six times, plus `size-3.5`, `size-3`, `size-6` and
410
+ `size-7`, with no rule behind any of them.
411
+
412
+ ```tsx
413
+ import { GraduationCap } from '@phosphor-icons/react';
414
+ import { Icon } from '@eduardoalvarez/arrecife/icons';
415
+
416
+ <Icon as={GraduationCap} />
417
+ ```
418
+
419
+ 1em, so the icon takes the size of the text it sits in and nobody picks a number.
420
+ Weight `regular` by default, and **that is the whole reason the set is
421
+ Phosphor**: it bakes the weight into the path instead of exposing a
422
+ `strokeWidth`, and its regular lands on the one stroke the identity document
423
+ names. Measured on the `Minus` path itself, whose regular form is a bar of radius
424
+ 8 on a 256 grid:
425
+
426
+ | | Line | As a fraction of the rendered size |
427
+ | --- | --- | --- |
428
+ | phosphor `regular` | 16 on a 256 grid | **0.0625em** |
429
+ | the document | 1.6 on a 24 grid | **0.0667em** |
430
+ | `lib/glyphs.tsx` | 1.75 on a 16 grid | 0.109em |
431
+
432
+ Six per cent apart, which is no pixel on any screen. Nothing had to be derived and
433
+ no number had to be invented. The `Icons/Icon` → `regular IS the document's
434
+ stroke` story alternates the bars so the claim can be checked instead of believed
435
+ — and it also shows the third row, because **`glyphs.tsx` is the outlier**: at
436
+ 0.109em it is three quarters heavier than both, it was never argued anywhere, and
437
+ aligning it would restyle every primitive in the library. That is a separate
438
+ change and `docs/decisions/0.7.md` § 29 says so.
439
+
440
+ **The weight is an axis with three values, and `tone` is how you name them.**
441
+ `weight` is not a prop: Phosphor ships six and this system reads three, because
442
+ `thin`, `bold` and `duotone` have no role behind them here.
443
+
444
+ | `tone` | Weight | What it is |
445
+ | --- | --- | --- |
446
+ | `action` · the default | `regular` | An icon that is a control or names one |
447
+ | `current` | `fill` | The one of a set you are on — the item carrying `aria-current` |
448
+ | `quiet` | `light` | Furniture: a marker in a metadata row, not a control and not a state |
449
+
450
+ `current` is the value that earns the axis, and the argument is not taste.
451
+ An active sidebar item already says so in biolume, and **colour on its own is the
452
+ one channel WCAG 1.4.1 says may not carry meaning** — the fill is the second
453
+ channel, and it is the one that survives a forced-colours mode where the biolume
454
+ does not. `quiet` is the opposite problem: in a metadata row the icon is not the
455
+ point of the line, and at `regular` it draws as heavy as the date beside it.
456
+ `docs/decisions/0.7.md` § 35 has the rest.
457
+
458
+ `@phosphor-icons/react` is an **optional** peer dependency on its own subpath, by
459
+ the same rule as `./form` and `./chart`: two of the five projects use no icons and
460
+ install nothing.
461
+
462
+ **An icon is not illustration.** Tiburoncín — the faces, the poses, the fin — is
463
+ the mascot, it comes from `./brand`, and the manual doses it by surface: a face
464
+ only in an empty state, a confirmation, an error, course progress or a
465
+ celebration. An icon is functional vocabulary and goes wherever a control needs a
466
+ label it cannot spell. Adopting a set changed nothing about the first, and
467
+ neither stands in for the other in either direction.
468
+
469
+ **In Next, import from `@phosphor-icons/react/ssr` inside a Server Component.**
470
+ Phosphor's default build reads `IconContext` through `useContext`, and a hook in a
471
+ Server Component throws — and it ships no `"use client"` to stop you, so the
472
+ failure arrives at render. The `/ssr` entry is the same icons without the context
473
+ read, and `Icon` works with either.
474
+
312
475
  ### `"use client"` is in the published `dist`
313
476
 
314
477
  The root, `./brand`, `./form` and `./chart` carry the directive. They render
@@ -320,18 +483,24 @@ It blocked `cursos` for a whole version, and the workaround there was a
320
483
  that is a `<span>` with no interaction. It cost 272 KB of client chunk.
321
484
 
322
485
  The five portable subpaths do NOT carry it — `./tokens`, `./theme`,
323
- `./variants`, `./og` and `./shiki` — and that is the half that matters more.
486
+ `./variants`, `./social/data`, `./og` and `./shiki` — and that is the half that matters more.
324
487
  Marking them client would be a lie with a cost: a Server Component importing
325
488
  `buttonVariants`, a function that returns a string, would pull a client boundary
326
489
  in with it.
327
490
 
491
+ `./social` is the third case, and it is why the check stopped looking only at the
492
+ portable ones. It renders React — it is ten `<svg>` — so it can never be
493
+ portable, and it holds no state, so it must not be a client entry either. Listed
494
+ in neither set, nothing would have noticed it being marked client by mistake, and
495
+ that mistake undoes the only reason the subpath exists. See `docs/decisions/0.7.md` § 26.
496
+
328
497
  It is stamped by `scripts/add-use-client.mjs` after tsup, and not by tsup's
329
498
  `banner`. That was tried first: esbuild writes the directive and the bundling
330
499
  pass strips it back out with a `Module level directives cause errors when
331
500
  bundled` warning. The build stayed green and the published package was broken for
332
501
  Next — the worst way to fail, because the failure surfaces in somebody else's
333
502
  project. `check:exports` now verifies it in both directions: present on the four
334
- client entries, absent from the portable ones.
503
+ client entries, absent from every other subpath.
335
504
 
336
505
  It is inert outside Next. In Astro and in plain Vite it is a string literal at
337
506
  the top of a module; Rollup may warn and nothing else happens. One `dist` serves
@@ -354,9 +523,11 @@ five projects.
354
523
  In `cursos` it forced a `"use client"` on an adapter whose entire content was one
355
524
  call to CVA. In `links`, which depends on no React at all, it was not even an
356
525
  option: that project copied the class vocabulary by hand into `LinkRow.astro` and
357
- `Footer.astro`, and the copy had already drifted once — the hero gradient sat at
358
- `55%` and `#e9eeea` against the token's `60%` and `#EFE9DE`, and nothing compared
359
- them.
526
+ `Footer.astro`, and the copy had already drifted once: the hero gradient sat at
527
+ `55%` and `#e9eeea` against the token's `60%` and, at the time, `#EFE9DE`, and
528
+ nothing compared them. That light stop is `#FFFFFF` now — § 9 measured it — which
529
+ is the same lesson seen from the other end. A copied value goes stale the moment
530
+ the original moves, and only the original is ever right.
360
531
 
361
532
  The rule for what belongs in the subpath: if it returns classes, it goes there;
362
533
  if it returns markup, it stays in the component. `Button` renders a `<button>`,
@@ -382,10 +553,21 @@ import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage }
382
553
 
383
554
  import { ChartContainer, ChartTooltip, ChartTooltipContent, seriesColor }
384
555
  from '@eduardoalvarez/arrecife/chart';
556
+
557
+ // And the three chart types, which are what a project actually reaches for.
558
+ import { AreaChart, BarChart, LineChart } from '@eduardoalvarez/arrecife/chart';
385
559
  ```
386
560
 
387
- `check:exports` verifies that the five portable ones — `./tokens`, `./theme`,
388
- `./variants`, `./og` and `./shiki` — bring no React into the published `dist/`,
561
+ `AreaChart`, `BarChart` and `LineChart` take `data`, `series` and `xKey` and draw
562
+ the whole thing. **They are not Recharts' components of the same name**, and the
563
+ collision is deliberate: what they replace is not an import, it is sixty lines of
564
+ composition — a `linearGradient` with a hardcoded id, a `CartesianGrid
565
+ vertical={false}`, two axes with the line and the tick off, a `type="natural"`
566
+ and a `strokeWidth` — which `cursos` wrote four times, once per chart. None of
567
+ that is a decision the project made. See `docs/decisions/0.8.md` § 43.
568
+
569
+ `check:exports` verifies that the six portable ones — `./tokens`, `./theme`,
570
+ `./variants`, `./social/data`, `./og` and `./shiki` — bring no React into the published `dist/`,
389
571
  **by following the relative imports**. Without that the check was worthless: with `treeshake`
390
572
  on, each portable entry ends up as two lines re-exporting from a
391
573
  `chunk-XXXX.js`, and a grep over those two lines finds no React even when the
@@ -442,6 +624,20 @@ contrast ratio, which overshoots near white.
442
624
  `textMuted` never goes over `surfaceRaised`: in dark it gives 4.07. Menus use
443
625
  `textSecondary`, which gives 6.96.
444
626
 
627
+ **And no gradient ends there either**, which is the same rule applied to a
628
+ surface that moves. The light `hero` and `section` blocks used to sweep from the
629
+ page down onto `surfaceRaised`, where light `accent` reads **4.21** and `warm`
630
+ **4.19** — both under 4.5, and both of them fine at 4.55 and 4.53 on the page
631
+ they started from. That makes a token's contrast a function of **where in the
632
+ panel the text happens to sit**, which no token can guarantee and the suite
633
+ cannot see: axe does not evaluate text over a gradient, so both modes passed it.
634
+ `Hero` puts an `accent` eyebrow directly on that gradient.
635
+
636
+ The light blocks now sweep between `background` and `surface` and never touch
637
+ `surfaceRaised`, so the darkest point of either one is the page itself — a token
638
+ that passes on the page passes at every point of the sweep. `docs/decisions/0.6.md` § 9 has the measurements, including the two other things the first composition
639
+ got wrong.
640
+
445
641
  ### The third correction: a semantic color is not a text color over its own tint
446
642
 
447
643
  It came up while implementing the document's alert recipe — background at 8 % of
@@ -632,8 +828,13 @@ run summary.
632
828
  - **Phase 4** · `AudioPlayer`, migrated. Done.
633
829
  - **Phase 5** · done. `ArticleCard`, `AuthorCard`, `TalkCard`, `CourseCard`,
634
830
  `LinkRow`, `CodeBlock`, `Blockquote`, `PageHeader`, `EmptyState`, `Breadcrumb`,
635
- `Nav`, `SidebarNav`, `TableOfContents`, `Stat`, `Footer`, `Hero`,
636
- `NewsletterForm`, `og/` and `shiki/`.
831
+ `Nav`, `TableOfContents`, `Stat`, `Footer`, `Hero`, `NewsletterForm`, `og/` and
832
+ `shiki/`.
833
+
834
+ `SidebarNav` was on that list and came off it in 0.8.0. It met the rule below
835
+ on the identity half and never on the consumer half: the two admin projects it
836
+ was built for each wrote their own and never imported it. See
837
+ `docs/decisions/0.8.md` § 48.
637
838
 
638
839
  The criterion for deciding what gets in is still the same: **it encodes an
639
840
  identity rule, it has two or more consumers, and it drags in no project
@@ -668,9 +869,13 @@ about as library pieces. They get in anyway: the CLI aesthetic — the bar's
668
869
 
669
870
  ### Phase 3 decisions
670
871
 
671
- - **No `lucide-react`.** The eight glyphs the primitives need are inline in
672
- `src/lib/glyphs.tsx`, inherit `currentColor` and measure 1em. An icon library
673
- as a dependency is something each of the five projects pays for.
872
+ - **It ships no icon set**, and that has not changed. The eight glyphs the
873
+ primitives need are inline in `src/lib/glyphs.tsx`, inherit `currentColor` and
874
+ measure 1em, and they are not exported. What DID change is that
875
+ `@phosphor-icons/react` is now an optional peer on `./icons`, so the set a
876
+ project chooses is drawn at the system's weight — see «The icons are yours»
877
+ above. Optional and on a subpath is the point: the two projects that use no
878
+ icons install nothing.
674
879
  - **No entrance animations.** Modals, menus, tooltips and toasts appear where
675
880
  they will stay. The `Switch` knob changes position without sliding. The
676
881
  system's only transition is `transition-standard`, which can only animate color
@@ -694,6 +899,15 @@ about as library pieces. They get in anyway: the CLI aesthetic — the bar's
694
899
  And the **menu items** of `Select` and `DropdownMenu` stay on `cursor-default`:
695
900
  a native menu does not show the pointing hand, and the row highlight already
696
901
  says the row responds.
902
+ - **`Table`'s scroll region is a focus stop.** A region you can pan with a mouse
903
+ has to be reachable with a keyboard — WCAG 2.1.1 — and a table of text holds
904
+ nothing focusable to land on, so the columns past the right edge were simply
905
+ unreadable without one. It had been true since the container started scrolling
906
+ and no story was ever narrow enough to say so; the first one with more columns
907
+ than width failed axe on `scrollable-region-focusable` immediately. It is
908
+ unconditional, because whether a table overflows depends on the viewport and
909
+ the only alternative is a ResizeObserver on every table in the system. See
910
+ `docs/decisions/0.8.md` § 39.
697
911
 
698
912
  ### The syntax palette
699
913
 
@@ -839,22 +1053,47 @@ maintain for nothing.
839
1053
  `role="dialog"` on the content, and a dialog with no accessible name says nothing
840
1054
  to a screen reader: now it cannot be forgotten because it does not compile.
841
1055
 
842
- ### The fifth motion exception: the footer's caret
843
-
844
- The CLI signature ends in a block caret that blinks, behind `motion-safe`. It is
845
- the first exception that is not feedback about progress, so it needed a different
846
- argument.
847
-
848
- The signature is a **prompt** — that is why it is mono, why the `$` is in accent
849
- and why it sits in a footer instead of a `<p>` saying «© 2026». A prompt whose
850
- caret does not blink is a terminal that has hung, and a still block at the end of
851
- a line reads as a stray character.
852
-
853
- So the criterion splits in two. The first four exceptions are feedback about
854
- progress or spatial continuity; this one is legibility: it is not decoration, it
855
- is what makes the piece readable as what it is. `step-end` and not a fade,
856
- because a real caret is on or off and easing it turns a terminal into a pulsing
857
- dot. See `docs/decisions.md` § 23.
1056
+ ### The signature's mark: the halo, and the caret it removed
1057
+
1058
+ The CLI signature ends in a 2px bar that stays solid and **radiates** — a
1059
+ `box-shadow` ring grows out to 5px and fades, 1.5s `ease-in-out`, behind
1060
+ `motion-safe`. It is the fifth declared motion exception and it lands on the
1061
+ one criterion the other four share, next to the button spinner: a prompt that
1062
+ radiates says the terminal is live, which is what a still mark cannot say.
1063
+
1064
+ **It replaced a blink this library had invented.** The signature used to end in a
1065
+ half-em block blinking at `step-end`, argued from first principles: a prompt
1066
+ whose caret does not blink is a terminal that has hung. Every sentence of that
1067
+ argument is true, and it was answering a question the identity had already
1068
+ answered — `cursos` and `eduardoalvarez.dev` both shipped `@keyframes cursor-ping`,
1069
+ the same effect under the same name, written before this library had a `Footer`.
1070
+ The blog's copy is still in its `base.css` with nothing rendering it, because
1071
+ adopting the component replaced its mark. That is the drift this library exists
1072
+ to remove, arriving through the library.
1073
+
1074
+ **`caret` is gone, not merely unused.** It arrived in 0.6.0 and it is removed
1075
+ here. A published utility is normally not withdrawn the day its one consumer
1076
+ changes its mind, but it never had a consumer to change its mind: no project ever
1077
+ wrote the class, and the argument that justified it was reasoned rather than
1078
+ read. Leaving it published leaves the invention in the package under a label that
1079
+ makes it look like a feature.
1080
+
1081
+ **The halo is not that utility with a setting**, which is why it arrives under
1082
+ its own name. The caret's whole case rested on `step-end`: a real caret is on or
1083
+ off, and easing it turns a terminal into a pulsing dot. Folding a halo into that
1084
+ name would make the argument contradict itself.
1085
+
1086
+ **And the rule gets its shape back.** The caret was the only member of the
1087
+ «legibility» criterion that was invented to admit it, so the five exceptions —
1088
+ the button spinner, the `Sheet` panel, the `Skeleton` shimmer, the `Accordion`
1089
+ height and this halo — are again all one thing: feedback about progress or about
1090
+ spatial continuity. A sixth lands on that or it does not exist.
1091
+
1092
+ The bar is 2px and not a block because a halo needs something thin to radiate
1093
+ from, the colour is `var(--color-accent)` so it follows the mode, and
1094
+ `motion-safe` is the one thing not copied from `cursos` — whose span animates
1095
+ regardless of the setting. See `docs/decisions/0.8.md` § 45, and § 23 for the entry
1096
+ it reverses.
858
1097
 
859
1098
  ### The second motion exception
860
1099
 
@@ -900,7 +1139,7 @@ text, and `surfaceRaised` is where a toolbar lives.
900
1139
  `destructiveOutline` fills on hover, and that is a declared exception to
901
1140
  «secondary is never filled» — a destructive that looks identical to a secondary
902
1141
  until you read it is the problem the variant exists to fix. See
903
- `docs/decisions.md` § 21.
1142
+ `docs/decisions/0.6.md` § 21.
904
1143
 
905
1144
  ### `icon-sm`, for the one admin app
906
1145
 
@@ -911,7 +1150,7 @@ three actions per table row, and at 42 the row grows with them.
911
1150
  `size="icon-sm"` is 32×32, and it is 32 and not the 28 that project actually had:
912
1151
  32 is `sm`'s height, so a dense icon button lines up with a small text button and
913
1152
  a toolbar mixing the two stays on one baseline. It does not replace `icon` — a
914
- page's primary action stays at 42. See `docs/decisions.md` § 22.
1153
+ page's primary action stays at 42. See `docs/decisions/0.6.md` § 22.
915
1154
 
916
1155
  ### The theme script, and the mode a site already decided
917
1156
 
@@ -75,7 +75,7 @@ var typeScale = {
75
75
  * (plankton 5.57:1 over abyss), which is the part that is not negotiable.
76
76
  *
77
77
  * At 13 the three badge families grew past the size of a small button and
78
- * outweighed the title they accompany. See `docs/decisions.md`.
78
+ * outweighed the title they accompany. See `docs/decisions/`.
79
79
  */
80
80
  chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
81
81
  /**
@@ -121,7 +121,7 @@ var control = {
121
121
  * baseline; 28 would have been a fifth height that matches nothing.
122
122
  *
123
123
  * It does not replace `icon`. A page's primary action stays at 42; this is for
124
- * a row of a table. See `docs/decisions.md` § 22.
124
+ * a row of a table. See `docs/decisions/0.6.md` § 22.
125
125
  */
126
126
  iconSm: 32
127
127
  };
@@ -1,6 +1,6 @@
1
1
  'use client';
2
- export { Isotype, Logo, Mascot, MascotFace } from '../chunk-6O3KWB6P.js';
2
+ export { Isotype, Logo, Mascot, MascotFace } from '../chunk-MPZBF2TZ.js';
3
3
  export { ASSETS_PATH, faceList, faceUsage, faces, fins, poseList, poses } from '../chunk-CKRSQPTX.js';
4
- import '../chunk-XKYHTOUJ.js';
5
- import '../chunk-O4TAH7YJ.js';
4
+ import '../chunk-FAAGZG7A.js';
5
+ import '../chunk-FGFNK72B.js';
6
6
  import '../chunk-MLKGABMK.js';