@burdenoff/fe-libs 2026.1022.2 → 2026.1022.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.
@@ -12,6 +12,16 @@ export interface LanguageSwitcherProps {
12
12
  /** Show as icon only (for small screens) */
13
13
  compact?: boolean;
14
14
  }
15
+ /**
16
+ * Accessible name of the switcher's trigger button.
17
+ *
18
+ * WCAG 2.5.3 (Label in Name; axe `label-content-name-mismatch`): when the
19
+ * trigger shows the current language code, its accessible name must contain
20
+ * that visible text, so a speech-input user who says "click EN" hits it. The
21
+ * name therefore leads with the code ("EN, Change language"). The compact
22
+ * trigger shows only a globe icon, so its name stays the plain action label.
23
+ */
24
+ export declare function languageTriggerLabel(currentCode: string | undefined, changeLabel: string, compact: boolean): string;
15
25
  /**
16
26
  * LanguageSwitcher Component
17
27
  *
@@ -3,46 +3,50 @@ import { useAssistantControl as t } from "../shared/assistant/appControls/useAss
3
3
  import { useEffect as n, useMemo as r, useRef as i, useState as a } from "react";
4
4
  import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
5
5
  //#region src/chrome/LanguageSwitcher.tsx
6
- var l = ({ languages: l, currentLanguage: u, onLanguageChange: d, compact: f = !1 }) => {
7
- let p = e(), [m, h] = a(!1), g = i(null), _ = l.find((e) => e.code === u) || l[0], v = i({
8
- languages: l,
9
- currentLanguage: u,
10
- onLanguageChange: d
6
+ function l(e, t, n) {
7
+ let r = e?.trim().toUpperCase();
8
+ return n || !r ? t : `${r}, ${t}`;
9
+ }
10
+ var u = ({ languages: u, currentLanguage: d, onLanguageChange: f, compact: p = !1 }) => {
11
+ let m = e(), [h, g] = a(!1), _ = i(null), v = u.find((e) => e.code === d) || u[0], y = i({
12
+ languages: u,
13
+ currentLanguage: d,
14
+ onLanguageChange: f
11
15
  });
12
- v.current = {
13
- languages: l,
14
- currentLanguage: u,
15
- onLanguageChange: d
16
+ y.current = {
17
+ languages: u,
18
+ currentLanguage: d,
19
+ onLanguageChange: f
16
20
  };
17
- let y = d !== void 0;
18
- t("language", r(() => y ? {
19
- languages: () => v.current.languages.map((e) => ({
21
+ let b = f !== void 0;
22
+ t("language", r(() => b ? {
23
+ languages: () => y.current.languages.map((e) => ({
20
24
  code: e.code,
21
25
  label: e.nativeName || e.name
22
26
  })),
23
- current: () => v.current.currentLanguage,
27
+ current: () => y.current.currentLanguage,
24
28
  set: (e) => {
25
- let { languages: t, currentLanguage: n, onLanguageChange: r } = v.current, i = t.find((t) => t.code.toLowerCase() === e.toLowerCase());
29
+ let { languages: t, currentLanguage: n, onLanguageChange: r } = y.current, i = t.find((t) => t.code.toLowerCase() === e.toLowerCase());
26
30
  return !i || !r ? { status: "failed" } : i.code === n ? { status: "already" } : (r(i), { status: "done" });
27
31
  }
28
- } : null, [y])), n(() => {
32
+ } : null, [b])), n(() => {
29
33
  let e = (e) => {
30
- g.current && !g.current.contains(e.target) && h(!1);
34
+ _.current && !_.current.contains(e.target) && g(!1);
31
35
  };
32
- if (m) return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
33
- }, [m]);
34
- let b = (e) => {
35
- d && d(e), h(!1);
36
+ if (h) return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
37
+ }, [h]);
38
+ let x = (e) => {
39
+ f && f(e), g(!1);
36
40
  };
37
41
  return /* @__PURE__ */ c("div", {
38
42
  className: "relative",
39
- ref: g,
43
+ ref: _,
40
44
  children: [/* @__PURE__ */ s("button", {
41
- onClick: () => h(!m),
42
- className: `flex items-center gap-2 px-3 py-2 rounded-button hover:bg-bg-sunken transition-colors ${m ? "bg-bg-sunken" : ""}`,
43
- "aria-label": p("languageSwitcher.change", "Change language"),
44
- "aria-expanded": m,
45
- children: f ? /* @__PURE__ */ s("svg", {
45
+ onClick: () => g(!h),
46
+ className: `flex items-center gap-2 px-3 py-2 rounded-button hover:bg-bg-sunken transition-colors ${h ? "bg-bg-sunken" : ""}`,
47
+ "aria-label": l(v?.code, m("languageSwitcher.change", "Change language"), p),
48
+ "aria-expanded": h,
49
+ children: p ? /* @__PURE__ */ s("svg", {
46
50
  className: "size-5 text-text-primary",
47
51
  fill: "none",
48
52
  stroke: "currentColor",
@@ -55,9 +59,9 @@ var l = ({ languages: l, currentLanguage: u, onLanguageChange: d, compact: f = !
55
59
  })
56
60
  }) : /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s("span", {
57
61
  className: "text-body-sm font-medium text-text-primary uppercase",
58
- children: _?.code
62
+ children: v?.code.toUpperCase()
59
63
  }), /* @__PURE__ */ s("svg", {
60
- className: `w-4 h-4 text-text-secondary transition-transform ${m ? "rotate-180" : ""}`,
64
+ className: `w-4 h-4 text-text-secondary transition-transform ${h ? "rotate-180" : ""}`,
61
65
  fill: "none",
62
66
  stroke: "currentColor",
63
67
  viewBox: "0 0 24 24",
@@ -68,19 +72,19 @@ var l = ({ languages: l, currentLanguage: u, onLanguageChange: d, compact: f = !
68
72
  d: "M19 9l-7 7-7-7"
69
73
  })
70
74
  })] })
71
- }), m && /* @__PURE__ */ c("div", {
75
+ }), h && /* @__PURE__ */ c("div", {
72
76
  className: "absolute right-0 mt-2 w-[calc(100vw-1.5rem)] sm:w-64 max-w-64 bg-bg-elevated border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] z-50 py-2 overflow-hidden animate-in fade-in-0 zoom-in-95 slide-in-from-top-2 duration-200",
73
77
  children: [/* @__PURE__ */ s("div", {
74
78
  className: "px-4 py-2 border-b border-border-subtle",
75
79
  children: /* @__PURE__ */ s("p", {
76
80
  className: "text-[11px] font-medium text-text-muted uppercase tracking-wider",
77
- children: p("languageSwitcher.title", "Select Language")
81
+ children: m("languageSwitcher.title", "Select Language")
78
82
  })
79
83
  }), /* @__PURE__ */ s("div", {
80
84
  className: "py-1 max-h-96 overflow-y-auto",
81
- children: l.map((e) => /* @__PURE__ */ c("button", {
82
- onClick: () => b(e),
83
- className: `w-full flex items-center gap-3 px-4 py-2.5 text-body-sm transition-colors ${e.code === u ? "bg-[var(--color-accent-soft)] text-text-primary font-medium" : "text-text-primary hover:bg-bg-sunken"}`,
85
+ children: u.map((e) => /* @__PURE__ */ c("button", {
86
+ onClick: () => x(e),
87
+ className: `w-full flex items-center gap-3 px-4 py-2.5 text-body-sm transition-colors ${e.code === d ? "bg-[var(--color-accent-soft)] text-text-primary font-medium" : "text-text-primary hover:bg-bg-sunken"}`,
84
88
  children: [
85
89
  e.flag && /* @__PURE__ */ s("span", {
86
90
  className: "text-xl flex-shrink-0",
@@ -96,7 +100,7 @@ var l = ({ languages: l, currentLanguage: u, onLanguageChange: d, compact: f = !
96
100
  children: e.name
97
101
  })]
98
102
  }),
99
- e.code === u && /* @__PURE__ */ s("svg", {
103
+ e.code === d && /* @__PURE__ */ s("svg", {
100
104
  className: "size-5 text-action-primary-bg flex-shrink-0",
101
105
  fill: "currentColor",
102
106
  viewBox: "0 0 20 20",
@@ -113,4 +117,4 @@ var l = ({ languages: l, currentLanguage: u, onLanguageChange: d, compact: f = !
113
117
  });
114
118
  };
115
119
  //#endregion
116
- export { l as LanguageSwitcher };
120
+ export { u as LanguageSwitcher };
@@ -83,7 +83,7 @@ function g({ children: n, defaultMode: r = "system", defaultBrand: l = "default"
83
83
  let n = document.documentElement;
84
84
  if (n.setAttribute("data-theme", P), n.setAttribute("data-brand", t(x)), n.setAttribute("data-density", L), n.setAttribute("data-typography", R), n.setAttribute("data-glass", z), n.toggleAttribute("data-reduce-motion", B), I === "dark" ? n.classList.add("dark") : n.classList.remove("dark"), N) {
85
85
  let e = N.accent;
86
- n.style.setProperty("--color-action-primary-bg", e), n.style.setProperty("--color-action-primary-bgHover", `color-mix(in oklab, ${e} 84%, black)`), n.style.setProperty("--color-action-primary-bgActive", `color-mix(in oklab, ${e} 70%, black)`), p(e) ? n.style.setProperty("--color-action-primary-text", "#0a0a0a") : n.style.removeProperty("--color-action-primary-text");
86
+ n.style.setProperty("--color-action-primary-bg", e), n.style.setProperty("--color-action-primary-bgHover", `color-mix(in oklab, ${e} 84%, black)`), n.style.setProperty("--color-action-primary-bgActive", `color-mix(in oklab, ${e} 70%, black)`), n.style.setProperty("--color-action-primary-text", p(e) ? "#0a0a0a" : "#ffffff");
87
87
  } else n.style.removeProperty("--color-action-primary-bg"), n.style.removeProperty("--color-action-primary-bgHover"), n.style.removeProperty("--color-action-primary-bgActive"), n.style.removeProperty("--color-action-primary-text");
88
88
  let r = () => {
89
89
  let t = getComputedStyle(n).getPropertyValue("--color-bg-canvas").trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fe-libs",
3
- "version": "2026.1022.2",
3
+ "version": "2026.1022.3",
4
4
  "description": "Burdenoff frontend primitives and domain libraries",
5
5
  "type": "module",
6
6
  "bin": {
@@ -330,7 +330,7 @@
330
330
  "test:package-livekit-optional": "node scripts/validate-livekit-optional.mjs",
331
331
  "verify:package": "node scripts/verify-package-contents.mjs",
332
332
  "test:handsfree-browser": "node scripts/handsfree-browser-run.mjs",
333
- "test": "bun test scripts/graphql-schema-drift/__tests__ scripts/a11y-audit/__tests__ scripts/i18n/__tests__ src/credentials src/connections src/storage src/shared/assistant src/shared/feature-flags src/platform-pages src/admin-overview src/shared/components src/shared/graphql/links/sse-link.test.ts src/shared/graphql/fetch.test.ts src/shared/graphql/prepareWorkspaceRequest.test.ts src/shared/graphql/client.test.ts src/shared/utils/apollo.test.ts src/shared/utils/canvasTokens.test.ts src/shared/graphql/tourWorkspaceAuth.test.ts src/shared/config/gatewayUrls.test.ts src/shared/config/authBridgeUrls.test.ts src/shared/config/shellRoutes.test.ts src/shared/config/gatewayFallback.test.ts scripts/__tests__ src/shared/config/products.test.ts src/shared/config/docsUrls.test.ts src/shared/config/productSlug.test.ts src/shared/hooks/shell/useProducts.test.ts src/shared/hooks/shell/useGlobalSearch.test.ts src/shared/providers/shell/brandAttribute.test.ts src/chrome/ProductSwitcher.test.ts src/shared/utils/backendErrors.test.ts src/shared/utils/gatewayFetchRewrite.test.ts src/shared/utils/authCookie.test.ts src/shared/utils/redirectAllowlist.test.ts src/shared/providers/shell/ProfileStorageManager.test.ts src/shared/providers/AppShellProvider.test.ts src/shared/providers/shell/interpolate.test.ts src/shared/providers/shell/resolveTranslation.test.ts src/shared/providers/shell/I18nProvider.render.test.tsx src/shared/providers/shell/I18nProvider.client.test.tsx src/shared/providers/shell/translationStore.test.ts src/shared/providers/shell/translationLayers.test.ts src/shared/providers/shell/directionMode.test.ts src/shared/providers/shell/authProvider/ssoRelay.test.ts src/shared/providers/shell/authProvider/useAuthActions.signOut.test.tsx src/shared/providers/shell/personaSurfaceRemote.test.ts src/shared/providers/shell/workspaceSwitch.client.test.tsx src/shared/events/sinks/RybbitSink.test.ts src/shared/events/sinks/RybbitSink.client.test.tsx src/shell-native src/shared/native src/shared/navigation src/shared/tours src/chrome/__tests__ src/ui/__tests__",
333
+ "test": "bun test scripts/graphql-schema-drift/__tests__ scripts/a11y-audit/__tests__ scripts/i18n/__tests__ src/credentials src/connections src/storage src/shared/assistant src/shared/feature-flags src/platform-pages src/admin-overview src/shared/components src/shared/graphql/links/sse-link.test.ts src/shared/graphql/fetch.test.ts src/shared/graphql/prepareWorkspaceRequest.test.ts src/shared/graphql/client.test.ts src/shared/utils/apollo.test.ts src/shared/utils/canvasTokens.test.ts src/shared/utils/brandContrast.test.ts src/shared/graphql/tourWorkspaceAuth.test.ts src/shared/config/gatewayUrls.test.ts src/shared/config/authBridgeUrls.test.ts src/shared/config/shellRoutes.test.ts src/shared/config/gatewayFallback.test.ts scripts/__tests__ src/shared/config/products.test.ts src/shared/config/docsUrls.test.ts src/shared/config/productSlug.test.ts src/shared/hooks/shell/useProducts.test.ts src/shared/hooks/shell/useGlobalSearch.test.ts src/shared/providers/shell/brandAttribute.test.ts src/chrome/ProductSwitcher.test.ts src/shared/utils/backendErrors.test.ts src/shared/utils/gatewayFetchRewrite.test.ts src/shared/utils/authCookie.test.ts src/shared/utils/redirectAllowlist.test.ts src/shared/providers/shell/ProfileStorageManager.test.ts src/shared/providers/AppShellProvider.test.ts src/shared/providers/shell/interpolate.test.ts src/shared/providers/shell/resolveTranslation.test.ts src/shared/providers/shell/I18nProvider.render.test.tsx src/shared/providers/shell/I18nProvider.client.test.tsx src/shared/providers/shell/translationStore.test.ts src/shared/providers/shell/translationLayers.test.ts src/shared/providers/shell/directionMode.test.ts src/shared/providers/shell/authProvider/ssoRelay.test.ts src/shared/providers/shell/authProvider/useAuthActions.signOut.test.tsx src/shared/providers/shell/personaSurfaceRemote.test.ts src/shared/providers/shell/workspaceSwitch.client.test.tsx src/shared/events/sinks/RybbitSink.test.ts src/shared/events/sinks/RybbitSink.client.test.tsx src/shell-native src/shared/native src/shared/navigation src/shared/tours src/chrome/__tests__ src/ui/__tests__",
334
334
  "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run lint:sanity && bun run format && bun run type:check && bun run test && bun run build && bun run verify:package'",
335
335
  "switch:status": "bun scripts/deps-mode.ts status",
336
336
  "switch:remote": "bun scripts/deps-mode.ts remote",
@@ -1,6 +1,25 @@
1
- /* FluidGrids brand overrides - Auto-generated */
1
+ /* FluidGrids brand overrides
2
+ *
3
+ * WCAG 2.1 AA (1.4.3), parity G23. The accent is both the primary-button fill
4
+ * (white label) and, through --color-accent-primary, small accent text on the
5
+ * canvas, so one cyan cannot serve both themes:
6
+ * - light: cyan-700 #0E7490 carries a white label at 5.36:1. The former
7
+ * cyan-600 #0891B2 measured 3.68:1 and failed axe color-contrast on every
8
+ * primary button of the landing page.
9
+ * - dark: cyan-600 stays (5.2:1 as accent text on the #0A0E16 canvas), and
10
+ * the button label turns dark (5.7:1); hover/active step LIGHTER so the
11
+ * dark label keeps its contrast in every state.
12
+ */
2
13
  [data-brand="fluidgrids"] {
14
+ --color-action-primary-bg: #0E7490;
15
+ --color-action-primary-bgHover: #155E75;
16
+ --color-action-primary-bgActive: #164E63;
17
+ }
18
+
19
+ :root.dark[data-brand="fluidgrids"],
20
+ [data-brand="fluidgrids"].dark {
3
21
  --color-action-primary-bg: #0891B2;
4
- --color-action-primary-bgHover: #0E7490;
5
- --color-action-primary-bgActive: #155E75;
22
+ --color-action-primary-bgHover: #06B6D4;
23
+ --color-action-primary-bgActive: #22D3EE;
24
+ --color-action-primary-text: #0A0E16;
6
25
  }