@eduardoalvarez/arrecife 0.7.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 +43 -0
  2. package/README.md +115 -41
  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-TA7TLWW4.js → chunk-5A5GH2PF.js} +1 -1
  10. package/dist/{chunk-727HCBD4.js → chunk-6IGD5REB.js} +1 -1
  11. package/dist/{chunk-2WPWEIMD.js → chunk-FAAGZG7A.js} +1 -1
  12. package/dist/{chunk-OMKSESQB.js → chunk-FGFNK72B.js} +3 -3
  13. package/dist/chunk-HOADZ6GS.js +72 -0
  14. package/dist/chunk-LXRGQKMG.js +145 -0
  15. package/dist/{chunk-JN3IS5OS.js → chunk-MPZBF2TZ.js} +2 -2
  16. package/dist/{chunk-WGNIRIN7.js → chunk-TRPBID2W.js} +1 -1
  17. package/dist/{chunk-E6KFUSKB.js → chunk-XXDATT3A.js} +1 -1
  18. package/dist/doctor.mjs +95 -13
  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 +2 -2
  24. package/dist/icons/index.d.cts +2 -2
  25. package/dist/icons/index.d.ts +2 -2
  26. package/dist/icons/index.js +2 -2
  27. package/dist/{index-DlAO2JZs.d.cts → index-BbRplw_B.d.cts} +15 -4
  28. package/dist/{index-DlAO2JZs.d.ts → index-BbRplw_B.d.ts} +15 -4
  29. package/dist/index.cjs +262 -264
  30. package/dist/index.d.cts +194 -124
  31. package/dist/index.d.ts +194 -124
  32. package/dist/index.js +151 -236
  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.js +1 -1
  36. package/dist/shiki/index.js +1 -1
  37. package/dist/social/data.cjs +161 -0
  38. package/dist/social/data.d.cts +161 -0
  39. package/dist/social/data.d.ts +161 -0
  40. package/dist/social/data.js +2 -0
  41. package/dist/social/index.cjs +124 -38
  42. package/dist/social/index.d.cts +1 -1
  43. package/dist/social/index.d.ts +1 -1
  44. package/dist/social/index.js +2 -1
  45. package/dist/tokens/index.cjs +3 -3
  46. package/dist/tokens/index.d.cts +5 -5
  47. package/dist/tokens/index.d.ts +5 -5
  48. package/dist/tokens/index.js +2 -2
  49. package/dist/tokens/theme.css +46 -19
  50. package/dist/variants/index.cjs +1 -1
  51. package/dist/variants/index.d.cts +2 -2
  52. package/dist/variants/index.d.ts +2 -2
  53. package/dist/variants/index.js +1 -1
  54. package/llms.txt +257 -140
  55. package/package.json +6 -1
  56. package/dist/chunk-45HVCTB7.js +0 -70
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
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
+
3
46
  ## [0.7.0](https://github.com/Proskynete/arrecife/compare/v0.6.0...v0.7.0) (2026-09-04)
4
47
 
5
48
 
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
@@ -130,17 +130,32 @@ so the command reports the value on each side and only fails on the ones that
130
130
  differ. A collision that agrees is worth knowing about and is not worth failing
131
131
  over.
132
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
+
133
149
  > **Coming from 0.6.0.** One break, and it is a find and replace the type checker
134
150
  > points at: `Stat`'s `tone="alerta"` is `tone="alert"`.
135
151
  >
136
152
  > Everything else is additive, and most of it lets a project delete something it
137
153
  > was maintaining by hand: `./social` and `./icons` are two new subpaths,
138
- > `EmptyState` has a shape with no face, `Stat` covers the KPI cards, `SidebarNav`
139
- > groups and collapses, and `npx arrecife` catches two failures that produce no
140
- > error at all.
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.
141
156
  >
142
157
  > Run `npx arrecife` first, then read
143
- > [`docs/migration-0.7.md`](docs/migration-0.7.md).
158
+ > [`docs/runbooks/migration-0.7.md`](docs/runbooks/migration-0.7.md).
144
159
 
