@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
@@ -1,8 +1,8 @@
1
1
  'use client';
2
- import { Text } from '../chunk-TRPBID2W.js';
2
+ import { Text } from '../chunk-BFYNBIVJ.js';
3
3
  import '../chunk-ODBFN44D.js';
4
- import { cn } from '../chunk-FAAGZG7A.js';
5
- import { radius } from '../chunk-FGFNK72B.js';
4
+ import { cn } from '../chunk-TMUGT3Y3.js';
5
+ import { radius } from '../chunk-5YWGOFDX.js';
6
6
  import { useId } from 'react';
7
7
  import { ResponsiveContainer, Tooltip, Legend, AreaChart as AreaChart$1, CartesianGrid, XAxis, YAxis, Area, BarChart as BarChart$1, Bar, LineChart as LineChart$1, Line } from 'recharts';
8
8
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -106,6 +106,7 @@ var color = (series, index) => series.color ?? seriesColor(index);
106
106
  var MARGIN = { left: 0, right: 12, top: 8, bottom: 0 };
107
107
  var AXIS = { tickLine: false, axisLine: false };
108
108
  var tickOf = (format) => format ? { tickFormatter: format } : {};
109
+ var domainOf = (max) => max === void 0 ? {} : { domain: [0, max] };
109
110
  var legendOf = (series, legend) => legend ?? series.length > 1 ? /* @__PURE__ */ jsx(ChartLegend, { content: /* @__PURE__ */ jsx(ChartLegendContent, {}) }, "legend") : null;
110
111
  var tooltipOf = (formatter) => /* @__PURE__ */ jsx(
111
112
  ChartTooltip,
@@ -122,6 +123,7 @@ function AreaChart({
122
123
  formatter,
123
124
  xTickFormatter,
124
125
  yTickFormatter,
126
+ valueMax,
125
127
  legend,
126
128
  stacked = false,
127
129
  ...container
@@ -134,7 +136,7 @@ function AreaChart({
134
136
  ] }, s.key)) }),
135
137
  /* @__PURE__ */ jsx(CartesianGrid, { vertical: false }),
136
138
  /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8, ...tickOf(xTickFormatter) }),
