@eduardoalvarez/arrecife 0.10.0 → 0.12.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 (42) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +44 -13
  3. package/dist/brand/index.cjs +11 -5
  4. package/dist/brand/index.d.cts +45 -6
  5. package/dist/brand/index.d.ts +45 -6
  6. package/dist/brand/index.js +3 -3
  7. package/dist/chart/index.cjs +28 -6
  8. package/dist/chart/index.d.cts +21 -5
  9. package/dist/chart/index.d.ts +21 -5
  10. package/dist/chart/index.js +29 -7
  11. package/dist/{chunk-FGFNK72B.js → chunk-5YWGOFDX.js} +17 -4
  12. package/dist/{chunk-5A5GH2PF.js → chunk-6QJQ6K7J.js} +1 -1
  13. package/dist/{chunk-TRPBID2W.js → chunk-BFYNBIVJ.js} +1 -1
  14. package/dist/{chunk-IIT3YLYN.js → chunk-BRDTB44R.js} +12 -6
  15. package/dist/{chunk-XXDATT3A.js → chunk-FF33ARRA.js} +1 -1
  16. package/dist/{chunk-FAAGZG7A.js → chunk-TMUGT3Y3.js} +1 -1
  17. package/dist/{chunk-6IGD5REB.js → chunk-W75O3Z77.js} +1 -1
  18. package/dist/{chunk-ZSCSKCTY.js → chunk-WU66TPJT.js} +1 -1
  19. package/dist/form/index.cjs +2 -2
  20. package/dist/form/index.js +4 -4
  21. package/dist/icons/index.cjs +2 -2
  22. package/dist/icons/index.d.cts +2 -2
  23. package/dist/icons/index.d.ts +2 -2
  24. package/dist/icons/index.js +3 -3
  25. package/dist/index.cjs +153 -31
  26. package/dist/index.d.cts +95 -18
  27. package/dist/index.d.ts +95 -18
  28. package/dist/index.js +141 -38
  29. package/dist/og/index.cjs +1 -2
  30. package/dist/og/index.js +1 -1
  31. package/dist/shiki/index.js +1 -1
  32. package/dist/tokens/index.cjs +17 -4
  33. package/dist/tokens/index.d.cts +20 -5
  34. package/dist/tokens/index.d.ts +20 -5
  35. package/dist/tokens/index.js +2 -2
  36. package/dist/tokens/theme.css +20 -3
  37. package/dist/variants/index.cjs +1 -1
  38. package/dist/variants/index.d.cts +2 -2
  39. package/dist/variants/index.d.ts +2 -2
  40. package/dist/variants/index.js +1 -1
  41. package/llms.txt +71 -13
  42. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.12.0](https://github.com/Proskynete/arrecife/compare/v0.11.0...v0.12.0) (2026-09-12)
4
+
5
+
6
+ ### 🚀 Novedades
7
+
8
+ * **components:** the footer can credit the library, and it is off by default ([9b80bd8](https://github.com/Proskynete/arrecife/commit/9b80bd80dc2bc1d4637a132e3b3b9dfbc254f653))
9
+ * **storybook:** the site wears the projects' mark and prints its version ([4c36b94](https://github.com/Proskynete/arrecife/commit/4c36b94d31d7353982eaa58ab75da2b309d972ef))
10
+
11
+
12
+ ### 🐛 Correcciones
13
+
14
+ * **storybook:** the docs page stops showing the first story twice ([66f0016](https://github.com/Proskynete/arrecife/commit/66f00163ee6b1f5feb111022762928db112b709e))
15
+ * **tokens:** emit the whole token set, so a var() read from JS resolves ([68836c8](https://github.com/Proskynete/arrecife/commit/68836c861a6a80d74bd6d23c279bfc05978626dc))
16
+
17
+
18
+ ### 📚 Documentación
19
+
20
+ * one file per decision, so a reference stops carrying a version ([1deda8e](https://github.com/Proskynete/arrecife/commit/1deda8e3f09585856741e23bceebf5d18e5b5336))
21
+ * **readme:** [@theme](https://github.com/theme) static, builtWith, and how a decision is cited ([ded6f2c](https://github.com/Proskynete/arrecife/commit/ded6f2ca6cc32c4a6746ad66417afa29f66b881d))
22
+ * three decisions, and two of them are about what the library says of itself ([c52c89f](https://github.com/Proskynete/arrecife/commit/c52c89f0507d99bb3886d412e91fc11922debcd4))
23
+
24
+ ## [0.11.0](https://github.com/Proskynete/arrecife/compare/v0.10.0...v0.11.0) (2026-09-11)
25
+
26
+
27
+ ### 🚀 Novedades
28
+
29
+ * **brand:** the fin can follow the theme with background="auto" ([3021f24](https://github.com/Proskynete/arrecife/commit/3021f24575c6f9422b7f322bf825a878a735306d))
30
+ * **chart:** valueMax sets the top of the value axis ([ccebe24](https://github.com/Proskynete/arrecife/commit/ccebe24361ce4004916b433819ddf7f53f89907a))
31
+ * **components:** CourseCard takes a cover and a closing row ([a3c73d0](https://github.com/Proskynete/arrecife/commit/a3c73d0b21edffc16c2965475fce9de6e1502926))
32
+ * **components:** PageHeader lets the screen pick the title scale ([99bd553](https://github.com/Proskynete/arrecife/commit/99bd55376992109ea90c8eaedc435ffcadffdb0e))
33
+
34
+
35
+ ### 📚 Documentación
36
+
37
+ * four decisions from cursos adopting the whole library ([2a712b8](https://github.com/Proskynete/arrecife/commit/2a712b871edd73f63fddfd2733279bf3e2834e07))
38
+ * **readme:** background="auto", valueMax, and the story count ([149442c](https://github.com/Proskynete/arrecife/commit/149442cb2748da9cb06b3df23254cba46aae378a))
39
+
3
40
  ## [0.10.0](https://github.com/Proskynete/arrecife/compare/v0.9.0...v0.10.0) (2026-09-09)
4
41
 
5
42
 
package/README.md CHANGED
@@ -16,7 +16,10 @@ in a project with a `#E05252` that this README has declared wrong for months, an
16
16
  nobody saw it because the document was not greppable from the code.
17
17
 
18
18
  `docs/decisions/` is the other half: the points where the code and the document
19
- do not say the same thing, each with its resolution and its reason.
19
+ do not say the same thing, each with its resolution and its reason. One file per
20
+ decision, named for its number — `045-signature-halo.md` — so a «§ 45» written
21
+ in a comment, a commit or a PR resolves by sorting the folder, and no reference
22
+ has to carry a version.
20
23
 
21
24
  ## The two documents for agents
22
25
 
@@ -52,6 +55,16 @@ One source, `src/tokens/tokens.ts`. One generated output,
52
55
  `scripts/build-tokens.mjs`; it is not edited by hand and it is regenerated on
53
56
  every build.
54
57
 
58
+ The block is `@theme static`, so **every token reaches `:root` whether or not a
59
+ class asks for it** — you can read any of them with `var(--color-…)` from your
60
+ own JavaScript or an inline style and it will be there. That is one word and it
61
+ is a bug fix: Tailwind v4 emits a theme variable only when some generated
62
+ utility uses one, and the four `--color-series-*` are read by `seriesColor()`
63
+ through `var()` and requested by no class anywhere, so they were being dropped
64
+ as unused and the charts drew black on a dark page with nothing erroring. It
65
+ costs +234 B gzipped for a project using the system's whole vocabulary, and the
66
+ ceiling is the token set. See `docs/decisions/` § 61.
67
+
55
68
  Phase 0 decision: **v4 only**. The portfolio (`eduardoalvarez.dev`) migrates from
56
69
  Tailwind v3 to v4 before consuming Arrecife. If that migration slips, publishing
57
70
  the v3 preset again is one more emitter in `build-tokens.mjs` reading the same
@@ -404,7 +417,7 @@ now draws Phosphor through `astro-icon` and its own docstring lists the divergen
404
417
  «every icon on the page — the six in the cards and the four here — is then one set
405
418
  at one weight». `eduardoalvarez.dev` did the same in `site-footer.tsx`. Two
406
419
  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.
420
+ step, not the consumers. See `docs/decisions/` § 51.
408
421
 
409
422
  **In a Next Server Component, take the glyph from `@phosphor-icons/react/ssr`.**
410
423
  `./icons` carries no `"use client"` on purpose, so an icon renders on the server
@@ -456,7 +469,7 @@ stroke` story shows the bars so the claim can be checked instead of believed.
456
469
  `glyphs.tsx` drew at 0.109em — three quarters heavier than both — and 0.7.0
457
470
  measured it, said so and left it alone, because aligning it restyles every
458
471
  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.
472
+ one line in the system. See `docs/decisions/` § 51.
460
473
 
461
474
  **The weight is an axis with three values, and `tone` is how you name them.**
462
475
  `weight` is not a prop: Phosphor ships six and this system reads three, because
@@ -474,7 +487,7 @@ one channel WCAG 1.4.1 says may not carry meaning** — the fill is the second
474
487
  channel, and it is the one that survives a forced-colours mode where the biolume
475
488
  does not. `quiet` is the opposite problem: in a metadata row the icon is not the
476
489
  point of the line, and at `regular` it draws as heavy as the date beside it.
477
- `docs/decisions/0.7.md` § 35 has the rest.
490
+ `docs/decisions/` § 35 has the rest.
478
491
 
479
492
  `@phosphor-icons/react` is an **optional** peer dependency on its own subpath, by
480
493
  the same rule as `./form` and `./chart`: two of the five projects use no icons and
@@ -586,7 +599,19 @@ collision is deliberate: what they replace is not an import, it is sixty lines o
586
599
  composition — a `linearGradient` with a hardcoded id, a `CartesianGrid
587
600
  vertical={false}`, two axes with the line and the tick off, a `type="natural"`
588
601
  and a `strokeWidth` — which `cursos` wrote four times, once per chart. None of
589
- that is a decision the project made. See `docs/decisions/0.8.md` § 43.
602
+ that is a decision the project made. See `docs/decisions/` § 43.
603
+
604
+ A chart of a quantity with a ceiling passes it: `valueMax={100}` on a percentage.
605
+ Without it the value axis ends at the largest datum, and a horizontal ranking —
606
+ whose value axis is hidden — draws a course watched to 40 % as a full bar. See
607
+ `docs/decisions/` § 58.
608
+
609
+ `seriesColor(i)` returns `var(--color-series-N)` and not a hexadecimal, so the
610
+ palette follows the mode instead of freezing to whichever one was live when the
611
+ chart mounted. **Do not add a fallback to it**: the variable is always in
612
+ `:root`, because the token block is `@theme static` for exactly this reason. On a
613
+ version before that fix the `var()` resolved to nothing and the bars came out
614
+ black — see `docs/decisions/` § 61.
590
615
 
591
616
  `check:exports` verifies that the five portable ones — `./tokens`, `./theme`,
592
617
  `./variants`, `./og` and `./shiki` — bring no React into the published `dist/`,
@@ -603,7 +628,7 @@ chunk imports it.
603
628
  | `pnpm typecheck` | `tsc --noEmit` |
604
629
  | `pnpm lint` | ESLint, including the ban on literal hexes outside `tokens.ts` |
605
630
  | `pnpm check:tokens` | fails if `src/tokens/` imports anything from outside |
606
- | `pnpm test` | compiles Tailwind and runs axe over the 208 stories, in both modes |
631
+ | `pnpm test` | compiles Tailwind and runs axe over the 248 stories, in both modes |
607
632
  | `pnpm check:exports` | verifies that `dist/` holds what `exports` promises |
608
633
  | `pnpm check:release` | validates `release-please-config.json` against the official schema |
609
634
  | `pnpm storybook` | generates the tokens and serves Storybook on 6006 |
@@ -657,7 +682,7 @@ cannot see: axe does not evaluate text over a gradient, so both modes passed it.
657
682
 
658
683
  The light blocks now sweep between `background` and `surface` and never touch
659
684
  `surfaceRaised`, so the darkest point of either one is the page itself — a token
660
- 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
685
+ that passes on the page passes at every point of the sweep. `docs/decisions/` § 9 has the measurements, including the two other things the first composition
661
686
  got wrong.
662
687
 
663
688
  ### The third correction: a semantic color is not a text color over its own tint
@@ -856,7 +881,7 @@ run summary.
856
881
  `SidebarNav` was on that list and came off it in 0.8.0. It met the rule below
857
882
  on the identity half and never on the consumer half: the two admin projects it
858
883
  was built for each wrote their own and never imported it. See
859
- `docs/decisions/0.8.md` § 48.
884
+ `docs/decisions/` § 48.
860
885
 
861
886
  The criterion for deciding what gets in is still the same: **it encodes an
862
887
  identity rule, it has two or more consumers, and it drags in no project
@@ -897,7 +922,7 @@ about as library pieces. They get in anyway: the CLI aesthetic — the bar's
897
922
  optional peer on `./icons` so the set a project chooses is drawn at the system's
898
923
  weight; and 0.10.0 deleted `glyphs.tsx` and `./social` and made Phosphor
899
924
  required, because a library drawing in three hands cannot say which one is
900
- right. See «The social icons are yours too» above and `docs/decisions/0.10.md`
925
+ right. See «The social icons are yours too» above and `docs/decisions/`
901
926
  § 51. What survives verbatim is the part that was always the real rule: the
902
927
  icons are the project's, the LINE is the system's. The rest of this bullet is
903
928
  the 0.7.0 argument, kept because it is what the reversal was measured against:
@@ -934,7 +959,7 @@ about as library pieces. They get in anyway: the CLI aesthetic — the bar's
934
959
  than width failed axe on `scrollable-region-focusable` immediately. It is
935
960
  unconditional, because whether a table overflows depends on the viewport and
936
961
  the only alternative is a ResizeObserver on every table in the system. See
937
- `docs/decisions/0.8.md` § 39.
962
+ `docs/decisions/` § 39.
938
963
 
939
964
  ### The syntax palette
940
965
 
@@ -1047,6 +1072,12 @@ adding a line to the catalog.
1047
1072
  Pixel analysis confirms it — 94 % of `fin-foam.png` is `#EDF4F3`, which is the
1048
1073
  foam token.
1049
1074
 
1075
+ On a site that switches theme, `background="auto"` renders both and the `light:`
1076
+ variant shows the one that reads, so no call site has to know which theme is on.
1077
+ A background that does not follow the theme — a dark panel on a light page —
1078
+ still names itself: `auto` reads the page, not the panel. See
1079
+ `docs/decisions/` § 60.
1080
+
1050
1081
  **Rule 5 as API.** The wordmark comes from `naming.wordmark` and always reads
1051
1082
  «Eduardo Álvarez». There is no prop that changes that text, and Tiburoncín never
1052
1083
  appears written inside the logo.
@@ -1122,7 +1153,7 @@ spatial continuity. A sixth lands on that or it does not exist.
1122
1153
  The bar is 2px and not a block because a halo needs something thin to radiate
1123
1154
  from, the colour is `var(--color-accent)` so it follows the mode, and
1124
1155
  `motion-safe` is the one thing not copied from `cursos` — whose span animates
1125
- regardless of the setting. See `docs/decisions/0.8.md` § 45, and § 23 for the entry
1156
+ regardless of the setting. See `docs/decisions/` § 45, and § 23 for the entry
1126
1157
  it reverses.
1127
1158
 
1128
1159
  ### The second motion exception
@@ -1169,7 +1200,7 @@ text, and `surfaceRaised` is where a toolbar lives.
1169
1200
  `destructiveOutline` fills on hover, and that is a declared exception to
1170
1201
  «secondary is never filled» — a destructive that looks identical to a secondary
1171
1202
  until you read it is the problem the variant exists to fix. See
1172
- `docs/decisions/0.6.md` § 21.
1203
+ `docs/decisions/` § 21.
1173
1204
 
1174
1205
  ### `icon-sm`, for the one admin app
1175
1206
 
@@ -1180,7 +1211,7 @@ three actions per table row, and at 42 the row grows with them.
1180
1211
  `size="icon-sm"` is 32×32, and it is 32 and not the 28 that project actually had:
1181
1212
  32 is `sm`'s height, so a dense icon button lines up with a small text button and
1182
1213
  a toolbar mixing the two stays on one baseline. It does not replace `icon` — a
1183
- page's primary action stays at 42. See `docs/decisions/0.6.md` § 22.
1214
+ page's primary action stays at 42. See `docs/decisions/` § 22.
1184
1215
 
1185
1216
  ### The theme script, and the mode a site already decided
1186
1217
 
@@ -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/`.
78
+ * outweighed the title they accompany. See `docs/decisions/` § 10.
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/0.6.md` § 22.
124
+ * a row of a table. See `docs/decisions/` § 22.
125
125
  */
126
126
  iconSm: 32
127
127
  };
@@ -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/` § 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/` § 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,5 +1,5 @@
1
1
  'use client';
2
- export { Isotype, Logo, Mascot, MascotFace } from '../chunk-IIT3YLYN.js';
3
- import '../chunk-FAAGZG7A.js';
2
+ export { Isotype, Logo, Mascot, MascotFace } from '../chunk-BRDTB44R.js';
3
+ import '../chunk-TMUGT3Y3.js';
4
4
  export { ASSETS_PATH, faceList, faceUsage, faces, fins, poseList, poses } from '../chunk-CKRSQPTX.js';
5
- import '../chunk-FGFNK72B.js';
5
+ import '../chunk-5YWGOFDX.js';
@@ -46,7 +46,7 @@ var typeScale = {
46
46
  * (plankton 5.57:1 over abyss), which is the part that is not negotiable.
47
47
  *
48
48
  * At 13 the three badge families grew past the size of a small button and
49
- * outweighed the title they accompany. See `docs/decisions/`.
49
+ * outweighed the title they accompany. See `docs/decisions/` § 10.
50
50
  */
51
51
  chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
52
52
  /**
@@ -92,7 +92,7 @@ var control = {
92
92
  * baseline; 28 would have been a fifth height that matches nothing.
93
93
  *
94
94
  * It does not replace `icon`. A page's primary action stays at 42; this is for
95
- * a row of a table. See `docs/decisions/0.6.md` § 22.
95
+ * a row of a table. See `docs/decisions/` § 22.
96
96
  */
97
97
  iconSm: 32
98
98
  };
@@ -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(
@@ -29,7 +29,7 @@ import { Legend, Tooltip } from 'recharts';
29
29
  * What IS published, since this version, are three CHART TYPES on top of that
30
30
  * chassis: `AreaChart`, `BarChart` and `LineChart`. They are not wrappers over
31
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
32
+ * and draw the whole thing. See `docs/decisions/` § 43 for why the names
33
33
  * collide on purpose.
34
34
  */
35
35
  /**
@@ -175,7 +175,7 @@ type SeriesChartProps = Omit<ChartContainerProps, 'children'> & {
175
175
  * `2026-07-15` repeated thirty times, overlapping into a grey band.
176
176
  *
177
177
  * The library imposes no format: a date key can be a day, a month or a course
178
- * name, and only the project knows which. See `docs/decisions/0.9.md` § 50.
178
+ * name, and only the project knows which. See `docs/decisions/` § 50.
179
179
  */
180
180
  xTickFormatter?: ((value: unknown) => string) | undefined;
181
181
  /**
@@ -187,6 +187,22 @@ type SeriesChartProps = Omit<ChartContainerProps, 'children'> & {
187
187
  * «$1200» in the tooltip, and the axis is the one you read while comparing.
188
188
  */
189
189
  yTickFormatter?: ((value: unknown) => string) | undefined;
190
+ /**
191
+ * The top of the value axis, when the scale has one that the data does not
192
+ * reach — 100 for a percentage.
193
+ *
194
+ * Without it the axis ends at the largest datum, and on a chart of «% visto»
195
+ * a course watched to 40 % draws as a full bar when it is the highest on the
196
+ * list. On `BarChart orientation="horizontal"` nothing gives that away: the
197
+ * value axis is hidden by design, so the chart says something false and
198
+ * looks exactly like one that does not.
199
+ *
200
+ * The bottom is always zero. A bar that does not start at zero is a
201
+ * different lie, and this prop is not a way into it. It is a floor for the
202
+ * top and not a clip either: a datum above it extends the axis instead of
203
+ * running off the edge. See `docs/decisions/` § 58.
204
+ */
205
+ valueMax?: number | undefined;
190
206
  /**
191
207
  * Shows the legend. It defaults to «only when there is more than one series»:
192
208
  * a legend naming the one line already named by the chart's own heading is a
@@ -217,7 +233,7 @@ type AreaChartProps = SeriesChartProps & {
217
233
  /** Adds the series up instead of overlaying them. */
218
234
  stacked?: boolean | undefined;
219
235
  };
220
- declare function AreaChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
236
+ declare function AreaChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
221
237
  /**
222
238
  * Bars, upright or lying down.
223
239
  *
@@ -241,7 +257,7 @@ type BarChartProps = SeriesChartProps & {
241
257
  /** Stacks the series instead of putting them side by side. */
242
258
  stacked?: boolean | undefined;
243
259
  };
244
- declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
260
+ declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
245
261
  /**
246
262
  * Lines, for comparing series against each other.
247
263
  *
@@ -254,6 +270,6 @@ declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTick
254
270
  * where it means «this one».
255
271
  */
256
272
  type LineChartProps = SeriesChartProps;
257
- declare function LineChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, ...container }: LineChartProps): react.JSX.Element;
273
+ declare function LineChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, ...container }: LineChartProps): react.JSX.Element;
258
274
 
259
275
  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 };
@@ -29,7 +29,7 @@ import { Legend, Tooltip } from 'recharts';
29
29
  * What IS published, since this version, are three CHART TYPES on top of that
30
30
  * chassis: `AreaChart`, `BarChart` and `LineChart`. They are not wrappers over
31
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
32
+ * and draw the whole thing. See `docs/decisions/` § 43 for why the names
33
33
  * collide on purpose.
34
34
  */
35
35
  /**
@@ -175,7 +175,7 @@ type SeriesChartProps = Omit<ChartContainerProps, 'children'> & {
175
175
  * `2026-07-15` repeated thirty times, overlapping into a grey band.
176
176
  *
177
177
  * The library imposes no format: a date key can be a day, a month or a course
178
- * name, and only the project knows which. See `docs/decisions/0.9.md` § 50.
178
+ * name, and only the project knows which. See `docs/decisions/` § 50.
179
179
  */
180
180
  xTickFormatter?: ((value: unknown) => string) | undefined;
181
181
  /**
@@ -187,6 +187,22 @@ type SeriesChartProps = Omit<ChartContainerProps, 'children'> & {
187
187
  * «$1200» in the tooltip, and the axis is the one you read while comparing.
188
188
  */
189
189
  yTickFormatter?: ((value: unknown) => string) | undefined;
190
+ /**
191
+ * The top of the value axis, when the scale has one that the data does not
192
+ * reach — 100 for a percentage.
193
+ *
194
+ * Without it the axis ends at the largest datum, and on a chart of «% visto»
195
+ * a course watched to 40 % draws as a full bar when it is the highest on the
196
+ * list. On `BarChart orientation="horizontal"` nothing gives that away: the
197
+ * value axis is hidden by design, so the chart says something false and
198
+ * looks exactly like one that does not.
199
+ *
200
+ * The bottom is always zero. A bar that does not start at zero is a
201
+ * different lie, and this prop is not a way into it. It is a floor for the
202
+ * top and not a clip either: a datum above it extends the axis instead of
203
+ * running off the edge. See `docs/decisions/` § 58.
204
+ */
205
+ valueMax?: number | undefined;
190
206
  /**
191
207
  * Shows the legend. It defaults to «only when there is more than one series»:
192
208
  * a legend naming the one line already named by the chart's own heading is a
@@ -217,7 +233,7 @@ type AreaChartProps = SeriesChartProps & {
217
233
  /** Adds the series up instead of overlaying them. */
218
234
  stacked?: boolean | undefined;
219
235
  };
220
- declare function AreaChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
236
+ declare function AreaChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, stacked, ...container }: AreaChartProps): react.JSX.Element;
221
237
  /**
222
238
  * Bars, upright or lying down.
223
239
  *
@@ -241,7 +257,7 @@ type BarChartProps = SeriesChartProps & {
241
257
  /** Stacks the series instead of putting them side by side. */
242
258
  stacked?: boolean | undefined;
243
259
  };
244
- declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
260
+ declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, orientation, stacked, ...container }: BarChartProps): react.JSX.Element;
245
261
  /**
246
262
  * Lines, for comparing series against each other.
247
263
  *
@@ -254,6 +270,6 @@ declare function BarChart({ data, series, xKey, formatter, xTickFormatter, yTick
254
270
  * where it means «this one».
255
271
  */
256
272
  type LineChartProps = SeriesChartProps;
257
- declare function LineChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, legend, ...container }: LineChartProps): react.JSX.Element;
273
+ declare function LineChart({ data, series, xKey, formatter, xTickFormatter, yTickFormatter, valueMax, legend, ...container }: LineChartProps): react.JSX.Element;
258
274
 
259
275
  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 };