@eduardoalvarez/arrecife 0.9.0 → 0.11.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 (41) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +111 -70
  3. package/dist/brand/index.cjs +9 -3
  4. package/dist/brand/index.d.cts +45 -6
  5. package/dist/brand/index.d.ts +45 -6
  6. package/dist/brand/index.js +2 -3
  7. package/dist/chart/index.cjs +26 -4
  8. package/dist/chart/index.d.cts +19 -3
  9. package/dist/chart/index.d.ts +19 -3
  10. package/dist/chart/index.js +26 -5
  11. package/dist/{chunk-MPZBF2TZ.js → chunk-RKGKO2TW.js} +11 -5
  12. package/dist/chunk-ZSCSKCTY.js +26 -0
  13. package/dist/form/index.js +0 -1
  14. package/dist/icons/index.d.cts +19 -10
  15. package/dist/icons/index.d.ts +19 -10
  16. package/dist/icons/index.js +2 -27
  17. package/dist/index.cjs +277 -401
  18. package/dist/index.d.cts +114 -13
  19. package/dist/index.d.ts +114 -13
  20. package/dist/index.js +211 -203
  21. package/dist/og/index.js +0 -1
  22. package/dist/shiki/index.js +0 -1
  23. package/dist/theme/index.js +0 -1
  24. package/dist/tokens/index.js +0 -1
  25. package/dist/tokens/theme.css +21 -1
  26. package/dist/variants/index.js +0 -1
  27. package/llms.txt +99 -138
  28. package/package.json +18 -30
  29. package/dist/chunk-HOADZ6GS.js +0 -72
  30. package/dist/chunk-LXRGQKMG.js +0 -145
  31. package/dist/chunk-MLKGABMK.js +0 -7
  32. package/dist/index-BbRplw_B.d.cts +0 -58
  33. package/dist/index-BbRplw_B.d.ts +0 -58
  34. package/dist/social/data.cjs +0 -161
  35. package/dist/social/data.d.cts +0 -161
  36. package/dist/social/data.d.ts +0 -161
  37. package/dist/social/data.js +0 -2
  38. package/dist/social/index.cjs +0 -153
  39. package/dist/social/index.d.cts +0 -2
  40. package/dist/social/index.d.ts +0 -2
  41. package/dist/social/index.js +0 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.0](https://github.com/Proskynete/arrecife/compare/v0.10.0...v0.11.0) (2026-09-11)
4
+
5
+
6
+ ### 🚀 Novedades
7
+
8
+ * **brand:** the fin can follow the theme with background="auto" ([3021f24](https://github.com/Proskynete/arrecife/commit/3021f24575c6f9422b7f322bf825a878a735306d))
9
+ * **chart:** valueMax sets the top of the value axis ([ccebe24](https://github.com/Proskynete/arrecife/commit/ccebe24361ce4004916b433819ddf7f53f89907a))
10
+ * **components:** CourseCard takes a cover and a closing row ([a3c73d0](https://github.com/Proskynete/arrecife/commit/a3c73d0b21edffc16c2965475fce9de6e1502926))
11
+ * **components:** PageHeader lets the screen pick the title scale ([99bd553](https://github.com/Proskynete/arrecife/commit/99bd55376992109ea90c8eaedc435ffcadffdb0e))
12
+
13
+
14
+ ### 📚 Documentación
15
+
16
+ * four decisions from cursos adopting the whole library ([2a712b8](https://github.com/Proskynete/arrecife/commit/2a712b871edd73f63fddfd2733279bf3e2834e07))
17
+ * **readme:** background="auto", valueMax, and the story count ([149442c](https://github.com/Proskynete/arrecife/commit/149442cb2748da9cb06b3df23254cba46aae378a))
18
+
19
+ ## [0.10.0](https://github.com/Proskynete/arrecife/compare/v0.9.0...v0.10.0) (2026-09-09)
20
+
21
+
22
+ ### ⚠ BREAKING CHANGES
23
+
24
+ * **icons:** `@phosphor-icons/react` becomes a REQUIRED peer dependency, and `@eduardoalvarez/arrecife/social`, `/social/data` and the root's `social` namespace are removed. Phosphor has all ten marks; the mapping table and the `tone` each one takes are in docs/runbooks/migration-0.10.md.
25
+
26
+ ### 🚀 Novedades
27
+
28
+ * **icons:** Phosphor is the only hand the library draws in ([833090e](https://github.com/Proskynete/arrecife/commit/833090e80d8d259f6965f389bc455cc72945a9c0))
29
+ * **primitives:** the alert's four marks come from Phosphor ([0a4c86e](https://github.com/Proskynete/arrecife/commit/0a4c86ef7a8f5375c27bd7cea3afa3ab7ec76c35))
30
+ * **storybook:** a way in, and a generated agent block on every component ([08eb4dd](https://github.com/Proskynete/arrecife/commit/08eb4ddcc8044ce561f88eb30894faaf00ff6e95))
31
+
32
+
33
+ ### 🐛 Correcciones
34
+
35
+ * **a11y:** the code block's scroll region is reachable by keyboard ([5de3618](https://github.com/Proskynete/arrecife/commit/5de3618a81badb92a374e7dc40d0d251c133794d))
36
+ * **components:** the audio player's waveform is Phosphor's, not five spans ([a0d92cd](https://github.com/Proskynete/arrecife/commit/a0d92cd99eaecbe1c506b60d03fa4b76f623faf0))
37
+ * **components:** the footer stacks and centres below sm ([723920e](https://github.com/Proskynete/arrecife/commit/723920e0d3513d2834ac6215b6245491ac82be54))
38
+ * **components:** the nav item row scrolls instead of pushing the page sideways ([716bb55](https://github.com/Proskynete/arrecife/commit/716bb55ccdecb796132928a73caa09e980b78015))
39
+ * **components:** ThemeToggle switches the theme without onThemeChange ([c947036](https://github.com/Proskynete/arrecife/commit/c9470363618c3c70948616fb8e1a49b54921a830))
40
+ * **primitives:** the tab list scrolls and the pagination row wraps ([e44a3dc](https://github.com/Proskynete/arrecife/commit/e44a3dc43022e25545c5802543f77e26b5bccdad))
41
+ * **primitives:** the toast carries its tone as a surface, not as a hairline ([f1c6352](https://github.com/Proskynete/arrecife/commit/f1c6352de323acef2cb4ab55555d088da380b8ae))
42
+
43
+
44
+ ### 📚 Documentación
45
+
46
+ * § 51 names the waveform, and the runbook says it moved ([aea65a1](https://github.com/Proskynete/arrecife/commit/aea65a108e11e3352c4fa35a2f27c19b2b6fac9b))
47
+ * **readme:** the library stops drawing icons of its own ([7d56012](https://github.com/Proskynete/arrecife/commit/7d5601241eef5855148f3075a805585bc56b0fcf))
48
+ * the 0.10 decision log ([254ab6a](https://github.com/Proskynete/arrecife/commit/254ab6a72a662c7303cac690eeb2e887735bd7d4))
49
+ * the 0.10 migration runbook ([1118afd](https://github.com/Proskynete/arrecife/commit/1118afdc6221f6b7a22d5b7d8f7478ea875890dc))
50
+
51
+
52
+ ### 📦 Dependencias
53
+
54
+ * ignoreDeprecations, because tsup injects a baseUrl TypeScript 6 rejects ([5eb20c8](https://github.com/Proskynete/arrecife/commit/5eb20c897346ed68b98e03b1c7f1519d57e7b513))
55
+
56
+
57
+ ### 🚀 CI/CD
58
+
59
+ * the social scope goes with the subpath that named it ([b02ab52](https://github.com/Proskynete/arrecife/commit/b02ab525f6e30a6114abada71afcd8bb6f3d06a7))
60
+
3
61
  ## [0.9.0](https://github.com/Proskynete/arrecife/compare/v0.8.0...v0.9.0) (2026-09-08)
4
62
 
5
63
 
package/README.md CHANGED
@@ -130,6 +130,22 @@ 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.9.0.** Two breaks and they are the same one twice: the library
134
+ > stopped drawing icons of its own. `@phosphor-icons/react` becomes a REQUIRED
135
+ > peer dependency — even if your project draws no icons, the `Alert` and the
136
+ > `Select` you import do — and `./social` and `./social/data` are removed.
137
+ > Phosphor has all ten marks and `tsc` points at every call site; the mapping
138
+ > table is in the runbook.
139
+ >
140
+ > Everything else is additive and asks nothing of you: the footer centres itself
141
+ > on a phone, `Nav` and `TabsList` scroll instead of pushing the page sideways,
142
+ > `Pagination` wraps, `Toast` carries a real colour again, `Alert`'s four mono
143
+ > characters become Phosphor marks, and `ThemeToggle` toggles without
144
+ > `onThemeChange` — which was a genuine bug.
145
+ >
146
+ > Run `npx arrecife` first, then read
147
+ > [`docs/runbooks/migration-0.10.md`](docs/runbooks/migration-0.10.md).
148
+
133
149
  > **Coming from 0.7.0.** Four breaks, and three of them are things the library
134
150
  > takes back OUT because nothing in the four projects drew them: the `caret`
135
151
  > utility, the footer's row of loose text links, and `SidebarNav`.
@@ -141,7 +157,7 @@ over.
141
157
  >
142
158
  > Everything else is additive: `Footer variant="full"` with columns, three chart
143
159
  > shapes, `./social/data` for a project that mounts no React, and a `Website`
144
- > glyph.
160
+ > glyph. Both `./social` subpaths were removed again in 0.10.0.
145
161
  >
146
162
  > Run `npx arrecife` first, then read
147
163
  > [`docs/runbooks/migration-0.8.md`](docs/runbooks/migration-0.8.md).
@@ -346,60 +362,63 @@ Verified by packing the library with `pnpm pack` and installing it in a separate
346
362
  project: the types resolve from `dist/`, `./tokens` loads without dragging React
347
363
  in and `./tokens/theme.css` resolves by subpath.
348
364
 
349
- ### The social icons come from `./social`
365
+ ### The social icons are yours too, and they come from Phosphor
350
366
 
351
- ```tsx
352
- // ❌ does not exist: the root publishes them grouped, not loose
353
- import { GitHub, LinkedIn } from '@eduardoalvarez/arrecife';
367
+ Until 0.10.0 this section documented a catalogue of ten hand-drawn marks at
368
+ `./social`, published a second time as shapes at `./social/data` for a consumer
369
+ that mounts no React. Both are gone.
354
370
 
355
- // ✅ the normal form
371
+ ```tsx
372
+ // ❌ removed in 0.10.0
356
373
  import { GitHub, LinkedIn } from '@eduardoalvarez/arrecife/social';
357
-
358
- // ✅ for iterating the catalogue
359
374
  import { social } from '@eduardoalvarez/arrecife';
360
- <social.GitHub />
375
+
376
+ // ✅
377
+ import { GithubLogo, LinkedinLogo } from '@phosphor-icons/react';
378
+ import { Icon } from '@eduardoalvarez/arrecife/icons';
379
+
380
+ <Icon as={GithubLogo} tone="current" />
361
381
  ```
362
382
 
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 }`.
391
-
392
- `Newsletter` is the bell, and it is named for what it means and not for what it
393
- draws — same as everything else in the system. It plays `Rss`'s role: a way to
394
- follow, not a social network. That is what keeps it inside this catalogue and
395
- keeps the catalogue from turning into an icon library.
396
-
397
- **The internal glyphs are NOT exported.** `Close`, `ChevronDown`, `Copy`, `Sun`
398
- and company are the minimum set the primitives need and they stay inside.
399
- Publishing them would turn `lib/glyphs.tsx` into the icon library the system
400
- decided not to have, and from there it grows on its own. A project that needs an
401
- icon passes its own: `Stat` receives `icon`, `Footer` receives each social link's
402
- `icon`.
383
+ | Removed | Phosphor | `tone` |
384
+ | --- | --- | --- |
385
+ | `GitHub` | `GithubLogo` | `current` |
386
+ | `LinkedIn` | `LinkedinLogo` | `current` |
387
+ | `X` | `XLogo` | `current` |
388
+ | `Instagram` | `InstagramLogo` | `current` |
389
+ | `Discord` | `DiscordLogo` | `current` |
390
+ | `YouTube` | `YoutubeLogo` | `current` |
391
+ | `Rss` | `Rss` | `action` |
392
+ | `Email` | `Envelope` | `action` |
393
+ | `Newsletter` | `BellSimple` | `action` |
394
+ | `Website` | `Globe` | `action` |
395
+
396
+ **The `tone` column is the old drawing rule, not a new one.** Six are brands and
397
+ went SOLID, which is `tone="current"` — Phosphor's `fill`. Four were functional
398
+ and used the document's 1.6 stroke, which is the default `action`. Nothing about
399
+ how the row looks changed; what changed is who draws it.
400
+
401
+ **Why they went, when the whole point of `./social/data` was that `links` had
402
+ pasted four `<path>`s by hand.** Because `links` left first. Its `Footer.astro`
403
+ now draws Phosphor through `astro-icon` and its own docstring lists the divergence:
404
+ «every icon on the page — the six in the cards and the four here — is then one set
405
+ at one weight». `eduardoalvarez.dev` did the same in `site-footer.tsx`. Two
406
+ consumers walking away from a subpath built for them is the library being out of
407
+ step, not the consumers. See `docs/decisions/0.10.md` § 51.
408
+
409
+ **In a Next Server Component, take the glyph from `@phosphor-icons/react/ssr`.**
410
+ `./icons` carries no `"use client"` on purpose, so an icon renders on the server
411
+ and ships no client JS. Phosphor's default build reads `IconContext` through
412
+ `useContext` and a hook in a Server Component throws; the `/ssr` entry is the same
413
+ icons without that read, and `Icon` works with either.
414
+
415
+ **There are no internal glyphs left to not export.** `lib/glyphs.tsx` held
416
+ `Close`, `ChevronDown`, `Copy`, `Sun` and fifteen others, and the rule was that
417
+ publishing them would turn it into the icon library the system decided not to
418
+ have. The file is deleted: the primitives draw from Phosphor like everything else.
419
+ A component that needs an icon still takes it as a prop — `Stat` receives `icon`,
420
+ `Footer` receives each social link's `icon` — and the project passes
421
+ `<Icon as={…} />`.
403
422
 
404
423
  ### The icons are yours, the way they are drawn is not
405
424
 
@@ -427,15 +446,17 @@ names. Measured on the `Minus` path itself, whose regular form is a bar of radiu
427
446
  | --- | --- | --- |
428
447
  | phosphor `regular` | 16 on a 256 grid | **0.0625em** |
429
448
  | the document | 1.6 on a 24 grid | **0.0667em** |
430
- | `lib/glyphs.tsx` | 1.75 on a 16 grid | 0.109em |
449
+ | `lib/glyphs.tsx`, until 0.10.0 | 1.75 on a 16 grid | 0.109em |
431
450
 
432
451
  Six per cent apart, which is no pixel on any screen. Nothing had to be derived and
433
452
  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.
453
+ stroke` story shows the bars so the claim can be checked instead of believed.
454
+
455
+ **The third row is history now, and it is left in because it is the argument.**
456
+ `glyphs.tsx` drew at 0.109em — three quarters heavier than both — and 0.7.0
457
+ measured it, said so and left it alone, because aligning it restyles every
458
+ primitive in the library. 0.10.0 is that change: the file is gone and there is
459
+ one line in the system. See `docs/decisions/0.10.md` § 51.
439
460
 
440
461
  **The weight is an axis with three values, and `tone` is how you name them.**
441
462
  `weight` is not a prop: Phosphor ships six and this system reads three, because
@@ -483,16 +504,17 @@ It blocked `cursos` for a whole version, and the workaround there was a
483
504
  that is a `<span>` with no interaction. It cost 272 KB of client chunk.
484
505
 
485
506
  The five portable subpaths do NOT carry it — `./tokens`, `./theme`,
486
- `./variants`, `./social/data`, `./og` and `./shiki` — and that is the half that matters more.
507
+ `./variants`, `./og` and `./shiki` — and that is the half that matters more.
487
508
  Marking them client would be a lie with a cost: a Server Component importing
488
509
  `buttonVariants`, a function that returns a string, would pull a client boundary
489
510
  in with it.
490
511
 
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
512
+ `./icons` is the third case, and it is why the check stopped looking only at the
513
+ portable ones. It renders React — it is one `<svg>` — so it can never be
493
514
  portable, and it holds no state, so it must not be a client entry either. Listed
494
515
  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.
516
+ that mistake undoes the only reason the subpath exists: an icon in a Server
517
+ Component with no client boundary opened for it.
496
518
 
497
519
  It is stamped by `scripts/add-use-client.mjs` after tsup, and not by tsup's
498
520
  `banner`. That was tried first: esbuild writes the directive and the bundling
@@ -566,8 +588,13 @@ vertical={false}`, two axes with the line and the tick off, a `type="natural"`
566
588
  and a `strokeWidth` — which `cursos` wrote four times, once per chart. None of
567
589
  that is a decision the project made. See `docs/decisions/0.8.md` § 43.
568
590
 
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/`,
591
+ A chart of a quantity with a ceiling passes it: `valueMax={100}` on a percentage.
592
+ Without it the value axis ends at the largest datum, and a horizontal ranking —
593
+ whose value axis is hidden — draws a course watched to 40 % as a full bar. See
594
+ `docs/decisions/0.11.md` § 58.
595
+
596
+ `check:exports` verifies that the five portable ones — `./tokens`, `./theme`,
597
+ `./variants`, `./og` and `./shiki` — bring no React into the published `dist/`,
571
598
  **by following the relative imports**. Without that the check was worthless: with `treeshake`
572
599
  on, each portable entry ends up as two lines re-exporting from a
573
600
  `chunk-XXXX.js`, and a grep over those two lines finds no React even when the
@@ -581,7 +608,7 @@ chunk imports it.
581
608
  | `pnpm typecheck` | `tsc --noEmit` |
582
609
  | `pnpm lint` | ESLint, including the ban on literal hexes outside `tokens.ts` |
583
610
  | `pnpm check:tokens` | fails if `src/tokens/` imports anything from outside |
584
- | `pnpm test` | compiles Tailwind and runs axe over the 208 stories, in both modes |
611
+ | `pnpm test` | compiles Tailwind and runs axe over the 248 stories, in both modes |
585
612
  | `pnpm check:exports` | verifies that `dist/` holds what `exports` promises |
586
613
  | `pnpm check:release` | validates `release-please-config.json` against the official schema |
587
614
  | `pnpm storybook` | generates the tokens and serves Storybook on 6006 |
@@ -869,12 +896,17 @@ about as library pieces. They get in anyway: the CLI aesthetic — the bar's
869
896
 
870
897
  ### Phase 3 decisions
871
898
 
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
899
+ - **It shipped no icon set, and by 0.10.0 that position had been abandoned from
900
+ both ends.** Phase 3 said the eight glyphs the primitives need are inline in
901
+ `src/lib/glyphs.tsx` and not exported; 0.7.0 added `@phosphor-icons/react` as an
902
+ optional peer on `./icons` so the set a project chooses is drawn at the system's
903
+ weight; and 0.10.0 deleted `glyphs.tsx` and `./social` and made Phosphor
904
+ required, because a library drawing in three hands cannot say which one is
905
+ right. See «The social icons are yours too» above and `docs/decisions/0.10.md`
906
+ § 51. What survives verbatim is the part that was always the real rule: the
907
+ icons are the project's, the LINE is the system's. The rest of this bullet is
908
+ the 0.7.0 argument, kept because it is what the reversal was measured against:
909
+ optional and on a subpath was the point, since the two projects that use no
878
910
  icons install nothing.
879
911
  - **No entrance animations.** Modals, menus, tooltips and toasts appear where
880
912
  they will stay. The `Switch` knob changes position without sliding. The
@@ -968,8 +1000,11 @@ skips, the 1 → 1.25 → 1.5 → 1.75 → 2 speed cycle and the volume with mut
968
1000
  portfolio's. What changed:
969
1001
 
970
1002
  **Two dependencies a package cannot have.** The portfolio's `Icon` became
971
- `src/lib/glyphs.tsx` with identical paths; `trackEvent` became the `onFirstPlay`
972
- prop, which still fires exactly once per load.
1003
+ `src/lib/glyphs.tsx` with identical paths — and since 0.10.0 those eight paths are
1004
+ gone too, replaced by `Play`, `Pause`, `SpeakerHigh`, `SpeakerSlash`,
1005
+ `CircleNotch`, `ArrowsClockwise` and the two seek arrows from Phosphor.
1006
+ `trackEvent` became the `onFirstPlay` prop, which still fires exactly once per
1007
+ load.
973
1008
 
974
1009
  **One API change.** `compact`/`banner` as two booleans became
975
1010
  `mode="full" | "compact" | "banner"`, which is the vocabulary the three modes were
@@ -1017,6 +1052,12 @@ adding a line to the catalog.
1017
1052
  Pixel analysis confirms it — 94 % of `fin-foam.png` is `#EDF4F3`, which is the
1018
1053
  foam token.
1019
1054
 
1055
+ On a site that switches theme, `background="auto"` renders both and the `light:`
1056
+ variant shows the one that reads, so no call site has to know which theme is on.
1057
+ A background that does not follow the theme — a dark panel on a light page —
1058
+ still names itself: `auto` reads the page, not the panel. See
1059
+ `docs/decisions/0.11.md` § 60.
1060
+
1020
1061
  **Rule 5 as API.** The wordmark comes from `naming.wordmark` and always reads
1021
1062
  «Eduardo Álvarez». There is no prop that changes that text, and Tiburoncín never
1022
1063
  appears written inside the logo.
@@ -168,18 +168,24 @@ function Isotype({
168
168
  className,
169
169
  ...props
170
170
  }) {
171
- const file = background === "dark" ? fins.foam : fins.color;
172
- return /* @__PURE__ */ jsxRuntime.jsx(
171
+ const fin = (file, mode) => /* @__PURE__ */ jsxRuntime.jsx(
173
172
  "img",
174
173
  {
175
174
  src: `${basePath}/${file}`,
176
175
  alt,
177
176
  width: 147,
178
177
  height: 111,
179
- className: cn("h-8 w-auto select-none", className),
178
+ className: cn("h-8 w-auto select-none", className, mode),
180
179
  ...props
181
180
  }
182
181
  );
182
+ if (background === "auto") {
183
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
184
+ fin(fins.foam, "light:hidden"),
185
+ fin(fins.color, "not-light:hidden")
186
+ ] });
187
+ }
188
+ return fin(background === "dark" ? fins.foam : fins.color);
183
189
  }
184
190
  function Logo({
185
191
  background = "dark",
@@ -3,21 +3,60 @@ export { A as ASSETS_PATH, a as Fin, f as faceList, b as faceUsage, c as faces,
3
3
  import * as react from 'react';
4
4
  import { ComponentPropsWithoutRef } from 'react';
5
5
 
6
+ /**
7
+ * Which background the fin sits on, or `auto` to let the theme decide.
8
+ *
9
+ * It is its own type and not a third member of `Background`, because
10
+ * `Background` is also what the OG templates read, and Satori has no CSS to
11
+ * resolve `auto` with: a template knows its mode and has to say it.
12
+ */
13
+ type IsotypeBackground = Background | 'auto';
6
14
  type IsotypeProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'alt'> & {
7
15
  /**
8
- * Which background it sits on. Deciding is mandatory even though it has a
9
- * default: the fin's body is nearly black, so the two-blue variant disappears
10
- * over abyss. Being a prop, the rule stops being something to remember.
16
+ * Which background it sits on, or `auto` on a site that switches theme: both
17
+ * fins are rendered and CSS shows the one that reads. Deciding is mandatory
18
+ * even though it has a default: the fin's body is nearly black, so the
19
+ * two-blue variant disappears over abyss.
20
+ *
21
+ * Use `dark` or `light` when the background does not follow the theme. A
22
+ * panel that stays dark in both modes is a fixed background, and `auto` would
23
+ * pick the page's fin instead of the panel's.
11
24
  */
12
- background?: Background | undefined;
25
+ background?: IsotypeBackground | undefined;
13
26
  basePath?: string | undefined;
14
27
  /** Alt text. Empty when the isotype accompanies text that already names it. */
15
28
  alt?: string | undefined;
16
29
  };
30
+ /**
31
+ * The fin, in the variant its background asks for.
32
+ *
33
+ * `auto` renders both and lets the `light:` variant choose, which is what
34
+ * `ThemeToggle` already does with its two icons and for the same reason: the
35
+ * server does not know the theme, and a fin picked in JavaScript would be wrong
36
+ * on the first paint half the time. `cursos` had written exactly this by hand
37
+ * after a surface changed mode and its fin vanished — not looked wrong: vanished,
38
+ * with no error and no gap in the layout. See `docs/decisions/0.11.md` § 60.
39
+ *
40
+ * Dark is the system default, so the foam fin is the one shown unless
41
+ * `data-theme="light"` says otherwise, and the two-blue one hides everywhere
42
+ * else. `not-light:hidden` rather than `hidden light:block`: the visible fin
43
+ * keeps whatever `display` the call site gave it.
44
+ *
45
+ * What it costs: the hidden image is still downloaded, 14 or 21 KB. And with
46
+ * `auto` the props land on BOTH `<img>` — an `id` or a `ref` included — so a
47
+ * call site that needs to reach the one image passes the background it is on.
48
+ * The `alt` does not double: an image with `display: none` is out of the
49
+ * accessibility tree.
50
+ */
17
51
  declare function Isotype({ background, basePath, alt, className, ...props }: IsotypeProps): react.JSX.Element;
18
52
 
19
53
  type LogoProps = Omit<ComponentPropsWithoutRef<'span'>, 'children'> & {
20
- background?: Background | undefined;
54
+ /**
55
+ * The background the logo sits on, handed to its fin. `auto` follows the
56
+ * theme — see `Isotype`. The wordmark needs no help: it is `textPrimary`, which
57
+ * already follows the mode.
58
+ */
59
+ background?: IsotypeBackground | undefined;
21
60
  basePath?: string | undefined;
22
61
  /** Hides the wordmark and leaves only the fin, for very narrow bars. */
23
62
  isotypeOnly?: boolean | undefined;
@@ -69,4 +108,4 @@ type MascotFaceProps = Base & {
69
108
  */
70
109
  declare function MascotFace({ expression, basePath, alt, className, ...props }: MascotFaceProps): react.JSX.Element;
71
110
 
72
- export { Background, Face, Isotype, type IsotypeProps, Logo, type LogoProps, Mascot, MascotFace, type MascotFaceProps, type MascotProps, Pose };
111
+ export { Background, Face, Isotype, type IsotypeBackground, type IsotypeProps, Logo, type LogoProps, Mascot, MascotFace, type MascotFaceProps, type MascotProps, Pose };
@@ -3,21 +3,60 @@ export { A as ASSETS_PATH, a as Fin, f as faceList, b as faceUsage, c as faces,
3
3
  import * as react from 'react';
4
4
  import { ComponentPropsWithoutRef } from 'react';
5
5
 
6
+ /**
7
+ * Which background the fin sits on, or `auto` to let the theme decide.
8
+ *
9
+ * It is its own type and not a third member of `Background`, because
10
+ * `Background` is also what the OG templates read, and Satori has no CSS to
11
+ * resolve `auto` with: a template knows its mode and has to say it.
12
+ */
13
+ type IsotypeBackground = Background | 'auto';
6
14
  type IsotypeProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'alt'> & {
7
15
  /**
8
- * Which background it sits on. Deciding is mandatory even though it has a
9
- * default: the fin's body is nearly black, so the two-blue variant disappears
10
- * over abyss. Being a prop, the rule stops being something to remember.
16
+ * Which background it sits on, or `auto` on a site that switches theme: both
17
+ * fins are rendered and CSS shows the one that reads. Deciding is mandatory
18
+ * even though it has a default: the fin's body is nearly black, so the
19
+ * two-blue variant disappears over abyss.
20
+ *
21
+ * Use `dark` or `light` when the background does not follow the theme. A
22
+ * panel that stays dark in both modes is a fixed background, and `auto` would
23
+ * pick the page's fin instead of the panel's.
11
24
  */
12
- background?: Background | undefined;
25
+ background?: IsotypeBackground | undefined;
13
26
  basePath?: string | undefined;
14
27
  /** Alt text. Empty when the isotype accompanies text that already names it. */
15
28
  alt?: string | undefined;
16
29
  };
30
+ /**
31
+ * The fin, in the variant its background asks for.
32
+ *
33
+ * `auto` renders both and lets the `light:` variant choose, which is what
34
+ * `ThemeToggle` already does with its two icons and for the same reason: the
35
+ * server does not know the theme, and a fin picked in JavaScript would be wrong
36
+ * on the first paint half the time. `cursos` had written exactly this by hand
37
+ * after a surface changed mode and its fin vanished — not looked wrong: vanished,
38
+ * with no error and no gap in the layout. See `docs/decisions/0.11.md` § 60.
39
+ *
40
+ * Dark is the system default, so the foam fin is the one shown unless
41
+ * `data-theme="light"` says otherwise, and the two-blue one hides everywhere
42
+ * else. `not-light:hidden` rather than `hidden light:block`: the visible fin
43
+ * keeps whatever `display` the call site gave it.
44
+ *
45
+ * What it costs: the hidden image is still downloaded, 14 or 21 KB. And with
46
+ * `auto` the props land on BOTH `<img>` — an `id` or a `ref` included — so a
47
+ * call site that needs to reach the one image passes the background it is on.
48
+ * The `alt` does not double: an image with `display: none` is out of the
49
+ * accessibility tree.
50
+ */
17
51
  declare function Isotype({ background, basePath, alt, className, ...props }: IsotypeProps): react.JSX.Element;
18
52
 
19
53
  type LogoProps = Omit<ComponentPropsWithoutRef<'span'>, 'children'> & {
20
- background?: Background | undefined;
54
+ /**
55
+ * The background the logo sits on, handed to its fin. `auto` follows the
56
+ * theme — see `Isotype`. The wordmark needs no help: it is `textPrimary`, which
57
+ * already follows the mode.
58
+ */
59
+ background?: IsotypeBackground | undefined;
21
60
  basePath?: string | undefined;
22
61
  /** Hides the wordmark and leaves only the fin, for very narrow bars. */
23
62
  isotypeOnly?: boolean | undefined;
@@ -69,4 +108,4 @@ type MascotFaceProps = Base & {
69
108
  */
70
109
  declare function MascotFace({ expression, basePath, alt, className, ...props }: MascotFaceProps): react.JSX.Element;
71
110
 
72
- export { Background, Face, Isotype, type IsotypeProps, Logo, type LogoProps, Mascot, MascotFace, type MascotFaceProps, type MascotProps, Pose };
111
+ export { Background, Face, Isotype, type IsotypeBackground, type IsotypeProps, Logo, type LogoProps, Mascot, MascotFace, type MascotFaceProps, type MascotProps, Pose };
@@ -1,6 +1,5 @@
1
1
  'use client';
2
- export { Isotype, Logo, Mascot, MascotFace } from '../chunk-MPZBF2TZ.js';
3
- export { ASSETS_PATH, faceList, faceUsage, faces, fins, poseList, poses } from '../chunk-CKRSQPTX.js';
2
+ export { Isotype, Logo, Mascot, MascotFace } from '../chunk-RKGKO2TW.js';
4
3
  import '../chunk-FAAGZG7A.js';
4
+ export { ASSETS_PATH, faceList, faceUsage, faces, fins, poseList, poses } from '../chunk-CKRSQPTX.js';
5
5
  import '../chunk-FGFNK72B.js';
6
- import '../chunk-MLKGABMK.js';
@@ -300,6 +300,7 @@ var color = (series2, index) => series2.color ?? seriesColor(index);
300
300
  var MARGIN = { left: 0, right: 12, top: 8, bottom: 0 };
301
301
  var AXIS = { tickLine: false, axisLine: false };
302
302
  var tickOf = (format) => format ? { tickFormatter: format } : {};
303
+ var domainOf = (max) => max === void 0 ? {} : { domain: [0, max] };
303
304
  var legendOf = (series2, legend) => legend ?? series2.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(ChartLegend, { content: /* @__PURE__ */ jsxRuntime.jsx(ChartLegendContent, {}) }, "legend") : null;
304
305
  var tooltipOf = (formatter) => /* @__PURE__ */ jsxRuntime.jsx(
305
306
  ChartTooltip,
@@ -316,6 +317,7 @@ function AreaChart({
316
317
  formatter,
317
318
  xTickFormatter,
318
319
  yTickFormatter,
320
+ valueMax,
319
321
  legend,
320
322
  stacked = false,
321
323
  ...container
@@ -328,7 +330,7 @@ function AreaChart({
328
330
  ] }, s.key)) }),
329
331
  /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { vertical: false }),
330
332
  /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8, ...tickOf(xTickFormatter) }),
331
- /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }),
333
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter), ...domainOf(valueMax) }),
332
334
  tooltipOf(formatter),
333
335
  legendOf(series2, legend),
334
336
  series2.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -354,6 +356,7 @@ function BarChart({
354
356
  formatter,
355
357
  xTickFormatter,
356
358
  yTickFormatter,
359
+ valueMax,
357
360
  legend,
358
361
  orientation = "vertical",
359
362
  stacked = false,
@@ -380,7 +383,16 @@ function BarChart({
380
383
  },
381
384
  "category"
382
385
  ),
383
- /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { type: "number", hide: true, ...tickOf(yTickFormatter) }, "value")
386
+ /* @__PURE__ */ jsxRuntime.jsx(
387
+ recharts.XAxis,
388
+ {
389
+ type: "number",
390
+ hide: true,
391
+ ...tickOf(yTickFormatter),
392
+ ...domainOf(valueMax)
393
+ },
394
+ "value"
395
+ )
384
396
  ] : [
385
397
  /* @__PURE__ */ jsxRuntime.jsx(
386
398
  recharts.XAxis,
@@ -392,7 +404,16 @@ function BarChart({
392
404
  },
393
405
  "category"
394
406
  ),
395
- /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }, "value")
407
+ /* @__PURE__ */ jsxRuntime.jsx(
408
+ recharts.YAxis,
409
+ {
410
+ ...AXIS,
411
+ width: 40,
412
+ ...tickOf(yTickFormatter),
413
+ ...domainOf(valueMax)
414
+ },
415
+ "value"
416
+ )
396
417
  ],
397
418
  tooltipOf(formatter),
398
419
  legendOf(series2, legend),
@@ -419,13 +440,14 @@ function LineChart({
419
440
  formatter,
420
441
  xTickFormatter,
421
442
  yTickFormatter,
443
+ valueMax,
422
444
  legend,
423
445
  ...container
424
446
  }) {
425
447
  return /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { ...container, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data: rows(data), margin: MARGIN, children: [
426
448
  /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { vertical: false }),
427
449
  /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8, ...tickOf(xTickFormatter) }),
428
- /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }),
450
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter), ...domainOf(valueMax) }),
429
451
  tooltipOf(formatter),
430
452
  legendOf(series2, legend),
431
453
  series2.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(