137
- /* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }),
139
+ /* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter), ...domainOf(valueMax) }),
138
140
  tooltipOf(formatter),
139
141
  legendOf(series, legend),
140
142
  series.map((s, i) => /* @__PURE__ */ jsx(
@@ -160,6 +162,7 @@ function BarChart({
160
162
  formatter,
161
163
  xTickFormatter,
162
164
  yTickFormatter,
165
+ valueMax,
163
166
  legend,
164
167
  orientation = "vertical",
165
168
  stacked = false,
@@ -186,7 +189,16 @@ function BarChart({
186
189
  },
187
190
  "category"
188
191
  ),
189
- /* @__PURE__ */ jsx(XAxis, { type: "number", hide: true, ...tickOf(yTickFormatter) }, "value")
192
+ /* @__PURE__ */ jsx(
193
+ XAxis,
194
+ {
195
+ type: "number",
196
+ hide: true,
197
+ ...tickOf(yTickFormatter),
198
+ ...domainOf(valueMax)
199
+ },
200
+ "value"
201
+ )
190
202
  ] : [
191
203
  /* @__PURE__ */ jsx(
192
204
  XAxis,
@@ -198,7 +210,16 @@ function BarChart({
198
210
  },
199
211
  "category"
200
212
  ),
201
- /* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }, "value")
213
+ /* @__PURE__ */ jsx(
214
+ YAxis,
215
+ {
216
+ ...AXIS,
217
+ width: 40,
218
+ ...tickOf(yTickFormatter),
219
+ ...domainOf(valueMax)
220
+ },
221
+ "value"
222
+ )
202
223
  ],
203
224
  tooltipOf(formatter),
204
225
  legendOf(series, legend),
@@ -225,13 +246,14 @@ function LineChart({
225
246
  formatter,
226
247
  xTickFormatter,
227
248
  yTickFormatter,
249
+ valueMax,
228
250
  legend,
229
251
  ...container
230
252
  }) {
231
253
  return /* @__PURE__ */ jsx(ChartContainer, { ...container, children: /* @__PURE__ */ jsxs(LineChart$1, { data: rows(data), margin: MARGIN, children: [
232
254
  /* @__PURE__ */ jsx(CartesianGrid, { vertical: false }),
233
255
  /* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...AXIS, tickMargin: 8, ...tickOf(xTickFormatter) }),
234
- /* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter) }),
256
+ /* @__PURE__ */ jsx(YAxis, { ...AXIS, width: 40, ...tickOf(yTickFormatter), ...domainOf(valueMax) }),
235
257
  tooltipOf(formatter),
236
258
  legendOf(series, legend),
237
259
  series.map((s, i) => /* @__PURE__ */ jsx(
@@ -144,7 +144,7 @@ var typeScale = {
144
144
  * (plankton 5.57:1 over abyss), which is the part that is not negotiable.
145
145
  *
146
146
  * At 13 the three badge families grew past the size of a small button and
147
- * outweighed the title they accompany. See `docs/decisions/`.
147
+ * outweighed the title they accompany. See `docs/decisions/` § 10.
148
148
  */
149
149
  chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
150
150
  /**
@@ -198,7 +198,7 @@ var control = {
198
198
  * baseline; 28 would have been a fifth height that matches nothing.
199
199
  *
200
200
  * It does not replace `icon`. A page's primary action stays at 42; this is for
201
- * a row of a table. See `docs/decisions/0.6.md` § 22.
201
+ * a row of a table. See `docs/decisions/` § 22.
202
202
  */
203
203
  iconSm: 32
204
204
  };
@@ -221,7 +221,7 @@ var size = {
221
221
  * together eat the top of the content area. 56 is what `cursos` had arrived at
222
222
  * on its own, and it is the same reasoning as `control.iconSm`: the one admin
223
223
  * app of the five is denser than the four reading sites. See
224
- * `docs/decisions/0.7.md` § 30.
224
+ * `docs/decisions/` § 30.
225
225
  */
226
226
  navCompact: 56,
227
227
  /**
@@ -294,7 +294,20 @@ var naming = {
294
294
  wordmark: "Eduardo \xC1lvarez",
295
295
  mascot: "Tiburonc\xEDn",
296
296
  /** The domain, for the footer CLI signature: `$ cd ~/eduardoalvarez.dev/2026`. */
297
- domain: "eduardoalvarez.dev"
297
+ domain: "eduardoalvarez.dev",
298
+ /** The library's own name, for a footer that credits it. */
299
+ library: "Arrecife",
300
+ /**
301
+ * Where the library is published, for `Footer builtWith`.
302
+ *
303
+ * It is here and not written into the component for the reason the domain is:
304
+ * one string, five projects. It is the same URL as `homepage` in
305
+ * `package.json` and it cannot be derived from it — this file imports nothing
306
+ * — so `check:exports` compares the two on every build. Without that, moving
307
+ * the Storybook would update what npm prints and leave five production
308
+ * footers linking at the old address.
309
+ */
310
+ libraryUrl: "https://arrecife.eduardoalvarez.dev"
298
311
  };
299
312
 
300
313
  export { brand, colors, control, dark, fonts, gradient, light, limits, motion, naming, radius, series, shadow, size, spacing, syntax, tagline, typeScale };
@@ -1,4 +1,4 @@
1
- import { naming, tagline, motion, shadow, series, syntax, gradient, size, spacing, control, radius, limits, typeScale, fonts, brand, colors } from './chunk-FGFNK72B.js';
1
+ import { naming, tagline, motion, shadow, series, syntax, gradient, size, spacing, control, radius, limits, typeScale, fonts, brand, colors } from './chunk-5YWGOFDX.js';
2
2
 
3
3
  // src/tokens/index.ts
4
4
  var tokens = {
@@ -1,5 +1,5 @@
1
1
  import { text } from './chunk-ODBFN44D.js';
2
- import { cn } from './chunk-FAAGZG7A.js';
2
+ import { cn } from './chunk-TMUGT3Y3.js';
3
3
  import { Slot } from '@radix-ui/react-slot';
4
4
  import 'class-variance-authority';
5
5
  import { jsx } from 'react/jsx-runtime';
@@ -1,7 +1,7 @@
1
- import { cn } from './chunk-FAAGZG7A.js';
1
+ import { cn } from './chunk-TMUGT3Y3.js';
2
2
  import { fins, ASSETS_PATH, poses, faces } from './chunk-CKRSQPTX.js';
3
- import { naming, tagline } from './chunk-FGFNK72B.js';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { naming, tagline } from './chunk-5YWGOFDX.js';
4
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
5
 
6
6
  function Isotype({
7
7
  background = "dark",
@@ -10,18 +10,24 @@ function Isotype({
10
10
  className,
11
11
  ...props
12
12
  }) {
13
- const file = background === "dark" ? fins.foam : fins.color;
14
- return /* @__PURE__ */ jsx(
13
+ const fin = (file, mode) => /* @__PURE__ */ jsx(
15
14
  "img",
16
15
  {
17
16
  src: `${basePath}/${file}`,
18
17
  alt,
19
18
  width: 147,
20
19
  height: 111,
21
- className: cn("h-8 w-auto select-none", className),
20
+ className: cn("h-8 w-auto select-none", className, mode),
22
21
  ...props
23
22
  }
24
23
  );
24
+ if (background === "auto") {
25
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
26
+ fin(fins.foam, "light:hidden"),
27
+ fin(fins.color, "not-light:hidden")
28
+ ] });
29
+ }
30
+ return fin(background === "dark" ? fins.foam : fins.color);
25
31
  }
26
32
  function Logo({
27
33
  background = "dark",
@@ -19,7 +19,7 @@ var button = cva(
19
19
  ],
20
20
  // The one control whose focus ring is not biolume. It is the system's
21
21
  // only sand fill, and a biolume ring three pixels off a sand button puts
22
- // both of the brand's accents in the same glance. See `docs/decisions/0.7.md` § 37.
22
+ // both of the brand's accents in the same glance. See `docs/decisions/` § 37.
23
23
  conversion: "bg-warm text-warm-on hover:bg-warm-hover focus-ring-warm",
24
24
  secondary: [
25
25
  "border-hairline-hover border bg-transparent text-text-primary",
@@ -1,4 +1,4 @@
1
- import { spacing, control, radius, typeScale } from './chunk-FGFNK72B.js';
1
+ import { spacing, control, radius, typeScale } from './chunk-5YWGOFDX.js';
2
2
  import { clsx } from 'clsx';
3
3
  import { extendTailwindMerge } from 'tailwind-merge';
4
4
 
@@ -1,4 +1,4 @@
1
- import { cn } from './chunk-FAAGZG7A.js';
1
+ import { cn } from './chunk-TMUGT3Y3.js';
2
2
  import * as LabelPrimitive from '@radix-ui/react-label';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
@@ -1,4 +1,4 @@
1
- import { cn } from './chunk-FAAGZG7A.js';
1
+ import { cn } from './chunk-TMUGT3Y3.js';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
 
4
4
  var ICON_WEIGHT = "regular";
@@ -67,7 +67,7 @@ var typeScale = {
67
67
  * (plankton 5.57:1 over abyss), which is the part that is not negotiable.
68
68
  *
69
69
  * At 13 the three badge families grew past the size of a small button and
70
- * outweighed the title they accompany. See `docs/decisions/`.
70
+ * outweighed the title they accompany. See `docs/decisions/` § 10.
71
71
  */
72
72
  chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
73
73
  /**
@@ -113,7 +113,7 @@ var control = {
113
113
  * baseline; 28 would have been a fifth height that matches nothing.
114
114
  *
115
115
  * It does not replace `icon`. A page's primary action stays at 42; this is for
116
- * a row of a table. See `docs/decisions/0.6.md` § 22.
116
+ * a row of a table. See `docs/decisions/` § 22.
117
117
  */
118
118
  iconSm: 32
119
119
  };
@@ -1,9 +1,9 @@
1
1
  'use client';
2
- import { Label } from '../chunk-6IGD5REB.js';
3
- import { Text } from '../chunk-TRPBID2W.js';
2
+ import { Label } from '../chunk-W75O3Z77.js';
3
+ import { Text } from '../chunk-BFYNBIVJ.js';
4
4
  import '../chunk-ODBFN44D.js';
5
- import { cn } from '../chunk-FAAGZG7A.js';
6
- import '../chunk-FGFNK72B.js';
5
+ import { cn } from '../chunk-TMUGT3Y3.js';
6
+ import '../chunk-5YWGOFDX.js';
7
7
  import { createContext, useContext, useId } from 'react';
8
8
  import { FormProvider, Controller, useFormContext, useFormState } from 'react-hook-form';
9
9
  import { Slot } from '@radix-ui/react-slot';
@@ -41,7 +41,7 @@ var typeScale = {
41
41
  * (plankton 5.57:1 over abyss), which is the part that is not negotiable.
42
42
  *
43
43
  * At 13 the three badge families grew past the size of a small button and
44
- * outweighed the title they accompany. See `docs/decisions/`.
44
+ * outweighed the title they accompany. See `docs/decisions/` § 10.
45
45
  */
46
46
  chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
47
47
  /**
@@ -87,7 +87,7 @@ var control = {
87
87
  * baseline; 28 would have been a fifth height that matches nothing.
88
88
  *
89
89
  * It does not replace `icon`. A page's primary action stays at 42; this is for
90
- * a row of a table. See `docs/decisions/0.6.md` § 22.
90
+ * a row of a table. See `docs/decisions/` § 22.
91
91
  */
92
92
  iconSm: 32
93
93
  };
@@ -43,7 +43,7 @@ import { IconWeight, IconProps as IconProps$1, Icon as Icon$1 } from '@phosphor-
43
43
  * THE WEIGHT IS AN AXIS, AND IT IS THE ONLY ONE AN ICON HAS. There are three
44
44
  * roles and `tone` is how you name them; the weights themselves are not a prop,
45
45
  * because Phosphor's other three — `thin`, `bold`, `duotone` — say nothing this
46
- * system means. See `TONE_WEIGHT` below and `docs/decisions/0.7.md` § 35.
46
+ * system means. See `TONE_WEIGHT` below and `docs/decisions/` § 35.
47
47
  *
48
48
  * IT IS NOW THE ONLY LINE THE LIBRARY DRAWS, which is what 0.10.0 changed.
49
49
  * `lib/glyphs.tsx` drew at 1.75 on a 16 grid — 0.109em, three quarters heavier
@@ -51,7 +51,7 @@ import { IconWeight, IconProps as IconProps$1, Icon as Icon$1 } from '@phosphor-
51
51
  * third hand. 0.7.0 left both alone because reconciling them restyles every
52
52
  * primitive in the library; that is exactly what 0.10.0 did, and both files are
53
53
  * gone. There is one description of what a line looks like here, and it is
54
- * Phosphor's. See `docs/decisions/0.10.md` § 51.
54
+ * Phosphor's. See `docs/decisions/` § 51.
55
55
  *
56
56
  * IN NEXT, IMPORT FROM `@phosphor-icons/react/ssr` inside a Server Component.
57
57
  * Phosphor's default build reads `IconContext` through `useContext`, and a hook
@@ -43,7 +43,7 @@ import { IconWeight, IconProps as IconProps$1, Icon as Icon$1 } from '@phosphor-
43
43
  * THE WEIGHT IS AN AXIS, AND IT IS THE ONLY ONE AN ICON HAS. There are three
44
44
  * roles and `tone` is how you name them; the weights themselves are not a prop,
45
45
  * because Phosphor's other three — `thin`, `bold`, `duotone` — say nothing this
46
- * system means. See `TONE_WEIGHT` below and `docs/decisions/0.7.md` § 35.
46
+ * system means. See `TONE_WEIGHT` below and `docs/decisions/` § 35.
47
47
  *
48
48
  * IT IS NOW THE ONLY LINE THE LIBRARY DRAWS, which is what 0.10.0 changed.
49
49
  * `lib/glyphs.tsx` drew at 1.75 on a 16 grid — 0.109em, three quarters heavier
@@ -51,7 +51,7 @@ import { IconWeight, IconProps as IconProps$1, Icon as Icon$1 } from '@phosphor-
51
51
  * third hand. 0.7.0 left both alone because reconciling them restyles every
52
52
  * primitive in the library; that is exactly what 0.10.0 did, and both files are
53
53
  * gone. There is one description of what a line looks like here, and it is
54
- * Phosphor's. See `docs/decisions/0.10.md` § 51.
54
+ * Phosphor's. See `docs/decisions/` § 51.
55
55
  *
56
56
  * IN NEXT, IMPORT FROM `@phosphor-icons/react/ssr` inside a Server Component.
57
57
  * Phosphor's default build reads `IconContext` through `useContext`, and a hook
@@ -1,3 +1,3 @@
1
- export { ICON_WEIGHT, Icon, TONE_WEIGHT } from '../chunk-ZSCSKCTY.js';
2
- import '../chunk-FAAGZG7A.js';
3
- import '../chunk-FGFNK72B.js';
1
+ export { ICON_WEIGHT, Icon, TONE_WEIGHT } from '../chunk-WU66TPJT.js';
2
+ import '../chunk-TMUGT3Y3.js';
3
+ import '../chunk-5YWGOFDX.js';
package/dist/index.cjs CHANGED
@@ -209,7 +209,7 @@ var typeScale = {
209
209
  * (plankton 5.57:1 over abyss), which is the part that is not negotiable.
210
210
  *
211
211
  * At 13 the three badge families grew past the size of a small button and
212
- * outweighed the title they accompany. See `docs/decisions/`.
212
+ * outweighed the title they accompany. See `docs/decisions/` § 10.
213
213
  */
214
214
  chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
215
215
  /**
@@ -263,7 +263,7 @@ var control = {
263
263
  * baseline; 28 would have been a fifth height that matches nothing.
264
264
  *
265
265
  * It does not replace `icon`. A page's primary action stays at 42; this is for
266
- * a row of a table. See `docs/decisions/0.6.md` § 22.
266
+ * a row of a table. See `docs/decisions/` § 22.
267
267
  */
268
268
  iconSm: 32
269
269
  };
@@ -286,7 +286,7 @@ var size = {
286
286
  * together eat the top of the content area. 56 is what `cursos` had arrived at
287
287
  * on its own, and it is the same reasoning as `control.iconSm`: the one admin
288
288
  * app of the five is denser than the four reading sites. See
289
- * `docs/decisions/0.7.md` § 30.
289
+ * `docs/decisions/` § 30.
290
290
  */
291
291
  navCompact: 56,
292
292
  /**
@@ -359,7 +359,20 @@ var naming = {
359
359
  wordmark: "Eduardo \xC1lvarez",
360
360
  mascot: "Tiburonc\xEDn",
361
361
  /** The domain, for the footer CLI signature: `$ cd ~/eduardoalvarez.dev/2026`. */
362
- domain: "eduardoalvarez.dev"
362
+ domain: "eduardoalvarez.dev",
363
+ /** The library's own name, for a footer that credits it. */
364
+ library: "Arrecife",
365
+ /**
366
+ * Where the library is published, for `Footer builtWith`.
367
+ *
368
+ * It is here and not written into the component for the reason the domain is:
369
+ * one string, five projects. It is the same URL as `homepage` in
370
+ * `package.json` and it cannot be derived from it — this file imports nothing
371
+ * — so `check:exports` compares the two on every build. Without that, moving
372
+ * the Storybook would update what npm prints and leave five production
373
+ * footers linking at the old address.
374
+ */
375
+ libraryUrl: "https://arrecife.eduardoalvarez.dev"
363
376
  };
364
377
 
365
378
  // src/tokens/index.ts
@@ -484,7 +497,7 @@ var button = classVarianceAuthority.cva(
484
497
  ],
485
498
  // The one control whose focus ring is not biolume. It is the system's
486
499
  // only sand fill, and a biolume ring three pixels off a sand button puts
487
- // both of the brand's accents in the same glance. See `docs/decisions/0.7.md` § 37.
500
+ // both of the brand's accents in the same glance. See `docs/decisions/` § 37.
488
501
  conversion: "bg-warm text-warm-on hover:bg-warm-hover focus-ring-warm",
489
502
  secondary: [
490
503
  "border-hairline-hover border bg-transparent text-text-primary",
@@ -2874,25 +2887,62 @@ function CourseCard({
2874
2887
  meta,
2875
2888
  status,
2876
2889
  progress,
2890
+ media,
2891
+ footer,
2877
2892
  className,
2878
2893
  ...props
2879
2894
  }) {
2880
- return /* @__PURE__ */ jsxRuntime.jsx(CardShell, { className: cn("p-step-lg", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
2881
- status ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "warm", children: status }) }) : null,
2882
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", className: "group-hover:text-accent transition-standard", children: title }),
2883
- summary ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-3", children: summary }) : null,
2884
- meta && meta.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "gap-step-xs flex flex-wrap items-center", children: meta.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "gap-step-xs flex items-center", children: [
2885
- i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
2886
- m
2887
- ] }, i)) }) : null,
2888
- typeof progress === "number" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs mt-auto flex flex-col pt-step-sm", children: [
2889
- /* @__PURE__ */ jsxRuntime.jsx(Progress, { value: progress, tone: "warm", label: `Progreso del curso: ${progress}%` }),
2890
- /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "label", tone: "muted", as: "p", children: [
2891
- progress,
2892
- "% cursado"
2893
- ] })
2894
- ] }) : null
2895
- ] }) });
2895
+ const hasProgress = typeof progress === "number";
2896
+ return (
2897
+ // `overflow-hidden` only with a cover: it is what rounds the cover's top
2898
+ // corners to the card's. It clips the content and not the `<a>` itself, so
2899
+ // the focus ring — an outline three pixels outside — still shows.
2900
+ /* @__PURE__ */ jsxRuntime.jsx(
2901
+ CardShell,
2902
+ {
2903
+ className: cn("p-step-lg", media ? "overflow-hidden" : null, className),
2904
+ ...props,
2905
+ children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
2906
+ media ? (
2907
+ // The bleed is the card's own padding, negated. A call site that
2908
+ // changes the padding through `className` has to change this too, and
2909
+ // that is the trade for keeping the padding on the shell like every
2910
+ // other card instead of in a second place only when there is a cover.
2911
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-step-lg -mt-step-lg mb-step-xs", children: media })
2912
+ ) : null,
2913
+ status ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "warm", children: status }) }) : null,
2914
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", className: "group-hover:text-accent transition-standard", children: title }),
2915
+ summary ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-3", children: summary }) : null,
2916
+ meta && meta.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "gap-step-xs flex flex-wrap items-center", children: meta.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "gap-step-xs flex items-center", children: [
2917
+ i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
2918
+ m
2919
+ ] }, i)) }) : null,
2920
+ hasProgress ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs mt-auto flex flex-col pt-step-sm", children: [
2921
+ /* @__PURE__ */ jsxRuntime.jsx(Progress, { value: progress, tone: "warm", label: `Progreso del curso: ${progress}%` }),
2922
+ /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "label", tone: "muted", as: "p", children: [
2923
+ progress,
2924
+ "% cursado"
2925
+ ] })
2926
+ ] }) : null,
2927
+ footer ? (
2928
+ // Only one of the two takes `mt-auto`. Two auto margins in one column
2929
+ // split the free space between them, and the progress bar would float
2930
+ // halfway down the card.
2931
+ /* @__PURE__ */ jsxRuntime.jsx(
2932
+ "div",
2933
+ {
2934
+ className: cn(
2935
+ "gap-step-sm pt-step-sm flex flex-wrap items-center justify-between",
2936
+ hasProgress ? null : "mt-auto"
2937
+ ),
2938
+ children: footer
2939
+ }
2940
+ )
2941
+ ) : null
2942
+ ] })
2943
+ }
2944
+ )
2945
+ );
2896
2946
  }
