@studiolxd/brand 49.9.0 → 49.10.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,60 @@ 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
+ ## [49.10.0] — 2026-09-18
11
+
12
+ > **Minor.** La suite marca productos como «próximamente»: se ven en su sitio pero apagados,
13
+ > sin navegación y con un distintivo. Ni la baldosa del `AppLauncher` ni el ítem del `SiteNav`
14
+ > lo admitían.
15
+
16
+ ### La entrada apagada con distintivo, en el lanzador y en el índice del sitio
17
+
18
+ `LauncherApp` y `SiteNavItem` ganan las mismas dos claves, `disabled` y `badge`.
19
+
20
+ Con **`disabled`** la entrada se ve en su sitio pero **no navega**: deja de ser un `<a>` —en
21
+ `SiteNav`, ni siquiera pasa por `renderLink`: no hay destino que dar—, se pinta como un enlace
22
+ inactivo (`<span role="link" aria-disabled="true">`, donde el atributo sí es válido y el lector
23
+ lo anuncia como no disponible) y **sin `tabIndex`**, así que el tabulador la salta. La tinta
24
+ apagada es la de un control deshabilitado del sistema (`color.disabled.text-on-light|dark`), que
25
+ no baja de 3:1 sobre la superficie — el mínimo de WCAG 1.4.11 para un componente inactivo, y el
26
+ motivo de no inventar un gris más claro. Tampoco se rellena ni subraya bajo el puntero: el
27
+ `:hover` alcanza también a un `<span>`, de ahí el `:not()` en las reglas de estado.
28
+
29
+ **`badge`** es el distintivo, y su texto llega **ya traducido** desde la aplicación: es
30
+ contenido —como el nombre de la app o el rótulo del ítem—, no cromo. Vale con `disabled`
31
+ («Próximamente») y sin él («Beta»), y sobre una entrada apagada va en `Tag neutral` en vez de
32
+ `info`: una píldora de novedad sobre algo que no lleva a ningún sitio se leería como disponible.
33
+ En `SiteNav` el distintivo es **hermano del enlace**, no hijo suyo — dentro, la línea de hover
34
+ (que en el DS es una línea bajo el elemento, no `text-decoration`) cruzaría también la píldora,
35
+ y el nombre accesible del enlace arrastraría el estado.
36
+
37
+ `badge` **generaliza `isNew`** del lanzador, que es este mismo distintivo con el texto del
38
+ catálogo (`appLauncher.new`). `isNew` **sigue funcionando y no se retira**; con los dos puestos,
39
+ manda `badge`.
40
+
41
+ ### Los `exports` declaran `default`, para que un Node CommonJS pueda `require()`
42
+
43
+ Las 170 subrutas condicionales de `package.json#exports` llevaban solo `types` e `import`.
44
+ `import` casa únicamente con la condición de importación, así que un Node CommonJS —los
45
+ workers de la suite empaquetados con esbuild en formato cjs, o un `tsx` cualquiera— que hiciera
46
+ `require('@studiolxd/brand/email')` moría con `ERR_PACKAGE_PATH_NOT_EXPORTED`. Ahora cada
47
+ subruta añade `default` apuntando **al mismo fichero ESM**, que es lo que Node necesita para
48
+ cargarlo por `require(esm)`: no se publica una segunda copia en CJS ni cambia el formato de
49
+ nada. Las subrutas que ya eran una cadena (CSS, SCSS, JSON, assets) no cambian — una cadena
50
+ vale para toda condición.
51
+
52
+ Lo vigilan dos guardianes, porque quedarse sin `default` no rompe ningún build: un test
53
+ (`scripts/exports-default.test.ts`, en `pnpm test`) y un paso de `release:check`, ambos
54
+ fallando si alguna subruta condicional se queda sin `default` o si `default` no apunta al mismo
55
+ fichero que `import`.
56
+
57
+ Tokens nuevos: `app-launcher.tile-disabled-color`, `site-nav.item-disabled-color`,
58
+ `site-nav.item-badge-gap` y sus pares `surface-dark-*`, todos por referencia a
59
+ `color.disabled.text-on-light|dark` — sin colores nuevos en la paleta.
60
+
61
+ De paso, la doc del `AppLauncher` pierde el apartado «Color de dato», que describía una prop
62
+ `accent` de `LauncherApp` retirada hace versiones.
63
+
10
64
  ## [49.9.0] — 2026-09-18
11
65
 
12
66
  > **Minor.** Tres huecos del sistema: la segunda línea del ítem de menú admite un nodo, el
