@eduardoalvarez/arrecife 0.5.0 → 0.6.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 (66) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +706 -470
  3. package/dist/brand/index.cjs +112 -95
  4. package/dist/brand/index.d.cts +40 -39
  5. package/dist/brand/index.d.ts +40 -39
  6. package/dist/brand/index.js +5 -4
  7. package/dist/catalog-D13txprv.d.cts +78 -0
  8. package/dist/catalog-D13txprv.d.ts +78 -0
  9. package/dist/chart/index.cjs +100 -83
  10. package/dist/chart/index.d.cts +66 -66
  11. package/dist/chart/index.d.ts +66 -66
  12. package/dist/chart/index.js +14 -12
  13. package/dist/chunk-25YNFCIF.js +141 -0
  14. package/dist/{chunk-YZ2SDOVZ.js → chunk-6O3KWB6P.js} +30 -30
  15. package/dist/chunk-CKRSQPTX.js +36 -0
  16. package/dist/{chunk-ZEOQKRQ7.js → chunk-DKCN7BAL.js} +1 -1
  17. package/dist/chunk-GCRII2KQ.js +86 -0
  18. package/dist/chunk-JMOOFZ3B.js +42 -0
  19. package/dist/chunk-O4TAH7YJ.js +276 -0
  20. package/dist/chunk-ODBFN44D.js +45 -0
  21. package/dist/{chunk-VPT32GPG.js → chunk-PMN7NR3G.js} +2 -2
  22. package/dist/chunk-XKYHTOUJ.js +27 -0
  23. package/dist/form/index.cjs +109 -92
  24. package/dist/form/index.d.cts +43 -42
  25. package/dist/form/index.d.ts +43 -42
  26. package/dist/form/index.js +25 -23
  27. package/dist/index.cjs +1068 -929
  28. package/dist/index.d.cts +770 -773
  29. package/dist/index.d.ts +770 -773
  30. package/dist/index.js +629 -675
  31. package/dist/{label-DuTvJGxD.d.ts → label-MgHFKnFy.d.cts} +3 -3
  32. package/dist/{label-DuTvJGxD.d.cts → label-MgHFKnFy.d.ts} +3 -3
  33. package/dist/og/index.cjs +130 -130
  34. package/dist/og/index.d.cts +93 -89
  35. package/dist/og/index.d.ts +93 -89
  36. package/dist/og/index.js +106 -106
  37. package/dist/shiki/index.cjs +28 -30
  38. package/dist/shiki/index.d.cts +4 -4
  39. package/dist/shiki/index.d.ts +4 -4
  40. package/dist/shiki/index.js +12 -12
  41. package/dist/theme/index.cjs +97 -0
  42. package/dist/theme/index.d.cts +144 -0
  43. package/dist/theme/index.d.ts +144 -0
  44. package/dist/theme/index.js +2 -0
  45. package/dist/tokens/index.cjs +133 -86
  46. package/dist/tokens/index.d.cts +246 -161
  47. package/dist/tokens/index.d.ts +246 -161
  48. package/dist/tokens/index.js +2 -2
  49. package/dist/tokens/theme.css +133 -98
  50. package/dist/variants/index.cjs +192 -0
  51. package/dist/variants/index.d.cts +192 -0
  52. package/dist/variants/index.d.ts +192 -0
  53. package/dist/variants/index.js +3 -0
  54. package/llms.txt +810 -744
  55. package/package.json +20 -11
  56. package/dist/catalogo-Du5ID-Hi.d.cts +0 -77
  57. package/dist/catalogo-Du5ID-Hi.d.ts +0 -77
  58. package/dist/chunk-E3OMP2DL.js +0 -36
  59. package/dist/chunk-KPZNNMV5.js +0 -83
  60. package/dist/chunk-NHS7ETKJ.js +0 -27
  61. package/dist/chunk-TSPJOM6K.js +0 -229
  62. package/dist/chunk-UOWIDFCB.js +0 -81
  63. package/dist/tema/index.cjs +0 -94
  64. package/dist/tema/index.d.cts +0 -110
  65. package/dist/tema/index.d.ts +0 -110
  66. package/dist/tema/index.js +0 -2
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@eduardoalvarez/arrecife",
3
- "version": "0.5.0",
4
- "description": "Librería de componentes de la identidad visual de Eduardo Álvarez",
3
+ "version": "0.6.0",
4
+ "description": "The component library of Eduardo Álvarez’s visual identity",
5
5
  "license": "MIT",
6
6
  "author": "Eduardo Esteban Álvarez Castañeda <soy@eduardoalvarez.dev>",