2897
2947
 
2898
2948
  // src/brand/catalog.ts
@@ -3203,8 +3253,7 @@ function SocialRow({ social }) {
3203
3253
  function Signature({
3204
3254
  year,
3205
3255
  href,
3206
- domain: domainName = naming.domain,
3207
- className
3256
+ domain: domainName = naming.domain
3208
3257
  }) {
3209
3258
  const domain = href ? /* @__PURE__ */ jsxRuntime.jsx(
3210
3259
  "a",
@@ -3214,7 +3263,7 @@ function Signature({
3214
3263
  children: domainName
3215
3264
  }
3216
3265
  ) : domainName;
3217
- return /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "meta", tone: "muted", as: "p", className: cn("shrink-0", className), children: [
3266
+ return /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "meta", tone: "muted", as: "p", children: [
3218
3267
  /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "text-accent", children: [
3219
3268
  "$",
3220
3269
  " "
@@ -3232,6 +3281,46 @@ function Signature({
3232
3281
  )
3233
3282
  ] });
3234
3283
  }
3284
+ function BuiltWith() {
3285
+ return /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "meta", tone: "muted", as: "p", children: [
3286
+ "Creado con",
3287
+ " ",
3288
+ /* @__PURE__ */ jsxRuntime.jsx(
3289
+ "a",
3290
+ {
3291
+ href: naming.libraryUrl,
3292
+ target: "_blank",
3293
+ rel: "noopener noreferrer",
3294
+ className: cn(
3295
+ "text-accent hover:text-text-primary transition-standard cursor-pointer",
3296
+ "underline decoration-1 underline-offset-4",
3297
+ "rounded-chip focus-ring"
3298
+ ),
3299
+ children: naming.library
3300
+ }
3301
+ ),
3302
+ " ",
3303
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { as: react.Heart, tone: "current", className: "text-accent inline align-middle" })
3304
+ ] });
3305
+ }
3306
+ function SignatureBlock({
3307
+ year,
3308
+ href,
3309
+ domain,
3310
+ builtWith,
3311
+ className
3312
+ }) {
3313
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3314
+ "div",
3315
+ {
3316
+ className: cn("gap-step-xs flex shrink-0 flex-col items-center sm:items-end", className),
3317
+ children: [
3318
+ /* @__PURE__ */ jsxRuntime.jsx(Signature, { year, href, domain }),
3319
+ builtWith ? /* @__PURE__ */ jsxRuntime.jsx(BuiltWith, {}) : null
3320
+ ]
3321
+ }
3322
+ );
3323
+ }
3235
3324
  function Shell({
3236
3325
  className,
3237
3326
  contentClassName,
@@ -3254,6 +3343,7 @@ function Footer({
3254
3343
  linkAsChild,
3255
3344
  social,
3256
3345
  brand: brand2,
3346
+ builtWith,
3257
3347
  year = (/* @__PURE__ */ new Date()).getFullYear(),
3258
3348
  signatureHref,
3259
3349
  domain,
@@ -3303,7 +3393,15 @@ function Footer({
3303
3393
  column.title
3304
3394
  )) })
3305
3395
  ] }),
