Coverage Summary for Class: AppColors (com.georgeci.moneysurfer.uikit.tokens)

Class Method, % Branch, % Line, % Instruction, %
AppColors 100% (42/42) 100% (49/49) 100% (215/215)
AppColors$Dark 100% (42/42) 100% (49/49) 100% (217/217)
Total 100% (84/84) 100% (98/98) 100% (432/432)


 package com.georgeci.moneysurfer.uikit.tokens
 
 import androidx.compose.ui.graphics.Color
 
 object AppColors {
 
     // Primary
     val Primary = Color(0xFF1B5E20)
     val OnPrimary = Color(0xFFFFFFFF)
     val PrimaryContainer = Color(0xFFA5D6A7)
     val OnPrimaryContainer = Color(0xFF002204)
 
     // Secondary
     val Secondary = Color(0xFF4E6352)
     val OnSecondary = Color(0xFFFFFFFF)
     val SecondaryContainer = Color(0xFFD0E8D2)
     val OnSecondaryContainer = Color(0xFF0B1F12)
 
     // Tertiary
     val Tertiary = Color(0xFF39656B)
     val OnTertiary = Color(0xFFFFFFFF)
     val TertiaryContainer = Color(0xFFBCEBF1)
     val OnTertiaryContainer = Color(0xFF001F23)
 
     // Error
     val Error = Color(0xFFBA1A1A)
     val OnError = Color(0xFFFFFFFF)
     val ErrorContainer = Color(0xFFFFDAD6)
     val OnErrorContainer = Color(0xFF410002)
 
     // Background & Surface
     val Background = Color(0xFFFCFDF7)
     val OnBackground = Color(0xFF1A1C19)
     val Surface = Color(0xFFFCFDF7)
     val OnSurface = Color(0xFF1A1C19)
     val SurfaceVariant = Color(0xFFDEE5D9)
     val OnSurfaceVariant = Color(0xFF424940)
     val SurfaceTint = Color(0xFF1B5E20)
 
     // Surface container ramp. Left unset these fall back to Material's baseline tonal palette,
     // which is purple-tinted, so cards sat on a visibly different neutral hue from the scaffold
     // behind them. Each level keeps M3's tonal step relative to [Surface] and is re-tinted to
     // [Surface]'s own neutral: red one below the green channel, blue six below.
     val SurfaceContainerLowest = Color(0xFFFEFFF9)
     val SurfaceContainerLow = Color(0xFFF7F8F2)
     val SurfaceContainer = Color(0xFFF2F3ED)
     val SurfaceContainerHigh = Color(0xFFEBECE6)
     val SurfaceContainerHighest = Color(0xFFE5E6E0)
     val SurfaceBright = Color(0xFFFCFDF7)
     val SurfaceDim = Color(0xFFDDDED8)
 
     // Outline
     val Outline = Color(0xFF72796F)
     val OutlineVariant = Color(0xFFC2C9BD)
 
     // Inverse
     val InverseSurface = Color(0xFF2F312D)
     val InverseOnSurface = Color(0xFFF0F1EB)
     val InversePrimary = Color(0xFF6EDD72)
 
     // Scrim
     val Scrim = Color(0xFF000000)
 
     // Semantic — transaction type accents. Material 3 has no income/expense/transfer
     // slots; expose via [SemanticColors] / `AppTheme.semanticColors` so screens never
     // reach for the hex.
     //
     // These are *text* colours (amounts, type pills), so each one holds ≥4.5:1 against
     // every surface it can land on — down to `surfaceContainerHighest`, and through the
     // 14–18% self-tinted pill washes mixed in `SurferRecentTransactionsWidget` and the
     // transaction creation `TypePill`. That worst case is what drove the light values
     // darker than the mockup's mid-tones; `ColorContrastTest` pins it.
     val Income = Color(0xFF1C5E41)
     val Expense = Color(0xFFA3403D)
     val Transfer = Color(0xFF2E5AA8)
 
     /**
      * Amber "approaching the limit" accent. M3 has no warning slot, and `error` is already
      * spoken for by the over-budget state, so a budget nearing its cap needs a third colour
      * that reads as caution rather than failure. Darkened from the mockup's
      * `oklch(56% 0.13 68)`, which only reached 3.2:1 on the tonal surface containers.
      */
     val Warning = Color(0xFF83580C)
 
     /**
      * Category tints, in palette order (mint, amber, indigo, citrus, plum, rose, clay, moss).
      * Chosen against the light surface; [Dark.CategoryTints] lifts the same hues for the dark
      * one. Consumed through `SemanticColors.categoryTints` / `SurferCategoryPalette.tints` —
      * screens and components never index into this list directly.
      *
      * Positionally paired with `SurferCategoryPalette.hues`; both must stay in lockstep with
      * `CategoryAppearance` in the domain module.
      */
     val CategoryTints: List<Color> = listOf(
         Color(0xFF2F8E6E), // mint   — hue 162
         Color(0xFFB5533B), // amber  — hue  35
         Color(0xFF2E5AA8), // indigo — hue 258
         Color(0xFFA06A1E), // citrus — hue  68
         Color(0xFF6B4E99), // plum   — hue 303
         Color(0xFFB54A6B), // rose   — hue 340
         Color(0xFFB54744), // clay   — hue   8
         Color(0xFF6B7D3D), // moss   — hue  90
     )
 
     // Dark theme overrides
     object Dark {
         val Primary = Color(0xFF6EDD72)
         val OnPrimary = Color(0xFF00390A)
         val PrimaryContainer = Color(0xFF005313)
         val OnPrimaryContainer = Color(0xFF8AFB8C)
 
         val Secondary = Color(0xFFB5CCB7)
         val OnSecondary = Color(0xFF203526)
         val SecondaryContainer = Color(0xFF364B3B)
         val OnSecondaryContainer = Color(0xFFD0E8D2)
 
         val Tertiary = Color(0xFFA1CED5)
         val OnTertiary = Color(0xFF00363C)
         val TertiaryContainer = Color(0xFF1F4D53)
         val OnTertiaryContainer = Color(0xFFBCEBF1)
 
         val Error = Color(0xFFFFB4AB)
         val OnError = Color(0xFF690005)
         val ErrorContainer = Color(0xFF93000A)
         val OnErrorContainer = Color(0xFFFFDAD6)
 
         val Background = Color(0xFF1A1C19)
         val OnBackground = Color(0xFFE2E3DD)
         val Surface = Color(0xFF1A1C19)
         val OnSurface = Color(0xFFE2E3DD)
         val SurfaceVariant = Color(0xFF424940)
         val OnSurfaceVariant = Color(0xFFC2C9BD)
         val SurfaceTint = Color(0xFF6EDD72)
 
         /**
          * Dark counterpart of the light ramp: the same M3 tonal steps relative to [Surface],
          * re-tinted to [Surface]'s neutral — red two below the green channel, blue three below.
          */
         val SurfaceContainerLowest = Color(0xFF151714)
         val SurfaceContainerLow = Color(0xFF232522)
         val SurfaceContainer = Color(0xFF272926)
         val SurfaceContainerHigh = Color(0xFF313330)
         val SurfaceContainerHighest = Color(0xFF3C3E3B)
         val SurfaceBright = Color(0xFF40423F)
         val SurfaceDim = Color(0xFF1A1C19)
 
         val Outline = Color(0xFF8C9388)
         val OutlineVariant = Color(0xFF424940)
 
         val InverseSurface = Color(0xFFE2E3DD)
         val InverseOnSurface = Color(0xFF2F312D)
         val InversePrimary = Color(0xFF1B5E20)
 
         val Scrim = Color(0xFF000000)
 
         // Light-on-dark already clears AA on every surface these land on, so the dark
         // accents keep the mockup's values. See `ColorContrastTest` for the exact pairs.
         val Income = Color(0xFF6FCB9F)
         val Expense = Color(0xFFE89B98)
         val Transfer = Color(0xFF8FB0E0)
 
         /** Same amber, lifted to the mockup's dark-scheme lightness (`oklch(78% 0.13 68)`). */
         val Warning = Color(0xFFE9B45E)
 
         /**
          * [AppColors.CategoryTints] with the same hues taken to the lightness the dark scheme's
          * other accents sit at (HSL L 72% / S 55% — the rule that already produces [Expense] and
          * [Transfer] from their light counterparts). The light tints are mid-dark on the dark
          * surface, both as the icon tint and as the 18% wash behind it in `SurferCategoryBubble`.
          *
          * Index 2 (indigo) and index 6 (clay) are the same colours as [Transfer] and [Expense]
          * in the light palette, so they reuse the dark values of those accents verbatim.
          */
         val CategoryTints: List<Color> = listOf(
             Color(0xFF90DFC4), // mint
             Color(0xFFDFA090), // amber
             Transfer, //          indigo
             Color(0xFFDFBE90), // citrus
             Color(0xFFAF90DF), // plum
             Color(0xFFDF90A9), // rose
             Expense, //           clay
             Color(0xFFC9DF90), // moss
         )
     }
 }