refactor(theme): split per-preset color schemes into theme/presets/ (#594)

* refactor(theme): split per-preset color schemes into theme/presets/

Move each ThemePreset's ColorScheme + status colors into its own file under
theme/presets/ (Default, Monochrome, Gruvbox, Catppuccin, Amoled, NeonNoir).
Theme.kt becomes a thin dispatcher via resolveColorScheme/resolveStatusColors,
making individual themes far easier to create and edit.

- AMOLED keeps a dark-only scheme (falls back to default light)
- MONOCHROME/AMOLED reuse default status colors (no bespoke semantic set)
- aligned to current Voltage Purple palette + on* status-color params

Verified: ktlint clean, compileDebugKotlin passes.

* refactor(theme): make all preset files uniform 4-token shape

Every preset file now exports XxxDarkColorScheme / XxxLightColorScheme /
XxxDarkStatusColors / XxxLightStatusColors so they share one shape and import
order. MONOCHROME + AMOLED alias the default status colors; AMOLED also gets an
explicit AmoledLightColorScheme = DefaultLightColorScheme alias.

Theme.kt dispatcher is now a pure lookup (no special-casing) for both color
scheme and status colors. No runtime behavior change — pure structural cleanup.

Verified: ktlint clean, compileDebugKotlin passes.

* docs(theme): add THEMES.md documenting the per-preset convention

Co-located in theme/ — covers layout, the uniform 4-token shape, the pure
lookup dispatcher, why DEFAULT uses design tokens (intentional, not a bug),
and a step-by-step 'add a new theme' recipe.

Verified: ktlint clean.

---------

Co-authored-by: M57 <m57@hermes-control.app>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-07-14 11:09:02 +03:00
1 parent 29b630a2ec
commit 47258bd8b0
8 files changed
+695 -424

No files matched your search

@@ -0,0 +1,113 @@
# Themes
How theming works in Hermes Control, and how to add or edit a theme.
## Where themes live
All theme code is under:
```
app/src/main/java/com/m57/hermescontrol/theme/
├── Theme.kt # dispatcher — resolves scheme + status colors per preset
├── Color.kt # brand design tokens (DarkSurface, HermesPurple, StatusGreen, …)
├── HermesStatusColors.kt # semantic status color model (success/warning/error/info + on*)
├── Type.kt / Shapes.kt # typography + shape tokens
├── Spacing.kt / Motion.kt # spacing + motion tokens
└── presets/ # one file per ThemePreset
├── DefaultScheme.kt
├── MonochromeScheme.kt
├── GruvboxScheme.kt
├── CatppuccinScheme.kt
├── AmoledScheme.kt
└── NeonNoirScheme.kt
```
## The 4-token shape
Every file in `presets/` is uniform — it exports the same four tokens:
| Token | Type | Purpose |
|-------|------|---------|
| `XxxDarkColorScheme` | `ColorScheme` | dark-mode Material 3 scheme |
| `XxxLightColorScheme` | `ColorScheme` | light-mode Material 3 scheme |
| `XxxDarkStatusColors` | `HermesStatusColors` | dark semantic status colors |
| `XxxLightStatusColors` | `HermesStatusColors` | light semantic status colors |
A preset with no bespoke status set (MONOCHROME, AMOLED) aliases the defaults:
```kotlin
val MonochromeDarkStatusColors = DefaultDarkStatusColors
val MonochromeLightStatusColors = DefaultLightStatusColors
```
AMOLED has no bespoke light scheme, so it aliases the default light scheme too:
```kotlin
val AmoledLightColorScheme = DefaultLightColorScheme
```
## The dispatcher
`Theme.kt` is a **pure lookup** — no special-casing. Given a `ThemePreset`
and a dark flag, it returns the matching scheme/status from the preset file:
```kotlin
private fun resolveColorScheme(preset: ThemePreset, darkTheme: Boolean) = when (preset) {
ThemePreset.DEFAULT -> if (darkTheme) DefaultDarkColorScheme else DefaultLightColorScheme
ThemePreset.MONOCHROME -> if (darkTheme) MonochromeDarkColorScheme else MonochromeLightColorScheme
// …one line per preset…
}
private fun resolveStatusColors(preset: ThemePreset, darkTheme: Boolean) = when (preset) {
ThemePreset.DEFAULT -> if (darkTheme) DefaultDarkStatusColors else DefaultLightStatusColors
// …one line per preset…
}
```
Dynamic (Material You) color on API 31+ overrides the resolved scheme when
`useDynamicColors = true`.
## DEFAULT uses design tokens — that's intentional
`DefaultScheme.kt` imports ~47 named tokens from `Color.kt`
(`DarkBackground`, `HermesPurple`, `StatusGreenContainer`, …) and builds its
schemes from them. This is **by design**: DEFAULT is the brand reference theme,
so a palette change in `Color.kt` flows straight through.
The other presets are hand-authored community palettes and use **raw hex**
(`Color(0xFFFE8019)`), so they only import `Color` (+ `HermesStatusColors`).
Do **not** "fix" DEFAULT to raw hex to match them — that would break the token
linkage. The asymmetry is semantic, not sloppy.
## Adding a new theme
1. **Create** `presets/<Name>Scheme.kt` exporting the 4 tokens:
```kotlin
package com.m57.hermescontrol.theme.presets
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color
import com.m57.hermescontrol.theme.HermesStatusColors
val MyThemeDarkColorScheme = darkColorScheme(/* … */)
val MyThemeLightColorScheme = lightColorScheme(/* … */)
val MyThemeDarkStatusColors = HermesStatusColors(/* … */)
val MyThemeLightStatusColors = HermesStatusColors(/* … */)
```
2. **Add 2 lines** to each `when` in `Theme.kt` (`resolveColorScheme` +
`resolveStatusColors`), importing the new tokens at the top of the file.
3. **Add the enum entry** `MY_THEME` to `ThemePreset` in `Theme.kt`.
4. **Wire the UI**: add the label + selection in
`ui/settings/components/AppearanceSection.kt` (and any string resource).
5. **Verify**:
```bash
./ktlint --format app/src/main/java/com/m57/hermescontrol/theme/presets/<Name>Scheme.kt
./gradlew compileDebugKotlin
```
## Conventions
- ktlint 1.2.1 is enforced in CI. Run `./ktlint --format` before committing.
- Import order is ASCII-lexicographic (uppercase before lowercase).
- Every change goes through a PR — never push directly to `main`.
@@ -3,15 +3,36 @@ package com.m57.hermescontrol.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import com.m57.hermescontrol.theme.presets.AmoledDarkColorScheme
import com.m57.hermescontrol.theme.presets.AmoledDarkStatusColors
import com.m57.hermescontrol.theme.presets.AmoledLightColorScheme
import com.m57.hermescontrol.theme.presets.AmoledLightStatusColors
import com.m57.hermescontrol.theme.presets.CatppuccinDarkColorScheme
import com.m57.hermescontrol.theme.presets.CatppuccinDarkStatusColors
import com.m57.hermescontrol.theme.presets.CatppuccinLightColorScheme
import com.m57.hermescontrol.theme.presets.CatppuccinLightStatusColors
import com.m57.hermescontrol.theme.presets.DefaultDarkColorScheme
import com.m57.hermescontrol.theme.presets.DefaultDarkStatusColors
import com.m57.hermescontrol.theme.presets.DefaultLightColorScheme
import com.m57.hermescontrol.theme.presets.DefaultLightStatusColors
import com.m57.hermescontrol.theme.presets.GruvboxDarkColorScheme
import com.m57.hermescontrol.theme.presets.GruvboxDarkStatusColors
import com.m57.hermescontrol.theme.presets.GruvboxLightColorScheme
import com.m57.hermescontrol.theme.presets.GruvboxLightStatusColors
import com.m57.hermescontrol.theme.presets.MonochromeDarkColorScheme
import com.m57.hermescontrol.theme.presets.MonochromeDarkStatusColors
import com.m57.hermescontrol.theme.presets.MonochromeLightColorScheme
import com.m57.hermescontrol.theme.presets.MonochromeLightStatusColors
import com.m57.hermescontrol.theme.presets.NeonNoirDarkColorScheme
import com.m57.hermescontrol.theme.presets.NeonNoirDarkStatusColors
import com.m57.hermescontrol.theme.presets.NeonNoirLightColorScheme
import com.m57.hermescontrol.theme.presets.NeonNoirLightStatusColors
import kotlinx.serialization.Serializable
@Serializable
@@ -26,274 +47,41 @@ enum class BottomNavDisplayMode { ICON_AND_TEXT, ICON_ONLY, TEXT_ONLY }
val LocalThemePreference = compositionLocalOf { ThemePreference.SYSTEM }
val LocalThemePreset = compositionLocalOf { ThemePreset.DEFAULT }
private val HermesDarkColorScheme =
darkColorScheme(
primary = HermesPurple,
onPrimary = Color.White,
primaryContainer = HermesPurpleContainer,
onPrimaryContainer = HermesPurpleOnContainer,
secondary = HermesAmber,
onSecondary = Color.Black,
secondaryContainer = Color(0xFF3D2F0F),
onSecondaryContainer = HermesAmberLight,
tertiary = HermesAmberLight,
onTertiary = Color.Black,
background = DarkBackground,
onBackground = DarkOnSurface,
surface = DarkSurface,
onSurface = DarkOnSurface,
surfaceVariant = DarkSurfaceVariant,
onSurfaceVariant = DarkOnSurfaceVariant,
surfaceTint = HermesPurple,
surfaceContainer = DarkSurfaceContainer,
surfaceContainerHigh = DarkSurfaceContainerHigh,
surfaceContainerHighest = DarkSurfaceContainerHighest,
inverseSurface = DarkInverseSurface,
inverseOnSurface = DarkInverseOnSurface,
error = StatusRed,
onError = Color.White,
errorContainer = StatusRedContainer,
onErrorContainer = Color(0xFFFFB4B4),
outline = DarkOutline,
outlineVariant = DarkOutlineVariant,
scrim = DarkScrim,
)
/**
* Resolve the Material 3 [ColorScheme] for a preset + dark flag.
*
* Every preset exports both a dark and light scheme. AMOLED's light scheme is
* an explicit alias of the brand default light scheme.
*/
private fun resolveColorScheme(
preset: ThemePreset,
darkTheme: Boolean,
) = when (preset) {
ThemePreset.DEFAULT -> if (darkTheme) DefaultDarkColorScheme else DefaultLightColorScheme
ThemePreset.MONOCHROME -> if (darkTheme) MonochromeDarkColorScheme else MonochromeLightColorScheme
ThemePreset.GRUVBOX -> if (darkTheme) GruvboxDarkColorScheme else GruvboxLightColorScheme
ThemePreset.CATPPUCCIN -> if (darkTheme) CatppuccinDarkColorScheme else CatppuccinLightColorScheme
ThemePreset.AMOLED -> if (darkTheme) AmoledDarkColorScheme else AmoledLightColorScheme
ThemePreset.NEON_NOIR -> if (darkTheme) NeonNoirDarkColorScheme else NeonNoirLightColorScheme
}
private val HermesLightColorScheme =
lightColorScheme(
primary = HermesPurpleDark,
onPrimary = Color.White,
primaryContainer = HermesPurpleLight,
onPrimaryContainer = Color(0xFF1E0F66),
secondary = HermesAmberDark,
onSecondary = Color.White,
secondaryContainer = HermesAmberLight,
onSecondaryContainer = Color(0xFF3D2F0F),
tertiary = HermesAmberDark,
onTertiary = Color.White,
background = LightBackground,
onBackground = LightOnSurface,
surface = LightSurface,
onSurface = LightOnSurface,
surfaceVariant = LightSurfaceVariant,
onSurfaceVariant = LightOnSurfaceVariant,
surfaceTint = HermesPurple,
surfaceContainer = LightSurfaceContainer,
surfaceContainerHigh = LightSurfaceContainerHigh,
surfaceContainerHighest = LightSurfaceContainerHighest,
inverseSurface = LightInverseSurface,
inverseOnSurface = LightInverseOnSurface,
error = Color(0xFFB3261E),
onError = Color.White,
errorContainer = Color(0xFFF9DEDC),
onErrorContainer = Color(0xFF410E0B),
outline = LightOutline,
outlineVariant = LightOutlineVariant,
scrim = LightScrim,
)
private val MonochromeDarkColorScheme =
darkColorScheme(
primary = Color.White,
onPrimary = Color.Black,
primaryContainer = Color(0xFF2B2B2B),
onPrimaryContainer = Color.White,
secondary = Color(0xFFB0B0B0),
onSecondary = Color.Black,
secondaryContainer = Color(0xFF222222),
onSecondaryContainer = Color(0xFFE0E0E0),
background = Color(0xFF121212),
onBackground = Color(0xFFE0E0E0),
surface = Color(0xFF1E1E1E),
onSurface = Color(0xFFE0E0E0),
surfaceVariant = Color(0xFF2B2B2B),
onSurfaceVariant = Color(0xFFB0B0B0),
outline = Color(0xFF666666),
)
private val MonochromeLightColorScheme =
lightColorScheme(
primary = Color.Black,
onPrimary = Color.White,
primaryContainer = Color(0xFFE0E0E0),
onPrimaryContainer = Color.Black,
secondary = Color(0xFF4A4A4A),
onSecondary = Color.White,
secondaryContainer = Color(0xFFF0F0F0),
onSecondaryContainer = Color(0xFF111111),
background = Color(0xFFFFFFFF),
onBackground = Color(0xFF111111),
surface = Color(0xFFF5F5F5),
onSurface = Color(0xFF111111),
surfaceVariant = Color(0xFFE0E0E0),
onSurfaceVariant = Color(0xFF4A4A4A),
outline = Color(0xFF888888),
)
private val GruvboxDarkColorScheme =
darkColorScheme(
primary = Color(0xFFFE8019),
onPrimary = Color(0xFF282828),
primaryContainer = Color(0xFFD65D0E),
onPrimaryContainer = Color(0xFFFBF1C7),
secondary = Color(0xFFFABD2F),
onSecondary = Color(0xFF282828),
secondaryContainer = Color(0xFF3C3836),
onSecondaryContainer = Color(0xFFBDAE93),
background = Color(0xFF282828),
onBackground = Color(0xFFFBF1C7),
surface = Color(0xFF32302F),
onSurface = Color(0xFFFBF1C7),
surfaceVariant = Color(0xFF3C3836),
onSurfaceVariant = Color(0xFFBDAE93),
outline = Color(0xFF665C54),
)
private val GruvboxLightColorScheme =
lightColorScheme(
primary = Color(0xFFD65D0E),
onPrimary = Color(0xFFFBF1C7),
primaryContainer = Color(0xFFFE8019),
onPrimaryContainer = Color(0xFF282828),
secondary = Color(0xFF458588),
onSecondary = Color(0xFFFBF1C7),
secondaryContainer = Color(0xFFEBDBB2),
onSecondaryContainer = Color(0xFF3C3836),
background = Color(0xFFFBF1C7),
onBackground = Color(0xFF282828),
surface = Color(0xFFF2E5BC),
onSurface = Color(0xFF282828),
surfaceVariant = Color(0xFFEBDBB2),
onSurfaceVariant = Color(0xFF7C6F64),
outline = Color(0xFFA89984),
)
private val CatppuccinDarkColorScheme =
darkColorScheme(
primary = Color(0xFFCBA6F7),
onPrimary = Color(0xFF11111B),
primaryContainer = Color(0xFF585B70),
onPrimaryContainer = Color(0xFFF5E0DC),
secondary = Color(0xFF89B4FA),
onSecondary = Color(0xFF11111B),
secondaryContainer = Color(0xFF313244),
onSecondaryContainer = Color(0xFFBAC2DE),
background = Color(0xFF1E1E2E),
onBackground = Color(0xFFCDD6F4),
surface = Color(0xFF252538),
onSurface = Color(0xFFCDD6F4),
surfaceVariant = Color(0xFF313244),
onSurfaceVariant = Color(0xFFA6ADC8),
outline = Color(0xFF585B70),
)
private val CatppuccinLightColorScheme =
lightColorScheme(
primary = Color(0xFF8839EF),
onPrimary = Color(0xFFEFF1F5),
primaryContainer = Color(0xFFCCD0DA),
onPrimaryContainer = Color(0xFF4C4F69),
secondary = Color(0xFF1E66F5),
onSecondary = Color(0xFFEFF1F5),
secondaryContainer = Color(0xFFE6E9EF),
onSecondaryContainer = Color(0xFF4C4F69),
background = Color(0xFFEFF1F5),
onBackground = Color(0xFF4C4F69),
surface = Color(0xFFE6E9EF),
onSurface = Color(0xFF4C4F69),
surfaceVariant = Color(0xFFCCD0DA),
onSurfaceVariant = Color(0xFF6C6F85),
outline = Color(0xFF9CA0B0),
)
private val AmoledDarkColorScheme =
darkColorScheme(
primary = HermesPurple,
onPrimary = Color.White,
primaryContainer = Color(0xFF1A1040),
onPrimaryContainer = HermesPurpleOnContainer,
secondary = HermesAmber,
onSecondary = Color.Black,
secondaryContainer = Color(0xFF2A2000),
onSecondaryContainer = HermesAmberLight,
background = Color.Black,
onBackground = Color(0xFFE8E6EE),
surface = Color(0xFF08080A),
onSurface = Color(0xFFE8E6EE),
surfaceVariant = Color(0xFF121218),
onSurfaceVariant = Color(0xFFB6B2C4),
outline = Color(0xFF3A3A4A),
)
private val NeonNoirDarkColorScheme =
darkColorScheme(
primary = Color(0xFFFF6BF4),
onPrimary = Color(0xFF1A001A),
primaryContainer = Color(0xFF3A003A),
onPrimaryContainer = Color(0xFFFFCCF2),
secondary = Color(0xFFA78BFA),
onSecondary = Color(0xFF0F0B1A),
secondaryContainer = Color(0xFF2B1F59),
onSecondaryContainer = Color(0xFFD9CCFF),
tertiary = Color(0xFF60F0FF),
onTertiary = Color(0xFF003B40),
tertiaryContainer = Color(0xFF004F59),
onTertiaryContainer = Color(0xFFBFFAFF),
background = Color(0xFF0F0B1A),
onBackground = Color(0xFFE8E0F0),
surface = Color(0xFF1A1530),
onSurface = Color(0xFFE8E0F0),
surfaceVariant = Color(0xFF2B2540),
onSurfaceVariant = Color(0xFF9A90B0),
surfaceTint = Color(0xFFFF6BF4),
surfaceContainer = Color(0xFF1A1530),
surfaceContainerHigh = Color(0xFF252040),
surfaceContainerHighest = Color(0xFF2F2A50),
inverseSurface = Color(0xFFE8E0F0),
inverseOnSurface = Color(0xFF0F0B1A),
error = Color(0xFFFF3D68),
onError = Color.White,
errorContainer = Color(0xFF3A0A14),
onErrorContainer = Color(0xFFFFB3C0),
outline = Color(0xFF3A3060),
outlineVariant = Color(0xFF2E2545),
scrim = Color(0xFF000000),
)
private val NeonNoirLightColorScheme =
lightColorScheme(
primary = Color(0xFFC440A8),
onPrimary = Color.White,
primaryContainer = Color(0xFFFFD9F2),
onPrimaryContainer = Color(0xFF3A003A),
secondary = Color(0xFF7C4DFF),
onSecondary = Color.White,
secondaryContainer = Color(0xFFEDE0FF),
onSecondaryContainer = Color(0xFF1E0066),
tertiary = Color(0xFF0098A6),
onTertiary = Color.White,
tertiaryContainer = Color(0xFFBFFAFF),
onTertiaryContainer = Color(0xFF003B40),
background = Color(0xFFF5F0FA),
onBackground = Color(0xFF0F0B1A),
surface = Color.White,
onSurface = Color(0xFF0F0B1A),
surfaceVariant = Color(0xFFEDE6F5),
onSurfaceVariant = Color(0xFF7A7090),
surfaceTint = Color(0xFFC440A8),
surfaceContainer = Color(0xFFFDF8FF),
surfaceContainerHigh = Color(0xFFF0EDF5),
surfaceContainerHighest = Color(0xFFE6E0ED),
inverseSurface = Color(0xFF0F0B1A),
inverseOnSurface = Color(0xFFE8E0F0),
error = Color(0xFFB3261E),
onError = Color.White,
errorContainer = Color(0xFFF9DEDC),
onErrorContainer = Color(0xFF410E0B),
outline = Color(0xFFBFB8CC),
outlineVariant = Color(0xFFD9D2E3),
scrim = Color(0xFF000000),
)
/**
* Resolve the semantic status colors for a preset + dark flag.
*
* Every preset exports both a dark and light status set. MONOCHROME and AMOLED
* alias the brand default status colors (they have no bespoke semantic set).
*/
private fun resolveStatusColors(
preset: ThemePreset,
darkTheme: Boolean,
) = when (preset) {
ThemePreset.DEFAULT -> if (darkTheme) DefaultDarkStatusColors else DefaultLightStatusColors
ThemePreset.MONOCHROME -> if (darkTheme) MonochromeDarkStatusColors else MonochromeLightStatusColors
ThemePreset.GRUVBOX -> if (darkTheme) GruvboxDarkStatusColors else GruvboxLightStatusColors
ThemePreset.CATPPUCCIN -> if (darkTheme) CatppuccinDarkStatusColors else CatppuccinLightStatusColors
ThemePreset.AMOLED -> if (darkTheme) AmoledDarkStatusColors else AmoledLightStatusColors
ThemePreset.NEON_NOIR -> if (darkTheme) NeonNoirDarkStatusColors else NeonNoirLightStatusColors
}
@Composable
fun HermesControlTheme(
@@ -312,165 +100,17 @@ fun HermesControlTheme(
val context = LocalContext.current
val dynamicAvailable = useDynamicColors && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colorScheme =
when {
dynamicAvailable && darkTheme -> {
if (dynamicAvailable) {
if (darkTheme) {
dynamicDarkColorScheme(context)
}
dynamicAvailable && !darkTheme -> {
} else {
dynamicLightColorScheme(context)
}
else -> {
when (themePreset) {
ThemePreset.DEFAULT -> if (darkTheme) HermesDarkColorScheme else HermesLightColorScheme
ThemePreset.MONOCHROME -> if (darkTheme) MonochromeDarkColorScheme else MonochromeLightColorScheme
ThemePreset.GRUVBOX -> if (darkTheme) GruvboxDarkColorScheme else GruvboxLightColorScheme
ThemePreset.CATPPUCCIN -> if (darkTheme) CatppuccinDarkColorScheme else CatppuccinLightColorScheme
ThemePreset.AMOLED -> if (darkTheme) AmoledDarkColorScheme else HermesLightColorScheme
ThemePreset.NEON_NOIR -> if (darkTheme) NeonNoirDarkColorScheme else NeonNoirLightColorScheme
}
}
} else {
resolveColorScheme(themePreset, darkTheme)
}
val statusColors =
when {
themePreset == ThemePreset.GRUVBOX && darkTheme -> {
HermesStatusColors(
success = Color(0xFFB8BB26),
successContainer = Color(0xFF32302F),
onSuccess = Color(0xFF282828),
warning = Color(0xFFFABD2F),
warningContainer = Color(0xFF3C3836),
onWarning = Color(0xFF282828),
error = Color(0xFFFB4934),
errorContainer = Color(0xFF282828),
onError = Color(0xFF282828),
info = Color(0xFF83A598),
infoContainer = Color(0xFF3C3836),
onInfo = Color(0xFF282828),
)
}
themePreset == ThemePreset.GRUVBOX && !darkTheme -> {
HermesStatusColors(
success = Color(0xFF98971A),
successContainer = Color(0xFFEBDBB2),
onSuccess = Color(0xFFEBDBB2),
warning = Color(0xFFD79921),
warningContainer = Color(0xFFEBDBB2),
onWarning = Color(0xFFEBDBB2),
error = Color(0xFFCC241D),
errorContainer = Color(0xFFEBDBB2),
onError = Color(0xFFEBDBB2),
info = Color(0xFF458588),
infoContainer = Color(0xFFEBDBB2),
onInfo = Color(0xFFEBDBB2),
)
}
themePreset == ThemePreset.CATPPUCCIN && darkTheme -> {
HermesStatusColors(
success = Color(0xFFA6E3A1),
successContainer = Color(0xFF313244),
onSuccess = Color(0xFF11111B),
warning = Color(0xFFF9E2AF),
warningContainer = Color(0xFF313244),
onWarning = Color(0xFF11111B),
error = Color(0xFFF38BA8),
errorContainer = Color(0xFF313244),
onError = Color(0xFF11111B),
info = Color(0xFF89B4FA),
infoContainer = Color(0xFF313244),
onInfo = Color(0xFF11111B),
)
}
themePreset == ThemePreset.CATPPUCCIN && !darkTheme -> {
HermesStatusColors(
success = Color(0xFF40A02B),
successContainer = Color(0xFFE6E9EF),
onSuccess = Color(0xFFE6E9EF),
warning = Color(0xFFDF8E1D),
warningContainer = Color(0xFFE6E9EF),
onWarning = Color(0xFFE6E9EF),
error = Color(0xFFD20F39),
errorContainer = Color(0xFFE6E9EF),
onError = Color(0xFFE6E9EF),
info = Color(0xFF1E66F5),
infoContainer = Color(0xFFE6E9EF),
onInfo = Color(0xFFE6E9EF),
)
}
themePreset == ThemePreset.NEON_NOIR && darkTheme -> {
HermesStatusColors(
success = Color(0xFF6FEA8B),
successContainer = Color(0xFF0A3A14),
onSuccess = Color(0xFF0F0B1A),
warning = Color(0xFFFFB347),
warningContainer = Color(0xFF3A2000),
onWarning = Color(0xFF0F0B1A),
error = Color(0xFFFF3D68),
errorContainer = Color(0xFF3A0A14),
onError = Color(0xFF0F0B1A),
info = Color(0xFF60F0FF),
infoContainer = Color(0xFF003B40),
onInfo = Color(0xFF0F0B1A),
)
}
themePreset == ThemePreset.NEON_NOIR && !darkTheme -> {
HermesStatusColors(
success = Color(0xFF1B873A),
successContainer = Color(0xFFD7F5E0),
onSuccess = Color(0xFFFDF8FF),
warning = Color(0xFFB87800),
warningContainer = Color(0xFFFFEAB3),
onWarning = Color(0xFFFDF8FF),
error = Color(0xFFB3261E),
errorContainer = Color(0xFFF9DEDC),
onError = Color(0xFFFDF8FF),
info = Color(0xFF2E6FBD),
infoContainer = Color(0xFFD4E7FF),
onInfo = Color(0xFFFDF8FF),
)
}
darkTheme -> {
HermesStatusColors(
success = StatusGreen,
successContainer = StatusGreenContainer,
onSuccess = StatusGreyLight,
warning = StatusYellow,
warningContainer = StatusYellowContainer,
onWarning = StatusGreyLight,
error = StatusRed,
errorContainer = StatusRedContainer,
onError = StatusGreyLight,
info = StatusBlue,
infoContainer = StatusBlueContainer,
onInfo = StatusGreyLight,
)
}
else -> {
HermesStatusColors(
success = Color(0xFF1B873A),
successContainer = Color(0xFFD7F5E0),
onSuccess = Color(0xFFFDF8FF),
warning = HermesAmberDark,
warningContainer = Color(0xFFFFEAB3),
onWarning = Color(0xFFFDF8FF),
error = Color(0xFFB3261E),
errorContainer = Color(0xFFF9DEDC),
onError = Color(0xFFFDF8FF),
info = Color(0xFF2E6FBD),
infoContainer = Color(0xFFD4E7FF),
onInfo = Color(0xFFFDF8FF),
)
}
}
val statusColors = resolveStatusColors(themePreset, darkTheme)
CompositionLocalProvider(
LocalThemePreference provides themePreference,
@@ -0,0 +1,44 @@
package com.m57.hermescontrol.theme.presets
import androidx.compose.material3.darkColorScheme
import androidx.compose.ui.graphics.Color
import com.m57.hermescontrol.theme.HermesAmber
import com.m57.hermescontrol.theme.HermesAmberLight
import com.m57.hermescontrol.theme.HermesPurple
import com.m57.hermescontrol.theme.HermesPurpleOnContainer
/**
* AMOLED color scheme (dark only).
*
* AMOLED has no light variant — callers fall back to the brand default light
* scheme for light mode.
*/
val AmoledDarkColorScheme =
darkColorScheme(
primary = HermesPurple,
onPrimary = Color.White,
primaryContainer = Color(0xFF1A1040),
onPrimaryContainer = HermesPurpleOnContainer,
secondary = HermesAmber,
onSecondary = Color.Black,
secondaryContainer = Color(0xFF2A2000),
onSecondaryContainer = HermesAmberLight,
background = Color.Black,
onBackground = Color(0xFFE8E6EE),
surface = Color(0xFF08080A),
onSurface = Color(0xFFE8E6EE),
surfaceVariant = Color(0xFF121218),
onSurfaceVariant = Color(0xFFB6B2C4),
outline = Color(0xFF3A3A4A),
)
/**
* AMOLED has no bespoke light scheme — reuse the brand default light scheme.
*/
val AmoledLightColorScheme = DefaultLightColorScheme
/** AMOLED status colors (dark) — reuses the brand default set. */
val AmoledDarkStatusColors = DefaultDarkStatusColors
/** AMOLED status colors (light) — reuses the brand default set. */
val AmoledLightStatusColors = DefaultLightStatusColors
@@ -0,0 +1,80 @@
package com.m57.hermescontrol.theme.presets
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color
import com.m57.hermescontrol.theme.HermesStatusColors
/** Catppuccin color scheme (dark). */
val CatppuccinDarkColorScheme =
darkColorScheme(
primary = Color(0xFFCBA6F7),
onPrimary = Color(0xFF11111B),
primaryContainer = Color(0xFF585B70),
onPrimaryContainer = Color(0xFFF5E0DC),
secondary = Color(0xFF89B4FA),
onSecondary = Color(0xFF11111B),
secondaryContainer = Color(0xFF313244),
onSecondaryContainer = Color(0xFFBAC2DE),
background = Color(0xFF1E1E2E),
onBackground = Color(0xFFCDD6F4),
surface = Color(0xFF252538),
onSurface = Color(0xFFCDD6F4),
surfaceVariant = Color(0xFF313244),
onSurfaceVariant = Color(0xFFA6ADC8),
outline = Color(0xFF585B70),
)
/** Catppuccin color scheme (light). */
val CatppuccinLightColorScheme =
lightColorScheme(
primary = Color(0xFF8839EF),
onPrimary = Color(0xFFEFF1F5),
primaryContainer = Color(0xFFCCD0DA),
onPrimaryContainer = Color(0xFF4C4F69),
secondary = Color(0xFF1E66F5),
onSecondary = Color(0xFFEFF1F5),
secondaryContainer = Color(0xFFE6E9EF),
onSecondaryContainer = Color(0xFF4C4F69),
background = Color(0xFFEFF1F5),
onBackground = Color(0xFF4C4F69),
surface = Color(0xFFE6E9EF),
onSurface = Color(0xFF4C4F69),
surfaceVariant = Color(0xFFCCD0DA),
onSurfaceVariant = Color(0xFF6C6F85),
outline = Color(0xFF9CA0B0),
)
/** Catppuccin status colors (dark). */
val CatppuccinDarkStatusColors =
HermesStatusColors(
success = Color(0xFFA6E3A1),
successContainer = Color(0xFF313244),
onSuccess = Color(0xFF11111B),
warning = Color(0xFFF9E2AF),
warningContainer = Color(0xFF313244),
onWarning = Color(0xFF11111B),
error = Color(0xFFF38BA8),
errorContainer = Color(0xFF313244),
onError = Color(0xFF11111B),
info = Color(0xFF89B4FA),
infoContainer = Color(0xFF313244),
onInfo = Color(0xFF11111B),
)
/** Catppuccin status colors (light). */
val CatppuccinLightStatusColors =
HermesStatusColors(
success = Color(0xFF40A02B),
successContainer = Color(0xFFE6E9EF),
onSuccess = Color(0xFFE6E9EF),
warning = Color(0xFFDF8E1D),
warningContainer = Color(0xFFE6E9EF),
onWarning = Color(0xFFE6E9EF),
error = Color(0xFFD20F39),
errorContainer = Color(0xFFE6E9EF),
onError = Color(0xFFE6E9EF),
info = Color(0xFF1E66F5),
infoContainer = Color(0xFFE6E9EF),
onInfo = Color(0xFFE6E9EF),
)
@@ -0,0 +1,151 @@
package com.m57.hermescontrol.theme.presets
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color
import com.m57.hermescontrol.theme.DarkBackground
import com.m57.hermescontrol.theme.DarkInverseOnSurface
import com.m57.hermescontrol.theme.DarkInverseSurface
import com.m57.hermescontrol.theme.DarkOnSurface
import com.m57.hermescontrol.theme.DarkOnSurfaceVariant
import com.m57.hermescontrol.theme.DarkOutline
import com.m57.hermescontrol.theme.DarkOutlineVariant
import com.m57.hermescontrol.theme.DarkScrim
import com.m57.hermescontrol.theme.DarkSurface
import com.m57.hermescontrol.theme.DarkSurfaceContainer
import com.m57.hermescontrol.theme.DarkSurfaceContainerHigh
import com.m57.hermescontrol.theme.DarkSurfaceContainerHighest
import com.m57.hermescontrol.theme.DarkSurfaceVariant
import com.m57.hermescontrol.theme.HermesAmber
import com.m57.hermescontrol.theme.HermesAmberDark
import com.m57.hermescontrol.theme.HermesAmberLight
import com.m57.hermescontrol.theme.HermesPurple
import com.m57.hermescontrol.theme.HermesPurpleContainer
import com.m57.hermescontrol.theme.HermesPurpleDark
import com.m57.hermescontrol.theme.HermesPurpleLight
import com.m57.hermescontrol.theme.HermesPurpleOnContainer
import com.m57.hermescontrol.theme.HermesStatusColors
import com.m57.hermescontrol.theme.LightBackground
import com.m57.hermescontrol.theme.LightInverseOnSurface
import com.m57.hermescontrol.theme.LightInverseSurface
import com.m57.hermescontrol.theme.LightOnSurface
import com.m57.hermescontrol.theme.LightOnSurfaceVariant
import com.m57.hermescontrol.theme.LightOutline
import com.m57.hermescontrol.theme.LightOutlineVariant
import com.m57.hermescontrol.theme.LightScrim
import com.m57.hermescontrol.theme.LightSurface
import com.m57.hermescontrol.theme.LightSurfaceContainer
import com.m57.hermescontrol.theme.LightSurfaceContainerHigh
import com.m57.hermescontrol.theme.LightSurfaceContainerHighest
import com.m57.hermescontrol.theme.LightSurfaceVariant
import com.m57.hermescontrol.theme.StatusBlue
import com.m57.hermescontrol.theme.StatusBlueContainer
import com.m57.hermescontrol.theme.StatusGreen
import com.m57.hermescontrol.theme.StatusGreenContainer
import com.m57.hermescontrol.theme.StatusGreyLight
import com.m57.hermescontrol.theme.StatusRed
import com.m57.hermescontrol.theme.StatusRedContainer
import com.m57.hermescontrol.theme.StatusYellow
import com.m57.hermescontrol.theme.StatusYellowContainer
/** Brand default color scheme (dark) — Voltage Purple primary. */
val DefaultDarkColorScheme =
darkColorScheme(
primary = HermesPurple,
onPrimary = Color.White,
primaryContainer = HermesPurpleContainer,
onPrimaryContainer = HermesPurpleOnContainer,
secondary = HermesAmber,
onSecondary = Color.Black,
secondaryContainer = Color(0xFF3D2F0F),
onSecondaryContainer = HermesAmberLight,
tertiary = HermesAmberLight,
onTertiary = Color.Black,
background = DarkBackground,
onBackground = DarkOnSurface,
surface = DarkSurface,
onSurface = DarkOnSurface,
surfaceVariant = DarkSurfaceVariant,
onSurfaceVariant = DarkOnSurfaceVariant,
surfaceTint = HermesPurple,
surfaceContainer = DarkSurfaceContainer,
surfaceContainerHigh = DarkSurfaceContainerHigh,
surfaceContainerHighest = DarkSurfaceContainerHighest,
inverseSurface = DarkInverseSurface,
inverseOnSurface = DarkInverseOnSurface,
error = StatusRed,
onError = Color.White,
errorContainer = StatusRedContainer,
onErrorContainer = Color(0xFFFFB4B4),
outline = DarkOutline,
outlineVariant = DarkOutlineVariant,
scrim = DarkScrim,
)
/** Brand default color scheme (light) — Voltage Purple primary. */
val DefaultLightColorScheme =
lightColorScheme(
primary = HermesPurpleDark,
onPrimary = Color.White,
primaryContainer = HermesPurpleLight,
onPrimaryContainer = Color(0xFF1E0F66),
secondary = HermesAmberDark,
onSecondary = Color.White,
secondaryContainer = HermesAmberLight,
onSecondaryContainer = Color(0xFF3D2F0F),
tertiary = HermesAmberDark,
onTertiary = Color.White,
background = LightBackground,
onBackground = LightOnSurface,
surface = LightSurface,
onSurface = LightOnSurface,
surfaceVariant = LightSurfaceVariant,
onSurfaceVariant = LightOnSurfaceVariant,
surfaceTint = HermesPurple,
surfaceContainer = LightSurfaceContainer,
surfaceContainerHigh = LightSurfaceContainerHigh,
surfaceContainerHighest = LightSurfaceContainerHighest,
inverseSurface = LightInverseSurface,
inverseOnSurface = LightInverseOnSurface,
error = Color(0xFFB3261E),
onError = Color.White,
errorContainer = Color(0xFFF9DEDC),
onErrorContainer = Color(0xFF410E0B),
outline = LightOutline,
outlineVariant = LightOutlineVariant,
scrim = LightScrim,
)
/** Default status colors (dark). */
val DefaultDarkStatusColors =
HermesStatusColors(
success = StatusGreen,
successContainer = StatusGreenContainer,
onSuccess = StatusGreyLight,
warning = StatusYellow,
warningContainer = StatusYellowContainer,
onWarning = StatusGreyLight,
error = StatusRed,
errorContainer = StatusRedContainer,
onError = StatusGreyLight,
info = StatusBlue,
infoContainer = StatusBlueContainer,
onInfo = StatusGreyLight,
)
/** Default status colors (light). */
val DefaultLightStatusColors =
HermesStatusColors(
success = Color(0xFF1B873A),
successContainer = Color(0xFFD7F5E0),
onSuccess = Color(0xFFFDF8FF),
warning = HermesAmberDark,
warningContainer = Color(0xFFFFEAB3),
onWarning = Color(0xFFFDF8FF),
error = Color(0xFFB3261E),
errorContainer = Color(0xFFF9DEDC),
onError = Color(0xFFFDF8FF),
info = Color(0xFF2E6FBD),
infoContainer = Color(0xFFD4E7FF),
onInfo = Color(0xFFFDF8FF),
)
@@ -0,0 +1,80 @@
package com.m57.hermescontrol.theme.presets
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color
import com.m57.hermescontrol.theme.HermesStatusColors
/** Gruvbox color scheme (dark). */
val GruvboxDarkColorScheme =
darkColorScheme(
primary = Color(0xFFFE8019),
onPrimary = Color(0xFF282828),
primaryContainer = Color(0xFFD65D0E),
onPrimaryContainer = Color(0xFFFBF1C7),
secondary = Color(0xFFFABD2F),
onSecondary = Color(0xFF282828),
secondaryContainer = Color(0xFF3C3836),
onSecondaryContainer = Color(0xFFBDAE93),
background = Color(0xFF282828),
onBackground = Color(0xFFFBF1C7),
surface = Color(0xFF32302F),
onSurface = Color(0xFFFBF1C7),
surfaceVariant = Color(0xFF3C3836),
onSurfaceVariant = Color(0xFFBDAE93),
outline = Color(0xFF665C54),
)
/** Gruvbox color scheme (light). */
val GruvboxLightColorScheme =
lightColorScheme(
primary = Color(0xFFD65D0E),
onPrimary = Color(0xFFFBF1C7),
primaryContainer = Color(0xFFFE8019),
onPrimaryContainer = Color(0xFF282828),
secondary = Color(0xFF458588),
onSecondary = Color(0xFFFBF1C7),
secondaryContainer = Color(0xFFEBDBB2),
onSecondaryContainer = Color(0xFF3C3836),
background = Color(0xFFFBF1C7),
onBackground = Color(0xFF282828),
surface = Color(0xFFF2E5BC),
onSurface = Color(0xFF282828),
surfaceVariant = Color(0xFFEBDBB2),
onSurfaceVariant = Color(0xFF7C6F64),
outline = Color(0xFFA89984),
)
/** Gruvbox status colors (dark). */
val GruvboxDarkStatusColors =
HermesStatusColors(
success = Color(0xFFB8BB26),
successContainer = Color(0xFF32302F),
onSuccess = Color(0xFF282828),
warning = Color(0xFFFABD2F),
warningContainer = Color(0xFF3C3836),
onWarning = Color(0xFF282828),
error = Color(0xFFFB4934),
errorContainer = Color(0xFF282828),
onError = Color(0xFF282828),
info = Color(0xFF83A598),
infoContainer = Color(0xFF3C3836),
onInfo = Color(0xFF282828),
)
/** Gruvbox status colors (light). */
val GruvboxLightStatusColors =
HermesStatusColors(
success = Color(0xFF98971A),
successContainer = Color(0xFFEBDBB2),
onSuccess = Color(0xFFEBDBB2),
warning = Color(0xFFD79921),
warningContainer = Color(0xFFEBDBB2),
onWarning = Color(0xFFEBDBB2),
error = Color(0xFFCC241D),
errorContainer = Color(0xFFEBDBB2),
onError = Color(0xFFEBDBB2),
info = Color(0xFF458588),
infoContainer = Color(0xFFEBDBB2),
onInfo = Color(0xFFEBDBB2),
)
@@ -0,0 +1,51 @@
package com.m57.hermescontrol.theme.presets
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color
/** Monochrome color scheme (dark). */
val MonochromeDarkColorScheme =
darkColorScheme(
primary = Color.White,
onPrimary = Color.Black,
primaryContainer = Color(0xFF2B2B2B),
onPrimaryContainer = Color.White,
secondary = Color(0xFFB0B0B0),
onSecondary = Color.Black,
secondaryContainer = Color(0xFF222222),
onSecondaryContainer = Color(0xFFE0E0E0),
background = Color(0xFF121212),
onBackground = Color(0xFFE0E0E0),
surface = Color(0xFF1E1E1E),
onSurface = Color(0xFFE0E0E0),
surfaceVariant = Color(0xFF2B2B2B),
onSurfaceVariant = Color(0xFFB0B0B0),
outline = Color(0xFF666666),
)
/** Monochrome color scheme (light). */
val MonochromeLightColorScheme =
lightColorScheme(
primary = Color.Black,
onPrimary = Color.White,
primaryContainer = Color(0xFFE0E0E0),
onPrimaryContainer = Color.Black,
secondary = Color(0xFF4A4A4A),
onSecondary = Color.White,
secondaryContainer = Color(0xFFF0F0F0),
onSecondaryContainer = Color(0xFF111111),
background = Color(0xFFFFFFFF),
onBackground = Color(0xFF111111),
surface = Color(0xFFF5F5F5),
onSurface = Color(0xFF111111),
surfaceVariant = Color(0xFFE0E0E0),
onSurfaceVariant = Color(0xFF4A4A4A),
outline = Color(0xFF888888),
)
/** Monochrome status colors (dark) — reuses the brand default set. */
val MonochromeDarkStatusColors = DefaultDarkStatusColors
/** Monochrome status colors (light) — reuses the brand default set. */
val MonochromeLightStatusColors = DefaultLightStatusColors
@@ -0,0 +1,112 @@
package com.m57.hermescontrol.theme.presets
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color
import com.m57.hermescontrol.theme.HermesStatusColors
/** Neon Noir color scheme (dark). */
val NeonNoirDarkColorScheme =
darkColorScheme(
primary = Color(0xFFFF6BF4),
onPrimary = Color(0xFF1A001A),
primaryContainer = Color(0xFF3A003A),
onPrimaryContainer = Color(0xFFFFCCF2),
secondary = Color(0xFFA78BFA),
onSecondary = Color(0xFF0F0B1A),
secondaryContainer = Color(0xFF2B1F59),
onSecondaryContainer = Color(0xFFD9CCFF),
tertiary = Color(0xFF60F0FF),
onTertiary = Color(0xFF003B40),
tertiaryContainer = Color(0xFF004F59),
onTertiaryContainer = Color(0xFFBFFAFF),
background = Color(0xFF0F0B1A),
onBackground = Color(0xFFE8E0F0),
surface = Color(0xFF1A1530),
onSurface = Color(0xFFE8E0F0),
surfaceVariant = Color(0xFF2B2540),
onSurfaceVariant = Color(0xFF9A90B0),
surfaceTint = Color(0xFFFF6BF4),
surfaceContainer = Color(0xFF1A1530),
surfaceContainerHigh = Color(0xFF252040),
surfaceContainerHighest = Color(0xFF2F2A50),
inverseSurface = Color(0xFFE8E0F0),
inverseOnSurface = Color(0xFF0F0B1A),
error = Color(0xFFFF3D68),
onError = Color.White,
errorContainer = Color(0xFF3A0A14),
onErrorContainer = Color(0xFFFFB3C0),
outline = Color(0xFF3A3060),
outlineVariant = Color(0xFF2E2545),
scrim = Color(0xFF000000),
)
/** Neon Noir color scheme (light). */
val NeonNoirLightColorScheme =
lightColorScheme(
primary = Color(0xFFC440A8),
onPrimary = Color.White,
primaryContainer = Color(0xFFFFD9F2),
onPrimaryContainer = Color(0xFF3A003A),
secondary = Color(0xFF7C4DFF),
onSecondary = Color.White,
secondaryContainer = Color(0xFFEDE0FF),
onSecondaryContainer = Color(0xFF1E0066),
tertiary = Color(0xFF0098A6),
onTertiary = Color.White,
tertiaryContainer = Color(0xFFBFFAFF),
onTertiaryContainer = Color(0xFF003B40),
background = Color(0xFFF5F0FA),
onBackground = Color(0xFF0F0B1A),
surface = Color.White,
onSurface = Color(0xFF0F0B1A),
surfaceVariant = Color(0xFFEDE6F5),
onSurfaceVariant = Color(0xFF7A7090),
surfaceTint = Color(0xFFC440A8),
surfaceContainer = Color(0xFFFDF8FF),
surfaceContainerHigh = Color(0xFFF0EDF5),
surfaceContainerHighest = Color(0xFFE6E0ED),
inverseSurface = Color(0xFF0F0B1A),
inverseOnSurface = Color(0xFFE8E0F0),
error = Color(0xFFB3261E),
onError = Color.White,
errorContainer = Color(0xFFF9DEDC),
onErrorContainer = Color(0xFF410E0B),
outline = Color(0xFFBFB8CC),
outlineVariant = Color(0xFFD9D2E3),
scrim = Color(0xFF000000),
)
/** Neon Noir status colors (dark). */
val NeonNoirDarkStatusColors =
HermesStatusColors(
success = Color(0xFF6FEA8B),
successContainer = Color(0xFF0A3A14),
onSuccess = Color(0xFF0F0B1A),
warning = Color(0xFFFFB347),
warningContainer = Color(0xFF3A2000),
onWarning = Color(0xFF0F0B1A),
error = Color(0xFFFF3D68),
errorContainer = Color(0xFF3A0A14),
onError = Color(0xFF0F0B1A),
info = Color(0xFF60F0FF),
infoContainer = Color(0xFF003B40),
onInfo = Color(0xFF0F0B1A),
)
/** Neon Noir status colors (light). */
val NeonNoirLightStatusColors =
HermesStatusColors(
success = Color(0xFF1B873A),
successContainer = Color(0xFFD7F5E0),
onSuccess = Color(0xFFFDF8FF),
warning = Color(0xFFB87800),
warningContainer = Color(0xFFFFEAB3),
onWarning = Color(0xFFFDF8FF),
error = Color(0xFFB3261E),
errorContainer = Color(0xFFF9DEDC),
onError = Color(0xFFFDF8FF),
info = Color(0xFF2E6FBD),
infoContainer = Color(0xFFD4E7FF),
onInfo = Color(0xFFFDF8FF),
)