3306
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-hairline mt-step-xl pt-step-lg border-t", children: /* @__PURE__ */ jsxRuntime.jsx(Signature, { year, href: signatureHref, domain, className: "text-center sm:text-right" }) })
3396
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-hairline mt-step-xl pt-step-lg border-t", children: /* @__PURE__ */ jsxRuntime.jsx(
3397
+ SignatureBlock,
3398
+ {
3399
+ year,
3400
+ href: signatureHref,
3401
+ domain,
3402
+ builtWith
3403
+ }
3404
+ ) })
3307
3405
  ] });
3308
3406
  }
3309
3407
  const rows = [
@@ -3314,7 +3412,16 @@ function Footer({
3314
3412
  return /* @__PURE__ */ jsxRuntime.jsxs(Shell, { className, contentClassName: "gap-step-lg", ...rest, children: [
3315
3413
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-md flex flex-col items-center sm:flex-row sm:flex-wrap", children: [
3316
3414
  first ?? null,
3317
- /* @__PURE__ */ jsxRuntime.jsx(Signature, { year, href: signatureHref, domain, className: "sm:ml-auto" })
3415
+ /* @__PURE__ */ jsxRuntime.jsx(
3416
+ SignatureBlock,
3417
+ {
3418
+ year,
3419
+ href: signatureHref,
3420
+ domain,
3421
+ builtWith,
3422
+ className: "sm:ml-auto"
3423
+ }
3424
+ )
3318
3425
  ] }),
3319
3426
  others
3320
3427
  ] });
