mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-10 10:36:38 -06:00
fix: derive styling colors from brand color and add description font weight
- Adjust suggest colors mix ratios to match original behavior (inputs at 92% white, input borders at 60% white, question color at 35% black) - Derive button, progress, input, option, headline, and accent colors from the saved brand color on initial load via getBrandDerivedDefaults - Update CSS defaults in globals.css to reference --fb-survey-brand-color - Add elementDescriptionFontWeight field to styling type, form, and addCustomThemeToDom - Add e2e test for initial load brand color derivation
This commit is contained in:
@@ -1934,6 +1934,7 @@ checksums:
|
||||
environments/workspace/look/advanced_styling_field_button_text: 3304e88bcc3869f3a306634b541e1e07
|
||||
environments/workspace/look/advanced_styling_field_description_color: e2f4cbc96d3f0b75837a9edc95a5eeda
|
||||
environments/workspace/look/advanced_styling_field_description_size: a0d51c3ab7dc56320ecedc2b27917842
|
||||
environments/workspace/look/advanced_styling_field_description_weight: 514680cc7202ad29835c1cbcde3def1c
|
||||
environments/workspace/look/advanced_styling_field_font_size: ca44d14429b2175a1b194793b4ab8f6b
|
||||
environments/workspace/look/advanced_styling_field_font_weight: bfef83778146cf40550df9650d8a07da
|
||||
environments/workspace/look/advanced_styling_field_headline_color: 4ccf3935ad90c88ad4add24f498673ce
|
||||
|
||||
@@ -157,20 +157,22 @@ export const getSuggestedColors = (brandColor: string) => {
|
||||
|
||||
// Accent text: darken the brand if it's light, otherwise use as-is
|
||||
const accentText = isBrandLight ? mixColor(brandColor, "#000000", 0.6) : brandColor;
|
||||
// Dark text: mostly dark with a hint of brand
|
||||
const darkText = mixColor(brandColor, "#0f172a", 0.9);
|
||||
// Input / option background: almost white with a touch of brand
|
||||
const inputBg = mixColor(brandColor, "#ffffff", 0.97);
|
||||
// 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.96);
|
||||
const cardBorder = mixColor(brandColor, "#ffffff", 0.9);
|
||||
const cardBg = mixColor(brandColor, "#ffffff", 0.97);
|
||||
const cardBorder = mixColor(brandColor, "#ffffff", 0.8);
|
||||
// Page background
|
||||
const pageBg = mixColor(brandColor, "#ffffff", 0.95);
|
||||
const pageBg = mixColor(brandColor, "#ffffff", 0.855);
|
||||
|
||||
return {
|
||||
// General
|
||||
"brandColor.light": brandColor,
|
||||
"questionColor.light": darkText,
|
||||
"questionColor.light": questionColor,
|
||||
|
||||
// Accent
|
||||
"accentBgColor.light": brandColor,
|
||||
@@ -187,19 +189,19 @@ export const getSuggestedColors = (brandColor: string) => {
|
||||
|
||||
// Inputs
|
||||
"inputColor.light": inputBg,
|
||||
"inputBorderColor.light": cardBorder,
|
||||
"inputTextColor.light": darkText,
|
||||
"inputBorderColor.light": inputBorder,
|
||||
"inputTextColor.light": questionColor,
|
||||
|
||||
// Options (Radio / Checkbox)
|
||||
"optionBgColor.light": inputBg,
|
||||
"optionLabelColor.light": darkText,
|
||||
"optionLabelColor.light": questionColor,
|
||||
|
||||
// Card
|
||||
"cardBackgroundColor.light": cardBg,
|
||||
"cardBorderColor.light": cardBorder,
|
||||
|
||||
// Highlight / Focus
|
||||
"highlightBorderColor.light": brandColor,
|
||||
"highlightBorderColor.light": mixColor(brandColor, "#ffffff", 0.25),
|
||||
|
||||
// Progress Bar
|
||||
"progressIndicatorBgColor.light": brandColor,
|
||||
|
||||
@@ -111,6 +111,11 @@ export const FormStylingSettings = ({
|
||||
name="elementHeadlineFontWeight"
|
||||
label={t("environments.workspace.look.advanced_styling_field_headline_weight")}
|
||||
/>
|
||||
<NumberField
|
||||
form={form}
|
||||
name="elementDescriptionFontWeight"
|
||||
label={t("environments.workspace.look.advanced_styling_field_description_weight")}
|
||||
/>
|
||||
<ColorField
|
||||
form={form}
|
||||
name="elementUpperLabelColor.light"
|
||||
|
||||
@@ -193,9 +193,7 @@ test.describe("Survey Styling", async () => {
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Verify non-color CSS vars are set before suggesting
|
||||
let cssBefore = await page.evaluate(
|
||||
() => document.getElementById("formbricks__css__custom")?.innerHTML
|
||||
);
|
||||
let cssBefore = await page.evaluate(() => document.getElementById("formbricks__css__custom")?.innerHTML);
|
||||
expect(cssBefore).toContain("--fb-input-border-radius: 12px");
|
||||
expect(cssBefore).toContain("--fb-input-padding-y: 20px");
|
||||
expect(cssBefore).toContain("--fb-option-border-radius: 10px");
|
||||
@@ -214,9 +212,7 @@ test.describe("Survey Styling", async () => {
|
||||
// Wait for the preview to update with derived colors
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
const css = await page.evaluate(
|
||||
() => document.getElementById("formbricks__css__custom")?.innerHTML
|
||||
);
|
||||
const css = await page.evaluate(() => document.getElementById("formbricks__css__custom")?.innerHTML);
|
||||
expect(css).toBeDefined();
|
||||
|
||||
// --- Verify colors ARE derived from brand (not hardcoded) ---
|
||||
@@ -255,6 +251,48 @@ test.describe("Survey Styling", async () => {
|
||||
expect(css).toContain("--fb-option-padding-y: 14px");
|
||||
});
|
||||
|
||||
test("Initial load derives button, progress, input, and option colors from brand color", async ({
|
||||
page,
|
||||
users,
|
||||
}) => {
|
||||
const user = await users.create();
|
||||
await user.login();
|
||||
|
||||
// Navigate to Look & Feel settings
|
||||
await page.getByRole("link", { name: "Configuration" }).click();
|
||||
await page.getByRole("link", { name: "Look & Feel" }).click();
|
||||
await page.waitForURL(/\/environments\/[^/]+\/workspace\/look/);
|
||||
|
||||
// Toggle "Enable custom styling"
|
||||
const addCustomStyles = page.getByLabel("Enable custom styling");
|
||||
if (!(await addCustomStyles.isChecked())) {
|
||||
await addCustomStyles.click();
|
||||
}
|
||||
|
||||
// Wait for the preview to render with default styling
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
const css = await page.evaluate(() => document.getElementById("formbricks__css__custom")?.innerHTML);
|
||||
expect(css).toBeDefined();
|
||||
|
||||
// On initial load (no saved styling), button and progress bar should derive from brand color (#64748b)
|
||||
// NOT from the old hardcoded dark navy (#0f172a)
|
||||
expect(css).not.toContain("--fb-button-bg-color: #0f172a");
|
||||
expect(css).not.toContain("--fb-progress-indicator-bg-color: #0f172a");
|
||||
|
||||
// Input text and option label colors should be brand-derived, not hardcoded
|
||||
expect(css).toContain("--fb-input-text-color:");
|
||||
expect(css).not.toContain("--fb-input-text-color: #0f172a");
|
||||
expect(css).toContain("--fb-option-label-color:");
|
||||
expect(css).not.toContain("--fb-option-label-color: #0f172a");
|
||||
|
||||
// Option background should be brand-tinted, not plain white
|
||||
expect(css).toContain("--fb-option-bg-color:");
|
||||
|
||||
// Headline color should be brand-derived
|
||||
expect(css).toContain("--fb-element-headline-color:");
|
||||
});
|
||||
|
||||
test("Survey Specific Styling (Survey Editor Override)", async ({ page, users }) => {
|
||||
const user = await users.create();
|
||||
await user.login();
|
||||
|
||||
@@ -126,7 +126,7 @@ export const CustomStyles: Story = {
|
||||
control: "color",
|
||||
table: {
|
||||
category: "Progress Styling",
|
||||
defaultValue: { summary: "hsl(222.2 47.4% 11.2% / 0.2)" },
|
||||
defaultValue: { summary: "brand color at 20% opacity" },
|
||||
},
|
||||
},
|
||||
trackBorderRadius: {
|
||||
@@ -140,7 +140,7 @@ export const CustomStyles: Story = {
|
||||
control: "color",
|
||||
table: {
|
||||
category: "Progress Styling",
|
||||
defaultValue: { summary: "hsl(222.2 47.4% 11.2%)" },
|
||||
defaultValue: { summary: "brand color (#64748b)" },
|
||||
},
|
||||
},
|
||||
indicatorBorderRadius: {
|
||||
|
||||
@@ -59,8 +59,8 @@
|
||||
--------------------------------------------------------------------------- */
|
||||
--fb-survey-brand-color: #64748b;
|
||||
|
||||
--fb-accent-background-color: var(--slate-200);
|
||||
--fb-accent-background-color-selected: var(--slate-100);
|
||||
--fb-accent-background-color: var(--fb-survey-brand-color);
|
||||
--fb-accent-background-color-selected: color-mix(in srgb, var(--fb-survey-brand-color) 90%, white);
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Element Headline Tokens
|
||||
@@ -69,7 +69,7 @@
|
||||
--fb-element-headline-font-family: inherit;
|
||||
--fb-element-headline-font-weight: 600;
|
||||
--fb-element-headline-font-size: 1rem;
|
||||
--fb-element-headline-color: var(--input);
|
||||
--fb-element-headline-color: var(--fb-survey-brand-color);
|
||||
--fb-element-headline-opacity: 1;
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
@@ -79,7 +79,7 @@
|
||||
--fb-element-description-font-family: inherit;
|
||||
--fb-element-description-font-weight: 400;
|
||||
--fb-element-description-font-size: 0.875rem;
|
||||
--fb-element-description-color: var(--input);
|
||||
--fb-element-description-color: color-mix(in srgb, var(--fb-survey-brand-color) 70%, white);
|
||||
/* ---------------------------------------------------------------------------
|
||||
Element Upper Label Tokens
|
||||
Used for "Required" label and other upper label text.
|
||||
@@ -87,8 +87,8 @@
|
||||
--fb-element-upper-label-font-family: inherit;
|
||||
--fb-element-upper-label-font-weight: 400;
|
||||
--fb-element-upper-label-font-size: 0.75rem;
|
||||
--fb-element-upper-label-color: var(--muted-foreground);
|
||||
--fb-element-upper-label-opacity: 0.6;
|
||||
--fb-element-upper-label-color: color-mix(in srgb, var(--fb-survey-brand-color) 50%, white);
|
||||
--fb-element-upper-label-opacity: 1;
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Label Tokens
|
||||
@@ -125,7 +125,7 @@
|
||||
--fb-input-font-family: inherit;
|
||||
--fb-input-font-size: 0.875rem;
|
||||
--fb-input-font-weight: 400;
|
||||
--fb-input-color: var(--foreground);
|
||||
--fb-input-color: color-mix(in srgb, var(--fb-survey-brand-color) 10%, #0f172a);
|
||||
--fb-input-placeholder-color: var(--fb-input-color);
|
||||
--fb-input-placeholder-opacity: 0.5;
|
||||
--fb-input-width: 100%;
|
||||
@@ -139,9 +139,9 @@
|
||||
Used for the Progress component track and indicator.
|
||||
--------------------------------------------------------------------------- */
|
||||
--fb-progress-track-height: 0.5rem;
|
||||
--fb-progress-track-bg-color: hsl(222.2 47.4% 11.2% / 0.2);
|
||||
--fb-progress-track-bg-color: color-mix(in srgb, var(--fb-survey-brand-color) 20%, transparent);
|
||||
--fb-progress-track-border-radius: var(--radius);
|
||||
--fb-progress-indicator-bg-color: hsl(222.2 47.4% 11.2%);
|
||||
--fb-progress-indicator-bg-color: var(--fb-survey-brand-color);
|
||||
--fb-progress-indicator-border-radius: var(--radius);
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
|
||||
@@ -248,6 +248,8 @@ export const addCustomThemeToDom = ({ styling }: { styling: TProjectStyling | TS
|
||||
|
||||
if (styling.elementDescriptionFontSize !== undefined)
|
||||
appendCssVariable("element-description-font-size", formatDimension(styling.elementDescriptionFontSize));
|
||||
if (styling.elementDescriptionFontWeight !== undefined && styling.elementDescriptionFontWeight !== null)
|
||||
appendCssVariable("element-description-font-weight", `${styling.elementDescriptionFontWeight}`);
|
||||
appendCssVariable(
|
||||
"element-description-color",
|
||||
styling.elementDescriptionColor?.light ?? styling.questionColor?.light
|
||||
@@ -293,6 +295,7 @@ export const addCustomThemeToDom = ({ styling }: { styling: TProjectStyling | TS
|
||||
#fbjs .label-description,
|
||||
#fbjs .label-description * {
|
||||
font-size: var(--fb-element-description-font-size) !important;
|
||||
font-weight: var(--fb-element-description-font-weight) !important;
|
||||
color: var(--fb-element-description-color) !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ export const ZBaseStyling = z.object({
|
||||
elementHeadlineFontWeight: z.union([z.string(), z.number()]).nullish(),
|
||||
elementHeadlineColor: ZStylingColor.nullish(),
|
||||
elementDescriptionFontSize: z.union([z.number(), z.string()]).nullish(),
|
||||
elementDescriptionFontWeight: z.union([z.string(), z.number()]).nullish(),
|
||||
elementDescriptionColor: ZStylingColor.nullish(),
|
||||
elementUpperLabelFontSize: z.union([z.number(), z.string()]).nullish(),
|
||||
elementUpperLabelColor: ZStylingColor.nullish(),
|
||||
|
||||
Reference in New Issue
Block a user