Themes vs Styles vs Attributes
The Three Levels of Customization
Android's theming system has three layers, each serving a distinct purpose:
- Theme — a collection of attributes applied to an entire Activity or Application. It defines the overall look: colors, typography defaults, component defaults.
- Style — a reusable set of attributes applied to individual views. It defines how a specific component looks.
- Attribute — a single customizable property (like
colorPrimaryorandroid:textSize).
How They Relate
A theme provides default values for attributes. A style overrides specific attributes for individual views. When you set a style on a view, the view first inherits from the theme, then applies the style on top:
<!-- Theme defines the default button text color -->
<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
<item name="colorPrimary">@color/blue_500</item>
</style>
<!-- Style overrides for a specific button variant -->
<style name="Widget.MyApp.Button.Danger">
<item name="backgroundTint">@color/red_500</item>
<item name="android:textColor">@color/white</item>
</style>
<!-- Button inherits from theme, then applies the style -->
<com.google.android.material.button.MaterialButton
style="@style/Widget.MyApp.Button.Danger"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete" />
Inheritance in Themes and Styles
Both themes and styles support inheritance using dot notation:
<!-- Parent theme -->
<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
<item name="colorPrimary">@color/blue_500</item>
</style>
<!-- Child theme inherits everything, overrides one attribute -->
<style name="Theme.MyApp.Red" parent="Theme.MyApp">
<item name="colorPrimary">@color/red_500</item>
</style>
Apply different themes to activities:
<activity android:name=".MainActivity"
android:theme="@style/Theme.MyApp" />
<activity android:name=".DangerZoneActivity"
android:theme="@style/Theme.MyApp.Red" />
Defining Custom Attributes
For complex components that need custom properties, define attributes in res/values/attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RatingBar">
<attr name="maxRating" format="integer" />
<attr name="filledStar" format="reference" />
<attr name="emptyStar" format="reference" />
</declare-styleable>
</resources>
Reference them in a style:
<style name="Widget.MyApp.RatingBar">
<item name="maxRating">5</item>
<item name="filledStar">@drawable/star_filled</item>
<item name="emptyStar">@drawable/star_empty</item>
</style>
Read them in a custom view constructor:
val attrs = context.obtainStyledAttributes(set, R.styleable.RatingBar)
val maxRating = attrs.getInt(R.styleable.RatingBar_maxRating, 5)
attrs.recycle()
Dark Mode with DayNight
How Dark Mode Works
Android 10+ supports system-wide dark mode. The DayNight theme reads the system setting and swaps resource qualifiers automatically:
res/values/colors.xml— light mode colors (default)res/values-night/colors.xml— dark mode colors
Setting Up DayNight
<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
<!-- Colors used in both modes -->
<item name="colorPrimary">@color/primary</item>
</style>
Define separate color files:
<!-- res/values/colors.xml -->
<resources>
<color name="primary">#1976D2</color>
<color name="background">#FFFFFF</color>
<color name="onBackground">#1C1B1F</color>
</resources>
<!-- res/values-night/colors.xml -->
<resources>
<color name="primary">#90CAF9</color>
<color name="background">#1C1B1F</color>
<color name="onBackground">#E6E1E5</color>
</resources>
Material Color Scheme Attributes
Material Design 3 defines a set of standard color attributes that map to specific roles:
| Attribute | Role |
|---|---|
colorPrimary |
Brand color, used for key components |
colorOnPrimary |
Text/icon color on top of colorPrimary |
colorSecondary |
Accent color for less prominent components |
colorSurface |
Background for cards, sheets, menus |
colorOnSurface |
Text on surfaces |
android:colorBackground |
App-level background |
Reference these attributes in your views instead of hardcoded colors:
<!-- GOOD — adapts to theme and dark mode -->
<TextView
android:textColor="?attr/colorOnBackground"
android:background="?attr/colorSurface" />
<!-- BAD — hardcoded, breaks in dark mode -->
<TextView
android:textColor="#000000"
android:background="#FFFFFF" />
Enabling Force Dark for Legacy Apps
If you have an existing app without Night resources, enable Force Dark in the Activity:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
}
This applies a dark overlay to your existing colors. It works but produces inconsistent results — define proper Night resources instead.
Testing Dark Mode
Use Android Studio's Theme Editor (Tools → Theme Editor) to preview both modes. On a real device, toggle dark mode in System Settings → Display. Test your app in both states to catch hardcoded colors that don't adapt.
Quiz
1. What is the difference between a theme and a style in Android?
2. Where should you define colors for dark mode?
3. Why should you use ?attr/colorOnBackground instead of #000000 for text color?
4. How do you override a theme attribute for a single Activity?
Flashcards
Question
What is the hierarchy: theme → style → attribute?
Click to reveal answer
Answer
Theme defines defaults for an Activity. Style overrides attributes for individual views. Attribute is a single customizable property like colorPrimary.
Question
How does Android handle dark mode resources?
Click to reveal answer
Answer
Use the -night resource qualifier. Define colors in res/values/colors.xml for light mode and res/values-night/colors.xml for dark mode. The system selects automatically.
Question
What is the difference between colorPrimary and colorOnPrimary?
Click to reveal answer
Answer
colorPrimary is the brand color used for key components. colorOnPrimary is the text/icon color that appears on top of colorPrimary surfaces.
Question
What parent theme should you extend for Material Design 3?
Click to reveal answer
Answer
Theme.Material3.DayNight — it supports both light and dark mode with Material Design 3 defaults.
Revision Notes
Key Takeaways
- 1. Themes set defaults for an Activity; styles override for individual views — they compose, not replace
- 2. Always reference theme attributes (?attr/colorOnBackground) instead of hardcoded colors
- 3. Dark mode is automatic when using DayNight theme with -night resource qualifiers
- 4. Material color attributes (colorPrimary, colorSurface) ensure consistent theming across components
- 5. Define custom attributes in attrs.xml when building reusable custom components
Interview Tips
- • Explain the theme → style → attribute inheritance chain clearly
- • Be ready to describe how DayNight theme swaps resources automatically
- • Know the Material color attribute roles (Primary, Surface, OnSurface)
- • Discuss how custom attributes work with obtainStyledAttributes for custom views
Cheat Sheet
Themes & Styles Cheat Sheet
Hierarchy: Theme → Style → Attribute
- Theme: applied to Activity/Application via manifest or setTheme()
- Style: applied to individual views via style attribute
- Attribute: single customizable property
Inheritance:
<style name="Theme.MyApp.Red" parent="Theme.MyApp">
<item name="colorPrimary">@color/red</item>
</style>
Dark Mode:
- Use Theme.Material3.DayNight as parent
- Light: res/values/colors.xml
- Dark: res/values-night/colors.xml
- Reference ?attr/colorOnBackground, not hardcoded #000000
Key Material Color Attributes:
- colorPrimary — brand color
- colorOnPrimary — text on primary
- colorSurface — card/sheet background
- colorOnSurface — text on surfaces
- android:colorBackground — app background
Per-Activity Theme Override:
- Manifest: android:theme on
- Code: setTheme() before setContentView()