From 2f053554874191a2f07f5a3381c7bf3240db4c6a Mon Sep 17 00:00:00 2001 From: David Perez <david@livefront.com> Date: Mon, 30 Sep 2024 12:56:32 -0500 Subject: [PATCH] PM-12322: New color scheme (#3995) --- .../theme/color/BitwardenColorScheme.kt | 139 +++++++++ .../ui/platform/theme/color/ColorScheme.kt | 278 ++++++++++++++++++ 2 files changed, 417 insertions(+) create mode 100644 app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/BitwardenColorScheme.kt create mode 100644 app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/ColorScheme.kt diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/BitwardenColorScheme.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/BitwardenColorScheme.kt new file mode 100644 index 000000000..20409dc46 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/BitwardenColorScheme.kt @@ -0,0 +1,139 @@ +package com.x8bit.bitwarden.ui.platform.theme.color + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color + +/** + * Defines all the colors for the app. + */ +@Immutable +data class BitwardenColorScheme( + val text: TextColors, + val background: BackgroundColors, + val stroke: StrokeColors, + val icon: IconColors, + val filledButton: FilledButtonColors, + val outlineButton: OutlineButtonColors, + val tonalButton: TonalButtonColors, + val toggleButton: ToggleButtonColors, + val sliderButton: SliderButtonColors, + val status: StatusColors, +) { + /** + * Defines all the text colors for the app. + */ + @Immutable + data class TextColors( + val primary: Color, + val secondary: Color, + val interaction: Color, + val reversed: Color, + val codePink: Color, + val codeBlue: Color, + ) + + /** + * Defines all the background colors for the app. + */ + @Immutable + data class BackgroundColors( + val primary: Color, + val secondary: Color, + val tertiary: Color, + val alert: Color, + val scrim: Color, + val pressed: Color, + ) + + /** + * Defines all the stroke colors for the app. + */ + @Immutable + data class StrokeColors( + val divider: Color, + val border: Color, + val segmentedNav: Color, + ) + + /** + * Defines all the icons colors for the app. + */ + @Immutable + data class IconColors( + val primary: Color, + val secondary: Color, + val reversed: Color, + val badgeBackground: Color, + val badgeForeground: Color, + ) + + /** + * Defines all the filled button colors for the app. + */ + @Immutable + data class FilledButtonColors( + val background: Color, + val backgroundDisabled: Color, + val backgroundReversed: Color, + val foreground: Color, + val foregroundDisabled: Color, + val foregroundReversed: Color, + ) + + /** + * Defines all the outline button colors for the app. + */ + @Immutable + data class OutlineButtonColors( + val border: Color, + val borderDisabled: Color, + val borderReversed: Color, + val foreground: Color, + val foregroundDisabled: Color, + val foregroundReversed: Color, + ) + + /** + * Defines all the tonal button colors for the app. + */ + @Immutable + data class TonalButtonColors( + val background: Color, + val backgroundDisabled: Color, + val foreground: Color, + val foregroundDisabled: Color, + ) + + /** + * Defines all the toggle colors for the app. + */ + @Immutable + data class ToggleButtonColors( + val backgroundOn: Color, + val backgroundOff: Color, + val switch: Color, + ) + + /** + * Defines all the slider colors for the app. + */ + @Immutable + data class SliderButtonColors( + val knobBackground: Color, + val knobLabel: Color, + val filled: Color, + val unfilled: Color, + ) + + /** + * Defines all the status colors for the app. + */ + @Immutable + data class StatusColors( + val strong: Color, + val good: Color, + val weak1: Color, + val weak2: Color, + val error: Color, + ) +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/ColorScheme.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/ColorScheme.kt new file mode 100644 index 000000000..4ff0c02fa --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/theme/color/ColorScheme.kt @@ -0,0 +1,278 @@ +package com.x8bit.bitwarden.ui.platform.theme.color + +import androidx.compose.material3.ColorScheme +import androidx.compose.ui.graphics.Color + +/** + * The default [BitwardenColorScheme] for dark mode. + */ +val darkBitwardenColorScheme: BitwardenColorScheme = BitwardenColorScheme( + text = BitwardenColorScheme.TextColors( + primary = PrimitiveColors.gray200, + secondary = PrimitiveColors.gray600, + interaction = PrimitiveColors.blue400, + reversed = PrimitiveColors.gray1200, + codePink = PrimitiveColors.pink200, + codeBlue = PrimitiveColors.blue400, + ), + background = BitwardenColorScheme.BackgroundColors( + primary = PrimitiveColors.gray1200, + secondary = PrimitiveColors.gray1100, + tertiary = PrimitiveColors.gray1000, + alert = PrimitiveColors.gray300, + scrim = PrimitiveColors.gray1400.copy(alpha = 0.4f), + pressed = PrimitiveColors.gray500, + ), + stroke = BitwardenColorScheme.StrokeColors( + divider = PrimitiveColors.gray900, + border = PrimitiveColors.blue400, + segmentedNav = PrimitiveColors.gray900, + ), + icon = BitwardenColorScheme.IconColors( + primary = PrimitiveColors.gray500, + secondary = PrimitiveColors.blue400, + reversed = PrimitiveColors.gray1100, + badgeBackground = PrimitiveColors.pink200, + badgeForeground = PrimitiveColors.gray1100, + ), + filledButton = BitwardenColorScheme.FilledButtonColors( + background = PrimitiveColors.blue400, + backgroundDisabled = PrimitiveColors.gray900, + backgroundReversed = PrimitiveColors.gray1100, + foreground = PrimitiveColors.gray1100, + foregroundDisabled = PrimitiveColors.gray500, + foregroundReversed = PrimitiveColors.blue400, + ), + outlineButton = BitwardenColorScheme.OutlineButtonColors( + border = PrimitiveColors.blue400, + borderDisabled = PrimitiveColors.gray900, + borderReversed = PrimitiveColors.gray1100, + foreground = PrimitiveColors.blue400, + foregroundDisabled = PrimitiveColors.gray900, + foregroundReversed = PrimitiveColors.gray1100, + ), + tonalButton = BitwardenColorScheme.TonalButtonColors( + background = PrimitiveColors.gray900, + backgroundDisabled = PrimitiveColors.gray900, + foreground = PrimitiveColors.gray200, + foregroundDisabled = PrimitiveColors.gray500, + ), + toggleButton = BitwardenColorScheme.ToggleButtonColors( + backgroundOn = PrimitiveColors.blue300, + backgroundOff = PrimitiveColors.gray900, + switch = PrimitiveColors.gray100, + ), + sliderButton = BitwardenColorScheme.SliderButtonColors( + knobBackground = PrimitiveColors.blue400, + knobLabel = PrimitiveColors.gray1100, + filled = PrimitiveColors.blue400, + unfilled = PrimitiveColors.gray900, + ), + status = BitwardenColorScheme.StatusColors( + strong = PrimitiveColors.green200, + good = PrimitiveColors.blue400, + weak1 = PrimitiveColors.red200, + weak2 = PrimitiveColors.yellow200, + error = PrimitiveColors.red200, + ), +) + +/** + * The default [BitwardenColorScheme] for light mode. + */ +val lightBitwardenColorScheme: BitwardenColorScheme = BitwardenColorScheme( + text = BitwardenColorScheme.TextColors( + primary = PrimitiveColors.gray1300, + secondary = PrimitiveColors.gray700, + interaction = PrimitiveColors.blue500, + reversed = PrimitiveColors.gray100, + codePink = PrimitiveColors.pink100, + codeBlue = PrimitiveColors.blue500, + ), + background = BitwardenColorScheme.BackgroundColors( + primary = PrimitiveColors.gray200, + secondary = PrimitiveColors.gray100, + tertiary = PrimitiveColors.blue100, + alert = PrimitiveColors.blue700, + scrim = PrimitiveColors.gray1400.copy(alpha = 0.4f), + pressed = PrimitiveColors.gray1000, + ), + stroke = BitwardenColorScheme.StrokeColors( + divider = PrimitiveColors.gray400, + border = PrimitiveColors.blue500, + segmentedNav = PrimitiveColors.blue100, + ), + icon = BitwardenColorScheme.IconColors( + primary = PrimitiveColors.gray700, + secondary = PrimitiveColors.blue500, + reversed = PrimitiveColors.gray100, + badgeBackground = PrimitiveColors.pink100, + badgeForeground = PrimitiveColors.gray100, + ), + filledButton = BitwardenColorScheme.FilledButtonColors( + background = PrimitiveColors.blue500, + backgroundDisabled = PrimitiveColors.gray400, + backgroundReversed = PrimitiveColors.gray100, + foreground = PrimitiveColors.gray100, + foregroundDisabled = PrimitiveColors.gray500, + foregroundReversed = PrimitiveColors.blue500, + ), + outlineButton = BitwardenColorScheme.OutlineButtonColors( + border = PrimitiveColors.blue500, + borderDisabled = PrimitiveColors.gray500, + borderReversed = PrimitiveColors.gray100, + foreground = PrimitiveColors.blue500, + foregroundDisabled = PrimitiveColors.gray500, + foregroundReversed = PrimitiveColors.gray100, + ), + tonalButton = BitwardenColorScheme.TonalButtonColors( + background = PrimitiveColors.blue100, + backgroundDisabled = PrimitiveColors.gray400, + foreground = PrimitiveColors.gray1300, + foregroundDisabled = PrimitiveColors.gray500, + ), + toggleButton = BitwardenColorScheme.ToggleButtonColors( + backgroundOn = PrimitiveColors.blue500, + backgroundOff = PrimitiveColors.gray500, + switch = PrimitiveColors.gray100, + ), + sliderButton = BitwardenColorScheme.SliderButtonColors( + knobBackground = PrimitiveColors.blue500, + knobLabel = PrimitiveColors.gray100, + filled = PrimitiveColors.blue500, + unfilled = PrimitiveColors.gray300, + ), + status = BitwardenColorScheme.StatusColors( + strong = PrimitiveColors.green300, + good = PrimitiveColors.blue500, + weak1 = PrimitiveColors.red300, + weak2 = PrimitiveColors.yellow300, + error = PrimitiveColors.red300, + ), +) + +/** + * Creates a [BitwardenColorScheme] for dark mode based on dynamic Material You colors. + */ +@Suppress("LongMethod") +fun dynamicBitwardenColorScheme( + materialColorScheme: ColorScheme, + isDarkTheme: Boolean, +): BitwardenColorScheme { + val defaultTheme = if (isDarkTheme) darkBitwardenColorScheme else lightBitwardenColorScheme + return BitwardenColorScheme( + text = BitwardenColorScheme.TextColors( + primary = materialColorScheme.onSurface, + secondary = materialColorScheme.onSecondaryContainer, + interaction = materialColorScheme.primary, + reversed = materialColorScheme.onTertiary, + codePink = defaultTheme.text.codePink, + codeBlue = defaultTheme.text.codeBlue, + ), + background = BitwardenColorScheme.BackgroundColors( + primary = materialColorScheme.surface, + secondary = materialColorScheme.secondary, + tertiary = materialColorScheme.tertiary, + alert = materialColorScheme.error, + scrim = materialColorScheme.scrim, + pressed = materialColorScheme.onSurfaceVariant, + ), + stroke = BitwardenColorScheme.StrokeColors( + divider = materialColorScheme.outline, + border = materialColorScheme.primary, + segmentedNav = materialColorScheme.outline, + ), + icon = BitwardenColorScheme.IconColors( + primary = materialColorScheme.onPrimary, + secondary = materialColorScheme.onSecondary, + reversed = materialColorScheme.inversePrimary, + badgeBackground = materialColorScheme.error, + badgeForeground = materialColorScheme.onError, + ), + filledButton = BitwardenColorScheme.FilledButtonColors( + background = materialColorScheme.surface, + backgroundDisabled = materialColorScheme.onSurface.copy(alpha = 0.12f), + backgroundReversed = materialColorScheme.inversePrimary, + foreground = materialColorScheme.onSurface, + foregroundDisabled = materialColorScheme.onSurface.copy(alpha = 0.38f), + foregroundReversed = materialColorScheme.inverseOnSurface, + ), + outlineButton = BitwardenColorScheme.OutlineButtonColors( + border = materialColorScheme.outline, + borderDisabled = materialColorScheme.outlineVariant, + borderReversed = materialColorScheme.outlineVariant, + foreground = materialColorScheme.outlineVariant, + foregroundDisabled = materialColorScheme.outlineVariant, + foregroundReversed = materialColorScheme.outlineVariant, + ), + tonalButton = BitwardenColorScheme.TonalButtonColors( + background = materialColorScheme.secondaryContainer, + backgroundDisabled = materialColorScheme.onSurface.copy(alpha = 0.12f), + foreground = materialColorScheme.onSecondaryContainer, + foregroundDisabled = materialColorScheme.onSurface.copy(alpha = 0.38f), + ), + toggleButton = BitwardenColorScheme.ToggleButtonColors( + backgroundOn = materialColorScheme.primary, + backgroundOff = materialColorScheme.surfaceContainerHighest, + switch = materialColorScheme.onPrimary, + ), + sliderButton = BitwardenColorScheme.SliderButtonColors( + knobBackground = materialColorScheme.primary, + knobLabel = materialColorScheme.onPrimary, + filled = materialColorScheme.primary, + unfilled = materialColorScheme.secondaryContainer, + ), + status = BitwardenColorScheme.StatusColors( + strong = defaultTheme.status.strong, + good = defaultTheme.status.good, + weak1 = defaultTheme.status.weak1, + weak2 = defaultTheme.status.weak2, + error = defaultTheme.status.error, + ), + ) +} + +/** + * The raw colors that support the [BitwardenColorScheme]. + */ +private data object PrimitiveColors { + val gray100: Color = Color(color = 0xFFFFFFFF) + val gray200: Color = Color(color = 0xFFF3F6F9) + val gray300: Color = Color(color = 0xFFE6E9EF) + val gray400: Color = Color(color = 0xFFD3D9E3) + val gray500: Color = Color(color = 0xFF96A3BB) + val gray600: Color = Color(color = 0xFF8898B5) + val gray700: Color = Color(color = 0xFF5A6D91) + val gray800: Color = Color(color = 0xFF79808E) + val gray900: Color = Color(color = 0xFF525B6A) + val gray1000: Color = Color(color = 0xFF303946) + val gray1100: Color = Color(color = 0xFF202733) + val gray1200: Color = Color(color = 0xFF121A27) + val gray1300: Color = Color(color = 0xFF1B2029) + val gray1400: Color = Color(color = 0xFF000000) + + val blue100: Color = Color(color = 0xFFDBE5F6) + val blue200: Color = Color(color = 0xFFAAC3EF) + val blue300: Color = Color(color = 0xFF79A1E9) + val blue400: Color = Color(color = 0xFF65ABFF) + val blue500: Color = Color(color = 0xFF175DDC) + val blue600: Color = Color(color = 0xFF1A41AC) + val blue700: Color = Color(color = 0xFF020F66) + + val green100: Color = Color(color = 0xFFBFECC3) + val green200: Color = Color(color = 0xFF6BF178) + val green300: Color = Color(color = 0xFF0C8018) + val green400: Color = Color(color = 0xFF08540F) + + val red100: Color = Color(color = 0xFFFFECEF) + val red200: Color = Color(color = 0xFFFF4E63) + val red300: Color = Color(color = 0xFFCB263A) + val red400: Color = Color(color = 0xFF951B2A) + + val yellow100: Color = Color(color = 0xFFFFF8E4) + val yellow200: Color = Color(color = 0xFFFFBF00) + val yellow300: Color = Color(color = 0xFFAC5800) + + val pink100: Color = Color(color = 0xFFC01176) + val pink200: Color = Color(color = 0xFFFF8FD0) +}