From 9611051b22c4b21ecf9f44b30dbc6ef0334a297a Mon Sep 17 00:00:00 2001 From: vadim Date: Thu, 13 Mar 2025 09:30:17 +0100 Subject: [PATCH] chore: Update WDS outline buttons subtlety (#39696) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Subtler borders to align better with the proposed design for FC. | Before | After | |--------|--------| | ![before](https://github.com/user-attachments/assets/f445e4bc-89c3-4a21-90ec-a503423bfbee) | ![after](https://github.com/user-attachments/assets/bf9e5f53-ff52-481b-9a11-b1e6616fb9e7) | ## Automation /ok-to-test tags="@tag.Sanity" ### :mag: Cypress test results > [!TIP] > 🟒 🟒 🟒 All cypress tests have passed! πŸŽ‰ πŸŽ‰ πŸŽ‰ > Workflow run: > Commit: d01d374dbc71cb38996b122cffab44c2719efa31 > Cypress dashboard. > Tags: `@tag.Sanity` > Spec: >
Wed, 12 Mar 2025 16:33:43 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No ## Summary by CodeRabbit - **New Features** - Introduced additional, nuanced border color variants for both dark and light themes, providing refined color distinctions across accent, neutral, positive, negative, and warning elements. - **Style** - Updated outlined button styling to utilize the new subtle color variations, enhancing the overall visual polish and clarity of UI elements. - **Bug Fixes** - Adjusted expected output values for the `bgNeutralHover` color in test cases to reflect updated RGB values under various conditions. --- .../theming/src/color/src/DarkModeTheme.ts | 54 +++++++++++++++ .../theming/src/color/src/LightModeTheme.ts | 67 +++++++++++++++++-- .../theming/src/color/src/types.ts | 5 ++ .../src/color/tests/LightModeTheme.test.ts | 8 +-- .../components/Button/src/styles.module.css | 2 +- 5 files changed, 124 insertions(+), 12 deletions(-) diff --git a/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts b/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts index 844dfb005b..e14c1d94f3 100644 --- a/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts +++ b/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts @@ -110,15 +110,20 @@ export class DarkModeTheme implements ColorModeTheme { bd: this.bd.to("sRGB").toString(), bdAccent: this.bdAccent.to("sRGB").toString(), + bdAccentSubtle: this.bdAccentSubtle.to("sRGB").toString(), bdFocus: this.bdFocus.to("sRGB").toString(), bdNeutral: this.bdNeutral.to("sRGB").toString(), bdNeutralHover: this.bdNeutralHover.to("sRGB").toString(), + bdNeutralSubtle: this.bdNeutralSubtle.to("sRGB").toString(), bdPositive: this.bdPositive.to("sRGB").toString(), bdPositiveHover: this.bdPositiveHover.to("sRGB").toString(), + bdPositiveSubtle: this.bdPositiveSubtle.to("sRGB").toString(), bdNegative: this.bdNegative.to("sRGB").toString(), bdNegativeHover: this.bdNegativeHover.to("sRGB").toString(), + bdNegativeSubtle: this.bdNegativeSubtle.to("sRGB").toString(), bdWarning: this.bdWarning.to("sRGB").toString(), bdWarningHover: this.bdWarningHover.to("sRGB").toString(), + bdWarningSubtle: this.bdWarningSubtle.to("sRGB").toString(), bdOnAccent: this.bdOnAccent.to("sRGB").toString(), bdOnNeutral: this.bdOnNeutral.to("sRGB").toString(), @@ -980,6 +985,16 @@ export class DarkModeTheme implements ColorModeTheme { return color; } + private get bdAccentSubtle() { + // Slightly subtler version of accent border, used in outlined buttons + const color = this.bdAccent.clone(); + + color.oklch.l -= 0.02; + color.oklch.c -= 0.01; + + return color; + } + private get bdFocus() { // Keyboard focus outline const color = this.bdAccent.clone(); @@ -1031,6 +1046,15 @@ export class DarkModeTheme implements ColorModeTheme { return color; } + private get bdNeutralSubtle() { + // Slightly subtler version of neutral border, used in outlined buttons + const color = this.bdNeutral.clone(); + + color.oklch.l -= 0.07; + + return color; + } + private get bdPositive() { const color = this.bgPositive.clone(); @@ -1054,6 +1078,16 @@ export class DarkModeTheme implements ColorModeTheme { return color; } + private get bdPositiveSubtle() { + // Slightly subtler version of positive border, used in outlined buttons + const color = this.bdPositive.clone(); + + color.oklch.l -= 0.05; + color.oklch.c -= 0.01; + + return color; + } + private get bdNegative() { // Negative (red) border. Produced out of bgNegative. Additional compensations are applied if seed is within red range. const color = this.bgNegative.clone(); @@ -1099,6 +1133,16 @@ export class DarkModeTheme implements ColorModeTheme { return color; } + private get bdNegativeSubtle() { + // Slightly subtler version of negative border, used in outlined buttons + const color = this.bdNegative.clone(); + + color.oklch.l -= 0.03; + color.oklch.c -= 0.01; + + return color; + } + private get bdWarning() { const color = this.bgWarning.clone(); @@ -1149,6 +1193,16 @@ export class DarkModeTheme implements ColorModeTheme { return color; } + private get bdWarningSubtle() { + // Slightly subtler version of warning border, used in outlined buttons + const color = this.bdWarning.clone(); + + color.oklch.l -= 0.03; + color.oklch.c -= 0.02; + + return color; + } + private get bdOnAccent() { // Separator on bgAccent, low contrast to not pull attention from actual separated content elements const color = this.bgAccent.clone(); diff --git a/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts b/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts index 4322adead1..de7882268e 100644 --- a/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts +++ b/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts @@ -107,15 +107,20 @@ export class LightModeTheme implements ColorModeTheme { bd: this.bd.to("sRGB").toString(), bdAccent: this.bdAccent.to("sRGB").toString(), + bdAccentSubtle: this.bdAccentSubtle.to("sRGB").toString(), bdFocus: this.bdFocus.to("sRGB").toString(), bdNeutral: this.bdNeutral.to("sRGB").toString(), bdNeutralHover: this.bdNeutralHover.to("sRGB").toString(), + bdNeutralSubtle: this.bdNeutralSubtle.to("sRGB").toString(), bdPositive: this.bdPositive.to("sRGB").toString(), bdPositiveHover: this.bdPositiveHover.to("sRGB").toString(), + bdPositiveSubtle: this.bdPositiveSubtle.to("sRGB").toString(), bdNegative: this.bdNegative.to("sRGB").toString(), bdNegativeHover: this.bdNegativeHover.to("sRGB").toString(), + bdNegativeSubtle: this.bdNegativeSubtle.to("sRGB").toString(), bdWarning: this.bdWarning.to("sRGB").toString(), bdWarningHover: this.bdWarningHover.to("sRGB").toString(), + bdWarningSubtle: this.bdWarningSubtle.to("sRGB").toString(), bdOnAccent: this.bdOnAccent.to("sRGB").toString(), bdOnNeutral: this.bdOnNeutral.to("sRGB").toString(), @@ -349,7 +354,7 @@ export class LightModeTheme implements ColorModeTheme { // Simplified and adjusted version of bgAccentHover algorithm (bgNeutral has very low or no chroma) if (this.bgNeutral.oklch.l < 0.06) { - color.oklch.l += 0.3; + color.oklch.l += 0.5; } if (this.bgNeutral.oklch.l > 0.06 && this.bgNeutral.oklch.l < 0.14) { @@ -744,7 +749,7 @@ export class LightModeTheme implements ColorModeTheme { // For dark content on light background APCA contrast is positive. 60 is β€œThe minimum level recommended for content text that is not body, column, or block text. In other words, text you want people to read.” Failure to reach this contrast level is most likely due to high lightness. Lightness and chroma are set to ones that reach the threshold universally regardless of hue. if (this.bg.contrastAPCA(this.seedColor) <= 60) { - color.oklch.l = 0.45; + color.oklch.l = 0.35; if (this.seedIsAchromatic) { color.oklch.c = 0; @@ -962,24 +967,34 @@ export class LightModeTheme implements ColorModeTheme { private get bdAccent() { // Accent border color. Lighter and less saturated than accent to put focus on the text label and create nice-looking harmony. - const color = this.seedColor.clone(); + const color = this.fgAccent.clone(); // For dark content on light background APCA contrast is positive. 15 is β€œThe absolute minimum for any non-text that needs to be discernible and differentiable, but does not apply to semantic non-text such as icons”. In practice, thin borders are perceptually too subtle when using this as a threshould. 25 is used as the required minimum instead. Failure to reach this contrast level is most likely due to high lightness. Lightness and chroma are set to ones that reach the threshold universally regardless of hue. - if (this.bg.contrastAPCA(this.seedColor) <= 25) { - if (this.seedIsAchromatic) { + if (this.bg.contrastAPCA(color) <= 25) { + // If seed is achromatic make sure we don't produce parasitic coloring. Our standard achromatic cut-off is set too high for the very light seeds, so using chroma value checks instead. + if (color.oklch.c <= 0.04) { color.oklch.l = 0.55; color.oklch.c = 0; } - if (!this.seedIsAchromatic) { + if (color.oklch.c > 0.04) { color.oklch.l = 0.55; - color.oklch.c = 0.15; + color.oklch.c = 0.08; } } return color; } + private get bdAccentSubtle() { + // Slightly subtler version of accent border, used in outlined buttons + const color = this.bdAccent.clone(); + + color.oklch.l += 0.35; + + return color; + } + private get bdFocus() { // Keyboard focus outline const color = this.bgAccent.clone(); @@ -1034,6 +1049,15 @@ export class LightModeTheme implements ColorModeTheme { return color; } + private get bdNeutralSubtle() { + // Slightly subtler version of neutral border, used in outlined buttons + const color = this.bdNeutral.clone(); + + color.oklch.l += 0.35; + + return color; + } + private get bdPositive() { // Positive (green) border. Additional compensations are applied if seed is withing green range. const color = this.bgPositive.clone(); @@ -1070,6 +1094,16 @@ export class LightModeTheme implements ColorModeTheme { return color; } + private get bdPositiveSubtle() { + // Slightly subtler version of negative border, used in outlined buttons + const color = this.bdPositive.clone(); + + color.oklch.l += 0.07; + color.oklch.c -= 0.09; + + return color; + } + private get bdNegative() { // Negative (red) border. Produced out of bgNegative. Additional compensations are applied if seed is within red range. const color = this.bgNegative.clone(); @@ -1106,6 +1140,16 @@ export class LightModeTheme implements ColorModeTheme { return color; } + private get bdNegativeSubtle() { + // Slightly subtler version of negative border, used in outlined buttons + const color = this.bdNegative.clone(); + + color.oklch.l += 0.1; + color.oklch.h -= 0.05; + + return color; + } + private get bdWarning() { // Warning (yellow) border. Produced out of bgNegative. Additional compensations are applied if seed is within yellow range. const color = this.bgWarning.clone(); @@ -1142,6 +1186,15 @@ export class LightModeTheme implements ColorModeTheme { return color; } + private get bdWarningSubtle() { + // Slightly subtler version of warning border, used in outlined buttons + const color = this.bdWarning.clone(); + + color.oklch.l += 0.05; + color.oklch.c -= 0.05; + + return color; + } private get bdOnAccent() { // Separator on bgAccent, low contrast to not pull attention from actual separated content elements const color = this.bgAccent.clone(); diff --git a/app/client/packages/design-system/theming/src/color/src/types.ts b/app/client/packages/design-system/theming/src/color/src/types.ts index 586aa9496f..84bbfe3053 100644 --- a/app/client/packages/design-system/theming/src/color/src/types.ts +++ b/app/client/packages/design-system/theming/src/color/src/types.ts @@ -57,15 +57,20 @@ export interface ColorModeTheme { fgOnWarning: string; // bd bdAccent: string; + bdAccentSubtle: string; bdFocus: string; bdNeutral: string; bdNeutralHover: string; + bdNeutralSubtle: string; bdPositive: string; bdPositiveHover: string; + bdPositiveSubtle: string; bdNegative: string; bdNegativeHover: string; + bdNegativeSubtle: string; bdWarning: string; bdWarningHover: string; + bdWarningSubtle: string; // bd on bg* bdOnAccent: string; bdOnNeutral: string; diff --git a/app/client/packages/design-system/theming/src/color/tests/LightModeTheme.test.ts b/app/client/packages/design-system/theming/src/color/tests/LightModeTheme.test.ts index 6e9473c460..b766060d06 100644 --- a/app/client/packages/design-system/theming/src/color/tests/LightModeTheme.test.ts +++ b/app/client/packages/design-system/theming/src/color/tests/LightModeTheme.test.ts @@ -274,7 +274,7 @@ describe("bgNeutralHover color", () => { "oklch(0.05 0.03 170)", ).getColors(); - expect(bgNeutralHover).toEqual("rgb(22.901% 22.901% 22.901%)"); + expect(bgNeutralHover).toEqual("rgb(44.47% 44.47% 44.47%)"); }); it("should return correct color when lightness is between 0.06 and 0.14", () => { @@ -298,7 +298,7 @@ describe("bgNeutralHover color", () => { "oklch(0.35 0.03 170)", ).getColors(); - expect(bgNeutralHover).toEqual("rgb(17.924% 17.924% 17.924%)"); + expect(bgNeutralHover).toEqual("rgb(38.857% 38.857% 38.857%)"); }); it("should return correct color when lightness is between 0.7 and 0.955", () => { @@ -306,7 +306,7 @@ describe("bgNeutralHover color", () => { "oklch(0.75 0.03 170)", ).getColors(); - expect(bgNeutralHover).toEqual("rgb(4.3484% 4.3484% 4.3484%)"); + expect(bgNeutralHover).toEqual("rgb(22.901% 22.901% 22.901%)"); }); it("should return correct color when lightness > or equal to 0.955", () => { @@ -314,7 +314,7 @@ describe("bgNeutralHover color", () => { "oklch(0.96 0.03 170)", ).getColors(); - expect(bgNeutralHover).toEqual("rgb(4.3484% 4.3484% 4.3484%)"); + expect(bgNeutralHover).toEqual("rgb(22.901% 22.901% 22.901%)"); }); }); diff --git a/app/client/packages/design-system/widgets/src/components/Button/src/styles.module.css b/app/client/packages/design-system/widgets/src/components/Button/src/styles.module.css index 2fcae3bec8..57b6e43e2f 100644 --- a/app/client/packages/design-system/widgets/src/components/Button/src/styles.module.css +++ b/app/client/packages/design-system/widgets/src/components/Button/src/styles.module.css @@ -30,7 +30,7 @@ background-color: transparent; color: var(--color-fg-$(color)); /** We use !important here to override the disabled outline styles in the main app. */ - outline: var(--border-width-1) solid var(--color-bd-$(color)) !important; + outline: var(--border-width-1) solid var(--color-bd-$(color)-subtle) !important; outline-offset: calc(-1 * var(--border-width-1)) !important; &[data-hovered]:not([data-loading]) {