Files
marketplaces/src/app/theme/mappers/theme-css-vars.mapper.ts
sdarbinyan b04e3a67f5 feat: wire up dark mode selector with a real dark palette
theme.mode wrote data-theme-mode to the DOM but no CSS ever reacted to
it, and mapThemeConfigToCssVariables() never looked at mode at all -
selecting Dark had zero visible effect.

Added dark-mode neutral overrides (background/text/border axis only -
brand colors stay as configured) using the palette provided by the
user (colorhunt.co/palette/091413285a48408a71b0e4cc):
  --bg-primary: #091413   --bg-secondary: #285a48
  --text-primary: #b0e4cc --text-secondary: #408a71
  --border-color: #285a48

ThemeEngineService now resolves 'system' mode via
prefers-color-scheme and re-renders live on OS theme changes, and
sets data-theme-mode to the *effective* resolved mode instead of the
raw setting.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 09:35:09 +04:00

68 lines
2.9 KiB
TypeScript

import { ThemeConfig } from '../../shared/models/config';
import { ThemeCssVariables } from '../tokens/theme-css-variable.model';
import { THEME_VARIABLE_MAP } from '../tokens/theme-variable-map';
/**
* Dark-mode neutral/surface overrides. Brand colors (primary/secondary/
* accent/success/warning/danger/info) are intentionally left as configured -
* only the background/text/border axis flips for dark mode, same as most
* dark-theme implementations.
*/
const DARK_MODE_OVERRIDES: ThemeCssVariables = {
[THEME_VARIABLE_MAP.backgroundPrimary]: '#091413',
[THEME_VARIABLE_MAP.backgroundSecondary]: '#285a48',
[THEME_VARIABLE_MAP.textPrimary]: '#b0e4cc',
[THEME_VARIABLE_MAP.textSecondary]: '#408a71',
[THEME_VARIABLE_MAP.border]: '#285a48',
};
export function mapThemeConfigToCssVariables(theme: ThemeConfig, effectiveMode: 'light' | 'dark' = 'light'): ThemeCssVariables {
const spacingScale = Array.isArray(theme.spacing.scale) && theme.spacing.scale.length > 0
? theme.spacing.scale
: [0.25, 0.5, 1, 1.5, 2];
const spacingValues = spacingScale
.slice(0, 5)
.map(multiplier => `${Math.max(0, Number(multiplier) || 0) * theme.spacing.unit}px`);
while (spacingValues.length < 5) {
spacingValues.push(`${theme.spacing.unit}px`);
}
const vars: ThemeCssVariables = {
[THEME_VARIABLE_MAP.primary]: theme.palette.primary,
[THEME_VARIABLE_MAP.secondary]: theme.palette.secondary,
[THEME_VARIABLE_MAP.accent]: theme.palette.accent,
[THEME_VARIABLE_MAP.success]: theme.palette.success,
[THEME_VARIABLE_MAP.warning]: theme.palette.warning,
[THEME_VARIABLE_MAP.danger]: theme.palette.danger,
[THEME_VARIABLE_MAP.info]: theme.palette.info,
[THEME_VARIABLE_MAP.textPrimary]: theme.palette.textPrimary,
[THEME_VARIABLE_MAP.textSecondary]: theme.palette.textSecondary,
[THEME_VARIABLE_MAP.backgroundPrimary]: theme.palette.backgroundPrimary,
[THEME_VARIABLE_MAP.backgroundSecondary]: theme.palette.backgroundSecondary,
[THEME_VARIABLE_MAP.border]: theme.palette.border,
[THEME_VARIABLE_MAP.spaceXs]: spacingValues[0],
[THEME_VARIABLE_MAP.spaceSm]: spacingValues[1],
[THEME_VARIABLE_MAP.spaceMd]: spacingValues[2],
[THEME_VARIABLE_MAP.spaceLg]: spacingValues[3],
[THEME_VARIABLE_MAP.spaceXl]: spacingValues[4],
[THEME_VARIABLE_MAP.transitionFast]: '120ms ease',
[THEME_VARIABLE_MAP.transitionNormal]: '180ms ease',
[THEME_VARIABLE_MAP.transitionSlow]: '300ms ease',
'--app-font-family': theme.typography.primaryFontFamily,
'--app-font-size': `${theme.typography.baseFontSize}px`,
'--app-spacing-unit': `${theme.spacing.unit}px`
};
for (const [key, value] of Object.entries(theme.borderRadiusScale)) {
vars[`--radius-${key}`] = value;
}
for (const [key, value] of Object.entries(theme.shadows)) {
vars[`--shadow-${key}`] = value;
}
return effectiveMode === 'dark' ? { ...vars, ...DARK_MODE_OVERRIDES } : vars;
}