UI Theming and Styling
Relevant source files
The following files were used as context for generating this wiki page:The Vocabri application utilizes Jetpack Compose with Material 3 for its UI, providing a modern and flexible theming system. This system supports both light and dark modes, dynamic color on compatible Android versions (12+), and custom typography. The styling is primarily managed through a combination of Compose Composable functions and a small set of legacy XML theme files for base application attributes.
The core of the theming is encapsulated within the com.vocabri.ui.theme package. This structure allows for a clear separation of concerns, with distinct files for color palettes, typography scales, and the main theme composable that brings them all together. This approach ensures a consistent look and feel across the entire application.
Theming Architecture
The theming architecture is centered around the VocabriTheme composable function, which wraps the entire UI tree. This function selects the appropriate color scheme and typography and applies them using the MaterialTheme composable.
The diagram below illustrates the relationship between the main components of the theming system.
Key Files
| File | Purpose |
|---|---|
Theme.kt | Contains the main VocabriTheme composable, which orchestrates the theming logic, and defines the lightScheme and darkScheme color sets. |
color/Color.kt | Defines the extensive color palette for both light and dark themes, including variants for different contrast levels. |
Typography.kt | Defines custom font families using Google Fonts and creates a custom Typography object (AppTypography) to be used by the theme. |
values/themes.xml | Provides the base Android application theme, primarily setting the status bar color. It inherits from Theme.MaterialComponents.DayNight.DarkActionBar. |
values-night/themes.xml | A qualifier-specific version of themes.xml for dark mode, though its content is identical to the base file in this project. |
MainActivity.kt | The main entry point of the app, which calls the VocabriTheme composable to apply the theme to the UI. |
Sources: app/src/main/java/com/vocabri/ui/theme/Theme.kt, app/src/main/java/com/vocabri/ui/theme/color/Color.kt, app/src/main/java/com/vocabri/ui/theme/Typography.kt, app/src/main/res/values/themes.xml, app/src/main/java/com/vocabri/ui/main/MainActivity.kt
Theme Application
The application theme is applied in MainActivity.kt by wrapping the MainScreen() composable within the VocabriTheme composable.
// app/src/main/java/com/vocabri/ui/main/MainActivity.kt:23-27
setContent {
VocabriTheme {
MainScreen()
}
}
Sources: app/src/main/java/com/vocabri/ui/main/MainActivity.kt:23-27
VocabriTheme Composable
The VocabriTheme function is the central point for applying styling. It determines which color scheme to use based on system settings and parameters.
Parameters
darkTheme: Boolean: Defaults toisSystemInDarkTheme(). Determines if the dark theme should be used.dynamicColor: Boolean: Defaults totrue. Enables the use of dynamic colors from the user's wallpaper on Android 12 and higher.
Logic Flow
The function selects a colorScheme based on a series of conditions. This logic is visualized in the flowchart below.
This diagram shows the decision process for selecting the color scheme within the VocabriTheme composable.
Sources: app/src/main/java/com/vocabri/ui/theme/Theme.kt:208-222
// app/src/main/java/com/vocabri/ui/theme/Theme.kt:208-222
@Composable
fun VocabriTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit,
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
if (darkTheme) darkScheme else lightScheme
}
darkTheme -> darkScheme
else -> lightScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
content = content,
)
}
Sources: app/src/main/java/com/vocabri/ui/theme/Theme.kt:208-222
Color System
The color system is defined across two files: color/Color.kt for the raw color values and Theme.kt for assembling those values into lightColorScheme and darkColorScheme objects.
Color Palette (color/Color.kt)
This file contains an extensive list of Color objects for light and dark themes, including variants for medium and high contrast modes.
Example Light Theme Colors:
| Name | Hex Value |
|---|---|
primaryLight | 0xFF5E5791 |
onPrimaryLight | 0xFFFFFFFF |
backgroundLight | 0xFFFCF8FF |
errorLight | 0xFFBA1A1A |
Example Dark Theme Colors:
| Name | Hex Value |
|---|---|
primaryDark | 0xFFC8BFFF |
onPrimaryDark | 0xFF30285F |
backgroundDark | 0xFF141318 |
errorDark | 0xFFFFB4AB |
Sources: app/src/main/java/com/vocabri/ui/theme/color/Color.kt:25-68, app/src/main/java/com/vocabri/ui/theme/color/Color.kt:151-194
Color Schemes (Theme.kt)
The lightScheme and darkScheme variables map the raw color values from Color.kt to the semantic color roles defined by MaterialTheme.
// app/src/main/java/com/vocabri/ui/theme/Theme.kt:111-140
private val lightScheme = lightColorScheme(
primary = primaryLight,
onPrimary = onPrimaryLight,
primaryContainer = primaryContainerLight,
// ... more colors
background = backgroundLight,
onBackground = onBackgroundLight,
surface = surfaceLight,
onSurface = onSurfaceLight,
// ... more colors
)
// app/src/main/java/com/vocabri/ui/theme/Theme.kt:142-171
private val darkScheme = darkColorScheme(
primary = primaryDark,
onPrimary = onPrimaryDark,
primaryContainer = primaryContainerDark,
// ... more colors
background = backgroundDark,
onBackground = onBackgroundDark,
surface = surfaceDark,
onSurface = onSurfaceDark,
// ... more colors
)
Sources: app/src/main/java/com/vocabri/ui/theme/Theme.kt:111-171
Typography
Custom typography is defined in Typography.kt. The system uses downloadable Google Fonts, specifically "Inter" for body text and "DM Serif Display" for display text.
Font Definitions
The fonts are configured using a GoogleFont.Provider.
| Font Family | Google Font Name | Usage |
|---|---|---|
bodyFontFamily | Inter | Used for body and label text styles. |
displayFontFamily | DM Serif Display | Used for display, headline, and title text styles. |
Sources: app/src/main/java/com/vocabri/ui/theme/Typography.kt:32-45
AppTypography Object
The AppTypography object takes the baseline Material 3 typography and overrides the fontFamily for each text style with the custom bodyFontFamily or displayFontFamily. This object is then passed to the MaterialTheme in VocabriTheme.
// app/src/main/java/com/vocabri/ui/theme/Typography.kt:50-65
val AppTypography = Typography(
displayLarge = baseline.displayLarge.copy(fontFamily = displayFontFamily),
displayMedium = baseline.displayMedium.copy(fontFamily = displayFontFamily),
displaySmall = baseline.displaySmall.copy(fontFamily = displayFontFamily),
headlineLarge = baseline.headlineLarge.copy(fontFamily = displayFontFamily),
// ...
bodyLarge = baseline.bodyLarge.copy(fontFamily = bodyFontFamily),
bodyMedium = baseline.bodyMedium.copy(fontFamily = bodyFontFamily),
bodySmall = baseline.bodySmall.copy(fontFamily = bodyFontFamily),
// ...
)
Sources: app/src/main/java/com/vocabri/ui/theme/Typography.kt:50-65
Legacy XML Theming
While the majority of styling is handled in Jetpack Compose, a base theme is still defined in XML. This is used for attributes that apply to the application window before Compose takes over, such as the status bar color.
The application's AndroidManifest.xml specifies android:theme="@style/Theme.Vocabri".
Sources: app/src/main/AndroidManifest.xml:28
themes.xml
The Theme.Vocabri style inherits from Theme.MaterialComponents.DayNight.DarkActionBar and sets the android:statusBarColor. The DayNight parent allows the system to automatically select appropriate base styles for light and dark modes.
<!-- app/src/main/res/values/themes.xml:23-27 -->
<resources>
<!-- Base application theme. -->
<style name="Theme.Vocabri" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
</style>
</resources>
The file values-night/themes.xml exists but contains the exact same definition, relying on the DayNight parent theme to handle dark mode specifics at the XML level.
Sources: app/src/main/res/values/themes.xml:25-27, app/src/main/res/values-night/themes.xml:25-27