Theming¶
AppTheme extends Material 3 with semantic tokens. Components read colors, spacing, typography, shapes, motion, and icon contracts from AppTheme; apps supply their own values into the same slots.
Token bundles¶
| Slot bundle | Type | Default | Accessor |
|---|---|---|---|
| Colors | AppColors |
AppColors.light() / AppColors.dark() |
AppTheme.colors |
| Spacing | AppSpacing |
AppSpacing.default() (8-dp grid) |
AppTheme.spacing |
| Typography | AppTypography |
AppTypography.default() (M3 scale + extensions) |
AppTheme.typography |
| Shapes | AppShapes |
AppShapes.default() (standard/small/large/image/pill/badge) |
AppTheme.shapes |
| Motion | AppMotion |
AppMotion.default() (short/medium/long + emphasized easing) |
AppTheme.motion |
| Icons | AppIcons |
AppIcons.default() (placeholder glyphs) |
AppTheme.icons |
For non-@Composable code that needs default spacing values, the static AppSpacingDefaults object exposes the same defaults as plain Dp constants.
Light + dark color pair¶
AppTheme takes both light and dark color schemes and swaps between them based on darkTheme. Brand apps typically pass both:
AppTheme(
lightColors = NovalingoColors.light(),
darkColors = NovalingoColors.dark(),
) {
// content
}
If only one set is provided, the other defaults to the library's neutral defaults.
Dynamic color¶
Android 12+ dynamic color is opt-in:
AppTheme(dynamicColor = true) {
// Content
}
When dynamic color is unavailable, AppTheme falls back to the supplied AppColors and the current dark mode setting. Extension slots (status, overlay, level palette) are always sourced from the supplied AppColors, never overridden by the dynamic Material You scheme.
Color slot taxonomy¶
The AppColors data class groups slots into four buckets:
- Core (Material 3 standard) —
primary,onPrimary,primaryContainer,onPrimaryContainer,secondary,onSecondary,background,onBackground,surface,onSurface,surfaceVariant,onSurfaceVariant,surfaceContainer,outline,outlineVariant,error,onError,scrim. These map straight onto Material 3'sColorScheme. - Status (extensions) —
success,onSuccess,warning,onWarning. Apps that need success/warning indicators no longer have to repurposetertiaryor invent their own. - Overlay (extensions) —
overlayHeavy,overlayMedium,overlaySubtle,overlayShadowStart,overlayShadowEnd,overlayBottomTint. Useful for media-overlay UIs (player thumbnails, gradient scrims) without baking magic alphas into call sites. - Levels (extension) —
levels: LevelPalette. ALevelPaletteis an ordered list ofLevelTier(background, foreground)pairs that powers tiered-badge components likeLevelBadge. Apps own the ordering and color values.
Spacing slot taxonomy¶
Two parallel naming schemes are exposed for back-compat:
- Multiplier scale —
half(4 dp),x1(8 dp),x1_5(12 dp),x2(16 dp),x3(24 dp),x4(32 dp),x5(40 dp),x6(48 dp),x9(72 dp). Preferred for new code. - T-shirt scale —
xs(4 dp) …xl(24 dp). Kept for back-compat and Material-style component sizing. - Semantic aliases —
padding,contentPadding,itemSpacing,gridSpacing. - Strokes —
strokeThin,strokeRegular,strokeThick. - Interaction —
minTapTarget(44 dp). - Layout dimensions (apps override) —
gridItemMinWidth,gridImageHeight,videoCardThumbnailHeight,maxContentWidthForm,maxContentWidthList,maxContentWidthPlayer. Default to0.dp; apps that use those layouts must supply real values.
Typography slot taxonomy¶
AppTypography exposes three groups:
- Original 4-slot scale —
display,title,body,label. Kept for back-compat with the library's first cut. - Material 3 standard scale —
displayLarge/Medium/Small,headlineLarge/Medium/Small,titleLarge/Medium/Small,bodyLarge/Medium/Small,labelLarge/Medium/Small. These also flow intoMaterialTheme.typography. - Custom extensions —
subtitle,bodyMonospaced,captionMonospaced,displayRounded,iconSmall/Medium/Large/ExtraLarge.
Brand overrides¶
val brandLight = AppColors.light().copy(
primary = BrandBlue,
primaryContainer = BrandBlueSubtle,
success = BrandGreen,
overlayHeavy = Color(0xBF000000), // app-specific gradient overlays
levels = LevelPalette(
listOf(
LevelTier(background = LevelGreen, foreground = Color.White),
LevelTier(background = LevelOrange, foreground = Color.White),
LevelTier(background = LevelPurple, foreground = Color.White),
),
),
)
AppTheme(lightColors = brandLight) {
// Content reads tokens from `AppTheme.colors.*` etc.
}
Catalog token coverage¶
Showkase discovers the light and dark semantic color tokens plus the default typography scale through annotated catalog entries in :components. Spacing, motion, shapes, and icons remain visible through component previews because Showkase only has first-class token annotations for colors and typography.