From e02a2db3d3e523d88b352ac253dc664799939ae3 Mon Sep 17 00:00:00 2001 From: Brynn Crowley Date: Wed, 26 Feb 2025 21:46:37 -0800 Subject: feat(web): add new oled theme (#8838) This adds an OLED tuned dark mode theme to the web frontend. --- internal/configuration/schema/configuration.go | 2 +- internal/configuration/validator/const.go | 2 +- internal/configuration/validator/theme_test.go | 2 +- web/src/contexts/ThemeContext.tsx | 2 + web/src/themes/Oled.ts | 87 ++++++++++++++++++++++++++ web/src/themes/index.ts | 2 + 6 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 web/src/themes/Oled.ts diff --git a/internal/configuration/schema/configuration.go b/internal/configuration/schema/configuration.go index 607dbf64b..25cd85c1a 100644 --- a/internal/configuration/schema/configuration.go +++ b/internal/configuration/schema/configuration.go @@ -6,7 +6,7 @@ import ( // Configuration object extracted from YAML configuration file. type Configuration struct { - Theme string `koanf:"theme" json:"theme" jsonschema:"default=light,enum=auto,enum=light,enum=dark,enum=grey,title=Theme Name" jsonschema_description:"The name of the theme to apply to the web UI."` + Theme string `koanf:"theme" json:"theme" jsonschema:"default=light,enum=auto,enum=light,enum=dark,enum=grey,enum=oled,title=Theme Name" jsonschema_description:"The name of the theme to apply to the web UI."` CertificatesDirectory string `koanf:"certificates_directory" json:"certificates_directory" jsonschema:"title=Certificates Directory Path" jsonschema_description:"The path to a directory which is used to determine the certificates that are trusted."` Default2FAMethod string `koanf:"default_2fa_method" json:"default_2fa_method" jsonschema:"enum=totp,enum=webauthn,enum=mobile_push,title=Default 2FA method" jsonschema_description:"When a user logs in for the first time this is the 2FA method configured for them."` diff --git a/internal/configuration/validator/const.go b/internal/configuration/validator/const.go index 588c9d30b..7d3340d68 100644 --- a/internal/configuration/validator/const.go +++ b/internal/configuration/validator/const.go @@ -519,7 +519,7 @@ var ( var ( validStoragePostgreSQLSSLModes = []string{"disable", "require", "verify-ca", "verify-full"} - validThemeNames = []string{"light", "dark", "grey", auto} + validThemeNames = []string{"light", "dark", "grey", "oled", auto} validSessionSameSiteValues = []string{"none", "lax", "strict"} validLogLevels = []string{logging.LevelTrace, logging.LevelDebug, logging.LevelInfo, logging.LevelWarn, logging.LevelError} validLogFormats = []string{logging.FormatText, logging.FormatJSON} diff --git a/internal/configuration/validator/theme_test.go b/internal/configuration/validator/theme_test.go index b1c8b89bc..95cb355f9 100644 --- a/internal/configuration/validator/theme_test.go +++ b/internal/configuration/validator/theme_test.go @@ -36,7 +36,7 @@ func (suite *Theme) TestShouldRaiseErrorWhenInvalidThemeProvided() { suite.Assert().Len(suite.validator.Warnings(), 0) suite.Require().Len(suite.validator.Errors(), 1) - suite.Assert().EqualError(suite.validator.Errors()[0], "option 'theme' must be one of 'light', 'dark', 'grey', or 'auto' but it's configured as 'invalid'") + suite.Assert().EqualError(suite.validator.Errors()[0], "option 'theme' must be one of 'light', 'dark', 'grey', 'oled', or 'auto' but it's configured as 'invalid'") } func TestThemes(t *testing.T) { diff --git a/web/src/contexts/ThemeContext.tsx b/web/src/contexts/ThemeContext.tsx index 5841a6b6a..862ded0c2 100644 --- a/web/src/contexts/ThemeContext.tsx +++ b/web/src/contexts/ThemeContext.tsx @@ -128,6 +128,8 @@ function ThemeFromName(name: string) { return themes.Dark; case themes.ThemeNameGrey: return themes.Grey; + case themes.ThemeNameOled: + return themes.Oled; case themes.ThemeNameAuto: return window.matchMedia(MediaQueryDarkMode).matches ? themes.Dark : themes.Light; default: diff --git a/web/src/themes/Oled.ts b/web/src/themes/Oled.ts new file mode 100644 index 000000000..6fec57b2b --- /dev/null +++ b/web/src/themes/Oled.ts @@ -0,0 +1,87 @@ +import { createTheme } from "@mui/material/styles"; + +const Oled = createTheme({ + custom: { + icon: "#fff", + loadingBar: "#fff", + }, + palette: { + mode: "dark", + primary: { + main: "#1976d2", + }, + secondary: { + light: "#ff4081", + main: "#f50057", + dark: "#c51162", + contrastText: "#ffffff", + }, + error: { + light: "#e57373", + main: "#f44336", + dark: "#d32f2f", + contrastText: "#ffffff", + }, + warning: { + light: "#ffb74d", + main: "#ff9800", + dark: "#f57c00", + contrastText: "rgba(255, 255, 255, 0.87)", + }, + info: { + light: "#64b5f6", + main: "#2196f3", + dark: "#1976d2", + contrastText: "#ffffff", + }, + success: { + light: "#81c784", + main: "#4caf50", + dark: "#388e3c", + contrastText: "rgba(255, 255, 255, 0.87)", + }, + grey: { + "50": "#fafafa", + "100": "#f5f5f5", + "200": "#eeeeee", + "300": "#e0e0e0", + "400": "#bdbdbd", + "500": "#9e9e9e", + "600": "#757575", + "700": "#616161", + "800": "#424242", + "900": "#212121", + A100: "#d5d5d5", + A200: "#aaaaaa", + A400: "#303030", + A700: "#616161", + }, + contrastThreshold: 3, + tonalOffset: 0.2, + text: { + primary: "#ffffff", + secondary: "rgba(255, 255, 255, 0.7)", + disabled: "rgba(255, 255, 255, 0.5)", + }, + divider: "rgba(255, 255, 255, 0.12)", + background: { + paper: "#000000", + default: "#000000", + }, + action: { + active: "#ffffff", + hover: "rgba(255, 255, 255, 0.08)", + hoverOpacity: 0.08, + selected: "rgba(255, 255, 255, 0.16)", + selectedOpacity: 0.16, + disabled: "rgba(255, 255, 255, 0.3)", + disabledBackground: "rgba(255, 255, 255, 0.12)", + disabledOpacity: 0.38, + focus: "rgba(255, 255, 255, 0.12)", + focusOpacity: 0.12, + activatedOpacity: 0.24, + }, + }, +}); + +export default Oled; diff --git a/web/src/themes/index.ts b/web/src/themes/index.ts index d44ee18f0..4aa8644a9 100644 --- a/web/src/themes/index.ts +++ b/web/src/themes/index.ts @@ -27,7 +27,9 @@ export const ThemeNameAuto = "auto"; export const ThemeNameLight = "light"; export const ThemeNameDark = "dark"; export const ThemeNameGrey = "grey"; +export const ThemeNameOled = "oled"; export { default as Light } from "@themes/Light"; export { default as Dark } from "@themes/Dark"; export { default as Grey } from "@themes/Grey"; +export { default as Oled } from "@themes/Oled"; -- cgit v1.2.3