@dataimago/ui 0.1.0-alpha.16 → 0.1.0-alpha.17
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/dist/index.cjs +27 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -40
- package/dist/index.d.ts +49 -40
- package/dist/index.js +26 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -42,6 +42,7 @@ __export(index_exports, {
|
|
|
42
42
|
THEME_STORAGE_KEY: () => THEME_STORAGE_KEY,
|
|
43
43
|
ThemeScript: () => ThemeScript,
|
|
44
44
|
ThemeToggle: () => ThemeToggle,
|
|
45
|
+
nextThemePreference: () => nextThemePreference,
|
|
45
46
|
readThemePreference: () => readThemePreference,
|
|
46
47
|
resolveTheme: () => resolveTheme,
|
|
47
48
|
setThemePreference: () => setThemePreference,
|
|
@@ -388,6 +389,9 @@ function NavbarLogo({ name, width, style, ...rest }) {
|
|
|
388
389
|
);
|
|
389
390
|
}
|
|
390
391
|
|
|
392
|
+
// src/theme/ThemeToggle.tsx
|
|
393
|
+
var React5 = __toESM(require("react"), 1);
|
|
394
|
+
|
|
391
395
|
// src/theme/theme.ts
|
|
392
396
|
var React4 = __toESM(require("react"), 1);
|
|
393
397
|
var THEME_STORAGE_KEY = "dataimago-theme";
|
|
@@ -480,12 +484,19 @@ var DEFAULT_LABELS = {
|
|
|
480
484
|
light: "Light",
|
|
481
485
|
dark: "Dark"
|
|
482
486
|
};
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
+
function systemTheme() {
|
|
488
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
489
|
+
}
|
|
490
|
+
function nextThemePreference(shown, system) {
|
|
491
|
+
const next = shown === "dark" ? "light" : "dark";
|
|
492
|
+
return next === system ? "system" : next;
|
|
493
|
+
}
|
|
494
|
+
var noSubscribe = () => () => {
|
|
487
495
|
};
|
|
488
|
-
function
|
|
496
|
+
function useHydrated() {
|
|
497
|
+
return React5.useSyncExternalStore(noSubscribe, () => true, () => false);
|
|
498
|
+
}
|
|
499
|
+
function Icon({ theme }) {
|
|
489
500
|
const common = {
|
|
490
501
|
width: 20,
|
|
491
502
|
height: 20,
|
|
@@ -498,24 +509,21 @@ function Icon({ preference }) {
|
|
|
498
509
|
"aria-hidden": true,
|
|
499
510
|
focusable: false
|
|
500
511
|
};
|
|
501
|
-
if (
|
|
502
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...common, children: [
|
|
503
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "12", r: "4" }),
|
|
504
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" })
|
|
505
|
-
] });
|
|
506
|
-
}
|
|
507
|
-
if (preference === "dark") {
|
|
512
|
+
if (theme === "dark") {
|
|
508
513
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { ...common, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) });
|
|
509
514
|
}
|
|
510
515
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { ...common, children: [
|
|
511
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("
|
|
512
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "
|
|
516
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "12", r: "4" }),
|
|
517
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" })
|
|
513
518
|
] });
|
|
514
519
|
}
|
|
515
520
|
function ThemeToggle({ labels, className = "", style, ...rest }) {
|
|
516
521
|
const [preference, setPreference] = useThemePreference();
|
|
522
|
+
const resolved = useResolvedTheme();
|
|
523
|
+
const hydrated = useHydrated();
|
|
524
|
+
const shown = hydrated ? resolved : void 0;
|
|
517
525
|
const text = { ...DEFAULT_LABELS, ...labels };
|
|
518
|
-
const label = `${text.name}: ${text[
|
|
526
|
+
const label = shown ? `${text.name}: ${text[shown]}${preference === "system" ? ` (${text.system})` : ""}` : text.name;
|
|
519
527
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
520
528
|
"button",
|
|
521
529
|
{
|
|
@@ -524,6 +532,7 @@ function ThemeToggle({ labels, className = "", style, ...rest }) {
|
|
|
524
532
|
"aria-label": label,
|
|
525
533
|
title: label,
|
|
526
534
|
"data-theme-preference": preference,
|
|
535
|
+
"data-theme": shown,
|
|
527
536
|
style: {
|
|
528
537
|
alignItems: "center",
|
|
529
538
|
background: "var(--di-theme-toggle-bg, transparent)",
|
|
@@ -540,8 +549,8 @@ function ThemeToggle({ labels, className = "", style, ...rest }) {
|
|
|
540
549
|
...style
|
|
541
550
|
},
|
|
542
551
|
type: "button",
|
|
543
|
-
onClick: () => setPreference(
|
|
544
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, {
|
|
552
|
+
onClick: () => setPreference(nextThemePreference(resolveTheme(), systemTheme())),
|
|
553
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { display: "inline-flex", visibility: shown ? "visible" : "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { theme: shown ?? "light" }) })
|
|
545
554
|
}
|
|
546
555
|
);
|
|
547
556
|
}
|
|
@@ -564,6 +573,7 @@ function ThemeScript({ nonce }) {
|
|
|
564
573
|
THEME_STORAGE_KEY,
|
|
565
574
|
ThemeScript,
|
|
566
575
|
ThemeToggle,
|
|
576
|
+
nextThemePreference,
|
|
567
577
|
readThemePreference,
|
|
568
578
|
resolveTheme,
|
|
569
579
|
setThemePreference,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Button.tsx","../src/brand/LogoMonogram.tsx","../src/brand/LogoMonogramBg.tsx","../src/brand/LogoWordmark.tsx","../src/brand/Logo.tsx","../src/brand/BrandAnimation.tsx","../src/brand/NavbarLogo.tsx","../src/theme/theme.ts","../src/theme/ThemeToggle.tsx","../src/theme/ThemeScript.tsx"],"sourcesContent":["export { Button } from './Button.js';\nexport type { ButtonProps, ButtonVariant, ButtonSize } from './Button.js';\n\n// Brand assets (outlined-path SVG components — no font dependency at render).\n// See packages/ui/src/brand/Logos.stories.tsx for visual reference.\nexport { LogoMonogram } from './brand/LogoMonogram.js';\nexport type { LogoMonogramProps } from './brand/LogoMonogram.js';\nexport { LogoMonogramBg } from './brand/LogoMonogramBg.js';\nexport type { LogoMonogramBgProps } from './brand/LogoMonogramBg.js';\nexport { LogoWordmark } from './brand/LogoWordmark.js';\nexport type { LogoWordmarkProps } from './brand/LogoWordmark.js';\n\n// Manifest-driven marks from @dataimago/brand (the logo-foundry output).\nexport { Logo } from './brand/Logo.js';\nexport type { LogoProps } from './brand/Logo.js';\n\n// Decorative brand animations from @dataimago/brand (the animation-foundry).\n// Client component: enforces the prefers-reduced-motion poster fallback.\nexport { BrandAnimation } from './brand/BrandAnimation.js';\nexport type { BrandAnimationProps } from './brand/BrandAnimation.js';\n\n// Animated navbar marks from @dataimago/brand (the animation-foundry navbar set).\n// Client component: mount/hover replay + prefers-reduced-motion poster fallback.\nexport { NavbarLogo } from './brand/NavbarLogo.js';\nexport type { NavbarLogoProps } from './brand/NavbarLogo.js';\n\n// Light/dark theme: the reader's choice on <html data-bs-theme>, which the house\n// tokens obey over the operating system. Client component + hooks.\nexport { ThemeToggle } from './theme/ThemeToggle.js';\nexport type { ThemeToggleProps, ThemeToggleLabels } from './theme/ThemeToggle.js';\nexport { ThemeScript } from './theme/ThemeScript.js';\nexport type { ThemeScriptProps } from './theme/ThemeScript.js';\nexport {\n THEME_ATTRIBUTE,\n THEME_STORAGE_KEY,\n readThemePreference,\n resolveTheme,\n setThemePreference,\n themeInitScript,\n useResolvedTheme,\n useThemePreference,\n} from './theme/theme.js';\nexport type { ResolvedTheme, ThemePreference } from './theme/theme.js';\n","import * as React from 'react';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nconst variantClasses: Record<ButtonVariant, string> = {\n primary: 'di-ui-button--primary',\n secondary: 'di-ui-button--secondary',\n ghost: 'di-ui-button--ghost',\n};\n\nconst sizeClasses: Record<ButtonSize, string> = {\n sm: 'di-ui-button--sm',\n md: 'di-ui-button--md',\n lg: 'di-ui-button--lg',\n};\n\nconst baseStyle: React.CSSProperties = {\n alignItems: 'center',\n borderRadius: 'var(--radius-md, 0.5rem)',\n borderStyle: 'solid',\n borderWidth: '1px',\n cursor: 'pointer',\n display: 'inline-flex',\n fontFamily: 'var(--font-family-sans, system-ui, sans-serif)',\n fontWeight: 'var(--font-weight-medium, 500)',\n gap: 'var(--spacing-sm, 0.5rem)',\n justifyContent: 'center',\n textDecoration: 'none',\n transition: 'background-color 150ms ease, border-color 150ms ease, color 150ms ease',\n};\n\nconst variantStyles: Record<ButtonVariant, React.CSSProperties> = {\n primary: {\n backgroundColor: 'var(--color-interactive-primary)',\n borderColor: 'var(--color-interactive-primary)',\n color: 'var(--color-content-inverse)',\n },\n secondary: {\n backgroundColor: 'var(--color-surface-elevated)',\n borderColor: 'var(--color-border-moderate)',\n color: 'var(--color-content-primary)',\n },\n ghost: {\n backgroundColor: 'transparent',\n borderColor: 'transparent',\n color: 'var(--color-content-secondary)',\n },\n};\n\nconst sizeStyles: Record<ButtonSize, React.CSSProperties> = {\n sm: {\n fontSize: 'var(--font-size-small, 0.875rem)',\n minHeight: '2rem',\n padding: '0.375rem 0.75rem',\n },\n md: {\n fontSize: 'var(--font-size-base, 1rem)',\n minHeight: '2.5rem',\n padding: '0.625rem 1rem',\n },\n lg: {\n fontSize: 'var(--font-size-readable, 1.125rem)',\n minHeight: '3rem',\n padding: '0.75rem 1.25rem',\n },\n};\n\n/**\n * Smoke-level `<Button>` for the alpha release of @dataimago/ui.\n *\n * Styling is delegated to semantic CSS variables published by @dataimago/css\n * so that the component stays small and theme-aware.\n * Consumers that want to override visuals can pass their own className.\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n function Button(\n { variant = 'primary', size = 'md', className = '', style, type, children, ...rest },\n ref,\n ) {\n const classes = [\n 'di-ui-button',\n variantClasses[variant],\n sizeClasses[size],\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <button\n ref={ref}\n type={type ?? 'button'}\n className={classes}\n style={{\n ...baseStyle,\n ...variantStyles[variant],\n ...sizeStyles[size],\n ...style,\n }}\n {...rest}\n >\n {children}\n </button>\n );\n },\n);\n","import * as React from 'react';\n\n/**\n * <LogoMonogram /> — the dataimago 'a + i' superimposed monogram, transparent\n * background. Glyphs are outlined paths (no font dependency at render time);\n * fill defaults to `currentColor` so the mark inherits from the surrounding\n * text color and respects light/dark theme switches automatically.\n *\n * Symbolism: 'a' (data, agency) superimposed with 'i' (imago, AI) — the\n * fusion is the brand's central idea. See wiki/principles/visual-identity.md\n * (Stage L follow-up) for the full brand-mark rationale.\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * ai_monogram_supreme_1_PATH.svg\n *\n * Usage:\n * <LogoMonogram size={48} aria-label=\"dataimago\" />\n * <span style={{ color: 'var(--color-interactive-primary)' }}>\n * <LogoMonogram size={32} />\n * </span>\n */\n\nexport interface LogoMonogramProps extends React.SVGAttributes<SVGSVGElement> {\n /** Render size in px (or any CSS length). Default: 24. Width and height match. */\n size?: number | string;\n /**\n * Accessible name announced by screen readers. Set to `undefined` (or pass\n * `aria-hidden` directly) when the mark is purely decorative.\n */\n title?: string;\n}\n\nexport function LogoMonogram({\n size = 24,\n title = 'dataimago',\n ...rest\n}: LogoMonogramProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 480\"\n width={size}\n height={size}\n fill=\"currentColor\"\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* 'i' (imago) glyph */}\n <path d=\"m 337.3457,97.089844 q 0,8.906246 -6.49414,15.400386 -6.49414,6.49415 -15.58594,6.49415 -9.09179,0 -15.77148,-6.67969 Q 293,105.25391 293,96.162109 q 0,-9.091797 6.30859,-15.40039 6.49414,-6.494141 15.40039,-6.494141 9.0918,0 15.77149,6.865234 6.86523,6.679688 6.86523,15.957032 z M 324.54297,180.21484 303.01953,355 h -41.00586 l 21.52344,-174.78516 z\" />\n {/* 'a' (data) glyph */}\n <path d=\"M 325.70312,176.01562 303.04687,360 h -43.16406 l 2.53906,-19.92188 Q 234.6875,364.29687 205,364.29687 q -36.52344,0 -59.57031,-24.80468 -23.04688,-24.80469 -23.04688,-64.25782 0,-45.50781 26.36719,-74.60937 26.75781,-29.10156 68.55469,-29.10156 19.72656,0 33.59375,6.44531 14.0625,6.44531 28.71093,22.26562 l 2.92969,-24.21875 z m -52.53906,86.71875 q 0,-24.41406 -14.0625,-39.64843 -14.0625,-15.42969 -36.32812,-15.42969 -24.21875,0 -40.625,18.94531 -16.40625,19.14063 -16.40625,46.875 0,24.21875 13.67187,39.64844 13.67188,15.42969 35.15625,15.42969 23.82813,0 41.21094,-19.33594 17.38281,-19.72656 17.38281,-46.48438 z\" />\n </svg>\n );\n}\n","import * as React from 'react';\n\n/**\n * <LogoMonogramBg /> — the dataimago 'a + i' monogram with a rounded accent\n * backplate. White letters on the brand-accent role\n * (var(--color-interactive-accent), iteration-2 sky-blue).\n *\n * Use this variant on neutral surfaces where the no-background monogram\n * would lack presence — favicons, app icons, social-media avatars, or any\n * branded chrome where the mark should read as a self-contained badge.\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * ai_monogram_supreme_bg_1_PATH.svg\n *\n * Theming: the background uses var(--color-interactive-accent), which\n * resolves via theme-colors.json's mode-aware role mapping — accent-500\n * (#1f618d) on light surfaces, accent-300 (#3498DB) on dark. The letter\n * fill is hard-set to white; both accent stops have AA-or-better contrast\n * with white letters (light-mode 6.66:1 AA, dark-mode 3.15:1 AA-large for\n * the dot-and-stem heading-size letters used in the monogram). The\n * literal-fallback in this component is light-mode (#1f618d, sky-blue\n * iteration-2) for rare non-CSS-aware contexts. Iteration-1's copper-red\n * fallback (#b54a27) was retired in iteration-2-B.2; see\n * wiki/decisions/iteration-2-blue-accent.md for the rationale.\n *\n * Usage:\n * <LogoMonogramBg size={64} aria-label=\"dataimago\" />\n */\n\nexport interface LogoMonogramBgProps extends React.SVGAttributes<SVGSVGElement> {\n /** Render size in px. Default: 32 (favicon-sized). Width and height match. */\n size?: number | string;\n /** Accessible name. Set undefined or pass aria-hidden for decorative use. */\n title?: string;\n}\n\nexport function LogoMonogramBg({\n size = 32,\n title = 'dataimago',\n ...rest\n}: LogoMonogramBgProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 480\"\n width={size}\n height={size}\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* Rounded accent backplate — themed via --color-interactive-accent\n (mode-aware: accent-500 #1f618d on light, accent-300 #3498DB on dark).\n Literal fallback is the light-mode canonical for non-CSS consumers. */}\n <rect\n width=\"100%\"\n height=\"100%\"\n rx=\"40\"\n ry=\"40\"\n fill=\"var(--color-interactive-accent, #1f618d)\"\n />\n {/* 'a' (data) glyph in white */}\n <path\n fill=\"#ffffff\"\n d=\"M 325.70312,176.01562 303.04687,360 h -43.16406 l 2.53906,-19.92188 Q 234.6875,364.29687 205,364.29687 q -36.52344,0 -59.57031,-24.80468 -23.04688,-24.80469 -23.04688,-64.25782 0,-45.50781 26.36719,-74.60937 26.75781,-29.10156 68.55469,-29.10156 19.72656,0 33.59375,6.44531 14.0625,6.44531 28.71093,22.26562 l 2.92969,-24.21875 z m -52.53906,86.71875 q 0,-24.41406 -14.0625,-39.64843 -14.0625,-15.42969 -36.32812,-15.42969 -24.21875,0 -40.625,18.94531 -16.40625,19.14063 -16.40625,46.875 0,24.21875 13.67187,39.64844 13.67188,15.42969 35.15625,15.42969 23.82813,0 41.21094,-19.33594 17.38281,-19.72656 17.38281,-46.48438 z\"\n />\n {/* 'i' (imago) glyph in white */}\n <path\n fill=\"#ffffff\"\n d=\"m 337.3457,97.089844 q 0,8.906246 -6.49414,15.400386 -6.49414,6.49415 -15.58594,6.49415 -9.09179,0 -15.77148,-6.67969 Q 293,105.25391 293,96.162109 q 0,-9.091797 6.30859,-15.40039 6.49414,-6.494141 15.40039,-6.494141 9.0918,0 15.77149,6.865234 6.86523,6.679688 6.86523,15.957032 z M 324.54297,180.21484 303.01953,355 h -41.00586 l 21.52344,-174.78516 z\"\n />\n </svg>\n );\n}\n","import * as React from 'react';\n\n/**\n * <LogoWordmark /> — the full \"dataimago\" wordmark with the 'ai' overlap.\n * White letters on a rounded accent backplate, themed via\n * var(--color-interactive-accent).\n *\n * Use this variant for primary brand surfaces: site headers, document\n * letterheads, large-format presentations, business materials. For compact\n * contexts (favicons, social avatars) use <LogoMonogramBg /> instead; for\n * inline use within prose use <LogoMonogram /> with currentColor.\n *\n * The wordmark preserves the philosophical idea of the brand: the visible\n * superposition of 'a' (data, human agency) and 'i' (imago, AI) creates a new\n * unified glyph that is neither pure human nor pure artificial — genuine\n * collaboration. See wiki/principles/visual-identity.md (Stage L follow-up).\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * dataimago_supreme_1_PATH.svg\n *\n * Theming: the backplate uses var(--color-interactive-accent), which resolves\n * via theme-colors.json's mode-aware role mapping — accent-500 (#1f618d) on\n * light surfaces, accent-300 (#3498DB) on dark. The literal-fallback in this\n * component is light-mode (#1f618d, sky-blue iteration-2) for the rare cases\n * where the SVG is consumed without CSS-variable resolution (printed PDFs from\n * raw SVG, third-party platforms that strip CSS). Iteration-1's copper-red\n * fallback (#b54a27) was retired in iteration-2-B.2; see\n * wiki/decisions/iteration-2-blue-accent.md for the rationale.\n *\n * Usage:\n * <LogoWordmark width={240} aria-label=\"dataimago\" />\n *\n * The wordmark's natural aspect ratio is 480x150 (~3.2:1). Pass `width` and\n * height auto-scales, or pass both explicitly.\n */\n\nexport interface LogoWordmarkProps extends React.SVGAttributes<SVGSVGElement> {\n /** Width in px. Default: 240. Height auto-scales to the 480:150 ratio. */\n width?: number | string;\n /** Accessible name. Set undefined or pass aria-hidden for decorative use. */\n title?: string;\n}\n\nexport function LogoWordmark({\n width = 240,\n title = 'dataimago',\n ...rest\n}: LogoWordmarkProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 150\"\n width={width}\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* Rounded accent backplate — themed via --color-interactive-accent\n (mode-aware: accent-500 #1f618d on light, accent-300 #3498DB on dark).\n Literal fallback is the light-mode canonical for non-CSS consumers. */}\n <rect\n width=\"100%\"\n height=\"100%\"\n rx=\"5\"\n ry=\"5\"\n fill=\"var(--color-interactive-accent, #1f618d)\"\n />\n {/* \"data\" — outlined glyphs, white fill */}\n <path\n fill=\"#ffffff\"\n d=\"M 104.80469,35.621094 97.492187,95 h -7.734375 l 0.421875,-3.550781 q -2.882812,2.566406 -4.992187,3.480468 -2.074219,0.84375 -5.132813,0.84375 -6.960937,0 -11.390625,-4.605468 -4.429687,-4.640625 -4.429687,-11.953125 0,-7.804688 4.851562,-12.972657 4.851563,-5.167968 12.199219,-5.167968 3.761719,0 6.328125,1.300781 2.671875,1.335937 5.589844,4.535156 L 97.035156,35.621094 Z M 92.007812,77 q 0,-4.21875 -2.601562,-6.820313 -2.601563,-2.601562 -6.820313,-2.601562 -4.570312,0 -7.558593,3.410156 -2.988282,3.410156 -2.988282,8.613281 0,4.394532 2.566407,7.066407 2.566406,2.636718 6.820312,2.636718 4.429688,0 7.488281,-3.585937 Q 92.007812,82.203125 92.007812,77 Z M 145.51562,61.882812 141.4375,95 h -7.76953 l 0.45703,-3.585938 q -4.99219,4.359375 -10.33594,4.359375 -6.57422,0 -10.72265,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.74609,-13.429687 4.81641,-5.238281 12.33985,-5.238281 3.55078,0 6.04687,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52734,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53906,-2.777344 -4.35937,0 -7.3125,3.410156 -2.95312,3.445313 -2.95312,8.4375 0,4.359375 2.46093,7.136719 2.46094,2.777344 6.32813,2.777344 4.28906,0 7.41797,-3.480469 3.1289,-3.550781 3.1289,-8.367188 z M 163.79687,69.089844 160.59766,95 h -7.76954 l 3.19922,-25.910156 h -4.39453 l 0.87891,-7.207032 h 4.39453 l 1.89844,-15.363281 h 7.76953 l -1.89844,15.363281 h 7.17188 l -0.87891,7.207032 z M 211.29297,61.882812 207.21484,95 h -7.76953 l 0.45703,-3.585938 q -4.99218,4.359375 -10.33593,4.359375 -6.57422,0 -10.72266,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.7461,-13.429687 4.8164,-5.238281 12.33984,-5.238281 3.55078,0 6.04687,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52735,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53907,-2.777344 -4.35937,0 -7.3125,3.410156 -2.95312,3.445313 -2.95312,8.4375 0,4.359375 2.46094,7.136719 2.46093,2.777344 6.32812,2.777344 4.28906,0 7.41797,-3.480469 3.12891,-3.550781 3.12891,-8.367188 z\"\n />\n {/* \"imago\" — outlined glyphs, white fill, superimposed at the 'a/i' overlap */}\n <path\n fill=\"#ffffff\"\n d=\"m 213.40234,46.132812 q 0,1.6875 -1.23047,2.917969 -1.23046,1.230469 -2.95312,1.230469 -1.72266,0 -2.98828,-1.265625 Q 205,47.679687 205,45.957031 q 0,-1.722656 1.19531,-2.917969 1.23047,-1.230468 2.91797,-1.230468 1.72266,0 2.98828,1.300781 1.30078,1.265625 1.30078,3.023437 z m -2.42578,15.75 L 206.89844,95 h -7.76953 l 4.07812,-33.117188 z m 18.87891,0 -0.35156,3.09375 q 3.9375,-3.9375 9.10546,-3.9375 3.12891,0 4.81641,1.160157 1.6875,1.089843 3.02344,4.113281 4.92187,-5.238281 10.61719,-5.238281 9.84375,0 9.84375,9.035156 0,0.914062 -0.17579,2.566406 -0.14062,1.652344 -0.42187,4.078125 L 264.0625,95 h -7.76953 l 2.39062,-19.160156 Q 259,73.167969 259,71.902344 q 0,-4.324219 -4.35938,-4.324219 -1.72265,0 -2.98828,0.527344 -1.26562,0.492187 -2.17968,1.582031 -0.87891,1.054687 -1.47657,2.777344 -0.5625,1.6875 -0.8789,4.042968 L 244.86719,95 h -7.76953 l 2.42578,-19.652344 q 0.3164,-2.671875 0.3164,-3.65625 0,-4.113281 -4.46484,-4.113281 -6.46875,0 -7.55859,8.964844 L 225.53125,95 h -7.76953 l 4.07812,-33.117188 z m 81.63281,0 L 307.41016,95 h -7.76954 l 0.45704,-3.585938 q -4.99219,4.359375 -10.33594,4.359375 -6.57422,0 -10.72266,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.7461,-13.429687 4.8164,-5.238281 12.33984,-5.238281 3.55078,0 6.04688,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52734,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53906,-2.777344 -4.35938,0 -7.3125,3.410156 -2.95313,3.445313 -2.95313,8.4375 0,4.359375 2.46094,7.136719 2.46094,2.777344 6.32812,2.777344 4.28907,0 7.41797,-3.480469 3.12891,-3.550781 3.12891,-8.367188 z m 53.57812,-15.609375 -4.07812,33.363282 q -0.38672,2.988281 -0.87891,5.203126 -0.45703,2.21484 -1.05468,3.72656 -1.23047,3.09375 -3.65625,5.3086 -4.71094,4.21875 -12.23438,4.21875 -7.76953,0 -12.58594,-4.07813 -2.46093,-2.03906 -3.44531,-4.64062 -0.98437,-2.39063 -0.98437,-6.714849 h 7.52343 q 0.28125,4.675779 2.39063,6.785159 2.10937,2.14453 6.53906,2.14453 4.78125,0 7.27734,-2.56641 2.4961,-2.53125 3.16407,-8.085935 l 0.5625,-4.535156 q -4.92188,3.761718 -10.33594,3.761718 -6.75,0 -11.14453,-4.605468 -4.39453,-4.710938 -4.39453,-11.742188 0,-7.910156 5.09765,-13.113281 5.13282,-5.238281 12.83203,-5.238281 3.69141,0 5.97657,1.089843 2.49609,1.054688 5.16797,3.832032 l 0.49218,-4.113282 z m -8.92968,15.714844 q 0,-4.5 -2.74219,-7.242187 -2.74219,-2.777344 -7.17188,-2.777344 -4.60546,0 -7.66406,3.375 -3.02344,3.339844 -3.02344,8.402344 0,4.429687 2.60157,7.242187 2.63672,2.707031 6.96093,2.707031 4.81641,0 7.91016,-3.304687 3.12891,-3.339844 3.12891,-8.402344 z m 52.3125,0.527344 q 0,7.59375 -5.27344,12.761719 -5.23828,5.167968 -12.9375,5.167968 -8.05078,0 -13.14844,-4.886718 Q 362.5,86.246094 362.5,78.722656 q 0,-7.699219 5.30859,-12.796875 5.34375,-5.132812 13.35938,-5.132812 7.875,0 12.83203,4.851562 4.99219,4.78125 4.99219,12.480469 z m -7.83985,-0.386719 q 0,-4.78125 -2.77734,-7.558594 -2.77734,-2.882812 -7.27734,-2.882812 -4.67579,0 -7.73438,3.199219 -3.05859,3.234375 -3.05859,8.121093 0,4.921875 2.88281,7.910157 2.88281,3.058593 7.45312,3.058593 4.57032,0 7.52344,-3.339843 2.98828,-3.304688 2.98828,-8.507813 z\"\n />\n </svg>\n );\n}\n","import * as React from 'react';\nimport { svgs, type LogoId, type LogoVariant, type LogoKey } from '@dataimago/brand';\n\nexport interface LogoProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Mark id, e.g. `dissertation_ai_OVERLAP`. */\n name: LogoId;\n /** PATH (default), CUTOUT, BG, or LETTERS. See @dataimago/brand. */\n variant?: LogoVariant;\n /**\n * Render width (px number or any CSS length). Height scales proportionally\n * via the SVG viewBox. Omit to use the mark's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * Manifest-driven brand mark from `@dataimago/brand`. Inlines the SVG string so\n * `currentColor` (PATH/LETTERS), the `--color-interactive-accent` badge fill\n * (CUTOUT/BG), and the per-letter interaction classes (`.glyph`, `.logo-*`) all\n * work — an `<img src>` can't receive those interior styles.\n *\n * Color flows from CSS: set `color` (PATH/LETTERS) on this element or an\n * ancestor. Size via `width` (or CSS on the wrapper).\n */\nexport function Logo({ name, variant = 'PATH', width, style, ...rest }: LogoProps) {\n const key = `${name}_${variant}` as LogoKey;\n let markup = svgs[key];\n\n if (!markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <Logo>: unknown mark \"${key}\".`);\n return null;\n }\n\n if (width != null) {\n // Set width + drop height so the viewBox preserves the aspect ratio.\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n // The SVG is generated, font-free, and carries its own <title>/aria-label.\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport {\n animations,\n animationManifest,\n type AnimationId,\n type LogoId,\n type LogoVariant,\n} from '@dataimago/brand';\nimport { Logo } from './Logo';\n\nexport interface BrandAnimationProps\n extends React.HTMLAttributes<HTMLSpanElement> {\n /** Animation id, e.g. `dissertation-ai`. See @dataimago/brand animations.json. */\n name: AnimationId;\n /**\n * Render width (px number or any CSS length). Height scales via the SVG\n * viewBox. Omit to use the animation's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * SSR-safe `prefers-reduced-motion` detection. Starts `true` (reduced) so the\n * server and the first client render agree on the static poster — no hydration\n * mismatch, and no motion flashes before JS decides. After mount it reflects the\n * real media query and updates live if the user toggles the OS setting.\n */\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(true);\n React.useEffect(() => {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReduced(mq.matches);\n const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/**\n * Decorative brand animation from `@dataimago/brand` (the animation-foundry).\n * Inlines the pure-SMIL SVG string so its interior `<style>` works — an\n * `<img src>` can't receive those styles (same rationale as `<Logo>`).\n *\n * Motion-dignity: decorative brand animation is a distinct category from the\n * 300ms UI-transition ceiling, but it is contractually gated on\n * `prefers-reduced-motion`. When the user prefers reduced motion this renders\n * the static brand mark the animation settles to (its `poster`) via `<Logo>`,\n * so no consumer can ship the motion without the fallback.\n */\nexport function BrandAnimation({\n name,\n width,\n style,\n ...rest\n}: BrandAnimationProps) {\n const reduced = usePrefersReducedMotion();\n const entry = animationManifest.find((a) => a.id === name);\n let markup = animations[name];\n\n if (!entry || !markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <BrandAnimation>: unknown animation \"${name}\".`);\n return null;\n }\n\n if (reduced) {\n // Static settle-frame: the brand mark the animation resolves to.\n return (\n <Logo\n name={entry.poster.id as LogoId}\n variant={entry.poster.variant as LogoVariant}\n width={width}\n style={style}\n {...rest}\n />\n );\n }\n\n if (width != null) {\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport {\n navbarLogos,\n navbarManifest,\n type NavbarLogoId,\n type LogoId,\n type LogoVariant,\n} from '@dataimago/brand';\nimport { Logo } from './Logo';\n\nexport interface NavbarLogoProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Navbar mark id, e.g. `dissertation-ai`. See @dataimago/brand navbar.json. */\n name: NavbarLogoId;\n /**\n * Render width (px number or any CSS length). Height scales via the SVG\n * viewBox. Omit to use the mark's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * SSR-safe `prefers-reduced-motion`. Starts `true` (reduced) so server and first\n * client render agree on the static poster — no hydration mismatch, no motion\n * flash. After mount it reflects the real query and updates live. (Mirrors\n * BrandAnimation; kept local to avoid a shared-hook refactor.)\n */\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(true);\n React.useEffect(() => {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReduced(mq.matches);\n const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/**\n * Animated brand mark for an app navbar, from `@dataimago/brand` (the\n * animation-foundry's navbar set). Inlines the self-contained CSS-animated SVG\n * so its interior `<style>` works — an `<img src>` can't receive those styles.\n *\n * Behaviour by mark kind (from the navbar manifest):\n * - `assemble` (L2: dissertation-ai, rpkg-ai, SGPc-ai): the trailing `a`/`i`\n * start apart and the `i` slides onto the `a`. Plays once on mount; replays\n * on pointer-enter via a remount (`replayKey`).\n * - `shimmer` (L1: dataimago-ai): the monogram is always superimposed; an\n * accent shimmer sweeps on `:hover` from the SVG's own CSS (no JS trigger).\n * - `dream` (promoted dream marks, `tools/dream-foundry/drop.mjs promote`):\n * a self-contained CSS hover gesture like liftwash; the poster is the mark's\n * own static form (`dream_<slug>` / `DREAM`). Ink is `currentColor`.\n *\n * Motion-dignity: the embedded CSS animation is <= 300ms and gated on\n * `@media (prefers-reduced-motion: no-preference)`. When the user prefers\n * reduced motion this renders the static settled mark (the manifest `poster`,\n * the superimposed monogram) via `<Logo>` — so no consumer can ship the motion\n * without the fallback.\n */\nexport function NavbarLogo({ name, width, style, ...rest }: NavbarLogoProps) {\n const reduced = usePrefersReducedMotion();\n\n const entry = navbarManifest.find((n) => n.id === name);\n let markup = navbarLogos[name];\n\n if (!entry || !markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <NavbarLogo>: unknown navbar mark \"${name}\".`);\n return null;\n }\n\n if (reduced) {\n // Static settle-frame: the superimposed mark the animation resolves to.\n return (\n <Logo\n name={entry.poster.id as LogoId}\n variant={entry.poster.variant as LogoVariant}\n width={width}\n style={style}\n {...rest}\n />\n );\n }\n\n if (width != null) {\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n // The SVG is fully self-driving: it plays the assemble once on mount (its\n // `.di-play` class) and runs the smooth hover round-trip from its own `:hover`\n // CSS — no remount/replay logic here (remounting is what caused the teleport).\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n // Self-contained, font-free SVG; carries its own <title>/aria-label.\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","import * as React from 'react';\n\n/**\n * Light/dark theme preference for dataimago apps.\n *\n * The house tokens (`@dataimago/tokens`) follow the operating system through\n * `prefers-color-scheme` and yield to an explicit `data-bs-theme` on the root:\n * `light` or `dark` forces that theme, no attribute means \"follow the system\".\n * This module owns that attribute: it reads and stores the reader's choice,\n * applies it, and reports the theme actually in effect (for canvas and WebGL\n * surfaces that cannot read CSS variables).\n *\n * The choice is a per-browser convenience in localStorage. Every read and write\n * is guarded: in a private window or with storage blocked the page simply\n * follows the system.\n */\n\nexport type ThemePreference = 'system' | 'light' | 'dark';\nexport type ResolvedTheme = 'light' | 'dark';\n\nexport const THEME_STORAGE_KEY = 'dataimago-theme';\nexport const THEME_ATTRIBUTE = 'data-bs-theme';\n\n/** Fired on `window` whenever `setThemePreference` changes the choice. */\nconst CHANGE_EVENT = 'dataimago:themechange';\n\n/**\n * Inline this in `<head>` before any stylesheet paints, so a stored choice is\n * on the root before first paint (no flash of the other theme). The server\n * markup cannot know the choice, so the consumer also puts\n * `suppressHydrationWarning` on `<html>`. From a React server layout, render\n * `<ThemeScript />` instead: this package is a client bundle, so a server\n * component cannot import the string itself.\n *\n * ```tsx\n * <html lang=\"en\" suppressHydrationWarning>\n * <head><ThemeScript /></head>\n * ```\n */\nexport const themeInitScript =\n `(function(){try{var p=localStorage.getItem(${JSON.stringify(THEME_STORAGE_KEY)});` +\n `if(p==='light'||p==='dark'){document.documentElement.setAttribute(${JSON.stringify(THEME_ATTRIBUTE)},p);}}` +\n `catch(e){}})();`;\n\nfunction isPreference(value: unknown): value is ThemePreference {\n return value === 'system' || value === 'light' || value === 'dark';\n}\n\n/** The stored choice, or `'system'` when there is none (or storage is unavailable). */\nexport function readThemePreference(): ThemePreference {\n if (typeof window === 'undefined') return 'system';\n try {\n const stored = window.localStorage.getItem(THEME_STORAGE_KEY);\n return isPreference(stored) ? stored : 'system';\n } catch {\n return 'system';\n }\n}\n\nfunction applyAttribute(preference: ThemePreference): void {\n const root = document.documentElement;\n if (preference === 'system') root.removeAttribute(THEME_ATTRIBUTE);\n else root.setAttribute(THEME_ATTRIBUTE, preference);\n}\n\n/** Store, apply and announce a choice. `'system'` clears the stored choice. */\nexport function setThemePreference(preference: ThemePreference): void {\n if (typeof window === 'undefined') return;\n try {\n if (preference === 'system') window.localStorage.removeItem(THEME_STORAGE_KEY);\n else window.localStorage.setItem(THEME_STORAGE_KEY, preference);\n } catch {\n // Storage blocked: the choice still applies to this page view.\n }\n applyAttribute(preference);\n window.dispatchEvent(new Event(CHANGE_EVENT));\n}\n\n/** The theme in effect: the root attribute when set, otherwise the system's. */\nexport function resolveTheme(): ResolvedTheme {\n if (typeof document === 'undefined') return 'light';\n const forced = document.documentElement.getAttribute(THEME_ATTRIBUTE);\n if (forced === 'light' || forced === 'dark') return forced;\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\n/**\n * Put the stored choice on the root. `ThemeScript` does this before first\n * paint when it is server-rendered; this covers an app without it (or one that\n * inserts it on the client, where an inline script never runs). Idempotent.\n */\nfunction syncAttribute(): void {\n applyAttribute(readThemePreference());\n}\n\nfunction subscribePreference(onChange: () => void): () => void {\n syncAttribute();\n // Another tab changed the choice: follow it here too.\n const onStorage = (e: StorageEvent) => {\n if (e.key !== null && e.key !== THEME_STORAGE_KEY) return;\n applyAttribute(readThemePreference());\n onChange();\n };\n window.addEventListener(CHANGE_EVENT, onChange);\n window.addEventListener('storage', onStorage);\n return () => {\n window.removeEventListener(CHANGE_EVENT, onChange);\n window.removeEventListener('storage', onStorage);\n };\n}\n\nfunction subscribeResolved(onChange: () => void): () => void {\n // Watch first, then restore: the observer reports the restored attribute,\n // so a canvas surface hears about a stored choice applied on mount.\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [THEME_ATTRIBUTE],\n });\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n media.addEventListener('change', onChange);\n window.addEventListener(CHANGE_EVENT, onChange);\n syncAttribute();\n return () => {\n observer.disconnect();\n media.removeEventListener('change', onChange);\n window.removeEventListener(CHANGE_EVENT, onChange);\n };\n}\n\n/**\n * The reader's choice and a setter. Server render and hydration see\n * `'system'`; the stored choice arrives right after.\n */\nexport function useThemePreference(): [ThemePreference, (p: ThemePreference) => void] {\n const preference = React.useSyncExternalStore(\n subscribePreference,\n readThemePreference,\n () => 'system' as const,\n );\n return [preference, setThemePreference];\n}\n\n/**\n * The theme in effect, for surfaces that cannot read CSS variables (canvas,\n * WebGL maps). Follows the toggle and, under \"system\", the operating system.\n * Server render and hydration see `'light'`.\n */\nexport function useResolvedTheme(): ResolvedTheme {\n return React.useSyncExternalStore(subscribeResolved, resolveTheme, () => 'light' as const);\n}\n","import * as React from 'react';\nimport { useThemePreference, type ThemePreference } from './theme.js';\n\nexport interface ThemeToggleLabels {\n /** What the control sets, read before the current choice (\"Theme: Light\"). */\n name: string;\n system: string;\n light: string;\n dark: string;\n}\n\nexport interface ThemeToggleProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {\n /** Accessible and tooltip text; pass the property's own wording. */\n labels?: Partial<ThemeToggleLabels>;\n}\n\nconst DEFAULT_LABELS: ThemeToggleLabels = {\n name: 'Theme',\n system: 'System',\n light: 'Light',\n dark: 'Dark',\n};\n\nconst NEXT: Record<ThemePreference, ThemePreference> = {\n system: 'light',\n light: 'dark',\n dark: 'system',\n};\n\nfunction Icon({ preference }: { preference: ThemePreference }) {\n const common = {\n width: 20,\n height: 20,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.75,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n 'aria-hidden': true,\n focusable: false,\n };\n if (preference === 'light') {\n return (\n <svg {...common}>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41\" />\n </svg>\n );\n }\n if (preference === 'dark') {\n return (\n <svg {...common}>\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n );\n }\n return (\n <svg {...common}>\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M8 21h8M12 17v4\" />\n </svg>\n );\n}\n\n/**\n * Light/dark toggle for an app navbar: one button that steps\n * System → Light → Dark → System. \"System\" follows the operating system; the\n * other two set `data-bs-theme` on the root, which the house tokens obey.\n *\n * A 48px target (the dataimago touch floor). The icon is `currentColor`.\n * Inline styles would beat a consumer's `:hover` rule, so the two colours a\n * navbar restyles read custom properties instead: set `--di-theme-toggle-fg`\n * and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).\n * Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before\n * first paint.\n */\nexport function ThemeToggle({ labels, className = '', style, ...rest }: ThemeToggleProps) {\n const [preference, setPreference] = useThemePreference();\n const text = { ...DEFAULT_LABELS, ...labels };\n const label = `${text.name}: ${text[preference]}`;\n return (\n <button\n {...rest}\n className={['di-ui-theme-toggle', className].filter(Boolean).join(' ')}\n aria-label={label}\n title={label}\n data-theme-preference={preference}\n style={{\n alignItems: 'center',\n background: 'var(--di-theme-toggle-bg, transparent)',\n border: 0,\n borderRadius: 'var(--radius-md, 0.5rem)',\n color: 'var(--di-theme-toggle-fg, inherit)',\n cursor: 'pointer',\n display: 'inline-flex',\n height: '48px',\n justifyContent: 'center',\n padding: 0,\n transition: 'background-color 150ms ease',\n width: '48px',\n ...style,\n }}\n // After the spread, so the control never submits a form or loses its cycle.\n type=\"button\"\n onClick={() => setPreference(NEXT[preference])}\n >\n <Icon preference={preference} />\n </button>\n );\n}\n","import * as React from 'react';\nimport { themeInitScript } from './theme.js';\n\nexport interface ThemeScriptProps {\n /** Content-Security-Policy nonce, when the app sets one. */\n nonce?: string;\n}\n\n/**\n * Renders `themeInitScript` as an inline `<script>`, so a stored choice is on\n * the root before first paint. Use this from a server layout: this package is\n * a client bundle, so a server component cannot import the plain string, but\n * it can render this component. Put it in `<head>` and add\n * `suppressHydrationWarning` to `<html>` (the script changes its attributes\n * before React hydrates).\n */\nexport function ThemeScript({ nonce }: ThemeScriptProps) {\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: themeInitScript }} />;\n}\n"],"mappings":"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","names":["Button","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","React","import_brand","import_jsx_runtime","React","import_brand","import_jsx_runtime","usePrefersReducedMotion","React","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Button.tsx","../src/brand/LogoMonogram.tsx","../src/brand/LogoMonogramBg.tsx","../src/brand/LogoWordmark.tsx","../src/brand/Logo.tsx","../src/brand/BrandAnimation.tsx","../src/brand/NavbarLogo.tsx","../src/theme/ThemeToggle.tsx","../src/theme/theme.ts","../src/theme/ThemeScript.tsx"],"sourcesContent":["export { Button } from './Button.js';\nexport type { ButtonProps, ButtonVariant, ButtonSize } from './Button.js';\n\n// Brand assets (outlined-path SVG components — no font dependency at render).\n// See packages/ui/src/brand/Logos.stories.tsx for visual reference.\nexport { LogoMonogram } from './brand/LogoMonogram.js';\nexport type { LogoMonogramProps } from './brand/LogoMonogram.js';\nexport { LogoMonogramBg } from './brand/LogoMonogramBg.js';\nexport type { LogoMonogramBgProps } from './brand/LogoMonogramBg.js';\nexport { LogoWordmark } from './brand/LogoWordmark.js';\nexport type { LogoWordmarkProps } from './brand/LogoWordmark.js';\n\n// Manifest-driven marks from @dataimago/brand (the logo-foundry output).\nexport { Logo } from './brand/Logo.js';\nexport type { LogoProps } from './brand/Logo.js';\n\n// Decorative brand animations from @dataimago/brand (the animation-foundry).\n// Client component: enforces the prefers-reduced-motion poster fallback.\nexport { BrandAnimation } from './brand/BrandAnimation.js';\nexport type { BrandAnimationProps } from './brand/BrandAnimation.js';\n\n// Animated navbar marks from @dataimago/brand (the animation-foundry navbar set).\n// Client component: mount/hover replay + prefers-reduced-motion poster fallback.\nexport { NavbarLogo } from './brand/NavbarLogo.js';\nexport type { NavbarLogoProps } from './brand/NavbarLogo.js';\n\n// Light/dark theme: the reader's choice on <html data-bs-theme>, which the house\n// tokens obey over the operating system. Client component + hooks.\nexport { ThemeToggle } from './theme/ThemeToggle.js';\nexport { nextThemePreference } from './theme/ThemeToggle.js';\nexport type { ThemeToggleProps, ThemeToggleLabels } from './theme/ThemeToggle.js';\nexport { ThemeScript } from './theme/ThemeScript.js';\nexport type { ThemeScriptProps } from './theme/ThemeScript.js';\nexport {\n THEME_ATTRIBUTE,\n THEME_STORAGE_KEY,\n readThemePreference,\n resolveTheme,\n setThemePreference,\n themeInitScript,\n useResolvedTheme,\n useThemePreference,\n} from './theme/theme.js';\nexport type { ResolvedTheme, ThemePreference } from './theme/theme.js';\n","import * as React from 'react';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nconst variantClasses: Record<ButtonVariant, string> = {\n primary: 'di-ui-button--primary',\n secondary: 'di-ui-button--secondary',\n ghost: 'di-ui-button--ghost',\n};\n\nconst sizeClasses: Record<ButtonSize, string> = {\n sm: 'di-ui-button--sm',\n md: 'di-ui-button--md',\n lg: 'di-ui-button--lg',\n};\n\nconst baseStyle: React.CSSProperties = {\n alignItems: 'center',\n borderRadius: 'var(--radius-md, 0.5rem)',\n borderStyle: 'solid',\n borderWidth: '1px',\n cursor: 'pointer',\n display: 'inline-flex',\n fontFamily: 'var(--font-family-sans, system-ui, sans-serif)',\n fontWeight: 'var(--font-weight-medium, 500)',\n gap: 'var(--spacing-sm, 0.5rem)',\n justifyContent: 'center',\n textDecoration: 'none',\n transition: 'background-color 150ms ease, border-color 150ms ease, color 150ms ease',\n};\n\nconst variantStyles: Record<ButtonVariant, React.CSSProperties> = {\n primary: {\n backgroundColor: 'var(--color-interactive-primary)',\n borderColor: 'var(--color-interactive-primary)',\n color: 'var(--color-content-inverse)',\n },\n secondary: {\n backgroundColor: 'var(--color-surface-elevated)',\n borderColor: 'var(--color-border-moderate)',\n color: 'var(--color-content-primary)',\n },\n ghost: {\n backgroundColor: 'transparent',\n borderColor: 'transparent',\n color: 'var(--color-content-secondary)',\n },\n};\n\nconst sizeStyles: Record<ButtonSize, React.CSSProperties> = {\n sm: {\n fontSize: 'var(--font-size-small, 0.875rem)',\n minHeight: '2rem',\n padding: '0.375rem 0.75rem',\n },\n md: {\n fontSize: 'var(--font-size-base, 1rem)',\n minHeight: '2.5rem',\n padding: '0.625rem 1rem',\n },\n lg: {\n fontSize: 'var(--font-size-readable, 1.125rem)',\n minHeight: '3rem',\n padding: '0.75rem 1.25rem',\n },\n};\n\n/**\n * Smoke-level `<Button>` for the alpha release of @dataimago/ui.\n *\n * Styling is delegated to semantic CSS variables published by @dataimago/css\n * so that the component stays small and theme-aware.\n * Consumers that want to override visuals can pass their own className.\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n function Button(\n { variant = 'primary', size = 'md', className = '', style, type, children, ...rest },\n ref,\n ) {\n const classes = [\n 'di-ui-button',\n variantClasses[variant],\n sizeClasses[size],\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <button\n ref={ref}\n type={type ?? 'button'}\n className={classes}\n style={{\n ...baseStyle,\n ...variantStyles[variant],\n ...sizeStyles[size],\n ...style,\n }}\n {...rest}\n >\n {children}\n </button>\n );\n },\n);\n","import * as React from 'react';\n\n/**\n * <LogoMonogram /> — the dataimago 'a + i' superimposed monogram, transparent\n * background. Glyphs are outlined paths (no font dependency at render time);\n * fill defaults to `currentColor` so the mark inherits from the surrounding\n * text color and respects light/dark theme switches automatically.\n *\n * Symbolism: 'a' (data, agency) superimposed with 'i' (imago, AI) — the\n * fusion is the brand's central idea. See wiki/principles/visual-identity.md\n * (Stage L follow-up) for the full brand-mark rationale.\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * ai_monogram_supreme_1_PATH.svg\n *\n * Usage:\n * <LogoMonogram size={48} aria-label=\"dataimago\" />\n * <span style={{ color: 'var(--color-interactive-primary)' }}>\n * <LogoMonogram size={32} />\n * </span>\n */\n\nexport interface LogoMonogramProps extends React.SVGAttributes<SVGSVGElement> {\n /** Render size in px (or any CSS length). Default: 24. Width and height match. */\n size?: number | string;\n /**\n * Accessible name announced by screen readers. Set to `undefined` (or pass\n * `aria-hidden` directly) when the mark is purely decorative.\n */\n title?: string;\n}\n\nexport function LogoMonogram({\n size = 24,\n title = 'dataimago',\n ...rest\n}: LogoMonogramProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 480\"\n width={size}\n height={size}\n fill=\"currentColor\"\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* 'i' (imago) glyph */}\n <path d=\"m 337.3457,97.089844 q 0,8.906246 -6.49414,15.400386 -6.49414,6.49415 -15.58594,6.49415 -9.09179,0 -15.77148,-6.67969 Q 293,105.25391 293,96.162109 q 0,-9.091797 6.30859,-15.40039 6.49414,-6.494141 15.40039,-6.494141 9.0918,0 15.77149,6.865234 6.86523,6.679688 6.86523,15.957032 z M 324.54297,180.21484 303.01953,355 h -41.00586 l 21.52344,-174.78516 z\" />\n {/* 'a' (data) glyph */}\n <path d=\"M 325.70312,176.01562 303.04687,360 h -43.16406 l 2.53906,-19.92188 Q 234.6875,364.29687 205,364.29687 q -36.52344,0 -59.57031,-24.80468 -23.04688,-24.80469 -23.04688,-64.25782 0,-45.50781 26.36719,-74.60937 26.75781,-29.10156 68.55469,-29.10156 19.72656,0 33.59375,6.44531 14.0625,6.44531 28.71093,22.26562 l 2.92969,-24.21875 z m -52.53906,86.71875 q 0,-24.41406 -14.0625,-39.64843 -14.0625,-15.42969 -36.32812,-15.42969 -24.21875,0 -40.625,18.94531 -16.40625,19.14063 -16.40625,46.875 0,24.21875 13.67187,39.64844 13.67188,15.42969 35.15625,15.42969 23.82813,0 41.21094,-19.33594 17.38281,-19.72656 17.38281,-46.48438 z\" />\n </svg>\n );\n}\n","import * as React from 'react';\n\n/**\n * <LogoMonogramBg /> — the dataimago 'a + i' monogram with a rounded accent\n * backplate. White letters on the brand-accent role\n * (var(--color-interactive-accent), iteration-2 sky-blue).\n *\n * Use this variant on neutral surfaces where the no-background monogram\n * would lack presence — favicons, app icons, social-media avatars, or any\n * branded chrome where the mark should read as a self-contained badge.\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * ai_monogram_supreme_bg_1_PATH.svg\n *\n * Theming: the background uses var(--color-interactive-accent), which\n * resolves via theme-colors.json's mode-aware role mapping — accent-500\n * (#1f618d) on light surfaces, accent-300 (#3498DB) on dark. The letter\n * fill is hard-set to white; both accent stops have AA-or-better contrast\n * with white letters (light-mode 6.66:1 AA, dark-mode 3.15:1 AA-large for\n * the dot-and-stem heading-size letters used in the monogram). The\n * literal-fallback in this component is light-mode (#1f618d, sky-blue\n * iteration-2) for rare non-CSS-aware contexts. Iteration-1's copper-red\n * fallback (#b54a27) was retired in iteration-2-B.2; see\n * wiki/decisions/iteration-2-blue-accent.md for the rationale.\n *\n * Usage:\n * <LogoMonogramBg size={64} aria-label=\"dataimago\" />\n */\n\nexport interface LogoMonogramBgProps extends React.SVGAttributes<SVGSVGElement> {\n /** Render size in px. Default: 32 (favicon-sized). Width and height match. */\n size?: number | string;\n /** Accessible name. Set undefined or pass aria-hidden for decorative use. */\n title?: string;\n}\n\nexport function LogoMonogramBg({\n size = 32,\n title = 'dataimago',\n ...rest\n}: LogoMonogramBgProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 480\"\n width={size}\n height={size}\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* Rounded accent backplate — themed via --color-interactive-accent\n (mode-aware: accent-500 #1f618d on light, accent-300 #3498DB on dark).\n Literal fallback is the light-mode canonical for non-CSS consumers. */}\n <rect\n width=\"100%\"\n height=\"100%\"\n rx=\"40\"\n ry=\"40\"\n fill=\"var(--color-interactive-accent, #1f618d)\"\n />\n {/* 'a' (data) glyph in white */}\n <path\n fill=\"#ffffff\"\n d=\"M 325.70312,176.01562 303.04687,360 h -43.16406 l 2.53906,-19.92188 Q 234.6875,364.29687 205,364.29687 q -36.52344,0 -59.57031,-24.80468 -23.04688,-24.80469 -23.04688,-64.25782 0,-45.50781 26.36719,-74.60937 26.75781,-29.10156 68.55469,-29.10156 19.72656,0 33.59375,6.44531 14.0625,6.44531 28.71093,22.26562 l 2.92969,-24.21875 z m -52.53906,86.71875 q 0,-24.41406 -14.0625,-39.64843 -14.0625,-15.42969 -36.32812,-15.42969 -24.21875,0 -40.625,18.94531 -16.40625,19.14063 -16.40625,46.875 0,24.21875 13.67187,39.64844 13.67188,15.42969 35.15625,15.42969 23.82813,0 41.21094,-19.33594 17.38281,-19.72656 17.38281,-46.48438 z\"\n />\n {/* 'i' (imago) glyph in white */}\n <path\n fill=\"#ffffff\"\n d=\"m 337.3457,97.089844 q 0,8.906246 -6.49414,15.400386 -6.49414,6.49415 -15.58594,6.49415 -9.09179,0 -15.77148,-6.67969 Q 293,105.25391 293,96.162109 q 0,-9.091797 6.30859,-15.40039 6.49414,-6.494141 15.40039,-6.494141 9.0918,0 15.77149,6.865234 6.86523,6.679688 6.86523,15.957032 z M 324.54297,180.21484 303.01953,355 h -41.00586 l 21.52344,-174.78516 z\"\n />\n </svg>\n );\n}\n","import * as React from 'react';\n\n/**\n * <LogoWordmark /> — the full \"dataimago\" wordmark with the 'ai' overlap.\n * White letters on a rounded accent backplate, themed via\n * var(--color-interactive-accent).\n *\n * Use this variant for primary brand surfaces: site headers, document\n * letterheads, large-format presentations, business materials. For compact\n * contexts (favicons, social avatars) use <LogoMonogramBg /> instead; for\n * inline use within prose use <LogoMonogram /> with currentColor.\n *\n * The wordmark preserves the philosophical idea of the brand: the visible\n * superposition of 'a' (data, human agency) and 'i' (imago, AI) creates a new\n * unified glyph that is neither pure human nor pure artificial — genuine\n * collaboration. See wiki/principles/visual-identity.md (Stage L follow-up).\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * dataimago_supreme_1_PATH.svg\n *\n * Theming: the backplate uses var(--color-interactive-accent), which resolves\n * via theme-colors.json's mode-aware role mapping — accent-500 (#1f618d) on\n * light surfaces, accent-300 (#3498DB) on dark. The literal-fallback in this\n * component is light-mode (#1f618d, sky-blue iteration-2) for the rare cases\n * where the SVG is consumed without CSS-variable resolution (printed PDFs from\n * raw SVG, third-party platforms that strip CSS). Iteration-1's copper-red\n * fallback (#b54a27) was retired in iteration-2-B.2; see\n * wiki/decisions/iteration-2-blue-accent.md for the rationale.\n *\n * Usage:\n * <LogoWordmark width={240} aria-label=\"dataimago\" />\n *\n * The wordmark's natural aspect ratio is 480x150 (~3.2:1). Pass `width` and\n * height auto-scales, or pass both explicitly.\n */\n\nexport interface LogoWordmarkProps extends React.SVGAttributes<SVGSVGElement> {\n /** Width in px. Default: 240. Height auto-scales to the 480:150 ratio. */\n width?: number | string;\n /** Accessible name. Set undefined or pass aria-hidden for decorative use. */\n title?: string;\n}\n\nexport function LogoWordmark({\n width = 240,\n title = 'dataimago',\n ...rest\n}: LogoWordmarkProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 150\"\n width={width}\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* Rounded accent backplate — themed via --color-interactive-accent\n (mode-aware: accent-500 #1f618d on light, accent-300 #3498DB on dark).\n Literal fallback is the light-mode canonical for non-CSS consumers. */}\n <rect\n width=\"100%\"\n height=\"100%\"\n rx=\"5\"\n ry=\"5\"\n fill=\"var(--color-interactive-accent, #1f618d)\"\n />\n {/* \"data\" — outlined glyphs, white fill */}\n <path\n fill=\"#ffffff\"\n d=\"M 104.80469,35.621094 97.492187,95 h -7.734375 l 0.421875,-3.550781 q -2.882812,2.566406 -4.992187,3.480468 -2.074219,0.84375 -5.132813,0.84375 -6.960937,0 -11.390625,-4.605468 -4.429687,-4.640625 -4.429687,-11.953125 0,-7.804688 4.851562,-12.972657 4.851563,-5.167968 12.199219,-5.167968 3.761719,0 6.328125,1.300781 2.671875,1.335937 5.589844,4.535156 L 97.035156,35.621094 Z M 92.007812,77 q 0,-4.21875 -2.601562,-6.820313 -2.601563,-2.601562 -6.820313,-2.601562 -4.570312,0 -7.558593,3.410156 -2.988282,3.410156 -2.988282,8.613281 0,4.394532 2.566407,7.066407 2.566406,2.636718 6.820312,2.636718 4.429688,0 7.488281,-3.585937 Q 92.007812,82.203125 92.007812,77 Z M 145.51562,61.882812 141.4375,95 h -7.76953 l 0.45703,-3.585938 q -4.99219,4.359375 -10.33594,4.359375 -6.57422,0 -10.72265,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.74609,-13.429687 4.81641,-5.238281 12.33985,-5.238281 3.55078,0 6.04687,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52734,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53906,-2.777344 -4.35937,0 -7.3125,3.410156 -2.95312,3.445313 -2.95312,8.4375 0,4.359375 2.46093,7.136719 2.46094,2.777344 6.32813,2.777344 4.28906,0 7.41797,-3.480469 3.1289,-3.550781 3.1289,-8.367188 z M 163.79687,69.089844 160.59766,95 h -7.76954 l 3.19922,-25.910156 h -4.39453 l 0.87891,-7.207032 h 4.39453 l 1.89844,-15.363281 h 7.76953 l -1.89844,15.363281 h 7.17188 l -0.87891,7.207032 z M 211.29297,61.882812 207.21484,95 h -7.76953 l 0.45703,-3.585938 q -4.99218,4.359375 -10.33593,4.359375 -6.57422,0 -10.72266,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.7461,-13.429687 4.8164,-5.238281 12.33984,-5.238281 3.55078,0 6.04687,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52735,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53907,-2.777344 -4.35937,0 -7.3125,3.410156 -2.95312,3.445313 -2.95312,8.4375 0,4.359375 2.46094,7.136719 2.46093,2.777344 6.32812,2.777344 4.28906,0 7.41797,-3.480469 3.12891,-3.550781 3.12891,-8.367188 z\"\n />\n {/* \"imago\" — outlined glyphs, white fill, superimposed at the 'a/i' overlap */}\n <path\n fill=\"#ffffff\"\n d=\"m 213.40234,46.132812 q 0,1.6875 -1.23047,2.917969 -1.23046,1.230469 -2.95312,1.230469 -1.72266,0 -2.98828,-1.265625 Q 205,47.679687 205,45.957031 q 0,-1.722656 1.19531,-2.917969 1.23047,-1.230468 2.91797,-1.230468 1.72266,0 2.98828,1.300781 1.30078,1.265625 1.30078,3.023437 z m -2.42578,15.75 L 206.89844,95 h -7.76953 l 4.07812,-33.117188 z m 18.87891,0 -0.35156,3.09375 q 3.9375,-3.9375 9.10546,-3.9375 3.12891,0 4.81641,1.160157 1.6875,1.089843 3.02344,4.113281 4.92187,-5.238281 10.61719,-5.238281 9.84375,0 9.84375,9.035156 0,0.914062 -0.17579,2.566406 -0.14062,1.652344 -0.42187,4.078125 L 264.0625,95 h -7.76953 l 2.39062,-19.160156 Q 259,73.167969 259,71.902344 q 0,-4.324219 -4.35938,-4.324219 -1.72265,0 -2.98828,0.527344 -1.26562,0.492187 -2.17968,1.582031 -0.87891,1.054687 -1.47657,2.777344 -0.5625,1.6875 -0.8789,4.042968 L 244.86719,95 h -7.76953 l 2.42578,-19.652344 q 0.3164,-2.671875 0.3164,-3.65625 0,-4.113281 -4.46484,-4.113281 -6.46875,0 -7.55859,8.964844 L 225.53125,95 h -7.76953 l 4.07812,-33.117188 z m 81.63281,0 L 307.41016,95 h -7.76954 l 0.45704,-3.585938 q -4.99219,4.359375 -10.33594,4.359375 -6.57422,0 -10.72266,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.7461,-13.429687 4.8164,-5.238281 12.33984,-5.238281 3.55078,0 6.04688,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52734,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53906,-2.777344 -4.35938,0 -7.3125,3.410156 -2.95313,3.445313 -2.95313,8.4375 0,4.359375 2.46094,7.136719 2.46094,2.777344 6.32812,2.777344 4.28907,0 7.41797,-3.480469 3.12891,-3.550781 3.12891,-8.367188 z m 53.57812,-15.609375 -4.07812,33.363282 q -0.38672,2.988281 -0.87891,5.203126 -0.45703,2.21484 -1.05468,3.72656 -1.23047,3.09375 -3.65625,5.3086 -4.71094,4.21875 -12.23438,4.21875 -7.76953,0 -12.58594,-4.07813 -2.46093,-2.03906 -3.44531,-4.64062 -0.98437,-2.39063 -0.98437,-6.714849 h 7.52343 q 0.28125,4.675779 2.39063,6.785159 2.10937,2.14453 6.53906,2.14453 4.78125,0 7.27734,-2.56641 2.4961,-2.53125 3.16407,-8.085935 l 0.5625,-4.535156 q -4.92188,3.761718 -10.33594,3.761718 -6.75,0 -11.14453,-4.605468 -4.39453,-4.710938 -4.39453,-11.742188 0,-7.910156 5.09765,-13.113281 5.13282,-5.238281 12.83203,-5.238281 3.69141,0 5.97657,1.089843 2.49609,1.054688 5.16797,3.832032 l 0.49218,-4.113282 z m -8.92968,15.714844 q 0,-4.5 -2.74219,-7.242187 -2.74219,-2.777344 -7.17188,-2.777344 -4.60546,0 -7.66406,3.375 -3.02344,3.339844 -3.02344,8.402344 0,4.429687 2.60157,7.242187 2.63672,2.707031 6.96093,2.707031 4.81641,0 7.91016,-3.304687 3.12891,-3.339844 3.12891,-8.402344 z m 52.3125,0.527344 q 0,7.59375 -5.27344,12.761719 -5.23828,5.167968 -12.9375,5.167968 -8.05078,0 -13.14844,-4.886718 Q 362.5,86.246094 362.5,78.722656 q 0,-7.699219 5.30859,-12.796875 5.34375,-5.132812 13.35938,-5.132812 7.875,0 12.83203,4.851562 4.99219,4.78125 4.99219,12.480469 z m -7.83985,-0.386719 q 0,-4.78125 -2.77734,-7.558594 -2.77734,-2.882812 -7.27734,-2.882812 -4.67579,0 -7.73438,3.199219 -3.05859,3.234375 -3.05859,8.121093 0,4.921875 2.88281,7.910157 2.88281,3.058593 7.45312,3.058593 4.57032,0 7.52344,-3.339843 2.98828,-3.304688 2.98828,-8.507813 z\"\n />\n </svg>\n );\n}\n","import * as React from 'react';\nimport { svgs, type LogoId, type LogoVariant, type LogoKey } from '@dataimago/brand';\n\nexport interface LogoProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Mark id, e.g. `dissertation_ai_OVERLAP`. */\n name: LogoId;\n /** PATH (default), CUTOUT, BG, or LETTERS. See @dataimago/brand. */\n variant?: LogoVariant;\n /**\n * Render width (px number or any CSS length). Height scales proportionally\n * via the SVG viewBox. Omit to use the mark's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * Manifest-driven brand mark from `@dataimago/brand`. Inlines the SVG string so\n * `currentColor` (PATH/LETTERS), the `--color-interactive-accent` badge fill\n * (CUTOUT/BG), and the per-letter interaction classes (`.glyph`, `.logo-*`) all\n * work — an `<img src>` can't receive those interior styles.\n *\n * Color flows from CSS: set `color` (PATH/LETTERS) on this element or an\n * ancestor. Size via `width` (or CSS on the wrapper).\n */\nexport function Logo({ name, variant = 'PATH', width, style, ...rest }: LogoProps) {\n const key = `${name}_${variant}` as LogoKey;\n let markup = svgs[key];\n\n if (!markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <Logo>: unknown mark \"${key}\".`);\n return null;\n }\n\n if (width != null) {\n // Set width + drop height so the viewBox preserves the aspect ratio.\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n // The SVG is generated, font-free, and carries its own <title>/aria-label.\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport {\n animations,\n animationManifest,\n type AnimationId,\n type LogoId,\n type LogoVariant,\n} from '@dataimago/brand';\nimport { Logo } from './Logo';\n\nexport interface BrandAnimationProps\n extends React.HTMLAttributes<HTMLSpanElement> {\n /** Animation id, e.g. `dissertation-ai`. See @dataimago/brand animations.json. */\n name: AnimationId;\n /**\n * Render width (px number or any CSS length). Height scales via the SVG\n * viewBox. Omit to use the animation's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * SSR-safe `prefers-reduced-motion` detection. Starts `true` (reduced) so the\n * server and the first client render agree on the static poster — no hydration\n * mismatch, and no motion flashes before JS decides. After mount it reflects the\n * real media query and updates live if the user toggles the OS setting.\n */\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(true);\n React.useEffect(() => {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReduced(mq.matches);\n const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/**\n * Decorative brand animation from `@dataimago/brand` (the animation-foundry).\n * Inlines the pure-SMIL SVG string so its interior `<style>` works — an\n * `<img src>` can't receive those styles (same rationale as `<Logo>`).\n *\n * Motion-dignity: decorative brand animation is a distinct category from the\n * 300ms UI-transition ceiling, but it is contractually gated on\n * `prefers-reduced-motion`. When the user prefers reduced motion this renders\n * the static brand mark the animation settles to (its `poster`) via `<Logo>`,\n * so no consumer can ship the motion without the fallback.\n */\nexport function BrandAnimation({\n name,\n width,\n style,\n ...rest\n}: BrandAnimationProps) {\n const reduced = usePrefersReducedMotion();\n const entry = animationManifest.find((a) => a.id === name);\n let markup = animations[name];\n\n if (!entry || !markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <BrandAnimation>: unknown animation \"${name}\".`);\n return null;\n }\n\n if (reduced) {\n // Static settle-frame: the brand mark the animation resolves to.\n return (\n <Logo\n name={entry.poster.id as LogoId}\n variant={entry.poster.variant as LogoVariant}\n width={width}\n style={style}\n {...rest}\n />\n );\n }\n\n if (width != null) {\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport {\n navbarLogos,\n navbarManifest,\n type NavbarLogoId,\n type LogoId,\n type LogoVariant,\n} from '@dataimago/brand';\nimport { Logo } from './Logo';\n\nexport interface NavbarLogoProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Navbar mark id, e.g. `dissertation-ai`. See @dataimago/brand navbar.json. */\n name: NavbarLogoId;\n /**\n * Render width (px number or any CSS length). Height scales via the SVG\n * viewBox. Omit to use the mark's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * SSR-safe `prefers-reduced-motion`. Starts `true` (reduced) so server and first\n * client render agree on the static poster — no hydration mismatch, no motion\n * flash. After mount it reflects the real query and updates live. (Mirrors\n * BrandAnimation; kept local to avoid a shared-hook refactor.)\n */\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(true);\n React.useEffect(() => {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReduced(mq.matches);\n const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/**\n * Animated brand mark for an app navbar, from `@dataimago/brand` (the\n * animation-foundry's navbar set). Inlines the self-contained CSS-animated SVG\n * so its interior `<style>` works — an `<img src>` can't receive those styles.\n *\n * Behaviour by mark kind (from the navbar manifest):\n * - `assemble` (L2: dissertation-ai, rpkg-ai, SGPc-ai): the trailing `a`/`i`\n * start apart and the `i` slides onto the `a`. Plays once on mount; replays\n * on pointer-enter via a remount (`replayKey`).\n * - `shimmer` (L1: dataimago-ai): the monogram is always superimposed; an\n * accent shimmer sweeps on `:hover` from the SVG's own CSS (no JS trigger).\n * - `dream` (promoted dream marks, `tools/dream-foundry/drop.mjs promote`):\n * a self-contained CSS hover gesture like liftwash; the poster is the mark's\n * own static form (`dream_<slug>` / `DREAM`). Ink is `currentColor`.\n *\n * Motion-dignity: the embedded CSS animation is <= 300ms and gated on\n * `@media (prefers-reduced-motion: no-preference)`. When the user prefers\n * reduced motion this renders the static settled mark (the manifest `poster`,\n * the superimposed monogram) via `<Logo>` — so no consumer can ship the motion\n * without the fallback.\n */\nexport function NavbarLogo({ name, width, style, ...rest }: NavbarLogoProps) {\n const reduced = usePrefersReducedMotion();\n\n const entry = navbarManifest.find((n) => n.id === name);\n let markup = navbarLogos[name];\n\n if (!entry || !markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <NavbarLogo>: unknown navbar mark \"${name}\".`);\n return null;\n }\n\n if (reduced) {\n // Static settle-frame: the superimposed mark the animation resolves to.\n return (\n <Logo\n name={entry.poster.id as LogoId}\n variant={entry.poster.variant as LogoVariant}\n width={width}\n style={style}\n {...rest}\n />\n );\n }\n\n if (width != null) {\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n // The SVG is fully self-driving: it plays the assemble once on mount (its\n // `.di-play` class) and runs the smooth hover round-trip from its own `:hover`\n // CSS — no remount/replay logic here (remounting is what caused the teleport).\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n // Self-contained, font-free SVG; carries its own <title>/aria-label.\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","import * as React from 'react';\nimport {\n resolveTheme,\n useResolvedTheme,\n useThemePreference,\n type ResolvedTheme,\n type ThemePreference,\n} from './theme.js';\n\nexport interface ThemeToggleLabels {\n /** What the control sets, read before the theme in effect (\"Theme: Light\"). */\n name: string;\n /** Added when the theme follows the operating system (\"Theme: Light (System)\"). */\n system: string;\n light: string;\n dark: string;\n}\n\nexport interface ThemeToggleProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {\n /** Accessible and tooltip text; pass the property's own wording. */\n labels?: Partial<ThemeToggleLabels>;\n}\n\nconst DEFAULT_LABELS: ThemeToggleLabels = {\n name: 'Theme',\n system: 'System',\n light: 'Light',\n dark: 'Dark',\n};\n\n/** The operating system's theme. */\nfunction systemTheme(): ResolvedTheme {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\n/**\n * The choice one click makes: the other theme, stored as \"system\" when that is\n * the operating system's own, so the page goes back to following it.\n */\nexport function nextThemePreference(shown: ResolvedTheme, system: ResolvedTheme): ThemePreference {\n const next: ResolvedTheme = shown === 'dark' ? 'light' : 'dark';\n return next === system ? 'system' : next;\n}\n\nconst noSubscribe = () => () => {};\n\n/**\n * False in the server markup and during hydration, true after. The theme in\n * effect is only known on the client (the server snapshot is always light), so\n * until then the button claims no theme: its name is the bare `name` and its\n * icon is hidden, keeping the 48px box.\n */\nfunction useHydrated(): boolean {\n return React.useSyncExternalStore(noSubscribe, () => true, () => false);\n}\n\nfunction Icon({ theme }: { theme: ResolvedTheme }) {\n const common = {\n width: 20,\n height: 20,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.75,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n 'aria-hidden': true,\n focusable: false,\n };\n if (theme === 'dark') {\n return (\n <svg {...common}>\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n );\n }\n return (\n <svg {...common}>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41\" />\n </svg>\n );\n}\n\n/**\n * Light/dark toggle for an app navbar: one button, and one click always flips\n * the theme on screen. The page follows the operating system until the first\n * click; a click that lands on the system's own theme goes back to following\n * it (no stored choice), and the other theme is stored and set as\n * `data-bs-theme` on the root, which the house tokens obey. The icon shows the\n * theme in effect: a sun in light, a moon in dark.\n *\n * A 48px target (the dataimago touch floor). The icon is `currentColor`.\n * Inline styles would beat a consumer's `:hover` rule, so the two colours a\n * navbar restyles read custom properties instead: set `--di-theme-toggle-fg`\n * and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).\n * Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before\n * first paint.\n */\nexport function ThemeToggle({ labels, className = '', style, ...rest }: ThemeToggleProps) {\n const [preference, setPreference] = useThemePreference();\n const resolved = useResolvedTheme();\n const hydrated = useHydrated();\n const shown = hydrated ? resolved : undefined;\n const text = { ...DEFAULT_LABELS, ...labels };\n const label = shown\n ? `${text.name}: ${text[shown]}${preference === 'system' ? ` (${text.system})` : ''}`\n : text.name;\n return (\n <button\n {...rest}\n className={['di-ui-theme-toggle', className].filter(Boolean).join(' ')}\n aria-label={label}\n title={label}\n data-theme-preference={preference}\n data-theme={shown}\n style={{\n alignItems: 'center',\n background: 'var(--di-theme-toggle-bg, transparent)',\n border: 0,\n borderRadius: 'var(--radius-md, 0.5rem)',\n color: 'var(--di-theme-toggle-fg, inherit)',\n cursor: 'pointer',\n display: 'inline-flex',\n height: '48px',\n justifyContent: 'center',\n padding: 0,\n transition: 'background-color 150ms ease',\n width: '48px',\n ...style,\n }}\n // After the spread, so the control never submits a form or loses its flip.\n type=\"button\"\n // Live reads, not the rendered snapshot: a click always flips what is on screen.\n onClick={() => setPreference(nextThemePreference(resolveTheme(), systemTheme()))}\n >\n <span style={{ display: 'inline-flex', visibility: shown ? 'visible' : 'hidden' }}>\n <Icon theme={shown ?? 'light'} />\n </span>\n </button>\n );\n}\n","import * as React from 'react';\n\n/**\n * Light/dark theme preference for dataimago apps.\n *\n * The house tokens (`@dataimago/tokens`) follow the operating system through\n * `prefers-color-scheme` and yield to an explicit `data-bs-theme` on the root:\n * `light` or `dark` forces that theme, no attribute means \"follow the system\".\n * This module owns that attribute: it reads and stores the reader's choice,\n * applies it, and reports the theme actually in effect (for canvas and WebGL\n * surfaces that cannot read CSS variables).\n *\n * The choice is a per-browser convenience in localStorage. Every read and write\n * is guarded: in a private window or with storage blocked the page simply\n * follows the system.\n */\n\nexport type ThemePreference = 'system' | 'light' | 'dark';\nexport type ResolvedTheme = 'light' | 'dark';\n\nexport const THEME_STORAGE_KEY = 'dataimago-theme';\nexport const THEME_ATTRIBUTE = 'data-bs-theme';\n\n/** Fired on `window` whenever `setThemePreference` changes the choice. */\nconst CHANGE_EVENT = 'dataimago:themechange';\n\n/**\n * Inline this in `<head>` before any stylesheet paints, so a stored choice is\n * on the root before first paint (no flash of the other theme). The server\n * markup cannot know the choice, so the consumer also puts\n * `suppressHydrationWarning` on `<html>`. From a React server layout, render\n * `<ThemeScript />` instead: this package is a client bundle, so a server\n * component cannot import the string itself.\n *\n * ```tsx\n * <html lang=\"en\" suppressHydrationWarning>\n * <head><ThemeScript /></head>\n * ```\n */\nexport const themeInitScript =\n `(function(){try{var p=localStorage.getItem(${JSON.stringify(THEME_STORAGE_KEY)});` +\n `if(p==='light'||p==='dark'){document.documentElement.setAttribute(${JSON.stringify(THEME_ATTRIBUTE)},p);}}` +\n `catch(e){}})();`;\n\nfunction isPreference(value: unknown): value is ThemePreference {\n return value === 'system' || value === 'light' || value === 'dark';\n}\n\n/** The stored choice, or `'system'` when there is none (or storage is unavailable). */\nexport function readThemePreference(): ThemePreference {\n if (typeof window === 'undefined') return 'system';\n try {\n const stored = window.localStorage.getItem(THEME_STORAGE_KEY);\n return isPreference(stored) ? stored : 'system';\n } catch {\n return 'system';\n }\n}\n\nfunction applyAttribute(preference: ThemePreference): void {\n const root = document.documentElement;\n if (preference === 'system') root.removeAttribute(THEME_ATTRIBUTE);\n else root.setAttribute(THEME_ATTRIBUTE, preference);\n}\n\n/** Store, apply and announce a choice. `'system'` clears the stored choice. */\nexport function setThemePreference(preference: ThemePreference): void {\n if (typeof window === 'undefined') return;\n try {\n if (preference === 'system') window.localStorage.removeItem(THEME_STORAGE_KEY);\n else window.localStorage.setItem(THEME_STORAGE_KEY, preference);\n } catch {\n // Storage blocked: the choice still applies to this page view.\n }\n applyAttribute(preference);\n window.dispatchEvent(new Event(CHANGE_EVENT));\n}\n\n/** The theme in effect: the root attribute when set, otherwise the system's. */\nexport function resolveTheme(): ResolvedTheme {\n if (typeof document === 'undefined') return 'light';\n const forced = document.documentElement.getAttribute(THEME_ATTRIBUTE);\n if (forced === 'light' || forced === 'dark') return forced;\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\n/**\n * Put the stored choice on the root. `ThemeScript` does this before first\n * paint when it is server-rendered; this covers an app without it (or one that\n * inserts it on the client, where an inline script never runs). Idempotent.\n */\nfunction syncAttribute(): void {\n applyAttribute(readThemePreference());\n}\n\nfunction subscribePreference(onChange: () => void): () => void {\n syncAttribute();\n // Another tab changed the choice: follow it here too.\n const onStorage = (e: StorageEvent) => {\n if (e.key !== null && e.key !== THEME_STORAGE_KEY) return;\n applyAttribute(readThemePreference());\n onChange();\n };\n window.addEventListener(CHANGE_EVENT, onChange);\n window.addEventListener('storage', onStorage);\n return () => {\n window.removeEventListener(CHANGE_EVENT, onChange);\n window.removeEventListener('storage', onStorage);\n };\n}\n\nfunction subscribeResolved(onChange: () => void): () => void {\n // Watch first, then restore: the observer reports the restored attribute,\n // so a canvas surface hears about a stored choice applied on mount.\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [THEME_ATTRIBUTE],\n });\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n media.addEventListener('change', onChange);\n window.addEventListener(CHANGE_EVENT, onChange);\n syncAttribute();\n return () => {\n observer.disconnect();\n media.removeEventListener('change', onChange);\n window.removeEventListener(CHANGE_EVENT, onChange);\n };\n}\n\n/**\n * The reader's choice and a setter. Server render and hydration see\n * `'system'`; the stored choice arrives right after.\n */\nexport function useThemePreference(): [ThemePreference, (p: ThemePreference) => void] {\n const preference = React.useSyncExternalStore(\n subscribePreference,\n readThemePreference,\n () => 'system' as const,\n );\n return [preference, setThemePreference];\n}\n\n/**\n * The theme in effect, for surfaces that cannot read CSS variables (canvas,\n * WebGL maps). Follows the toggle and, under \"system\", the operating system.\n * Server render and hydration see `'light'`.\n */\nexport function useResolvedTheme(): ResolvedTheme {\n return React.useSyncExternalStore(subscribeResolved, resolveTheme, () => 'light' as const);\n}\n","import * as React from 'react';\nimport { themeInitScript } from './theme.js';\n\nexport interface ThemeScriptProps {\n /** Content-Security-Policy nonce, when the app sets one. */\n nonce?: string;\n}\n\n/**\n * Renders `themeInitScript` as an inline `<script>`, so a stored choice is on\n * the root before first paint. Use this from a server layout: this package is\n * a client bundle, so a server component cannot import the plain string, but\n * it can render this component. Put it in `<head>` and add\n * `suppressHydrationWarning` to `<html>` (the script changes its attributes\n * before React hydrates).\n */\nexport function ThemeScript({ nonce }: ThemeScriptProps) {\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: themeInitScript }} />;\n}\n"],"mappings":"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","names":["Button","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","React","import_brand","import_jsx_runtime","React","import_brand","import_jsx_runtime","usePrefersReducedMotion","React","React","import_jsx_runtime","import_jsx_runtime"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -201,45 +201,6 @@ interface NavbarLogoProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
|
201
201
|
*/
|
|
202
202
|
declare function NavbarLogo({ name, width, style, ...rest }: NavbarLogoProps): react_jsx_runtime.JSX.Element | null;
|
|
203
203
|
|
|
204
|
-
interface ThemeToggleLabels {
|
|
205
|
-
/** What the control sets, read before the current choice ("Theme: Light"). */
|
|
206
|
-
name: string;
|
|
207
|
-
system: string;
|
|
208
|
-
light: string;
|
|
209
|
-
dark: string;
|
|
210
|
-
}
|
|
211
|
-
interface ThemeToggleProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {
|
|
212
|
-
/** Accessible and tooltip text; pass the property's own wording. */
|
|
213
|
-
labels?: Partial<ThemeToggleLabels>;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Light/dark toggle for an app navbar: one button that steps
|
|
217
|
-
* System → Light → Dark → System. "System" follows the operating system; the
|
|
218
|
-
* other two set `data-bs-theme` on the root, which the house tokens obey.
|
|
219
|
-
*
|
|
220
|
-
* A 48px target (the dataimago touch floor). The icon is `currentColor`.
|
|
221
|
-
* Inline styles would beat a consumer's `:hover` rule, so the two colours a
|
|
222
|
-
* navbar restyles read custom properties instead: set `--di-theme-toggle-fg`
|
|
223
|
-
* and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).
|
|
224
|
-
* Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before
|
|
225
|
-
* first paint.
|
|
226
|
-
*/
|
|
227
|
-
declare function ThemeToggle({ labels, className, style, ...rest }: ThemeToggleProps): react_jsx_runtime.JSX.Element;
|
|
228
|
-
|
|
229
|
-
interface ThemeScriptProps {
|
|
230
|
-
/** Content-Security-Policy nonce, when the app sets one. */
|
|
231
|
-
nonce?: string;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Renders `themeInitScript` as an inline `<script>`, so a stored choice is on
|
|
235
|
-
* the root before first paint. Use this from a server layout: this package is
|
|
236
|
-
* a client bundle, so a server component cannot import the plain string, but
|
|
237
|
-
* it can render this component. Put it in `<head>` and add
|
|
238
|
-
* `suppressHydrationWarning` to `<html>` (the script changes its attributes
|
|
239
|
-
* before React hydrates).
|
|
240
|
-
*/
|
|
241
|
-
declare function ThemeScript({ nonce }: ThemeScriptProps): react_jsx_runtime.JSX.Element;
|
|
242
|
-
|
|
243
204
|
/**
|
|
244
205
|
* Light/dark theme preference for dataimago apps.
|
|
245
206
|
*
|
|
@@ -290,4 +251,52 @@ declare function useThemePreference(): [ThemePreference, (p: ThemePreference) =>
|
|
|
290
251
|
*/
|
|
291
252
|
declare function useResolvedTheme(): ResolvedTheme;
|
|
292
253
|
|
|
293
|
-
|
|
254
|
+
interface ThemeToggleLabels {
|
|
255
|
+
/** What the control sets, read before the theme in effect ("Theme: Light"). */
|
|
256
|
+
name: string;
|
|
257
|
+
/** Added when the theme follows the operating system ("Theme: Light (System)"). */
|
|
258
|
+
system: string;
|
|
259
|
+
light: string;
|
|
260
|
+
dark: string;
|
|
261
|
+
}
|
|
262
|
+
interface ThemeToggleProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {
|
|
263
|
+
/** Accessible and tooltip text; pass the property's own wording. */
|
|
264
|
+
labels?: Partial<ThemeToggleLabels>;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The choice one click makes: the other theme, stored as "system" when that is
|
|
268
|
+
* the operating system's own, so the page goes back to following it.
|
|
269
|
+
*/
|
|
270
|
+
declare function nextThemePreference(shown: ResolvedTheme, system: ResolvedTheme): ThemePreference;
|
|
271
|
+
/**
|
|
272
|
+
* Light/dark toggle for an app navbar: one button, and one click always flips
|
|
273
|
+
* the theme on screen. The page follows the operating system until the first
|
|
274
|
+
* click; a click that lands on the system's own theme goes back to following
|
|
275
|
+
* it (no stored choice), and the other theme is stored and set as
|
|
276
|
+
* `data-bs-theme` on the root, which the house tokens obey. The icon shows the
|
|
277
|
+
* theme in effect: a sun in light, a moon in dark.
|
|
278
|
+
*
|
|
279
|
+
* A 48px target (the dataimago touch floor). The icon is `currentColor`.
|
|
280
|
+
* Inline styles would beat a consumer's `:hover` rule, so the two colours a
|
|
281
|
+
* navbar restyles read custom properties instead: set `--di-theme-toggle-fg`
|
|
282
|
+
* and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).
|
|
283
|
+
* Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before
|
|
284
|
+
* first paint.
|
|
285
|
+
*/
|
|
286
|
+
declare function ThemeToggle({ labels, className, style, ...rest }: ThemeToggleProps): react_jsx_runtime.JSX.Element;
|
|
287
|
+
|
|
288
|
+
interface ThemeScriptProps {
|
|
289
|
+
/** Content-Security-Policy nonce, when the app sets one. */
|
|
290
|
+
nonce?: string;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Renders `themeInitScript` as an inline `<script>`, so a stored choice is on
|
|
294
|
+
* the root before first paint. Use this from a server layout: this package is
|
|
295
|
+
* a client bundle, so a server component cannot import the plain string, but
|
|
296
|
+
* it can render this component. Put it in `<head>` and add
|
|
297
|
+
* `suppressHydrationWarning` to `<html>` (the script changes its attributes
|
|
298
|
+
* before React hydrates).
|
|
299
|
+
*/
|
|
300
|
+
declare function ThemeScript({ nonce }: ThemeScriptProps): react_jsx_runtime.JSX.Element;
|
|
301
|
+
|
|
302
|
+
export { BrandAnimation, type BrandAnimationProps, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Logo, LogoMonogram, LogoMonogramBg, type LogoMonogramBgProps, type LogoMonogramProps, type LogoProps, LogoWordmark, type LogoWordmarkProps, NavbarLogo, type NavbarLogoProps, type ResolvedTheme, THEME_ATTRIBUTE, THEME_STORAGE_KEY, type ThemePreference, ThemeScript, type ThemeScriptProps, ThemeToggle, type ThemeToggleLabels, type ThemeToggleProps, nextThemePreference, readThemePreference, resolveTheme, setThemePreference, themeInitScript, useResolvedTheme, useThemePreference };
|
package/dist/index.d.ts
CHANGED
|
@@ -201,45 +201,6 @@ interface NavbarLogoProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
|
201
201
|
*/
|
|
202
202
|
declare function NavbarLogo({ name, width, style, ...rest }: NavbarLogoProps): react_jsx_runtime.JSX.Element | null;
|
|
203
203
|
|
|
204
|
-
interface ThemeToggleLabels {
|
|
205
|
-
/** What the control sets, read before the current choice ("Theme: Light"). */
|
|
206
|
-
name: string;
|
|
207
|
-
system: string;
|
|
208
|
-
light: string;
|
|
209
|
-
dark: string;
|
|
210
|
-
}
|
|
211
|
-
interface ThemeToggleProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {
|
|
212
|
-
/** Accessible and tooltip text; pass the property's own wording. */
|
|
213
|
-
labels?: Partial<ThemeToggleLabels>;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Light/dark toggle for an app navbar: one button that steps
|
|
217
|
-
* System → Light → Dark → System. "System" follows the operating system; the
|
|
218
|
-
* other two set `data-bs-theme` on the root, which the house tokens obey.
|
|
219
|
-
*
|
|
220
|
-
* A 48px target (the dataimago touch floor). The icon is `currentColor`.
|
|
221
|
-
* Inline styles would beat a consumer's `:hover` rule, so the two colours a
|
|
222
|
-
* navbar restyles read custom properties instead: set `--di-theme-toggle-fg`
|
|
223
|
-
* and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).
|
|
224
|
-
* Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before
|
|
225
|
-
* first paint.
|
|
226
|
-
*/
|
|
227
|
-
declare function ThemeToggle({ labels, className, style, ...rest }: ThemeToggleProps): react_jsx_runtime.JSX.Element;
|
|
228
|
-
|
|
229
|
-
interface ThemeScriptProps {
|
|
230
|
-
/** Content-Security-Policy nonce, when the app sets one. */
|
|
231
|
-
nonce?: string;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Renders `themeInitScript` as an inline `<script>`, so a stored choice is on
|
|
235
|
-
* the root before first paint. Use this from a server layout: this package is
|
|
236
|
-
* a client bundle, so a server component cannot import the plain string, but
|
|
237
|
-
* it can render this component. Put it in `<head>` and add
|
|
238
|
-
* `suppressHydrationWarning` to `<html>` (the script changes its attributes
|
|
239
|
-
* before React hydrates).
|
|
240
|
-
*/
|
|
241
|
-
declare function ThemeScript({ nonce }: ThemeScriptProps): react_jsx_runtime.JSX.Element;
|
|
242
|
-
|
|
243
204
|
/**
|
|
244
205
|
* Light/dark theme preference for dataimago apps.
|
|
245
206
|
*
|
|
@@ -290,4 +251,52 @@ declare function useThemePreference(): [ThemePreference, (p: ThemePreference) =>
|
|
|
290
251
|
*/
|
|
291
252
|
declare function useResolvedTheme(): ResolvedTheme;
|
|
292
253
|
|
|
293
|
-
|
|
254
|
+
interface ThemeToggleLabels {
|
|
255
|
+
/** What the control sets, read before the theme in effect ("Theme: Light"). */
|
|
256
|
+
name: string;
|
|
257
|
+
/** Added when the theme follows the operating system ("Theme: Light (System)"). */
|
|
258
|
+
system: string;
|
|
259
|
+
light: string;
|
|
260
|
+
dark: string;
|
|
261
|
+
}
|
|
262
|
+
interface ThemeToggleProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {
|
|
263
|
+
/** Accessible and tooltip text; pass the property's own wording. */
|
|
264
|
+
labels?: Partial<ThemeToggleLabels>;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The choice one click makes: the other theme, stored as "system" when that is
|
|
268
|
+
* the operating system's own, so the page goes back to following it.
|
|
269
|
+
*/
|
|
270
|
+
declare function nextThemePreference(shown: ResolvedTheme, system: ResolvedTheme): ThemePreference;
|
|
271
|
+
/**
|
|
272
|
+
* Light/dark toggle for an app navbar: one button, and one click always flips
|
|
273
|
+
* the theme on screen. The page follows the operating system until the first
|
|
274
|
+
* click; a click that lands on the system's own theme goes back to following
|
|
275
|
+
* it (no stored choice), and the other theme is stored and set as
|
|
276
|
+
* `data-bs-theme` on the root, which the house tokens obey. The icon shows the
|
|
277
|
+
* theme in effect: a sun in light, a moon in dark.
|
|
278
|
+
*
|
|
279
|
+
* A 48px target (the dataimago touch floor). The icon is `currentColor`.
|
|
280
|
+
* Inline styles would beat a consumer's `:hover` rule, so the two colours a
|
|
281
|
+
* navbar restyles read custom properties instead: set `--di-theme-toggle-fg`
|
|
282
|
+
* and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).
|
|
283
|
+
* Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before
|
|
284
|
+
* first paint.
|
|
285
|
+
*/
|
|
286
|
+
declare function ThemeToggle({ labels, className, style, ...rest }: ThemeToggleProps): react_jsx_runtime.JSX.Element;
|
|
287
|
+
|
|
288
|
+
interface ThemeScriptProps {
|
|
289
|
+
/** Content-Security-Policy nonce, when the app sets one. */
|
|
290
|
+
nonce?: string;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Renders `themeInitScript` as an inline `<script>`, so a stored choice is on
|
|
294
|
+
* the root before first paint. Use this from a server layout: this package is
|
|
295
|
+
* a client bundle, so a server component cannot import the plain string, but
|
|
296
|
+
* it can render this component. Put it in `<head>` and add
|
|
297
|
+
* `suppressHydrationWarning` to `<html>` (the script changes its attributes
|
|
298
|
+
* before React hydrates).
|
|
299
|
+
*/
|
|
300
|
+
declare function ThemeScript({ nonce }: ThemeScriptProps): react_jsx_runtime.JSX.Element;
|
|
301
|
+
|
|
302
|
+
export { BrandAnimation, type BrandAnimationProps, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Logo, LogoMonogram, LogoMonogramBg, type LogoMonogramBgProps, type LogoMonogramProps, type LogoProps, LogoWordmark, type LogoWordmarkProps, NavbarLogo, type NavbarLogoProps, type ResolvedTheme, THEME_ATTRIBUTE, THEME_STORAGE_KEY, type ThemePreference, ThemeScript, type ThemeScriptProps, ThemeToggle, type ThemeToggleLabels, type ThemeToggleProps, nextThemePreference, readThemePreference, resolveTheme, setThemePreference, themeInitScript, useResolvedTheme, useThemePreference };
|
package/dist/index.js
CHANGED
|
@@ -343,6 +343,9 @@ function NavbarLogo({ name, width, style, ...rest }) {
|
|
|
343
343
|
);
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
// src/theme/ThemeToggle.tsx
|
|
347
|
+
import * as React5 from "react";
|
|
348
|
+
|
|
346
349
|
// src/theme/theme.ts
|
|
347
350
|
import * as React4 from "react";
|
|
348
351
|
var THEME_STORAGE_KEY = "dataimago-theme";
|
|
@@ -435,12 +438,19 @@ var DEFAULT_LABELS = {
|
|
|
435
438
|
light: "Light",
|
|
436
439
|
dark: "Dark"
|
|
437
440
|
};
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
441
|
+
function systemTheme() {
|
|
442
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
443
|
+
}
|
|
444
|
+
function nextThemePreference(shown, system) {
|
|
445
|
+
const next = shown === "dark" ? "light" : "dark";
|
|
446
|
+
return next === system ? "system" : next;
|
|
447
|
+
}
|
|
448
|
+
var noSubscribe = () => () => {
|
|
442
449
|
};
|
|
443
|
-
function
|
|
450
|
+
function useHydrated() {
|
|
451
|
+
return React5.useSyncExternalStore(noSubscribe, () => true, () => false);
|
|
452
|
+
}
|
|
453
|
+
function Icon({ theme }) {
|
|
444
454
|
const common = {
|
|
445
455
|
width: 20,
|
|
446
456
|
height: 20,
|
|
@@ -453,24 +463,21 @@ function Icon({ preference }) {
|
|
|
453
463
|
"aria-hidden": true,
|
|
454
464
|
focusable: false
|
|
455
465
|
};
|
|
456
|
-
if (
|
|
457
|
-
return /* @__PURE__ */ jsxs4("svg", { ...common, children: [
|
|
458
|
-
/* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "4" }),
|
|
459
|
-
/* @__PURE__ */ jsx8("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" })
|
|
460
|
-
] });
|
|
461
|
-
}
|
|
462
|
-
if (preference === "dark") {
|
|
466
|
+
if (theme === "dark") {
|
|
463
467
|
return /* @__PURE__ */ jsx8("svg", { ...common, children: /* @__PURE__ */ jsx8("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) });
|
|
464
468
|
}
|
|
465
469
|
return /* @__PURE__ */ jsxs4("svg", { ...common, children: [
|
|
466
|
-
/* @__PURE__ */ jsx8("
|
|
467
|
-
/* @__PURE__ */ jsx8("path", { d: "
|
|
470
|
+
/* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "4" }),
|
|
471
|
+
/* @__PURE__ */ jsx8("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" })
|
|
468
472
|
] });
|
|
469
473
|
}
|
|
470
474
|
function ThemeToggle({ labels, className = "", style, ...rest }) {
|
|
471
475
|
const [preference, setPreference] = useThemePreference();
|
|
476
|
+
const resolved = useResolvedTheme();
|
|
477
|
+
const hydrated = useHydrated();
|
|
478
|
+
const shown = hydrated ? resolved : void 0;
|
|
472
479
|
const text = { ...DEFAULT_LABELS, ...labels };
|
|
473
|
-
const label = `${text.name}: ${text[
|
|
480
|
+
const label = shown ? `${text.name}: ${text[shown]}${preference === "system" ? ` (${text.system})` : ""}` : text.name;
|
|
474
481
|
return /* @__PURE__ */ jsx8(
|
|
475
482
|
"button",
|
|
476
483
|
{
|
|
@@ -479,6 +486,7 @@ function ThemeToggle({ labels, className = "", style, ...rest }) {
|
|
|
479
486
|
"aria-label": label,
|
|
480
487
|
title: label,
|
|
481
488
|
"data-theme-preference": preference,
|
|
489
|
+
"data-theme": shown,
|
|
482
490
|
style: {
|
|
483
491
|
alignItems: "center",
|
|
484
492
|
background: "var(--di-theme-toggle-bg, transparent)",
|
|
@@ -495,8 +503,8 @@ function ThemeToggle({ labels, className = "", style, ...rest }) {
|
|
|
495
503
|
...style
|
|
496
504
|
},
|
|
497
505
|
type: "button",
|
|
498
|
-
onClick: () => setPreference(
|
|
499
|
-
children: /* @__PURE__ */ jsx8(Icon, {
|
|
506
|
+
onClick: () => setPreference(nextThemePreference(resolveTheme(), systemTheme())),
|
|
507
|
+
children: /* @__PURE__ */ jsx8("span", { style: { display: "inline-flex", visibility: shown ? "visible" : "hidden" }, children: /* @__PURE__ */ jsx8(Icon, { theme: shown ?? "light" }) })
|
|
500
508
|
}
|
|
501
509
|
);
|
|
502
510
|
}
|
|
@@ -518,6 +526,7 @@ export {
|
|
|
518
526
|
THEME_STORAGE_KEY,
|
|
519
527
|
ThemeScript,
|
|
520
528
|
ThemeToggle,
|
|
529
|
+
nextThemePreference,
|
|
521
530
|
readThemePreference,
|
|
522
531
|
resolveTheme,
|
|
523
532
|
setThemePreference,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Button.tsx","../src/brand/LogoMonogram.tsx","../src/brand/LogoMonogramBg.tsx","../src/brand/LogoWordmark.tsx","../src/brand/Logo.tsx","../src/brand/BrandAnimation.tsx","../src/brand/NavbarLogo.tsx","../src/theme/theme.ts","../src/theme/ThemeToggle.tsx","../src/theme/ThemeScript.tsx"],"sourcesContent":["import * as React from 'react';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nconst variantClasses: Record<ButtonVariant, string> = {\n primary: 'di-ui-button--primary',\n secondary: 'di-ui-button--secondary',\n ghost: 'di-ui-button--ghost',\n};\n\nconst sizeClasses: Record<ButtonSize, string> = {\n sm: 'di-ui-button--sm',\n md: 'di-ui-button--md',\n lg: 'di-ui-button--lg',\n};\n\nconst baseStyle: React.CSSProperties = {\n alignItems: 'center',\n borderRadius: 'var(--radius-md, 0.5rem)',\n borderStyle: 'solid',\n borderWidth: '1px',\n cursor: 'pointer',\n display: 'inline-flex',\n fontFamily: 'var(--font-family-sans, system-ui, sans-serif)',\n fontWeight: 'var(--font-weight-medium, 500)',\n gap: 'var(--spacing-sm, 0.5rem)',\n justifyContent: 'center',\n textDecoration: 'none',\n transition: 'background-color 150ms ease, border-color 150ms ease, color 150ms ease',\n};\n\nconst variantStyles: Record<ButtonVariant, React.CSSProperties> = {\n primary: {\n backgroundColor: 'var(--color-interactive-primary)',\n borderColor: 'var(--color-interactive-primary)',\n color: 'var(--color-content-inverse)',\n },\n secondary: {\n backgroundColor: 'var(--color-surface-elevated)',\n borderColor: 'var(--color-border-moderate)',\n color: 'var(--color-content-primary)',\n },\n ghost: {\n backgroundColor: 'transparent',\n borderColor: 'transparent',\n color: 'var(--color-content-secondary)',\n },\n};\n\nconst sizeStyles: Record<ButtonSize, React.CSSProperties> = {\n sm: {\n fontSize: 'var(--font-size-small, 0.875rem)',\n minHeight: '2rem',\n padding: '0.375rem 0.75rem',\n },\n md: {\n fontSize: 'var(--font-size-base, 1rem)',\n minHeight: '2.5rem',\n padding: '0.625rem 1rem',\n },\n lg: {\n fontSize: 'var(--font-size-readable, 1.125rem)',\n minHeight: '3rem',\n padding: '0.75rem 1.25rem',\n },\n};\n\n/**\n * Smoke-level `<Button>` for the alpha release of @dataimago/ui.\n *\n * Styling is delegated to semantic CSS variables published by @dataimago/css\n * so that the component stays small and theme-aware.\n * Consumers that want to override visuals can pass their own className.\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n function Button(\n { variant = 'primary', size = 'md', className = '', style, type, children, ...rest },\n ref,\n ) {\n const classes = [\n 'di-ui-button',\n variantClasses[variant],\n sizeClasses[size],\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <button\n ref={ref}\n type={type ?? 'button'}\n className={classes}\n style={{\n ...baseStyle,\n ...variantStyles[variant],\n ...sizeStyles[size],\n ...style,\n }}\n {...rest}\n >\n {children}\n </button>\n );\n },\n);\n","import * as React from 'react';\n\n/**\n * <LogoMonogram /> — the dataimago 'a + i' superimposed monogram, transparent\n * background. Glyphs are outlined paths (no font dependency at render time);\n * fill defaults to `currentColor` so the mark inherits from the surrounding\n * text color and respects light/dark theme switches automatically.\n *\n * Symbolism: 'a' (data, agency) superimposed with 'i' (imago, AI) — the\n * fusion is the brand's central idea. See wiki/principles/visual-identity.md\n * (Stage L follow-up) for the full brand-mark rationale.\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * ai_monogram_supreme_1_PATH.svg\n *\n * Usage:\n * <LogoMonogram size={48} aria-label=\"dataimago\" />\n * <span style={{ color: 'var(--color-interactive-primary)' }}>\n * <LogoMonogram size={32} />\n * </span>\n */\n\nexport interface LogoMonogramProps extends React.SVGAttributes<SVGSVGElement> {\n /** Render size in px (or any CSS length). Default: 24. Width and height match. */\n size?: number | string;\n /**\n * Accessible name announced by screen readers. Set to `undefined` (or pass\n * `aria-hidden` directly) when the mark is purely decorative.\n */\n title?: string;\n}\n\nexport function LogoMonogram({\n size = 24,\n title = 'dataimago',\n ...rest\n}: LogoMonogramProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 480\"\n width={size}\n height={size}\n fill=\"currentColor\"\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* 'i' (imago) glyph */}\n <path d=\"m 337.3457,97.089844 q 0,8.906246 -6.49414,15.400386 -6.49414,6.49415 -15.58594,6.49415 -9.09179,0 -15.77148,-6.67969 Q 293,105.25391 293,96.162109 q 0,-9.091797 6.30859,-15.40039 6.49414,-6.494141 15.40039,-6.494141 9.0918,0 15.77149,6.865234 6.86523,6.679688 6.86523,15.957032 z M 324.54297,180.21484 303.01953,355 h -41.00586 l 21.52344,-174.78516 z\" />\n {/* 'a' (data) glyph */}\n <path d=\"M 325.70312,176.01562 303.04687,360 h -43.16406 l 2.53906,-19.92188 Q 234.6875,364.29687 205,364.29687 q -36.52344,0 -59.57031,-24.80468 -23.04688,-24.80469 -23.04688,-64.25782 0,-45.50781 26.36719,-74.60937 26.75781,-29.10156 68.55469,-29.10156 19.72656,0 33.59375,6.44531 14.0625,6.44531 28.71093,22.26562 l 2.92969,-24.21875 z m -52.53906,86.71875 q 0,-24.41406 -14.0625,-39.64843 -14.0625,-15.42969 -36.32812,-15.42969 -24.21875,0 -40.625,18.94531 -16.40625,19.14063 -16.40625,46.875 0,24.21875 13.67187,39.64844 13.67188,15.42969 35.15625,15.42969 23.82813,0 41.21094,-19.33594 17.38281,-19.72656 17.38281,-46.48438 z\" />\n </svg>\n );\n}\n","import * as React from 'react';\n\n/**\n * <LogoMonogramBg /> — the dataimago 'a + i' monogram with a rounded accent\n * backplate. White letters on the brand-accent role\n * (var(--color-interactive-accent), iteration-2 sky-blue).\n *\n * Use this variant on neutral surfaces where the no-background monogram\n * would lack presence — favicons, app icons, social-media avatars, or any\n * branded chrome where the mark should read as a self-contained badge.\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * ai_monogram_supreme_bg_1_PATH.svg\n *\n * Theming: the background uses var(--color-interactive-accent), which\n * resolves via theme-colors.json's mode-aware role mapping — accent-500\n * (#1f618d) on light surfaces, accent-300 (#3498DB) on dark. The letter\n * fill is hard-set to white; both accent stops have AA-or-better contrast\n * with white letters (light-mode 6.66:1 AA, dark-mode 3.15:1 AA-large for\n * the dot-and-stem heading-size letters used in the monogram). The\n * literal-fallback in this component is light-mode (#1f618d, sky-blue\n * iteration-2) for rare non-CSS-aware contexts. Iteration-1's copper-red\n * fallback (#b54a27) was retired in iteration-2-B.2; see\n * wiki/decisions/iteration-2-blue-accent.md for the rationale.\n *\n * Usage:\n * <LogoMonogramBg size={64} aria-label=\"dataimago\" />\n */\n\nexport interface LogoMonogramBgProps extends React.SVGAttributes<SVGSVGElement> {\n /** Render size in px. Default: 32 (favicon-sized). Width and height match. */\n size?: number | string;\n /** Accessible name. Set undefined or pass aria-hidden for decorative use. */\n title?: string;\n}\n\nexport function LogoMonogramBg({\n size = 32,\n title = 'dataimago',\n ...rest\n}: LogoMonogramBgProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 480\"\n width={size}\n height={size}\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* Rounded accent backplate — themed via --color-interactive-accent\n (mode-aware: accent-500 #1f618d on light, accent-300 #3498DB on dark).\n Literal fallback is the light-mode canonical for non-CSS consumers. */}\n <rect\n width=\"100%\"\n height=\"100%\"\n rx=\"40\"\n ry=\"40\"\n fill=\"var(--color-interactive-accent, #1f618d)\"\n />\n {/* 'a' (data) glyph in white */}\n <path\n fill=\"#ffffff\"\n d=\"M 325.70312,176.01562 303.04687,360 h -43.16406 l 2.53906,-19.92188 Q 234.6875,364.29687 205,364.29687 q -36.52344,0 -59.57031,-24.80468 -23.04688,-24.80469 -23.04688,-64.25782 0,-45.50781 26.36719,-74.60937 26.75781,-29.10156 68.55469,-29.10156 19.72656,0 33.59375,6.44531 14.0625,6.44531 28.71093,22.26562 l 2.92969,-24.21875 z m -52.53906,86.71875 q 0,-24.41406 -14.0625,-39.64843 -14.0625,-15.42969 -36.32812,-15.42969 -24.21875,0 -40.625,18.94531 -16.40625,19.14063 -16.40625,46.875 0,24.21875 13.67187,39.64844 13.67188,15.42969 35.15625,15.42969 23.82813,0 41.21094,-19.33594 17.38281,-19.72656 17.38281,-46.48438 z\"\n />\n {/* 'i' (imago) glyph in white */}\n <path\n fill=\"#ffffff\"\n d=\"m 337.3457,97.089844 q 0,8.906246 -6.49414,15.400386 -6.49414,6.49415 -15.58594,6.49415 -9.09179,0 -15.77148,-6.67969 Q 293,105.25391 293,96.162109 q 0,-9.091797 6.30859,-15.40039 6.49414,-6.494141 15.40039,-6.494141 9.0918,0 15.77149,6.865234 6.86523,6.679688 6.86523,15.957032 z M 324.54297,180.21484 303.01953,355 h -41.00586 l 21.52344,-174.78516 z\"\n />\n </svg>\n );\n}\n","import * as React from 'react';\n\n/**\n * <LogoWordmark /> — the full \"dataimago\" wordmark with the 'ai' overlap.\n * White letters on a rounded accent backplate, themed via\n * var(--color-interactive-accent).\n *\n * Use this variant for primary brand surfaces: site headers, document\n * letterheads, large-format presentations, business materials. For compact\n * contexts (favicons, social avatars) use <LogoMonogramBg /> instead; for\n * inline use within prose use <LogoMonogram /> with currentColor.\n *\n * The wordmark preserves the philosophical idea of the brand: the visible\n * superposition of 'a' (data, human agency) and 'i' (imago, AI) creates a new\n * unified glyph that is neither pure human nor pure artificial — genuine\n * collaboration. See wiki/principles/visual-identity.md (Stage L follow-up).\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * dataimago_supreme_1_PATH.svg\n *\n * Theming: the backplate uses var(--color-interactive-accent), which resolves\n * via theme-colors.json's mode-aware role mapping — accent-500 (#1f618d) on\n * light surfaces, accent-300 (#3498DB) on dark. The literal-fallback in this\n * component is light-mode (#1f618d, sky-blue iteration-2) for the rare cases\n * where the SVG is consumed without CSS-variable resolution (printed PDFs from\n * raw SVG, third-party platforms that strip CSS). Iteration-1's copper-red\n * fallback (#b54a27) was retired in iteration-2-B.2; see\n * wiki/decisions/iteration-2-blue-accent.md for the rationale.\n *\n * Usage:\n * <LogoWordmark width={240} aria-label=\"dataimago\" />\n *\n * The wordmark's natural aspect ratio is 480x150 (~3.2:1). Pass `width` and\n * height auto-scales, or pass both explicitly.\n */\n\nexport interface LogoWordmarkProps extends React.SVGAttributes<SVGSVGElement> {\n /** Width in px. Default: 240. Height auto-scales to the 480:150 ratio. */\n width?: number | string;\n /** Accessible name. Set undefined or pass aria-hidden for decorative use. */\n title?: string;\n}\n\nexport function LogoWordmark({\n width = 240,\n title = 'dataimago',\n ...rest\n}: LogoWordmarkProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 150\"\n width={width}\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* Rounded accent backplate — themed via --color-interactive-accent\n (mode-aware: accent-500 #1f618d on light, accent-300 #3498DB on dark).\n Literal fallback is the light-mode canonical for non-CSS consumers. */}\n <rect\n width=\"100%\"\n height=\"100%\"\n rx=\"5\"\n ry=\"5\"\n fill=\"var(--color-interactive-accent, #1f618d)\"\n />\n {/* \"data\" — outlined glyphs, white fill */}\n <path\n fill=\"#ffffff\"\n d=\"M 104.80469,35.621094 97.492187,95 h -7.734375 l 0.421875,-3.550781 q -2.882812,2.566406 -4.992187,3.480468 -2.074219,0.84375 -5.132813,0.84375 -6.960937,0 -11.390625,-4.605468 -4.429687,-4.640625 -4.429687,-11.953125 0,-7.804688 4.851562,-12.972657 4.851563,-5.167968 12.199219,-5.167968 3.761719,0 6.328125,1.300781 2.671875,1.335937 5.589844,4.535156 L 97.035156,35.621094 Z M 92.007812,77 q 0,-4.21875 -2.601562,-6.820313 -2.601563,-2.601562 -6.820313,-2.601562 -4.570312,0 -7.558593,3.410156 -2.988282,3.410156 -2.988282,8.613281 0,4.394532 2.566407,7.066407 2.566406,2.636718 6.820312,2.636718 4.429688,0 7.488281,-3.585937 Q 92.007812,82.203125 92.007812,77 Z M 145.51562,61.882812 141.4375,95 h -7.76953 l 0.45703,-3.585938 q -4.99219,4.359375 -10.33594,4.359375 -6.57422,0 -10.72265,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.74609,-13.429687 4.81641,-5.238281 12.33985,-5.238281 3.55078,0 6.04687,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52734,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53906,-2.777344 -4.35937,0 -7.3125,3.410156 -2.95312,3.445313 -2.95312,8.4375 0,4.359375 2.46093,7.136719 2.46094,2.777344 6.32813,2.777344 4.28906,0 7.41797,-3.480469 3.1289,-3.550781 3.1289,-8.367188 z M 163.79687,69.089844 160.59766,95 h -7.76954 l 3.19922,-25.910156 h -4.39453 l 0.87891,-7.207032 h 4.39453 l 1.89844,-15.363281 h 7.76953 l -1.89844,15.363281 h 7.17188 l -0.87891,7.207032 z M 211.29297,61.882812 207.21484,95 h -7.76953 l 0.45703,-3.585938 q -4.99218,4.359375 -10.33593,4.359375 -6.57422,0 -10.72266,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.7461,-13.429687 4.8164,-5.238281 12.33984,-5.238281 3.55078,0 6.04687,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52735,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53907,-2.777344 -4.35937,0 -7.3125,3.410156 -2.95312,3.445313 -2.95312,8.4375 0,4.359375 2.46094,7.136719 2.46093,2.777344 6.32812,2.777344 4.28906,0 7.41797,-3.480469 3.12891,-3.550781 3.12891,-8.367188 z\"\n />\n {/* \"imago\" — outlined glyphs, white fill, superimposed at the 'a/i' overlap */}\n <path\n fill=\"#ffffff\"\n d=\"m 213.40234,46.132812 q 0,1.6875 -1.23047,2.917969 -1.23046,1.230469 -2.95312,1.230469 -1.72266,0 -2.98828,-1.265625 Q 205,47.679687 205,45.957031 q 0,-1.722656 1.19531,-2.917969 1.23047,-1.230468 2.91797,-1.230468 1.72266,0 2.98828,1.300781 1.30078,1.265625 1.30078,3.023437 z m -2.42578,15.75 L 206.89844,95 h -7.76953 l 4.07812,-33.117188 z m 18.87891,0 -0.35156,3.09375 q 3.9375,-3.9375 9.10546,-3.9375 3.12891,0 4.81641,1.160157 1.6875,1.089843 3.02344,4.113281 4.92187,-5.238281 10.61719,-5.238281 9.84375,0 9.84375,9.035156 0,0.914062 -0.17579,2.566406 -0.14062,1.652344 -0.42187,4.078125 L 264.0625,95 h -7.76953 l 2.39062,-19.160156 Q 259,73.167969 259,71.902344 q 0,-4.324219 -4.35938,-4.324219 -1.72265,0 -2.98828,0.527344 -1.26562,0.492187 -2.17968,1.582031 -0.87891,1.054687 -1.47657,2.777344 -0.5625,1.6875 -0.8789,4.042968 L 244.86719,95 h -7.76953 l 2.42578,-19.652344 q 0.3164,-2.671875 0.3164,-3.65625 0,-4.113281 -4.46484,-4.113281 -6.46875,0 -7.55859,8.964844 L 225.53125,95 h -7.76953 l 4.07812,-33.117188 z m 81.63281,0 L 307.41016,95 h -7.76954 l 0.45704,-3.585938 q -4.99219,4.359375 -10.33594,4.359375 -6.57422,0 -10.72266,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.7461,-13.429687 4.8164,-5.238281 12.33984,-5.238281 3.55078,0 6.04688,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52734,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53906,-2.777344 -4.35938,0 -7.3125,3.410156 -2.95313,3.445313 -2.95313,8.4375 0,4.359375 2.46094,7.136719 2.46094,2.777344 6.32812,2.777344 4.28907,0 7.41797,-3.480469 3.12891,-3.550781 3.12891,-8.367188 z m 53.57812,-15.609375 -4.07812,33.363282 q -0.38672,2.988281 -0.87891,5.203126 -0.45703,2.21484 -1.05468,3.72656 -1.23047,3.09375 -3.65625,5.3086 -4.71094,4.21875 -12.23438,4.21875 -7.76953,0 -12.58594,-4.07813 -2.46093,-2.03906 -3.44531,-4.64062 -0.98437,-2.39063 -0.98437,-6.714849 h 7.52343 q 0.28125,4.675779 2.39063,6.785159 2.10937,2.14453 6.53906,2.14453 4.78125,0 7.27734,-2.56641 2.4961,-2.53125 3.16407,-8.085935 l 0.5625,-4.535156 q -4.92188,3.761718 -10.33594,3.761718 -6.75,0 -11.14453,-4.605468 -4.39453,-4.710938 -4.39453,-11.742188 0,-7.910156 5.09765,-13.113281 5.13282,-5.238281 12.83203,-5.238281 3.69141,0 5.97657,1.089843 2.49609,1.054688 5.16797,3.832032 l 0.49218,-4.113282 z m -8.92968,15.714844 q 0,-4.5 -2.74219,-7.242187 -2.74219,-2.777344 -7.17188,-2.777344 -4.60546,0 -7.66406,3.375 -3.02344,3.339844 -3.02344,8.402344 0,4.429687 2.60157,7.242187 2.63672,2.707031 6.96093,2.707031 4.81641,0 7.91016,-3.304687 3.12891,-3.339844 3.12891,-8.402344 z m 52.3125,0.527344 q 0,7.59375 -5.27344,12.761719 -5.23828,5.167968 -12.9375,5.167968 -8.05078,0 -13.14844,-4.886718 Q 362.5,86.246094 362.5,78.722656 q 0,-7.699219 5.30859,-12.796875 5.34375,-5.132812 13.35938,-5.132812 7.875,0 12.83203,4.851562 4.99219,4.78125 4.99219,12.480469 z m -7.83985,-0.386719 q 0,-4.78125 -2.77734,-7.558594 -2.77734,-2.882812 -7.27734,-2.882812 -4.67579,0 -7.73438,3.199219 -3.05859,3.234375 -3.05859,8.121093 0,4.921875 2.88281,7.910157 2.88281,3.058593 7.45312,3.058593 4.57032,0 7.52344,-3.339843 2.98828,-3.304688 2.98828,-8.507813 z\"\n />\n </svg>\n );\n}\n","import * as React from 'react';\nimport { svgs, type LogoId, type LogoVariant, type LogoKey } from '@dataimago/brand';\n\nexport interface LogoProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Mark id, e.g. `dissertation_ai_OVERLAP`. */\n name: LogoId;\n /** PATH (default), CUTOUT, BG, or LETTERS. See @dataimago/brand. */\n variant?: LogoVariant;\n /**\n * Render width (px number or any CSS length). Height scales proportionally\n * via the SVG viewBox. Omit to use the mark's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * Manifest-driven brand mark from `@dataimago/brand`. Inlines the SVG string so\n * `currentColor` (PATH/LETTERS), the `--color-interactive-accent` badge fill\n * (CUTOUT/BG), and the per-letter interaction classes (`.glyph`, `.logo-*`) all\n * work — an `<img src>` can't receive those interior styles.\n *\n * Color flows from CSS: set `color` (PATH/LETTERS) on this element or an\n * ancestor. Size via `width` (or CSS on the wrapper).\n */\nexport function Logo({ name, variant = 'PATH', width, style, ...rest }: LogoProps) {\n const key = `${name}_${variant}` as LogoKey;\n let markup = svgs[key];\n\n if (!markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <Logo>: unknown mark \"${key}\".`);\n return null;\n }\n\n if (width != null) {\n // Set width + drop height so the viewBox preserves the aspect ratio.\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n // The SVG is generated, font-free, and carries its own <title>/aria-label.\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport {\n animations,\n animationManifest,\n type AnimationId,\n type LogoId,\n type LogoVariant,\n} from '@dataimago/brand';\nimport { Logo } from './Logo';\n\nexport interface BrandAnimationProps\n extends React.HTMLAttributes<HTMLSpanElement> {\n /** Animation id, e.g. `dissertation-ai`. See @dataimago/brand animations.json. */\n name: AnimationId;\n /**\n * Render width (px number or any CSS length). Height scales via the SVG\n * viewBox. Omit to use the animation's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * SSR-safe `prefers-reduced-motion` detection. Starts `true` (reduced) so the\n * server and the first client render agree on the static poster — no hydration\n * mismatch, and no motion flashes before JS decides. After mount it reflects the\n * real media query and updates live if the user toggles the OS setting.\n */\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(true);\n React.useEffect(() => {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReduced(mq.matches);\n const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/**\n * Decorative brand animation from `@dataimago/brand` (the animation-foundry).\n * Inlines the pure-SMIL SVG string so its interior `<style>` works — an\n * `<img src>` can't receive those styles (same rationale as `<Logo>`).\n *\n * Motion-dignity: decorative brand animation is a distinct category from the\n * 300ms UI-transition ceiling, but it is contractually gated on\n * `prefers-reduced-motion`. When the user prefers reduced motion this renders\n * the static brand mark the animation settles to (its `poster`) via `<Logo>`,\n * so no consumer can ship the motion without the fallback.\n */\nexport function BrandAnimation({\n name,\n width,\n style,\n ...rest\n}: BrandAnimationProps) {\n const reduced = usePrefersReducedMotion();\n const entry = animationManifest.find((a) => a.id === name);\n let markup = animations[name];\n\n if (!entry || !markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <BrandAnimation>: unknown animation \"${name}\".`);\n return null;\n }\n\n if (reduced) {\n // Static settle-frame: the brand mark the animation resolves to.\n return (\n <Logo\n name={entry.poster.id as LogoId}\n variant={entry.poster.variant as LogoVariant}\n width={width}\n style={style}\n {...rest}\n />\n );\n }\n\n if (width != null) {\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport {\n navbarLogos,\n navbarManifest,\n type NavbarLogoId,\n type LogoId,\n type LogoVariant,\n} from '@dataimago/brand';\nimport { Logo } from './Logo';\n\nexport interface NavbarLogoProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Navbar mark id, e.g. `dissertation-ai`. See @dataimago/brand navbar.json. */\n name: NavbarLogoId;\n /**\n * Render width (px number or any CSS length). Height scales via the SVG\n * viewBox. Omit to use the mark's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * SSR-safe `prefers-reduced-motion`. Starts `true` (reduced) so server and first\n * client render agree on the static poster — no hydration mismatch, no motion\n * flash. After mount it reflects the real query and updates live. (Mirrors\n * BrandAnimation; kept local to avoid a shared-hook refactor.)\n */\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(true);\n React.useEffect(() => {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReduced(mq.matches);\n const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/**\n * Animated brand mark for an app navbar, from `@dataimago/brand` (the\n * animation-foundry's navbar set). Inlines the self-contained CSS-animated SVG\n * so its interior `<style>` works — an `<img src>` can't receive those styles.\n *\n * Behaviour by mark kind (from the navbar manifest):\n * - `assemble` (L2: dissertation-ai, rpkg-ai, SGPc-ai): the trailing `a`/`i`\n * start apart and the `i` slides onto the `a`. Plays once on mount; replays\n * on pointer-enter via a remount (`replayKey`).\n * - `shimmer` (L1: dataimago-ai): the monogram is always superimposed; an\n * accent shimmer sweeps on `:hover` from the SVG's own CSS (no JS trigger).\n * - `dream` (promoted dream marks, `tools/dream-foundry/drop.mjs promote`):\n * a self-contained CSS hover gesture like liftwash; the poster is the mark's\n * own static form (`dream_<slug>` / `DREAM`). Ink is `currentColor`.\n *\n * Motion-dignity: the embedded CSS animation is <= 300ms and gated on\n * `@media (prefers-reduced-motion: no-preference)`. When the user prefers\n * reduced motion this renders the static settled mark (the manifest `poster`,\n * the superimposed monogram) via `<Logo>` — so no consumer can ship the motion\n * without the fallback.\n */\nexport function NavbarLogo({ name, width, style, ...rest }: NavbarLogoProps) {\n const reduced = usePrefersReducedMotion();\n\n const entry = navbarManifest.find((n) => n.id === name);\n let markup = navbarLogos[name];\n\n if (!entry || !markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <NavbarLogo>: unknown navbar mark \"${name}\".`);\n return null;\n }\n\n if (reduced) {\n // Static settle-frame: the superimposed mark the animation resolves to.\n return (\n <Logo\n name={entry.poster.id as LogoId}\n variant={entry.poster.variant as LogoVariant}\n width={width}\n style={style}\n {...rest}\n />\n );\n }\n\n if (width != null) {\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n // The SVG is fully self-driving: it plays the assemble once on mount (its\n // `.di-play` class) and runs the smooth hover round-trip from its own `:hover`\n // CSS — no remount/replay logic here (remounting is what caused the teleport).\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n // Self-contained, font-free SVG; carries its own <title>/aria-label.\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","import * as React from 'react';\n\n/**\n * Light/dark theme preference for dataimago apps.\n *\n * The house tokens (`@dataimago/tokens`) follow the operating system through\n * `prefers-color-scheme` and yield to an explicit `data-bs-theme` on the root:\n * `light` or `dark` forces that theme, no attribute means \"follow the system\".\n * This module owns that attribute: it reads and stores the reader's choice,\n * applies it, and reports the theme actually in effect (for canvas and WebGL\n * surfaces that cannot read CSS variables).\n *\n * The choice is a per-browser convenience in localStorage. Every read and write\n * is guarded: in a private window or with storage blocked the page simply\n * follows the system.\n */\n\nexport type ThemePreference = 'system' | 'light' | 'dark';\nexport type ResolvedTheme = 'light' | 'dark';\n\nexport const THEME_STORAGE_KEY = 'dataimago-theme';\nexport const THEME_ATTRIBUTE = 'data-bs-theme';\n\n/** Fired on `window` whenever `setThemePreference` changes the choice. */\nconst CHANGE_EVENT = 'dataimago:themechange';\n\n/**\n * Inline this in `<head>` before any stylesheet paints, so a stored choice is\n * on the root before first paint (no flash of the other theme). The server\n * markup cannot know the choice, so the consumer also puts\n * `suppressHydrationWarning` on `<html>`. From a React server layout, render\n * `<ThemeScript />` instead: this package is a client bundle, so a server\n * component cannot import the string itself.\n *\n * ```tsx\n * <html lang=\"en\" suppressHydrationWarning>\n * <head><ThemeScript /></head>\n * ```\n */\nexport const themeInitScript =\n `(function(){try{var p=localStorage.getItem(${JSON.stringify(THEME_STORAGE_KEY)});` +\n `if(p==='light'||p==='dark'){document.documentElement.setAttribute(${JSON.stringify(THEME_ATTRIBUTE)},p);}}` +\n `catch(e){}})();`;\n\nfunction isPreference(value: unknown): value is ThemePreference {\n return value === 'system' || value === 'light' || value === 'dark';\n}\n\n/** The stored choice, or `'system'` when there is none (or storage is unavailable). */\nexport function readThemePreference(): ThemePreference {\n if (typeof window === 'undefined') return 'system';\n try {\n const stored = window.localStorage.getItem(THEME_STORAGE_KEY);\n return isPreference(stored) ? stored : 'system';\n } catch {\n return 'system';\n }\n}\n\nfunction applyAttribute(preference: ThemePreference): void {\n const root = document.documentElement;\n if (preference === 'system') root.removeAttribute(THEME_ATTRIBUTE);\n else root.setAttribute(THEME_ATTRIBUTE, preference);\n}\n\n/** Store, apply and announce a choice. `'system'` clears the stored choice. */\nexport function setThemePreference(preference: ThemePreference): void {\n if (typeof window === 'undefined') return;\n try {\n if (preference === 'system') window.localStorage.removeItem(THEME_STORAGE_KEY);\n else window.localStorage.setItem(THEME_STORAGE_KEY, preference);\n } catch {\n // Storage blocked: the choice still applies to this page view.\n }\n applyAttribute(preference);\n window.dispatchEvent(new Event(CHANGE_EVENT));\n}\n\n/** The theme in effect: the root attribute when set, otherwise the system's. */\nexport function resolveTheme(): ResolvedTheme {\n if (typeof document === 'undefined') return 'light';\n const forced = document.documentElement.getAttribute(THEME_ATTRIBUTE);\n if (forced === 'light' || forced === 'dark') return forced;\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\n/**\n * Put the stored choice on the root. `ThemeScript` does this before first\n * paint when it is server-rendered; this covers an app without it (or one that\n * inserts it on the client, where an inline script never runs). Idempotent.\n */\nfunction syncAttribute(): void {\n applyAttribute(readThemePreference());\n}\n\nfunction subscribePreference(onChange: () => void): () => void {\n syncAttribute();\n // Another tab changed the choice: follow it here too.\n const onStorage = (e: StorageEvent) => {\n if (e.key !== null && e.key !== THEME_STORAGE_KEY) return;\n applyAttribute(readThemePreference());\n onChange();\n };\n window.addEventListener(CHANGE_EVENT, onChange);\n window.addEventListener('storage', onStorage);\n return () => {\n window.removeEventListener(CHANGE_EVENT, onChange);\n window.removeEventListener('storage', onStorage);\n };\n}\n\nfunction subscribeResolved(onChange: () => void): () => void {\n // Watch first, then restore: the observer reports the restored attribute,\n // so a canvas surface hears about a stored choice applied on mount.\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [THEME_ATTRIBUTE],\n });\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n media.addEventListener('change', onChange);\n window.addEventListener(CHANGE_EVENT, onChange);\n syncAttribute();\n return () => {\n observer.disconnect();\n media.removeEventListener('change', onChange);\n window.removeEventListener(CHANGE_EVENT, onChange);\n };\n}\n\n/**\n * The reader's choice and a setter. Server render and hydration see\n * `'system'`; the stored choice arrives right after.\n */\nexport function useThemePreference(): [ThemePreference, (p: ThemePreference) => void] {\n const preference = React.useSyncExternalStore(\n subscribePreference,\n readThemePreference,\n () => 'system' as const,\n );\n return [preference, setThemePreference];\n}\n\n/**\n * The theme in effect, for surfaces that cannot read CSS variables (canvas,\n * WebGL maps). Follows the toggle and, under \"system\", the operating system.\n * Server render and hydration see `'light'`.\n */\nexport function useResolvedTheme(): ResolvedTheme {\n return React.useSyncExternalStore(subscribeResolved, resolveTheme, () => 'light' as const);\n}\n","import * as React from 'react';\nimport { useThemePreference, type ThemePreference } from './theme.js';\n\nexport interface ThemeToggleLabels {\n /** What the control sets, read before the current choice (\"Theme: Light\"). */\n name: string;\n system: string;\n light: string;\n dark: string;\n}\n\nexport interface ThemeToggleProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {\n /** Accessible and tooltip text; pass the property's own wording. */\n labels?: Partial<ThemeToggleLabels>;\n}\n\nconst DEFAULT_LABELS: ThemeToggleLabels = {\n name: 'Theme',\n system: 'System',\n light: 'Light',\n dark: 'Dark',\n};\n\nconst NEXT: Record<ThemePreference, ThemePreference> = {\n system: 'light',\n light: 'dark',\n dark: 'system',\n};\n\nfunction Icon({ preference }: { preference: ThemePreference }) {\n const common = {\n width: 20,\n height: 20,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.75,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n 'aria-hidden': true,\n focusable: false,\n };\n if (preference === 'light') {\n return (\n <svg {...common}>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41\" />\n </svg>\n );\n }\n if (preference === 'dark') {\n return (\n <svg {...common}>\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n );\n }\n return (\n <svg {...common}>\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M8 21h8M12 17v4\" />\n </svg>\n );\n}\n\n/**\n * Light/dark toggle for an app navbar: one button that steps\n * System → Light → Dark → System. \"System\" follows the operating system; the\n * other two set `data-bs-theme` on the root, which the house tokens obey.\n *\n * A 48px target (the dataimago touch floor). The icon is `currentColor`.\n * Inline styles would beat a consumer's `:hover` rule, so the two colours a\n * navbar restyles read custom properties instead: set `--di-theme-toggle-fg`\n * and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).\n * Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before\n * first paint.\n */\nexport function ThemeToggle({ labels, className = '', style, ...rest }: ThemeToggleProps) {\n const [preference, setPreference] = useThemePreference();\n const text = { ...DEFAULT_LABELS, ...labels };\n const label = `${text.name}: ${text[preference]}`;\n return (\n <button\n {...rest}\n className={['di-ui-theme-toggle', className].filter(Boolean).join(' ')}\n aria-label={label}\n title={label}\n data-theme-preference={preference}\n style={{\n alignItems: 'center',\n background: 'var(--di-theme-toggle-bg, transparent)',\n border: 0,\n borderRadius: 'var(--radius-md, 0.5rem)',\n color: 'var(--di-theme-toggle-fg, inherit)',\n cursor: 'pointer',\n display: 'inline-flex',\n height: '48px',\n justifyContent: 'center',\n padding: 0,\n transition: 'background-color 150ms ease',\n width: '48px',\n ...style,\n }}\n // After the spread, so the control never submits a form or loses its cycle.\n type=\"button\"\n onClick={() => setPreference(NEXT[preference])}\n >\n <Icon preference={preference} />\n </button>\n );\n}\n","import * as React from 'react';\nimport { themeInitScript } from './theme.js';\n\nexport interface ThemeScriptProps {\n /** Content-Security-Policy nonce, when the app sets one. */\n nonce?: string;\n}\n\n/**\n * Renders `themeInitScript` as an inline `<script>`, so a stored choice is on\n * the root before first paint. Use this from a server layout: this package is\n * a client bundle, so a server component cannot import the plain string, but\n * it can render this component. Put it in `<head>` and add\n * `suppressHydrationWarning` to `<html>` (the script changes its attributes\n * before React hydrates).\n */\nexport function ThemeScript({ nonce }: ThemeScriptProps) {\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: themeInitScript }} />;\n}\n"],"mappings":"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","names":["Button","jsx","jsx","jsxs","jsx","jsxs","jsx","React","jsx","React","jsx","usePrefersReducedMotion","React","jsx","jsxs","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/Button.tsx","../src/brand/LogoMonogram.tsx","../src/brand/LogoMonogramBg.tsx","../src/brand/LogoWordmark.tsx","../src/brand/Logo.tsx","../src/brand/BrandAnimation.tsx","../src/brand/NavbarLogo.tsx","../src/theme/ThemeToggle.tsx","../src/theme/theme.ts","../src/theme/ThemeScript.tsx"],"sourcesContent":["import * as React from 'react';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nconst variantClasses: Record<ButtonVariant, string> = {\n primary: 'di-ui-button--primary',\n secondary: 'di-ui-button--secondary',\n ghost: 'di-ui-button--ghost',\n};\n\nconst sizeClasses: Record<ButtonSize, string> = {\n sm: 'di-ui-button--sm',\n md: 'di-ui-button--md',\n lg: 'di-ui-button--lg',\n};\n\nconst baseStyle: React.CSSProperties = {\n alignItems: 'center',\n borderRadius: 'var(--radius-md, 0.5rem)',\n borderStyle: 'solid',\n borderWidth: '1px',\n cursor: 'pointer',\n display: 'inline-flex',\n fontFamily: 'var(--font-family-sans, system-ui, sans-serif)',\n fontWeight: 'var(--font-weight-medium, 500)',\n gap: 'var(--spacing-sm, 0.5rem)',\n justifyContent: 'center',\n textDecoration: 'none',\n transition: 'background-color 150ms ease, border-color 150ms ease, color 150ms ease',\n};\n\nconst variantStyles: Record<ButtonVariant, React.CSSProperties> = {\n primary: {\n backgroundColor: 'var(--color-interactive-primary)',\n borderColor: 'var(--color-interactive-primary)',\n color: 'var(--color-content-inverse)',\n },\n secondary: {\n backgroundColor: 'var(--color-surface-elevated)',\n borderColor: 'var(--color-border-moderate)',\n color: 'var(--color-content-primary)',\n },\n ghost: {\n backgroundColor: 'transparent',\n borderColor: 'transparent',\n color: 'var(--color-content-secondary)',\n },\n};\n\nconst sizeStyles: Record<ButtonSize, React.CSSProperties> = {\n sm: {\n fontSize: 'var(--font-size-small, 0.875rem)',\n minHeight: '2rem',\n padding: '0.375rem 0.75rem',\n },\n md: {\n fontSize: 'var(--font-size-base, 1rem)',\n minHeight: '2.5rem',\n padding: '0.625rem 1rem',\n },\n lg: {\n fontSize: 'var(--font-size-readable, 1.125rem)',\n minHeight: '3rem',\n padding: '0.75rem 1.25rem',\n },\n};\n\n/**\n * Smoke-level `<Button>` for the alpha release of @dataimago/ui.\n *\n * Styling is delegated to semantic CSS variables published by @dataimago/css\n * so that the component stays small and theme-aware.\n * Consumers that want to override visuals can pass their own className.\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n function Button(\n { variant = 'primary', size = 'md', className = '', style, type, children, ...rest },\n ref,\n ) {\n const classes = [\n 'di-ui-button',\n variantClasses[variant],\n sizeClasses[size],\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <button\n ref={ref}\n type={type ?? 'button'}\n className={classes}\n style={{\n ...baseStyle,\n ...variantStyles[variant],\n ...sizeStyles[size],\n ...style,\n }}\n {...rest}\n >\n {children}\n </button>\n );\n },\n);\n","import * as React from 'react';\n\n/**\n * <LogoMonogram /> — the dataimago 'a + i' superimposed monogram, transparent\n * background. Glyphs are outlined paths (no font dependency at render time);\n * fill defaults to `currentColor` so the mark inherits from the surrounding\n * text color and respects light/dark theme switches automatically.\n *\n * Symbolism: 'a' (data, agency) superimposed with 'i' (imago, AI) — the\n * fusion is the brand's central idea. See wiki/principles/visual-identity.md\n * (Stage L follow-up) for the full brand-mark rationale.\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * ai_monogram_supreme_1_PATH.svg\n *\n * Usage:\n * <LogoMonogram size={48} aria-label=\"dataimago\" />\n * <span style={{ color: 'var(--color-interactive-primary)' }}>\n * <LogoMonogram size={32} />\n * </span>\n */\n\nexport interface LogoMonogramProps extends React.SVGAttributes<SVGSVGElement> {\n /** Render size in px (or any CSS length). Default: 24. Width and height match. */\n size?: number | string;\n /**\n * Accessible name announced by screen readers. Set to `undefined` (or pass\n * `aria-hidden` directly) when the mark is purely decorative.\n */\n title?: string;\n}\n\nexport function LogoMonogram({\n size = 24,\n title = 'dataimago',\n ...rest\n}: LogoMonogramProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 480\"\n width={size}\n height={size}\n fill=\"currentColor\"\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* 'i' (imago) glyph */}\n <path d=\"m 337.3457,97.089844 q 0,8.906246 -6.49414,15.400386 -6.49414,6.49415 -15.58594,6.49415 -9.09179,0 -15.77148,-6.67969 Q 293,105.25391 293,96.162109 q 0,-9.091797 6.30859,-15.40039 6.49414,-6.494141 15.40039,-6.494141 9.0918,0 15.77149,6.865234 6.86523,6.679688 6.86523,15.957032 z M 324.54297,180.21484 303.01953,355 h -41.00586 l 21.52344,-174.78516 z\" />\n {/* 'a' (data) glyph */}\n <path d=\"M 325.70312,176.01562 303.04687,360 h -43.16406 l 2.53906,-19.92188 Q 234.6875,364.29687 205,364.29687 q -36.52344,0 -59.57031,-24.80468 -23.04688,-24.80469 -23.04688,-64.25782 0,-45.50781 26.36719,-74.60937 26.75781,-29.10156 68.55469,-29.10156 19.72656,0 33.59375,6.44531 14.0625,6.44531 28.71093,22.26562 l 2.92969,-24.21875 z m -52.53906,86.71875 q 0,-24.41406 -14.0625,-39.64843 -14.0625,-15.42969 -36.32812,-15.42969 -24.21875,0 -40.625,18.94531 -16.40625,19.14063 -16.40625,46.875 0,24.21875 13.67187,39.64844 13.67188,15.42969 35.15625,15.42969 23.82813,0 41.21094,-19.33594 17.38281,-19.72656 17.38281,-46.48438 z\" />\n </svg>\n );\n}\n","import * as React from 'react';\n\n/**\n * <LogoMonogramBg /> — the dataimago 'a + i' monogram with a rounded accent\n * backplate. White letters on the brand-accent role\n * (var(--color-interactive-accent), iteration-2 sky-blue).\n *\n * Use this variant on neutral surfaces where the no-background monogram\n * would lack presence — favicons, app icons, social-media avatars, or any\n * branded chrome where the mark should read as a self-contained badge.\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * ai_monogram_supreme_bg_1_PATH.svg\n *\n * Theming: the background uses var(--color-interactive-accent), which\n * resolves via theme-colors.json's mode-aware role mapping — accent-500\n * (#1f618d) on light surfaces, accent-300 (#3498DB) on dark. The letter\n * fill is hard-set to white; both accent stops have AA-or-better contrast\n * with white letters (light-mode 6.66:1 AA, dark-mode 3.15:1 AA-large for\n * the dot-and-stem heading-size letters used in the monogram). The\n * literal-fallback in this component is light-mode (#1f618d, sky-blue\n * iteration-2) for rare non-CSS-aware contexts. Iteration-1's copper-red\n * fallback (#b54a27) was retired in iteration-2-B.2; see\n * wiki/decisions/iteration-2-blue-accent.md for the rationale.\n *\n * Usage:\n * <LogoMonogramBg size={64} aria-label=\"dataimago\" />\n */\n\nexport interface LogoMonogramBgProps extends React.SVGAttributes<SVGSVGElement> {\n /** Render size in px. Default: 32 (favicon-sized). Width and height match. */\n size?: number | string;\n /** Accessible name. Set undefined or pass aria-hidden for decorative use. */\n title?: string;\n}\n\nexport function LogoMonogramBg({\n size = 32,\n title = 'dataimago',\n ...rest\n}: LogoMonogramBgProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 480\"\n width={size}\n height={size}\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* Rounded accent backplate — themed via --color-interactive-accent\n (mode-aware: accent-500 #1f618d on light, accent-300 #3498DB on dark).\n Literal fallback is the light-mode canonical for non-CSS consumers. */}\n <rect\n width=\"100%\"\n height=\"100%\"\n rx=\"40\"\n ry=\"40\"\n fill=\"var(--color-interactive-accent, #1f618d)\"\n />\n {/* 'a' (data) glyph in white */}\n <path\n fill=\"#ffffff\"\n d=\"M 325.70312,176.01562 303.04687,360 h -43.16406 l 2.53906,-19.92188 Q 234.6875,364.29687 205,364.29687 q -36.52344,0 -59.57031,-24.80468 -23.04688,-24.80469 -23.04688,-64.25782 0,-45.50781 26.36719,-74.60937 26.75781,-29.10156 68.55469,-29.10156 19.72656,0 33.59375,6.44531 14.0625,6.44531 28.71093,22.26562 l 2.92969,-24.21875 z m -52.53906,86.71875 q 0,-24.41406 -14.0625,-39.64843 -14.0625,-15.42969 -36.32812,-15.42969 -24.21875,0 -40.625,18.94531 -16.40625,19.14063 -16.40625,46.875 0,24.21875 13.67187,39.64844 13.67188,15.42969 35.15625,15.42969 23.82813,0 41.21094,-19.33594 17.38281,-19.72656 17.38281,-46.48438 z\"\n />\n {/* 'i' (imago) glyph in white */}\n <path\n fill=\"#ffffff\"\n d=\"m 337.3457,97.089844 q 0,8.906246 -6.49414,15.400386 -6.49414,6.49415 -15.58594,6.49415 -9.09179,0 -15.77148,-6.67969 Q 293,105.25391 293,96.162109 q 0,-9.091797 6.30859,-15.40039 6.49414,-6.494141 15.40039,-6.494141 9.0918,0 15.77149,6.865234 6.86523,6.679688 6.86523,15.957032 z M 324.54297,180.21484 303.01953,355 h -41.00586 l 21.52344,-174.78516 z\"\n />\n </svg>\n );\n}\n","import * as React from 'react';\n\n/**\n * <LogoWordmark /> — the full \"dataimago\" wordmark with the 'ai' overlap.\n * White letters on a rounded accent backplate, themed via\n * var(--color-interactive-accent).\n *\n * Use this variant for primary brand surfaces: site headers, document\n * letterheads, large-format presentations, business materials. For compact\n * contexts (favicons, social avatars) use <LogoMonogramBg /> instead; for\n * inline use within prose use <LogoMonogram /> with currentColor.\n *\n * The wordmark preserves the philosophical idea of the brand: the visible\n * superposition of 'a' (data, human agency) and 'i' (imago, AI) creates a new\n * unified glyph that is neither pure human nor pure artificial — genuine\n * collaboration. See wiki/principles/visual-identity.md (Stage L follow-up).\n *\n * Source artwork: outlined-path SVG from\n * FOUNDATIONS/background/04_dataimago_Content/Design_Assets/logos/\n * dataimago_supreme_1_PATH.svg\n *\n * Theming: the backplate uses var(--color-interactive-accent), which resolves\n * via theme-colors.json's mode-aware role mapping — accent-500 (#1f618d) on\n * light surfaces, accent-300 (#3498DB) on dark. The literal-fallback in this\n * component is light-mode (#1f618d, sky-blue iteration-2) for the rare cases\n * where the SVG is consumed without CSS-variable resolution (printed PDFs from\n * raw SVG, third-party platforms that strip CSS). Iteration-1's copper-red\n * fallback (#b54a27) was retired in iteration-2-B.2; see\n * wiki/decisions/iteration-2-blue-accent.md for the rationale.\n *\n * Usage:\n * <LogoWordmark width={240} aria-label=\"dataimago\" />\n *\n * The wordmark's natural aspect ratio is 480x150 (~3.2:1). Pass `width` and\n * height auto-scales, or pass both explicitly.\n */\n\nexport interface LogoWordmarkProps extends React.SVGAttributes<SVGSVGElement> {\n /** Width in px. Default: 240. Height auto-scales to the 480:150 ratio. */\n width?: number | string;\n /** Accessible name. Set undefined or pass aria-hidden for decorative use. */\n title?: string;\n}\n\nexport function LogoWordmark({\n width = 240,\n title = 'dataimago',\n ...rest\n}: LogoWordmarkProps) {\n const a11yProps = title ? { role: 'img' as const } : { 'aria-hidden': true as const };\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 480 150\"\n width={width}\n aria-label={title}\n {...a11yProps}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n {/* Rounded accent backplate — themed via --color-interactive-accent\n (mode-aware: accent-500 #1f618d on light, accent-300 #3498DB on dark).\n Literal fallback is the light-mode canonical for non-CSS consumers. */}\n <rect\n width=\"100%\"\n height=\"100%\"\n rx=\"5\"\n ry=\"5\"\n fill=\"var(--color-interactive-accent, #1f618d)\"\n />\n {/* \"data\" — outlined glyphs, white fill */}\n <path\n fill=\"#ffffff\"\n d=\"M 104.80469,35.621094 97.492187,95 h -7.734375 l 0.421875,-3.550781 q -2.882812,2.566406 -4.992187,3.480468 -2.074219,0.84375 -5.132813,0.84375 -6.960937,0 -11.390625,-4.605468 -4.429687,-4.640625 -4.429687,-11.953125 0,-7.804688 4.851562,-12.972657 4.851563,-5.167968 12.199219,-5.167968 3.761719,0 6.328125,1.300781 2.671875,1.335937 5.589844,4.535156 L 97.035156,35.621094 Z M 92.007812,77 q 0,-4.21875 -2.601562,-6.820313 -2.601563,-2.601562 -6.820313,-2.601562 -4.570312,0 -7.558593,3.410156 -2.988282,3.410156 -2.988282,8.613281 0,4.394532 2.566407,7.066407 2.566406,2.636718 6.820312,2.636718 4.429688,0 7.488281,-3.585937 Q 92.007812,82.203125 92.007812,77 Z M 145.51562,61.882812 141.4375,95 h -7.76953 l 0.45703,-3.585938 q -4.99219,4.359375 -10.33594,4.359375 -6.57422,0 -10.72265,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.74609,-13.429687 4.81641,-5.238281 12.33985,-5.238281 3.55078,0 6.04687,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52734,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53906,-2.777344 -4.35937,0 -7.3125,3.410156 -2.95312,3.445313 -2.95312,8.4375 0,4.359375 2.46093,7.136719 2.46094,2.777344 6.32813,2.777344 4.28906,0 7.41797,-3.480469 3.1289,-3.550781 3.1289,-8.367188 z M 163.79687,69.089844 160.59766,95 h -7.76954 l 3.19922,-25.910156 h -4.39453 l 0.87891,-7.207032 h 4.39453 l 1.89844,-15.363281 h 7.76953 l -1.89844,15.363281 h 7.17188 l -0.87891,7.207032 z M 211.29297,61.882812 207.21484,95 h -7.76953 l 0.45703,-3.585938 q -4.99218,4.359375 -10.33593,4.359375 -6.57422,0 -10.72266,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.7461,-13.429687 4.8164,-5.238281 12.33984,-5.238281 3.55078,0 6.04687,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52735,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53907,-2.777344 -4.35937,0 -7.3125,3.410156 -2.95312,3.445313 -2.95312,8.4375 0,4.359375 2.46094,7.136719 2.46093,2.777344 6.32812,2.777344 4.28906,0 7.41797,-3.480469 3.12891,-3.550781 3.12891,-8.367188 z\"\n />\n {/* \"imago\" — outlined glyphs, white fill, superimposed at the 'a/i' overlap */}\n <path\n fill=\"#ffffff\"\n d=\"m 213.40234,46.132812 q 0,1.6875 -1.23047,2.917969 -1.23046,1.230469 -2.95312,1.230469 -1.72266,0 -2.98828,-1.265625 Q 205,47.679687 205,45.957031 q 0,-1.722656 1.19531,-2.917969 1.23047,-1.230468 2.91797,-1.230468 1.72266,0 2.98828,1.300781 1.30078,1.265625 1.30078,3.023437 z m -2.42578,15.75 L 206.89844,95 h -7.76953 l 4.07812,-33.117188 z m 18.87891,0 -0.35156,3.09375 q 3.9375,-3.9375 9.10546,-3.9375 3.12891,0 4.81641,1.160157 1.6875,1.089843 3.02344,4.113281 4.92187,-5.238281 10.61719,-5.238281 9.84375,0 9.84375,9.035156 0,0.914062 -0.17579,2.566406 -0.14062,1.652344 -0.42187,4.078125 L 264.0625,95 h -7.76953 l 2.39062,-19.160156 Q 259,73.167969 259,71.902344 q 0,-4.324219 -4.35938,-4.324219 -1.72265,0 -2.98828,0.527344 -1.26562,0.492187 -2.17968,1.582031 -0.87891,1.054687 -1.47657,2.777344 -0.5625,1.6875 -0.8789,4.042968 L 244.86719,95 h -7.76953 l 2.42578,-19.652344 q 0.3164,-2.671875 0.3164,-3.65625 0,-4.113281 -4.46484,-4.113281 -6.46875,0 -7.55859,8.964844 L 225.53125,95 h -7.76953 l 4.07812,-33.117188 z m 81.63281,0 L 307.41016,95 h -7.76954 l 0.45704,-3.585938 q -4.99219,4.359375 -10.33594,4.359375 -6.57422,0 -10.72266,-4.464843 -4.14844,-4.464844 -4.14844,-11.566407 0,-8.191406 4.7461,-13.429687 4.8164,-5.238281 12.33984,-5.238281 3.55078,0 6.04688,1.160156 2.53125,1.160156 5.16797,4.007812 l 0.52734,-4.359375 z m -9.45703,15.609375 q 0,-4.394531 -2.53125,-7.136718 -2.53125,-2.777344 -6.53906,-2.777344 -4.35938,0 -7.3125,3.410156 -2.95313,3.445313 -2.95313,8.4375 0,4.359375 2.46094,7.136719 2.46094,2.777344 6.32812,2.777344 4.28907,0 7.41797,-3.480469 3.12891,-3.550781 3.12891,-8.367188 z m 53.57812,-15.609375 -4.07812,33.363282 q -0.38672,2.988281 -0.87891,5.203126 -0.45703,2.21484 -1.05468,3.72656 -1.23047,3.09375 -3.65625,5.3086 -4.71094,4.21875 -12.23438,4.21875 -7.76953,0 -12.58594,-4.07813 -2.46093,-2.03906 -3.44531,-4.64062 -0.98437,-2.39063 -0.98437,-6.714849 h 7.52343 q 0.28125,4.675779 2.39063,6.785159 2.10937,2.14453 6.53906,2.14453 4.78125,0 7.27734,-2.56641 2.4961,-2.53125 3.16407,-8.085935 l 0.5625,-4.535156 q -4.92188,3.761718 -10.33594,3.761718 -6.75,0 -11.14453,-4.605468 -4.39453,-4.710938 -4.39453,-11.742188 0,-7.910156 5.09765,-13.113281 5.13282,-5.238281 12.83203,-5.238281 3.69141,0 5.97657,1.089843 2.49609,1.054688 5.16797,3.832032 l 0.49218,-4.113282 z m -8.92968,15.714844 q 0,-4.5 -2.74219,-7.242187 -2.74219,-2.777344 -7.17188,-2.777344 -4.60546,0 -7.66406,3.375 -3.02344,3.339844 -3.02344,8.402344 0,4.429687 2.60157,7.242187 2.63672,2.707031 6.96093,2.707031 4.81641,0 7.91016,-3.304687 3.12891,-3.339844 3.12891,-8.402344 z m 52.3125,0.527344 q 0,7.59375 -5.27344,12.761719 -5.23828,5.167968 -12.9375,5.167968 -8.05078,0 -13.14844,-4.886718 Q 362.5,86.246094 362.5,78.722656 q 0,-7.699219 5.30859,-12.796875 5.34375,-5.132812 13.35938,-5.132812 7.875,0 12.83203,4.851562 4.99219,4.78125 4.99219,12.480469 z m -7.83985,-0.386719 q 0,-4.78125 -2.77734,-7.558594 -2.77734,-2.882812 -7.27734,-2.882812 -4.67579,0 -7.73438,3.199219 -3.05859,3.234375 -3.05859,8.121093 0,4.921875 2.88281,7.910157 2.88281,3.058593 7.45312,3.058593 4.57032,0 7.52344,-3.339843 2.98828,-3.304688 2.98828,-8.507813 z\"\n />\n </svg>\n );\n}\n","import * as React from 'react';\nimport { svgs, type LogoId, type LogoVariant, type LogoKey } from '@dataimago/brand';\n\nexport interface LogoProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Mark id, e.g. `dissertation_ai_OVERLAP`. */\n name: LogoId;\n /** PATH (default), CUTOUT, BG, or LETTERS. See @dataimago/brand. */\n variant?: LogoVariant;\n /**\n * Render width (px number or any CSS length). Height scales proportionally\n * via the SVG viewBox. Omit to use the mark's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * Manifest-driven brand mark from `@dataimago/brand`. Inlines the SVG string so\n * `currentColor` (PATH/LETTERS), the `--color-interactive-accent` badge fill\n * (CUTOUT/BG), and the per-letter interaction classes (`.glyph`, `.logo-*`) all\n * work — an `<img src>` can't receive those interior styles.\n *\n * Color flows from CSS: set `color` (PATH/LETTERS) on this element or an\n * ancestor. Size via `width` (or CSS on the wrapper).\n */\nexport function Logo({ name, variant = 'PATH', width, style, ...rest }: LogoProps) {\n const key = `${name}_${variant}` as LogoKey;\n let markup = svgs[key];\n\n if (!markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <Logo>: unknown mark \"${key}\".`);\n return null;\n }\n\n if (width != null) {\n // Set width + drop height so the viewBox preserves the aspect ratio.\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n // The SVG is generated, font-free, and carries its own <title>/aria-label.\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport {\n animations,\n animationManifest,\n type AnimationId,\n type LogoId,\n type LogoVariant,\n} from '@dataimago/brand';\nimport { Logo } from './Logo';\n\nexport interface BrandAnimationProps\n extends React.HTMLAttributes<HTMLSpanElement> {\n /** Animation id, e.g. `dissertation-ai`. See @dataimago/brand animations.json. */\n name: AnimationId;\n /**\n * Render width (px number or any CSS length). Height scales via the SVG\n * viewBox. Omit to use the animation's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * SSR-safe `prefers-reduced-motion` detection. Starts `true` (reduced) so the\n * server and the first client render agree on the static poster — no hydration\n * mismatch, and no motion flashes before JS decides. After mount it reflects the\n * real media query and updates live if the user toggles the OS setting.\n */\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(true);\n React.useEffect(() => {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReduced(mq.matches);\n const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/**\n * Decorative brand animation from `@dataimago/brand` (the animation-foundry).\n * Inlines the pure-SMIL SVG string so its interior `<style>` works — an\n * `<img src>` can't receive those styles (same rationale as `<Logo>`).\n *\n * Motion-dignity: decorative brand animation is a distinct category from the\n * 300ms UI-transition ceiling, but it is contractually gated on\n * `prefers-reduced-motion`. When the user prefers reduced motion this renders\n * the static brand mark the animation settles to (its `poster`) via `<Logo>`,\n * so no consumer can ship the motion without the fallback.\n */\nexport function BrandAnimation({\n name,\n width,\n style,\n ...rest\n}: BrandAnimationProps) {\n const reduced = usePrefersReducedMotion();\n const entry = animationManifest.find((a) => a.id === name);\n let markup = animations[name];\n\n if (!entry || !markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <BrandAnimation>: unknown animation \"${name}\".`);\n return null;\n }\n\n if (reduced) {\n // Static settle-frame: the brand mark the animation resolves to.\n return (\n <Logo\n name={entry.poster.id as LogoId}\n variant={entry.poster.variant as LogoVariant}\n width={width}\n style={style}\n {...rest}\n />\n );\n }\n\n if (width != null) {\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport {\n navbarLogos,\n navbarManifest,\n type NavbarLogoId,\n type LogoId,\n type LogoVariant,\n} from '@dataimago/brand';\nimport { Logo } from './Logo';\n\nexport interface NavbarLogoProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Navbar mark id, e.g. `dissertation-ai`. See @dataimago/brand navbar.json. */\n name: NavbarLogoId;\n /**\n * Render width (px number or any CSS length). Height scales via the SVG\n * viewBox. Omit to use the mark's intrinsic size.\n */\n width?: number | string;\n}\n\n/**\n * SSR-safe `prefers-reduced-motion`. Starts `true` (reduced) so server and first\n * client render agree on the static poster — no hydration mismatch, no motion\n * flash. After mount it reflects the real query and updates live. (Mirrors\n * BrandAnimation; kept local to avoid a shared-hook refactor.)\n */\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(true);\n React.useEffect(() => {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReduced(mq.matches);\n const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/**\n * Animated brand mark for an app navbar, from `@dataimago/brand` (the\n * animation-foundry's navbar set). Inlines the self-contained CSS-animated SVG\n * so its interior `<style>` works — an `<img src>` can't receive those styles.\n *\n * Behaviour by mark kind (from the navbar manifest):\n * - `assemble` (L2: dissertation-ai, rpkg-ai, SGPc-ai): the trailing `a`/`i`\n * start apart and the `i` slides onto the `a`. Plays once on mount; replays\n * on pointer-enter via a remount (`replayKey`).\n * - `shimmer` (L1: dataimago-ai): the monogram is always superimposed; an\n * accent shimmer sweeps on `:hover` from the SVG's own CSS (no JS trigger).\n * - `dream` (promoted dream marks, `tools/dream-foundry/drop.mjs promote`):\n * a self-contained CSS hover gesture like liftwash; the poster is the mark's\n * own static form (`dream_<slug>` / `DREAM`). Ink is `currentColor`.\n *\n * Motion-dignity: the embedded CSS animation is <= 300ms and gated on\n * `@media (prefers-reduced-motion: no-preference)`. When the user prefers\n * reduced motion this renders the static settled mark (the manifest `poster`,\n * the superimposed monogram) via `<Logo>` — so no consumer can ship the motion\n * without the fallback.\n */\nexport function NavbarLogo({ name, width, style, ...rest }: NavbarLogoProps) {\n const reduced = usePrefersReducedMotion();\n\n const entry = navbarManifest.find((n) => n.id === name);\n let markup = navbarLogos[name];\n\n if (!entry || !markup) {\n // eslint-disable-next-line no-console\n console.warn(`[@dataimago/ui] <NavbarLogo>: unknown navbar mark \"${name}\".`);\n return null;\n }\n\n if (reduced) {\n // Static settle-frame: the superimposed mark the animation resolves to.\n return (\n <Logo\n name={entry.poster.id as LogoId}\n variant={entry.poster.variant as LogoVariant}\n width={width}\n style={style}\n {...rest}\n />\n );\n }\n\n if (width != null) {\n markup = markup\n .replace(/\\swidth=\"[^\"]*\"/, ` width=\"${width}\"`)\n .replace(/\\sheight=\"[^\"]*\"/, '');\n }\n\n // The SVG is fully self-driving: it plays the assemble once on mount (its\n // `.di-play` class) and runs the smooth hover round-trip from its own `:hover`\n // CSS — no remount/replay logic here (remounting is what caused the teleport).\n return (\n <span\n style={{ display: 'inline-flex', ...style }}\n // Self-contained, font-free SVG; carries its own <title>/aria-label.\n dangerouslySetInnerHTML={{ __html: markup }}\n {...rest}\n />\n );\n}\n","import * as React from 'react';\nimport {\n resolveTheme,\n useResolvedTheme,\n useThemePreference,\n type ResolvedTheme,\n type ThemePreference,\n} from './theme.js';\n\nexport interface ThemeToggleLabels {\n /** What the control sets, read before the theme in effect (\"Theme: Light\"). */\n name: string;\n /** Added when the theme follows the operating system (\"Theme: Light (System)\"). */\n system: string;\n light: string;\n dark: string;\n}\n\nexport interface ThemeToggleProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {\n /** Accessible and tooltip text; pass the property's own wording. */\n labels?: Partial<ThemeToggleLabels>;\n}\n\nconst DEFAULT_LABELS: ThemeToggleLabels = {\n name: 'Theme',\n system: 'System',\n light: 'Light',\n dark: 'Dark',\n};\n\n/** The operating system's theme. */\nfunction systemTheme(): ResolvedTheme {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\n/**\n * The choice one click makes: the other theme, stored as \"system\" when that is\n * the operating system's own, so the page goes back to following it.\n */\nexport function nextThemePreference(shown: ResolvedTheme, system: ResolvedTheme): ThemePreference {\n const next: ResolvedTheme = shown === 'dark' ? 'light' : 'dark';\n return next === system ? 'system' : next;\n}\n\nconst noSubscribe = () => () => {};\n\n/**\n * False in the server markup and during hydration, true after. The theme in\n * effect is only known on the client (the server snapshot is always light), so\n * until then the button claims no theme: its name is the bare `name` and its\n * icon is hidden, keeping the 48px box.\n */\nfunction useHydrated(): boolean {\n return React.useSyncExternalStore(noSubscribe, () => true, () => false);\n}\n\nfunction Icon({ theme }: { theme: ResolvedTheme }) {\n const common = {\n width: 20,\n height: 20,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.75,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n 'aria-hidden': true,\n focusable: false,\n };\n if (theme === 'dark') {\n return (\n <svg {...common}>\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n );\n }\n return (\n <svg {...common}>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41\" />\n </svg>\n );\n}\n\n/**\n * Light/dark toggle for an app navbar: one button, and one click always flips\n * the theme on screen. The page follows the operating system until the first\n * click; a click that lands on the system's own theme goes back to following\n * it (no stored choice), and the other theme is stored and set as\n * `data-bs-theme` on the root, which the house tokens obey. The icon shows the\n * theme in effect: a sun in light, a moon in dark.\n *\n * A 48px target (the dataimago touch floor). The icon is `currentColor`.\n * Inline styles would beat a consumer's `:hover` rule, so the two colours a\n * navbar restyles read custom properties instead: set `--di-theme-toggle-fg`\n * and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).\n * Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before\n * first paint.\n */\nexport function ThemeToggle({ labels, className = '', style, ...rest }: ThemeToggleProps) {\n const [preference, setPreference] = useThemePreference();\n const resolved = useResolvedTheme();\n const hydrated = useHydrated();\n const shown = hydrated ? resolved : undefined;\n const text = { ...DEFAULT_LABELS, ...labels };\n const label = shown\n ? `${text.name}: ${text[shown]}${preference === 'system' ? ` (${text.system})` : ''}`\n : text.name;\n return (\n <button\n {...rest}\n className={['di-ui-theme-toggle', className].filter(Boolean).join(' ')}\n aria-label={label}\n title={label}\n data-theme-preference={preference}\n data-theme={shown}\n style={{\n alignItems: 'center',\n background: 'var(--di-theme-toggle-bg, transparent)',\n border: 0,\n borderRadius: 'var(--radius-md, 0.5rem)',\n color: 'var(--di-theme-toggle-fg, inherit)',\n cursor: 'pointer',\n display: 'inline-flex',\n height: '48px',\n justifyContent: 'center',\n padding: 0,\n transition: 'background-color 150ms ease',\n width: '48px',\n ...style,\n }}\n // After the spread, so the control never submits a form or loses its flip.\n type=\"button\"\n // Live reads, not the rendered snapshot: a click always flips what is on screen.\n onClick={() => setPreference(nextThemePreference(resolveTheme(), systemTheme()))}\n >\n <span style={{ display: 'inline-flex', visibility: shown ? 'visible' : 'hidden' }}>\n <Icon theme={shown ?? 'light'} />\n </span>\n </button>\n );\n}\n","import * as React from 'react';\n\n/**\n * Light/dark theme preference for dataimago apps.\n *\n * The house tokens (`@dataimago/tokens`) follow the operating system through\n * `prefers-color-scheme` and yield to an explicit `data-bs-theme` on the root:\n * `light` or `dark` forces that theme, no attribute means \"follow the system\".\n * This module owns that attribute: it reads and stores the reader's choice,\n * applies it, and reports the theme actually in effect (for canvas and WebGL\n * surfaces that cannot read CSS variables).\n *\n * The choice is a per-browser convenience in localStorage. Every read and write\n * is guarded: in a private window or with storage blocked the page simply\n * follows the system.\n */\n\nexport type ThemePreference = 'system' | 'light' | 'dark';\nexport type ResolvedTheme = 'light' | 'dark';\n\nexport const THEME_STORAGE_KEY = 'dataimago-theme';\nexport const THEME_ATTRIBUTE = 'data-bs-theme';\n\n/** Fired on `window` whenever `setThemePreference` changes the choice. */\nconst CHANGE_EVENT = 'dataimago:themechange';\n\n/**\n * Inline this in `<head>` before any stylesheet paints, so a stored choice is\n * on the root before first paint (no flash of the other theme). The server\n * markup cannot know the choice, so the consumer also puts\n * `suppressHydrationWarning` on `<html>`. From a React server layout, render\n * `<ThemeScript />` instead: this package is a client bundle, so a server\n * component cannot import the string itself.\n *\n * ```tsx\n * <html lang=\"en\" suppressHydrationWarning>\n * <head><ThemeScript /></head>\n * ```\n */\nexport const themeInitScript =\n `(function(){try{var p=localStorage.getItem(${JSON.stringify(THEME_STORAGE_KEY)});` +\n `if(p==='light'||p==='dark'){document.documentElement.setAttribute(${JSON.stringify(THEME_ATTRIBUTE)},p);}}` +\n `catch(e){}})();`;\n\nfunction isPreference(value: unknown): value is ThemePreference {\n return value === 'system' || value === 'light' || value === 'dark';\n}\n\n/** The stored choice, or `'system'` when there is none (or storage is unavailable). */\nexport function readThemePreference(): ThemePreference {\n if (typeof window === 'undefined') return 'system';\n try {\n const stored = window.localStorage.getItem(THEME_STORAGE_KEY);\n return isPreference(stored) ? stored : 'system';\n } catch {\n return 'system';\n }\n}\n\nfunction applyAttribute(preference: ThemePreference): void {\n const root = document.documentElement;\n if (preference === 'system') root.removeAttribute(THEME_ATTRIBUTE);\n else root.setAttribute(THEME_ATTRIBUTE, preference);\n}\n\n/** Store, apply and announce a choice. `'system'` clears the stored choice. */\nexport function setThemePreference(preference: ThemePreference): void {\n if (typeof window === 'undefined') return;\n try {\n if (preference === 'system') window.localStorage.removeItem(THEME_STORAGE_KEY);\n else window.localStorage.setItem(THEME_STORAGE_KEY, preference);\n } catch {\n // Storage blocked: the choice still applies to this page view.\n }\n applyAttribute(preference);\n window.dispatchEvent(new Event(CHANGE_EVENT));\n}\n\n/** The theme in effect: the root attribute when set, otherwise the system's. */\nexport function resolveTheme(): ResolvedTheme {\n if (typeof document === 'undefined') return 'light';\n const forced = document.documentElement.getAttribute(THEME_ATTRIBUTE);\n if (forced === 'light' || forced === 'dark') return forced;\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\n/**\n * Put the stored choice on the root. `ThemeScript` does this before first\n * paint when it is server-rendered; this covers an app without it (or one that\n * inserts it on the client, where an inline script never runs). Idempotent.\n */\nfunction syncAttribute(): void {\n applyAttribute(readThemePreference());\n}\n\nfunction subscribePreference(onChange: () => void): () => void {\n syncAttribute();\n // Another tab changed the choice: follow it here too.\n const onStorage = (e: StorageEvent) => {\n if (e.key !== null && e.key !== THEME_STORAGE_KEY) return;\n applyAttribute(readThemePreference());\n onChange();\n };\n window.addEventListener(CHANGE_EVENT, onChange);\n window.addEventListener('storage', onStorage);\n return () => {\n window.removeEventListener(CHANGE_EVENT, onChange);\n window.removeEventListener('storage', onStorage);\n };\n}\n\nfunction subscribeResolved(onChange: () => void): () => void {\n // Watch first, then restore: the observer reports the restored attribute,\n // so a canvas surface hears about a stored choice applied on mount.\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [THEME_ATTRIBUTE],\n });\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n media.addEventListener('change', onChange);\n window.addEventListener(CHANGE_EVENT, onChange);\n syncAttribute();\n return () => {\n observer.disconnect();\n media.removeEventListener('change', onChange);\n window.removeEventListener(CHANGE_EVENT, onChange);\n };\n}\n\n/**\n * The reader's choice and a setter. Server render and hydration see\n * `'system'`; the stored choice arrives right after.\n */\nexport function useThemePreference(): [ThemePreference, (p: ThemePreference) => void] {\n const preference = React.useSyncExternalStore(\n subscribePreference,\n readThemePreference,\n () => 'system' as const,\n );\n return [preference, setThemePreference];\n}\n\n/**\n * The theme in effect, for surfaces that cannot read CSS variables (canvas,\n * WebGL maps). Follows the toggle and, under \"system\", the operating system.\n * Server render and hydration see `'light'`.\n */\nexport function useResolvedTheme(): ResolvedTheme {\n return React.useSyncExternalStore(subscribeResolved, resolveTheme, () => 'light' as const);\n}\n","import * as React from 'react';\nimport { themeInitScript } from './theme.js';\n\nexport interface ThemeScriptProps {\n /** Content-Security-Policy nonce, when the app sets one. */\n nonce?: string;\n}\n\n/**\n * Renders `themeInitScript` as an inline `<script>`, so a stored choice is on\n * the root before first paint. Use this from a server layout: this package is\n * a client bundle, so a server component cannot import the plain string, but\n * it can render this component. Put it in `<head>` and add\n * `suppressHydrationWarning` to `<html>` (the script changes its attributes\n * before React hydrates).\n */\nexport function ThemeScript({ nonce }: ThemeScriptProps) {\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: themeInitScript }} />;\n}\n"],"mappings":"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","names":["Button","jsx","jsx","jsxs","jsx","jsxs","jsx","React","jsx","React","jsx","usePrefersReducedMotion","React","React","jsx","jsxs","jsx"]}
|