fix(theme): complete color and component contrast contracts (#1417)
This commit is contained in:
22 files changed
+1013
-318
No files matched your search
@@ -10,8 +10,13 @@ import androidx.compose.ui.graphics.luminance
|
||||
* Material You changes the *primary* / *surface* palette based on the user's
|
||||
* wallpaper, but success / warning / error / info colours must stay
|
||||
* semantically correct (green = good, red = bad) regardless of what the
|
||||
* wallpaper-derived palette would choose. This struct is provided via
|
||||
* [LocalHermesStatusColors] and should be used for all status indicators,
|
||||
* wallpaper-derived palette would choose.
|
||||
*
|
||||
* `onSuccess`/`onWarning`/`onError`/`onInfo` are foregrounds on the matching
|
||||
* status fills, not on their container colors. Reusable status badges use
|
||||
* these fill/on pairs; error-container content uses `onErrorContainer`.
|
||||
*
|
||||
* Use [LocalHermesStatusColors] for all status indicators,
|
||||
* badges, and semantic feedback colours.
|
||||
*/
|
||||
data class HermesStatusColors(
|
||||
|
||||
@@ -28,6 +28,9 @@ enum class ThemeMode { FULL, DARK_ONLY, LIGHT_ONLY }
|
||||
*
|
||||
* The Material error slots are derived from [status] — the theme author
|
||||
* defines semantic colors once and the scheme inherits them.
|
||||
* [primary] and the status error are also used directly for text on surfaces.
|
||||
* [outline] must contrast with [surface] and [surfaceContainerHighest], which
|
||||
* Material uses as the unchecked switch track.
|
||||
*/
|
||||
data class PaletteColors(
|
||||
val primary: Color,
|
||||
@@ -48,6 +51,20 @@ data class PaletteColors(
|
||||
val onSurface: Color,
|
||||
val surfaceVariant: Color,
|
||||
val onSurfaceVariant: Color,
|
||||
val surfaceDim: Color,
|
||||
val surfaceBright: Color,
|
||||
val primaryFixed: Color,
|
||||
val primaryFixedDim: Color,
|
||||
val onPrimaryFixed: Color,
|
||||
val onPrimaryFixedVariant: Color,
|
||||
val secondaryFixed: Color,
|
||||
val secondaryFixedDim: Color,
|
||||
val onSecondaryFixed: Color,
|
||||
val onSecondaryFixedVariant: Color,
|
||||
val tertiaryFixed: Color,
|
||||
val tertiaryFixedDim: Color,
|
||||
val onTertiaryFixed: Color,
|
||||
val onTertiaryFixedVariant: Color,
|
||||
val surfaceContainerLowest: Color,
|
||||
val surfaceContainerLow: Color,
|
||||
val surfaceContainer: Color,
|
||||
@@ -123,6 +140,20 @@ private fun darkSchemeOf(c: PaletteColors): ColorScheme =
|
||||
surfaceVariant = c.surfaceVariant,
|
||||
onSurfaceVariant = c.onSurfaceVariant,
|
||||
surfaceTint = c.primary,
|
||||
surfaceDim = c.surfaceDim,
|
||||
surfaceBright = c.surfaceBright,
|
||||
primaryFixed = c.primaryFixed,
|
||||
primaryFixedDim = c.primaryFixedDim,
|
||||
onPrimaryFixed = c.onPrimaryFixed,
|
||||
onPrimaryFixedVariant = c.onPrimaryFixedVariant,
|
||||
secondaryFixed = c.secondaryFixed,
|
||||
secondaryFixedDim = c.secondaryFixedDim,
|
||||
onSecondaryFixed = c.onSecondaryFixed,
|
||||
onSecondaryFixedVariant = c.onSecondaryFixedVariant,
|
||||
tertiaryFixed = c.tertiaryFixed,
|
||||
tertiaryFixedDim = c.tertiaryFixedDim,
|
||||
onTertiaryFixed = c.onTertiaryFixed,
|
||||
onTertiaryFixedVariant = c.onTertiaryFixedVariant,
|
||||
surfaceContainerLowest = c.surfaceContainerLowest,
|
||||
surfaceContainerLow = c.surfaceContainerLow,
|
||||
surfaceContainer = c.surfaceContainer,
|
||||
@@ -161,6 +192,20 @@ private fun lightSchemeOf(c: PaletteColors): ColorScheme =
|
||||
surfaceVariant = c.surfaceVariant,
|
||||
onSurfaceVariant = c.onSurfaceVariant,
|
||||
surfaceTint = c.primary,
|
||||
surfaceDim = c.surfaceDim,
|
||||
surfaceBright = c.surfaceBright,
|
||||
primaryFixed = c.primaryFixed,
|
||||
primaryFixedDim = c.primaryFixedDim,
|
||||
onPrimaryFixed = c.onPrimaryFixed,
|
||||
onPrimaryFixedVariant = c.onPrimaryFixedVariant,
|
||||
secondaryFixed = c.secondaryFixed,
|
||||
secondaryFixedDim = c.secondaryFixedDim,
|
||||
onSecondaryFixed = c.onSecondaryFixed,
|
||||
onSecondaryFixedVariant = c.onSecondaryFixedVariant,
|
||||
tertiaryFixed = c.tertiaryFixed,
|
||||
tertiaryFixedDim = c.tertiaryFixedDim,
|
||||
onTertiaryFixed = c.onTertiaryFixed,
|
||||
onTertiaryFixedVariant = c.onTertiaryFixedVariant,
|
||||
surfaceContainerLowest = c.surfaceContainerLowest,
|
||||
surfaceContainerLow = c.surfaceContainerLow,
|
||||
surfaceContainer = c.surfaceContainer,
|
||||
|
||||
@@ -12,7 +12,8 @@ All theme code is under:
|
||||
|
||||
```
|
||||
app/src/main/java/com/m57/hermescontrol/theme/
|
||||
├── Theme.kt # dispatcher — preset lookup + mode fallback
|
||||
├── Theme.kt # dispatcher — mode fallback + dynamic colors
|
||||
├── ThemeRegistry.kt # single preset/palette registry
|
||||
├── PaletteTemplate.kt # the template — the ONE shape every theme follows
|
||||
├── Color.kt # shared tokens for non-theme code (status fallbacks, code blocks, …)
|
||||
├── HermesStatusColors.kt # semantic status color model (success/warning/error/info + on*)
|
||||
@@ -55,7 +56,14 @@ val MyTheme =
|
||||
The template maps `PaletteColors` into the Material 3 `ColorScheme` slots. The
|
||||
Material `error` slots (`error`, `onError`, `errorContainer`, `onErrorContainer`)
|
||||
are **derived** from the theme's status set — the theme author defines semantic
|
||||
colors once.
|
||||
colors once. `surfaceTint` comes from `primary`. Every other Material color role
|
||||
is required explicitly, including bright/dim surfaces and all fixed accent roles.
|
||||
No role may inherit Material baseline colors. Fixed accent and foreground roles
|
||||
stay identical across a preset’s shipped modes; Fixed/Dim may share an official
|
||||
swatch when the source palette has no accessible tonal ladder.
|
||||
|
||||
Prefer upstream swatches. Document every derived color beside its declaration
|
||||
with the official pairing’s measured contrast and why a derived role is needed.
|
||||
|
||||
## Preset conventions
|
||||
|
||||
@@ -69,8 +77,33 @@ colors once.
|
||||
light text would fail contrast.
|
||||
- **Grayscale status colors** (Monochrome, AMOLED) separate success/warning/
|
||||
error/info by lightness only — consumers must pair with icons or labels.
|
||||
- Every shipped mode's error slot pairs are enforced at **>= 3:1 contrast** by
|
||||
`ThemePaletteTest` — a preset that breaks this fails the unit-test gate.
|
||||
- Normal-text foreground/background pairs, including accent containers, fixed
|
||||
accents, semantic status fills, errors, and the surface ladder, are enforced at **>= 4.5:1 contrast**
|
||||
by `ThemePaletteTest`. The separate full-bleed header guard remains >= 3:1.
|
||||
|
||||
Reusable `StatusBadge` and `SessionLiveStatusIndicator` use a shared
|
||||
`statusBadgeColors` mapping: status fills (`success`, `warning`, `error`, `info`)
|
||||
with their matching `on*` text/icons. `onSuccess` is not a foreground token for
|
||||
`successContainer`. Palette tests exercise this exact renderer mapping in every
|
||||
resolved mode, including fallback modes; neutral badges use Material tokens.
|
||||
|
||||
Enabled Material components also have a component-state contract:
|
||||
- Graphics/boundaries target >= 3:1: switch thumbs/tracks and borders, radio and
|
||||
checkbox states, slider active/inactive tracks, text-field borders, and segmented borders.
|
||||
- Primary/error text and field labels on surface/background target >= 4.5:1.
|
||||
- Segmented content uses the corresponding container foreground at >= 4.5:1.
|
||||
|
||||
`ThemeComponentContrastTest` checks the pinned Material role combinations in every
|
||||
resolved preset/mode. `ThemeComponentGalleryTest` reads the installed Compose
|
||||
public color defaults and verifies rendered pixels for all twelve preset/mode
|
||||
combinations, including focused/error fields. Disabled controls are deliberately
|
||||
excluded from the enabled contrast contract; Material uses opacity to mute them.
|
||||
Settings switches use Material defaults (`primary` track / `onPrimary` thumb),
|
||||
not the reversed `primaryContainer` track / `primary` thumb combination.
|
||||
|
||||
Outlines must contrast against both surface and the unchecked switch track
|
||||
(`surfaceContainerHighest`). Named palettes may need documented derived primary
|
||||
or error tones for direct text; fixed roles still preserve their upstream accents.
|
||||
|
||||
## Theme modes
|
||||
|
||||
@@ -87,37 +120,33 @@ doesn't ship — never to a sibling preset.
|
||||
|
||||
## The dispatcher
|
||||
|
||||
`Theme.kt` is a **pure lookup** — no special-casing. Given a `ThemePreset` and
|
||||
a dark flag, `themeFor()` returns the preset's `ThemePalette`, then the scheme /
|
||||
status are read off it (with the mode fallback above):
|
||||
`ThemeRegistry.kt` maps every `ThemePreset` to one `ThemePalette`. Resolution,
|
||||
UI selection, and palette tests use this registry. An exact enum/registry coverage
|
||||
test rejects missing, duplicate, or reordered entries. Android string resources
|
||||
stay in the UI’s exhaustive label mapping, outside the palette model.
|
||||
|
||||
```kotlin
|
||||
private fun themeFor(preset: ThemePreset): ThemePalette = when (preset) {
|
||||
ThemePreset.DEFAULT -> DefaultTheme
|
||||
ThemePreset.MONOCHROME -> MonochromeTheme
|
||||
// …one line per preset…
|
||||
}
|
||||
```
|
||||
`Theme.kt` reads `preset.palette()` and applies the mode fallback above.
|
||||
|
||||
Dynamic (Material You) color on API 31+ can optionally override the preset scheme when
|
||||
`useDynamicColors = true` (defaults to `false`). Semantic status colors are always resolved from the
|
||||
active preset via `LocalHermesStatusColors`.
|
||||
active preset via `LocalHermesStatusColors`. The preset selector stays enabled
|
||||
and explains that it controls status colors while Material You supplies app colors.
|
||||
|
||||
## Adding a new theme
|
||||
|
||||
1. **Create** `presets/<Name>Scheme.kt` as a template fill (see above) —
|
||||
`buildTheme` for a full theme, `buildThemeDarkOnly` / `buildThemeLightOnly`
|
||||
for single-mode themes. Every status color must be bespoke — no aliasing.
|
||||
2. **Add 1 line** to `themeFor()` in `Theme.kt` + import the new val.
|
||||
2. **Register** the new palette in `ThemeRegistry.kt`.
|
||||
3. **Add the enum entry** `MY_THEME` to `ThemePreset` in `Theme.kt`.
|
||||
4. **Wire the UI**: add the label + selection in
|
||||
4. **Wire the UI**: add the exhaustive label mapping in
|
||||
`ui/settings/components/AppearanceSection.kt` (and any string resource).
|
||||
5. **Verify**:
|
||||
```bash
|
||||
./gradlew ktlintCheck testDebugUnitTest
|
||||
```
|
||||
`ThemePaletteTest` asserts >= 3:1 contrast on every shipped mode's error
|
||||
slot pairs and the ThemeMode invariants — it must stay green.
|
||||
`ThemePaletteTest` checks text contrast, complete Material slot mapping,
|
||||
mode-independent fixed roles, registry coverage, and mode/fallback invariants.
|
||||
|
||||
## Conventions
|
||||
|
||||
|
||||
@@ -10,12 +10,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.m57.hermescontrol.theme.presets.AmoledTheme
|
||||
import com.m57.hermescontrol.theme.presets.CatppuccinTheme
|
||||
import com.m57.hermescontrol.theme.presets.DefaultTheme
|
||||
import com.m57.hermescontrol.theme.presets.GruvboxTheme
|
||||
import com.m57.hermescontrol.theme.presets.MonochromeTheme
|
||||
import com.m57.hermescontrol.theme.presets.NordTheme
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@@ -28,20 +23,6 @@ val LocalThemePreference = compositionLocalOf { ThemePreference.SYSTEM }
|
||||
val LocalThemePreset = compositionLocalOf { ThemePreset.DEFAULT }
|
||||
val LocalChatFontScale = compositionLocalOf { 1.0f }
|
||||
|
||||
/**
|
||||
* The 6 preset themes — one file each, all built from the same
|
||||
* [PaletteTemplate] shape (see `PaletteTemplate.kt`).
|
||||
*/
|
||||
private fun themeFor(preset: ThemePreset): ThemePalette =
|
||||
when (preset) {
|
||||
ThemePreset.DEFAULT -> DefaultTheme
|
||||
ThemePreset.MONOCHROME -> MonochromeTheme
|
||||
ThemePreset.GRUVBOX -> GruvboxTheme
|
||||
ThemePreset.CATPPUCCIN -> CatppuccinTheme
|
||||
ThemePreset.AMOLED -> AmoledTheme
|
||||
ThemePreset.NORD -> NordTheme
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the Material 3 [ColorScheme] for a preset + dark flag.
|
||||
*
|
||||
@@ -53,7 +34,7 @@ internal fun resolveColorScheme(
|
||||
preset: ThemePreset,
|
||||
darkTheme: Boolean,
|
||||
): ColorScheme {
|
||||
val theme = themeFor(preset)
|
||||
val theme = preset.palette()
|
||||
// DefaultTheme is FULL — its scheme is never null for either mode.
|
||||
return theme.schemeFor(darkTheme) ?: requireNotNull(DefaultTheme.schemeFor(darkTheme))
|
||||
}
|
||||
@@ -66,7 +47,7 @@ internal fun resolveStatusColors(
|
||||
preset: ThemePreset,
|
||||
darkTheme: Boolean,
|
||||
): HermesStatusColors {
|
||||
val theme = themeFor(preset)
|
||||
val theme = preset.palette()
|
||||
// DefaultTheme is FULL — its status colors are never null for either mode.
|
||||
return theme.statusFor(darkTheme) ?: requireNotNull(DefaultTheme.statusFor(darkTheme))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.m57.hermescontrol.theme
|
||||
|
||||
import com.m57.hermescontrol.theme.presets.AmoledTheme
|
||||
import com.m57.hermescontrol.theme.presets.CatppuccinTheme
|
||||
import com.m57.hermescontrol.theme.presets.DefaultTheme
|
||||
import com.m57.hermescontrol.theme.presets.GruvboxTheme
|
||||
import com.m57.hermescontrol.theme.presets.MonochromeTheme
|
||||
import com.m57.hermescontrol.theme.presets.NordTheme
|
||||
|
||||
internal data class ThemeDefinition(
|
||||
val preset: ThemePreset,
|
||||
val palette: ThemePalette,
|
||||
)
|
||||
|
||||
// Regression theme-contract: selection, resolution, and tests share this registry.
|
||||
internal val ThemeRegistry =
|
||||
listOf(
|
||||
ThemeDefinition(ThemePreset.DEFAULT, DefaultTheme),
|
||||
ThemeDefinition(ThemePreset.MONOCHROME, MonochromeTheme),
|
||||
ThemeDefinition(ThemePreset.GRUVBOX, GruvboxTheme),
|
||||
ThemeDefinition(ThemePreset.CATPPUCCIN, CatppuccinTheme),
|
||||
ThemeDefinition(ThemePreset.AMOLED, AmoledTheme),
|
||||
ThemeDefinition(ThemePreset.NORD, NordTheme),
|
||||
)
|
||||
|
||||
internal fun ThemePreset.palette(): ThemePalette = ThemeRegistry.single { it.preset == this }.palette
|
||||
@@ -59,6 +59,20 @@ val AmoledTheme =
|
||||
onSurface = AmoledTextBright,
|
||||
surfaceVariant = AmoledContainerHigh,
|
||||
onSurfaceVariant = AmoledTextDim,
|
||||
surfaceDim = AmoledFloor,
|
||||
surfaceBright = AmoledContainerHighest,
|
||||
primaryFixed = AmoledTextBright,
|
||||
primaryFixedDim = AmoledTextBright,
|
||||
onPrimaryFixed = AmoledFloor,
|
||||
onPrimaryFixedVariant = AmoledFloor,
|
||||
secondaryFixed = AmoledSecondary,
|
||||
secondaryFixedDim = AmoledSecondary,
|
||||
onSecondaryFixed = AmoledFloor,
|
||||
onSecondaryFixedVariant = AmoledFloor,
|
||||
tertiaryFixed = AmoledTertiary,
|
||||
tertiaryFixedDim = AmoledTertiary,
|
||||
onTertiaryFixed = AmoledFloor,
|
||||
onTertiaryFixedVariant = AmoledFloor,
|
||||
surfaceContainerLowest = AmoledFloor,
|
||||
surfaceContainerLow = AmoledFloor,
|
||||
surfaceContainer = AmoledContainer,
|
||||
|
||||
@@ -19,7 +19,6 @@ private val MochaRed = Color(0xFFF38BA8)
|
||||
private val MochaText = Color(0xFFCDD6F4)
|
||||
private val MochaSubtext1 = Color(0xFFBAC2DE)
|
||||
private val MochaSubtext0 = Color(0xFFA6ADC8)
|
||||
private val MochaOverlay0 = Color(0xFF6C7086)
|
||||
private val MochaSurface1 = Color(0xFF45475A)
|
||||
private val MochaSurface0 = Color(0xFF313244)
|
||||
private val MochaBase = Color(0xFF1E1E2E)
|
||||
@@ -31,24 +30,33 @@ private val MochaCrust = Color(0xFF11111B)
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
private val LatteMauve = Color(0xFF8839EF)
|
||||
|
||||
// Derived role, not an upstream Catppuccin swatch: Latte Text/Pink is 3.02:1
|
||||
// and Text/Green is 2.39:1. Deeper ink reaches 5.88:1 and 4.65:1 respectively.
|
||||
private val LatteDeepInk = Color(0xFF202334)
|
||||
|
||||
// Derived role, not an upstream Catppuccin swatch: Latte Base/Blue is only 4.34:1.
|
||||
// White/Blue reaches 4.94:1 for normal text.
|
||||
private val LatteOnBlue = Color(0xFFFFFFFF)
|
||||
private val LatteBlue = Color(0xFF1E66F5)
|
||||
private val LattePink = Color(0xFFEA76CB)
|
||||
private val LatteGreen = Color(0xFF40A02B)
|
||||
private val LatteYellow = Color(0xFFDF8E1D)
|
||||
private val LatteRed = Color(0xFFD20F39)
|
||||
private val LatteText = Color(0xFF4C4F69)
|
||||
|
||||
// Official Latte Subtext0 keeps borders visible against Surface0 (3.20:1).
|
||||
private val LatteSubtext0 = Color(0xFF6C6F85)
|
||||
private val LatteOverlay0 = Color(0xFF9CA0B0)
|
||||
private val LatteText = Color(0xFF4C4F69)
|
||||
private val LatteSurface1 = Color(0xFFBCC0CC)
|
||||
private val LatteSurface0 = Color(0xFFCCD0DA)
|
||||
private val LatteBase = Color(0xFFEFF1F5)
|
||||
private val LatteMantle = Color(0xFFE6E9EF)
|
||||
private val LatteCrust = Color(0xFFDCE0E8)
|
||||
|
||||
// Bespoke tint used only for the light primary container — not part of
|
||||
// the official Catppuccin swatch set, kept local to this file.
|
||||
private val LattePrimaryContainer = Color(0xFFE6CAFA)
|
||||
private val LatteOnPrimaryContainer = Color(0xFF380075)
|
||||
// Regression theme-contract: fixed accents use Mocha in both modes. Fixed/Dim
|
||||
// share official accents rather than inventing tonal steps; on-roles use Crust.
|
||||
// Latte surface containers stop at Surface0 so normal text stays >= 4.5:1.
|
||||
// Upstream: https://github.com/catppuccin/palette/blob/main/palette.json
|
||||
|
||||
/** Catppuccin theme — Mocha (dark) / Latte (light). */
|
||||
val CatppuccinTheme =
|
||||
@@ -73,6 +81,20 @@ val CatppuccinTheme =
|
||||
onSurface = MochaText,
|
||||
surfaceVariant = MochaSurface0,
|
||||
onSurfaceVariant = MochaSubtext0,
|
||||
surfaceDim = MochaBase,
|
||||
surfaceBright = MochaSurface1,
|
||||
primaryFixed = MochaMauve,
|
||||
primaryFixedDim = MochaMauve,
|
||||
onPrimaryFixed = MochaCrust,
|
||||
onPrimaryFixedVariant = MochaCrust,
|
||||
secondaryFixed = MochaBlue,
|
||||
secondaryFixedDim = MochaBlue,
|
||||
onSecondaryFixed = MochaCrust,
|
||||
onSecondaryFixedVariant = MochaCrust,
|
||||
tertiaryFixed = MochaPink,
|
||||
tertiaryFixedDim = MochaPink,
|
||||
onTertiaryFixed = MochaCrust,
|
||||
onTertiaryFixedVariant = MochaCrust,
|
||||
surfaceContainerLowest = MochaCrust,
|
||||
surfaceContainerLow = MochaMantle,
|
||||
surfaceContainer = MochaBase,
|
||||
@@ -81,7 +103,7 @@ val CatppuccinTheme =
|
||||
inverseSurface = MochaText,
|
||||
inverseOnSurface = MochaBase,
|
||||
inversePrimary = LatteMauve,
|
||||
outline = MochaOverlay0,
|
||||
outline = MochaSubtext0,
|
||||
outlineVariant = MochaSurface1,
|
||||
scrim = MochaCrust,
|
||||
status =
|
||||
@@ -105,48 +127,62 @@ val CatppuccinTheme =
|
||||
PaletteColors(
|
||||
primary = LatteMauve,
|
||||
onPrimary = LatteBase,
|
||||
primaryContainer = LattePrimaryContainer,
|
||||
onPrimaryContainer = LatteOnPrimaryContainer,
|
||||
primaryContainer = LatteBase,
|
||||
onPrimaryContainer = LatteMauve,
|
||||
secondary = LatteBlue,
|
||||
onSecondary = LatteBase,
|
||||
onSecondary = LatteOnBlue,
|
||||
secondaryContainer = LatteMantle,
|
||||
onSecondaryContainer = LatteText,
|
||||
tertiary = LattePink,
|
||||
onTertiary = LatteBase,
|
||||
tertiaryContainer = LatteSurface1,
|
||||
onTertiary = LatteDeepInk,
|
||||
tertiaryContainer = LatteSurface0,
|
||||
onTertiaryContainer = LatteText,
|
||||
background = LatteBase,
|
||||
onBackground = LatteText,
|
||||
surface = LatteBase,
|
||||
onSurface = LatteText,
|
||||
surfaceVariant = LatteSurface0,
|
||||
onSurfaceVariant = LatteSubtext0,
|
||||
onSurfaceVariant = LatteText,
|
||||
surfaceDim = LatteSurface0,
|
||||
surfaceBright = LatteBase,
|
||||
primaryFixed = MochaMauve,
|
||||
primaryFixedDim = MochaMauve,
|
||||
onPrimaryFixed = MochaCrust,
|
||||
onPrimaryFixedVariant = MochaCrust,
|
||||
secondaryFixed = MochaBlue,
|
||||
secondaryFixedDim = MochaBlue,
|
||||
onSecondaryFixed = MochaCrust,
|
||||
onSecondaryFixedVariant = MochaCrust,
|
||||
tertiaryFixed = MochaPink,
|
||||
tertiaryFixedDim = MochaPink,
|
||||
onTertiaryFixed = MochaCrust,
|
||||
onTertiaryFixedVariant = MochaCrust,
|
||||
surfaceContainerLowest = LatteCrust,
|
||||
surfaceContainerLow = LatteMantle,
|
||||
surfaceContainer = LatteBase,
|
||||
surfaceContainerHigh = LatteSurface0,
|
||||
surfaceContainerHighest = LatteSurface1,
|
||||
surfaceContainerHighest = LatteSurface0,
|
||||
inverseSurface = LatteText,
|
||||
inverseOnSurface = LatteBase,
|
||||
inversePrimary = MochaMauve,
|
||||
outline = LatteOverlay0,
|
||||
outline = LatteSubtext0,
|
||||
outlineVariant = LatteSurface1,
|
||||
scrim = LatteCrust,
|
||||
status =
|
||||
HermesStatusColors(
|
||||
success = LatteGreen,
|
||||
successContainer = LatteSurface0,
|
||||
onSuccess = LatteBase,
|
||||
onSuccess = LatteDeepInk,
|
||||
warning = LatteYellow,
|
||||
warningContainer = LatteSurface0,
|
||||
onWarning = LatteBase,
|
||||
onWarning = LatteDeepInk,
|
||||
error = LatteRed,
|
||||
errorContainer = LatteSurface0,
|
||||
onError = LatteBase,
|
||||
onErrorContainer = LatteRed,
|
||||
onErrorContainer = LatteText,
|
||||
info = LatteBlue,
|
||||
infoContainer = LatteSurface0,
|
||||
onInfo = LatteBase,
|
||||
onInfo = LatteOnBlue,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -73,6 +73,20 @@ val DefaultTheme =
|
||||
onSurface = SlateDarkFg1,
|
||||
surfaceVariant = SlateDarkBg1,
|
||||
onSurfaceVariant = SlateDarkFg3,
|
||||
surfaceDim = SlateDarkBg0,
|
||||
surfaceBright = SlateDarkBg4,
|
||||
primaryFixed = SlateBrightTeal,
|
||||
primaryFixedDim = SlateBrightTeal,
|
||||
onPrimaryFixed = SlateDarkBg0,
|
||||
onPrimaryFixedVariant = SlateDarkBg0,
|
||||
secondaryFixed = SlateBrightCyan,
|
||||
secondaryFixedDim = SlateBrightCyan,
|
||||
onSecondaryFixed = SlateDarkBg0,
|
||||
onSecondaryFixedVariant = SlateDarkBg0,
|
||||
tertiaryFixed = SlateBrightBlue,
|
||||
tertiaryFixedDim = SlateBrightBlue,
|
||||
onTertiaryFixed = SlateDarkBg0,
|
||||
onTertiaryFixedVariant = SlateDarkBg0,
|
||||
surfaceContainerLowest = SlateDarkBg0,
|
||||
surfaceContainerLow = SlateDarkBg1,
|
||||
surfaceContainer = SlateDarkBg2,
|
||||
@@ -121,6 +135,20 @@ val DefaultTheme =
|
||||
onSurface = SlateLightFg1,
|
||||
surfaceVariant = SlateLightBg1,
|
||||
onSurfaceVariant = SlateLightFg3,
|
||||
surfaceDim = SlateLightBg4,
|
||||
surfaceBright = SlateLightBg0,
|
||||
primaryFixed = SlateBrightTeal,
|
||||
primaryFixedDim = SlateBrightTeal,
|
||||
onPrimaryFixed = SlateDarkBg0,
|
||||
onPrimaryFixedVariant = SlateDarkBg0,
|
||||
secondaryFixed = SlateBrightCyan,
|
||||
secondaryFixedDim = SlateBrightCyan,
|
||||
onSecondaryFixed = SlateDarkBg0,
|
||||
onSecondaryFixedVariant = SlateDarkBg0,
|
||||
tertiaryFixed = SlateBrightBlue,
|
||||
tertiaryFixedDim = SlateBrightBlue,
|
||||
onTertiaryFixed = SlateDarkBg0,
|
||||
onTertiaryFixedVariant = SlateDarkBg0,
|
||||
surfaceContainerLowest = SlateLightBg0,
|
||||
surfaceContainerLow = SlateLightBg1,
|
||||
surfaceContainer = SlateLightBg2,
|
||||
@@ -129,7 +157,7 @@ val DefaultTheme =
|
||||
inverseSurface = SlateLightFg1,
|
||||
inverseOnSurface = SlateLightBg0,
|
||||
inversePrimary = SlateBrightTeal,
|
||||
outline = SlateGray,
|
||||
outline = SlateLightFg3,
|
||||
outlineVariant = SlateLightBg3,
|
||||
scrim = SlateLightBg0,
|
||||
status =
|
||||
|
||||
@@ -9,18 +9,20 @@ import com.m57.hermescontrol.theme.buildTheme
|
||||
// Gruvbox Dark (medium contrast) — named swatches
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Official hard-contrast bg0; medium bg0/red only reaches 4.29:1.
|
||||
private val GruvboxDarkInk = Color(0xFF1D2021)
|
||||
private val GruvboxDarkBg0 = Color(0xFF282828)
|
||||
private val GruvboxDarkBg1 = Color(0xFF3C3836)
|
||||
private val GruvboxDarkBg2 = Color(0xFF504945)
|
||||
private val GruvboxDarkBg3 = Color(0xFF665C54)
|
||||
private val GruvboxDarkBg4 = Color(0xFF7C6F64)
|
||||
private val GruvboxDarkFg1 = Color(0xFFEBDBB2)
|
||||
private val GruvboxDarkFg2 = Color(0xFFD5C4A1)
|
||||
private val GruvboxDarkFg3 = Color(0xFFBDAE93)
|
||||
private val GruvboxGray = Color(0xFF928374) // same neutral gray in both modes
|
||||
|
||||
// "Bright" accents — the variants gruvbox uses against a dark background.
|
||||
private val GruvboxBrightRed = Color(0xFFFB4934)
|
||||
// Derived role, not an upstream swatch: bright red/bg0 is only 4.29:1.
|
||||
// Lighter red reaches 4.92:1 for error text and field labels on bg0.
|
||||
private val GruvboxErrorText = Color(0xFFFF5F4F)
|
||||
private val GruvboxBrightGreen = Color(0xFFB8BB26)
|
||||
private val GruvboxBrightYellow = Color(0xFFFABD2F)
|
||||
private val GruvboxBrightBlue = Color(0xFF83A598)
|
||||
@@ -35,7 +37,6 @@ private val GruvboxLightBg0 = Color(0xFFFBF1C7)
|
||||
private val GruvboxLightBg1 = Color(0xFFEBDBB2)
|
||||
private val GruvboxLightBg2 = Color(0xFFD5C4A1)
|
||||
private val GruvboxLightBg3 = Color(0xFFBDAE93)
|
||||
private val GruvboxLightBg4 = Color(0xFFA89984)
|
||||
private val GruvboxLightFg1 = Color(0xFF3C3836)
|
||||
private val GruvboxLightFg2 = Color(0xFF504945)
|
||||
private val GruvboxLightFg3 = Color(0xFF665C54)
|
||||
@@ -43,28 +44,38 @@ private val GruvboxLightFg3 = Color(0xFF665C54)
|
||||
// "Faded" accents — the muted variants gruvbox uses against a light
|
||||
// background, so contrast holds up without the neon dark-mode saturation.
|
||||
private val GruvboxFadedRed = Color(0xFF9D0006)
|
||||
|
||||
// Derived roles, not upstream Gruvbox swatches: light bg0/faded green is 4.29:1;
|
||||
// white/green reaches 4.86:1. Hard dark bg0/faded yellow is below 4.5:1;
|
||||
// deeper neutral ink/yellow reaches 4.97:1.
|
||||
private val GruvboxOnFadedGreen = Color(0xFFFFFFFF)
|
||||
private val GruvboxOnFadedYellow = Color(0xFF121212)
|
||||
private val GruvboxFadedGreen = Color(0xFF79740E)
|
||||
private val GruvboxFadedYellow = Color(0xFFB57614)
|
||||
private val GruvboxFadedBlue = Color(0xFF076678)
|
||||
private val GruvboxFadedPurple = Color(0xFF8F3F71)
|
||||
private val GruvboxFadedOrange = Color(0xFFAF3A03)
|
||||
|
||||
/** Gruvbox theme — bright accents on dark bg0–bg4, faded accents on light bg0–bg4. */
|
||||
// Regression theme-contract: fixed roles reuse bright accents in both modes;
|
||||
// Fixed/Dim share a swatch. Containers stop at bg3 for >= 4.5:1 body text.
|
||||
// Upstream: https://github.com/morhetz/gruvbox/blob/master/colors/gruvbox.vim
|
||||
|
||||
/** Gruvbox theme — bright accents on dark bg0–bg3, faded accents on light bg0–bg3. */
|
||||
val GruvboxTheme =
|
||||
buildTheme(
|
||||
dark =
|
||||
PaletteColors(
|
||||
primary = GruvboxBrightOrange,
|
||||
onPrimary = GruvboxDarkBg0,
|
||||
primaryContainer = GruvboxDarkBg4,
|
||||
primaryContainer = GruvboxDarkBg2,
|
||||
onPrimaryContainer = GruvboxDarkFg1,
|
||||
secondary = GruvboxBrightBlue,
|
||||
onSecondary = GruvboxDarkBg0,
|
||||
secondaryContainer = GruvboxDarkBg3,
|
||||
secondaryContainer = GruvboxDarkBg2,
|
||||
onSecondaryContainer = GruvboxDarkFg2,
|
||||
tertiary = GruvboxBrightPurple,
|
||||
onTertiary = GruvboxDarkBg0,
|
||||
tertiaryContainer = GruvboxDarkBg4,
|
||||
tertiaryContainer = GruvboxDarkBg2,
|
||||
onTertiaryContainer = GruvboxDarkFg1,
|
||||
background = GruvboxDarkBg0,
|
||||
onBackground = GruvboxDarkFg1,
|
||||
@@ -72,15 +83,29 @@ val GruvboxTheme =
|
||||
onSurface = GruvboxDarkFg1,
|
||||
surfaceVariant = GruvboxDarkBg1,
|
||||
onSurfaceVariant = GruvboxDarkFg3,
|
||||
surfaceDim = GruvboxDarkBg0,
|
||||
surfaceBright = GruvboxDarkBg3,
|
||||
primaryFixed = GruvboxBrightOrange,
|
||||
primaryFixedDim = GruvboxBrightOrange,
|
||||
onPrimaryFixed = GruvboxDarkBg0,
|
||||
onPrimaryFixedVariant = GruvboxDarkBg0,
|
||||
secondaryFixed = GruvboxBrightBlue,
|
||||
secondaryFixedDim = GruvboxBrightBlue,
|
||||
onSecondaryFixed = GruvboxDarkBg0,
|
||||
onSecondaryFixedVariant = GruvboxDarkBg0,
|
||||
tertiaryFixed = GruvboxBrightPurple,
|
||||
tertiaryFixedDim = GruvboxBrightPurple,
|
||||
onTertiaryFixed = GruvboxDarkBg0,
|
||||
onTertiaryFixedVariant = GruvboxDarkBg0,
|
||||
surfaceContainerLowest = GruvboxDarkBg0,
|
||||
surfaceContainerLow = GruvboxDarkBg1,
|
||||
surfaceContainer = GruvboxDarkBg2,
|
||||
surfaceContainerHigh = GruvboxDarkBg3,
|
||||
surfaceContainerHighest = GruvboxDarkBg4,
|
||||
surfaceContainerHighest = GruvboxDarkBg3,
|
||||
inverseSurface = GruvboxDarkFg1,
|
||||
inverseOnSurface = GruvboxDarkBg0,
|
||||
inversePrimary = GruvboxFadedOrange,
|
||||
outline = GruvboxGray,
|
||||
outline = GruvboxDarkFg2,
|
||||
outlineVariant = GruvboxDarkBg3,
|
||||
scrim = GruvboxDarkBg0,
|
||||
status =
|
||||
@@ -91,9 +116,9 @@ val GruvboxTheme =
|
||||
warning = GruvboxBrightYellow,
|
||||
warningContainer = GruvboxDarkBg3,
|
||||
onWarning = GruvboxDarkBg0,
|
||||
error = GruvboxBrightRed,
|
||||
error = GruvboxErrorText,
|
||||
errorContainer = GruvboxDarkBg3,
|
||||
onError = GruvboxDarkBg0,
|
||||
onError = GruvboxDarkInk,
|
||||
onErrorContainer = GruvboxDarkFg1,
|
||||
info = GruvboxBrightBlue,
|
||||
infoContainer = GruvboxDarkBg3,
|
||||
@@ -104,15 +129,15 @@ val GruvboxTheme =
|
||||
PaletteColors(
|
||||
primary = GruvboxFadedOrange,
|
||||
onPrimary = GruvboxLightBg0,
|
||||
primaryContainer = GruvboxLightBg4,
|
||||
primaryContainer = GruvboxLightBg2,
|
||||
onPrimaryContainer = GruvboxLightFg1,
|
||||
secondary = GruvboxFadedBlue,
|
||||
onSecondary = GruvboxLightBg0,
|
||||
secondaryContainer = GruvboxLightBg3,
|
||||
secondaryContainer = GruvboxLightBg2,
|
||||
onSecondaryContainer = GruvboxLightFg2,
|
||||
tertiary = GruvboxFadedPurple,
|
||||
onTertiary = GruvboxLightBg0,
|
||||
tertiaryContainer = GruvboxLightBg4,
|
||||
tertiaryContainer = GruvboxLightBg2,
|
||||
onTertiaryContainer = GruvboxLightFg1,
|
||||
background = GruvboxLightBg0,
|
||||
onBackground = GruvboxLightFg1,
|
||||
@@ -120,29 +145,43 @@ val GruvboxTheme =
|
||||
onSurface = GruvboxLightFg1,
|
||||
surfaceVariant = GruvboxLightBg1,
|
||||
onSurfaceVariant = GruvboxLightFg3,
|
||||
surfaceDim = GruvboxLightBg3,
|
||||
surfaceBright = GruvboxLightBg0,
|
||||
primaryFixed = GruvboxBrightOrange,
|
||||
primaryFixedDim = GruvboxBrightOrange,
|
||||
onPrimaryFixed = GruvboxDarkBg0,
|
||||
onPrimaryFixedVariant = GruvboxDarkBg0,
|
||||
secondaryFixed = GruvboxBrightBlue,
|
||||
secondaryFixedDim = GruvboxBrightBlue,
|
||||
onSecondaryFixed = GruvboxDarkBg0,
|
||||
onSecondaryFixedVariant = GruvboxDarkBg0,
|
||||
tertiaryFixed = GruvboxBrightPurple,
|
||||
tertiaryFixedDim = GruvboxBrightPurple,
|
||||
onTertiaryFixed = GruvboxDarkBg0,
|
||||
onTertiaryFixedVariant = GruvboxDarkBg0,
|
||||
surfaceContainerLowest = GruvboxLightBg0,
|
||||
surfaceContainerLow = GruvboxLightBg1,
|
||||
surfaceContainer = GruvboxLightBg2,
|
||||
surfaceContainerHigh = GruvboxLightBg3,
|
||||
surfaceContainerHighest = GruvboxLightBg4,
|
||||
surfaceContainerHighest = GruvboxLightBg3,
|
||||
inverseSurface = GruvboxLightFg1,
|
||||
inverseOnSurface = GruvboxLightBg0,
|
||||
inversePrimary = GruvboxBrightOrange,
|
||||
outline = GruvboxGray,
|
||||
outline = GruvboxLightFg2,
|
||||
outlineVariant = GruvboxLightBg3,
|
||||
scrim = GruvboxLightBg0,
|
||||
status =
|
||||
HermesStatusColors(
|
||||
success = GruvboxFadedGreen,
|
||||
successContainer = GruvboxLightBg3,
|
||||
onSuccess = GruvboxLightBg0,
|
||||
onSuccess = GruvboxOnFadedGreen,
|
||||
warning = GruvboxFadedYellow,
|
||||
warningContainer = GruvboxLightBg3,
|
||||
onWarning = GruvboxLightBg0,
|
||||
onWarning = GruvboxOnFadedYellow,
|
||||
error = GruvboxFadedRed,
|
||||
errorContainer = GruvboxLightBg3,
|
||||
onError = GruvboxLightBg0,
|
||||
onErrorContainer = GruvboxFadedRed,
|
||||
onErrorContainer = GruvboxLightFg1,
|
||||
info = GruvboxFadedBlue,
|
||||
infoContainer = GruvboxLightBg3,
|
||||
onInfo = GruvboxLightBg0,
|
||||
|
||||
@@ -32,7 +32,6 @@ private val MonoLightBg = Color(0xFFFAFAFA) // background, surface, containerLow
|
||||
private val MonoLightContainer = Color(0xFFF0F0F0) // surfaceContainer
|
||||
private val MonoLightContainerHigh = Color(0xFFE0E0E0) // surfaceContainerHigh, surfaceVariant, status containers
|
||||
private val MonoLightContainerHighest = Color(0xFFD1D1D1) // surfaceContainerHighest, primaryContainer
|
||||
private val MonoLightOutline = Color(0xFF808080)
|
||||
private val MonoLightOutlineVariant = Color(0xFFCCCCCC)
|
||||
private val MonoLightTextDim = Color(0xFF4D4D4D) // onSurfaceVariant
|
||||
private val MonoLightTextBright = Color(0xFF121212) // onBackground/onSurface/inverseSurface/primary/error
|
||||
@@ -71,6 +70,20 @@ val MonochromeTheme =
|
||||
onSurface = MonoDarkTextBright,
|
||||
surfaceVariant = MonoDarkContainerHigh,
|
||||
onSurfaceVariant = MonoDarkTextDim,
|
||||
surfaceDim = MonoDarkBg,
|
||||
surfaceBright = MonoDarkContainerHighest,
|
||||
primaryFixed = MonoDarkTextBright,
|
||||
primaryFixedDim = MonoDarkTextBright,
|
||||
onPrimaryFixed = MonoDarkBg,
|
||||
onPrimaryFixedVariant = MonoDarkBg,
|
||||
secondaryFixed = MonoDarkSecondary,
|
||||
secondaryFixedDim = MonoDarkSecondary,
|
||||
onSecondaryFixed = MonoDarkBg,
|
||||
onSecondaryFixedVariant = MonoDarkBg,
|
||||
tertiaryFixed = MonoDarkTertiary,
|
||||
tertiaryFixedDim = MonoDarkTertiary,
|
||||
onTertiaryFixed = MonoDarkBg,
|
||||
onTertiaryFixedVariant = MonoDarkBg,
|
||||
surfaceContainerLowest = MonoDarkFloor,
|
||||
surfaceContainerLow = MonoDarkBg,
|
||||
surfaceContainer = MonoDarkContainer,
|
||||
@@ -112,13 +125,27 @@ val MonochromeTheme =
|
||||
tertiary = MonoLightTertiary,
|
||||
onTertiary = MonoLightBg,
|
||||
tertiaryContainer = MonoLightContainerHigh,
|
||||
onTertiaryContainer = MonoLightTertiary,
|
||||
onTertiaryContainer = MonoLightSecondary,
|
||||
background = MonoLightBg,
|
||||
onBackground = MonoLightTextBright,
|
||||
surface = MonoLightBg,
|
||||
onSurface = MonoLightTextBright,
|
||||
surfaceVariant = MonoLightContainerHigh,
|
||||
onSurfaceVariant = MonoLightTextDim,
|
||||
surfaceDim = MonoLightContainerHighest,
|
||||
surfaceBright = MonoLightBg,
|
||||
primaryFixed = MonoDarkTextBright,
|
||||
primaryFixedDim = MonoDarkTextBright,
|
||||
onPrimaryFixed = MonoDarkBg,
|
||||
onPrimaryFixedVariant = MonoDarkBg,
|
||||
secondaryFixed = MonoDarkSecondary,
|
||||
secondaryFixedDim = MonoDarkSecondary,
|
||||
onSecondaryFixed = MonoDarkBg,
|
||||
onSecondaryFixedVariant = MonoDarkBg,
|
||||
tertiaryFixed = MonoDarkTertiary,
|
||||
tertiaryFixedDim = MonoDarkTertiary,
|
||||
onTertiaryFixed = MonoDarkBg,
|
||||
onTertiaryFixedVariant = MonoDarkBg,
|
||||
surfaceContainerLowest = MonoLightCeiling,
|
||||
surfaceContainerLow = MonoLightBg,
|
||||
surfaceContainer = MonoLightContainer,
|
||||
@@ -127,7 +154,7 @@ val MonochromeTheme =
|
||||
inverseSurface = MonoLightTextBright,
|
||||
inverseOnSurface = MonoLightBg,
|
||||
inversePrimary = MonoDarkTextBright,
|
||||
outline = MonoLightOutline,
|
||||
outline = MonoLightTertiary,
|
||||
outlineVariant = MonoLightOutlineVariant,
|
||||
scrim = Color.Black,
|
||||
status =
|
||||
@@ -144,7 +171,7 @@ val MonochromeTheme =
|
||||
onErrorContainer = MonoLightTextBright,
|
||||
info = MonoLightInfo,
|
||||
infoContainer = MonoLightContainerHigh,
|
||||
onInfo = MonoLightBg,
|
||||
onInfo = MonoLightTextBright,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -11,9 +11,12 @@ import com.m57.hermescontrol.theme.buildTheme
|
||||
// supply the surfaces; Frost (7–10) and Aurora (11–15) supply the
|
||||
// accents. Unlike Gruvbox, Nord defines one accent set — not a
|
||||
// separate bright/faded pair per mode — so the same accent swatches
|
||||
// are reused in both palettes below.
|
||||
// are reused for fixed roles; primary/error need documented accessible tones.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Derived role, not an upstream Nord swatch: nord0/red is 3.05:1 and
|
||||
// nord0/purple is 4.41:1. Darker Polar Night ink meets 4.5:1 on both.
|
||||
private val NordPolarInk = Color(0xFF0F1218)
|
||||
private val Nord0 = Color(0xFF2E3440) // Polar Night — darkest
|
||||
private val Nord1 = Color(0xFF3B4252)
|
||||
private val Nord2 = Color(0xFF434C5E)
|
||||
@@ -23,11 +26,23 @@ private val Nord5 = Color(0xFFE5E9F0)
|
||||
private val Nord6 = Color(0xFFECEFF4) // Snow Storm — lightest
|
||||
private val Nord8 = Color(0xFF88C0D0) // Frost — primary accent
|
||||
private val Nord9 = Color(0xFF81A1C1) // Frost — secondary / info
|
||||
private val Nord11 = Color(0xFFBF616A) // Aurora red — error
|
||||
|
||||
// Derived roles, not upstream Nord swatches. Nord10/Snow Storm is only 3.50:1;
|
||||
// deeper Frost ink reaches 4.92:1 on nord6 for primary text/focus labels.
|
||||
private val NordFrostInk = Color(0xFF486991)
|
||||
|
||||
// Official Aurora red/nord0 is 3.05:1; a brighter red reaches 4.67:1.
|
||||
private val NordErrorBright = Color(0xFFDE858F)
|
||||
|
||||
// Official Aurora red/nord6 is 3.55:1; deeper red reaches 5.69:1.
|
||||
private val NordErrorInk = Color(0xFFA53547)
|
||||
private val Nord13 = Color(0xFFEBCB8B) // Aurora yellow — warning
|
||||
private val Nord14 = Color(0xFFA3BE8C) // Aurora green — success
|
||||
private val Nord15 = Color(0xFFB48EAD) // Aurora purple — tertiary
|
||||
|
||||
// Fixed/Dim reuse each official accent in both modes (no invented tonal ladder).
|
||||
// Upstream: https://www.nordtheme.com/docs/colors-and-palettes/
|
||||
|
||||
/**
|
||||
* Nord theme.
|
||||
*
|
||||
@@ -36,12 +51,11 @@ private val Nord15 = Color(0xFFB48EAD) // Aurora purple — tertiary
|
||||
* tiers on each side share a value (surfaceContainerHigh ==
|
||||
* surfaceContainerHighest) rather than inventing an off-palette hex.
|
||||
*
|
||||
* Frost/Aurora accents are pastel by design, so onPrimary/onSecondary/
|
||||
* onTertiary/status "on" colors use Polar Night (dark text) in *both*
|
||||
* modes — reusing Snow Storm for light-mode "on" text (the convention in
|
||||
* Pastel Frost/Aurora fills use dark ink in both modes — reusing
|
||||
* Snow Storm for light-mode "on" text (the convention in
|
||||
* the Catppuccin/Gruvbox presets) would fail contrast against colors like
|
||||
* Aurora yellow, which stays light even without a separate light-mode
|
||||
* variant.
|
||||
* variant. Light primary/error use Snow Storm ink on their darker fills.
|
||||
*/
|
||||
val NordTheme =
|
||||
buildTheme(
|
||||
@@ -56,7 +70,7 @@ val NordTheme =
|
||||
secondaryContainer = Nord2,
|
||||
onSecondaryContainer = Nord4,
|
||||
tertiary = Nord15,
|
||||
onTertiary = Nord0,
|
||||
onTertiary = NordPolarInk,
|
||||
tertiaryContainer = Nord3,
|
||||
onTertiaryContainer = Nord6,
|
||||
background = Nord0,
|
||||
@@ -65,6 +79,20 @@ val NordTheme =
|
||||
onSurface = Nord6,
|
||||
surfaceVariant = Nord1,
|
||||
onSurfaceVariant = Nord4,
|
||||
surfaceDim = Nord0,
|
||||
surfaceBright = Nord3,
|
||||
primaryFixed = Nord8,
|
||||
primaryFixedDim = Nord8,
|
||||
onPrimaryFixed = Nord0,
|
||||
onPrimaryFixedVariant = Nord0,
|
||||
secondaryFixed = Nord9,
|
||||
secondaryFixedDim = Nord9,
|
||||
onSecondaryFixed = Nord0,
|
||||
onSecondaryFixedVariant = Nord0,
|
||||
tertiaryFixed = Nord15,
|
||||
tertiaryFixedDim = Nord15,
|
||||
onTertiaryFixed = NordPolarInk,
|
||||
onTertiaryFixedVariant = NordPolarInk,
|
||||
surfaceContainerLowest = Nord0,
|
||||
surfaceContainerLow = Nord1,
|
||||
surfaceContainer = Nord2,
|
||||
@@ -72,8 +100,8 @@ val NordTheme =
|
||||
surfaceContainerHighest = Nord3,
|
||||
inverseSurface = Nord6,
|
||||
inverseOnSurface = Nord0,
|
||||
inversePrimary = Nord8,
|
||||
outline = Nord3,
|
||||
inversePrimary = NordFrostInk,
|
||||
outline = Nord4,
|
||||
outlineVariant = Nord2,
|
||||
scrim = Nord0,
|
||||
status =
|
||||
@@ -84,9 +112,9 @@ val NordTheme =
|
||||
warning = Nord13,
|
||||
warningContainer = Nord2,
|
||||
onWarning = Nord0,
|
||||
error = Nord11,
|
||||
error = NordErrorBright,
|
||||
errorContainer = Nord2,
|
||||
onError = Nord0,
|
||||
onError = NordPolarInk,
|
||||
onErrorContainer = Nord6,
|
||||
info = Nord9,
|
||||
infoContainer = Nord2,
|
||||
@@ -95,8 +123,8 @@ val NordTheme =
|
||||
),
|
||||
light =
|
||||
PaletteColors(
|
||||
primary = Nord8,
|
||||
onPrimary = Nord0,
|
||||
primary = NordFrostInk,
|
||||
onPrimary = Nord6,
|
||||
primaryContainer = Nord4,
|
||||
onPrimaryContainer = Nord0,
|
||||
secondary = Nord9,
|
||||
@@ -104,7 +132,7 @@ val NordTheme =
|
||||
secondaryContainer = Nord5,
|
||||
onSecondaryContainer = Nord1,
|
||||
tertiary = Nord15,
|
||||
onTertiary = Nord0,
|
||||
onTertiary = NordPolarInk,
|
||||
tertiaryContainer = Nord4,
|
||||
onTertiaryContainer = Nord0,
|
||||
background = Nord6,
|
||||
@@ -113,6 +141,20 @@ val NordTheme =
|
||||
onSurface = Nord0,
|
||||
surfaceVariant = Nord5,
|
||||
onSurfaceVariant = Nord2,
|
||||
surfaceDim = Nord4,
|
||||
surfaceBright = Nord6,
|
||||
primaryFixed = Nord8,
|
||||
primaryFixedDim = Nord8,
|
||||
onPrimaryFixed = Nord0,
|
||||
onPrimaryFixedVariant = Nord0,
|
||||
secondaryFixed = Nord9,
|
||||
secondaryFixedDim = Nord9,
|
||||
onSecondaryFixed = Nord0,
|
||||
onSecondaryFixedVariant = Nord0,
|
||||
tertiaryFixed = Nord15,
|
||||
tertiaryFixedDim = Nord15,
|
||||
onTertiaryFixed = NordPolarInk,
|
||||
onTertiaryFixedVariant = NordPolarInk,
|
||||
surfaceContainerLowest = Nord6,
|
||||
surfaceContainerLow = Nord5,
|
||||
surfaceContainer = Nord5,
|
||||
@@ -132,10 +174,10 @@ val NordTheme =
|
||||
warning = Nord13,
|
||||
warningContainer = Nord5,
|
||||
onWarning = Nord0,
|
||||
error = Nord11,
|
||||
error = NordErrorInk,
|
||||
errorContainer = Nord5,
|
||||
onError = Nord0,
|
||||
onErrorContainer = Nord11,
|
||||
onError = Nord6,
|
||||
onErrorContainer = Nord0,
|
||||
info = Nord9,
|
||||
infoContainer = Nord5,
|
||||
onInfo = Nord0,
|
||||
|
||||
Reference in new issue
Block a user