From b661d8878cd9846930b6b0e4845be31ae555a218 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Mon, 4 May 2026 17:03:30 -0400 Subject: [PATCH 1/6] chore(LXStudio): update theme --- .../gamut-styles/src/remoteAssets/fonts.ts | 5 + .../__snapshots__/theme.test.ts.snap | 529 +++++++++++++++++- .../src/themes/__tests__/theme.test.ts | 2 + packages/gamut-styles/src/themes/lxStudio.ts | 22 +- packages/gamut-styles/src/variables/colors.ts | 3 +- .../gamut-styles/src/variables/typography.ts | 7 +- packages/gamut/src/Menu/elements.tsx | 4 +- packages/gamut/src/Tabs/TabButton.tsx | 2 +- .../components/Headers/ComponentHeader.tsx | 2 +- .../lib/Foundations/Theme/LXStudioTheme.mdx | 8 +- .../Lists & Tables/List/List.stories.tsx | 4 +- .../List/ListCol/ListCol.stories.tsx | 4 +- 12 files changed, 570 insertions(+), 22 deletions(-) diff --git a/packages/gamut-styles/src/remoteAssets/fonts.ts b/packages/gamut-styles/src/remoteAssets/fonts.ts index 4e28b89471a..c9e584dd908 100644 --- a/packages/gamut-styles/src/remoteAssets/fonts.ts +++ b/packages/gamut-styles/src/remoteAssets/fonts.ts @@ -95,6 +95,11 @@ export const percipio: readonly FontConfig[] = [ }, ]; +/** + * LX Studio loads Hanken as fallback while font stacks prefer Skillsoft faces. + * When Skillsoft Sans / Skillsoft Text files are hosted under FONT_ASSET_PATH, + * add @font-face entries here (400, 500, 700, italics as needed) and remove Hanken. + */ export const lxStudio: readonly FontConfig[] = [ { filePath: `${FONT_ASSET_PATH}/hanken-grotesk-regular`, diff --git a/packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap b/packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap index d354180425d..518b7631f72 100644 --- a/packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap +++ b/packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`themes admin - theme shape 1`] = ` { @@ -1032,6 +1032,533 @@ monospace", } `; +exports[`themes lxStudio - theme shape 1`] = ` +{ + "_getColorValue": [Function], + "_tokens": { + "colors": { + "beige": "#FFF0E5", + "beige-100": "#FFF0E5", + "black": "#000000", + "blue": "#1557FF", + "blue-0": "#F5FCFF", + "blue-100": "#D3F2FF", + "blue-300": "#66C4FF", + "blue-400": "#3388FF", + "blue-500": "#1557FF", + "blue-800": "#1D2340", + "gray-100": "#F5F5F5", + "gray-200": "#EEEEEE", + "gray-300": "#E0E0E0", + "gray-600": "#9E9E9E", + "gray-800": "#616161", + "gray-900": "#424242", + "green": "#008A27", + "green-0": "#F5FFE3", + "green-100": "#EAFDC6", + "green-400": "#AEE938", + "green-700": "#008A27", + "green-900": "#151C07", + "hyper": "#3A10E5", + "hyper-400": "#5533FF", + "hyper-500": "#3A10E5", + "lightBlue": "#66C4FF", + "lightGreen": "#AEE938", + "lxStudioBgPrimary": "#FAFBFC", + "lxStudioSuccess": "#06844F", + "navy": "#10162F", + "navy-100": "rgba(16,22,47,0.04)", + "navy-200": "rgba(16,22,47,0.12)", + "navy-300": "rgba(16,22,47,0.28)", + "navy-400": "rgba(16,22,47,0.47)", + "navy-500": "rgba(16,22,47,0.63)", + "navy-600": "rgba(16,22,47,0.75)", + "navy-700": "rgba(16,22,47,0.86)", + "navy-800": "#10162F", + "navy-900": "#0A0D1C", + "orange": "#FF8C00", + "orange-100": "#FFE8CC", + "orange-500": "#FF8C00", + "paleBlue": "#F5FCFF", + "paleGreen": "#F5FFE3", + "palePink": "#FFF5FF", + "paleRed": "#DC5879", + "paleYellow": "#FFFAE5", + "pink": "#F966FF", + "pink-0": "#FFF5FF", + "pink-400": "#F966FF", + "red": "#E91C11", + "red-0": "#FBF1F0", + "red-300": "#E85D7F", + "red-400": "#DC5879", + "red-500": "#E91C11", + "red-600": "#BE1809", + "red-900": "#280503", + "sapphire": "#1C50BB", + "white": "#ffffff", + "white-100": "rgba(255,255,255,0.04)", + "white-200": "rgba(255,255,255,0.09)", + "white-300": "rgba(255,255,255,0.2)", + "white-400": "rgba(255,255,255,0.35)", + "white-500": "rgba(255,255,255,0.5)", + "white-600": "rgba(255,255,255,0.65)", + "white-700": "rgba(255,255,255,0.8)", + "yellow": "#FFD300", + "yellow-0": "#FFFAE5", + "yellow-400": "#CCA900", + "yellow-500": "#FFD300", + "yellow-900": "#211B00", + }, + "elements": { + "headerHeight": { + "base": "4rem", + "md": "5rem", + }, + "headerZ": 15, + }, + "modes": { + "dark": { + "background": "#10162F", + "background-contrast": "#000000", + "background-current": "#10162F", + "background-disabled": "rgba(255,255,255,0.09)", + "background-error": "#280503", + "background-hover": "rgba(255,255,255,0.09)", + "background-primary": "#0A0D1C", + "background-selected": "rgba(255,255,255,0.04)", + "background-success": "#151C07", + "background-warning": "#211B00", + "border-disabled": "rgba(255,255,255,0.5)", + "border-primary": "#ffffff", + "border-secondary": "rgba(255,255,255,0.65)", + "border-tertiary": "rgba(255,255,255,0.2)", + "danger": "#E85D7F", + "danger-hover": "#DC5879", + "feedback-error": "#E85D7F", + "feedback-success": "#AEE938", + "feedback-warning": "#FFFAE5", + "interface": "#FFD300", + "interface-hover": "#CCA900", + "primary": "#FFD300", + "primary-hover": "#CCA900", + "primary-inverse": "#3A10E5", + "secondary": "#ffffff", + "secondary-hover": "rgba(255,255,255,0.8)", + "shadow-primary": "#ffffff", + "shadow-secondary": "rgba(255,255,255,0.65)", + "text": "#ffffff", + "text-accent": "#FFF0E5", + "text-disabled": "rgba(255,255,255,0.5)", + "text-secondary": "rgba(255,255,255,0.65)", + }, + "light": { + "background": "#ffffff", + "background-contrast": "#ffffff", + "background-current": "#ffffff", + "background-disabled": "rgba(16,22,47,0.12)", + "background-error": "#FBF1F0", + "background-hover": "rgba(16,22,47,0.12)", + "background-primary": "#FAFBFC", + "background-selected": "rgba(16,22,47,0.04)", + "background-success": "#F5FFE3", + "background-warning": "#FFFAE5", + "border-disabled": "rgba(16,22,47,0.28)", + "border-primary": "rgba(16,22,47,0.47)", + "border-secondary": "rgba(16,22,47,0.75)", + "border-tertiary": "#10162F", + "danger": "#E91C11", + "danger-hover": "#BE1809", + "feedback-error": "#BE1809", + "feedback-success": "#06844F", + "feedback-warning": "#FFD300", + "interface": "#3A10E5", + "interface-hover": "#5533FF", + "primary": "#1C50BB", + "primary-hover": "#10162F", + "primary-inverse": "#FFD300", + "secondary": "#10162F", + "secondary-hover": "rgba(16,22,47,0.86)", + "shadow-primary": "rgba(16,22,47,0.12)", + "shadow-secondary": "rgba(16,22,47,0.75)", + "text": "#10162F", + "text-accent": "#0A0D1C", + "text-disabled": "rgba(16,22,47,0.63)", + "text-secondary": "rgba(16,22,47,0.75)", + }, + }, + }, + "_variables": { + "mode": { + "--color-background": "var(--color-white)", + "--color-background-contrast": "var(--color-white)", + "--color-background-current": "var(--color-white)", + "--color-background-disabled": "var(--color-navy-200)", + "--color-background-error": "var(--color-red-0)", + "--color-background-hover": "var(--color-navy-200)", + "--color-background-primary": "var(--color-lxStudioBgPrimary)", + "--color-background-selected": "var(--color-navy-100)", + "--color-background-success": "var(--color-green-0)", + "--color-background-warning": "var(--color-yellow-0)", + "--color-border-disabled": "var(--color-navy-300)", + "--color-border-primary": "var(--color-navy-400)", + "--color-border-secondary": "var(--color-navy-600)", + "--color-border-tertiary": "var(--color-navy-800)", + "--color-danger": "var(--color-red-500)", + "--color-danger-hover": "var(--color-red-600)", + "--color-feedback-error": "var(--color-red-600)", + "--color-feedback-success": "var(--color-lxStudioSuccess)", + "--color-feedback-warning": "var(--color-yellow)", + "--color-interface": "var(--color-hyper-500)", + "--color-interface-hover": "var(--color-hyper-400)", + "--color-primary": "var(--color-sapphire)", + "--color-primary-hover": "var(--color-navy-800)", + "--color-primary-inverse": "var(--color-yellow-500)", + "--color-secondary": "var(--color-navy-800)", + "--color-secondary-hover": "var(--color-navy-700)", + "--color-shadow-primary": "var(--color-navy-200)", + "--color-shadow-secondary": "var(--color-navy-600)", + "--color-text": "var(--color-navy-800)", + "--color-text-accent": "var(--color-navy-900)", + "--color-text-disabled": "var(--color-navy-500)", + "--color-text-secondary": "var(--color-navy-600)", + }, + "root": { + "--color-beige": "#FFF0E5", + "--color-beige-100": "#FFF0E5", + "--color-black": "#000000", + "--color-blue": "#1557FF", + "--color-blue-0": "#F5FCFF", + "--color-blue-100": "#D3F2FF", + "--color-blue-300": "#66C4FF", + "--color-blue-400": "#3388FF", + "--color-blue-500": "#1557FF", + "--color-blue-800": "#1D2340", + "--color-gray-100": "#F5F5F5", + "--color-gray-200": "#EEEEEE", + "--color-gray-300": "#E0E0E0", + "--color-gray-600": "#9E9E9E", + "--color-gray-800": "#616161", + "--color-gray-900": "#424242", + "--color-green": "#008A27", + "--color-green-0": "#F5FFE3", + "--color-green-100": "#EAFDC6", + "--color-green-400": "#AEE938", + "--color-green-700": "#008A27", + "--color-green-900": "#151C07", + "--color-hyper": "#3A10E5", + "--color-hyper-400": "#5533FF", + "--color-hyper-500": "#3A10E5", + "--color-lightBlue": "#66C4FF", + "--color-lightGreen": "#AEE938", + "--color-lxStudioBgPrimary": "#FAFBFC", + "--color-lxStudioSuccess": "#06844F", + "--color-navy": "#10162F", + "--color-navy-100": "rgba(16,22,47,0.04)", + "--color-navy-200": "rgba(16,22,47,0.12)", + "--color-navy-300": "rgba(16,22,47,0.28)", + "--color-navy-400": "rgba(16,22,47,0.47)", + "--color-navy-500": "rgba(16,22,47,0.63)", + "--color-navy-600": "rgba(16,22,47,0.75)", + "--color-navy-700": "rgba(16,22,47,0.86)", + "--color-navy-800": "#10162F", + "--color-navy-900": "#0A0D1C", + "--color-orange": "#FF8C00", + "--color-orange-100": "#FFE8CC", + "--color-orange-500": "#FF8C00", + "--color-paleBlue": "#F5FCFF", + "--color-paleGreen": "#F5FFE3", + "--color-palePink": "#FFF5FF", + "--color-paleRed": "#DC5879", + "--color-paleYellow": "#FFFAE5", + "--color-pink": "#F966FF", + "--color-pink-0": "#FFF5FF", + "--color-pink-400": "#F966FF", + "--color-red": "#E91C11", + "--color-red-0": "#FBF1F0", + "--color-red-300": "#E85D7F", + "--color-red-400": "#DC5879", + "--color-red-500": "#E91C11", + "--color-red-600": "#BE1809", + "--color-red-900": "#280503", + "--color-sapphire": "#1C50BB", + "--color-white": "#ffffff", + "--color-white-100": "rgba(255,255,255,0.04)", + "--color-white-200": "rgba(255,255,255,0.09)", + "--color-white-300": "rgba(255,255,255,0.2)", + "--color-white-400": "rgba(255,255,255,0.35)", + "--color-white-500": "rgba(255,255,255,0.5)", + "--color-white-600": "rgba(255,255,255,0.65)", + "--color-white-700": "rgba(255,255,255,0.8)", + "--color-yellow": "#FFD300", + "--color-yellow-0": "#FFFAE5", + "--color-yellow-400": "#CCA900", + "--color-yellow-500": "#FFD300", + "--color-yellow-900": "#211B00", + "--elements-headerHeight": "4rem", + "--elements-headerZ": 15, + "@media only screen and (min-width: 1024px)": { + "--elements-headerHeight": "5rem", + }, + }, + }, + "borderRadii": { + "full": "999px", + "lg": "12px", + "md": "8px", + "none": "0px", + "sm": "4px", + "xl": "16px", + }, + "borders": { + "1": "1px solid var(--color-border-primary)", + "2": "2px solid var(--color-border-primary)", + }, + "breakpoints": { + "c_base": "@container (min-width: 1px)", + "c_lg": "@container (min-width: 1200px)", + "c_md": "@container (min-width: 1024px)", + "c_sm": "@container (min-width: 768px)", + "c_xl": "@container (min-width: 1440px)", + "c_xs": "@container (min-width: 480px)", + "lg": "@media only screen and (min-width: 1200px)", + "md": "@media only screen and (min-width: 1024px)", + "sm": "@media only screen and (min-width: 768px)", + "xl": "@media only screen and (min-width: 1440px)", + "xs": "@media only screen and (min-width: 480px)", + }, + "colors": { + "background": "var(--color-background)", + "background-contrast": "var(--color-background-contrast)", + "background-current": "var(--color-background-current)", + "background-disabled": "var(--color-background-disabled)", + "background-error": "var(--color-background-error)", + "background-hover": "var(--color-background-hover)", + "background-primary": "var(--color-background-primary)", + "background-selected": "var(--color-background-selected)", + "background-success": "var(--color-background-success)", + "background-warning": "var(--color-background-warning)", + "beige": "var(--color-beige)", + "beige-100": "var(--color-beige-100)", + "black": "var(--color-black)", + "blue": "var(--color-blue)", + "blue-0": "var(--color-blue-0)", + "blue-100": "var(--color-blue-100)", + "blue-300": "var(--color-blue-300)", + "blue-400": "var(--color-blue-400)", + "blue-500": "var(--color-blue-500)", + "blue-800": "var(--color-blue-800)", + "border-disabled": "var(--color-border-disabled)", + "border-primary": "var(--color-border-primary)", + "border-secondary": "var(--color-border-secondary)", + "border-tertiary": "var(--color-border-tertiary)", + "danger": "var(--color-danger)", + "danger-hover": "var(--color-danger-hover)", + "feedback-error": "var(--color-feedback-error)", + "feedback-success": "var(--color-feedback-success)", + "feedback-warning": "var(--color-feedback-warning)", + "gray-100": "var(--color-gray-100)", + "gray-200": "var(--color-gray-200)", + "gray-300": "var(--color-gray-300)", + "gray-600": "var(--color-gray-600)", + "gray-800": "var(--color-gray-800)", + "gray-900": "var(--color-gray-900)", + "green": "var(--color-green)", + "green-0": "var(--color-green-0)", + "green-100": "var(--color-green-100)", + "green-400": "var(--color-green-400)", + "green-700": "var(--color-green-700)", + "green-900": "var(--color-green-900)", + "hyper": "var(--color-hyper)", + "hyper-400": "var(--color-hyper-400)", + "hyper-500": "var(--color-hyper-500)", + "interface": "var(--color-interface)", + "interface-hover": "var(--color-interface-hover)", + "lightBlue": "var(--color-lightBlue)", + "lightGreen": "var(--color-lightGreen)", + "lxStudioBgPrimary": "var(--color-lxStudioBgPrimary)", + "lxStudioSuccess": "var(--color-lxStudioSuccess)", + "navy": "var(--color-navy)", + "navy-100": "var(--color-navy-100)", + "navy-200": "var(--color-navy-200)", + "navy-300": "var(--color-navy-300)", + "navy-400": "var(--color-navy-400)", + "navy-500": "var(--color-navy-500)", + "navy-600": "var(--color-navy-600)", + "navy-700": "var(--color-navy-700)", + "navy-800": "var(--color-navy-800)", + "navy-900": "var(--color-navy-900)", + "orange": "var(--color-orange)", + "orange-100": "var(--color-orange-100)", + "orange-500": "var(--color-orange-500)", + "paleBlue": "var(--color-paleBlue)", + "paleGreen": "var(--color-paleGreen)", + "palePink": "var(--color-palePink)", + "paleRed": "var(--color-paleRed)", + "paleYellow": "var(--color-paleYellow)", + "pink": "var(--color-pink)", + "pink-0": "var(--color-pink-0)", + "pink-400": "var(--color-pink-400)", + "primary": "var(--color-primary)", + "primary-hover": "var(--color-primary-hover)", + "primary-inverse": "var(--color-primary-inverse)", + "red": "var(--color-red)", + "red-0": "var(--color-red-0)", + "red-300": "var(--color-red-300)", + "red-400": "var(--color-red-400)", + "red-500": "var(--color-red-500)", + "red-600": "var(--color-red-600)", + "red-900": "var(--color-red-900)", + "sapphire": "var(--color-sapphire)", + "secondary": "var(--color-secondary)", + "secondary-hover": "var(--color-secondary-hover)", + "shadow-primary": "var(--color-shadow-primary)", + "shadow-secondary": "var(--color-shadow-secondary)", + "text": "var(--color-text)", + "text-accent": "var(--color-text-accent)", + "text-disabled": "var(--color-text-disabled)", + "text-secondary": "var(--color-text-secondary)", + "white": "var(--color-white)", + "white-100": "var(--color-white-100)", + "white-200": "var(--color-white-200)", + "white-300": "var(--color-white-300)", + "white-400": "var(--color-white-400)", + "white-500": "var(--color-white-500)", + "white-600": "var(--color-white-600)", + "white-700": "var(--color-white-700)", + "yellow": "var(--color-yellow)", + "yellow-0": "var(--color-yellow-0)", + "yellow-400": "var(--color-yellow-400)", + "yellow-500": "var(--color-yellow-500)", + "yellow-900": "var(--color-yellow-900)", + }, + "elements": { + "headerHeight": "var(--elements-headerHeight)", + "headerZ": "var(--elements-headerZ)", + }, + "fontFamily": { + "accent": ""Skillsoft Sans", "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", +"Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", +sans-serif", + "base": ""Skillsoft Text", "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", +"Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", +sans-serif", + "monospace": "Monaco, Menlo, "Ubuntu Mono", "Droid Sans Mono", Consolas, +monospace", + "system": "-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", +"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif", + }, + "fontSize": { + "14": "0.875rem", + "16": "1rem", + "18": "1.125rem", + "20": "1.25rem", + "22": "1.375rem", + "26": "1.625rem", + "34": "2.125rem", + "44": "2.75rem", + "64": "4rem", + }, + "fontWeight": { + "400": 400, + "500": 500, + "700": 700, + "base": 400, + "bold": 500, + "title": 500, + }, + "lineHeight": { + "base": 1.5, + "spacedTitle": 1.3, + "title": 1.2, + }, + "mode": "light", + "modes": { + "dark": { + "background": "navy-800", + "background-contrast": "black", + "background-current": "navy-800", + "background-disabled": "white-200", + "background-error": "red-900", + "background-hover": "white-200", + "background-primary": "navy-900", + "background-selected": "white-100", + "background-success": "green-900", + "background-warning": "yellow-900", + "border-disabled": "white-500", + "border-primary": "white", + "border-secondary": "white-600", + "border-tertiary": "white-300", + "danger": "red-300", + "danger-hover": "red-400", + "feedback-error": "red-300", + "feedback-success": "green-400", + "feedback-warning": "yellow-0", + "interface": "yellow-500", + "interface-hover": "yellow-400", + "primary": "yellow-500", + "primary-hover": "yellow-400", + "primary-inverse": "hyper-500", + "secondary": "white", + "secondary-hover": "white-700", + "shadow-primary": "white", + "shadow-secondary": "white-600", + "text": "white", + "text-accent": "beige", + "text-disabled": "white-500", + "text-secondary": "white-600", + }, + "light": { + "background": "white", + "background-contrast": "white", + "background-current": "white", + "background-disabled": "navy-200", + "background-error": "red-0", + "background-hover": "navy-200", + "background-primary": "lxStudioBgPrimary", + "background-selected": "navy-100", + "background-success": "green-0", + "background-warning": "yellow-0", + "border-disabled": "navy-300", + "border-primary": "navy-400", + "border-secondary": "navy-600", + "border-tertiary": "navy-800", + "danger": "red-500", + "danger-hover": "red-600", + "feedback-error": "red-600", + "feedback-success": "lxStudioSuccess", + "feedback-warning": "yellow", + "interface": "hyper-500", + "interface-hover": "hyper-400", + "primary": "sapphire", + "primary-hover": "navy-800", + "primary-inverse": "yellow-500", + "secondary": "navy-800", + "secondary-hover": "navy-700", + "shadow-primary": "navy-200", + "shadow-secondary": "navy-600", + "text": "navy-800", + "text-accent": "navy-900", + "text-disabled": "navy-500", + "text-secondary": "navy-600", + }, + }, + "name": "lxStudio", + "spacing": { + "0": 0, + "12": "0.75rem", + "16": "1rem", + "24": "1.5rem", + "32": "2rem", + "4": "0.25rem", + "40": "2.5rem", + "48": "3rem", + "64": "4rem", + "8": "0.5rem", + "96": "6rem", + }, +} +`; + exports[`themes platform - theme shape 1`] = ` { "_getColorValue": [Function], diff --git a/packages/gamut-styles/src/themes/__tests__/theme.test.ts b/packages/gamut-styles/src/themes/__tests__/theme.test.ts index 8ad761e01b8..00a2ff34686 100644 --- a/packages/gamut-styles/src/themes/__tests__/theme.test.ts +++ b/packages/gamut-styles/src/themes/__tests__/theme.test.ts @@ -1,5 +1,6 @@ import { adminTheme } from '../admin'; import { coreTheme } from '../core'; +import { lxStudioTheme } from '../lxStudio'; import { platformTheme } from '../platform'; describe('themes', () => { @@ -7,5 +8,6 @@ describe('themes', () => { ['core', coreTheme], ['platform', platformTheme], ['admin', adminTheme], + ['lxStudio', lxStudioTheme], ])(`%s - theme shape`, (_, theme) => expect(theme).toMatchSnapshot()); }); diff --git a/packages/gamut-styles/src/themes/lxStudio.ts b/packages/gamut-styles/src/themes/lxStudio.ts index b37c060884c..526ed584f14 100644 --- a/packages/gamut-styles/src/themes/lxStudio.ts +++ b/packages/gamut-styles/src/themes/lxStudio.ts @@ -1,9 +1,11 @@ import { createTheme } from '@codecademy/variance'; import { - fontLxStudio, + fontLxStudioAccent, + fontLxStudioBase, fontMonospace, fontSystem, + fontWeight as coreFontWeight, lxStudioPalette, } from '../variables'; import { coreTheme } from './core'; @@ -14,12 +16,19 @@ import { coreTheme } from './core'; */ const lxStudioFontFamily = { - accent: fontLxStudio, - base: fontLxStudio, + accent: fontLxStudioAccent, + base: fontLxStudioBase, monospace: fontMonospace, system: fontSystem, } as const; +const lxStudioFontWeight = { + ...coreFontWeight, + title: 500, + bold: 500, + 500: 500, +} as const; + export const lxStudioBorderRadii = { none: '0px', sm: '4px', @@ -33,6 +42,7 @@ export const lxStudioTheme = createTheme({ ...coreTheme, borderRadii: lxStudioBorderRadii, fontFamily: lxStudioFontFamily, + fontWeight: lxStudioFontWeight, }) .addColors(lxStudioPalette) .addColorModes('light', { @@ -48,8 +58,8 @@ export const lxStudioTheme = createTheme({ primary: 'navy-200', }, primary: { - _: 'lxStudioPurple', - hover: 'lxStudioPurpleHover', + _: 'sapphire', + hover: 'navy-800', }, interface: { _: 'hyper-500', @@ -67,4 +77,4 @@ export const lxStudioTheme = createTheme({ export type LxStudioThemeShape = typeof lxStudioTheme; -export interface LxStudioTheme extends LxStudioThemeShape {} +export type LxStudioTheme = LxStudioThemeShape; diff --git a/packages/gamut-styles/src/variables/colors.ts b/packages/gamut-styles/src/variables/colors.ts index 51196adbb6f..15eace4c506 100644 --- a/packages/gamut-styles/src/variables/colors.ts +++ b/packages/gamut-styles/src/variables/colors.ts @@ -167,8 +167,7 @@ export const platformPalette = { export const lxStudioColors = { lxStudioSuccess: '#06844F', lxStudioBgPrimary: '#FAFBFC', - lxStudioPurple: '#5628FE', - lxStudioPurpleHover: '#7955FC', + sapphire: '#1C50BB', } as const; export const lxStudioPalette = { diff --git a/packages/gamut-styles/src/variables/typography.ts b/packages/gamut-styles/src/variables/typography.ts index e096c1b342a..d48bba52684 100644 --- a/packages/gamut-styles/src/variables/typography.ts +++ b/packages/gamut-styles/src/variables/typography.ts @@ -14,7 +14,12 @@ monospace`; export const fontSystem = `-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`; -export const fontLxStudio = `"Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", +/** Skillsoft rebrand: primary face + Hanken until Skillsoft files are hosted on the CDN. */ +export const fontLxStudioAccent = `"Skillsoft Sans", "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", +"Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", +sans-serif`; + +export const fontLxStudioBase = `"Skillsoft Text", "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`; diff --git a/packages/gamut/src/Menu/elements.tsx b/packages/gamut/src/Menu/elements.tsx index 6b2773b566a..f8eea4af20d 100644 --- a/packages/gamut/src/Menu/elements.tsx +++ b/packages/gamut/src/Menu/elements.tsx @@ -143,10 +143,10 @@ const activeStates = system.states({ bg: 'background-selected', }, active: { - fontWeight: 700, + fontWeight: 'title', }, 'active-navlink': { - fontWeight: 700, + fontWeight: 'title', bg: 'background-selected', [Selectors.BEFORE]: { content: "''", diff --git a/packages/gamut/src/Tabs/TabButton.tsx b/packages/gamut/src/Tabs/TabButton.tsx index 8a99b1566b1..0cbaf3f2582 100644 --- a/packages/gamut/src/Tabs/TabButton.tsx +++ b/packages/gamut/src/Tabs/TabButton.tsx @@ -15,7 +15,7 @@ export interface TabButtonProps TabElementStyleProps {} const tabSelectedStyles = { - fontWeight: 700, + fontWeight: 'title', pt: 12, pb: 8, borderBottomWidth: 4, diff --git a/packages/styleguide/.storybook/components/Headers/ComponentHeader.tsx b/packages/styleguide/.storybook/components/Headers/ComponentHeader.tsx index 5412eafee97..e899be3950b 100644 --- a/packages/styleguide/.storybook/components/Headers/ComponentHeader.tsx +++ b/packages/styleguide/.storybook/components/Headers/ComponentHeader.tsx @@ -94,7 +94,7 @@ export const ComponentHeader: React.FC = ({ diff --git a/packages/styleguide/src/lib/Foundations/Theme/LXStudioTheme.mdx b/packages/styleguide/src/lib/Foundations/Theme/LXStudioTheme.mdx index cff103a3988..ac141100995 100644 --- a/packages/styleguide/src/lib/Foundations/Theme/LXStudioTheme.mdx +++ b/packages/styleguide/src/lib/Foundations/Theme/LXStudioTheme.mdx @@ -30,12 +30,12 @@ If you are using the LX Studio Theme via the Gamut theme provider in your app (i import { css } from '@codecademy/gamut-styles'; import styled from '@emotion/styled'; -const Box = styled.div(css({ bg: 'lxStudioPurple', p: 4 })); // our system props inherently use the current theme +const Box = styled.div(css({ bg: 'sapphire', p: 4 })); // our system props inherently use the current theme const styles = styled.div` blue: ${({ theme }) => theme.colors - .lxStudioPurple}; // theme here refers to the current theme, which is the LX Studio theme + .sapphire}; // theme here refers to the current theme, which is the LX Studio theme `; ``` @@ -46,12 +46,12 @@ import { css } from '@emotion/react'; import { lxStudioTheme, theme } from '@codecademy/gamut-styles'; const myStyles = css` - color: ${lxStudioTheme.colors.lxStudioPurple}; + color: ${lxStudioTheme.colors.sapphire}; `; const notWorkingStyles = css` color: ${theme.colors - .lxStudioPurple}; // teal does not exist in the Core Theme colors so this does not work + .sapphire}; // sapphire does not exist on the Core Theme colors so this does not work `; ``` diff --git a/packages/styleguide/src/lib/Organisms/Lists & Tables/List/List.stories.tsx b/packages/styleguide/src/lib/Organisms/Lists & Tables/List/List.stories.tsx index 79b6237c40f..cdd5feba009 100644 --- a/packages/styleguide/src/lib/Organisms/Lists & Tables/List/List.stories.tsx +++ b/packages/styleguide/src/lib/Organisms/Lists & Tables/List/List.stories.tsx @@ -317,7 +317,7 @@ export const CondensedSpacingGuide: Story = { {rows.map(({ name, role, ship }, i, _, key = `example-row-${i}`) => ( - + {name} @@ -391,7 +391,7 @@ export const CondensedTableGuide: Story = { {rows.map(({ name, role, ship }, i, _, key = `example-row-${i}`) => ( - + {name} diff --git a/packages/styleguide/src/lib/Organisms/Lists & Tables/List/ListCol/ListCol.stories.tsx b/packages/styleguide/src/lib/Organisms/Lists & Tables/List/ListCol/ListCol.stories.tsx index 4cc200f8bcd..422e19c8393 100644 --- a/packages/styleguide/src/lib/Organisms/Lists & Tables/List/ListCol/ListCol.stories.tsx +++ b/packages/styleguide/src/lib/Organisms/Lists & Tables/List/ListCol/ListCol.stories.tsx @@ -110,7 +110,7 @@ export const HorizontalScrolling: ListCompositionStory = { {rows.map(({ name, role, ship }, i, _, key = `example-row-${i}`) => ( - + {name} @@ -223,7 +223,7 @@ export const Responsive: ListCompositionStory = { - + Ordered List Header From 3d17444b90dee607575f5c287c589c51010f8fbd Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Wed, 6 May 2026 11:07:38 -0400 Subject: [PATCH 2/6] update real font links --- .../gamut-styles/src/remoteAssets/fonts.ts | 75 +++++++++++++++---- .../__snapshots__/theme.test.ts.snap | 4 +- .../gamut-styles/src/variables/typography.ts | 5 +- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/packages/gamut-styles/src/remoteAssets/fonts.ts b/packages/gamut-styles/src/remoteAssets/fonts.ts index c9e584dd908..f997b2e90dc 100644 --- a/packages/gamut-styles/src/remoteAssets/fonts.ts +++ b/packages/gamut-styles/src/remoteAssets/fonts.ts @@ -96,33 +96,82 @@ export const percipio: readonly FontConfig[] = [ ]; /** - * LX Studio loads Hanken as fallback while font stacks prefer Skillsoft faces. - * When Skillsoft Sans / Skillsoft Text files are hosted under FONT_ASSET_PATH, - * add @font-face entries here (400, 500, 700, italics as needed) and remove Hanken. + * LX Studio: Skillsoft Sans (accent) + Skillsoft Text (base). + * Weights 400 / 500 / 700 + italics; 500 uses Medium files for title/bold tokens. */ export const lxStudio: readonly FontConfig[] = [ { - filePath: `${FONT_ASSET_PATH}/hanken-grotesk-regular`, + filePath: `${FONT_ASSET_PATH}/SkillsoftText-Regular`, extensions, - name: 'Hanken Grotesk', + name: 'Skillsoft Text', }, { - filePath: `${FONT_ASSET_PATH}/hanken-grotesk-italic`, + filePath: `${FONT_ASSET_PATH}/SkillsoftText-RegularItalic`, extensions, - name: 'Hanken Grotesk', + name: 'Skillsoft Text', style: 'italic', }, { - filePath: `${FONT_ASSET_PATH}/hanken-grotesk-bold`, + filePath: `${FONT_ASSET_PATH}/SkillsoftText-Medium`, extensions, - name: 'Hanken Grotesk', - weight: 'bold', + name: 'Skillsoft Text', + weight: 500, }, { - filePath: `${FONT_ASSET_PATH}/hanken-grotesk-bold-italic`, + filePath: `${FONT_ASSET_PATH}/SkillsoftText-MediumItalic`, extensions, - name: 'Hanken Grotesk', - weight: 'bold', + name: 'Skillsoft Text', + weight: 500, + style: 'italic', + }, + { + filePath: `${FONT_ASSET_PATH}/SkillsoftText-Bold`, + extensions, + name: 'Skillsoft Text', + weight: 700, + }, + { + filePath: `${FONT_ASSET_PATH}/SkillsoftText-BoldItalic`, + extensions, + name: 'Skillsoft Text', + weight: 700, + style: 'italic', + }, + { + filePath: `${FONT_ASSET_PATH}/SkillsoftSans-Regular`, + extensions, + name: 'Skillsoft Sans', + }, + { + filePath: `${FONT_ASSET_PATH}/SkillsoftSans-RegularItalic`, + extensions, + name: 'Skillsoft Sans', + style: 'italic', + }, + { + filePath: `${FONT_ASSET_PATH}/SkillsoftSans-Medium`, + extensions, + name: 'Skillsoft Sans', + weight: 500, + }, + { + filePath: `${FONT_ASSET_PATH}/SkillsoftSans-MediumItalic`, + extensions, + name: 'Skillsoft Sans', + weight: 500, + style: 'italic', + }, + { + filePath: `${FONT_ASSET_PATH}/SkillsoftSans-Bold`, + extensions, + name: 'Skillsoft Sans', + weight: 700, + }, + { + filePath: `${FONT_ASSET_PATH}/SkillsoftSans-BoldItalic`, + extensions, + name: 'Skillsoft Sans', + weight: 700, style: 'italic', }, ]; diff --git a/packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap b/packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap index 518b7631f72..37a4d44f75b 100644 --- a/packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap +++ b/packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap @@ -1436,10 +1436,10 @@ exports[`themes lxStudio - theme shape 1`] = ` "headerZ": "var(--elements-headerZ)", }, "fontFamily": { - "accent": ""Skillsoft Sans", "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", + "accent": ""Skillsoft Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif", - "base": ""Skillsoft Text", "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", + "base": ""Skillsoft Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif", "monospace": "Monaco, Menlo, "Ubuntu Mono", "Droid Sans Mono", Consolas, diff --git a/packages/gamut-styles/src/variables/typography.ts b/packages/gamut-styles/src/variables/typography.ts index d48bba52684..9dce0dc037d 100644 --- a/packages/gamut-styles/src/variables/typography.ts +++ b/packages/gamut-styles/src/variables/typography.ts @@ -14,12 +14,11 @@ monospace`; export const fontSystem = `-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`; -/** Skillsoft rebrand: primary face + Hanken until Skillsoft files are hosted on the CDN. */ -export const fontLxStudioAccent = `"Skillsoft Sans", "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", +export const fontLxStudioAccent = `"Skillsoft Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`; -export const fontLxStudioBase = `"Skillsoft Text", "Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", +export const fontLxStudioBase = `"Skillsoft Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`; From 68597d4441185af241089473143c8f96f7e431a8 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Wed, 6 May 2026 11:51:55 -0400 Subject: [PATCH 3/6] add release plans --- .nx/version-plans/version-plan-1778082684656.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .nx/version-plans/version-plan-1778082684656.md diff --git a/.nx/version-plans/version-plan-1778082684656.md b/.nx/version-plans/version-plan-1778082684656.md new file mode 100644 index 00000000000..f0c75b4b26c --- /dev/null +++ b/.nx/version-plans/version-plan-1778082684656.md @@ -0,0 +1,6 @@ +--- +gamut-styles: major +gamut: minor +--- + +Updates LXStudio theme to new guidelines From 926b6adf1a327d0e00764457333ebc2d43d2aa09 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Thu, 7 May 2026 14:44:38 -0400 Subject: [PATCH 4/6] font fix --- .../components/Elements/DocsContainer.tsx | 13 +++- .../.storybook/theming/GamutTheme.ts | 78 +++++++++++-------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/packages/styleguide/.storybook/components/Elements/DocsContainer.tsx b/packages/styleguide/.storybook/components/Elements/DocsContainer.tsx index 0e55152fda5..03c19bdc0c8 100644 --- a/packages/styleguide/.storybook/components/Elements/DocsContainer.tsx +++ b/packages/styleguide/.storybook/components/Elements/DocsContainer.tsx @@ -19,7 +19,7 @@ import { import { ThemeProvider } from 'storybook/theming'; import { useMemo } from 'react'; import { HelmetProvider } from 'react-helmet-async'; -import theme from '../../theming/GamutTheme'; +import { createGamutDocsTheme } from '../../theming/GamutTheme'; import { createTheme } from '@codecademy/variance'; export const storybookTheme = createTheme(coreTheme) .addColors(platformPalette) @@ -74,8 +74,17 @@ export const DocsContainer: React.FC<{ }; }, [storyId, globalTheme]); + const storybookDocsChromeTheme = useMemo( + () => createGamutDocsTheme(currentTheme.fontFamily.base), + [currentTheme] + ); + return ( - + Date: Thu, 7 May 2026 15:09:30 -0400 Subject: [PATCH 5/6] add percipio theme --- .../src/__tests__/AssetProvider.test.tsx | 69 +++++----- .../src/__tests__/fontLoading.test.tsx | 45 ++++--- .../gamut-styles/src/remoteAssets/fonts.ts | 121 ++++++++++-------- packages/gamut-styles/src/themes/lxStudio.ts | 18 +-- packages/gamut-styles/src/themes/percipio.ts | 14 +- .../src/utilities/__tests__/fontUtils.test.ts | 69 +++++----- .../gamut-styles/src/variables/typography.ts | 16 ++- .../lib/Foundations/Theme/PercipioTheme.mdx | 2 +- 8 files changed, 194 insertions(+), 160 deletions(-) diff --git a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx index 298c9954a95..7a38ef6fa61 100644 --- a/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx +++ b/packages/gamut-styles/src/__tests__/AssetProvider.test.tsx @@ -12,36 +12,45 @@ jest.mock('../utilities/fontUtils', () => ({ getFonts: jest.fn(), })); -jest.mock('../remoteAssets/fonts', () => ({ - webFonts: { - core: [ - { - filePath: 'https://www.codecademy.com/gamut/apercu-regular-pro', - extensions: ['woff2', 'woff'], - name: 'Apercu', - }, - { - filePath: 'https://www.codecademy.com/gamut/apercu-bold-pro', - extensions: ['woff2', 'woff'], - name: 'Apercu', - weight: 'bold', - }, - ], - percipio: [ - { - filePath: 'https://www.codecademy.com/gamut/roboto-regular', - extensions: ['woff2', 'woff'], - name: 'Roboto', - }, - { - filePath: 'https://www.codecademy.com/gamut/roboto-bold', - extensions: ['woff2', 'woff'], - name: 'Roboto', - weight: 'bold', - }, - ], - }, -})); +jest.mock('../remoteAssets/fonts', () => { + const percipio = [ + { + filePath: 'https://www.codecademy.com/gamut/SkillsoftText-Regular', + extensions: ['woff2', 'woff'], + name: 'Skillsoft Text', + }, + { + filePath: 'https://www.codecademy.com/gamut/roboto-regular', + extensions: ['woff2', 'woff'], + name: 'Roboto', + }, + { + filePath: 'https://www.codecademy.com/gamut/roboto-bold', + extensions: ['woff2', 'woff'], + name: 'Roboto', + weight: 'bold', + }, + ]; + return { + webFonts: { + core: [ + { + filePath: 'https://www.codecademy.com/gamut/apercu-regular-pro', + extensions: ['woff2', 'woff'], + name: 'Apercu', + }, + { + filePath: 'https://www.codecademy.com/gamut/apercu-bold-pro', + extensions: ['woff2', 'woff'], + name: 'Apercu', + weight: 'bold', + }, + ], + percipio, + lxStudio: percipio, + }, + }; +}); const mockGetFonts = require('../utilities/fontUtils').getFonts; diff --git a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx index 782c8bdb109..00bd1026b39 100644 --- a/packages/gamut-styles/src/__tests__/fontLoading.test.tsx +++ b/packages/gamut-styles/src/__tests__/fontLoading.test.tsx @@ -10,24 +10,33 @@ jest.mock('../utilities/fontUtils', () => ({ getFonts: jest.fn(), })); -jest.mock('../remoteAssets/fonts', () => ({ - webFonts: { - core: [ - { - filePath: 'https://www.codecademy.com/gamut/apercu-regular-pro', - extensions: ['woff2', 'woff'], - name: 'Apercu', - }, - ], - percipio: [ - { - filePath: 'https://www.codecademy.com/gamut/roboto-regular', - extensions: ['woff2', 'woff'], - name: 'Roboto', - }, - ], - }, -})); +jest.mock('../remoteAssets/fonts', () => { + const percipio = [ + { + filePath: 'https://www.codecademy.com/gamut/SkillsoftText-Regular', + extensions: ['woff2', 'woff'], + name: 'Skillsoft Text', + }, + { + filePath: 'https://www.codecademy.com/gamut/roboto-regular', + extensions: ['woff2', 'woff'], + name: 'Roboto', + }, + ]; + return { + webFonts: { + core: [ + { + filePath: 'https://www.codecademy.com/gamut/apercu-regular-pro', + extensions: ['woff2', 'woff'], + name: 'Apercu', + }, + ], + percipio, + lxStudio: percipio, + }, + }; +}); const mockGetFonts = require('../utilities/fontUtils').getFonts; diff --git a/packages/gamut-styles/src/remoteAssets/fonts.ts b/packages/gamut-styles/src/remoteAssets/fonts.ts index f997b2e90dc..765c333b878 100644 --- a/packages/gamut-styles/src/remoteAssets/fonts.ts +++ b/packages/gamut-styles/src/remoteAssets/fonts.ts @@ -1,4 +1,4 @@ -import { FontConfig } from '../utilities/fontUtils'; +import type { FontConfig } from '../utilities/fontUtils'; export const FONT_ASSET_PATH = `https://www.codecademy.com/gamut`; @@ -43,63 +43,11 @@ export const core: readonly FontConfig[] = [ }, ]; -export const percipio: readonly FontConfig[] = [ - { - filePath: `${FONT_ASSET_PATH}/roboto-regular`, - extensions, - name: 'Roboto', - }, - { - filePath: `${FONT_ASSET_PATH}/roboto-italic`, - extensions, - name: 'Roboto', - style: 'italic', - }, - // The default weight for bold fonts is 700, Percipio uses 500 for the base bold - { - filePath: `${FONT_ASSET_PATH}/roboto-bold`, - extensions, - name: 'Roboto', - weight: 'bold', - }, - { - filePath: `${FONT_ASSET_PATH}/roboto-bold`, - extensions, - name: 'Roboto', - weight: 500, - }, - { - filePath: `${FONT_ASSET_PATH}/roboto-bold-italic`, - extensions, - name: 'Roboto', - weight: 'bold', - style: 'italic', - }, - { - filePath: `${FONT_ASSET_PATH}/roboto-bold-italic`, - extensions, - name: 'Roboto', - weight: 500, - style: 'italic', - }, - { - filePath: `${FONT_ASSET_PATH}/roboto-mono-regular`, - extensions, - name: 'Roboto Mono', - }, - { - filePath: `${FONT_ASSET_PATH}/roboto-mono-bold`, - extensions, - name: 'Roboto Mono', - weight: 'bold', - }, -]; - /** - * LX Studio: Skillsoft Sans (accent) + Skillsoft Text (base). + * Skillsoft Sans (accent) + Skillsoft Text (base). * Weights 400 / 500 / 700 + italics; 500 uses Medium files for title/bold tokens. */ -export const lxStudio: readonly FontConfig[] = [ +const skillsoftFamilyFonts: readonly FontConfig[] = [ { filePath: `${FONT_ASSET_PATH}/SkillsoftText-Regular`, extensions, @@ -176,4 +124,65 @@ export const lxStudio: readonly FontConfig[] = [ }, ]; -export const webFonts = { core, percipio, lxStudio } as const; +/** + * Roboto + Roboto Mono for Percipio `system` and `monospace` theme slots. + * The default weight for bold fonts is 700; Percipio uses 500 for the base bold. + */ +const percipioRobotoFonts: readonly FontConfig[] = [ + { + filePath: `${FONT_ASSET_PATH}/roboto-regular`, + extensions, + name: 'Roboto', + }, + { + filePath: `${FONT_ASSET_PATH}/roboto-italic`, + extensions, + name: 'Roboto', + style: 'italic', + }, + { + filePath: `${FONT_ASSET_PATH}/roboto-bold`, + extensions, + name: 'Roboto', + weight: 'bold', + }, + { + filePath: `${FONT_ASSET_PATH}/roboto-bold`, + extensions, + name: 'Roboto', + weight: 500, + }, + { + filePath: `${FONT_ASSET_PATH}/roboto-bold-italic`, + extensions, + name: 'Roboto', + weight: 'bold', + style: 'italic', + }, + { + filePath: `${FONT_ASSET_PATH}/roboto-bold-italic`, + extensions, + name: 'Roboto', + weight: 500, + style: 'italic', + }, + { + filePath: `${FONT_ASSET_PATH}/roboto-mono-regular`, + extensions, + name: 'Roboto Mono', + }, + { + filePath: `${FONT_ASSET_PATH}/roboto-mono-bold`, + extensions, + name: 'Roboto Mono', + weight: 'bold', + }, +]; + +/** Percipio: Skillsoft (accent/base) plus Roboto system + Roboto Mono monospace. */ +export const percipio: readonly FontConfig[] = [ + ...skillsoftFamilyFonts, + ...percipioRobotoFonts, +]; + +export const webFonts = { core, percipio, lxStudio: percipio } as const; diff --git a/packages/gamut-styles/src/themes/lxStudio.ts b/packages/gamut-styles/src/themes/lxStudio.ts index 526ed584f14..7b787cdc4fb 100644 --- a/packages/gamut-styles/src/themes/lxStudio.ts +++ b/packages/gamut-styles/src/themes/lxStudio.ts @@ -1,12 +1,11 @@ import { createTheme } from '@codecademy/variance'; import { - fontLxStudioAccent, - fontLxStudioBase, fontMonospace, fontSystem, - fontWeight as coreFontWeight, + fontWeightMediumTitle, lxStudioPalette, + percipioFontFamily, } from '../variables'; import { coreTheme } from './core'; @@ -16,19 +15,12 @@ import { coreTheme } from './core'; */ const lxStudioFontFamily = { - accent: fontLxStudioAccent, - base: fontLxStudioBase, + accent: percipioFontFamily.accent, + base: percipioFontFamily.base, monospace: fontMonospace, system: fontSystem, } as const; -const lxStudioFontWeight = { - ...coreFontWeight, - title: 500, - bold: 500, - 500: 500, -} as const; - export const lxStudioBorderRadii = { none: '0px', sm: '4px', @@ -42,7 +34,7 @@ export const lxStudioTheme = createTheme({ ...coreTheme, borderRadii: lxStudioBorderRadii, fontFamily: lxStudioFontFamily, - fontWeight: lxStudioFontWeight, + fontWeight: fontWeightMediumTitle, }) .addColors(lxStudioPalette) .addColorModes('light', { diff --git a/packages/gamut-styles/src/themes/percipio.ts b/packages/gamut-styles/src/themes/percipio.ts index f5903dc904c..e7a5267b984 100644 --- a/packages/gamut-styles/src/themes/percipio.ts +++ b/packages/gamut-styles/src/themes/percipio.ts @@ -1,18 +1,16 @@ import { createTheme } from '@codecademy/variance'; -import { percipioFontFamily, percipioPalette } from '../variables'; +import { + fontWeightMediumTitle, + percipioFontFamily, + percipioPalette, +} from '../variables'; import { coreTheme } from './core'; export const percipioTheme = createTheme({ ...coreTheme, fontFamily: percipioFontFamily, - fontWeight: { - base: 400, - title: 500, - bold: 500, - 700: 700, - 400: 400, - }, + fontWeight: fontWeightMediumTitle, }) .addColors(percipioPalette) .addColorModes('light', { diff --git a/packages/gamut-styles/src/utilities/__tests__/fontUtils.test.ts b/packages/gamut-styles/src/utilities/__tests__/fontUtils.test.ts index f10a302a0ed..75880cdfd93 100644 --- a/packages/gamut-styles/src/utilities/__tests__/fontUtils.test.ts +++ b/packages/gamut-styles/src/utilities/__tests__/fontUtils.test.ts @@ -1,36 +1,39 @@ import { webFonts } from '../../remoteAssets/fonts'; import { getFonts } from '../fontUtils'; -jest.mock('../../remoteAssets/fonts', () => ({ - webFonts: { - core: [ - { - filePath: 'https://www.codecademy.com/gamut/apercu-regular-pro', - extensions: ['woff2', 'woff'], - name: 'Apercu', - }, - { - filePath: 'https://www.codecademy.com/gamut/apercu-bold-pro', - extensions: ['woff2', 'woff'], - name: 'Apercu', - weight: 'bold', - }, - ], - percipio: [ - { - filePath: 'https://www.codecademy.com/gamut/roboto-regular', - extensions: ['woff2', 'woff'], - name: 'Roboto', - }, - { - filePath: 'https://www.codecademy.com/gamut/roboto-bold', - extensions: ['woff2', 'woff'], - name: 'Roboto', - weight: 'bold', - }, - ], - }, -})); +jest.mock('../../remoteAssets/fonts', () => { + const percipio = [ + { + filePath: 'https://www.codecademy.com/gamut/SkillsoftText-Regular', + extensions: ['woff2', 'woff'], + name: 'Skillsoft Text', + }, + { + filePath: 'https://www.codecademy.com/gamut/roboto-regular', + extensions: ['woff2', 'woff'], + name: 'Roboto', + }, + ]; + return { + webFonts: { + core: [ + { + filePath: 'https://www.codecademy.com/gamut/apercu-regular-pro', + extensions: ['woff2', 'woff'], + name: 'Apercu', + }, + { + filePath: 'https://www.codecademy.com/gamut/apercu-bold-pro', + extensions: ['woff2', 'woff'], + name: 'Apercu', + weight: 'bold', + }, + ], + percipio, + lxStudio: percipio, + }, + }; +}); describe('fontUtils', () => { describe('getFonts', () => { @@ -55,6 +58,11 @@ describe('fontUtils', () => { expect(fonts).toBe(webFonts.percipio); }); + it('should return percipio fonts for lxStudio theme', () => { + const fonts = getFonts('lxStudio'); + expect(fonts).toBe(webFonts.percipio); + }); + it('should return core fonts for core theme', () => { const fonts = getFonts('core'); expect(fonts).toBe(webFonts.core); @@ -99,6 +107,7 @@ describe('fontUtils', () => { webFonts: { core: undefined, percipio: webFonts.percipio, + lxStudio: webFonts.percipio, }, })); diff --git a/packages/gamut-styles/src/variables/typography.ts b/packages/gamut-styles/src/variables/typography.ts index 9dce0dc037d..2bd2b4001c1 100644 --- a/packages/gamut-styles/src/variables/typography.ts +++ b/packages/gamut-styles/src/variables/typography.ts @@ -14,11 +14,11 @@ monospace`; export const fontSystem = `-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`; -export const fontLxStudioAccent = `"Skillsoft Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", +export const fontPercipioAccent = `"Skillsoft Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`; -export const fontLxStudioBase = `"Skillsoft Text", -apple-system, BlinkMacSystemFont, "Segoe UI", +export const fontPercipioBase = `"Skillsoft Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`; @@ -54,9 +54,17 @@ export const fontWeight = { 400: 400, } as const; +/** Title/bold semantic tokens use 500 (Medium), matching Skillsoft + Percipio Roboto webfonts. */ +export const fontWeightMediumTitle = { + ...fontWeight, + title: 500, + bold: 500, + 500: 500, +} as const; + export const percipioFontFamily = { - accent: '"Roboto", sans-serif', - base: '"Roboto", sans-serif', + accent: fontPercipioAccent, + base: fontPercipioBase, monospace: '"Roboto Mono", monospace', system: '"Roboto", sans-serif', } as const; diff --git a/packages/styleguide/src/lib/Foundations/Theme/PercipioTheme.mdx b/packages/styleguide/src/lib/Foundations/Theme/PercipioTheme.mdx index 0ff44224640..21ad62833cf 100644 --- a/packages/styleguide/src/lib/Foundations/Theme/PercipioTheme.mdx +++ b/packages/styleguide/src/lib/Foundations/Theme/PercipioTheme.mdx @@ -93,6 +93,6 @@ Percipio currently only supports `light` mode. **Key**: `fontFamily` -The Percipio theme uses Roboto fonts for all font families to provide a consistent typography experience. +The Percipio theme uses Skillsoft Sans for accent text and Skillsoft Text for body text. Roboto Mono is used for monospace, and Roboto sans-serif for the `system` font family slot. From b16d3490a601bb79049efc17eef2aaee0b3bf82d Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Thu, 7 May 2026 15:45:45 -0400 Subject: [PATCH 6/6] update percipio colors --- packages/gamut-styles/src/themes/percipio.ts | 4 ++-- packages/gamut-styles/src/variables/colors.ts | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/gamut-styles/src/themes/percipio.ts b/packages/gamut-styles/src/themes/percipio.ts index e7a5267b984..8ad0013945d 100644 --- a/packages/gamut-styles/src/themes/percipio.ts +++ b/packages/gamut-styles/src/themes/percipio.ts @@ -38,7 +38,7 @@ export const percipioTheme = createTheme({ secondary: 'navy-400', }, primary: { - _: 'percipioActionPrimary', + _: 'sapphire', hover: 'percipioActionPrimaryHover', inverse: 'white', }, @@ -51,7 +51,7 @@ export const percipioTheme = createTheme({ hover: 'percipioActionDangerHover', }, interface: { - _: 'percipioActionPrimary', + _: 'sapphire', hover: 'percipioActionPrimaryHover', }, border: { diff --git a/packages/gamut-styles/src/variables/colors.ts b/packages/gamut-styles/src/variables/colors.ts index 15eace4c506..38001718876 100644 --- a/packages/gamut-styles/src/variables/colors.ts +++ b/packages/gamut-styles/src/variables/colors.ts @@ -164,10 +164,13 @@ export const platformPalette = { * LX Studio Colors */ +/** Primary brand blue; used by LX Studio and Percipio theme `primary` tokens */ +export const sapphire = '#1C50BB'; + export const lxStudioColors = { lxStudioSuccess: '#06844F', lxStudioBgPrimary: '#FAFBFC', - sapphire: '#1C50BB', + sapphire, } as const; export const lxStudioPalette = { @@ -193,7 +196,7 @@ export const percipioColors = { percipioBgError: '#FFF1F5', // Action colors - percipioActionPrimary: '#0073C4', + sapphire, percipioActionPrimaryHover: '#141C36', percipioActionSecondary: '#6A6E75', percipioActionSecondaryHover: 'rgba(106, 110, 117, 0.86)', @@ -205,6 +208,8 @@ export const percipioColors = { // Multiuse colors percipioDanger: '#B83C3C', + + /** Shared with LX Studio; `primary` color mode resolves here */ } as const; export const percipioPalette = {