7
+ "homepage": "https://arrecife.eduardoalvarez.dev",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "git+https://github.com/Proskynete/arrecife.git"
10
11
  },
12
+ "bugs": {
13
+ "url": "https://github.com/Proskynete/arrecife/issues"
14
+ },
11
15
  "type": "module",
12
16
  "sideEffects": [
13
17
  "**/*.css"
@@ -30,10 +34,15 @@
30
34
  "require": "./dist/tokens/index.cjs"
31
35
  },
32
36
  "./tokens/theme.css": "./dist/tokens/theme.css",
33
- "./tema": {
34
- "types": "./dist/tema/index.d.ts",
35
- "import": "./dist/tema/index.js",
36
- "require": "./dist/tema/index.cjs"
37
+ "./variants": {
38
+ "types": "./dist/variants/index.d.ts",
39
+ "import": "./dist/variants/index.js",
40
+ "require": "./dist/variants/index.cjs"
41
+ },
42
+ "./theme": {
43
+ "types": "./dist/theme/index.d.ts",
44
+ "import": "./dist/theme/index.js",
45
+ "require": "./dist/theme/index.cjs"
37
46
  },
38
47
  "./brand": {
39
48
  "types": "./dist/brand/index.d.ts",
@@ -68,7 +77,7 @@
68
77
  "module": "./dist/index.js",
69
78
  "types": "./dist/index.d.ts",
70
79
  "scripts": {
71
- "build": "pnpm check:tokens && pnpm check:namespace && tsup && pnpm build:tokens && pnpm build:llms",
80
+ "build": "pnpm check:tokens && pnpm check:namespace && tsup && node scripts/add-use-client.mjs && pnpm build:tokens && pnpm build:llms",
72
81
  "build:tokens": "node scripts/build-tokens.mjs",
73
82
  "build:llms": "node scripts/build-llms.mjs",
74
83
  "check:tokens": "node scripts/check-tokens-purity.mjs",
@@ -78,14 +87,14 @@
78
87
  "check:release": "node scripts/check-release-config.mjs",
79
88
  "typecheck": "tsc --noEmit",
80
89
  "lint": "eslint .",
81
- "test": "pnpm test:unidad && pnpm test:oscuro && pnpm test:claro",
90
+ "test": "pnpm test:unit && pnpm test:dark && pnpm test:light",
82
91
  "test:watch": "vitest --project storybook",
83
92
  "storybook": "pnpm build:tokens && storybook dev -p 6006",
84
93
  "build-storybook": "pnpm build:tokens && storybook build",
85
94
  "prepublishOnly": "pnpm build",
86
- "test:unidad": "vitest run --project unidad",
87
- "test:oscuro": "vitest run --project storybook",
88
- "test:claro": "STORYBOOK_THEME=claro vitest run --project storybook"
95
+ "test:unit": "vitest run --project unit",
96
+ "test:dark": "vitest run --project storybook",
97
+ "test:light": "STORYBOOK_THEME=light vitest run --project storybook"
89
98
  },
90
99
  "peerDependencies": {
91
100
  "react": "^19.0.0",
@@ -1,77 +0,0 @@
1
- /**
2
- * El catálogo de la marca: qué piezas de Tiburoncín existen y cómo se llama su
3
- * archivo. Datos puros, sin React, para que también lo puedan leer las
4
- * plantillas de OG y cualquier script.
5
- *
6
- * Añadir una pieza nueva es soltar el PNG en `assets/brand/` y añadir una línea
7
- * aquí. El tipo se actualiza solo y el autocompletado la ofrece de inmediato.
8
- */
9
- /**
10
- * Las caras. Solo se usan en estados vacíos, confirmaciones, errores, progreso
11
- * de curso y celebración — nunca en hero, precios, servicios, contacto ni CV.
12
- * Por eso `EmptyState` recibe una cara y `PageHeader` no.
13
- */
14
- declare const caras: {
15
- readonly annoyed: "face-annoyed.png";
16
- readonly confused: "face-confused.png";
17
- readonly hearts: "face-hearts.png";
18
- readonly laughing: "face-laughing.png";
19
- readonly shades: "face-shades.png";
20
- readonly waiting: "face-waiting.png";
21
- readonly wink: "face-wink.png";
22
- };
23
- /** Poses de cuerpo entero. */
24
- declare const poses: {
25
- readonly desk: "pose-desk.png";
26
- readonly 'laptop-coffee': "pose-laptop-coffee.png";
27
- readonly peek: "pose-peek.png";
28
- readonly surf: "pose-surf.png";
29
- };
30
- /**
31
- * La aleta, en sus dos variantes.
32
- *
33
- * El cuerpo de la aleta es casi negro, así que sobre fondo oscuro la variante de
34
- * dos azules desaparece. `espuma` es la silueta a una tinta —el 94 % de sus
35
- * píxeles son `#EDF4F3`, o sea el token espuma— y es la que se ve sobre abismo.
36
- */
37
- declare const aletas: {
38
- /** Dos azules. Para fondos claros. */
39
- readonly color: "fin.png";
40
- /** Silueta a una tinta. Para fondos oscuros. */
41
- readonly espuma: "fin-foam.png";
42
- };
43
- /**
44
- * El uso asignado de cada cara, del inventario del manual.
45
- *
46
- * No es documentación suelta: es la parte del contrato de humor que se puede
47
- * escribir como dato. El manual asigna una situación a cada cara, y sin esto la
48
- * elección se hacía a ojo en cada sitio de uso — que es como `annoyed` termina
49
- * en una confirmación y `hearts` en un error.
50
- *
51
- * Falta una: el manual lista ocho caras y `head-5` (sorpresa · confirmación
52
- * destructiva) no tiene PNG en `assets/brand/`. Cuando llegue, entra aquí y en
53
- * `caras` a la vez.
54
- */
55
- declare const usoDeCara: {
56
- readonly wink: "Formularios, nota al pie amable";
57
- readonly waiting: "Sin resultados, estado de espera";
58
- readonly laughing: "Éxito, suscripción confirmada";
59
- readonly shades: "Módulo o curso completado";
60
- readonly hearts: "Agradecimiento, OG de artículo";
61
- readonly confused: "404, página no encontrada";
62
- readonly annoyed: "Error del servidor, fallo de build";
63
- };
64
- type Cara = keyof typeof caras;
65
- type Pose = keyof typeof poses;
66
- type Aleta = keyof typeof aletas;
67
- /** Sobre qué fondo se va a montar la pieza. Decide qué aleta se usa. */
68
- type Fondo = 'oscuro' | 'claro';
69
- /**
70
- * Dónde se sirven los PNG. Por defecto `/brand`, que es donde ya viven en los
71
- * cinco proyectos (`public/brand/`), así que no hay nada que configurar.
72
- */
73
- declare const RUTA_ASSETS = "/brand";
74
- declare const listaCaras: readonly Cara[];
75
- declare const listaPoses: readonly Pose[];
76
-
77
- export { type Aleta as A, type Cara as C, type Fondo as F, type Pose as P, RUTA_ASSETS as R, aletas as a, listaPoses as b, caras as c, listaCaras as l, poses as p, usoDeCara as u };
@@ -1,77 +0,0 @@
1
- /**
2
- * El catálogo de la marca: qué piezas de Tiburoncín existen y cómo se llama su
3
- * archivo. Datos puros, sin React, para que también lo puedan leer las
4
- * plantillas de OG y cualquier script.
5
- *
6
- * Añadir una pieza nueva es soltar el PNG en `assets/brand/` y añadir una línea
7
- * aquí. El tipo se actualiza solo y el autocompletado la ofrece de inmediato.
8
- */
9
- /**
10
- * Las caras. Solo se usan en estados vacíos, confirmaciones, errores, progreso
11
- * de curso y celebración — nunca en hero, precios, servicios, contacto ni CV.
12
- * Por eso `EmptyState` recibe una cara y `PageHeader` no.
13
- */
14
- declare const caras: {
15
- readonly annoyed: "face-annoyed.png";
16
- readonly confused: "face-confused.png";
17
- readonly hearts: "face-hearts.png";
18
- readonly laughing: "face-laughing.png";
19
- readonly shades: "face-shades.png";
20
- readonly waiting: "face-waiting.png";
21
- readonly wink: "face-wink.png";
22
- };
23
- /** Poses de cuerpo entero. */
24
- declare const poses: {
25
- readonly desk: "pose-desk.png";
26
- readonly 'laptop-coffee': "pose-laptop-coffee.png";
27
- readonly peek: "pose-peek.png";
28
- readonly surf: "pose-surf.png";
29
- };
30
- /**
31
- * La aleta, en sus dos variantes.
32
- *
33
- * El cuerpo de la aleta es casi negro, así que sobre fondo oscuro la variante de
34
- * dos azules desaparece. `espuma` es la silueta a una tinta —el 94 % de sus
35
- * píxeles son `#EDF4F3`, o sea el token espuma— y es la que se ve sobre abismo.
36
- */
37
- declare const aletas: {
38
- /** Dos azules. Para fondos claros. */
39
- readonly color: "fin.png";
40
- /** Silueta a una tinta. Para fondos oscuros. */
41
- readonly espuma: "fin-foam.png";
42
- };
43
- /**
44
- * El uso asignado de cada cara, del inventario del manual.
45
- *
46
- * No es documentación suelta: es la parte del contrato de humor que se puede
47
- * escribir como dato. El manual asigna una situación a cada cara, y sin esto la
48
- * elección se hacía a ojo en cada sitio de uso — que es como `annoyed` termina
49
- * en una confirmación y `hearts` en un error.
50
- *
51
- * Falta una: el manual lista ocho caras y `head-5` (sorpresa · confirmación
52
- * destructiva) no tiene PNG en `assets/brand/`. Cuando llegue, entra aquí y en
53
- * `caras` a la vez.
54
- */
55
- declare const usoDeCara: {
56
- readonly wink: "Formularios, nota al pie amable";
57
- readonly waiting: "Sin resultados, estado de espera";
58
- readonly laughing: "Éxito, suscripción confirmada";
59
- readonly shades: "Módulo o curso completado";
60
- readonly hearts: "Agradecimiento, OG de artículo";
61
- readonly confused: "404, página no encontrada";
62
- readonly annoyed: "Error del servidor, fallo de build";
63
- };
64
- type Cara = keyof typeof caras;
65
- type Pose = keyof typeof poses;
66
- type Aleta = keyof typeof aletas;
67
- /** Sobre qué fondo se va a montar la pieza. Decide qué aleta se usa. */
68
- type Fondo = 'oscuro' | 'claro';
69
- /**
70
- * Dónde se sirven los PNG. Por defecto `/brand`, que es donde ya viven en los
71
- * cinco proyectos (`public/brand/`), así que no hay nada que configurar.
72
- */
73
- declare const RUTA_ASSETS = "/brand";
74
- declare const listaCaras: readonly Cara[];
75
- declare const listaPoses: readonly Pose[];
76
-
77
- export { type Aleta as A, type Cara as C, type Fondo as F, type Pose as P, RUTA_ASSETS as R, aletas as a, listaPoses as b, caras as c, listaCaras as l, poses as p, usoDeCara as u };
@@ -1,36 +0,0 @@
1
- // src/brand/catalogo.ts
2
- var caras = {
3
- annoyed: "face-annoyed.png",
4
- confused: "face-confused.png",
5
- hearts: "face-hearts.png",
6
- laughing: "face-laughing.png",
7
- shades: "face-shades.png",
8
- waiting: "face-waiting.png",
9
- wink: "face-wink.png"
10
- };
11
- var poses = {
12
- desk: "pose-desk.png",
13
- "laptop-coffee": "pose-laptop-coffee.png",
14
- peek: "pose-peek.png",
15
- surf: "pose-surf.png"
16
- };
17
- var aletas = {
18
- /** Dos azules. Para fondos claros. */
19
- color: "fin.png",
20
- /** Silueta a una tinta. Para fondos oscuros. */
21
- espuma: "fin-foam.png"
22
- };
23
- var usoDeCara = {
24
- wink: "Formularios, nota al pie amable",
25
- waiting: "Sin resultados, estado de espera",
26
- laughing: "\xC9xito, suscripci\xF3n confirmada",
27
- shades: "M\xF3dulo o curso completado",
28
- hearts: "Agradecimiento, OG de art\xEDculo",
29
- confused: "404, p\xE1gina no encontrada",
30
- annoyed: "Error del servidor, fallo de build"
31
- };
32
- var RUTA_ASSETS = "/brand";
33
- var listaCaras = Object.keys(caras);
34
- var listaPoses = Object.keys(poses);
35
-
36
- export { RUTA_ASSETS, aletas, caras, listaCaras, listaPoses, poses, usoDeCara };
@@ -1,83 +0,0 @@
1
- // src/tema/index.ts
2
- var TEMA_CLAVE = "arrecife-tema";
3
- var TEMA_ATRIBUTO = "data-theme";
4
- var TEMA_EVENTO = "arrecife:tema";
5
- var esTema = (valor) => valor === "dark" || valor === "light";
6
- function temaActual() {
7
- if (typeof document === "undefined") return "dark";
8
- const puesto = document.documentElement.getAttribute(TEMA_ATRIBUTO);
9
- return esTema(puesto) ? puesto : "dark";
10
- }
11
- function temaGuardado() {
12
- if (typeof localStorage === "undefined") return null;
13
- try {
14
- const valor = localStorage.getItem(TEMA_CLAVE);
15
- return esTema(valor) ? valor : null;
16
- } catch {
17
- return null;
18
- }
19
- }
20
- function temaPreferido() {
21
- const guardado = temaGuardado();
22
- if (guardado) return guardado;
23
- if (typeof matchMedia === "undefined") return "dark";
24
- return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
25
- }
26
- function aplicarTema(tema, persistir = true) {
27
- if (typeof document === "undefined") return;
28
- document.documentElement.setAttribute(TEMA_ATRIBUTO, tema);
29
- document.documentElement.style.colorScheme = tema;
30
- if (persistir) {
31
- try {
32
- localStorage.setItem(TEMA_CLAVE, tema);
33
- } catch {
34
- }
35
- }
36
- dispatchEvent(new CustomEvent(TEMA_EVENTO, { detail: tema }));
37
- }
38
- function alternarTema() {
39
- const siguiente = temaActual() === "dark" ? "light" : "dark";
40
- aplicarTema(siguiente);
41
- return siguiente;
42
- }
43
- function escucharTema(alCambiar) {
44
- if (typeof window === "undefined") return () => {
45
- };
46
- const propio = (evento) => {
47
- const detalle = evento.detail;
48
- if (esTema(detalle)) alCambiar(detalle);
49
- };
50
- const otraPestana = (evento) => {
51
- if (evento.key !== TEMA_CLAVE) return;
52
- const tema = esTema(evento.newValue) ? evento.newValue : temaPreferido();
53
- aplicarTema(tema, false);
54
- alCambiar(tema);
55
- };
56
- addEventListener(TEMA_EVENTO, propio);
57
- addEventListener("storage", otraPestana);
58
- return () => {
59
- removeEventListener(TEMA_EVENTO, propio);
60
- removeEventListener("storage", otraPestana);
61
- };
62
- }
63
- var scriptTema = [
64
- "(function () {",
65
- " var CLAVE = " + JSON.stringify(TEMA_CLAVE) + ";",
66
- " function resolver() {",
67
- " try {",
68
- " var guardado = localStorage.getItem(CLAVE);",
69
- ' if (guardado === "dark" || guardado === "light") return guardado;',
70
- " } catch (e) {}",
71
- ' return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";',
72
- " }",
73
- " function aplicar() {",
74
- " var tema = resolver();",
75
- " document.documentElement.setAttribute(" + JSON.stringify(TEMA_ATRIBUTO) + ", tema);",
76
- " document.documentElement.style.colorScheme = tema;",
77
- " }",
78
- " aplicar();",
79
- ' document.addEventListener("astro:after-swap", aplicar);',
80
- "})();"
81
- ].join("\n");
82
-
83
- export { TEMA_ATRIBUTO, TEMA_CLAVE, TEMA_EVENTO, alternarTema, aplicarTema, escucharTema, scriptTema, temaActual, temaGuardado, temaPreferido };
@@ -1,27 +0,0 @@
1
- import { spacing, control, radius, typeScale } from './chunk-TSPJOM6K.js';
2
- import { clsx } from 'clsx';
3
- import { extendTailwindMerge } from 'tailwind-merge';
4
-
5
- var kebab = (nombre) => nombre.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
6
- var escalas = Object.keys(typeScale);
7
- var radios = Object.keys(radius);
8
- var espacios = Object.keys(spacing).map(kebab);
9
- var controles = Object.keys(control).map((nombre) => `control-${kebab(nombre)}`);
10
- var twMerge = extendTailwindMerge({
11
- extend: {
12
- classGroups: {
13
- "font-size": [{ text: escalas }],
14
- rounded: [{ rounded: radios }],
15
- p: [{ p: espacios }],
16
- px: [{ px: [...espacios, ...controles] }],
17
- py: [{ py: espacios }],
18
- gap: [{ gap: espacios }],
19
- size: [{ size: controles }]
20
- }
21
- }
22
- });
23
- function cn(...inputs) {
24
- return twMerge(clsx(inputs));
25
- }
26
-
27
- export { cn };
@@ -1,229 +0,0 @@
1
- // src/tokens/tokens.ts
2
- var dark = {
3
- background: "#091319",
4
- // abismo · fondo de página
5
- surface: "#10202B",
6
- // fosa · tarjetas y paneles
7
- surfaceRaised: "#17303E",
8
- // corriente · menús, tabs activos
9
- border: "#22414F",
10
- // bordes de control
11
- hairline: "#1E3441",
12
- // divisiones sutiles
13
- hairlineHover: "#2C4D5D",
14
- // hairline en hover de tarjeta
15
- textPrimary: "#EDF4F3",
16
- // espuma 16.84:1
17
- textSecondary: "#A7BCC4",
18
- // bruma 9.50:1
19
- textMuted: "#71919C",
20
- // plancton 5.57:1 nunca bajo 13px
21
- accent: "#35D6C0",
22
- // bioluz 10.31:1 interactivo
23
- accentHover: "#5FE3D1",
24
- accentOn: "#06171A",
25
- // tinta sobre bioluz
26
- warm: "#F2A65A",
27
- // arena 9.28:1 humano y conversión
28
- warmHover: "#F7BB7D",
29
- warmOn: "#2A1605",
30
- // tinta sobre arena
31
- success: "#4FB477",
32
- warning: "#E8A33D",
33
- error: "#E15757"
34
- // 4.51:1 sobre surface
35
- };
36
- var light = {
37
- background: "#F6F2EA",
38
- // papel
39
- surface: "#FFFFFF",
40
- surfaceRaised: "#EFE9DE",
41
- border: "#E6DFD2",
42
- hairline: "#EBE6DC",
43
- hairlineHover: "#D3C8B2",
44
- // hairline en hover de tarjeta
45
- textPrimary: "#0B1524",
46
- // 16.40:1
47
- textSecondary: "#3D4B58",
48
- textMuted: "#626A75",
49
- // 4.53:1 sobre surfaceRaised
50
- accent: "#0D7C6F",
51
- // bioluz oscura 4.55:1
52
- accentHover: "#0C7466",
53
- accentOn: "#FFFFFF",
54
- warm: "#A65B27",
55
- // arena oscura 4.54:1
56
- warmHover: "#96511F",
57
- warmOn: "#FFF7EE",
58
- success: "#0F6B52",
59
- warning: "#8D6111",
60
- // 4.51:1 sobre surfaceRaised
61
- error: "#C0392B"
62
- };
63
- var colors = { dark, light };
64
- var brand = {
65
- /** Cuerpo de la mascota. 4.22:1 → SOLO relleno, NUNCA texto. */
66
- body: "#3E7CB1",
67
- /** Patrón de manchas. */
68
- spots: "#C2D7E7",
69
- /** Casco · contorno y fondo de bloques de código. */
70
- hull: "#0B1524"
71
- };
72
- var fonts = {
73
- /** SOLO titulares y números grandes. Nunca cuerpo. */
74
- display: '"Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif',
75
- /** Cuerpo e interfaz. */
76
- sans: '"Geist", ui-sans-serif, system-ui, sans-serif',
77
- /** Código, rutas, etiquetas, metadatos, firma CLI. */
78
- mono: '"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace'
79
- };
80
- var typeScale = {
81
- display: { family: "display", size: 76, lineHeight: 0.96, weight: 800, tracking: "-0.035em" },
82
- /** Métricas grandes. Números, no prosa: interlineado 1 y sin descendentes. */
83
- stat: { family: "display", size: 46, lineHeight: 1, weight: 800, tracking: "-0.035em" },
84
- h1: { family: "display", size: 44, lineHeight: 1.05, weight: 700, tracking: "-0.03em" },
85
- h2: { family: "display", size: 30, lineHeight: 1.1, weight: 600, tracking: "-0.02em" },
86
- h3: { family: "display", size: 25, lineHeight: 1.15, weight: 600, tracking: "-0.02em" },
87
- body: { family: "sans", size: 18, lineHeight: 1.75, weight: 400 },
88
- /**
89
- * 17px. El botón grande y la bajada de las páginas internas.
90
- *
91
- * Entra como escalón propio porque el documento lo usa DOS veces y en piezas
92
- * distintas: «lg 15px 30px r12» con texto de 17, y «h1 44/700 · bajada 17px ·
93
- * párrafo de contexto 15px». Un solo uso no habría justificado partir la
94
- * distancia entre `ui` (15) y `body` (18); dos sí.
95
- */
96
- lead: { family: "sans", size: 17, lineHeight: 1.5, weight: 400 },
97
- ui: { family: "sans", size: 15, lineHeight: 1.6, weight: 400 },
98
- label: { family: "sans", size: 13, lineHeight: 1.5, weight: 500 },
99
- /**
100
- * La escala de las etiquetas de estado: sans 12.5/500, cuadrada.
101
- *
102
- * Es medio píxel por debajo de `label`, y esta vez el medio píxel sí importa:
103
- * una etiqueta de estado va dentro de una tabla o al lado de un título, y a 13
104
- * competía con el texto que acompaña. El documento la da en 12.5 y se ve.
105
- */
106
- tag: { family: "sans", size: 12.5, lineHeight: 1.4, weight: 500 },
107
- /**
108
- * La escala de categoría y métrica: mono 11.5.
109
- *
110
- * Rompe el suelo de `limits.minScreenPx`, y es a propósito. Ese suelo protege
111
- * al TEXTO —lo que se lee en una frase—, y una píldora de una palabra no es
112
- * texto corrido: es una marca. El contraste sigue medido y pasa AA (plancton
113
- * 5.57:1 sobre abismo), que es la parte que no se negocia.
114
- *
115
- * A 13 las tres familias de etiqueta salían del tamaño de un botón pequeño y
116
- * pesaban más que el título que acompañan. Ver `docs/decisiones.md`.
117
- */
118
- chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
119
- /**
120
- * Mono SIN transformar: fechas, rutas, versiones, nombres de archivo, la firma
121
- * del footer y las métricas de etiqueta. Es la mayoría del mono del sistema.
122
- *
123
- * Existe porque `eyebrow` lleva `uppercase` de fábrica y no es una preferencia
124
- * que se pueda apagar: es la escala del eyebrow. Un `18 ago 2026` en versalitas
125
- * o un `pose-laptop-coffee.png` en mayúsculas están mal, y sin este escalón la
126
- * única salida era un `normal-case` en cada sitio de uso.
127
- *
128
- * El documento dice 12.5. Son 13 por la misma razón por la que `textMuted` se
129
- * corrigió: `limits.minScreenPx` es 13 y plancton «nunca bajo 13px». Esta
130
- * escala es justo donde se escriben las metas en muted, así que ponerla en
131
- * 12.5 habría dejado ilegible su uso más común por medio píxel.
132
- */
133
- meta: { family: "mono", size: 13, lineHeight: 1.6, weight: 400 },
134
- eyebrow: { family: "mono", size: 12, tracking: "0.12em", transform: "uppercase" }
135
- };
136
- var limits = {
137
- /** Mínimo absoluto en pantalla. */
138
- minScreenPx: 13,
139
- /** Mínimo absoluto impreso. */
140
- minPrintPt: 12,
141
- /** Medida máxima de cuerpo. */
142
- measure: "68ch"
143
- };
144
- var radius = {
145
- chip: 6,
146
- control: 10,
147
- card: 14,
148
- panel: 16,
149
- pill: 999
150
- };
151
- var control = {
152
- sm: 14,
153
- md: 22,
154
- lg: 30,
155
- /** Botón de icono: cuadrado, sin texto. */
156
- icon: 42
157
- };
158
- var spacing = {
159
- stepXs: 8,
160
- stepSm: 12,
161
- stepMd: 16,
162
- stepLg: 26,
163
- stepXl: 40,
164
- section: 96
165
- };
166
- var size = {
167
- /** Alto de la barra de navegación. */
168
- nav: 64,
169
- /** Ancho de columna de lectura. */
170
- content: 760,
171
- /** Ancho máximo de página. */
172
- wide: 1180
173
- };
174
- var sintaxis = {
175
- /** El casco. Es el fondo del bloque en los dos modos. */
176
- fondo: brand.hull,
177
- /** Identificadores, funciones, tipos, variables. */
178
- identificador: dark.textPrimary,
179
- /** Cadenas, números, booleanos, null. */
180
- literal: dark.accent,
181
- /** Palabras clave, control de flujo, `import`, `this`. */
182
- palabraClave: dark.warm,
183
- /** Comentarios y puntuación. */
184
- comentario: dark.textMuted,
185
- /** `markup.deleted`, `invalid`. */
186
- invalido: dark.error
187
- };
188
- var series = {
189
- dark: [dark.accent, dark.warm, brand.body, dark.textMuted],
190
- light: [light.accent, light.warm, brand.body, light.textMuted]
191
- };
192
- var profundo = "#0D2129";
193
- var gradient = {
194
- dark: {
195
- hero: `linear-gradient(160deg, ${dark.background} 60%, ${profundo} 100%)`,
196
- seccion: `linear-gradient(150deg, ${dark.surface} 0%, ${profundo} 100%)`,
197
- /** El de la plantilla OG de artículo. 145°, del documento. */
198
- og: `linear-gradient(145deg, ${dark.background} 55%, ${profundo} 100%)`
199
- },
200
- light: {
201
- hero: `linear-gradient(160deg, ${light.background} 60%, ${light.surfaceRaised} 100%)`,
202
- seccion: `linear-gradient(150deg, ${light.surface} 0%, ${light.surfaceRaised} 100%)`,
203
- og: `linear-gradient(145deg, ${light.background} 55%, ${light.surfaceRaised} 100%)`
204
- }
205
- };
206
- var shadow = {
207
- standard: "0 1px 2px rgba(0, 0, 0, 0.35)"
208
- };
209
- var motion = {
210
- duration: "150ms",
211
- easing: "ease-out",
212
- properties: "color, background-color, border-color, fill, stroke"
213
- };
214
- var tagline = {
215
- /** Hero. */
216
- largo: "Ayudo a equipos de ingenier\xEDa a escalar con criterio",
217
- /** Header, una línea. */
218
- corto: "Ayudo a equipos a escalar con criterio",
219
- /** LinkedIn. */
220
- en: "Helping engineering teams scale with judgment"
221
- };
222
- var naming = {
223
- wordmark: "Eduardo \xC1lvarez",
224
- mascot: "Tiburonc\xEDn",
225
- /** El dominio, para la firma CLI del footer: `$ cd ~/eduardoalvarez.dev/2026`. */
226
- domain: "eduardoalvarez.dev"
227
- };
228
-
229
- export { brand, colors, control, dark, fonts, gradient, light, limits, motion, naming, radius, series, shadow, sintaxis, size, spacing, tagline, typeScale };
@@ -1,81 +0,0 @@
1
- import { cn } from './chunk-NHS7ETKJ.js';
2
- import { Slot } from '@radix-ui/react-slot';
3
- import { cva } from 'class-variance-authority';
4
- import { jsx } from 'react/jsx-runtime';
5
-
6
- var ETIQUETA = {
7
- display: "h1",
8
- stat: "p",
9
- h1: "h1",
10
- h2: "h2",
11
- h3: "h3",
12
- body: "p",
13
- lead: "p",
14
- ui: "p",
15
- label: "span",
16
- tag: "span",
17
- meta: "p",
18
- chip: "span",
19
- eyebrow: "p"
20
- };
21
- var texto = cva("", {
22
- variants: {
23
- variant: {
24
- display: "text-display font-display",
25
- // Métricas grandes: 46px de display. Es la única escala pensada para un
26
- // número, y por eso el interlineado es 1.
27
- stat: "text-stat font-display",
28
- h1: "text-h1 font-display",
29
- h2: "text-h2 font-display",
30
- h3: "text-h3 font-display",
31
- body: "text-body font-sans",
32
- // 17px: la bajada de las páginas internas y el botón grande.
33
- lead: "text-lead font-sans",
34
- ui: "text-ui font-sans",
35
- label: "text-label font-sans",
36
- // Las dos escalas de etiqueta. `tag` es el estado (sans 12.5/500) y `chip`
37
- // la categoría y la métrica (mono 11.5). Ninguna transforma el texto.
38
- tag: "text-tag font-sans",
39
- chip: "text-chip font-mono",
40
- // Mono SIN transformar: fechas, rutas, versiones, nombres de archivo y la
41
- // firma del footer. Es el escalón que faltaba, y por el que cada sitio de
42
- // uso terminaba escribiendo `variant="eyebrow" className="normal-case"`.
43
- meta: "text-meta font-mono",
44
- // `uppercase` no cabe en un token de tamaño: text-transform no es un
45
- // modificador de --text-*. Por eso lo pone la variante y no theme.css.
46
- eyebrow: "text-eyebrow font-mono uppercase"
47
- },
48
- tone: {
49
- primary: "text-text-primary",
50
- secondary: "text-text-secondary",
51
- muted: "text-text-muted",
52
- accent: "text-accent",
53
- warm: "text-warm",
54
- success: "text-success",
55
- warning: "text-warning",
56
- error: "text-error"
57
- }
58
- },
59
- defaultVariants: { variant: "body", tone: "primary" }
60
- });
61
- function Text({
62
- className,
63
- variant,
64
- tone,
65
- as,
66
- asChild = false,
67
- measure,
68
- children,
69
- ...props
70
- }) {
71
- const escala = variant ?? "body";
72
- const limitar = measure ?? escala === "body";
73
- const clases = cn(texto({ variant, tone }), limitar && "max-w-measure", className);
74
- if (asChild) {
75
- return /* @__PURE__ */ jsx(Slot, { className: clases, ...props, children });
76
- }
77
- const Etiqueta = as ?? ETIQUETA[escala];
78
- return /* @__PURE__ */ jsx(Etiqueta, { className: clases, ...props, children });
79
- }
80
-
81
- export { Text, texto };