145
160
  > **Coming from 0.5.x.** Two unrelated things landed in 0.6.0, and they ship
146
161
  > together because in `0.x` a breaking change bumps the minor.
@@ -154,19 +169,19 @@ over.
154
169
  > the type checker catches every one at the call site.
155
170
  >
156
171
  > Both halves, in order, with the full rename table and what each project can now
157
- > **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).
158
173
 
159
174
  > **Coming from 0.4.0 or earlier.** `Toast`, `ToastProvider`, `ToastViewport`,
160
175
  > `ToastTitle` and `ToastDescription` stopped being public API in 0.5.0: you use
161
176
  > `Toaster` and `toast()`. `ToastAction` stays. The migration, with the reasoning
162
- > 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).
163
178
 
164
179
  > **Coming from 0.2.0 or earlier.** The five spacing steps were renamed: `p-md`
165
180
  > is now `p-step-md`, `gap-sm` is `gap-step-sm`. It is a breaking change, and if
166
181
  > your project uses `max-w-sm`, `max-w-md` or `max-w-lg`, those were also worth
167
182
  > 12, 16 and 26px with nothing saying so. The reasoning, the migration pattern
168
183
  > and what to check afterwards are in
169
- > [`docs/migration-0.3.md`](docs/migration-0.3.md).
184
+ > [`docs/runbooks/migration-0.3.md`](docs/runbooks/migration-0.3.md).
170
185
 
171
186
  The font families are declared by name. Each project loads Bricolage Grotesque,
172
187
  Geist and JetBrains Mono however it prefers: the library does not dictate how.
@@ -345,8 +360,19 @@ import { social } from '@eduardoalvarez/arrecife';
345
360
  <social.GitHub />
346
361
  ```
347
362
 
348
- All nine are `GitHub`, `LinkedIn`, `X`, `Instagram`, `Discord`, `YouTube`, `Rss`,
349
- `Email` and `Newsletter`.
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.
350
376
 
351
377
  **The two forms are not taste, and in Next they are not interchangeable.** The
352
378
  root carries `"use client"`, and what crosses into a Server Component is a client
@@ -409,7 +435,7 @@ stroke` story alternates the bars so the claim can be checked instead of believe
409
435
  — and it also shows the third row, because **`glyphs.tsx` is the outlier**: at
410
436
  0.109em it is three quarters heavier than both, it was never argued anywhere, and
411
437
  aligning it would restyle every primitive in the library. That is a separate
412
- change and `docs/decisions.md` § 29 says so.
438
+ change and `docs/decisions/0.7.md` § 29 says so.
413
439
 
414
440
  **The weight is an axis with three values, and `tone` is how you name them.**
415
441
  `weight` is not a prop: Phosphor ships six and this system reads three, because
@@ -427,7 +453,7 @@ one channel WCAG 1.4.1 says may not carry meaning** — the fill is the second
427
453
  channel, and it is the one that survives a forced-colours mode where the biolume
428
454
  does not. `quiet` is the opposite problem: in a metadata row the icon is not the
429
455
  point of the line, and at `regular` it draws as heavy as the date beside it.
430
- `docs/decisions.md` § 35 has the rest.
456
+ `docs/decisions/0.7.md` § 35 has the rest.
431
457
 
432
458
  `@phosphor-icons/react` is an **optional** peer dependency on its own subpath, by
433
459
  the same rule as `./form` and `./chart`: two of the five projects use no icons and
@@ -457,17 +483,16 @@ It blocked `cursos` for a whole version, and the workaround there was a
457
483
  that is a `<span>` with no interaction. It cost 272 KB of client chunk.
458
484
 
459
485
  The five portable subpaths do NOT carry it — `./tokens`, `./theme`,
