diff --git a/plane-src/apps/web/app/root.tsx b/plane-src/apps/web/app/root.tsx index 3c38ddb..8b4c01e 100644 --- a/plane-src/apps/web/app/root.tsx +++ b/plane-src/apps/web/app/root.tsx @@ -78,6 +78,14 @@ const designConfigStyle = { "--bg-accent-primary": formatCssRgb(accentRgb), "--bg-accent-primary-hover": formatCssRgb(accentHoverRgb), "--bg-accent-primary-active": formatCssRgb(accentActiveRgb), + "--txt-accent-primary": formatCssRgb(accentRgb), + "--txt-accent-secondary": formatCssRgb(blendRgb(accentRgb, 0, 0.25)), + "--txt-icon-accent-primary": formatCssRgb(accentRgb), + "--text-color-accent-primary": formatCssRgb(accentRgb), + "--text-color-accent-secondary": formatCssRgb(blendRgb(accentRgb, 0, 0.25)), + "--text-color-icon-accent-primary": formatCssRgb(accentRgb), + "--stroke-accent-primary": formatCssRgb(accentRgb), + "--fill-accent-primary": formatCssRgb(accentRgb), "--txt-on-color": formatCssRgb(onAccentRgb), "--txt-icon-on-color": formatCssRgb(onAccentRgb), } as CSSProperties; diff --git a/plane-src/apps/web/ce/components/projects/external-contours/board-item.tsx b/plane-src/apps/web/ce/components/projects/external-contours/board-item.tsx index c3ac437..80a29cd 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/board-item.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/board-item.tsx @@ -129,7 +129,7 @@ export const ExternalContoursBoardItem = observer(function ExternalContoursBoard ? projectStateIds.map((stateId) => getStateById(stateId)).filter((state): state is IState => !!state) : sourceStateIds.map((stateId) => sourceStateMap[stateId]).filter((state): state is IState => !!state); const foregroundClasses = isActive ? "text-[rgb(var(--nodedc-on-card-active-rgb))]" : "text-white"; - const subtleTextClasses = isActive ? "text-[#2F4721]" : "text-[#B3B3B8]"; + const subtleTextClasses = isActive ? "text-[rgb(var(--nodedc-on-card-active-rgb))]/70" : "text-[#B3B3B8]"; const pillBackgroundClasses = isActive ? "bg-black/10 text-[rgb(var(--nodedc-on-card-active-rgb))]" : "bg-[rgb(var(--nodedc-card-passive-rgb))] text-white"; diff --git a/plane-src/apps/web/core/components/analytics/work-items/created-vs-resolved.tsx b/plane-src/apps/web/core/components/analytics/work-items/created-vs-resolved.tsx index 72cb1a1..e43c640 100644 --- a/plane-src/apps/web/core/components/analytics/work-items/created-vs-resolved.tsx +++ b/plane-src/apps/web/core/components/analytics/work-items/created-vs-resolved.tsx @@ -23,6 +23,8 @@ import AnalyticsSectionWrapper from "../analytics-section-wrapper"; import { ChartLoader } from "../loaders"; const analyticsService = new AnalyticsService(); +const NODEDC_ANALYTICS_ACCENT = "rgb(var(--nodedc-accent-rgb))"; +const NODEDC_ANALYTICS_ACCENT_SOFT = "rgb(var(--nodedc-accent-rgb) / 0.18)"; const CreatedVsResolved = observer(function CreatedVsResolved() { const { selectedDuration, @@ -66,12 +68,12 @@ const CreatedVsResolved = observer(function CreatedVsResolved() { { key: "completed_issues", label: "Решено", - fill: "rgba(195, 255, 102, 0.18)", + fill: NODEDC_ANALYTICS_ACCENT_SOFT, fillOpacity: 1, stackId: "bar-one", showDot: false, smoothCurves: true, - strokeColor: "#C3FF66", + strokeColor: NODEDC_ANALYTICS_ACCENT, strokeOpacity: 1, }, { diff --git a/plane-src/apps/web/core/components/analytics/work-items/utils.ts b/plane-src/apps/web/core/components/analytics/work-items/utils.ts index 6e3b530..5c6b5d2 100644 --- a/plane-src/apps/web/core/components/analytics/work-items/utils.ts +++ b/plane-src/apps/web/core/components/analytics/work-items/utils.ts @@ -15,7 +15,7 @@ interface ParamsProps { } export const NODEDC_ANALYTICS_COLORS = [ - "#C3FF66", + "rgb(var(--nodedc-accent-rgb))", "#F5F7FB", "#7C7F85", "#050505", @@ -29,12 +29,12 @@ const STATE_GROUP_COLORS: Record = { backlog: "#050505", unstarted: "#7C7F85", started: "#FFFFFF", - completed: "#C3FF66", + completed: "rgb(var(--nodedc-accent-rgb))", cancelled: "#050505", }; const PRIORITY_COLORS: Record = { - urgent: "#C3FF66", + urgent: "rgb(var(--nodedc-accent-rgb))", high: "#F5F7FB", medium: "#7C7F85", low: "#2A2B2E", diff --git a/plane-src/apps/web/core/components/issues/issue-layouts/kanban/internal-contour-card.tsx b/plane-src/apps/web/core/components/issues/issue-layouts/kanban/internal-contour-card.tsx index e76d044..987afe0 100644 --- a/plane-src/apps/web/core/components/issues/issue-layouts/kanban/internal-contour-card.tsx +++ b/plane-src/apps/web/core/components/issues/issue-layouts/kanban/internal-contour-card.tsx @@ -206,7 +206,12 @@ export const InternalContourKanbanCard = observer(function InternalContourKanban
{creatorName}
-
+
{sourceContourName}
diff --git a/plane-src/apps/web/core/components/issues/issue-layouts/shared/nodedc-work-item-card.tsx b/plane-src/apps/web/core/components/issues/issue-layouts/shared/nodedc-work-item-card.tsx index 9bc32c4..76ddc09 100644 --- a/plane-src/apps/web/core/components/issues/issue-layouts/shared/nodedc-work-item-card.tsx +++ b/plane-src/apps/web/core/components/issues/issue-layouts/shared/nodedc-work-item-card.tsx @@ -30,7 +30,7 @@ export const getNodedcWorkItemCardAppearance = (isActive: boolean) => ({ foregroundClasses: isActive ? "text-[rgb(var(--nodedc-on-card-active-rgb))]" : "text-[rgb(var(--nodedc-on-card-passive-rgb))]", - subtleTextClasses: isActive ? "text-[#2F4721]" : "text-[#B3B3B8]", + subtleTextClasses: isActive ? "text-[rgb(var(--nodedc-on-card-active-rgb))]/70" : "text-[#B3B3B8]", pillBackgroundClasses: isActive ? "bg-black/10 text-[rgb(var(--nodedc-on-card-active-rgb))]" : "bg-[rgb(var(--nodedc-card-passive-rgb))] text-[rgb(var(--nodedc-on-card-passive-rgb))]", @@ -61,7 +61,7 @@ export const NodedcWorkItemProgress = (props: TNodedcWorkItemProgressProps) => { {percentage}% @@ -70,10 +70,16 @@ export const NodedcWorkItemProgress = (props: TNodedcWorkItemProgressProps) => { {segments.map((segmentFill, segmentIndex) => (
diff --git a/plane-src/apps/web/core/components/profile/overview/helpers.ts b/plane-src/apps/web/core/components/profile/overview/helpers.ts index a65a505..d8c6939 100644 --- a/plane-src/apps/web/core/components/profile/overview/helpers.ts +++ b/plane-src/apps/web/core/components/profile/overview/helpers.ts @@ -10,7 +10,7 @@ export const PROFILE_STATE_GROUP_COLORS: Partial> = backlog: "#050505", unstarted: "#7C7F85", started: "#F5F7FB", - completed: "#C3FF66", + completed: "rgb(var(--nodedc-accent-rgb))", cancelled: "#050505", }; @@ -23,7 +23,7 @@ export const PROFILE_STATE_GROUP_LABELS: Partial> = }; export const PROFILE_PRIORITY_COLORS: Record = { - urgent: "#C3FF66", + urgent: "rgb(var(--nodedc-accent-rgb))", high: "#F5F7FB", medium: "#7C7F85", low: "#2A2B2E", diff --git a/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/accent-color.tsx b/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/accent-color.tsx new file mode 100644 index 0000000..ba8e01e --- /dev/null +++ b/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/accent-color.tsx @@ -0,0 +1,283 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { Fragment, useEffect, useMemo, useState } from "react"; +import { Popover, Transition } from "@headlessui/react"; +import { observer } from "mobx-react"; +import * as ColorPicker from "react-color"; +import type { ColorResult } from "react-color"; +// plane imports +import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { + applyNodedcAccent, + getReadableNodedcTextRgb, + nodedcAccentHexToRgb, + normalizeNodedcAccentHex, +} from "@plane/utils"; +// helpers +import { NODEDC_DEFAULT_ACCENT_HEX } from "@/helpers/nodedc-design"; +// components +import { SettingsControlItem } from "@/components/settings/control-item"; +// hooks +import { useUserProfile } from "@/hooks/store/user"; + +const ACCENT_PRESET_COLORS = [ + "#EF4444", + "#F97316", + "#FACC15", + "#8B5E34", + "#C3FF66", + "#5FBF2A", + "#A855F7", + "#7C3AED", + "#3B82F6", + "#2DD4BF", + "#86EFAC", + "#050505", + "#2A2B2E", + "#7C7F85", + "#F5F7FB", +]; + +const CHROME_PICKER_STYLES = { + default: { + picker: { + width: "100%", + background: "transparent", + borderRadius: 0, + boxShadow: "none", + fontFamily: "inherit", + }, + saturation: { + borderRadius: "1.35rem", + overflow: "hidden", + }, + Saturation: { + borderRadius: "1.35rem", + }, + body: { + padding: "0.9rem 0 0", + }, + controls: { + alignItems: "center", + gap: "0.75rem", + }, + color: { + width: "2rem", + }, + swatch: { + borderRadius: "999px", + boxShadow: "0 0 0 1px rgba(255,255,255,0.14)", + }, + hue: { + borderRadius: "999px", + overflow: "hidden", + }, + Hue: { + borderRadius: "999px", + }, + alpha: { + display: "none", + }, + Alpha: { + display: "none", + }, + }, +}; + +const getReadableColor = (hex: string) => { + const rgb = nodedcAccentHexToRgb(hex); + if (!rgb) return undefined; + return `rgb(${getReadableNodedcTextRgb(rgb).join(" ")})`; +}; + +export const ProfileSettingsAccentColor = observer(function ProfileSettingsAccentColor() { + const { data: userProfile, updateUserTheme } = useUserProfile(); + const [draftAccent, setDraftAccent] = useState(NODEDC_DEFAULT_ACCENT_HEX); + const [isSaving, setIsSaving] = useState(false); + + const savedAccent = useMemo( + () => normalizeNodedcAccentHex(userProfile?.theme?.nodedcAccent) || NODEDC_DEFAULT_ACCENT_HEX, + [userProfile?.theme?.nodedcAccent] + ); + const normalizedDraftAccent = normalizeNodedcAccentHex(draftAccent); + const isDraftValid = !!normalizedDraftAccent; + const isDirty = normalizedDraftAccent !== savedAccent; + + useEffect(() => { + setDraftAccent(savedAccent); + }, [savedAccent]); + + const handleAccentChange = (value: string) => { + const nextValue = value.startsWith("#") ? value : `#${value}`; + setDraftAccent(nextValue); + + const normalizedValue = normalizeNodedcAccentHex(nextValue); + if (normalizedValue) applyNodedcAccent(normalizedValue); + }; + + const handleColorPickerChange = (color: ColorResult) => { + handleAccentChange(color.hex); + }; + + const handleSave = async () => { + if (!normalizedDraftAccent) { + setToast({ + type: TOAST_TYPE.ERROR, + title: "Ошибка", + message: "Введите корректный HEX-цвет.", + }); + return; + } + + try { + setIsSaving(true); + applyNodedcAccent(normalizedDraftAccent); + await updateUserTheme({ nodedcAccent: normalizedDraftAccent }); + setToast({ + type: TOAST_TYPE.SUCCESS, + title: "Сохранено", + message: "Акцентный цвет обновлен.", + }); + } catch (_error) { + applyNodedcAccent(savedAccent); + setDraftAccent(savedAccent); + setToast({ + type: TOAST_TYPE.ERROR, + title: "Ошибка", + message: "Не удалось сохранить акцентный цвет.", + }); + } finally { + setIsSaving(false); + } + }; + + const handleReset = async () => { + try { + setIsSaving(true); + setDraftAccent(NODEDC_DEFAULT_ACCENT_HEX); + applyNodedcAccent(NODEDC_DEFAULT_ACCENT_HEX); + await updateUserTheme({ nodedcAccent: undefined }); + setToast({ + type: TOAST_TYPE.SUCCESS, + title: "Сброшено", + message: "Акцентный цвет возвращен к дизайн-конфигу.", + }); + } catch (_error) { + applyNodedcAccent(savedAccent); + setDraftAccent(savedAccent); + setToast({ + type: TOAST_TYPE.ERROR, + title: "Ошибка", + message: "Не удалось сбросить акцентный цвет.", + }); + } finally { + setIsSaving(false); + } + }; + + return ( + +
+ + {() => ( + <> + + + + + + +
+ {ACCENT_PRESET_COLORS.map((color) => { + const isSelected = color === normalizedDraftAccent; + + return ( + + ); + })} +
+
+
+ + )} +
+ handleAccentChange(event.target.value)} + placeholder={NODEDC_DEFAULT_ACCENT_HEX} + className="nodedc-settings-input h-11 min-h-11 w-full pl-10 pr-4 text-13 font-medium uppercase" + style={{ + color: normalizedDraftAccent ? getReadableColor(normalizedDraftAccent) : undefined, + }} + aria-invalid={!isDraftValid} + /> +
+
+ + +
+
+ } + /> + ); +}); diff --git a/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/default-list.tsx b/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/default-list.tsx index 8b3b010..37b7993 100644 --- a/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/default-list.tsx +++ b/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/default-list.tsx @@ -7,6 +7,8 @@ import { observer } from "mobx-react"; // components import { ThemeSwitcher } from "@/plane-web/components/preferences/theme-switcher"; +// local imports +import { ProfileSettingsAccentColor } from "./accent-color"; export const ProfileSettingsDefaultPreferencesList = observer(function ProfileSettingsDefaultPreferencesList() { return ( @@ -18,6 +20,7 @@ export const ProfileSettingsDefaultPreferencesList = observer(function ProfileSe description: "select_or_customize_your_interface_color_scheme", }} /> +
); }); diff --git a/plane-src/apps/web/core/helpers/nodedc-design.ts b/plane-src/apps/web/core/helpers/nodedc-design.ts new file mode 100644 index 0000000..0a5d89c --- /dev/null +++ b/plane-src/apps/web/core/helpers/nodedc-design.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { rgbToNodedcAccentHex } from "@plane/utils"; +import designConfig from "../../design.config.json"; + +const defaultAccentRgb = designConfig.nodedc.accent_rgb as [number, number, number]; + +export const NODEDC_DEFAULT_ACCENT_HEX = rgbToNodedcAccentHex(defaultAccentRgb); diff --git a/plane-src/apps/web/core/lib/wrappers/store-wrapper.tsx b/plane-src/apps/web/core/lib/wrappers/store-wrapper.tsx index bf9db3c..191de3b 100644 --- a/plane-src/apps/web/core/lib/wrappers/store-wrapper.tsx +++ b/plane-src/apps/web/core/lib/wrappers/store-wrapper.tsx @@ -12,7 +12,8 @@ import { useTheme } from "next-themes"; import type { TLanguage } from "@plane/i18n"; import { DEFAULT_LANGUAGE, useTranslation } from "@plane/i18n"; // helpers -import { applyCustomTheme, clearCustomTheme } from "@plane/utils"; +import { applyCustomTheme, applyNodedcAccent, clearCustomTheme } from "@plane/utils"; +import { NODEDC_DEFAULT_ACCENT_HEX } from "@/helpers/nodedc-design"; // hooks import { useAppTheme } from "@/hooks/store/use-app-theme"; import { useRouterParams } from "@/hooks/store/use-router-params"; @@ -107,6 +108,11 @@ function StoreWrapper(props: TStoreWrapper) { previousThemeRef.current = currentTheme; }, [userProfile?.theme]); + useEffect(() => { + if (!userProfile?.id) return; + applyNodedcAccent(userProfile?.theme?.nodedcAccent || NODEDC_DEFAULT_ACCENT_HEX); + }, [userProfile?.id, userProfile?.theme?.nodedcAccent]); + useEffect(() => { if (!userProfile?.id) return; changeLanguage((userProfile?.language as TLanguage) || DEFAULT_LANGUAGE); diff --git a/plane-src/apps/web/core/store/user/profile.store.ts b/plane-src/apps/web/core/store/user/profile.store.ts index e29089e..3ef4739 100644 --- a/plane-src/apps/web/core/store/user/profile.store.ts +++ b/plane-src/apps/web/core/store/user/profile.store.ts @@ -45,6 +45,7 @@ export class ProfileStore implements IUserProfileStore { primary: undefined, background: undefined, darkPalette: false, + nodedcAccent: undefined, }, onboarding_step: { workspace_join: false, diff --git a/plane-src/apps/web/styles/globals.css b/plane-src/apps/web/styles/globals.css index 6da5dda..46f30ba 100644 --- a/plane-src/apps/web/styles/globals.css +++ b/plane-src/apps/web/styles/globals.css @@ -1285,6 +1285,45 @@ color: var(--text-color-primary) !important; } + .nodedc-accent-picker-panel { + border: 0 !important; + outline: none !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.075) 0%, rgba(255, 255, 255, 0.026) 100%), + rgba(8, 9, 12, 0.78) !important; + -webkit-backdrop-filter: blur(28px); + backdrop-filter: blur(28px); + } + + .nodedc-accent-chrome-picker, + .nodedc-accent-chrome-picker * { + font-family: inherit !important; + } + + .nodedc-accent-chrome-picker input { + min-height: 2rem !important; + border: 0 !important; + outline: none !important; + border-radius: 0.85rem !important; + background: rgba(255, 255, 255, 0.07) !important; + box-shadow: none !important; + color: var(--text-color-primary) !important; + font-size: 0.75rem !important; + font-weight: 500 !important; + } + + .nodedc-accent-chrome-picker label { + color: var(--text-color-secondary) !important; + font-size: 0.62rem !important; + font-weight: 600 !important; + letter-spacing: 0.08em !important; + text-transform: uppercase !important; + } + + .nodedc-accent-chrome-picker .flexbox-fix { + border: 0 !important; + } + .nodedc-settings-danger-button { min-height: 2.75rem; border: 0 !important; diff --git a/plane-src/packages/types/src/current-user/profile.ts b/plane-src/packages/types/src/current-user/profile.ts index 677f8f7..167ef13 100644 --- a/plane-src/packages/types/src/current-user/profile.ts +++ b/plane-src/packages/types/src/current-user/profile.ts @@ -13,6 +13,7 @@ export type TUserProfile = { theme: { theme: string | undefined; + nodedcAccent?: string | undefined; }; onboarding_step: { diff --git a/plane-src/packages/types/src/users.ts b/plane-src/packages/types/src/users.ts index 7391343..cdb264b 100644 --- a/plane-src/packages/types/src/users.ts +++ b/plane-src/packages/types/src/users.ts @@ -69,6 +69,7 @@ export type TUserProfile = { primary: string | undefined; background: string | undefined; darkPalette: boolean | undefined; + nodedcAccent?: string | undefined; }; onboarding_step: TOnboardingSteps; is_onboarded: boolean; @@ -107,6 +108,7 @@ export interface IUserTheme { primary?: string | undefined; background?: string | undefined; darkPalette?: boolean | undefined; + nodedcAccent?: string | undefined; } export interface IUserMemberLite extends IUserLite { diff --git a/plane-src/packages/utils/src/theme/index.ts b/plane-src/packages/utils/src/theme/index.ts index bdb24fa..162d09c 100644 --- a/plane-src/packages/utils/src/theme/index.ts +++ b/plane-src/packages/utils/src/theme/index.ts @@ -27,6 +27,15 @@ export { type DarknessDetectionMethod, } from "./theme-application"; +// NODE.DC runtime accent +export { + applyNodedcAccent, + getReadableNodedcTextRgb, + nodedcAccentHexToRgb, + normalizeNodedcAccentHex, + rgbToNodedcAccentHex, +} from "./nodedc-accent"; + // Color conversion utilities export { hexToHSL, diff --git a/plane-src/packages/utils/src/theme/nodedc-accent.ts b/plane-src/packages/utils/src/theme/nodedc-accent.ts new file mode 100644 index 0000000..8271e40 --- /dev/null +++ b/plane-src/packages/utils/src/theme/nodedc-accent.ts @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { normalizeHexColor, validateHexColor } from "./color-validation"; + +type TRgbTuple = [number, number, number]; + +const DARK_TEXT_RGB: TRgbTuple = [11, 17, 23]; +const LIGHT_TEXT_RGB: TRgbTuple = [245, 247, 251]; + +const clampRgbChannel = (value: number) => Math.min(Math.max(Math.round(value), 0), 255); + +const formatRgbTuple = (rgb: readonly number[]) => rgb.map(clampRgbChannel).join(" "); +const formatCssRgb = (rgb: readonly number[]) => `rgb(${formatRgbTuple(rgb)})`; + +const blendRgb = (rgb: readonly number[], target: number, ratio: number): TRgbTuple => + rgb.map((channel) => clampRgbChannel(channel * (1 - ratio) + target * ratio)) as TRgbTuple; + +const toRelativeLuminance = (rgb: readonly number[]) => { + const [r, g, b] = rgb.map((channel) => { + const normalized = channel / 255; + return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4; + }); + + return 0.2126 * r + 0.7152 * g + 0.0722 * b; +}; + +export const normalizeNodedcAccentHex = (hex: string | null | undefined): string | undefined => { + if (!hex || !validateHexColor(hex)) return undefined; + return `#${normalizeHexColor(hex)}`; +}; + +export const nodedcAccentHexToRgb = (hex: string | null | undefined): TRgbTuple | undefined => { + const normalizedHex = normalizeNodedcAccentHex(hex); + if (!normalizedHex) return undefined; + + const cleanHex = normalizedHex.replace("#", ""); + return [0, 2, 4].map((start) => parseInt(cleanHex.slice(start, start + 2), 16)) as TRgbTuple; +}; + +export const rgbToNodedcAccentHex = (rgb: readonly number[]): string => + `#${rgb + .map((channel) => clampRgbChannel(channel).toString(16).padStart(2, "0")) + .join("") + .toUpperCase()}`; + +export const getReadableNodedcTextRgb = (rgb: readonly number[]): TRgbTuple => + toRelativeLuminance(rgb) > 0.52 ? DARK_TEXT_RGB : LIGHT_TEXT_RGB; + +export const applyNodedcAccent = (hex: string | null | undefined): boolean => { + if (typeof document === "undefined") return false; + + const accentRgb = nodedcAccentHexToRgb(hex); + if (!accentRgb) return false; + + const root = document.documentElement; + const onAccentRgb = getReadableNodedcTextRgb(accentRgb); + + const brandScale: Record = { + "100": blendRgb(accentRgb, 255, 0.9), + "200": blendRgb(accentRgb, 255, 0.8), + "300": blendRgb(accentRgb, 255, 0.65), + "400": blendRgb(accentRgb, 255, 0.45), + "500": blendRgb(accentRgb, 255, 0.25), + "600": blendRgb(accentRgb, 255, 0.1), + "700": blendRgb(accentRgb, 0, 0.25), + "800": blendRgb(accentRgb, 0, 0.35), + "900": blendRgb(accentRgb, 0, 0.45), + "1000": blendRgb(accentRgb, 0, 0.6), + "1100": blendRgb(accentRgb, 0, 0.72), + "1200": blendRgb(accentRgb, 0, 0.82), + }; + + root.style.setProperty("--nodedc-accent-rgb", formatRgbTuple(accentRgb)); + root.style.setProperty("--nodedc-card-active-rgb", formatRgbTuple(accentRgb)); + root.style.setProperty("--nodedc-on-accent-rgb", formatRgbTuple(onAccentRgb)); + root.style.setProperty("--nodedc-on-card-active-rgb", formatRgbTuple(onAccentRgb)); + + Object.entries(brandScale).forEach(([key, value]) => { + root.style.setProperty(`--brand-${key}`, formatCssRgb(value)); + }); + + root.style.setProperty("--brand-default", formatCssRgb(accentRgb)); + root.style.setProperty("--bg-accent-primary", formatCssRgb(accentRgb)); + root.style.setProperty("--bg-accent-primary-hover", formatCssRgb(blendRgb(accentRgb, 255, 0.18))); + root.style.setProperty("--bg-accent-primary-active", formatCssRgb(blendRgb(accentRgb, 0, 0.1))); + root.style.setProperty("--txt-accent-primary", formatCssRgb(accentRgb)); + root.style.setProperty("--txt-accent-secondary", formatCssRgb(blendRgb(accentRgb, 0, 0.25))); + root.style.setProperty("--txt-icon-accent-primary", formatCssRgb(accentRgb)); + root.style.setProperty("--text-color-accent-primary", formatCssRgb(accentRgb)); + root.style.setProperty("--text-color-accent-secondary", formatCssRgb(blendRgb(accentRgb, 0, 0.25))); + root.style.setProperty("--text-color-icon-accent-primary", formatCssRgb(accentRgb)); + root.style.setProperty("--stroke-accent-primary", formatCssRgb(accentRgb)); + root.style.setProperty("--fill-accent-primary", formatCssRgb(accentRgb)); + root.style.setProperty("--txt-on-color", formatCssRgb(onAccentRgb)); + root.style.setProperty("--txt-icon-on-color", formatCssRgb(onAccentRgb)); + + return true; +};