@eduardoalvarez/arrecife 0.12.1 → 0.12.3

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.12.3](https://github.com/Proskynete/arrecife/compare/v0.12.2...v0.12.3) (2026-09-24)
4
+
5
+
6
+ ### 🐛 Correcciones
7
+
8
+ * **components:** align ten components and the stories with the Design System canvas ([#85](https://github.com/Proskynete/arrecife/issues/85)) ([52f141d](https://github.com/Proskynete/arrecife/commit/52f141dcdc8739f6735f0b5082e7f2a19b54d383))
9
+ * **tokens:** the tagline follows the new positioning, so the header stops saying the old one ([#82](https://github.com/Proskynete/arrecife/issues/82)) ([01abbbf](https://github.com/Proskynete/arrecife/commit/01abbbf5f31fd0690137c5c169432bd8c34b7689))
10
+
11
+
12
+ ### 📚 Documentación
13
+
14
+ * re-sync the design system transcription and record where production diverged, §§ 69–75 ([#84](https://github.com/Proskynete/arrecife/issues/84)) ([64dddcf](https://github.com/Proskynete/arrecife/commit/64dddcf32490a65f0185cafffe3467d4fcf9f55d))
15
+
16
+ ## [0.12.2](https://github.com/Proskynete/arrecife/compare/v0.12.1...v0.12.2) (2026-09-23)
17
+
18
+
19
+ ### 📚 Documentación
20
+
21
+ * §§ 65–66, one positioning for the brand and where the avatar is a face ([#80](https://github.com/Proskynete/arrecife/issues/80)) ([40e76c5](https://github.com/Proskynete/arrecife/commit/40e76c5a2323f1c4447bfdac10204acb5cd593b1))
22
+
3
23
  ## [0.12.1](https://github.com/Proskynete/arrecife/compare/v0.12.0...v0.12.1) (2026-09-17)
4
24
 
5
25
 
package/README.md CHANGED
@@ -355,7 +355,7 @@ import { Text, Button, Badge, cn } from '@eduardoalvarez/arrecife';
355
355
  import type { TextProps } from '@eduardoalvarez/arrecife';
356
356
 
357
357
  <Text variant="eyebrow" tone="muted">charlas</Text>
358
- <Text as="h2" variant="h1">Escalar con criterio</Text>
358
+ <Text as="h2" variant="h1">Entender antes de construir</Text>
359
359
  <Text variant="body">Clamps itself to 68ch.</Text>
360
360
  <Text variant="ui" measure={false}>No clamp, for a narrow cell.</Text>
361
361
  ```
@@ -134,8 +134,8 @@ var spacing = {
134
134
  section: 96
135
135
  };
136
136
  var tagline = {
137
- /** Header, one line. */
138
- short: "Ayudo a equipos a escalar con criterio"};
137
+ /** Header, one line. Role and method, as in § 65. */
138
+ short: "Technical Lead \xB7 Spec-Driven Development"};
139
139
  var naming = {
140
140
  wordmark: "Eduardo \xC1lvarez"};
141
141
 
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- export { Isotype, Logo, Mascot, MascotFace } from '../chunk-BRDTB44R.js';
3
- import '../chunk-TMUGT3Y3.js';
2
+ export { Isotype, Logo, Mascot, MascotFace } from '../chunk-LS3PUZ6L.js';
3
+ import '../chunk-RMA2KF5B.js';
4
4
  export { ASSETS_PATH, faceList, faceUsage, faces, fins, poseList, poses } from '../chunk-CKRSQPTX.js';
5
- import '../chunk-5YWGOFDX.js';
5
+ import '../chunk-QQ5WRFHC.js';
@@ -1,8 +1,8 @@
1
1
  'use client';
2
- import { Text } from '../chunk-BFYNBIVJ.js';
2
+ import { Text } from '../chunk-TJRXYXMG.js';
3
3
  import '../chunk-ODBFN44D.js';
4
- import { cn } from '../chunk-TMUGT3Y3.js';
5
- import { radius } from '../chunk-5YWGOFDX.js';
4
+ import { cn } from '../chunk-RMA2KF5B.js';
5
+ import { radius } from '../chunk-QQ5WRFHC.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';
@@ -1,4 +1,4 @@
1
- import { cn } from './chunk-TMUGT3Y3.js';
1
+ import { cn } from './chunk-RMA2KF5B.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-TMUGT3Y3.js';
1
+ import { cn } from './chunk-RMA2KF5B.js';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
 
4
4
  var ICON_WEIGHT = "regular";
@@ -1,4 +1,4 @@
1
- import { naming, tagline, motion, shadow, series, syntax, gradient, size, spacing, control, radius, limits, typeScale, fonts, brand, colors } from './chunk-5YWGOFDX.js';
1
+ import { naming, tagline, motion, shadow, series, syntax, gradient, size, spacing, control, radius, limits, typeScale, fonts, brand, colors } from './chunk-QQ5WRFHC.js';
2
2
 
3
3
  // src/tokens/index.ts
4
4
  var tokens = {
@@ -1,6 +1,6 @@
1
- import { cn } from './chunk-TMUGT3Y3.js';
1
+ import { cn } from './chunk-RMA2KF5B.js';
2
2
  import { fins, ASSETS_PATH, poses, faces } from './chunk-CKRSQPTX.js';
3
- import { naming, tagline } from './chunk-5YWGOFDX.js';
3
+ import { naming, tagline } from './chunk-QQ5WRFHC.js';
4
4
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
5
 
6
6
  function Isotype({
@@ -261,17 +261,22 @@ var series = {
261
261
  light: [light.accent, light.warm, brand.body, light.textMuted]
262
262
  };
263
263
  var deep = "#0D2129";
264
+ var ogDeep = "#0E2A30";
264
265
  var gradient = {
265
266
  dark: {
266
267
  hero: `linear-gradient(160deg, ${dark.background} 60%, ${deep} 100%)`,
267
268
  section: `linear-gradient(150deg, ${dark.surface} 0%, ${deep} 100%)`,
268
- /** The one used by the article OG template. 145°, from the document. */
269
- og: `linear-gradient(145deg, ${dark.background} 55%, ${deep} 100%)`
269
+ /** The article OG template. 145°, from the document. */
270
+ og: `linear-gradient(145deg, ${dark.background} 58%, ${ogDeep} 100%)`,
271
+ /** The talk OG template. 150°, from the document. */
272
+ ogTalk: `linear-gradient(150deg, ${dark.background} 55%, ${ogDeep} 100%)`
270
273
  },
271
274
  light: {
272
275
  hero: `linear-gradient(160deg, ${light.background} 60%, ${light.surface} 100%)`,
273
276
  section: `linear-gradient(150deg, ${light.surface} 0%, ${light.background} 100%)`,
274
- og: `linear-gradient(145deg, ${light.background} 55%, ${light.surface} 100%)`
277
+ og: `linear-gradient(145deg, ${light.background} 55%, ${light.surface} 100%)`,
278
+ /** No light talk card exists; the key mirrors dark so both modes carry it. */
279
+ ogTalk: `linear-gradient(150deg, ${light.background} 55%, ${light.surface} 100%)`
275
280
  }
276
281
  };
277
282
  var shadow = {
@@ -283,12 +288,12 @@ var motion = {
283
288
  properties: "color, background-color, border-color, fill, stroke"
284
289
  };
285
290
  var tagline = {
286
- /** Hero. */
287
- long: "Ayudo a equipos de ingenier\xEDa a escalar con criterio",
288
- /** Header, one line. */
289
- short: "Ayudo a equipos a escalar con criterio",
290
- /** LinkedIn. */
291
- en: "Helping engineering teams scale with judgment"
291
+ /** Hero. The bajada approved in § 65. */
292
+ long: "Ense\xF1o a construir software con IA sin dejar de entender lo que hacemos.",
293
+ /** Header, one line. Role and method, as in § 65. */
294
+ short: "Technical Lead \xB7 Spec-Driven Development",
295
+ /** English surfaces. */
296
+ en: "I teach how to build software with AI without losing sight of what we build."
292
297
  };
293
298
  var naming = {
294
299
  wordmark: "Eduardo \xC1lvarez",
@@ -1,4 +1,4 @@
1
- import { spacing, control, radius, typeScale } from './chunk-5YWGOFDX.js';
1
+ import { spacing, control, radius, typeScale } from './chunk-QQ5WRFHC.js';
2
2
  import { clsx } from 'clsx';
3
3
  import { extendTailwindMerge } from 'tailwind-merge';
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { text } from './chunk-ODBFN44D.js';
2
- import { cn } from './chunk-TMUGT3Y3.js';
2
+ import { cn } from './chunk-RMA2KF5B.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,9 +1,9 @@
1
1
  'use client';
2
- import { Label } from '../chunk-W75O3Z77.js';
3
- import { Text } from '../chunk-BFYNBIVJ.js';
2
+ import { Label } from '../chunk-3SPOZC5F.js';
3
+ import { Text } from '../chunk-TJRXYXMG.js';
4
4
  import '../chunk-ODBFN44D.js';
5
- import { cn } from '../chunk-TMUGT3Y3.js';
6
- import '../chunk-5YWGOFDX.js';
5
+ import { cn } from '../chunk-RMA2KF5B.js';
6
+ import '../chunk-QQ5WRFHC.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';
@@ -1,3 +1,3 @@
1
- export { ICON_WEIGHT, Icon, TONE_WEIGHT } from '../chunk-WU66TPJT.js';
2
- import '../chunk-TMUGT3Y3.js';
3
- import '../chunk-5YWGOFDX.js';
1
+ export { ICON_WEIGHT, Icon, TONE_WEIGHT } from '../chunk-DKXTF6N7.js';
2
+ import '../chunk-RMA2KF5B.js';
3
+ import '../chunk-QQ5WRFHC.js';
package/dist/index.cjs CHANGED
@@ -326,17 +326,22 @@ var series = {
326
326
  light: [light.accent, light.warm, brand.body, light.textMuted]
327
327
  };
328
328
  var deep = "#0D2129";
329
+ var ogDeep = "#0E2A30";
329
330
  var gradient = {
330
331
  dark: {
331
332
  hero: `linear-gradient(160deg, ${dark.background} 60%, ${deep} 100%)`,
332
333
  section: `linear-gradient(150deg, ${dark.surface} 0%, ${deep} 100%)`,
333
- /** The one used by the article OG template. 145°, from the document. */
334
- og: `linear-gradient(145deg, ${dark.background} 55%, ${deep} 100%)`
334
+ /** The article OG template. 145°, from the document. */
335
+ og: `linear-gradient(145deg, ${dark.background} 58%, ${ogDeep} 100%)`,
336
+ /** The talk OG template. 150°, from the document. */
337
+ ogTalk: `linear-gradient(150deg, ${dark.background} 55%, ${ogDeep} 100%)`
335
338
  },
336
339
  light: {
337
340
  hero: `linear-gradient(160deg, ${light.background} 60%, ${light.surface} 100%)`,
338
341
  section: `linear-gradient(150deg, ${light.surface} 0%, ${light.background} 100%)`,
339
- og: `linear-gradient(145deg, ${light.background} 55%, ${light.surface} 100%)`
342
+ og: `linear-gradient(145deg, ${light.background} 55%, ${light.surface} 100%)`,
343
+ /** No light talk card exists; the key mirrors dark so both modes carry it. */
344
+ ogTalk: `linear-gradient(150deg, ${light.background} 55%, ${light.surface} 100%)`
340
345
  }
341
346
  };
342
347
  var shadow = {
@@ -348,12 +353,12 @@ var motion = {
348
353
  properties: "color, background-color, border-color, fill, stroke"
349
354
  };
350
355
  var tagline = {
351
- /** Hero. */
352
- long: "Ayudo a equipos de ingenier\xEDa a escalar con criterio",
353
- /** Header, one line. */
354
- short: "Ayudo a equipos a escalar con criterio",
355
- /** LinkedIn. */
356
- en: "Helping engineering teams scale with judgment"
356
+ /** Hero. The bajada approved in § 65. */
357
+ long: "Ense\xF1o a construir software con IA sin dejar de entender lo que hacemos.",
358
+ /** Header, one line. Role and method, as in § 65. */
359
+ short: "Technical Lead \xB7 Spec-Driven Development",
360
+ /** English surfaces. */
361
+ en: "I teach how to build software with AI without losing sight of what we build."
357
362
  };
358
363
  var naming = {
359
364
  wordmark: "Eduardo \xC1lvarez",
@@ -1225,7 +1230,7 @@ function DateField({
1225
1230
  "hover:border-hairline-hover",
1226
1231
  "focus-visible:border-accent focus-ring",
1227
1232
  "disabled:pointer-events-none disabled:opacity-50",
1228
- "data-[invalid]:border-error data-[invalid]:focus-visible:outline-error",
1233
+ "aria-[invalid=true]:border-error aria-[invalid=true]:focus-visible:outline-error",
1229
1234
  // The native indicator inherits the active theme's scheme.
1230
1235
  "scheme-light dark:scheme-dark [&::-webkit-calendar-picker-indicator]:cursor-pointer",
1231
1236
  className
@@ -1428,7 +1433,7 @@ function Input({ className, invalid = false, ...props }) {
1428
1433
  "hover:border-hairline-hover",
1429
1434
  "focus-visible:border-accent focus-ring",
1430
1435
  "disabled:pointer-events-none disabled:opacity-50",
1431
- "data-[invalid]:border-error data-[invalid]:focus-visible:outline-error",
1436
+ "aria-[invalid=true]:border-error aria-[invalid=true]:focus-visible:outline-error",
1432
1437
  "file:mr-step-sm file:border-0 file:bg-transparent file:font-sans file:text-label file:text-text-secondary",
1433
1438
  className
1434
1439
  ),
@@ -1478,12 +1483,11 @@ function PaginationLink({ className, isActive = false, ...props }) {
1478
1483
  {
1479
1484
  "aria-current": isActive ? "page" : void 0,
1480
1485
  className: cn(
1481
- "rounded-chip px-step-sm inline-flex h-9 min-w-9 cursor-pointer items-center justify-center",
1482
- "font-sans text-ui text-text-secondary",
1486
+ "rounded-chip px-step-sm inline-flex h-9 min-w-9 cursor-pointer items-center justify-center border",
1487
+ "font-mono text-meta",
1483
1488
  "transition-standard",
1484
- "hover:bg-surface hover:text-text-primary",
1485
1489
  "focus-ring",
1486
- isActive && "bg-surface-raised text-text-primary",
1490
+ isActive ? "border-accent bg-accent text-accent-on" : "border-border text-text-secondary hover:border-hairline-hover hover:text-text-primary",
1487
1491
  className
1488
1492
  ),
1489
1493
  ...props
@@ -1595,6 +1599,8 @@ function SelectTrigger({
1595
1599
  "transition-standard",
1596
1600
  "hover:border-hairline-hover",
1597
1601
  "focus-visible:border-accent focus-ring",
1602
+ // `FormControl` marks an invalid field with `aria-invalid` only.
1603
+ "aria-[invalid=true]:border-error aria-[invalid=true]:focus-visible:outline-error",
1598
1604
  "disabled:pointer-events-none disabled:opacity-50",
1599
1605
  "data-[placeholder]:text-text-muted",
1600
1606
  className
@@ -1978,7 +1984,7 @@ function Textarea({ className, invalid = false, ...props }) {
1978
1984
  "hover:border-hairline-hover",
1979
1985
  "focus-visible:border-accent focus-ring",
1980
1986
  "disabled:pointer-events-none disabled:opacity-50",
1981
- "data-[invalid]:border-error data-[invalid]:focus-visible:outline-error",
1987
+ "aria-[invalid=true]:border-error aria-[invalid=true]:focus-visible:outline-error",
1982
1988
  className
1983
1989
  ),
1984
1990
  ...props
@@ -2152,9 +2158,9 @@ function TooltipContent({
2152
2158
  {
2153
2159
  sideOffset,
2154
2160
  className: cn(
2155
- "z-50 px-step-sm py-1.5",
2156
- "rounded-chip border-border bg-surface-raised shadow-standard border",
2157
- "font-sans text-label text-text-primary",
2161
+ "z-50 px-step-sm py-step-xs",
2162
+ "rounded-chip bg-text-primary shadow-standard",
2163
+ "font-sans text-label text-background",
2158
2164
  className
2159
2165
  ),
2160
2166
  ...props
@@ -2258,7 +2264,9 @@ function ProgressBar({
2258
2264
  height,
2259
2265
  knob,
2260
2266
  progress,
2261
- redondeada = true,
2267
+ valueText,
2268
+ onKeyDown,
2269
+ rounded = true,
2262
2270
  tabIndex = 0,
2263
2271
  ...pointer
2264
2272
  }) {
@@ -2267,28 +2275,31 @@ function ProgressBar({
2267
2275
  {
2268
2276
  className: cn(
2269
2277
  "group bg-surface-raised relative cursor-pointer",
2278
+ "focus-ring",
2270
2279
  height,
2271
- redondeada && "rounded-pill"
2280
+ rounded && "rounded-pill"
2272
2281
  ),
2273
2282
  role: "slider",
2274
2283
  "aria-label": "Progreso del audio",
2275
2284
  "aria-valuemin": 0,
2276
2285
  "aria-valuemax": 100,
2277
2286
  "aria-valuenow": Math.round(progress),
2287
+ "aria-valuetext": valueText,
2278
2288
  tabIndex,
2289
+ onKeyDown,
2279
2290
  ...pointer,
2280
2291
  children: [
2281
2292
  /* @__PURE__ */ jsxRuntime.jsx(
2282
2293
  "div",
2283
2294
  {
2284
- className: cn("bg-accent absolute h-full", redondeada && "rounded-pill"),
2295
+ className: cn("bg-accent absolute h-full", rounded && "rounded-pill"),
2285
2296
  style: { width: `${progress}%` }
2286
2297
  }
2287
2298
  ),
2288
2299
  /* @__PURE__ */ jsxRuntime.jsx(
2289
2300
  "div",
2290
2301
  {
2291
- className: "rounded-pill bg-text-primary absolute top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100",
2302
+ className: "rounded-pill bg-text-primary absolute top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 group-focus-within:opacity-100",
2292
2303
  style: {
2293
2304
  width: knob,
2294
2305
  height: knob,
@@ -2374,6 +2385,7 @@ function Volume({
2374
2385
  className: cn(
2375
2386
  "bg-surface-raised rounded-pill h-1 cursor-pointer appearance-none",
2376
2387
  "[accent-color:var(--color-accent)]",
2388
+ "focus-ring",
2377
2389
  width
2378
2390
  ),
2379
2391
  "aria-label": "Volumen"
@@ -2385,7 +2397,7 @@ function PlayIcon({ className, loading, error, playing }) {
2385
2397
  if (loading)
2386
2398
  return /* @__PURE__ */ jsxRuntime.jsx(Icon, { as: react.CircleNotch, className: cn("motion-safe:animate-spin", className) });
2387
2399
  if (error) return /* @__PURE__ */ jsxRuntime.jsx(Icon, { as: react.ArrowsClockwise, className });
2388
- if (playing) return /* @__PURE__ */ jsxRuntime.jsx(Icon, { as: react.Pause, className });
2400
+ if (playing) return /* @__PURE__ */ jsxRuntime.jsx(Icon, { as: react.Pause, tone: "current", className });
2389
2401
  return /* @__PURE__ */ jsxRuntime.jsx(Icon, { as: react.Play, className });
2390
2402
  }
2391
2403
  function PlayButton({
@@ -2404,7 +2416,8 @@ function PlayButton({
2404
2416
  className: cn(
2405
2417
  "text-accent-on transition-standard cursor-pointer",
2406
2418
  "focus-ring",
2407
- state.error ? "bg-error hover:bg-error/80" : "bg-accent hover:bg-accent-hover",
2419
+ // `danger` is the fill and `error` is text: the retry button is a fill.
2420
+ state.error ? "bg-danger text-danger-on hover:bg-danger-hover" : "bg-accent hover:bg-accent-hover",
2408
2421
  className
2409
2422
  ),
2410
2423
  "aria-label": label,
@@ -2536,6 +2549,29 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2536
2549
  e.currentTarget.releasePointerCapture(e.pointerId);
2537
2550
  isDragging.current = false;
2538
2551
  }, []);
2552
+ const handleProgressKey = react$1.useCallback(
2553
+ (e) => {
2554
+ const audio = audioRef.current;
2555
+ if (!audio || !duration) return;
2556
+ const steps = {
2557
+ ArrowLeft: -5,
2558
+ ArrowDown: -5,
2559
+ ArrowRight: 5,
2560
+ ArrowUp: 5,
2561
+ PageDown: -15,
2562
+ PageUp: 15
2563
+ };
2564
+ let next;
2565
+ if (e.key === "Home") next = 0;
2566
+ else if (e.key === "End") next = duration;
2567
+ else if (e.key in steps) next = audio.currentTime + (steps[e.key] ?? 0);
2568
+ if (next === void 0) return;
2569
+ e.preventDefault();
2570
+ audio.currentTime = Math.max(0, Math.min(next, duration));
2571
+ setCurrentTime(audio.currentTime);
2572
+ },
2573
+ [duration]
2574
+ );
2539
2575
  const handleVolumeChange = react$1.useCallback((e) => {
2540
2576
  const audio = audioRef.current;
2541
2577
  if (!audio) return;
@@ -2576,7 +2612,9 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2576
2612
  const pointer = {
2577
2613
  onPointerDown: handlePointerDown,
2578
2614
  onPointerMove: handlePointerMove,
2579
- onPointerUp: handlePointerUp
2615
+ onPointerUp: handlePointerUp,
2616
+ onKeyDown: handleProgressKey,
2617
+ valueText: `${formatTime(currentTime)} de ${formatTime(duration)}`
2580
2618
  };
2581
2619
  const floatingJsx = /* @__PURE__ */ jsxRuntime.jsxs(
2582
2620
  "div",
@@ -2594,7 +2632,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2594
2632
  height: "h-1",
2595
2633
  knob: 10,
2596
2634
  progress,
2597
- redondeada: false,
2635
+ rounded: false,
2598
2636
  tabIndex: -1,
2599
2637
  ...pointer
2600
2638
  }
@@ -2780,10 +2818,10 @@ function Blockquote({ children, author, source, className, ...props }) {
2780
2818
  return /* @__PURE__ */ jsxRuntime.jsxs(
2781
2819
  "blockquote",
2782
2820
  {
2783
- className: cn("border-accent pl-step-md my-step-lg border-l-2", className),
2821
+ className: cn("border-warm pl-step-md my-step-lg border-l-3", className),
2784
2822
  ...props,
2785
2823
  children: [
2786
- /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "body", tone: "secondary", children }),
2824
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "body", tone: "secondary", className: "italic", children }),
2787
2825
  author || source ? /* @__PURE__ */ jsxRuntime.jsx("footer", { className: "mt-step-sm", children: /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "label", tone: "muted", as: "span", children: [
2788
2826
  author ? /* @__PURE__ */ jsxRuntime.jsx("cite", { className: "not-italic", children: author }) : null,
2789
2827
  author && source ? " \xB7 " : null,
@@ -2804,7 +2842,13 @@ function Breadcrumb({
2804
2842
  }) {
2805
2843
  const link = (href, content) => linkAsChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: LINK, children: linkAsChild({ href, children: content }) }) : /* @__PURE__ */ jsxRuntime.jsx("a", { href, className: LINK, children: content });
2806
2844
  return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Ruta", className: cn("font-mono text-meta text-text-muted", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("ol", { className: "gap-step-xs flex flex-wrap items-center", children: [
2807
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: link(homeHref, /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": homeLabel, children: "~" })) }),
2845
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: link(
2846
+ homeHref,
2847
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2848
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "~" }),
2849
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: homeLabel })
2850
+ ] })
2851
+ ) }),
2808
2852
  items.map((item2, i) => {
2809
2853
  const last = i === items.length - 1;
2810
2854
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "gap-step-xs flex items-center", children: [
@@ -3163,7 +3207,7 @@ function EventCalendar({
3163
3207
  size: "sm",
3164
3208
  onClick: () => edit(event),
3165
3209
  "aria-label": `Editar \xAB${event.title}\xBB`,
3166
- children: "./edit"
3210
+ children: "./editar"
3167
3211
  }
3168
3212
  ) : null,
3169
3213
  onDeleteEvent ? /* @__PURE__ */ jsxRuntime.jsxs(AlertDialog, { children: [
@@ -3185,7 +3229,7 @@ function EventCalendar({
3185
3229
  ] }),
3186
3230
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { children: [
3187
3231
  /* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { children: "Mejor no" }),
3188
- /* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { onClick: () => onDeleteEvent(event.id), children: "Borrar el event" })
3232
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { onClick: () => onDeleteEvent(event.id), children: "Borrar el evento" })
3189
3233
  ] })
3190
3234
  ] })
3191
3235
  ] }) : null
@@ -3611,13 +3655,13 @@ function NewsletterForm({
3611
3655
  description,
3612
3656
  state = "idle",
3613
3657
  onSubmitEmail,
3614
- successMessage = "Ya est\xE1s dentro. Te llega un correo cada dos semanas, y nada m\xE1s.",
3658
+ successMessage = "Ya est\xE1s dentro. Te llega un correo al mes, y nada m\xE1s.",
3615
3659
  errorMessage = "No se pudo suscribir ese correo. Rev\xEDsalo y vuelve a intentar.",
3616
3660
  disclaimer,
3617
3661
  expression,
3618
3662
  basePath,
3619
3663
  submitLabel = "Suscribirme",
3620
- placeholder = "tu@email.dev",
3664
+ placeholder = "tu@correo.dev",
3621
3665
  fieldLabel = "Correo electr\xF3nico",
3622
3666
  nameField = false,
3623
3667
  nameLabel = "Nombre",
@@ -3652,7 +3696,7 @@ function NewsletterForm({
3652
3696
  type: "submit",
3653
3697
  variant: "conversion",
3654
3698
  loading: sending,
3655
- className: cn("disabled:opacity-60", nameField && "sm:self-start"),
3699
+ className: cn("w-full disabled:opacity-60 sm:w-auto", nameField && "sm:self-start"),
3656
3700
  children: submitLabel
3657
3701
  }
3658
3702
  );
@@ -3723,7 +3767,7 @@ function NewsletterForm({
3723
3767
  state === "success" ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-notice`, variant: "success", emphasis: "strong", children: successMessage }) : null,
3724
3768
  error ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-notice`, variant: "error", emphasis: "strong", children: errorMessage }) : null,
3725
3769
  disclaimer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-center", children: [
3726
- expression ? /* @__PURE__ */ jsxRuntime.jsx(MascotFace, { expression, basePath, className: "w-10 max-w-none" }) : null,
3770
+ expression && !aside ? /* @__PURE__ */ jsxRuntime.jsx(MascotFace, { expression, basePath, className: "w-10 max-w-none" }) : null,
3727
3771
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "font-normal", children: disclaimer })
3728
3772
  ] }) : null
3729
3773
  ] }),
@@ -3833,6 +3877,7 @@ function Stat({
3833
3877
  label,
3834
3878
  tone = "neutral",
3835
3879
  progress,
3880
+ progressLabel,
3836
3881
  icon,
3837
3882
  description,
3838
3883
  delta,
@@ -3840,6 +3885,7 @@ function Stat({
3840
3885
  className,
3841
3886
  ...props
3842
3887
  }) {
3888
+ const barName = progressLabel ?? (typeof label === "string" ? label : "Progreso");
3843
3889
  const DeltaGlyph = delta ? DELTA[delta.direction].glyph : null;
3844
3890
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(CARD_SURFACE, "p-step-lg gap-step-xs flex flex-col", className), ...props, children: [
3845
3891
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-start justify-between", children: [
@@ -3876,7 +3922,7 @@ function Stat({
3876
3922
  {
3877
3923
  value: progress,
3878
3924
  tone: tone === "neutral" ? "accent" : "warm",
3879
- label: `${label}: ${progress}%`,
3925
+ label: `${barName}: ${progress}%`,
3880
3926
  className: "mt-step-sm"
3881
3927
  }
3882
3928
  ) : null,
package/dist/index.d.cts CHANGED
@@ -350,6 +350,11 @@ declare function PaginationItem(props: ComponentPropsWithoutRef<'li'>): react.JS
350
350
  type PaginationLinkProps = ComponentPropsWithoutRef<'a'> & {
351
351
  isActive?: boolean;
352
352
  };
353
+ /**
354
+ * Mono, bordered, and the current page in solid biolume, as the document draws
355
+ * it: «paginación mono, página actual en bioluz sólido». The page is where you
356
+ * are, so it is the one control that carries the accent fill.
357
+ */
353
358
  declare function PaginationLink({ className, isActive, ...props }: PaginationLinkProps): react.JSX.Element;
354
359
  declare function PaginationPrevious({ className, ...props }: PaginationLinkProps): react.JSX.Element;
355
360
  declare function PaginationNext({ className, ...props }: PaginationLinkProps): react.JSX.Element;
@@ -651,6 +656,13 @@ declare function Toaster({ duration, label }: ToasterProps): react.JSX.Element;
651
656
  declare const TooltipProvider: react.FC<TooltipPrimitive.TooltipProviderProps>;
652
657
  declare const Tooltip: react.FC<TooltipPrimitive.TooltipProps>;
653
658
  declare const TooltipTrigger: react.ForwardRefExoticComponent<TooltipPrimitive.TooltipTriggerProps & react.RefAttributes<HTMLButtonElement>>;
659
+ /**
660
+ * Inverted on purpose: the document draws it «espuma sobre oscuro, texto casco
661
+ * 13px/500 — se invierte a propósito para que destaque». `bg-text-primary` with
662
+ * `text-background` is that inversion in both modes: foam on the abyss page in
663
+ * dark, ink on paper in light. It is the one layer that does not sit on a
664
+ * surface token, which is exactly what makes it read as a layer.
665
+ */
654
666
  declare function TooltipContent({ className, sideOffset, ...props }: ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>): react.JSX.Element;
655
667
 
656
668
  /** Allowed tags. The list is short on purpose: this is not a styled `div`. */
@@ -812,15 +824,16 @@ type BlockquoteProps = Omit<ComponentPropsWithoutRef<'blockquote'>, 'cite'> & {
812
824
  source?: ReactNode;
813
825
  };
814
826
  /**
815
- * The side bar is `accent`, the interactive color, because a quotation is
816
- * somebody else's voice entering the text. It carries no decorative quote marks:
817
- * the system's glyphs are SVG, and an ornamental quote adds nothing the border
818
- * and the indent do not already say.
827
+ * The side bar is `warm`, 3px, and the text is italic, as the document draws it:
828
+ * «cita con borde arena 3px, cursiva, sin fondo». Sand is the human accent —
829
+ * quotes, categories, conversion — and biolume is the interactive one; a quote
830
+ * in biolume read as a link that could not be clicked. It carries no decorative
831
+ * quote marks: the border and the italic already say it is somebody's voice.
819
832
  */
820
833
  declare function Blockquote({ children, author, source, className, ...props }: BlockquoteProps): react.JSX.Element;
821
834
 
822
835
  /**
823
- * The path as a path: `~ / artículos / cómo-escalar-un-equipo`.
836
+ * The path as a path: `~ / artículos / el-camino-hacia-mi-primera-charla`.
824
837
  *
825
838
  * The `~` is neither decoration nor a house icon: it is the filesystem's home,
826
839
  * which is why the breadcrumb is mono and not sans. The separators use `border`
@@ -1375,6 +1388,11 @@ type NewsletterFormProps = Omit<ComponentPropsWithoutRef<'section'>, 'title' | '
1375
1388
  errorMessage?: ReactNode;
1376
1389
  /** The small print. It is the «sin spam», which is why it accepts a face. */
1377
1390
  disclaimer?: ReactNode;
1391
+ /**
1392
+ * The face beside the disclaimer. It is dropped when `aside` is set: the
1393
+ * manual allows one mascot per piece («Dos mascotas en la misma pieza.
1394
+ * Compiten entre sí y ninguna gana»), and the aside is the illustration.
1395
+ */
1378
1396
  expression?: Face | undefined;
1379
1397
  basePath?: string | undefined;
1380
1398
  submitLabel?: string;
@@ -1583,6 +1601,12 @@ type StatProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & {
1583
1601
  tone?: 'neutral' | 'alert' | 'achievement';
1584
1602
  /** With `progress`, the metric reads as progress and adds the bar. */
1585
1603
  progress?: number | undefined;
1604
+ /**
1605
+ * The bar's accessible name. It defaults to `label` when `label` is a string;
1606
+ * a `label` that is markup has to spell it here, or the bar would announce
1607
+ * «[object Object]: 38%».
1608
+ */
1609
+ progressLabel?: string | undefined;
1586
1610
  /**
1587
1611
  * Glyph in a tinted circle, in the corner opposite the title. At 1em, and it
1588
1612
  * inherits `currentColor` from the badge, so it takes the tone without being
@@ -1618,7 +1642,7 @@ type StatDelta = {
1618
1642
  */
1619
1643
  direction: 'up' | 'down' | 'flat';
1620
1644
  };
1621
- declare function Stat({ value, label, tone, progress, icon, description, delta, spark, className, ...props }: StatProps): react.JSX.Element;
1645
+ declare function Stat({ value, label, tone, progress, progressLabel, icon, description, delta, spark, className, ...props }: StatProps): react.JSX.Element;
1622
1646
 
1623
1647
  type TalkContent = {
1624
1648
  title: ReactNode;