460
- `./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.
461
487
  Marking them client would be a lie with a cost: a Server Component importing
462
488
  `buttonVariants`, a function that returns a string, would pull a client boundary
463
489
  in with it.
464
490
 
465
491
  `./social` is the third case, and it is why the check stopped looking only at the
466
- portable ones. It renders React — it is nine `<svg>` — so it can never be
492
+ portable ones. It renders React — it is ten `<svg>` — so it can never be
467
493
  portable, and it holds no state, so it must not be a client entry either. Listed
468
494
  in neither set, nothing would have noticed it being marked client by mistake, and
469
- that mistake undoes the only reason the subpath exists. See `docs/decisions.md`
470
- § 26.
495
+ that mistake undoes the only reason the subpath exists. See `docs/decisions/0.7.md` § 26.
471
496
 
472
497
  It is stamped by `scripts/add-use-client.mjs` after tsup, and not by tsup's
473
498
  `banner`. That was tried first: esbuild writes the directive and the bundling
@@ -528,10 +553,21 @@ import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage }
528
553
 
529
554
  import { ChartContainer, ChartTooltip, ChartTooltipContent, seriesColor }
530
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';
531
559
  ```
532
560
 
533
- `check:exports` verifies that the five portable ones — `./tokens`, `./theme`,
534
- `./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/`,
535
571
  **by following the relative imports**. Without that the check was worthless: with `treeshake`
536
572
  on, each portable entry ends up as two lines re-exporting from a
537
573
  `chunk-XXXX.js`, and a grep over those two lines finds no React even when the
@@ -599,8 +635,7 @@ cannot see: axe does not evaluate text over a gradient, so both modes passed it.
599
635
 
600
636
  The light blocks now sweep between `background` and `surface` and never touch
601
637
  `surfaceRaised`, so the darkest point of either one is the page itself — a token
602
- that passes on the page passes at every point of the sweep. `docs/decisions.md`
603
- § 9 has the measurements, including the two other things the first composition
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
604
639
  got wrong.
605
640
 
606
641
  ### The third correction: a semantic color is not a text color over its own tint
@@ -793,8 +828,13 @@ run summary.
793
828
  - **Phase 4** · `AudioPlayer`, migrated. Done.
794
829
  - **Phase 5** · done. `ArticleCard`, `AuthorCard`, `TalkCard`, `CourseCard`,
795
830
  `LinkRow`, `CodeBlock`, `Blockquote`, `PageHeader`, `EmptyState`, `Breadcrumb`,