@@ -214,6 +214,7 @@ var e = {
214
214
  "--app-launcher-tile-name-line-height": "1.3",
215
215
  "--app-launcher-tile-name-color": "#111e30",
216
216
  "--app-launcher-tile-name-active-font-weight": "500",
217
+ "--app-launcher-tile-disabled-color": "#4a4a4a",
217
218
  "--app-launcher-trigger-hover-bg": "#111e30",
218
219
  "--app-launcher-trigger-hover-color": "#ffffff",
219
220
  "--app-launcher-tile-highlighted-bg": "#111e30",
@@ -2888,6 +2889,8 @@ var e = {
2888
2889
  "--site-nav-item-hover-color": "#111e30",
2889
2890
  "--site-nav-item-underline-offset": "2px",
2890
2891
  "--site-nav-item-hover-line-width": "1px",
2892
+ "--site-nav-item-disabled-color": "#4a4a4a",
2893
+ "--site-nav-item-badge-gap": "0.5rem",
2891
2894
  "--site-nav-item-current-font-weight": "300",
2892
2895
  "--site-nav-focus-ring-width": "2px",
2893
2896
  "--site-nav-focus-ring-offset": "2px",
@@ -23,7 +23,26 @@ export interface LauncherApp {
23
23
  id: string;
24
24
  name: string;
25
25
  url: string;
26
+ /**
27
+ * Marca de app nueva. **`badge` lo generaliza**: `isNew` es el mismo
28
+ * distintivo con un único texto, el del catálogo (`appLauncher.new`). Se
29
+ * mantiene por compatibilidad y sigue valiendo; con `badge` puesto, manda
30
+ * `badge`.
31
+ */
26
32
  isNew?: boolean;
33
+ /**
34
+ * Producto anunciado y todavía no disponible: la baldosa se ve en su sitio
35
+ * pero **apagada y sin navegación** — no es un `<a>`, no tiene `href` que
36
+ * seguir y el tabulador no se detiene en ella.
37
+ */
38
+ disabled?: boolean;
39
+ /**
40
+ * Texto del distintivo de la baldosa («Próximamente», «Nuevo», «Beta»…).
41
+ * Llega **ya traducido** desde la aplicación: es contenido de la suite, como
42
+ * el nombre de la app, no cromo del lanzador. Generaliza `isNew`, que es
43
+ * este mismo distintivo con el texto del catálogo.
44
+ */
45
+ badge?: string;
27
46
  }
28
47
  export interface AppLauncherLabels {
29
48
  /**
@@ -10,6 +10,18 @@ export interface SiteNavItem {
10
10
  target?: string;
11
11
  /** Relación del enlace. Con `target="_blank"` y sin valor, se aplica `noopener noreferrer`. */
12
12
  rel?: string;
13
+ /**
14
+ * Producto anunciado y todavía no disponible: el ítem se ve en su sitio pero
15
+ * **apagado y sin navegación** — no es un enlace, no pasa por `renderLink` y
16
+ * el tabulador no se detiene en él.
17
+ */
18
+ disabled?: boolean;
19
+ /**
20
+ * Texto del distintivo junto al rótulo («Próximamente», «Nuevo», «Beta»…).
21
+ * Llega **ya traducido**: es contenido de ESTE sitio, como el propio rótulo,
22
+ * no cromo del índice.
23
+ */
24
+ badge?: string;
13
25
  }
14
26
  export interface SiteNavGroup {
15
27
  id: string;
@@ -1 +1 @@
1
- .app-launcher__trigger{all:unset;box-sizing:border-box;width:var(--app-launcher-trigger-size);height:var(--app-launcher-trigger-size);border-radius:var(--app-launcher-trigger-border-radius);color:var(--app-launcher-trigger-color);cursor:pointer;transition:box-shadow var(--app-launcher-transition-duration) var(--app-launcher-transition-easing);justify-content:center;align-items:center;display:flex}.app-launcher__trigger:hover{background-color:var(--app-launcher-trigger-hover-bg);color:var(--app-launcher-trigger-hover-color)}.app-launcher__trigger:focus-visible{outline:var(--app-launcher-focus-ring-width) solid var(--app-launcher-focus-ring-color);outline-offset:var(--app-launcher-focus-ring-offset)}.app-launcher__trigger--label{gap:var(--app-launcher-trigger-label-gap);width:auto;height:auto;padding-block:var(--app-launcher-trigger-label-padding-block);padding-inline:var(--app-launcher-trigger-label-padding-inline);border-radius:var(--app-launcher-trigger-label-border-radius);color:var(--app-launcher-trigger-label-color)}.app-launcher__trigger-label{font-size:var(--app-launcher-trigger-label-font-size);font-weight:var(--app-launcher-trigger-label-font-weight);line-height:var(--app-launcher-trigger-label-line-height)}.app-launcher__trigger--label:hover{color:var(--app-launcher-trigger-label-color);background-color:#0000}.app-launcher__content{box-sizing:border-box;background-color:var(--app-launcher-content-bg);border-width:var(--app-launcher-content-border-width);border-style:solid;border-color:var(--app-launcher-content-border-color);border-radius:var(--app-launcher-content-border-radius);box-shadow:var(--app-launcher-content-shadow);padding-block:var(--app-launcher-content-padding-block);padding-inline:var(--app-launcher-content-padding-inline);min-width:var(--app-launcher-content-min-width);transform-origin:var(--transform-origin);animation-duration:var(--app-launcher-transition-duration);animation-timing-function:var(--app-launcher-transition-easing);max-height:var(--app-launcher-content-max-height,var(--available-height));overscroll-behavior:contain;outline:none;overflow-y:auto}.app-launcher__content[data-open]{animation-name:app-launcher-in}.app-launcher__content[data-closed]{animation-name:app-launcher-out}@keyframes app-launcher-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes app-launcher-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}.app-launcher__grid{grid-template-columns:repeat(var(--app-launcher-content-columns), 1fr);gap:var(--app-launcher-content-gap);margin:0;padding:0;list-style:none;display:grid}.app-launcher__tile{box-sizing:border-box;align-items:center;gap:var(--app-launcher-tile-gap);padding-block:var(--app-launcher-tile-padding-block);padding-inline:var(--app-launcher-tile-padding-inline);border-radius:var(--app-launcher-tile-border-radius);box-shadow:none;text-align:start;cursor:pointer;transition:background-color var(--app-launcher-transition-duration) var(--app-launcher-transition-easing), box-shadow var(--app-launcher-transition-duration) var(--app-launcher-transition-easing);outline:none;text-decoration:none;display:flex}.app-launcher__tile:hover,.app-launcher__tile:focus-visible{background-color:var(--app-launcher-tile-highlighted-bg);color:var(--app-launcher-tile-highlighted-color)}.app-launcher__tile:hover .app-launcher__tile-name,.app-launcher__tile:focus-visible .app-launcher__tile-name{color:inherit}.app-launcher__tile:focus-visible{outline:var(--app-launcher-focus-ring-width) solid var(--app-launcher-focus-ring-color);outline-offset:var(--app-launcher-focus-ring-offset)}.app-launcher__tile--active .app-launcher__tile-name{font-weight:var(--app-launcher-tile-name-active-font-weight)}.app-launcher__tile-name{font-size:var(--app-launcher-tile-name-font-size);font-weight:var(--app-launcher-tile-name-font-weight);line-height:var(--app-launcher-tile-name-line-height);color:var(--app-launcher-tile-name-color)}.app-launcher__tile-badge{pointer-events:none}.app-launcher__tile:hover .app-launcher__tile-badge,.app-launcher__tile:focus-visible .app-launcher__tile-badge{--tag-info-bg:var(--app-launcher-tile-badge-hover-bg);--tag-info-color:var(--app-launcher-tile-badge-hover-color)}.app-launcher__positioner{z-index:var(--app-launcher-content-z-index)}
1
+ .app-launcher__trigger{all:unset;box-sizing:border-box;width:var(--app-launcher-trigger-size);height:var(--app-launcher-trigger-size);border-radius:var(--app-launcher-trigger-border-radius);color:var(--app-launcher-trigger-color);cursor:pointer;transition:box-shadow var(--app-launcher-transition-duration) var(--app-launcher-transition-easing);justify-content:center;align-items:center;display:flex}.app-launcher__trigger:hover{background-color:var(--app-launcher-trigger-hover-bg);color:var(--app-launcher-trigger-hover-color)}.app-launcher__trigger:focus-visible{outline:var(--app-launcher-focus-ring-width) solid var(--app-launcher-focus-ring-color);outline-offset:var(--app-launcher-focus-ring-offset)}.app-launcher__trigger--label{gap:var(--app-launcher-trigger-label-gap);width:auto;height:auto;padding-block:var(--app-launcher-trigger-label-padding-block);padding-inline:var(--app-launcher-trigger-label-padding-inline);border-radius:var(--app-launcher-trigger-label-border-radius);color:var(--app-launcher-trigger-label-color)}.app-launcher__trigger-label{font-size:var(--app-launcher-trigger-label-font-size);font-weight:var(--app-launcher-trigger-label-font-weight);line-height:var(--app-launcher-trigger-label-line-height)}.app-launcher__trigger--label:hover{color:var(--app-launcher-trigger-label-color);background-color:#0000}.app-launcher__content{box-sizing:border-box;background-color:var(--app-launcher-content-bg);border-width:var(--app-launcher-content-border-width);border-style:solid;border-color:var(--app-launcher-content-border-color);border-radius:var(--app-launcher-content-border-radius);box-shadow:var(--app-launcher-content-shadow);padding-block:var(--app-launcher-content-padding-block);padding-inline:var(--app-launcher-content-padding-inline);min-width:var(--app-launcher-content-min-width);transform-origin:var(--transform-origin);animation-duration:var(--app-launcher-transition-duration);animation-timing-function:var(--app-launcher-transition-easing);max-height:var(--app-launcher-content-max-height,var(--available-height));overscroll-behavior:contain;outline:none;overflow-y:auto}.app-launcher__content[data-open]{animation-name:app-launcher-in}.app-launcher__content[data-closed]{animation-name:app-launcher-out}@keyframes app-launcher-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes app-launcher-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}.app-launcher__grid{grid-template-columns:repeat(var(--app-launcher-content-columns), 1fr);gap:var(--app-launcher-content-gap);margin:0;padding:0;list-style:none;display:grid}.app-launcher__tile{box-sizing:border-box;align-items:center;gap:var(--app-launcher-tile-gap);padding-block:var(--app-launcher-tile-padding-block);padding-inline:var(--app-launcher-tile-padding-inline);border-radius:var(--app-launcher-tile-border-radius);box-shadow:none;text-align:start;cursor:pointer;transition:background-color var(--app-launcher-transition-duration) var(--app-launcher-transition-easing), box-shadow var(--app-launcher-transition-duration) var(--app-launcher-transition-easing);outline:none;text-decoration:none;display:flex}.app-launcher__tile:not(.app-launcher__tile--disabled):hover,.app-launcher__tile:focus-visible{background-color:var(--app-launcher-tile-highlighted-bg);color:var(--app-launcher-tile-highlighted-color)}.app-launcher__tile:not(.app-launcher__tile--disabled):hover .app-launcher__tile-name,.app-launcher__tile:focus-visible .app-launcher__tile-name{color:inherit}.app-launcher__tile:focus-visible{outline:var(--app-launcher-focus-ring-width) solid var(--app-launcher-focus-ring-color);outline-offset:var(--app-launcher-focus-ring-offset)}.app-launcher__tile--active .app-launcher__tile-name{font-weight:var(--app-launcher-tile-name-active-font-weight)}.app-launcher__tile.app-launcher__tile--disabled{cursor:default}.app-launcher__tile--disabled .app-launcher__tile-name{color:var(--app-launcher-tile-disabled-color)}.app-launcher__tile-name{font-size:var(--app-launcher-tile-name-font-size);font-weight:var(--app-launcher-tile-name-font-weight);line-height:var(--app-launcher-tile-name-line-height);color:var(--app-launcher-tile-name-color)}.app-launcher__tile-badge{pointer-events:none}.app-launcher__tile:not(.app-launcher__tile--disabled):hover .app-launcher__tile-badge,.app-launcher__tile:focus-visible .app-launcher__tile-badge{--tag-info-bg:var(--app-launcher-tile-badge-hover-bg);--tag-info-color:var(--app-launcher-tile-badge-hover-color)}.app-launcher__positioner{z-index:var(--app-launcher-content-z-index)}
@@ -10,19 +10,24 @@ import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
10
10
  import { Popover as l } from "@base-ui/react/popover";
11
11
  //#region src/stories/molecules/AppLauncher/AppLauncher.tsx
12
12
  function u({ app: t, isCurrent: n, newLabel: i }) {
13
- let a = e("appLauncher");
14
- return /* @__PURE__ */ c("a", {
13
+ let a = e("appLauncher"), l = t.badge ?? (t.isNew ? a("new", i) : void 0), u = /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s("span", {
14
+ className: "app-launcher__tile-name",
15
+ children: t.name
16
+ }), l && /* @__PURE__ */ s(r, {
17
+ variant: t.disabled ? "neutral" : "info",
18
+ className: "app-launcher__tile-badge",
19
+ children: l
20
+ })] });
21
+ return t.disabled ? /* @__PURE__ */ s("span", {
22
+ className: "app-launcher__tile app-launcher__tile--disabled",
23
+ role: "link",
24
+ "aria-disabled": "true",
25
+ children: u
26
+ }) : /* @__PURE__ */ s("a", {
15
27
  href: t.url,
16
28
  className: `app-launcher__tile${n ? " app-launcher__tile--active" : ""}`,
17
29
  "aria-current": n ? "page" : void 0,
18
- children: [/* @__PURE__ */ s("span", {
19
- className: "app-launcher__tile-name",
20
- children: t.name
21
- }), t.isNew && /* @__PURE__ */ s(r, {
22
- variant: "info",
23
- className: "app-launcher__tile-badge",
24
- children: a("new", i)
25
- })]
30
+ children: u
26
31
  });
27
32
  }
28
33
  function d({ apps: e, currentAppId: t, newLabel: n }) {