Dark Mode & Accessibility
Build inclusive apps with dark themes, TalkBack support, and scalable typography
Dark Theme
Switching
TalkBack
Font Scale
Overview
Modern Android apps should support dark themes for comfort and battery savings, and accessibility so all users — including those with visual, motor, or cognitive impairments — can use your app effectively.
User Preferences Your App
─────────────── ────────
System dark mode → values-night/ themes
TalkBack enabled → contentDescription, labels
Large font size → sp units, scalable layouts
High contrast → sufficient color contrast
Feature Benefit
Dark theme Reduced eye strain, OLED battery savings
Theme switching User choice: light, dark, or follow system
Content descriptions Screen readers announce UI elements
Font scaling Readable text at any system font size
Touch targets Minimum 48dp for motor accessibility
1 Dark Theme Setup
Android 10+ supports system-wide dark mode. Define light colors in values/ and dark variants in values-night/ — the system picks the right set automatically.
Enable DayNight theme
XML — themes.xml <style name="Theme.MyApp" parent="Theme.Material3.DayNight.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorOnPrimary">@color/on_primary</item>
<item name="colorSurface">@color/surface</item>
<item name="android:statusBarColor">@color/status_bar</item>
</style>
Light colors — values/colors.xml
XML — values/colors.xml <resources>
<color name="primary">#6750A4</color>
<color name="on_primary">#FFFFFF</color>
<color name="surface">#FFFBFE</color>
<color name="on_surface">#1C1B1F</color>
<color name="background">#FFFBFE</color>
<color name="status_bar">#6750A4</color>
</resources>
Dark colors — values-night/colors.xml
XML — values-night/colors.xml <resources>
<color name="primary">#D0BCFF</color>
<color name="on_primary">#381E72</color>
<color name="surface">#1C1B1F</color>
<color name="on_surface">#E6E1E5</color>
<color name="background">#1C1B1F</color>
<color name="status_bar">#1C1B1F</color>
</resources>
Jetpack Compose dark theme
Kotlin — Theme.kt @Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = if (darkTheme) {
darkColorScheme(
primary = Color(0xFFD0BCFF),
onPrimary = Color(0xFF381E72),
surface = Color(0xFF1C1B1F),
onSurface = Color(0xFFE6E1E5)
)
} else {
lightColorScheme(
primary = Color(0xFF6750A4),
onPrimary = Color(0xFFFFFFFF),
surface = Color(0xFFFFFBFE),
onSurface = Color(0xFF1C1B1F)
)
}
MaterialTheme(colorScheme = colorScheme, content = content)
}
Dark theme checklist
Use Theme.Material3.DayNight or AppCompat.DayNight
Define colors in both values/ and values-night/
Avoid hardcoded colors in layouts — use ?attr/colorSurface
Test icons and images for visibility in both themes
Ensure sufficient contrast (WCAG 4.5:1 for body text)
2 Theme Switching
Let users choose between Light , Dark , and System default . Use AppCompatDelegate for Views/XML apps or persist preference and pass to Compose theme.
Theme mode enum and preference
Kotlin — ThemePreference.kt enum class ThemeMode { LIGHT, DARK, SYSTEM }
class ThemePreference(context: Context) {
private val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
fun getThemeMode(): ThemeMode {
return ThemeMode.valueOf(prefs.getString("theme_mode", ThemeMode.SYSTEM.name)!!)
}
fun setThemeMode(mode: ThemeMode) {
prefs.edit().putString("theme_mode", mode.name).apply()
applyTheme(mode)
}
fun applyTheme(mode: ThemeMode) {
val nightMode = when (mode) {
ThemeMode.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
ThemeMode.DARK -> AppCompatDelegate.MODE_NIGHT_YES
ThemeMode.SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
AppCompatDelegate.setDefaultNightMode(nightMode)
}
}
Apply on app start
Kotlin — MyApplication.kt class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
ThemePreference(this).applyTheme(ThemePreference(this).getThemeMode())
}
}
Settings screen with SwitchMaterial
Kotlin — SettingsActivity.kt class SettingsActivity : AppCompatActivity() {
private lateinit var themePreference: ThemePreference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
themePreference = ThemePreference(this)
binding.radioLight.setOnClickListener {
themePreference.setThemeMode(ThemeMode.LIGHT)
}
binding.radioDark.setOnClickListener {
themePreference.setThemeMode(ThemeMode.DARK)
}
binding.radioSystem.setOnClickListener {
themePreference.setThemeMode(ThemeMode.SYSTEM)
}
}
}
Compose theme switching
Kotlin — Compose with user preference @Composable
fun AppRoot(themeMode: ThemeMode) {
val darkTheme = when (themeMode) {
ThemeMode.LIGHT -> false
ThemeMode.DARK -> true
ThemeMode.SYSTEM -> isSystemInDarkTheme()
}
MyAppTheme(darkTheme = darkTheme) {
MainScreen(
onThemeChange = { mode -> /* save to DataStore */ }
)
}
}
3 Accessibility Features
Android accessibility APIs help users with disabilities interact with your app. Focus on semantic labels, touch target size, color contrast, and keyboard/switch navigation support.
contentDescription for images and icons
XML — ImageView with label <!-- Meaningful description for screen readers -->
<ImageView
android:id="@+id/ivProfile"
android:layout_width="64dp"
android:layout_height="64dp"
android:contentDescription="@string/profile_photo_desc"
android:src="@drawable/ic_profile" />
<!-- Decorative image — hide from TalkBack -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:src="@drawable/decorative_divider" />
Kotlin accessibility helpers
Kotlin — Programmatic accessibility // Set content description
binding.btnDelete.contentDescription = "Delete task: ${task.title}"
// Mark as heading for screen readers
ViewCompat.setAccessibilityHeading(binding.tvSectionTitle, true)
// Custom click label (more descriptive than "double tap to activate")
ViewCompat.replaceAccessibilityAction(
binding.btnPlay,
AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfoCompat.ACTION_CLICK,
"Play audio"
),
null,
null
)
Minimum touch target size
XML — 48dp minimum touch target <ImageButton
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:contentDescription="@string/menu"
android:src="@drawable/ic_menu" />
Accessibility checklist
Requirement Implementation
Label all interactive elements contentDescription or visible text
Touch targets ≥ 48dp Button/icon minimum size
Color contrast ≥ 4.5:1 Test with Accessibility Scanner
Don't rely on color alone Add icons, text, or patterns
Support focus navigation Logical tab order, no traps
Announce dynamic changes announceForAccessibility()
4 Screen Readers
TalkBack is Android's built-in screen reader. It reads aloud what's on screen and lets users navigate by swipe gestures. Design your UI so TalkBack can announce every element meaningfully.
Enable TalkBack for testing
Settings → Accessibility → TalkBack → Turn on
Or use Android Studio emulator: Extended Controls → Settings → TalkBack
Navigate: swipe right/left to move focus, double-tap to activate
RecyclerView accessibility
Kotlin — Accessible ViewHolder class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val titleView: TextView = itemView.findViewById(R.id.tvTitle)
private val statusView: TextView = itemView.findViewById(R.id.tvStatus)
fun bind(task: Task) {
titleView.text = task.title
statusView.text = if (task.completed) "Completed" else "Pending"
itemView.contentDescription = buildString {
append(task.title)
append(", ")
append(if (task.completed) "completed" else "pending")
append(". Double tap to open.")
}
}
}
Live region — announce dynamic updates
Kotlin — Announce toast-like updates // XML: android:accessibilityLiveRegion="polite"
binding.statusText.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE
fun showSaveConfirmation() {
binding.statusText.text = "Task saved successfully"
binding.statusText.announceForAccessibility("Task saved successfully")
}
Compose accessibility
Kotlin — Compose semantics @Composable
fun TaskItem(task: Task, onClick: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.semantics {
contentDescription = "${task.title}, ${if (task.completed) "completed" else "pending"}"
role = Role.Button
onClick(label = "Open task") { onClick(); true }
}
.clickable(onClick = onClick)
) {
Text(text = task.title, modifier = Modifier.padding(16.dp))
}
}
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete task",
modifier = Modifier.size(48.dp)
)
Accessibility Scanner
Install Google's Accessibility Scanner app from Play Store. It scans your running app and suggests improvements — missing labels, small touch targets, low contrast, and more.
5 Font Scaling
Users can increase system font size in Settings. Use sp (scale-independent pixels) for all text so it grows with user preferences. Avoid fixed heights that clip enlarged text.
sp vs dp for text
XML — Correct text sizing <!-- CORRECT — scales with user font preference -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:text="Hello World" />
<!-- WRONG — dp does not scale with font settings -->
<TextView
android:textSize="16dp"
android:text="Hello World" />
Responsive layouts for large fonts
XML — Flexible layout <!-- Use wrap_content height, not fixed dp -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="3"
android:ellipsize="end"
android:textSize="20sp" />
<TextView
android:id="@+id/tvBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:lineSpacingMultiplier="1.2" />
</LinearLayout>
Detect font scale in code
Kotlin — Read font scale factor val fontScale = resources.configuration.fontScale
// 1.0 = default, 1.3 = large, 2.0 = largest
when {
fontScale >= 1.5f -> {
// Use compact layout or reduce secondary info
binding.tvSubtitle.isVisible = false
}
else -> {
binding.tvSubtitle.isVisible = true
}
}
Compose font scaling
Kotlin — Compose Text with sp Text(
text = "Welcome back!",
fontSize = 24.sp, // scales automatically
lineHeight = 32.sp,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
// Use Material typography — respects font scale
Text(
text = "Body content here",
style = MaterialTheme.typography.bodyLarge
)
Font scaling best practices
Do Don't
Use sp for all text sizes Use dp or pixels for text
Use wrap_content height for text views Fixed height that clips text
Test at 200% font scale in emulator Assume default font size only
Use ScrollView for long content Truncate critical info with ellipsize
Support dynamic type in Compose Disable font scaling with fontScale override
6 Hands-On Exercises
1
Add dark theme colors in values-night/colors.xml. Switch device to dark mode and verify all screens.
2
Build a Settings screen with theme switching — Light, Dark, and System options using AppCompatDelegate.
3
Add contentDescription to all ImageViews and icon buttons. Run Accessibility Scanner .
4
Enable TalkBack and navigate your app. Fix any unlabeled or confusing announcements.
5
Set emulator font scale to 200%. Ensure all text uses sp and layouts don't clip content.
6
Add a live region announcement when a task is saved. Verify TalkBack reads the update aloud.
7
Bonus: Implement the same dark theme and accessibility in a Jetpack Compose screen using semantics { }.
Dark Mode & Accessibility MCQ Practice
10 Basic MCQs
10 Advanced MCQs
10 Basic Dark Mode & Accessibility MCQs
1
Dark theme reduces?
A App size always
B Eye strain and OLED power usage
C Network usage
D Permissions
Explanation: Dark backgrounds especially benefit OLED screens.
2
Theme.MaterialComponents.DayNight?
A Light only
B Dark only always
C Switches light/dark based on system setting
D Deprecated
Explanation: DayNight parent theme follows uiMode night flag.
3
TalkBack is?
A Gradle plugin
B Camera filter
C Network tool
D Android screen reader
Explanation: Speaks UI elements for visually impaired users.
4
contentDescription on ImageView?
A Accessibility label for screen readers
B Encryption key
C URL
D SQL column
Explanation: Describe meaningful images; null decorative only.
5
values-night folder?
A Deprecated
B Night-specific resources (colors, themes)
C Gradle only
D Manifest
Explanation: Alternate resources when UI_MODE_NIGHT_YES.
6
Font scale setting?
A API level
B App version
C User system setting scaling text size
D Dex size
Explanation: Respect sp units so text scales with user preference.
7
AppCompatDelegate.setDefaultNightMode?
A Camera
B Change language
C GPS
D Force light, dark, or follow system
Explanation: MODE_NIGHT_YES, NO, or FOLLOW_SYSTEM.
8
ImportantForAccessibility?
A Control if view reported to accessibility services
B SQL priority
C Network
D Lint only
Explanation: yes, no, auto on Views.
9
Contrast ratio accessibility?
A Optional
B Sufficient text/background contrast for readability
C Ignored by TalkBack
D API 1 only
Explanation: WCAG guidelines — Material color roles help.
10
Switch access in settings?
A Theme switch only
B WiFi switch
C Accessibility service for switch users
D Gradle
Explanation: Alternative input for users with motor limitations.
10 Advanced Dark Mode & Accessibility MCQs
11
isNightModeActive check?
A GPS
B Configuration.UI_MODE_NIGHT_MASK
C Battery only
D Random
Explanation: resources.configuration.uiMode and NIGHT_YES flag.
12
Semantic properties Compose?
A GPS
B SQL semantics
C contentDescription, heading, mergeDescendants
D Dex
Explanation: Modifier.semantics { contentDescription = "..." }
13
AccessibilityEvent TYPE_VIEW_CLICKED?
A Camera
B Network event
C Gradle
D Fired when view clicked for a11y services
Explanation: Custom views should send accessibility events.
14
Large touch target minimum?
A 48dp recommended Material accessibility
B 12dp
C 1px
D No minimum
Explanation: Minimum interactive target for motor accessibility.
15
Reduce motion preference?
A Ban all UI
B Accessibility setting — minimize animations
C Network
D SQL
Explanation: Check Settings Global TRANSITION_ANIMATION_SCALE.
16
ExploreByTouchHelper?
A Deprecated all
B Map only
C Custom View accessibility for complex widgets
D Firebase
Explanation: For custom views needing granular a11y traversal.
17
Bold text accessibility setting?
A Camera
B Gradle bold
C API ban
D System makes text bolder — test layouts
Explanation: Layouts should accommodate thicker glyphs.
18
High contrast text (Android 14+)?
A System increases text contrast
B Removes dark mode
C Ban colors
D Root only
Explanation: Test themes with high contrast enabled.
19
announceForAccessibility?
A Toast only
B Speak message immediately to screen reader
C Notification
D Logcat
Explanation: view.announceForAccessibility("Item deleted") for dynamic updates.
20
Accessibility Scanner?
A Virus scan
B Play Store scanner
C Google tool suggesting a11y improvements
D ProGuard
Explanation: Run on app screens to find missing labels etc.
Check All Answers
Click an option to select, then check answers.
Dark Mode & Accessibility Interview Q&A
15 topic-focused questions for interviews and revision.
1 Enable dark theme in app.easy
Answer: Theme.DayNight parent; values-night/colors.xml; vector drawables support both modes.
2 In-app theme toggle.medium
Answer: AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES); save preference in DataStore.
3 TalkBack testing steps.easy
Answer: Enable TalkBack in settings; navigate app with swipe; verify all controls spoken.
4 contentDescription best practices.easy
Answer: Meaningful for action icons; empty for decorative; don't say "button button".
5 Support font scaling.medium
Answer: Use sp for text; avoid fixed height truncating scaled text; test at 200% scale.
6 Color contrast check.medium
Answer: Use Material color onSurface on background; WebAIM contrast checker for custom colors.
7 Focus order for TalkBack.hard
Answer: Logical traversal — set android:importantForAccessibility and focusable order.
8 Dark mode images.medium
Answer: Provide -night drawable variants or tint vectors; avoid harsh white flash images.
9 Accessibility Compose.medium
Answer: Modifier.semantics, mergeDescendants, custom actions, test with TalkBack.
10 Keyboard navigation.hard
Answer: Focusable views, nextFocusForward, action labels for D-pad and switch access.
11 Live region for dynamic content.hard
Answer: accessibilityLiveRegion polite for Snackbar-like updates.
12 Don't rely on color alone.easy
Answer: Add icons/text for errors — colorblind users need more than red/green.
13 Minimum touch target fix.easy
Answer: Increase padding or minHeight/minWidth 48dp on small icons.
14 Test with Accessibility Scanner.medium
Answer: Install scanner app; run on each screen; fix suggested issues.
15 Legal/compliance motivation.medium
Answer: ADA, EN 301 549, Play accessibility requirements — inclusive apps reach more users.