796
- `Nav`, `SidebarNav`, `TableOfContents`, `Stat`, `Footer`, `Hero`,
797
- `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.
798
838
 
799
839
  The criterion for deciding what gets in is still the same: **it encodes an
800
840
  identity rule, it has two or more consumers, and it drags in no project
@@ -859,6 +899,15 @@ about as library pieces. They get in anyway: the CLI aesthetic — the bar's
859
899
  And the **menu items** of `Select` and `DropdownMenu` stay on `cursor-default`:
860
900
  a native menu does not show the pointing hand, and the row highlight already
861
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.
862
911
 
863
912
  ### The syntax palette
864
913
 
@@ -1004,22 +1053,47 @@ maintain for nothing.
1004
1053
  `role="dialog"` on the content, and a dialog with no accessible name says nothing
1005
1054
  to a screen reader: now it cannot be forgotten because it does not compile.
1006
1055
 
1007
- ### The fifth motion exception: the footer's caret
1008
-
1009
- The CLI signature ends in a block caret that blinks, behind `motion-safe`. It is
1010
- the first exception that is not feedback about progress, so it needed a different
1011
- argument.
1012
-
1013
- The signature is a **prompt** — that is why it is mono, why the `$` is in accent
1014
- and why it sits in a footer instead of a `<p>` saying «© 2026». A prompt whose
1015
- caret does not blink is a terminal that has hung, and a still block at the end of
1016
- a line reads as a stray character.
1017
-
1018
- So the criterion splits in two. The first four exceptions are feedback about
1019
- progress or spatial continuity; this one is legibility: it is not decoration, it
1020
- is what makes the piece readable as what it is. `step-end` and not a fade,
1021
- because a real caret is on or off and easing it turns a terminal into a pulsing
1022
- 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.
1023
1097
 
1024
1098
  ### The second motion exception
1025
1099
 
@@ -1065,7 +1139,7 @@ text, and `surfaceRaised` is where a toolbar lives.
1065
1139
  `destructiveOutline` fills on hover, and that is a declared exception to
1066
1140
  «secondary is never filled» — a destructive that looks identical to a secondary
1067
1141
  until you read it is the problem the variant exists to fix. See
1068
- `docs/decisions.md` § 21.
1142
+ `docs/decisions/0.6.md` § 21.
1069
1143
 
1070
1144
  ### `icon-sm`, for the one admin app
1071
1145
 
@@ -1076,7 +1150,7 @@ three actions per table row, and at 42 the row grows with them.
1076
1150
  `size="icon-sm"` is 32×32, and it is 32 and not the 28 that project actually had:
1077
1151
  32 is `sm`'s height, so a dense icon button lines up with a small text button and
1078
1152
  a toolbar mixing the two stays on one baseline. It does not replace `icon` — a
1079
- 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.
1080
1154
 
1081
1155
  ### The theme script, and the mode a site already decided
1082
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-JN3IS5OS.js';
3
- import '../chunk-2WPWEIMD.js';
2
+ export { Isotype, Logo, Mascot, MascotFace } from '../chunk-MPZBF2TZ.js';
4
3
  export { ASSETS_PATH, faceList, faceUsage, faces, fins, poseList, poses } from '../chunk-CKRSQPTX.js';
5
- import '../chunk-OMKSESQB.js';
4
+ import '../chunk-FAAGZG7A.js';
5
+ import '../chunk-FGFNK72B.js';
6
6
  import '../chunk-MLKGABMK.js';
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
+ var react = require('react');
4
5
  var recharts = require('recharts');
5
6
  var clsx = require('clsx');
6
7
  var tailwindMerge = require('tailwind-merge');
@@ -45,7 +46,7 @@ var typeScale = {
45
46
  * (plankton 5.57:1 over abyss), which is the part that is not negotiable.
46
47
  *
47
48
  * At 13 the three badge families grew past the size of a small button and
48
- * outweighed the title they accompany. See `docs/decisions.md`.
49
+ * outweighed the title they accompany. See `docs/decisions/`.
49
50
  */
50
51
  chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
51
52
  /**
@@ -91,7 +92,7 @@ var control = {
91
92
  * baseline; 28 would have been a fifth height that matches nothing.
92
93
  *
93
94
  * It does not replace `icon`. A page's primary action stays at 42; this is for
94
- * a row of a table. See `docs/decisions.md` § 22.
95
+ * a row of a table. See `docs/decisions/0.6.md` § 22.
95
96
  */
96
97
  iconSm: 32
97
98
  };
@@ -294,11 +295,137 @@ function ChartLegendContent({ payload, className }) {
294
295
  /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "span", variant: "label", tone: "secondary", className: "font-normal", children: item.value })
295
296
  ] }, `${String(item.dataKey ?? item.value ?? i)}`)) });
296
297
  }
298
+ var rows = (data) => data;
299
+ var color = (series2, index) => series2.color ?? seriesColor(index);
300
+ var MARGIN = { left: 0, right: 12, top: 8, bottom: 0 };
301
+ var AXIS = { tickLine: false, axisLine: false };
302
+ var legendOf = (series2, legend) => legend ?? series2.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(ChartLegend, { content: /* @__PURE__ */ jsxRuntime.jsx(ChartLegendContent, {}) }, "legend") : null;
303
+ var tooltipOf = (formatter) => /* @__PURE__ */ jsxRuntime.jsx(
304
+ ChartTooltip,
305
+ {
306
+ cursor: false,
307
+ content: /* @__PURE__ */ jsxRuntime.jsx(ChartTooltipContent, { formatter: formatter ? (value) => formatter(value) : void 0 })
308
+ },
309
+ "tooltip"
310
+ );
311
+ function AreaChart({
312
+ data,
313
+ series: series2,
314
+ xKey,
315
+ formatter,
316
+ legend,
317
+ stacked = false,
318
+ ...container
319
+ }) {
320
+ const id = react.useId();
321
+ return /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { ...container, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.AreaChart, { data: rows(data), margin: MARGIN, children: [
322
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: series2.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: `${id}-${s.key}`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
323
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "5%", stopColor: color(s, i), stopOpacity: 0.5 }),
324
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "95%", stopColor: color(s, i), stopOpacity: 0.05 })
325
+ ] }, s.key)) }),
326
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { vertical: false }),
327
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8 }),
328
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40 }),
329
+ tooltipOf(formatter),
330
+ legendOf(series2, legend),
331
+ series2.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
332
+ recharts.Area,
333
+ {
334
+ dataKey: s.key,
335
+ name: s.label,
336
+ type: "natural",
337
+ stroke: color(s, i),
338
+ strokeWidth: 2,
339
+ fill: `url(#${id}-${s.key})`,
340
+ isAnimationActive: false,
341
+ ...stacked ? { stackId: "stack" } : {}
342
+ },
343
+ s.key
344
+ ))
345
+ ] }) });
346
+ }
347
+ function BarChart({
348
+ data,
349
+ series: series2,
350
+ xKey,
351
+ formatter,
352
+ legend,
353
+ orientation = "vertical",
354
+ stacked = false,
355
+ ...container
356
+ }) {
357
+ const lying = orientation === "horizontal";
358
+ return /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { ...container, children: /* @__PURE__ */ jsxRuntime.jsxs(
359
+ recharts.BarChart,
360
+ {
361
+ data: rows(data),
362
+ margin: MARGIN,
363
+ ...lying ? { layout: "vertical" } : {},
364
+ children: [
365
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { vertical: lying, horizontal: !lying }),
366
+ lying ? [
367
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { dataKey: xKey, type: "category", ...AXIS, width: 130 }, "category"),
368
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { type: "number", hide: true }, "value")
369
+ ] : [
370
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8 }, "category"),
371
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40 }, "value")
372
+ ],
373
+ tooltipOf(formatter),
374
+ legendOf(series2, legend),
375
+ series2.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
376
+ recharts.Bar,
377
+ {
378
+ dataKey: s.key,
379
+ name: s.label,
380
+ fill: color(s, i),
381
+ radius: lying ? [0, radius.chip, radius.chip, 0] : [radius.chip, radius.chip, 0, 0],
382
+ isAnimationActive: false,
383
+ ...stacked ? { stackId: "stack" } : {}
384
+ },
385
+ s.key
386
+ ))
387
+ ]
388
+ }
389
+ ) });
390
+ }
391
+ function LineChart({
392
+ data,
393
+ series: series2,
394
+ xKey,
395
+ formatter,
396
+ legend,
397
+ ...container
398
+ }) {
399
+ return /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { ...container, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data: rows(data), margin: MARGIN, children: [
400
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { vertical: false }),
401
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8 }),
402
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40 }),
403
+ tooltipOf(formatter),
404
+ legendOf(series2, legend),
405
+ series2.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
406
+ recharts.Line,
407
+ {
408
+ dataKey: s.key,
409
+ name: s.label,
410
+ type: "natural",
411
+ stroke: color(s, i),
412
+ strokeWidth: 2,
413
+ dot: false,
414
+ activeDot: { r: 4 },
415
+ isAnimationActive: false
416
+ },
417
+ s.key
418
+ ))
419
+ ] }) });
420
+ }
297
421
 
