Files
formbricks/apps/web/lib/styling/constants.ts
2026-02-17 05:11:29 +00:00

186 lines
7.2 KiB
TypeScript

// https://github.com/airbnb/javascript/#naming--uppercase
import { TProjectStyling } from "@formbricks/types/project";
import { isLight, mixColor } from "@/lib/utils/colors";
export const COLOR_DEFAULTS = {
brandColor: "#64748b",
questionColor: "#2b2524",
inputColor: "#ffffff",
inputBorderColor: "#cbd5e1",
cardBackgroundColor: "#ffffff",
cardBorderColor: "#f8fafc",
highlightBorderColor: "#64748b",
} as const;
const DEFAULT_BRAND_COLOR = "#64748b";
/**
* Derives a complete set of suggested color values from a single brand color.
*
* Used by the project-level "Suggest Colors" button **and** to build
* `STYLE_DEFAULTS` so that a fresh install always has colours that are
* visually cohesive with the default brand.
*
* The returned object is a flat map of form-field paths to values so it
* can be spread directly into form defaults or applied via `form.setValue`.
*/
export const getSuggestedColors = (brandColor: string = DEFAULT_BRAND_COLOR) => {
// Question / dark text: brand darkened with black (visible brand tint)
const questionColor = mixColor(brandColor, "#000000", 0.35);
// Input / option background: white with noticeable brand tint
const inputBg = mixColor(brandColor, "#ffffff", 0.92);
// Input border: visible brand-tinted border
const inputBorder = mixColor(brandColor, "#ffffff", 0.6);
// Card tones
const cardBg = mixColor(brandColor, "#ffffff", 0.97);
const cardBorder = mixColor(brandColor, "#ffffff", 0.8);
// Page background
const pageBg = mixColor(brandColor, "#ffffff", 0.855);
return {
// General
"brandColor.light": brandColor,
"questionColor.light": questionColor,
// Headlines & Descriptions — use questionColor to match the legacy behaviour
// where all text elements derived their color from questionColor.
"elementHeadlineColor.light": questionColor,
"elementDescriptionColor.light": questionColor,
"elementUpperLabelColor.light": questionColor,
// Buttons — use the brand color so the button matches the user's intent.
"buttonBgColor.light": brandColor,
"buttonTextColor.light": isLight(brandColor) ? "#0f172a" : "#ffffff",
// Inputs
"inputColor.light": inputBg,
"inputBorderColor.light": inputBorder,
"inputTextColor.light": questionColor,
// Options (Radio / Checkbox)
"optionBgColor.light": inputBg,
"optionLabelColor.light": questionColor,
// Card
"cardBackgroundColor.light": cardBg,
"cardBorderColor.light": cardBorder,
// Highlight / Focus
"highlightBorderColor.light": mixColor(brandColor, "#ffffff", 0.25),
// Progress Bar — indicator uses the brand color; track is a lighter tint.
"progressIndicatorBgColor.light": brandColor,
"progressTrackBgColor.light": mixColor(brandColor, "#ffffff", 0.8),
// Background
background: { bg: pageBg, bgType: "color" as const, brightness: 100 },
};
};
// Pre-compute colors derived from the default brand color.
const _colors = getSuggestedColors(DEFAULT_BRAND_COLOR);
/**
* Single source of truth for every styling default.
*
* Color values are derived from the default brand color (#64748b) via
* `getSuggestedColors()`. Non-color values (dimensions, weights, sizes)
* are hardcoded here and must be kept in sync with globals.css.
*
* Used everywhere: form defaults, preview rendering, email templates,
* and as the reset target for "Restore defaults".
*/
export const STYLE_DEFAULTS: TProjectStyling = {
allowStyleOverwrite: true,
brandColor: { light: _colors["brandColor.light"] },
questionColor: { light: _colors["questionColor.light"] },
inputColor: { light: _colors["inputColor.light"] },
inputBorderColor: { light: _colors["inputBorderColor.light"] },
cardBackgroundColor: { light: _colors["cardBackgroundColor.light"] },
cardBorderColor: { light: _colors["cardBorderColor.light"] },
isLogoHidden: false,
highlightBorderColor: { light: _colors["highlightBorderColor.light"] },
isDarkModeEnabled: false,
roundness: 8,
cardArrangement: { linkSurveys: "simple", appSurveys: "simple" },
// Headlines & Descriptions
elementHeadlineColor: { light: _colors["elementHeadlineColor.light"] },
elementHeadlineFontSize: 16,
elementHeadlineFontWeight: 600,
elementDescriptionColor: { light: _colors["elementDescriptionColor.light"] },
elementDescriptionFontSize: 14,
elementDescriptionFontWeight: 400,
elementUpperLabelColor: { light: _colors["elementUpperLabelColor.light"] },
elementUpperLabelFontSize: 12,
elementUpperLabelFontWeight: 400,
// Inputs
inputTextColor: { light: _colors["inputTextColor.light"] },
inputBorderRadius: 8,
inputHeight: 20,
inputFontSize: 14,
inputPaddingX: 8,
inputPaddingY: 8,
inputPlaceholderOpacity: 0.5,
inputShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
// Buttons
buttonBgColor: { light: _colors["buttonBgColor.light"] },
buttonTextColor: { light: _colors["buttonTextColor.light"] },
buttonBorderRadius: 8,
buttonHeight: "auto",
buttonFontSize: 16,
buttonFontWeight: 500,
buttonPaddingX: 12,
buttonPaddingY: 12,
// Options
optionBgColor: { light: _colors["optionBgColor.light"] },
optionLabelColor: { light: _colors["optionLabelColor.light"] },
optionBorderRadius: 8,
optionPaddingX: 16,
optionPaddingY: 16,
optionFontSize: 14,
// Progress Bar
progressTrackHeight: 8,
progressTrackBgColor: { light: _colors["progressTrackBgColor.light"] },
progressIndicatorBgColor: { light: _colors["progressIndicatorBgColor.light"] },
};
/**
* Builds a complete TProjectStyling object from a single brand color.
*
* Uses STYLE_DEFAULTS for all non-color properties (dimensions, weights, etc.)
* and derives every color from the given brand color via getSuggestedColors().
*
* Useful when only a brand color is known (e.g. onboarding) and a fully
* coherent styling object is needed for both preview rendering and persistence.
*/
export const buildStylingFromBrandColor = (brandColor: string = DEFAULT_BRAND_COLOR): TProjectStyling => {
const colors = getSuggestedColors(brandColor);
return {
...STYLE_DEFAULTS,
brandColor: { light: colors["brandColor.light"] },
questionColor: { light: colors["questionColor.light"] },
elementHeadlineColor: { light: colors["elementHeadlineColor.light"] },
elementDescriptionColor: { light: colors["elementDescriptionColor.light"] },
elementUpperLabelColor: { light: colors["elementUpperLabelColor.light"] },
buttonBgColor: { light: colors["buttonBgColor.light"] },
buttonTextColor: { light: colors["buttonTextColor.light"] },
inputColor: { light: colors["inputColor.light"] },
inputBorderColor: { light: colors["inputBorderColor.light"] },
inputTextColor: { light: colors["inputTextColor.light"] },
optionBgColor: { light: colors["optionBgColor.light"] },
optionLabelColor: { light: colors["optionLabelColor.light"] },
cardBackgroundColor: { light: colors["cardBackgroundColor.light"] },
cardBorderColor: { light: colors["cardBorderColor.light"] },
highlightBorderColor: { light: colors["highlightBorderColor.light"] },
progressIndicatorBgColor: { light: colors["progressIndicatorBgColor.light"] },
progressTrackBgColor: { light: colors["progressTrackBgColor.light"] },
background: colors.background,
};
};