kaizen-lib 0.2.8 → 0.2.9

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/README.md CHANGED
@@ -12,7 +12,7 @@ styled-components con `moduleResolution: "bundler"`, así que lo transpilan ello
12
12
  |---|---|---|
13
13
  | `kaizen-lib/tokens` | Referencias `var(--kz-*)` tipadas | listo |
14
14
  | `kaizen-lib/tokens.css` | Valores por defecto, tema oscuro y claro | listo |
15
- | `kaizen-lib/ui` | Componentes (`IconButton`, `ReactionButton`, `Medidor`, `Relieve`, `LangSelector`, `Modal`) | en curso |
15
+ | `kaizen-lib/ui` | Componentes (`IconButton`, `ReactionButton`, `Medidor`, `Relieve`, `LangSelector`, `Modal`, `Badge`) | en curso |
16
16
 
17
17
  ## ¿Qué uso para qué?
18
18
 
@@ -30,6 +30,7 @@ alcanza, extendé el componente en vez de bypassearlo (ver `implement.md` del ha
30
30
  | Un ciclador de idioma con banderas en la topbar | `LangSelector` | reconstruir el ciclado a mano — ya lo resuelve, con `Relieve` incluido |
31
31
  | Un panel flotante que bloquea el resto de la página hasta que se resuelve (formulario, confirmación, contenido a pantalla completa) | `Modal` | un overlay propio — portal, foco atrapado, `Escape`, scroll bloqueado y anidamiento ya están resueltos ahí |
32
32
  | Un arco/medidor que muestra cuánto de un total está ocupado | `Medidor` | un `<progress>` o una barra propia |
33
+ | Una píldora de estado (activo, vencido, un rol, una etiqueta) con fondo lavado y texto del mismo tono | `Badge` | un `styled.span` propio por producto — es exactamente lo que ya había triplicado |
33
34
  | Espaciado, radios, color, tipografía, sombra | `kaizen-lib/tokens` (`space`, `radius`, `fg`, `accent`, `font`, `shadow`, `relief`...) | un valor a mano — cada uno es una referencia `var(--kz-*)` |
34
35
 
35
36
  Ningún componente trae íconos ni banderas: los recibe como `children`/`icono`, y el
@@ -250,6 +251,20 @@ Lo que **no** trae: un `placement` lateral (drawer) y un padding compacto de cue
250
251
  casos de un solo consumidor cada uno (`components/ui/Dialog` de valle-verde) — se resuelven
251
252
  ahí con un wrapper local hasta que un segundo consumidor los pida acá.
252
253
 
254
+ ```tsx
255
+ import { Badge } from 'kaizen-lib/ui';
256
+
257
+ <Badge tone="success">Activo</Badge>
258
+ <Badge tone="danger" bordered>Vencido</Badge>
259
+ ```
260
+
261
+ **`Badge`** es una píldora de estado: fondo lavado + texto del mismo tono (`neutral` |
262
+ `accent` | `success` | `danger` | `warning` | `info`, default `neutral`), mayúsculas
263
+ apretadas. Elicitado de tres implementaciones ya duplicadas (taboo-next, valle-verde,
264
+ kaizen-next) — el look es el de valle-verde. `bordered` agrega un borde de 1px del color
265
+ pleno del tono, para cuando el fondo lavado solo no alcanza de contraste contra la
266
+ superficie de atrás; no es una segunda estética, es el mismo tono con más peso.
267
+
253
268
  ## Desarrollo
254
269
 
255
270
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-lib",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Tokens y componentes compartidos entre los fronts de Kaizen.",
5
5
  "license": "MIT",
6
6
  "author": "Nicolás Sandobal",
@@ -0,0 +1,77 @@
1
+ 'use client';
2
+
3
+ import type { HTMLAttributes, ReactNode } from 'react';
4
+ import styled from 'styled-components';
5
+ import { accent, danger, fg, font, info, radius, space, success, surface, warning } from '../tokens';
6
+
7
+ /**
8
+ * El tono resuelve un TRÍO por tabla: fondo lavado, texto y el color pleno
9
+ * (para `bordered`) — así no puede salir una píldora con el texto invisible
10
+ * sobre su propio fondo, ni un borde que no combina con ninguno de los dos.
11
+ */
12
+ export type BadgeTone = 'neutral' | 'accent' | 'success' | 'danger' | 'warning' | 'info';
13
+
14
+ const TONOS: Record<BadgeTone, { fondo: string; texto: string; pleno: string }> = {
15
+ neutral: { fondo: surface.hover, texto: fg.muted, pleno: fg.muted },
16
+ accent: { fondo: accent.soft, texto: accent.fg, pleno: accent.DEFAULT },
17
+ success: { fondo: success.soft, texto: success.fg, pleno: success.DEFAULT },
18
+ danger: { fondo: danger.soft, texto: danger.fg, pleno: danger.DEFAULT },
19
+ warning: { fondo: warning.soft, texto: warning.fg, pleno: warning.DEFAULT },
20
+ info: { fondo: info.soft, texto: info.fg, pleno: info.DEFAULT },
21
+ };
22
+
23
+ type Props = {
24
+ tone?: BadgeTone;
25
+ /** Borde de 1px del color pleno del tono. Para una píldora que necesita más peso que el fondo lavado solo. */
26
+ bordered?: boolean;
27
+ children: ReactNode;
28
+ className?: string;
29
+ };
30
+
31
+ const Raiz = styled.span<{ $tone: BadgeTone; $bordered: boolean }>`
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: ${space[1]};
35
+ padding: 3px ${space[2]};
36
+ border-radius: ${radius.sm};
37
+ border: 1px solid ${({ $tone, $bordered }) => ($bordered ? TONOS[$tone].pleno : 'transparent')};
38
+ font-size: ${font.xs};
39
+ font-weight: 600;
40
+ line-height: 1.35;
41
+ letter-spacing: 0.04em;
42
+ text-transform: uppercase;
43
+ white-space: nowrap;
44
+ background: ${({ $tone }) => TONOS[$tone].fondo};
45
+ color: ${({ $tone }) => TONOS[$tone].texto};
46
+ `;
47
+
48
+ /**
49
+ * Píldora de estado: fondo lavado + texto del mismo tono, mayúsculas
50
+ * apretadas. Elicitado de las implementaciones ya duplicadas en tres
51
+ * productos (taboo-next, valle-verde, kaizen-next) — el look es el de
52
+ * valle-verde, que era el más resuelto de los tres.
53
+ *
54
+ * ```tsx
55
+ * <Badge tone="success">Activo</Badge>
56
+ * <Badge tone="danger" bordered>Vencido</Badge>
57
+ * ```
58
+ *
59
+ * `bordered` es opcional: el fondo lavado solo ya es suficiente contraste
60
+ * para la mayoría de los casos (ver `bordered={false}`, el default). Se usa
61
+ * cuando la píldora flota sobre una superficie parecida a su propio fondo y
62
+ * necesita un borde para no perderse — no es una segunda variante estética,
63
+ * es el mismo tono con más peso.
64
+ */
65
+ export function Badge({
66
+ tone = 'neutral',
67
+ bordered = false,
68
+ children,
69
+ className,
70
+ ...rest
71
+ }: Props & Omit<HTMLAttributes<HTMLSpanElement>, keyof Props>) {
72
+ return (
73
+ <Raiz $tone={tone} $bordered={bordered} className={className} {...rest}>
74
+ {children}
75
+ </Raiz>
76
+ );
77
+ }
package/src/ui/index.ts CHANGED
@@ -9,3 +9,5 @@ export { LangSelector } from './LangSelector';
9
9
  export type { OpcionDeIdioma } from './LangSelector';
10
10
  export { Modal } from './Modal';
11
11
  export type { ModalSize } from './Modal';
12
+ export { Badge } from './Badge';
13
+ export type { BadgeTone } from './Badge';