@studiolxd/brand 38.15.0 → 38.17.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,96 @@ El paquete sigue [semver](https://semver.org/lang/es/): **patch** para bug fixes
7
7
  regeneración de `dist`, **minor** para componentes/props/variantes/tokens nuevos, **major**
8
8
  para breaking changes.
9
9
 
10
+ ## [38.17.0] — 2026-09-15
11
+
12
+ > **Minor.** En `ChatShell`, el botón que despliega el cajón de conversaciones
13
+ > deja de saltar: cerrado y abierto cae en el mismo punto exacto de la pantalla.
14
+
15
+ **`ChatShell` — el disparador del cajón no se mueve al desplegar.** En pantalla
16
+ estrecha hay dos botones gemelos con el mismo glifo: el de la cabecera, que abre
17
+ el cajón, y el del propio cajón, que lo cierra. La v38.15.0 ya los dejó
18
+ idénticos de forma —misma variante, misma talla, mismo icono— pero no de sitio,
19
+ porque cada uno se medía contra un origen distinto: el de la cabecera contra el
20
+ relleno que ponga quien monta el armazón (el armazón no tiene relleno propio: quien
21
+ compone, espacia) y el del cajón contra el `padding-block`, el `padding-inline` y el
22
+ `gap` del `Sheet`. Medido en `mobile1` con 24px de aire alrededor: cerrado caía en
23
+ (24,24) y abierto en (56,72) — 48px más abajo y 32 a la derecha, con la misma caja
24
+ de 32×32. Al plegar y desplegar, el glifo daba un salto.
25
+
26
+ Ahora el cajón **repite la misma fila `chat-shell__header`** que la columna
27
+ principal, con el botón dentro y la lista debajo en `chat-shell__drawer-list`;
28
+ `.chat-shell__drawer` anula el relleno propio del `Sheet` para que esa fila nazca
29
+ donde nace la cabecera, y no recorta (`overflow: visible`), que si no se comía dos
30
+ lados del anillo de foco. El disparador gana además `align-self: flex-start`: sin
31
+ eso el arreglo solo valía con cabeceras bajitas —con un título y un selector de
32
+ modelo, la fila crece, el botón centrado baja con ella y el del cajón se queda
33
+ arriba, y volvía a saltar 22px—. Anclado al arranque coinciden sea cual sea el alto
34
+ de la cabecera, el mismo criterio con el que el aspa de un diálogo se alinea con la
35
+ primera línea del título. Verificado en Chromium: (24,24,32,32) antes y después.
36
+
37
+ **Token nuevo:** `chat-shell.drawer-gap`, en cascada desde
38
+ `chat-shell.thread-padding-block` — la lista del cajón arranca donde arranca el
39
+ hilo. Ningún color nuevo.
40
+
41
+ **Para consumidores:** ninguna prop cambia. Dos efectos visibles: el disparador de
42
+ la cabecera pasa a ir pegado al canto superior de su fila en vez de centrado, así
43
+ que con cabeceras altas el glifo sube unos píxeles; y dentro del cajón hay marcado
44
+ nuevo (`.chat-shell__header`, `.chat-shell__drawer-list`), que afecta a quien
45
+ estuviera pintando a mano el interior de `.chat-shell__drawer`. `Sheet` no se toca.
46
+
47
+ ## [38.16.0] — 2026-09-15
48
+
49
+ > **Minor.** La barra de filtros deja de reorganizarse mientras escribes,
50
+ > `Alert` gana ranura de acciones y el pie de un diálogo se coloca por el ancho
51
+ > del diálogo, no por el de la ventana.
52
+
53
+ **`FilterBar` — una sola rejilla para los filtros y sus acciones.** Eran dos
54
+ cajas hermanas: la rejilla `auto-fit` de filtros y, aparte, un bloque de
55
+ acciones `flex: none` anclado al extremo con `margin-inline-start: auto`. De
56
+ ahí salían los dos defectos que se veían a diario. El botón de «Limpiar
57
+ filtros» solo se pinta cuando ya hay algo que limpiar —o sea, **mientras se
58
+ escribe**— y, al aparecer, le restaba a la rejilla su ancho más el hueco: si
59
+ ese recorte cruzaba un múltiplo del mínimo de columna, la rejilla perdía una
60
+ columna de golpe y un filtro se caía a la línea siguiente. Medido a 1024px con
61
+ cuatro filtros: sin el botón, una fila de cuatro columnas; con él, dos filas y
62
+ 733px de hueco. A 768px con tres filtros, lo mismo: de tres columnas a dos, con
63
+ 467px de hueco.
64
+
65
+ Ahora el botón es **una celda más de la misma rejilla**, la siguiente al último
66
+ filtro, alineada con los controles. Y la rejilla es `auto-fill` en vez de
67
+ `auto-fit`: las pistas las decide el ancho disponible y **solo** el ancho
68
+ disponible, así que ni el número de columnas ni el ancho de los campos cambian
69
+ porque el botón aparezca o desaparezca — medido a 1536, 1280, 1024, 900, 768,
70
+ 640 y 390px, con dos, tres y cuatro filtros. La contrapartida —con dos filtros
71
+ en una pantalla ancha sobra sitio a la derecha— es la buena: un filtro mide lo
72
+ que mide una celda del sistema (192px, `field-row.cell-md`), no media pantalla.
73
+ `.filter-bar__filters` pasa a `display: contents`: el envoltorio conserva su
74
+ clase pero deja de ser una caja.
75
+
76
+ **`Alert` gana ranura de acciones** (`actions`, o `<Alert.Actions>` para el
77
+ modo composición), con la misma norma y el mismo nombre de clase que sus
78
+ hermanos `Banner` y `ConsentBanner`: por debajo de `md` apila y cada botón
79
+ ocupa la línea. Hasta ahora, quien necesitaba un botón en un aviso lo metía
80
+ dentro de la descripción y en móvil se quedaba a medio ancho. La ranura vive
81
+ **dentro** de `.alert__content`, que es quien declara la superficie interior
82
+ del relleno: un botón fuera de ella leería con la página en vez de con el
83
+ aviso. Tokens nuevos: `alert.actions-gap` y
84
+ `alert.actions-margin-block-start`.
85
+
86
+ **El pie de un diálogo se coloca por el ancho del diálogo** (`@container`, con
87
+ umbral `sm`), no por el de la ventana. `Sheet` ya usaba la pieza compartida
88
+ `.dialog-footer` y apilaba en móvil; el fallo estaba en escritorio, donde un
89
+ cajón lateral mide lo mismo que en un teléfono —320px, 256 de hueco útil— y el
90
+ pie seguía siendo una fila: dos botones con su etiqueta no cabían. Ahora
91
+ `.sheet` y `.modal__content` se declaran contenedor de consulta, y el umbral es
92
+ el que hace falta para una fila de dos botones, no el ancho de una pantalla. El
93
+ cajón apila siempre; el diálogo centrado (560px, 494 de hueco) conserva su fila
94
+ donde la tenía. Es el caso que Fundamentos › Puntos de ruptura ya señalaba como
95
+ propio de `@container`.
96
+
97
+ Nueve historias de contrato nuevas o actualizadas, en el viewport que le toca a
98
+ cada una.
99
+
10
100
  ## [38.15.0] — 2026-09-15
11
101
 
12
102
  > **Minor.** El cajón de conversaciones del chat, en móvil: ocupa el ancho y su
@@ -141,6 +141,8 @@ var e = {
141
141
  "--alert-border-width": "1px",
142
142
  "--alert-gap": "0.75rem",
143
143
  "--alert-content-gap": "0.5rem",
144
+ "--alert-actions-gap": "0.75rem",
145
+ "--alert-actions-margin-block-start": "0.75rem",
144
146
  "--alert-bg": "#111e30",
145
147
  "--alert-border-color": "#ffffff",
146
148
  "--alert-title-font-size": "1rem",
@@ -3181,6 +3183,7 @@ var e = {
3181
3183
  "--chat-shell-list-width": "15rem",
3182
3184
  "--chat-shell-gap": "2rem",
3183
3185
  "--chat-shell-header-gap": "0.75rem",
3186
+ "--chat-shell-drawer-gap": "1rem",
3184
3187
  "--chat-shell-thread-padding-block": "1rem",
3185
3188
  "--chat-shell-thread-padding-inline": "calc(0.75rem + 1px)",
3186
3189
  "--chat-shell-bg": "#ffffff",
@@ -5,6 +5,13 @@ export interface AlertProps extends React.ComponentPropsWithoutRef<'div'> {
5
5
  /** Título del alert. **Opcional**: en modo composición usa `children` (p. ej. `<Alert.Title>`). */
6
6
  title?: string;
7
7
  description?: React.ReactNode;
8
+ /**
9
+ * Ranura de acciones del aviso —normalmente un `Button` del sistema—, al pie
10
+ * del cuerpo. Los botones van **aquí y no dentro de la descripción**: la
11
+ * ranura es la que aplica la norma del sistema (por debajo de `md` apilan y
12
+ * cada uno ocupa la línea), y dentro del texto se quedan a medio ancho.
13
+ */
14
+ actions?: React.ReactNode;
8
15
  dismissible?: boolean;
9
16
  onDismiss?: () => void;
10
17
  /**
@@ -24,10 +31,16 @@ export interface AlertProps extends React.ComponentPropsWithoutRef<'div'> {
24
31
  }
25
32
  export type AlertTitleProps = React.ComponentPropsWithoutRef<'p'>;
26
33
  export type AlertDescriptionProps = React.ComponentPropsWithoutRef<'div'>;
34
+ export type AlertActionsProps = React.ComponentPropsWithoutRef<'div'>;
27
35
  /** Subparte de composición: título del alert. */
28
36
  export declare const AlertTitle: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>, "ref"> & import("react").RefAttributes<HTMLParagraphElement>>;
29
37
  /** Subparte de composición: descripción del alert. */
30
38
  export declare const AlertDescription: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
39
+ /**
40
+ * Subparte de composición: las acciones del alert. Equivale a la prop
41
+ * `actions` y es la pieza para el modo composición.
42
+ */
43
+ export declare const AlertActions: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
31
44
  /**
32
45
  * Namespace de composición. Las subpartes también están disponibles como **named
33
46
  * exports** (`AlertTitle`, `AlertDescription`): en **Server Components (RSC)** usa los
@@ -36,4 +49,5 @@ export declare const AlertDescription: import("react").ForwardRefExoticComponent
36
49
  export declare const Alert: import("react").ForwardRefExoticComponent<AlertProps & import("react").RefAttributes<HTMLDivElement>> & {
37
50
  Title: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>, "ref"> & import("react").RefAttributes<HTMLParagraphElement>>;
38
51
  Description: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
52
+ Actions: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
39
53
  };
package/dist/alert.css CHANGED
@@ -1 +1 @@
1
- :root{--alert-padding-block:var(--spacing-4);--alert-padding-inline:var(--spacing-5);--alert-gap:var(--spacing-3);--alert-content-gap:var(--spacing-2);--alert-title-line-height:var(--line-height-snug);--alert-close-size:var(--size-component-sm);--alert-close-inset:var(--spacing-2);--alert-border-radius:var(--border-radius-default);--alert-border-width:var(--border-width-default);--alert-bg:var(--color-primary);--alert-border-color:var(--color-text-on-dark);--alert-title-font-size:var(--text-font-size);--alert-title-font-weight:var(--font-weight-emphasis);--alert-title-color:var(--color-text-on-dark);--alert-description-font-size:var(--text-font-size);--alert-description-line-height:var(--text-line-height);--alert-description-color:var(--color-text-on-dark);--alert-success-bg:var(--color-success-fill);--alert-success-border-color:var(--color-success-fill);--alert-success-title-color:var(--color-success-fill-text);--alert-success-description-color:var(--color-success-fill-text);--alert-error-bg:var(--color-error-fill);--alert-error-border-color:var(--color-error-fill);--alert-error-title-color:var(--color-error-fill-text);--alert-error-description-color:var(--color-error-fill-text);--alert-warning-bg:var(--color-warning-fill);--alert-warning-border-color:var(--color-warning-fill);--alert-warning-title-color:var(--color-warning-fill-text);--alert-warning-description-color:var(--color-warning-fill-text);--alert-warning-close-color:var(--color-warning-fill-text)}.surface-dark,.surface-invert,[data-theme=dark],html.dark{--alert-bg:var(--color-text-on-dark);--alert-border-color:var(--color-text-on-dark);--alert-title-color:var(--color-primary);--alert-description-color:var(--color-primary)}.alert{background-color:var(--alert-bg);border:var(--alert-border-width) solid var(--alert-border-color);border-radius:var(--alert-border-radius);padding-block:var(--alert-padding-block);padding-inline:var(--alert-padding-inline);align-items:flex-start;gap:var(--alert-gap);box-sizing:border-box;--alert-title-ink:var(--alert-title-color);--alert-description-ink:var(--alert-description-color);display:flex;position:relative}.alert.alert--dismissible{padding-inline-end:calc(var(--alert-close-inset) * 2 + var(--alert-close-size))}.alert__content{flex:1;min-width:0}.alert__title{font-size:var(--alert-title-font-size);font-weight:var(--alert-title-font-weight);color:var(--alert-title-ink);line-height:var(--alert-title-line-height);margin:0}.alert__description{font-size:var(--alert-description-font-size);color:var(--alert-description-ink);line-height:var(--alert-description-line-height);margin:0;align-items:start;gap:var(--alert-content-gap);flex-direction:column;margin-block-start:var(--alert-content-gap);display:flex}.alert .alert__close{--close-button-size:var(--alert-close-size);position:absolute;inset-block-start:var(--alert-close-inset);inset-inline-end:var(--alert-close-inset)}.alert.alert--success{--alert-bg:var(--alert-success-bg);--alert-border-color:var(--alert-success-border-color);--alert-title-color:var(--alert-success-title-color);--alert-description-color:var(--alert-success-description-color)}.alert.alert--error{--alert-bg:var(--alert-error-bg);--alert-border-color:var(--alert-error-border-color);--alert-title-color:var(--alert-error-title-color);--alert-description-color:var(--alert-error-description-color)}.alert.alert--warning{--alert-bg:var(--alert-warning-bg);--alert-border-color:var(--alert-warning-border-color);--alert-title-color:var(--alert-warning-title-color);--alert-description-color:var(--alert-warning-description-color);--close-button-color:var(--alert-warning-close-color);--close-button-focus-ring-color:var(--alert-warning-close-color)}
1
+ :root{--alert-padding-block:var(--spacing-4);--alert-padding-inline:var(--spacing-5);--alert-gap:var(--spacing-3);--alert-content-gap:var(--spacing-2);--alert-actions-margin-block-start:var(--spacing-3);--alert-title-line-height:var(--line-height-snug);--alert-close-size:var(--size-component-sm);--alert-close-inset:var(--spacing-2);--alert-border-radius:var(--border-radius-default);--alert-border-width:var(--border-width-default);--alert-actions-gap:var(--form-actions-gap);--alert-bg:var(--color-primary);--alert-border-color:var(--color-text-on-dark);--alert-title-font-size:var(--text-font-size);--alert-title-font-weight:var(--font-weight-emphasis);--alert-title-color:var(--color-text-on-dark);--alert-description-font-size:var(--text-font-size);--alert-description-line-height:var(--text-line-height);--alert-description-color:var(--color-text-on-dark);--alert-success-bg:var(--color-success-fill);--alert-success-border-color:var(--color-success-fill);--alert-success-title-color:var(--color-success-fill-text);--alert-success-description-color:var(--color-success-fill-text);--alert-error-bg:var(--color-error-fill);--alert-error-border-color:var(--color-error-fill);--alert-error-title-color:var(--color-error-fill-text);--alert-error-description-color:var(--color-error-fill-text);--alert-warning-bg:var(--color-warning-fill);--alert-warning-border-color:var(--color-warning-fill);--alert-warning-title-color:var(--color-warning-fill-text);--alert-warning-description-color:var(--color-warning-fill-text);--alert-warning-close-color:var(--color-warning-fill-text)}.surface-dark,.surface-invert,[data-theme=dark],html.dark{--alert-bg:var(--color-text-on-dark);--alert-border-color:var(--color-text-on-dark);--alert-title-color:var(--color-primary);--alert-description-color:var(--color-primary)}.alert{background-color:var(--alert-bg);border:var(--alert-border-width) solid var(--alert-border-color);border-radius:var(--alert-border-radius);padding-block:var(--alert-padding-block);padding-inline:var(--alert-padding-inline);align-items:flex-start;gap:var(--alert-gap);box-sizing:border-box;--alert-title-ink:var(--alert-title-color);--alert-description-ink:var(--alert-description-color);display:flex;position:relative}.alert.alert--dismissible{padding-inline-end:calc(var(--alert-close-inset) * 2 + var(--alert-close-size))}.alert__content{flex:1;min-width:0}.alert__title{font-size:var(--alert-title-font-size);font-weight:var(--alert-title-font-weight);color:var(--alert-title-ink);line-height:var(--alert-title-line-height);margin:0}.alert__description{font-size:var(--alert-description-font-size);color:var(--alert-description-ink);line-height:var(--alert-description-line-height);margin:0;align-items:start;gap:var(--alert-content-gap);flex-direction:column;margin-block-start:var(--alert-content-gap);display:flex}.alert__actions{align-items:stretch;gap:var(--alert-actions-gap);flex-direction:column;margin-block-start:var(--alert-actions-margin-block-start);display:flex}.alert__actions>*{inline-size:100%}@media (width>=768px){.alert__actions{flex-flow:wrap;align-items:center}.alert__actions>*{inline-size:auto}}.alert .alert__close{--close-button-size:var(--alert-close-size);position:absolute;inset-block-start:var(--alert-close-inset);inset-inline-end:var(--alert-close-inset)}.alert.alert--success{--alert-bg:var(--alert-success-bg);--alert-border-color:var(--alert-success-border-color);--alert-title-color:var(--alert-success-title-color);--alert-description-color:var(--alert-success-description-color)}.alert.alert--error{--alert-bg:var(--alert-error-bg);--alert-border-color:var(--alert-error-border-color);--alert-title-color:var(--alert-error-title-color);--alert-description-color:var(--alert-error-description-color)}.alert.alert--warning{--alert-bg:var(--alert-warning-bg);--alert-border-color:var(--alert-warning-border-color);--alert-title-color:var(--alert-warning-title-color);--alert-description-color:var(--alert-warning-description-color);--close-button-color:var(--alert-warning-close-color);--close-button-focus-ring-color:var(--alert-warning-close-color)}
package/dist/alert.js CHANGED
@@ -23,19 +23,26 @@ var a = {
23
23
  ...r,
24
24
  children: n
25
25
  });
26
- }), c = r(function({ variant: r = "default", title: o, description: s, dismissible: c = !1, onDismiss: l, finalFocus: u, closeLabel: d = "Cerrar", className: f, children: p, role: m, ...h }, g) {
27
- let [_, v] = i(!1);
28
- if (_) return null;
29
- let y = [
26
+ }), c = r(function({ className: e, children: n, ...r }, i) {
27
+ return /* @__PURE__ */ t("div", {
28
+ ref: i,
29
+ className: ["alert__actions", e ?? ""].filter(Boolean).join(" "),
30
+ ...r,
31
+ children: n
32
+ });
33
+ }), l = r(function({ variant: r = "default", title: o, description: s, actions: c, dismissible: l = !1, onDismiss: u, finalFocus: d, closeLabel: f = "Cerrar", className: p, children: m, role: h, ...g }, _) {
34
+ let [v, y] = i(!1);
35
+ if (v) return null;
36
+ let b = [
30
37
  "alert",
31
38
  r === "default" ? "" : `alert--${r}`,
32
39
  r === "success" || r === "error" ? "surface-dark" : "",
33
- c ? "alert--dismissible" : "",
34
- f ?? ""
35
- ].filter(Boolean).join(" "), b = r === "default" ? " surface-invert" : "";
36
- function x() {
40
+ l ? "alert--dismissible" : "",
41
+ p ?? ""
42
+ ].filter(Boolean).join(" "), x = r === "default" ? " surface-invert" : "";
43
+ function S() {
37
44
  if (typeof document > "u") return;
38
- let e = u?.current;
45
+ let e = d?.current;
39
46
  if (e) {
40
47
  e.focus();
41
48
  return;
@@ -43,16 +50,16 @@ var a = {
43
50
  let t = document.body, n = t.hasAttribute("tabindex");
44
51
  n || t.setAttribute("tabindex", "-1"), t.focus(), n || t.removeAttribute("tabindex");
45
52
  }
46
- function S() {
47
- x(), l ? l() : v(!0);
53
+ function C() {
54
+ S(), u ? u() : y(!0);
48
55
  }
49
56
  return /* @__PURE__ */ n("div", {
50
- ref: g,
51
- role: m ?? a[r],
52
- className: y,
53
- ...h,
57
+ ref: _,
58
+ role: h ?? a[r],
59
+ className: b,
60
+ ...g,
54
61
  children: [/* @__PURE__ */ n("div", {
55
- className: `alert__content${b}`,
62
+ className: `alert__content${x}`,
56
63
  children: [
57
64
  o && /* @__PURE__ */ t("p", {
58
65
  className: "alert__title",
@@ -62,17 +69,22 @@ var a = {
62
69
  className: "alert__description",
63
70
  children: s
64
71
  }),
65
- p
72
+ m,
73
+ c && /* @__PURE__ */ t("div", {
74
+ className: "alert__actions",
75
+ children: c
76
+ })
66
77
  ]
67
- }), c && /* @__PURE__ */ t(e, {
68
- className: `alert__close${b}`,
69
- label: d,
70
- onClick: S
78
+ }), l && /* @__PURE__ */ t(e, {
79
+ className: `alert__close${x}`,
80
+ label: f,
81
+ onClick: C
71
82
  })]
72
83
  });
73
- }), l = Object.assign(c, {
84
+ }), u = Object.assign(l, {
74
85
  Title: o,
75
- Description: s
86
+ Description: s,
87
+ Actions: c
76
88
  });
77
89
  //#endregion
78
- export { l as Alert, s as AlertDescription, o as AlertTitle };
90
+ export { u as Alert, c as AlertActions, s as AlertDescription, o as AlertTitle };