422
+ exports.AreaChart = AreaChart;
423
+ exports.BarChart = BarChart;
298
424
  exports.ChartContainer = ChartContainer;
299
425
  exports.ChartLegend = ChartLegend;
300
426
  exports.ChartLegendContent = ChartLegendContent;
301
427
  exports.ChartTooltip = ChartTooltip;
302
428
  exports.ChartTooltipContent = ChartTooltipContent;
429
+ exports.LineChart = LineChart;
303
430
  exports.SERIES_COLORS = SERIES_COLORS;
304
431
  exports.seriesColor = seriesColor;
@@ -22,9 +22,15 @@ import { Legend, Tooltip } from 'recharts';
22
22
  * 3. The tooltip appears where it will stay: `isAnimationActive` is off
23
23
  * everywhere, because the system does not animate position.
24
24
  *
25
- * The data pieces — `BarChart`, `Line`, `XAxis` — are NOT re-exported. They are
26
- * Recharts' API, the project already knows it, and wrapping them would be a
27
- * layer of names to keep in sync with the library's version.
25
+ * Recharts' data pieces — `Area`, `Line`, `XAxis` — are still NOT re-exported.
26
+ * They are Recharts' API, the project already knows it, and wrapping them would
27
+ * be a layer of names to keep in sync with the library's version.
28
+ *
29
+ * What IS published, since this version, are three CHART TYPES on top of that
30
+ * chassis: `AreaChart`, `BarChart` and `LineChart`. They are not wrappers over
31
+ * Recharts' components of the same name — they take `data`, `series` and `xKey`
32
+ * and draw the whole thing. See `docs/decisions/0.8.md` § 43 for why the names
33
+ * collide on purpose.
28
34
  */
