@keboola/design 4.0.0 → 4.1.2
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/AGENTS.md +47 -4
- package/COMPONENT-API.md +75 -80
- package/dist/editors.cjs +8 -34
- package/dist/editors.cjs.map +1 -1
- package/dist/editors.d.cts +1 -3
- package/dist/editors.d.ts +1 -3
- package/dist/editors.js +8 -34
- package/dist/editors.js.map +1 -1
- package/dist/icons.cjs.map +1 -1
- package/dist/icons.js.map +1 -1
- package/dist/index.cjs +3371 -641
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +341 -334
- package/dist/index.d.ts +341 -334
- package/dist/index.js +3190 -464
- package/dist/index.js.map +1 -1
- package/dist/markdown-editor.cjs +17 -31
- package/dist/markdown-editor.cjs.map +1 -1
- package/dist/markdown-editor.js +17 -31
- package/dist/markdown-editor.js.map +1 -1
- package/dist/mermaid.cjs +4 -18
- package/dist/mermaid.cjs.map +1 -1
- package/dist/mermaid.js +4 -18
- package/dist/mermaid.js.map +1 -1
- package/dist/styles.css +2 -2
- package/dist/testing.cjs +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +37 -33
- package/src/styles.css +52 -14
package/dist/testing.cjs
CHANGED
|
@@ -135,7 +135,7 @@ var ThemeProvider = ({
|
|
|
135
135
|
};
|
|
136
136
|
}, [brand, resolvedMode]);
|
|
137
137
|
return /* @__PURE__ */ jsxRuntime.jsxs(ThemeContext, { value: { brand, setBrand, colorScheme, resolvedMode }, children: [
|
|
138
|
-
/* @__PURE__ */ jsxRuntime.jsx(AIGradientsSvg, {}),
|
|
138
|
+
isRoot && /* @__PURE__ */ jsxRuntime.jsx(AIGradientsSvg, {}),
|
|
139
139
|
children
|
|
140
140
|
] });
|
|
141
141
|
};
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ThemeProvider/AIGradientSvg.tsx","../src/components/ThemeProvider/brandToCssVars.ts","../src/components/ThemeProvider/themeContext.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/testing/renderWithTheme.tsx"],"names":["jsx","jsxs","createContext","keboola","use","useState","useEffect","render"],"mappings":"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","file":"testing.cjs","sourcesContent":["export const AIGradientsSvg = () => (\n <svg width=\"0\" height=\"0\" style={{ position: 'absolute', visibility: 'hidden' }}>\n <defs>\n {/*aigradient-600*/}\n <linearGradient id=\"aigradient-600\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#61E4FF\" />\n <stop offset=\"50%\" stopColor=\"#1F8FFF\" />\n <stop offset=\"100%\" stopColor=\"#7C5CFF\" />\n </linearGradient>\n\n {/*aigradient-700*/}\n <linearGradient id=\"aigradient-700\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#075fb8\" />\n <stop offset=\"100%\" stopColor=\"#5b2cb8\" />\n </linearGradient>\n </defs>\n </svg>\n);\n","import type { Brand } from '@keboola/brand-registry';\n\nimport type { ResolvedMode } from './themeContext';\n\n/**\n * Map every brand-overridable slot to the matching CSS variable on `:root`.\n *\n * Brand colours are already stored as `R G B` channel strings (validated by\n * the brand schema), matching the shape Tailwind's\n * `rgb(var(--color-*) / <alpha-value>)` template consumes — they're written\n * straight into the CSS variable without conversion.\n *\n * Mode-aware: when `resolvedMode === 'dark'` and the brand ships a\n * `colorsDark` palette, that palette wins; otherwise `colors` (light) is\n * used. The crucial exception — if `resolvedMode === 'dark'` and the brand\n * has NO `colorsDark`, the 9 colour vars are omitted entirely so the\n * stylesheet's `[data-theme='dark']` block supplies the dark palette.\n * Emitting the light values here would clobber that with no `!important`\n * recourse. Non-colour vars (radii) are emitted regardless of mode.\n *\n * Slots the brand doesn't define are omitted — `ThemeProvider` only\n * captures/restores the keys returned here, so unset slots inherit whatever\n * is already live on `<html>` (an outer provider's value, or the `:root` /\n * `[data-theme='dark']` defaults in `styles.css` if no outer provider has\n * touched them). The accent colours (in `colors` / `colorsDark`), the sidebar\n * colour family (`brand.sidebarColors` / `brand.sidebarColorsDark`), and the\n * brand font (`typography.fontFamily`, emitted as `--font-sans`) follow the\n * same opt-in rule.\n */\nexport const brandToCssVars = (\n brand: Brand,\n resolvedMode: ResolvedMode,\n): Record<string, string> => {\n const vars: Record<string, string> = {};\n\n // In dark mode with no brand dark palette, leave the colour vars to the\n // stylesheet's `[data-theme='dark']` block (do not emit light values).\n const colors = resolvedMode === 'dark' ? brand.colorsDark : brand.colors;\n if (colors) {\n vars['--color-background'] = colors.background;\n vars['--color-foreground'] = colors.foreground;\n vars['--color-muted'] = colors.muted;\n vars['--color-muted-foreground'] = colors.mutedForeground;\n vars['--color-border'] = colors.border;\n vars['--color-primary'] = colors.primary;\n vars['--color-primary-foreground'] = colors.primaryForeground;\n vars['--color-destructive'] = colors.destructive;\n vars['--color-destructive-foreground'] = colors.destructiveForeground;\n // accent is a general semantic colour (content + sidebar); optional.\n if (colors.accent !== undefined) vars['--color-accent'] = colors.accent;\n if (colors.accentForeground !== undefined)\n vars['--color-accent-foreground'] = colors.accentForeground;\n }\n\n // Sidebar colour tokens — same dark-mode rule as the core palette: with no\n // `sidebarColorsDark`, leave the stylesheet `[data-theme='dark']` sidebar intact.\n const sidebar = resolvedMode === 'dark' ? brand.sidebarColorsDark : brand.sidebarColors;\n if (sidebar) {\n const sidebarVars: Record<string, string | undefined> = {\n '--color-sidebar': sidebar.sidebar,\n '--color-sidebar-foreground': sidebar.sidebarForeground,\n '--color-sidebar-primary': sidebar.sidebarPrimary,\n '--color-sidebar-primary-foreground': sidebar.sidebarPrimaryForeground,\n '--color-sidebar-accent': sidebar.sidebarAccent,\n '--color-sidebar-accent-foreground': sidebar.sidebarAccentForeground,\n '--color-sidebar-border': sidebar.sidebarBorder,\n '--color-sidebar-ring': sidebar.sidebarRing,\n };\n for (const [name, value] of Object.entries(sidebarVars)) {\n if (value !== undefined) vars[name] = value;\n }\n }\n\n // Trim at the runtime boundary too: ThemeProvider applies this output without\n // re-validating the schema, so a whitespace-only value must not emit\n // `--font-sans` (it would defeat the `var(--font-sans, …)` fallback).\n const fontFamily = brand.typography?.fontFamily?.trim();\n if (fontFamily) {\n vars['--font-sans'] = fontFamily;\n }\n\n if (brand.radii) {\n vars['--radius-sm'] = brand.radii.sm;\n vars['--radius-md'] = brand.radii.md;\n vars['--radius-lg'] = brand.radii.lg;\n }\n\n return vars;\n};\n","import { createContext } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nexport type ColorScheme = 'light' | 'dark' | 'system';\nexport type ResolvedMode = 'light' | 'dark';\n\nexport type ThemeContextValue = {\n brand: Brand;\n setBrand?: (brand: Brand) => void;\n /** The configured color scheme (may be `'system'`). */\n colorScheme: ColorScheme;\n /** The effective light/dark mode after resolving `'system'`. */\n resolvedMode: ResolvedMode;\n};\n\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n","import { type ReactNode, use, useEffect, useState } from 'react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { AIGradientsSvg } from './AIGradientSvg';\nimport { brandToCssVars } from './brandToCssVars';\nimport { type ColorScheme, type ResolvedMode, ThemeContext } from './themeContext';\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\nconst systemMode = (): ResolvedMode => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return 'light';\n return window.matchMedia(DARK_QUERY).matches ? 'dark' : 'light';\n};\n\ntype Props = {\n /**\n * Brand whose tokens drive the chrome (`@keboola/design`'s semantic CSS\n * variables) inside this provider's subtree. Defaults to the canonical\n * `keboola` brand so existing apps that mount `<ThemeProvider>` without a\n * `brand` prop continue to render as today.\n */\n brand?: Brand;\n /**\n * Optional setter exposed through `useTheme()` so consumers can flip\n * brands at runtime (Storybook toolbar, a customer-impersonation switch).\n */\n setBrand?: (brand: Brand) => void;\n /**\n * Light/dark mode. `'system'` follows `prefers-color-scheme` and updates\n * reactively. Defaults to `'light'` so existing mounts don't drift. Only\n * the ROOT provider applies this (sets `data-theme`, owns the media\n * listener); a nested provider inherits the root's resolved mode.\n */\n colorScheme?: ColorScheme;\n children: ReactNode;\n};\n\n/**\n * Responsibilities:\n * - Render hidden SVG gradient definitions (`aigradient-600`, `aigradient-700`).\n * - Set `data-brand=\"<brand.id>\"` on `<html>` and inject the matching `--color-*` /\n * `--radius-*` CSS variables (for the resolved light/dark mode) so chrome\n * re-skins for the supplied brand.\n * - ROOT only: set `data-theme` (`light` | `dark`) on `<html>` and own the\n * `prefers-color-scheme` media listener when `colorScheme === 'system'`.\n * A nested provider inherits the resolved mode from the parent context and\n * touches neither `data-theme` nor a media listener.\n * - Expose `{ brand, setBrand?, colorScheme, resolvedMode }` via `useTheme()`.\n *\n * Brand × mode: the active mode's palette is injected inline on `<html>`. When\n * the resolved mode is `dark` and the brand has no `colorsDark`, the colour\n * vars are NOT emitted — the stylesheet `[data-theme='dark']` block supplies\n * them, so no `!important` override is ever needed.\n *\n * `data-brand` (brand id) and `data-theme` (light/dark) are separate axes so\n * they compose without clobbering each other.\n */\nexport const ThemeProvider = ({\n brand = keboola,\n setBrand,\n colorScheme = 'light',\n children,\n}: Props) => {\n const parent = use(ThemeContext);\n const isRoot = parent === null;\n\n // System mode is tracked only by the root; nested providers inherit the\n // parent's resolved mode and never subscribe to media changes.\n const [systemResolved, setSystemResolved] = useState<ResolvedMode>(systemMode);\n\n useEffect(() => {\n if (!isRoot || colorScheme !== 'system') return;\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;\n const mql = window.matchMedia(DARK_QUERY);\n const onChange = (event: MediaQueryListEvent) =>\n setSystemResolved(event.matches ? 'dark' : 'light');\n // Re-sync in case the value changed between the lazy initializer and effect.\n setSystemResolved(mql.matches ? 'dark' : 'light');\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }, [isRoot, colorScheme]);\n\n const ownMode: ResolvedMode = colorScheme === 'system' ? systemResolved : colorScheme;\n // Nested providers inherit the root's resolved mode; the root uses its own.\n const resolvedMode: ResolvedMode = isRoot ? ownMode : parent.resolvedMode;\n\n // Root owns `data-theme` on `<html>`.\n useEffect(() => {\n if (!isRoot || typeof document === 'undefined') return;\n const root = document.documentElement;\n const previous = root.getAttribute('data-theme');\n root.setAttribute('data-theme', resolvedMode);\n return () => {\n if (root.getAttribute('data-theme') !== resolvedMode) return;\n if (previous === null) {\n root.removeAttribute('data-theme');\n } else {\n root.setAttribute('data-theme', previous);\n }\n };\n }, [isRoot, resolvedMode]);\n\n // Every provider (root or nested) injects ITS brand's vars for the resolved mode.\n useEffect(() => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n\n // Capture the previous `data-brand` + per-property inline-style values\n // so cleanup can restore them. Cleanup only restores values that\n // weren't overwritten by some other provider in between — without that\n // check, an inner provider unmounting after an outer one wrote new\n // values would clobber the outer's brand back to bare `:root` (React\n // runs child effects before parent effects on mount, so the inner's\n // captured \"previous\" is stale by the time it unmounts).\n const previousBrand = root.getAttribute('data-brand');\n const vars = brandToCssVars(brand, resolvedMode);\n const previousValues: Record<string, string> = {};\n for (const name of Object.keys(vars)) {\n previousValues[name] = root.style.getPropertyValue(name);\n }\n\n root.setAttribute('data-brand', brand.id);\n for (const [name, value] of Object.entries(vars)) {\n root.style.setProperty(name, value);\n }\n\n return () => {\n for (const [name, previousValue] of Object.entries(previousValues)) {\n // Only roll back if our value is still live — if someone else\n // (an outer provider, a sibling) has since written a different\n // value, leave it alone.\n if (root.style.getPropertyValue(name) !== vars[name]) continue;\n if (previousValue === '') {\n root.style.removeProperty(name);\n } else {\n root.style.setProperty(name, previousValue);\n }\n }\n if (root.getAttribute('data-brand') === brand.id) {\n if (previousBrand === null) {\n root.removeAttribute('data-brand');\n } else {\n root.setAttribute('data-brand', previousBrand);\n }\n }\n };\n }, [brand, resolvedMode]);\n\n return (\n <ThemeContext value={{ brand, setBrand, colorScheme, resolvedMode }}>\n <AIGradientsSvg />\n {children}\n </ThemeContext>\n );\n};\n","import type { ReactElement } from 'react';\nimport { render, type RenderOptions, type RenderResult } from '@testing-library/react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { ThemeProvider } from '../components/ThemeProvider';\n\ntype Options = Omit<RenderOptions, 'wrapper'> & {\n /** Brand to drive the wrapping `<ThemeProvider>`. Defaults to `keboola`. */\n brand?: Brand;\n};\n\n/**\n * `@testing-library/react` `render` wrapped in a `<ThemeProvider brand={...}>`.\n *\n * Use in unit tests for any component that calls `useTheme()` or relies on\n * the brand-driven CSS variables. Defaults to the canonical Keboola brand so\n * callers can opt into a specific brand only when they need to assert\n * brand-dependent behaviour (e.g. chrome re-skins under `keboola-legacy`).\n */\nexport const renderWithTheme = (ui: ReactElement, options: Options = {}): RenderResult => {\n const { brand = keboola, ...rest } = options;\n return render(ui, {\n ...rest,\n wrapper: ({ children }) => <ThemeProvider brand={brand}>{children}</ThemeProvider>,\n });\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/ThemeProvider/AIGradientSvg.tsx","../src/components/ThemeProvider/brandToCssVars.ts","../src/components/ThemeProvider/themeContext.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/testing/renderWithTheme.tsx"],"names":["jsx","jsxs","createContext","keboola","use","useState","useEffect","render"],"mappings":"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","file":"testing.cjs","sourcesContent":["export const AIGradientsSvg = () => (\n <svg width=\"0\" height=\"0\" style={{ position: 'absolute', visibility: 'hidden' }}>\n <defs>\n {/*aigradient-600*/}\n <linearGradient id=\"aigradient-600\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#61E4FF\" />\n <stop offset=\"50%\" stopColor=\"#1F8FFF\" />\n <stop offset=\"100%\" stopColor=\"#7C5CFF\" />\n </linearGradient>\n\n {/*aigradient-700*/}\n <linearGradient id=\"aigradient-700\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#075fb8\" />\n <stop offset=\"100%\" stopColor=\"#5b2cb8\" />\n </linearGradient>\n </defs>\n </svg>\n);\n","import type { Brand } from '@keboola/brand-registry';\n\nimport type { ResolvedMode } from './themeContext';\n\n/**\n * Map every brand-overridable slot to the matching CSS variable on `:root`.\n *\n * Brand colours are already stored as `R G B` channel strings (validated by\n * the brand schema), matching the shape Tailwind's\n * `rgb(var(--color-*) / <alpha-value>)` template consumes — they're written\n * straight into the CSS variable without conversion.\n *\n * Mode-aware: when `resolvedMode === 'dark'` and the brand ships a\n * `colorsDark` palette, that palette wins; otherwise `colors` (light) is\n * used. The crucial exception — if `resolvedMode === 'dark'` and the brand\n * has NO `colorsDark`, the 9 colour vars are omitted entirely so the\n * stylesheet's `[data-theme='dark']` block supplies the dark palette.\n * Emitting the light values here would clobber that with no `!important`\n * recourse. Non-colour vars (radii) are emitted regardless of mode.\n *\n * Slots the brand doesn't define are omitted — `ThemeProvider` only\n * captures/restores the keys returned here, so unset slots inherit whatever\n * is already live on `<html>` (an outer provider's value, or the `:root` /\n * `[data-theme='dark']` defaults in `styles.css` if no outer provider has\n * touched them). The accent colours (in `colors` / `colorsDark`), the sidebar\n * colour family (`brand.sidebarColors` / `brand.sidebarColorsDark`), and the\n * brand font (`typography.fontFamily`, emitted as `--font-sans`) follow the\n * same opt-in rule.\n */\nexport const brandToCssVars = (\n brand: Brand,\n resolvedMode: ResolvedMode,\n): Record<string, string> => {\n const vars: Record<string, string> = {};\n\n // In dark mode with no brand dark palette, leave the colour vars to the\n // stylesheet's `[data-theme='dark']` block (do not emit light values).\n const colors = resolvedMode === 'dark' ? brand.colorsDark : brand.colors;\n if (colors) {\n vars['--color-background'] = colors.background;\n vars['--color-foreground'] = colors.foreground;\n vars['--color-muted'] = colors.muted;\n vars['--color-muted-foreground'] = colors.mutedForeground;\n vars['--color-border'] = colors.border;\n vars['--color-primary'] = colors.primary;\n vars['--color-primary-foreground'] = colors.primaryForeground;\n vars['--color-destructive'] = colors.destructive;\n vars['--color-destructive-foreground'] = colors.destructiveForeground;\n // accent is a general semantic colour (content + sidebar); optional.\n if (colors.accent !== undefined) vars['--color-accent'] = colors.accent;\n if (colors.accentForeground !== undefined)\n vars['--color-accent-foreground'] = colors.accentForeground;\n }\n\n // Sidebar colour tokens — same dark-mode rule as the core palette: with no\n // `sidebarColorsDark`, leave the stylesheet `[data-theme='dark']` sidebar intact.\n const sidebar = resolvedMode === 'dark' ? brand.sidebarColorsDark : brand.sidebarColors;\n if (sidebar) {\n const sidebarVars: Record<string, string | undefined> = {\n '--color-sidebar': sidebar.sidebar,\n '--color-sidebar-foreground': sidebar.sidebarForeground,\n '--color-sidebar-primary': sidebar.sidebarPrimary,\n '--color-sidebar-primary-foreground': sidebar.sidebarPrimaryForeground,\n '--color-sidebar-accent': sidebar.sidebarAccent,\n '--color-sidebar-accent-foreground': sidebar.sidebarAccentForeground,\n '--color-sidebar-border': sidebar.sidebarBorder,\n '--color-sidebar-ring': sidebar.sidebarRing,\n };\n for (const [name, value] of Object.entries(sidebarVars)) {\n if (value !== undefined) vars[name] = value;\n }\n }\n\n // Trim at the runtime boundary too: ThemeProvider applies this output without\n // re-validating the schema, so a whitespace-only value must not emit\n // `--font-sans` (it would defeat the `var(--font-sans, …)` fallback).\n const fontFamily = brand.typography?.fontFamily?.trim();\n if (fontFamily) {\n vars['--font-sans'] = fontFamily;\n }\n\n if (brand.radii) {\n vars['--radius-sm'] = brand.radii.sm;\n vars['--radius-md'] = brand.radii.md;\n vars['--radius-lg'] = brand.radii.lg;\n }\n\n return vars;\n};\n","import { createContext } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nexport type ColorScheme = 'light' | 'dark' | 'system';\nexport type ResolvedMode = 'light' | 'dark';\n\nexport type ThemeContextValue = {\n brand: Brand;\n setBrand?: (brand: Brand) => void;\n /** The configured color scheme (may be `'system'`). */\n colorScheme: ColorScheme;\n /** The effective light/dark mode after resolving `'system'`. */\n resolvedMode: ResolvedMode;\n};\n\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n","import { type ReactNode, use, useEffect, useState } from 'react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { AIGradientsSvg } from './AIGradientSvg';\nimport { brandToCssVars } from './brandToCssVars';\nimport { type ColorScheme, type ResolvedMode, ThemeContext } from './themeContext';\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\nconst systemMode = (): ResolvedMode => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return 'light';\n return window.matchMedia(DARK_QUERY).matches ? 'dark' : 'light';\n};\n\ntype Props = {\n /**\n * Brand whose tokens drive the chrome (`@keboola/design`'s semantic CSS\n * variables) inside this provider's subtree. Defaults to the canonical\n * `keboola` brand so existing apps that mount `<ThemeProvider>` without a\n * `brand` prop continue to render as today.\n */\n brand?: Brand;\n /**\n * Optional setter exposed through `useTheme()` so consumers can flip\n * brands at runtime (Storybook toolbar, a customer-impersonation switch).\n */\n setBrand?: (brand: Brand) => void;\n /**\n * Light/dark mode. `'system'` follows `prefers-color-scheme` and updates\n * reactively. Defaults to `'light'` so existing mounts don't drift. Only\n * the ROOT provider applies this (sets `data-theme`, owns the media\n * listener); a nested provider inherits the root's resolved mode.\n */\n colorScheme?: ColorScheme;\n children: ReactNode;\n};\n\n/**\n * Responsibilities:\n * - ROOT only: render hidden SVG gradient definitions (`aigradient-600`,\n * `aigradient-700`). The IDs are hardcoded, so a nested provider must not\n * re-emit them or the document gains duplicate (HTML-invalid) IDs.\n * - Set `data-brand=\"<brand.id>\"` on `<html>` and inject the matching `--color-*` /\n * `--radius-*` CSS variables (for the resolved light/dark mode) so chrome\n * re-skins for the supplied brand.\n * - ROOT only: set `data-theme` (`light` | `dark`) on `<html>` and own the\n * `prefers-color-scheme` media listener when `colorScheme === 'system'`.\n * A nested provider inherits the resolved mode from the parent context and\n * touches neither `data-theme` nor a media listener.\n * - Expose `{ brand, setBrand?, colorScheme, resolvedMode }` via `useTheme()`.\n *\n * Brand × mode: the active mode's palette is injected inline on `<html>`. When\n * the resolved mode is `dark` and the brand has no `colorsDark`, the colour\n * vars are NOT emitted — the stylesheet `[data-theme='dark']` block supplies\n * them, so no `!important` override is ever needed.\n *\n * `data-brand` (brand id) and `data-theme` (light/dark) are separate axes so\n * they compose without clobbering each other.\n */\nexport const ThemeProvider = ({\n brand = keboola,\n setBrand,\n colorScheme = 'light',\n children,\n}: Props) => {\n const parent = use(ThemeContext);\n const isRoot = parent === null;\n\n // System mode is tracked only by the root; nested providers inherit the\n // parent's resolved mode and never subscribe to media changes.\n const [systemResolved, setSystemResolved] = useState<ResolvedMode>(systemMode);\n\n useEffect(() => {\n if (!isRoot || colorScheme !== 'system') return;\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;\n const mql = window.matchMedia(DARK_QUERY);\n const onChange = (event: MediaQueryListEvent) =>\n setSystemResolved(event.matches ? 'dark' : 'light');\n // Re-sync in case the value changed between the lazy initializer and effect.\n setSystemResolved(mql.matches ? 'dark' : 'light');\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }, [isRoot, colorScheme]);\n\n const ownMode: ResolvedMode = colorScheme === 'system' ? systemResolved : colorScheme;\n // Nested providers inherit the root's resolved mode; the root uses its own.\n const resolvedMode: ResolvedMode = isRoot ? ownMode : parent.resolvedMode;\n\n // Root owns `data-theme` on `<html>`.\n useEffect(() => {\n if (!isRoot || typeof document === 'undefined') return;\n const root = document.documentElement;\n const previous = root.getAttribute('data-theme');\n root.setAttribute('data-theme', resolvedMode);\n return () => {\n if (root.getAttribute('data-theme') !== resolvedMode) return;\n if (previous === null) {\n root.removeAttribute('data-theme');\n } else {\n root.setAttribute('data-theme', previous);\n }\n };\n }, [isRoot, resolvedMode]);\n\n // Every provider (root or nested) injects ITS brand's vars for the resolved mode.\n useEffect(() => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n\n // Capture the previous `data-brand` + per-property inline-style values\n // so cleanup can restore them. Cleanup only restores values that\n // weren't overwritten by some other provider in between — without that\n // check, an inner provider unmounting after an outer one wrote new\n // values would clobber the outer's brand back to bare `:root` (React\n // runs child effects before parent effects on mount, so the inner's\n // captured \"previous\" is stale by the time it unmounts).\n const previousBrand = root.getAttribute('data-brand');\n const vars = brandToCssVars(brand, resolvedMode);\n const previousValues: Record<string, string> = {};\n for (const name of Object.keys(vars)) {\n previousValues[name] = root.style.getPropertyValue(name);\n }\n\n root.setAttribute('data-brand', brand.id);\n for (const [name, value] of Object.entries(vars)) {\n root.style.setProperty(name, value);\n }\n\n return () => {\n for (const [name, previousValue] of Object.entries(previousValues)) {\n // Only roll back if our value is still live — if someone else\n // (an outer provider, a sibling) has since written a different\n // value, leave it alone.\n if (root.style.getPropertyValue(name) !== vars[name]) continue;\n if (previousValue === '') {\n root.style.removeProperty(name);\n } else {\n root.style.setProperty(name, previousValue);\n }\n }\n if (root.getAttribute('data-brand') === brand.id) {\n if (previousBrand === null) {\n root.removeAttribute('data-brand');\n } else {\n root.setAttribute('data-brand', previousBrand);\n }\n }\n };\n }, [brand, resolvedMode]);\n\n return (\n <ThemeContext value={{ brand, setBrand, colorScheme, resolvedMode }}>\n {/* ROOT only — the defs use hardcoded IDs; a nested provider re-emitting\n them would produce duplicate, HTML-invalid element IDs. */}\n {isRoot && <AIGradientsSvg />}\n {children}\n </ThemeContext>\n );\n};\n","import type { ReactElement } from 'react';\nimport { render, type RenderOptions, type RenderResult } from '@testing-library/react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { ThemeProvider } from '../components/ThemeProvider';\n\ntype Options = Omit<RenderOptions, 'wrapper'> & {\n /** Brand to drive the wrapping `<ThemeProvider>`. Defaults to `keboola`. */\n brand?: Brand;\n};\n\n/**\n * `@testing-library/react` `render` wrapped in a `<ThemeProvider brand={...}>`.\n *\n * Use in unit tests for any component that calls `useTheme()` or relies on\n * the brand-driven CSS variables. Defaults to the canonical Keboola brand so\n * callers can opt into a specific brand only when they need to assert\n * brand-dependent behaviour (e.g. chrome re-skins under `keboola-legacy`).\n */\nexport const renderWithTheme = (ui: ReactElement, options: Options = {}): RenderResult => {\n const { brand = keboola, ...rest } = options;\n return render(ui, {\n ...rest,\n wrapper: ({ children }) => <ThemeProvider brand={brand}>{children}</ThemeProvider>,\n });\n};\n"]}
|
package/dist/testing.js
CHANGED
|
@@ -133,7 +133,7 @@ var ThemeProvider = ({
|
|
|
133
133
|
};
|
|
134
134
|
}, [brand, resolvedMode]);
|
|
135
135
|
return /* @__PURE__ */ jsxs(ThemeContext, { value: { brand, setBrand, colorScheme, resolvedMode }, children: [
|
|
136
|
-
/* @__PURE__ */ jsx(AIGradientsSvg, {}),
|
|
136
|
+
isRoot && /* @__PURE__ */ jsx(AIGradientsSvg, {}),
|
|
137
137
|
children
|
|
138
138
|
] });
|
|
139
139
|
};
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ThemeProvider/AIGradientSvg.tsx","../src/components/ThemeProvider/brandToCssVars.ts","../src/components/ThemeProvider/themeContext.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/testing/renderWithTheme.tsx"],"names":["jsxs","jsx","keboola"],"mappings":"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","file":"testing.js","sourcesContent":["export const AIGradientsSvg = () => (\n <svg width=\"0\" height=\"0\" style={{ position: 'absolute', visibility: 'hidden' }}>\n <defs>\n {/*aigradient-600*/}\n <linearGradient id=\"aigradient-600\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#61E4FF\" />\n <stop offset=\"50%\" stopColor=\"#1F8FFF\" />\n <stop offset=\"100%\" stopColor=\"#7C5CFF\" />\n </linearGradient>\n\n {/*aigradient-700*/}\n <linearGradient id=\"aigradient-700\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#075fb8\" />\n <stop offset=\"100%\" stopColor=\"#5b2cb8\" />\n </linearGradient>\n </defs>\n </svg>\n);\n","import type { Brand } from '@keboola/brand-registry';\n\nimport type { ResolvedMode } from './themeContext';\n\n/**\n * Map every brand-overridable slot to the matching CSS variable on `:root`.\n *\n * Brand colours are already stored as `R G B` channel strings (validated by\n * the brand schema), matching the shape Tailwind's\n * `rgb(var(--color-*) / <alpha-value>)` template consumes — they're written\n * straight into the CSS variable without conversion.\n *\n * Mode-aware: when `resolvedMode === 'dark'` and the brand ships a\n * `colorsDark` palette, that palette wins; otherwise `colors` (light) is\n * used. The crucial exception — if `resolvedMode === 'dark'` and the brand\n * has NO `colorsDark`, the 9 colour vars are omitted entirely so the\n * stylesheet's `[data-theme='dark']` block supplies the dark palette.\n * Emitting the light values here would clobber that with no `!important`\n * recourse. Non-colour vars (radii) are emitted regardless of mode.\n *\n * Slots the brand doesn't define are omitted — `ThemeProvider` only\n * captures/restores the keys returned here, so unset slots inherit whatever\n * is already live on `<html>` (an outer provider's value, or the `:root` /\n * `[data-theme='dark']` defaults in `styles.css` if no outer provider has\n * touched them). The accent colours (in `colors` / `colorsDark`), the sidebar\n * colour family (`brand.sidebarColors` / `brand.sidebarColorsDark`), and the\n * brand font (`typography.fontFamily`, emitted as `--font-sans`) follow the\n * same opt-in rule.\n */\nexport const brandToCssVars = (\n brand: Brand,\n resolvedMode: ResolvedMode,\n): Record<string, string> => {\n const vars: Record<string, string> = {};\n\n // In dark mode with no brand dark palette, leave the colour vars to the\n // stylesheet's `[data-theme='dark']` block (do not emit light values).\n const colors = resolvedMode === 'dark' ? brand.colorsDark : brand.colors;\n if (colors) {\n vars['--color-background'] = colors.background;\n vars['--color-foreground'] = colors.foreground;\n vars['--color-muted'] = colors.muted;\n vars['--color-muted-foreground'] = colors.mutedForeground;\n vars['--color-border'] = colors.border;\n vars['--color-primary'] = colors.primary;\n vars['--color-primary-foreground'] = colors.primaryForeground;\n vars['--color-destructive'] = colors.destructive;\n vars['--color-destructive-foreground'] = colors.destructiveForeground;\n // accent is a general semantic colour (content + sidebar); optional.\n if (colors.accent !== undefined) vars['--color-accent'] = colors.accent;\n if (colors.accentForeground !== undefined)\n vars['--color-accent-foreground'] = colors.accentForeground;\n }\n\n // Sidebar colour tokens — same dark-mode rule as the core palette: with no\n // `sidebarColorsDark`, leave the stylesheet `[data-theme='dark']` sidebar intact.\n const sidebar = resolvedMode === 'dark' ? brand.sidebarColorsDark : brand.sidebarColors;\n if (sidebar) {\n const sidebarVars: Record<string, string | undefined> = {\n '--color-sidebar': sidebar.sidebar,\n '--color-sidebar-foreground': sidebar.sidebarForeground,\n '--color-sidebar-primary': sidebar.sidebarPrimary,\n '--color-sidebar-primary-foreground': sidebar.sidebarPrimaryForeground,\n '--color-sidebar-accent': sidebar.sidebarAccent,\n '--color-sidebar-accent-foreground': sidebar.sidebarAccentForeground,\n '--color-sidebar-border': sidebar.sidebarBorder,\n '--color-sidebar-ring': sidebar.sidebarRing,\n };\n for (const [name, value] of Object.entries(sidebarVars)) {\n if (value !== undefined) vars[name] = value;\n }\n }\n\n // Trim at the runtime boundary too: ThemeProvider applies this output without\n // re-validating the schema, so a whitespace-only value must not emit\n // `--font-sans` (it would defeat the `var(--font-sans, …)` fallback).\n const fontFamily = brand.typography?.fontFamily?.trim();\n if (fontFamily) {\n vars['--font-sans'] = fontFamily;\n }\n\n if (brand.radii) {\n vars['--radius-sm'] = brand.radii.sm;\n vars['--radius-md'] = brand.radii.md;\n vars['--radius-lg'] = brand.radii.lg;\n }\n\n return vars;\n};\n","import { createContext } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nexport type ColorScheme = 'light' | 'dark' | 'system';\nexport type ResolvedMode = 'light' | 'dark';\n\nexport type ThemeContextValue = {\n brand: Brand;\n setBrand?: (brand: Brand) => void;\n /** The configured color scheme (may be `'system'`). */\n colorScheme: ColorScheme;\n /** The effective light/dark mode after resolving `'system'`. */\n resolvedMode: ResolvedMode;\n};\n\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n","import { type ReactNode, use, useEffect, useState } from 'react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { AIGradientsSvg } from './AIGradientSvg';\nimport { brandToCssVars } from './brandToCssVars';\nimport { type ColorScheme, type ResolvedMode, ThemeContext } from './themeContext';\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\nconst systemMode = (): ResolvedMode => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return 'light';\n return window.matchMedia(DARK_QUERY).matches ? 'dark' : 'light';\n};\n\ntype Props = {\n /**\n * Brand whose tokens drive the chrome (`@keboola/design`'s semantic CSS\n * variables) inside this provider's subtree. Defaults to the canonical\n * `keboola` brand so existing apps that mount `<ThemeProvider>` without a\n * `brand` prop continue to render as today.\n */\n brand?: Brand;\n /**\n * Optional setter exposed through `useTheme()` so consumers can flip\n * brands at runtime (Storybook toolbar, a customer-impersonation switch).\n */\n setBrand?: (brand: Brand) => void;\n /**\n * Light/dark mode. `'system'` follows `prefers-color-scheme` and updates\n * reactively. Defaults to `'light'` so existing mounts don't drift. Only\n * the ROOT provider applies this (sets `data-theme`, owns the media\n * listener); a nested provider inherits the root's resolved mode.\n */\n colorScheme?: ColorScheme;\n children: ReactNode;\n};\n\n/**\n * Responsibilities:\n * - Render hidden SVG gradient definitions (`aigradient-600`, `aigradient-700`).\n * - Set `data-brand=\"<brand.id>\"` on `<html>` and inject the matching `--color-*` /\n * `--radius-*` CSS variables (for the resolved light/dark mode) so chrome\n * re-skins for the supplied brand.\n * - ROOT only: set `data-theme` (`light` | `dark`) on `<html>` and own the\n * `prefers-color-scheme` media listener when `colorScheme === 'system'`.\n * A nested provider inherits the resolved mode from the parent context and\n * touches neither `data-theme` nor a media listener.\n * - Expose `{ brand, setBrand?, colorScheme, resolvedMode }` via `useTheme()`.\n *\n * Brand × mode: the active mode's palette is injected inline on `<html>`. When\n * the resolved mode is `dark` and the brand has no `colorsDark`, the colour\n * vars are NOT emitted — the stylesheet `[data-theme='dark']` block supplies\n * them, so no `!important` override is ever needed.\n *\n * `data-brand` (brand id) and `data-theme` (light/dark) are separate axes so\n * they compose without clobbering each other.\n */\nexport const ThemeProvider = ({\n brand = keboola,\n setBrand,\n colorScheme = 'light',\n children,\n}: Props) => {\n const parent = use(ThemeContext);\n const isRoot = parent === null;\n\n // System mode is tracked only by the root; nested providers inherit the\n // parent's resolved mode and never subscribe to media changes.\n const [systemResolved, setSystemResolved] = useState<ResolvedMode>(systemMode);\n\n useEffect(() => {\n if (!isRoot || colorScheme !== 'system') return;\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;\n const mql = window.matchMedia(DARK_QUERY);\n const onChange = (event: MediaQueryListEvent) =>\n setSystemResolved(event.matches ? 'dark' : 'light');\n // Re-sync in case the value changed between the lazy initializer and effect.\n setSystemResolved(mql.matches ? 'dark' : 'light');\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }, [isRoot, colorScheme]);\n\n const ownMode: ResolvedMode = colorScheme === 'system' ? systemResolved : colorScheme;\n // Nested providers inherit the root's resolved mode; the root uses its own.\n const resolvedMode: ResolvedMode = isRoot ? ownMode : parent.resolvedMode;\n\n // Root owns `data-theme` on `<html>`.\n useEffect(() => {\n if (!isRoot || typeof document === 'undefined') return;\n const root = document.documentElement;\n const previous = root.getAttribute('data-theme');\n root.setAttribute('data-theme', resolvedMode);\n return () => {\n if (root.getAttribute('data-theme') !== resolvedMode) return;\n if (previous === null) {\n root.removeAttribute('data-theme');\n } else {\n root.setAttribute('data-theme', previous);\n }\n };\n }, [isRoot, resolvedMode]);\n\n // Every provider (root or nested) injects ITS brand's vars for the resolved mode.\n useEffect(() => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n\n // Capture the previous `data-brand` + per-property inline-style values\n // so cleanup can restore them. Cleanup only restores values that\n // weren't overwritten by some other provider in between — without that\n // check, an inner provider unmounting after an outer one wrote new\n // values would clobber the outer's brand back to bare `:root` (React\n // runs child effects before parent effects on mount, so the inner's\n // captured \"previous\" is stale by the time it unmounts).\n const previousBrand = root.getAttribute('data-brand');\n const vars = brandToCssVars(brand, resolvedMode);\n const previousValues: Record<string, string> = {};\n for (const name of Object.keys(vars)) {\n previousValues[name] = root.style.getPropertyValue(name);\n }\n\n root.setAttribute('data-brand', brand.id);\n for (const [name, value] of Object.entries(vars)) {\n root.style.setProperty(name, value);\n }\n\n return () => {\n for (const [name, previousValue] of Object.entries(previousValues)) {\n // Only roll back if our value is still live — if someone else\n // (an outer provider, a sibling) has since written a different\n // value, leave it alone.\n if (root.style.getPropertyValue(name) !== vars[name]) continue;\n if (previousValue === '') {\n root.style.removeProperty(name);\n } else {\n root.style.setProperty(name, previousValue);\n }\n }\n if (root.getAttribute('data-brand') === brand.id) {\n if (previousBrand === null) {\n root.removeAttribute('data-brand');\n } else {\n root.setAttribute('data-brand', previousBrand);\n }\n }\n };\n }, [brand, resolvedMode]);\n\n return (\n <ThemeContext value={{ brand, setBrand, colorScheme, resolvedMode }}>\n <AIGradientsSvg />\n {children}\n </ThemeContext>\n );\n};\n","import type { ReactElement } from 'react';\nimport { render, type RenderOptions, type RenderResult } from '@testing-library/react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { ThemeProvider } from '../components/ThemeProvider';\n\ntype Options = Omit<RenderOptions, 'wrapper'> & {\n /** Brand to drive the wrapping `<ThemeProvider>`. Defaults to `keboola`. */\n brand?: Brand;\n};\n\n/**\n * `@testing-library/react` `render` wrapped in a `<ThemeProvider brand={...}>`.\n *\n * Use in unit tests for any component that calls `useTheme()` or relies on\n * the brand-driven CSS variables. Defaults to the canonical Keboola brand so\n * callers can opt into a specific brand only when they need to assert\n * brand-dependent behaviour (e.g. chrome re-skins under `keboola-legacy`).\n */\nexport const renderWithTheme = (ui: ReactElement, options: Options = {}): RenderResult => {\n const { brand = keboola, ...rest } = options;\n return render(ui, {\n ...rest,\n wrapper: ({ children }) => <ThemeProvider brand={brand}>{children}</ThemeProvider>,\n });\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/ThemeProvider/AIGradientSvg.tsx","../src/components/ThemeProvider/brandToCssVars.ts","../src/components/ThemeProvider/themeContext.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/testing/renderWithTheme.tsx"],"names":["jsxs","jsx","keboola"],"mappings":"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","file":"testing.js","sourcesContent":["export const AIGradientsSvg = () => (\n <svg width=\"0\" height=\"0\" style={{ position: 'absolute', visibility: 'hidden' }}>\n <defs>\n {/*aigradient-600*/}\n <linearGradient id=\"aigradient-600\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#61E4FF\" />\n <stop offset=\"50%\" stopColor=\"#1F8FFF\" />\n <stop offset=\"100%\" stopColor=\"#7C5CFF\" />\n </linearGradient>\n\n {/*aigradient-700*/}\n <linearGradient id=\"aigradient-700\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#075fb8\" />\n <stop offset=\"100%\" stopColor=\"#5b2cb8\" />\n </linearGradient>\n </defs>\n </svg>\n);\n","import type { Brand } from '@keboola/brand-registry';\n\nimport type { ResolvedMode } from './themeContext';\n\n/**\n * Map every brand-overridable slot to the matching CSS variable on `:root`.\n *\n * Brand colours are already stored as `R G B` channel strings (validated by\n * the brand schema), matching the shape Tailwind's\n * `rgb(var(--color-*) / <alpha-value>)` template consumes — they're written\n * straight into the CSS variable without conversion.\n *\n * Mode-aware: when `resolvedMode === 'dark'` and the brand ships a\n * `colorsDark` palette, that palette wins; otherwise `colors` (light) is\n * used. The crucial exception — if `resolvedMode === 'dark'` and the brand\n * has NO `colorsDark`, the 9 colour vars are omitted entirely so the\n * stylesheet's `[data-theme='dark']` block supplies the dark palette.\n * Emitting the light values here would clobber that with no `!important`\n * recourse. Non-colour vars (radii) are emitted regardless of mode.\n *\n * Slots the brand doesn't define are omitted — `ThemeProvider` only\n * captures/restores the keys returned here, so unset slots inherit whatever\n * is already live on `<html>` (an outer provider's value, or the `:root` /\n * `[data-theme='dark']` defaults in `styles.css` if no outer provider has\n * touched them). The accent colours (in `colors` / `colorsDark`), the sidebar\n * colour family (`brand.sidebarColors` / `brand.sidebarColorsDark`), and the\n * brand font (`typography.fontFamily`, emitted as `--font-sans`) follow the\n * same opt-in rule.\n */\nexport const brandToCssVars = (\n brand: Brand,\n resolvedMode: ResolvedMode,\n): Record<string, string> => {\n const vars: Record<string, string> = {};\n\n // In dark mode with no brand dark palette, leave the colour vars to the\n // stylesheet's `[data-theme='dark']` block (do not emit light values).\n const colors = resolvedMode === 'dark' ? brand.colorsDark : brand.colors;\n if (colors) {\n vars['--color-background'] = colors.background;\n vars['--color-foreground'] = colors.foreground;\n vars['--color-muted'] = colors.muted;\n vars['--color-muted-foreground'] = colors.mutedForeground;\n vars['--color-border'] = colors.border;\n vars['--color-primary'] = colors.primary;\n vars['--color-primary-foreground'] = colors.primaryForeground;\n vars['--color-destructive'] = colors.destructive;\n vars['--color-destructive-foreground'] = colors.destructiveForeground;\n // accent is a general semantic colour (content + sidebar); optional.\n if (colors.accent !== undefined) vars['--color-accent'] = colors.accent;\n if (colors.accentForeground !== undefined)\n vars['--color-accent-foreground'] = colors.accentForeground;\n }\n\n // Sidebar colour tokens — same dark-mode rule as the core palette: with no\n // `sidebarColorsDark`, leave the stylesheet `[data-theme='dark']` sidebar intact.\n const sidebar = resolvedMode === 'dark' ? brand.sidebarColorsDark : brand.sidebarColors;\n if (sidebar) {\n const sidebarVars: Record<string, string | undefined> = {\n '--color-sidebar': sidebar.sidebar,\n '--color-sidebar-foreground': sidebar.sidebarForeground,\n '--color-sidebar-primary': sidebar.sidebarPrimary,\n '--color-sidebar-primary-foreground': sidebar.sidebarPrimaryForeground,\n '--color-sidebar-accent': sidebar.sidebarAccent,\n '--color-sidebar-accent-foreground': sidebar.sidebarAccentForeground,\n '--color-sidebar-border': sidebar.sidebarBorder,\n '--color-sidebar-ring': sidebar.sidebarRing,\n };\n for (const [name, value] of Object.entries(sidebarVars)) {\n if (value !== undefined) vars[name] = value;\n }\n }\n\n // Trim at the runtime boundary too: ThemeProvider applies this output without\n // re-validating the schema, so a whitespace-only value must not emit\n // `--font-sans` (it would defeat the `var(--font-sans, …)` fallback).\n const fontFamily = brand.typography?.fontFamily?.trim();\n if (fontFamily) {\n vars['--font-sans'] = fontFamily;\n }\n\n if (brand.radii) {\n vars['--radius-sm'] = brand.radii.sm;\n vars['--radius-md'] = brand.radii.md;\n vars['--radius-lg'] = brand.radii.lg;\n }\n\n return vars;\n};\n","import { createContext } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nexport type ColorScheme = 'light' | 'dark' | 'system';\nexport type ResolvedMode = 'light' | 'dark';\n\nexport type ThemeContextValue = {\n brand: Brand;\n setBrand?: (brand: Brand) => void;\n /** The configured color scheme (may be `'system'`). */\n colorScheme: ColorScheme;\n /** The effective light/dark mode after resolving `'system'`. */\n resolvedMode: ResolvedMode;\n};\n\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n","import { type ReactNode, use, useEffect, useState } from 'react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { AIGradientsSvg } from './AIGradientSvg';\nimport { brandToCssVars } from './brandToCssVars';\nimport { type ColorScheme, type ResolvedMode, ThemeContext } from './themeContext';\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\nconst systemMode = (): ResolvedMode => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return 'light';\n return window.matchMedia(DARK_QUERY).matches ? 'dark' : 'light';\n};\n\ntype Props = {\n /**\n * Brand whose tokens drive the chrome (`@keboola/design`'s semantic CSS\n * variables) inside this provider's subtree. Defaults to the canonical\n * `keboola` brand so existing apps that mount `<ThemeProvider>` without a\n * `brand` prop continue to render as today.\n */\n brand?: Brand;\n /**\n * Optional setter exposed through `useTheme()` so consumers can flip\n * brands at runtime (Storybook toolbar, a customer-impersonation switch).\n */\n setBrand?: (brand: Brand) => void;\n /**\n * Light/dark mode. `'system'` follows `prefers-color-scheme` and updates\n * reactively. Defaults to `'light'` so existing mounts don't drift. Only\n * the ROOT provider applies this (sets `data-theme`, owns the media\n * listener); a nested provider inherits the root's resolved mode.\n */\n colorScheme?: ColorScheme;\n children: ReactNode;\n};\n\n/**\n * Responsibilities:\n * - ROOT only: render hidden SVG gradient definitions (`aigradient-600`,\n * `aigradient-700`). The IDs are hardcoded, so a nested provider must not\n * re-emit them or the document gains duplicate (HTML-invalid) IDs.\n * - Set `data-brand=\"<brand.id>\"` on `<html>` and inject the matching `--color-*` /\n * `--radius-*` CSS variables (for the resolved light/dark mode) so chrome\n * re-skins for the supplied brand.\n * - ROOT only: set `data-theme` (`light` | `dark`) on `<html>` and own the\n * `prefers-color-scheme` media listener when `colorScheme === 'system'`.\n * A nested provider inherits the resolved mode from the parent context and\n * touches neither `data-theme` nor a media listener.\n * - Expose `{ brand, setBrand?, colorScheme, resolvedMode }` via `useTheme()`.\n *\n * Brand × mode: the active mode's palette is injected inline on `<html>`. When\n * the resolved mode is `dark` and the brand has no `colorsDark`, the colour\n * vars are NOT emitted — the stylesheet `[data-theme='dark']` block supplies\n * them, so no `!important` override is ever needed.\n *\n * `data-brand` (brand id) and `data-theme` (light/dark) are separate axes so\n * they compose without clobbering each other.\n */\nexport const ThemeProvider = ({\n brand = keboola,\n setBrand,\n colorScheme = 'light',\n children,\n}: Props) => {\n const parent = use(ThemeContext);\n const isRoot = parent === null;\n\n // System mode is tracked only by the root; nested providers inherit the\n // parent's resolved mode and never subscribe to media changes.\n const [systemResolved, setSystemResolved] = useState<ResolvedMode>(systemMode);\n\n useEffect(() => {\n if (!isRoot || colorScheme !== 'system') return;\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;\n const mql = window.matchMedia(DARK_QUERY);\n const onChange = (event: MediaQueryListEvent) =>\n setSystemResolved(event.matches ? 'dark' : 'light');\n // Re-sync in case the value changed between the lazy initializer and effect.\n setSystemResolved(mql.matches ? 'dark' : 'light');\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }, [isRoot, colorScheme]);\n\n const ownMode: ResolvedMode = colorScheme === 'system' ? systemResolved : colorScheme;\n // Nested providers inherit the root's resolved mode; the root uses its own.\n const resolvedMode: ResolvedMode = isRoot ? ownMode : parent.resolvedMode;\n\n // Root owns `data-theme` on `<html>`.\n useEffect(() => {\n if (!isRoot || typeof document === 'undefined') return;\n const root = document.documentElement;\n const previous = root.getAttribute('data-theme');\n root.setAttribute('data-theme', resolvedMode);\n return () => {\n if (root.getAttribute('data-theme') !== resolvedMode) return;\n if (previous === null) {\n root.removeAttribute('data-theme');\n } else {\n root.setAttribute('data-theme', previous);\n }\n };\n }, [isRoot, resolvedMode]);\n\n // Every provider (root or nested) injects ITS brand's vars for the resolved mode.\n useEffect(() => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n\n // Capture the previous `data-brand` + per-property inline-style values\n // so cleanup can restore them. Cleanup only restores values that\n // weren't overwritten by some other provider in between — without that\n // check, an inner provider unmounting after an outer one wrote new\n // values would clobber the outer's brand back to bare `:root` (React\n // runs child effects before parent effects on mount, so the inner's\n // captured \"previous\" is stale by the time it unmounts).\n const previousBrand = root.getAttribute('data-brand');\n const vars = brandToCssVars(brand, resolvedMode);\n const previousValues: Record<string, string> = {};\n for (const name of Object.keys(vars)) {\n previousValues[name] = root.style.getPropertyValue(name);\n }\n\n root.setAttribute('data-brand', brand.id);\n for (const [name, value] of Object.entries(vars)) {\n root.style.setProperty(name, value);\n }\n\n return () => {\n for (const [name, previousValue] of Object.entries(previousValues)) {\n // Only roll back if our value is still live — if someone else\n // (an outer provider, a sibling) has since written a different\n // value, leave it alone.\n if (root.style.getPropertyValue(name) !== vars[name]) continue;\n if (previousValue === '') {\n root.style.removeProperty(name);\n } else {\n root.style.setProperty(name, previousValue);\n }\n }\n if (root.getAttribute('data-brand') === brand.id) {\n if (previousBrand === null) {\n root.removeAttribute('data-brand');\n } else {\n root.setAttribute('data-brand', previousBrand);\n }\n }\n };\n }, [brand, resolvedMode]);\n\n return (\n <ThemeContext value={{ brand, setBrand, colorScheme, resolvedMode }}>\n {/* ROOT only — the defs use hardcoded IDs; a nested provider re-emitting\n them would produce duplicate, HTML-invalid element IDs. */}\n {isRoot && <AIGradientsSvg />}\n {children}\n </ThemeContext>\n );\n};\n","import type { ReactElement } from 'react';\nimport { render, type RenderOptions, type RenderResult } from '@testing-library/react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { ThemeProvider } from '../components/ThemeProvider';\n\ntype Options = Omit<RenderOptions, 'wrapper'> & {\n /** Brand to drive the wrapping `<ThemeProvider>`. Defaults to `keboola`. */\n brand?: Brand;\n};\n\n/**\n * `@testing-library/react` `render` wrapped in a `<ThemeProvider brand={...}>`.\n *\n * Use in unit tests for any component that calls `useTheme()` or relies on\n * the brand-driven CSS variables. Defaults to the canonical Keboola brand so\n * callers can opt into a specific brand only when they need to assert\n * brand-dependent behaviour (e.g. chrome re-skins under `keboola-legacy`).\n */\nexport const renderWithTheme = (ui: ReactElement, options: Options = {}): RenderResult => {\n const { brand = keboola, ...rest } = options;\n return render(ui, {\n ...rest,\n wrapper: ({ children }) => <ThemeProvider brand={brand}>{children}</ThemeProvider>,\n });\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keboola/design",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.2",
|
|
4
4
|
"description": "Keboola UI platform design system — shadcn-aligned React primitives, Keboola-branded blocks, and the form bindings built on TanStack Form.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -133,10 +133,12 @@
|
|
|
133
133
|
"@storybook/addon-links": "^10.2.19",
|
|
134
134
|
"@storybook/addon-vitest": "10.2.19",
|
|
135
135
|
"@storybook/react-vite": "^10.2.19",
|
|
136
|
+
"@tanstack/react-table": "^8.21.3",
|
|
136
137
|
"@testing-library/dom": "^10.4.1",
|
|
137
138
|
"@testing-library/react": "^16.3.2",
|
|
138
139
|
"@testing-library/user-event": "^14.6.1",
|
|
139
140
|
"@types/react": "^19.2.15",
|
|
141
|
+
"@types/react-dom": "^19.2.3",
|
|
140
142
|
"@vitejs/plugin-react": "^6.0.0",
|
|
141
143
|
"@vitest/browser": "4.1.8",
|
|
142
144
|
"@vitest/browser-playwright": "4.1.8",
|
|
@@ -145,6 +147,7 @@
|
|
|
145
147
|
"oxfmt": "^0.44.0",
|
|
146
148
|
"oxlint": "^1.56.0",
|
|
147
149
|
"playwright": "1.60.0",
|
|
150
|
+
"postcss-import": "^15.1.0",
|
|
148
151
|
"react": "^19.2.7",
|
|
149
152
|
"react-docgen-typescript": "^2.2.2",
|
|
150
153
|
"react-dom": "^19.2.7",
|
|
@@ -155,8 +158,8 @@
|
|
|
155
158
|
"typescript": "^6.0.3",
|
|
156
159
|
"vite": "^8.0.16",
|
|
157
160
|
"vitest": "^4.1.8",
|
|
161
|
+
"@keboola/oxlint-config": "^2.0.0",
|
|
158
162
|
"@keboola/postcss-config": "^0.1.1",
|
|
159
|
-
"@keboola/oxlint-config": "^1.0.0",
|
|
160
163
|
"@keboola/tsconfig": "^0.1.2"
|
|
161
164
|
},
|
|
162
165
|
"peerDependencies": {
|
|
@@ -171,22 +174,22 @@
|
|
|
171
174
|
},
|
|
172
175
|
"dependencies": {
|
|
173
176
|
"@base-ui/react": "1.5.0",
|
|
174
|
-
"@codemirror/autocomplete": "^6.20.
|
|
175
|
-
"@codemirror/commands": "^6.10.
|
|
177
|
+
"@codemirror/autocomplete": "^6.20.3",
|
|
178
|
+
"@codemirror/commands": "^6.10.4",
|
|
176
179
|
"@codemirror/lang-javascript": "^6.2.5",
|
|
177
180
|
"@codemirror/lang-json": "^6.0.2",
|
|
178
181
|
"@codemirror/lang-python": "^6.2.1",
|
|
179
182
|
"@codemirror/lang-sql": "^6.10.0",
|
|
180
|
-
"@codemirror/language": "^6.12.
|
|
181
|
-
"@codemirror/legacy-modes": "^6.5.
|
|
182
|
-
"@codemirror/lint": "^6.9.
|
|
183
|
-
"@codemirror/merge": "^6.12.
|
|
184
|
-
"@codemirror/state": "^6.
|
|
185
|
-
"@codemirror/view": "^6.
|
|
183
|
+
"@codemirror/language": "^6.12.4",
|
|
184
|
+
"@codemirror/legacy-modes": "^6.5.3",
|
|
185
|
+
"@codemirror/lint": "^6.9.7",
|
|
186
|
+
"@codemirror/merge": "^6.12.2",
|
|
187
|
+
"@codemirror/state": "^6.7.0",
|
|
188
|
+
"@codemirror/view": "^6.43.4",
|
|
186
189
|
"@keboola/brand-registry": "^1.1.0",
|
|
187
|
-
"@keboola/codemirror-lang-sfsql": "^1.2.
|
|
188
|
-
"@keboola/codemirror-lang-sql": "^0.2.
|
|
189
|
-
"@keboola/tailwind-config": "^0.
|
|
190
|
+
"@keboola/codemirror-lang-sfsql": "^1.2.1",
|
|
191
|
+
"@keboola/codemirror-lang-sql": "^0.2.1",
|
|
192
|
+
"@keboola/tailwind-config": "^0.3.0",
|
|
190
193
|
"@lexical/code": "^0.42.0",
|
|
191
194
|
"@lexical/link": "^0.42.0",
|
|
192
195
|
"@lexical/list": "^0.42.0",
|
|
@@ -194,26 +197,27 @@
|
|
|
194
197
|
"@lexical/react": "^0.42.0",
|
|
195
198
|
"@lexical/rich-text": "^0.42.0",
|
|
196
199
|
"@lexical/utils": "^0.42.0",
|
|
197
|
-
"@radix-ui/react-
|
|
198
|
-
"@radix-ui/react-
|
|
199
|
-
"@radix-ui/react-
|
|
200
|
-
"@radix-ui/react-
|
|
201
|
-
"@radix-ui/react-
|
|
202
|
-
"@radix-ui/react-
|
|
203
|
-
"@radix-ui/react-
|
|
204
|
-
"@radix-ui/react-
|
|
205
|
-
"@radix-ui/react-
|
|
206
|
-
"@radix-ui/react-
|
|
207
|
-
"@radix-ui/react-
|
|
208
|
-
"@radix-ui/react-
|
|
209
|
-
"@radix-ui/react-
|
|
210
|
-
"@radix-ui/react-
|
|
211
|
-
"@radix-ui/react-
|
|
212
|
-
"@radix-ui/react-
|
|
213
|
-
"@radix-ui/react-
|
|
214
|
-
"@radix-ui/react-
|
|
215
|
-
"@radix-ui/react-toggle
|
|
216
|
-
"@radix-ui/react-
|
|
200
|
+
"@radix-ui/react-alert-dialog": "^1.1.17",
|
|
201
|
+
"@radix-ui/react-avatar": "^1.2.0",
|
|
202
|
+
"@radix-ui/react-checkbox": "^1.3.5",
|
|
203
|
+
"@radix-ui/react-collapsible": "^1.1.14",
|
|
204
|
+
"@radix-ui/react-dialog": "^1.1.17",
|
|
205
|
+
"@radix-ui/react-dropdown-menu": "^2.1.18",
|
|
206
|
+
"@radix-ui/react-hover-card": "^1.1.17",
|
|
207
|
+
"@radix-ui/react-label": "^2.1.10",
|
|
208
|
+
"@radix-ui/react-popover": "^1.1.17",
|
|
209
|
+
"@radix-ui/react-progress": "^1.1.10",
|
|
210
|
+
"@radix-ui/react-radio-group": "^1.4.1",
|
|
211
|
+
"@radix-ui/react-scroll-area": "^1.2.12",
|
|
212
|
+
"@radix-ui/react-select": "^2.3.1",
|
|
213
|
+
"@radix-ui/react-separator": "^1.1.10",
|
|
214
|
+
"@radix-ui/react-slider": "^1.4.1",
|
|
215
|
+
"@radix-ui/react-slot": "^1.3.0",
|
|
216
|
+
"@radix-ui/react-switch": "^1.3.1",
|
|
217
|
+
"@radix-ui/react-tabs": "^1.1.15",
|
|
218
|
+
"@radix-ui/react-toggle": "^1.1.12",
|
|
219
|
+
"@radix-ui/react-toggle-group": "^1.1.13",
|
|
220
|
+
"@radix-ui/react-tooltip": "^1.2.10",
|
|
217
221
|
"@tanstack/react-form": "^1.29.1",
|
|
218
222
|
"@testing-library/jest-dom": "^6.8.0",
|
|
219
223
|
"@uiw/codemirror-theme-github": "^4.25.2",
|
package/src/styles.css
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/* react-day-picker structural CSS (the `.rdp-*` grid/layout). Imported here so
|
|
2
|
+
* it is inlined into the compiled `dist/styles.css` by postcss-import, keeping
|
|
3
|
+
* the JS entry free of a bare `.css` side-effect import (which breaks plain
|
|
4
|
+
* Node/Vitest ESM resolution). Consumers get it via `@keboola/design/styles`. */
|
|
5
|
+
@import 'react-day-picker/src/style.css';
|
|
6
|
+
|
|
1
7
|
@tailwind base;
|
|
2
8
|
@tailwind utilities;
|
|
3
9
|
@tailwind components;
|
|
@@ -9,8 +15,9 @@
|
|
|
9
15
|
* Primitive scales (neutral-*, primary-*, error-*, warning-*, etc.) remain hardcoded in colors.ts.
|
|
10
16
|
*/
|
|
11
17
|
:root {
|
|
12
|
-
/*
|
|
13
|
-
--color-
|
|
18
|
+
/* Card — raised container chrome (Modal/Card/Sheet). */
|
|
19
|
+
--color-card: 255 255 255; /* #ffffff */
|
|
20
|
+
--color-card-foreground: 30 41 59; /* #1e293b */
|
|
14
21
|
--color-background: 249 250 251; /* #f9fafb */
|
|
15
22
|
--color-foreground: 30 41 59; /* #1e293b */
|
|
16
23
|
|
|
@@ -18,6 +25,10 @@
|
|
|
18
25
|
--color-primary: 31 143 255; /* #1f8fff */
|
|
19
26
|
--color-primary-foreground: 255 255 255; /* #ffffff */
|
|
20
27
|
|
|
28
|
+
/* Secondary */
|
|
29
|
+
--color-secondary: 226 232 240; /* #e2e8f0 */
|
|
30
|
+
--color-secondary-foreground: 30 41 59; /* #1e293b */
|
|
31
|
+
|
|
21
32
|
/* Popover */
|
|
22
33
|
--color-popover: 255 255 255; /* #ffffff */
|
|
23
34
|
--color-popover-foreground: 30 41 59; /* #1e293b */
|
|
@@ -26,16 +37,27 @@
|
|
|
26
37
|
--color-accent: 224 242 254; /* #e0f2fe */
|
|
27
38
|
--color-accent-foreground: 13 65 127; /* #0d417f */
|
|
28
39
|
|
|
29
|
-
/* Border / Input */
|
|
40
|
+
/* Border / Input / Ring */
|
|
30
41
|
--color-border: 229 231 235; /* #e5e7eb */
|
|
31
42
|
--color-input: 203 213 225; /* #cbd5e1 */
|
|
43
|
+
--color-ring: 31 143 255; /* #1f8fff — focus ring (brand blue) */
|
|
32
44
|
|
|
33
|
-
/*
|
|
45
|
+
/* Chart — categorical palette (brand-derived, mutually distinct). */
|
|
46
|
+
--color-chart-1: 31 143 255; /* #1f8fff */
|
|
47
|
+
--color-chart-2: 0 194 135; /* #00c287 */
|
|
48
|
+
--color-chart-3: 146 92 255; /* #925cff */
|
|
49
|
+
--color-chart-4: 245 147 0; /* #f59300 */
|
|
50
|
+
--color-chart-5: 237 94 223; /* #ed5edf */
|
|
51
|
+
|
|
52
|
+
/* Status — categorical (meaning-encoding), tunable per color scheme. */
|
|
34
53
|
--color-destructive: 220 38 38; /* #dc2626 */
|
|
35
54
|
--color-destructive-foreground: 255 255 255; /* #ffffff */
|
|
36
|
-
|
|
37
|
-
/* Success */
|
|
38
55
|
--color-success: 31 157 97; /* #1f9d61 */
|
|
56
|
+
--color-success-foreground: 255 255 255; /* #ffffff */
|
|
57
|
+
--color-warning: 245 147 0; /* #f59300 */
|
|
58
|
+
--color-warning-foreground: 82 49 0; /* #523100 — dark text for legibility on amber */
|
|
59
|
+
--color-info: 31 143 255; /* #1f8fff */
|
|
60
|
+
--color-info-foreground: 255 255 255; /* #ffffff */
|
|
39
61
|
|
|
40
62
|
/* Muted */
|
|
41
63
|
--color-muted: 241 245 249; /* #f1f5f9 */
|
|
@@ -54,8 +76,9 @@
|
|
|
54
76
|
|
|
55
77
|
/* Dark mode (Graphite) — Keboola-blue accent, not reachable until the theme toggle ships. */
|
|
56
78
|
[data-theme='dark'] {
|
|
57
|
-
/*
|
|
58
|
-
--color-
|
|
79
|
+
/* Card — raised container chrome (Modal/Card/Sheet), lifted off background. */
|
|
80
|
+
--color-card: 34 34 40; /* #222228 */
|
|
81
|
+
--color-card-foreground: 229 230 236; /* #e5e6ec */
|
|
59
82
|
--color-background: 19 19 23; /* #131317 */
|
|
60
83
|
--color-foreground: 229 230 236; /* #e5e6ec */
|
|
61
84
|
|
|
@@ -63,6 +86,10 @@
|
|
|
63
86
|
--color-primary: 31 143 255; /* #1f8fff */
|
|
64
87
|
--color-primary-foreground: 255 255 255; /* #ffffff */
|
|
65
88
|
|
|
89
|
+
/* Secondary */
|
|
90
|
+
--color-secondary: 47 48 55; /* #2f3037 */
|
|
91
|
+
--color-secondary-foreground: 229 230 236; /* #e5e6ec */
|
|
92
|
+
|
|
66
93
|
/* Popover */
|
|
67
94
|
--color-popover: 38 38 45; /* #26262d */
|
|
68
95
|
--color-popover-foreground: 229 230 236; /* #e5e6ec */
|
|
@@ -71,16 +98,27 @@
|
|
|
71
98
|
--color-accent: 30 58 95; /* #1e3a5f */
|
|
72
99
|
--color-accent-foreground: 147 197 253; /* #93c5fd */
|
|
73
100
|
|
|
74
|
-
/* Border / Input */
|
|
101
|
+
/* Border / Input / Ring */
|
|
75
102
|
--color-border: 52 53 62; /* #34353e */
|
|
76
103
|
--color-input: 60 61 71; /* #3c3d47 */
|
|
104
|
+
--color-ring: 31 143 255; /* #1f8fff — focus ring (brand blue) */
|
|
77
105
|
|
|
78
|
-
/*
|
|
79
|
-
--color-
|
|
80
|
-
--color-
|
|
106
|
+
/* Chart — categorical palette (brand-derived, lightened for dark surfaces). */
|
|
107
|
+
--color-chart-1: 92 173 255; /* #5cadff */
|
|
108
|
+
--color-chart-2: 0 230 161; /* #00e6a1 */
|
|
109
|
+
--color-chart-3: 173 133 255; /* #ad85ff */
|
|
110
|
+
--color-chart-4: 255 190 92; /* #ffbe5c */
|
|
111
|
+
--color-chart-5: 242 140 232; /* #f28ce8 */
|
|
81
112
|
|
|
82
|
-
/*
|
|
113
|
+
/* Status — categorical (meaning-encoding), tuned for dark surfaces. */
|
|
114
|
+
--color-destructive: 229 72 77; /* #e5484d */
|
|
115
|
+
--color-destructive-foreground: 255 255 255; /* #ffffff */
|
|
83
116
|
--color-success: 62 207 127; /* #3ecf7f */
|
|
117
|
+
--color-success-foreground: 255 255 255; /* #ffffff */
|
|
118
|
+
--color-warning: 255 190 92; /* #ffbe5c */
|
|
119
|
+
--color-warning-foreground: 26 18 5; /* #1a1205 — dark text for legibility on amber */
|
|
120
|
+
--color-info: 92 173 255; /* #5cadff */
|
|
121
|
+
--color-info-foreground: 255 255 255; /* #ffffff */
|
|
84
122
|
|
|
85
123
|
/* Muted */
|
|
86
124
|
--color-muted: 38 38 45; /* #26262d */
|
|
@@ -102,7 +140,7 @@
|
|
|
102
140
|
to the alert group class — NOT bare [data-variant], which Toast/Badge/Button/
|
|
103
141
|
Select also set. `[data-theme='dark']` never activates for keboola-legacy
|
|
104
142
|
(light-only), so this is inert for kbc-ui. Pairs with AlertRoot's
|
|
105
|
-
`
|
|
143
|
+
brand-driven `tw-text-foreground` text (argo brands) so both bg and text flip. */
|
|
106
144
|
[data-theme='dark'] [class~='tw-group/alert'][data-variant='error'] {
|
|
107
145
|
background-color: rgb(51 22 25); /* #331619 */
|
|
108
146
|
}
|