@@ -3465,7 +3572,7 @@ function Nav({ brand: brand2, children, actions, size: size2 = "default", classN
3465
3572
  puts it in `actions`. A `Nav` that hid its own items would leave every
3466
3573
  consumer without a drawer with no navigation at all, silently.
3467
3574
 
3468
- See `docs/decisions/0.10.md` § 54.
3575
+ See `docs/decisions/` § 54.
3469
3576
  */
3470
3577
  /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Principal", className: "ml-auto min-w-0 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "gap-step-md flex w-max items-center", children: [
3471
3578
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-text-muted font-mono text-meta select-none", children: "~/" }),
@@ -3643,12 +3750,21 @@ function PageHeader({
3643
3750
  action,
3644
3751
  size: size2,
3645
3752
  as = "h1",
3753
+ titleVariant,
3646
3754
  className,
3647
3755
  ...props
3648
3756
  }) {
3649
3757
  return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: cn(header({ size: size2 }), className), ...props, children: [
3650
3758
  eyebrow ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: eyebrow }) : null,
3651
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as, variant: size2 === "display" ? "display" : "h1", className: "max-w-measure", children: title }),
3759
+ /* @__PURE__ */ jsxRuntime.jsx(
3760
+ Text,
3761
+ {
3762
+ as,
3763
+ variant: titleVariant ?? (size2 === "display" ? "display" : "h1"),
3764
+ className: "max-w-measure",
3765
+ children: title
3766
+ }
3767
+ ),
3652
3768
  description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "body", tone: "secondary", children: description }) : null,
3653
3769
  action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-sm mt-step-sm flex flex-wrap items-center", children: action }) : null
3654
3770
  ] });
@@ -3895,18 +4011,24 @@ function Isotype({
3895
4011
  className,
3896
4012
  ...props
3897
4013
  }) {
3898
- const file = background === "dark" ? fins.foam : fins.color;
3899
- return /* @__PURE__ */ jsxRuntime.jsx(
4014
+ const fin = (file, mode) => /* @__PURE__ */ jsxRuntime.jsx(
3900
4015
  "img",
3901
4016
  {
3902
4017
  src: `${basePath}/${file}`,
3903
4018
  alt,
3904
4019
  width: 147,
3905
4020
  height: 111,
3906
- className: cn("h-8 w-auto select-none", className),
4021
+ className: cn("h-8 w-auto select-none", className, mode),
3907
4022
  ...props
3908
4023
  }
3909
4024
  );
4025
+ if (background === "auto") {
4026
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4027
+ fin(fins.foam, "light:hidden"),
4028
+ fin(fins.color, "not-light:hidden")
4029
+ ] });
4030
+ }
4031
+ return fin(background === "dark" ? fins.foam : fins.color);
3910
4032
  }
3911
4033
  function Logo({
3912
4034
  background = "dark",