29
35
  /**
30
36
  * The color of series `index`, as a custom property.
@@ -123,5 +129,109 @@ type ChartLegendContentProps = {
123
129
  };
124
130
  /** The legend, with the tooltip's same square swatch and the `label` scale. */
125
131
  declare function ChartLegendContent({ payload, className }: ChartLegendContentProps): react.JSX.Element | null;
132
+ /**
133
+ * One series: which key of the datum it reads, what it is called, and — only if
134
+ * you insist — what color it is.
135
+ *
136
+ * `color` is there for the case the palette cannot serve: a status series where
137
+ * «pagado» has to be `success` and not «the first of the four». Leaving it out
138
+ * is the normal case and the right one — `seriesColor(i)` follows the mode, and
139
+ * a hard-coded hex does not. See the note on `seriesColor` above.
140
+ */
141
+ type ChartSeries = {
142
+ /** The key this series reads in each datum. */
143
+ key: string;
144
+ /** What the legend and the tooltip call it. User-facing, so Spanish. */
145
+ label: string;
146
+ /** Overrides the palette. Defaults to `seriesColor(index)`. */
147
+ color?: string | undefined;
148
+ };
149
+ /** A row of the chart: the category key plus one value per series. */
150
+ type ChartDatum = Record<string, unknown>;
151
+ /**
152
+ * What all three types take, and it is deliberately short: `data`, `series` and
153
+ * `xKey`, plus the container's own `label`.
154
+ *
155
+ * Everything that is NOT here is the part that was being copied — the gradient,
156
+ * the grid with no vertical lines, the axes with no line and no tick, the
157
+ * tooltip with no cursor. `cursos` wrote all of it four times, once per chart,
158
+ * and the fourth one would have had a different `strokeWidth` from the first.
159
+ * The drawing is the system's; the data is the project's.
160
+ */
161
+ type SeriesChartProps = Omit<ChartContainerProps, 'children'> & {
162
+ data: readonly ChartDatum[];
163
+ series: readonly ChartSeries[];
164
+ /** The key on the category axis: the day, the month, the course. */
165
+ xKey: string;
166
+ /** Formats the value in the tooltip. The library imposes no locale. */
167
+ formatter?: ((value: unknown) => ReactNode) | undefined;
168
+ /**
169
+ * Shows the legend. It defaults to «only when there is more than one series»:
170
+ * a legend naming the one line already named by the chart's own heading is a
171
+ * row of pixels that says nothing.
172
+ */
173
+ legend?: boolean | undefined;
174
+ };
175
+ /**
176
+ * A series over time, with the fill fading out underneath it.
177
+ *
178
+ * The gradient is the whole reason this is a component and not three lines at
179
+ * the call site: it needs a `<linearGradient>` with an id, the id has to be
180
+ * unique on the page, and the two projects that wrote it by hand both hardcoded
181
+ * one — a second chart on the same screen would have taken the first one's fill.
182
+ * `useId` settles it.
183
+ *
184
+ * 0.5 down to 0.05 is the ramp `cursos` arrived at, and `type="natural"` is its
185
+ * curve: a monotone spline reads as a measurement and a straight segment reads
186
+ * as a schedule. The stroke is 2, which is the one weight in the system that is
187
+ * neither a hairline nor a border.
188
+ *
189
+ * With more than one series the areas OVERLAP rather than stack, because a
190
+ * stacked area answers «how much in total» and this library has no way to know
191
+ * that is the question. When it is, pass `stacked`. When you want to compare
192
+ * rather than add up, the answer is `LineChart`.
193
+ */
194
+ type AreaChartProps = SeriesChartProps & {
195
+ /** Adds the series up instead of overlaying them. */
196
+ stacked?: boolean | undefined;
197
+ };
198
+ declare function AreaChart({ data, series, xKey, formatter, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
199
+ /**
200
+ * Bars, upright or lying down.
201
+ *
202
+ * `orientation` is named for what you SEE and not for what Recharts calls it,
203
+ * and the two are opposites: Recharts' `layout="vertical"` draws horizontal
204
+ * bars. That inversion is a coin flip every time somebody writes it, so it is
205
+ * settled here once — `vertical` means the bars grow upwards.
206
+ *
207
+ * `horizontal` is the shape for a ranking, which is what `cursos` uses it for:
208
+ * course names on the left, one bar each. It reserves 130px for the category
209
+ * axis and hides the value axis, because a ranking is read by comparing lengths
210
+ * and not by reading a number off a scale — the number is in the tooltip.
211
+ *
212
+ * The corner radius is `radius.chip`, the system's smallest. A bar is a chip
213
+ * lying down, and picking a number that is not a token is how a fifth radius
214
+ * gets into the system without anybody deciding it should.
215
+ */
216
+ type BarChartProps = SeriesChartProps & {
217
+ /** `vertical` grows the bars upwards, which is the default. `horizontal` is a ranking. */
218
+ orientation?: 'vertical' | 'horizontal' | undefined;
219
+ /** Stacks the series instead of putting them side by side. */
220
+ stacked?: boolean | undefined;
221
+ };
222
+ declare function BarChart({ data, series, xKey, formatter, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
223
+ /**
224
+ * Lines, for comparing series against each other.
225
+ *
226
+ * It is the type that does NOT add anything up, which is the whole reason it
227
+ * exists next to `AreaChart`: four filled areas on top of each other are a
228
+ * total, and four lines are four things you can tell apart.
229
+ *
230
+ * No dots at rest. A point per datum on a 90-day series is a dotted line, and
231
+ * the value is in the tooltip; the dot comes back on hover because that is
232
+ * where it means «this one».
233
+ */
234
+ type LineChartProps = SeriesChartProps;
235
+ declare function LineChart({ data, series, xKey, formatter, legend, ...container }: LineChartProps): react.JSX.Element;
126
236
 
127
- export { ChartContainer, type ChartContainerProps, ChartLegend, ChartLegendContent, type ChartLegendContentProps, type ChartPayloadItem, ChartTooltip, ChartTooltipContent, type ChartTooltipContentProps, SERIES_COLORS, seriesColor };
237
+ export { AreaChart, type AreaChartProps, BarChart, type BarChartProps, ChartContainer, type ChartContainerProps, type ChartDatum, ChartLegend, ChartLegendContent, type ChartLegendContentProps, type ChartPayloadItem, type ChartSeries, ChartTooltip, ChartTooltipContent, type ChartTooltipContentProps, LineChart, type LineChartProps, SERIES_COLORS, type SeriesChartProps, seriesColor };