Dark Mode & Accessibility

Build inclusive apps with dark themes, TalkBack support, and scalable typography

Dark Theme Switching TalkBack Font Scale

Table of Contents

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
FeatureBenefit
Dark themeReduced eye strain, OLED battery savings
Theme switchingUser choice: light, dark, or follow system
Content descriptionsScreen readers announce UI elements
Font scalingReadable text at any system font size
Touch targetsMinimum 48dp for motor accessibility

1Dark 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)

2Theme 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.ktenum 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.ktclass MyApplication : Application() { override fun onCreate() { super.onCreate() ThemePreference(this).applyTheme(ThemePreference(this).getThemeMode()) } }

Settings screen with SwitchMaterial

Kotlin — SettingsActivity.ktclass 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 */ } ) } }

3Accessibility 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

RequirementImplementation
Label all interactive elementscontentDescription or visible text
Touch targets ≥ 48dpButton/icon minimum size
Color contrast ≥ 4.5:1Test with Accessibility Scanner
Don't rely on color aloneAdd icons, text, or patterns
Support focus navigationLogical tab order, no traps
Announce dynamic changesannounceForAccessibility()

4Screen 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

  1. Settings → Accessibility → TalkBack → Turn on
  2. Or use Android Studio emulator: Extended Controls → Settings → TalkBack
  3. Navigate: swipe right/left to move focus, double-tap to activate

RecyclerView accessibility

Kotlin — Accessible ViewHolderclass 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.

5Font 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 factorval 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 spText( 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

DoDon't
Use sp for all text sizesUse dp or pixels for text
Use wrap_content height for text viewsFixed height that clips text
Test at 200% font scale in emulatorAssume default font size only
Use ScrollView for long contentTruncate critical info with ellipsize
Support dynamic type in ComposeDisable font scaling with fontScale override

6Hands-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?

AApp size always
BEye strain and OLED power usage
CNetwork usage
DPermissions
Explanation: Dark backgrounds especially benefit OLED screens.
2

Theme.MaterialComponents.DayNight?

ALight only
BDark only always
CSwitches light/dark based on system setting
DDeprecated
Explanation: DayNight parent theme follows uiMode night flag.
3

TalkBack is?

AGradle plugin
BCamera filter
CNetwork tool
DAndroid screen reader
Explanation: Speaks UI elements for visually impaired users.
4

contentDescription on ImageView?

AAccessibility label for screen readers
BEncryption key
CURL
DSQL column
Explanation: Describe meaningful images; null decorative only.
5

values-night folder?

ADeprecated
BNight-specific resources (colors, themes)
CGradle only
DManifest
Explanation: Alternate resources when UI_MODE_NIGHT_YES.
6

Font scale setting?

AAPI level
BApp version
CUser system setting scaling text size
DDex size
Explanation: Respect sp units so text scales with user preference.
7

AppCompatDelegate.setDefaultNightMode?

ACamera
BChange language
CGPS
DForce light, dark, or follow system
Explanation: MODE_NIGHT_YES, NO, or FOLLOW_SYSTEM.
8

ImportantForAccessibility?

AControl if view reported to accessibility services
BSQL priority
CNetwork
DLint only
Explanation: yes, no, auto on Views.
9

Contrast ratio accessibility?

AOptional
BSufficient text/background contrast for readability
CIgnored by TalkBack
DAPI 1 only
Explanation: WCAG guidelines — Material color roles help.
10

Switch access in settings?

ATheme switch only
BWiFi switch
CAccessibility service for switch users
DGradle
Explanation: Alternative input for users with motor limitations.

10 Advanced Dark Mode & Accessibility MCQs

11

isNightModeActive check?

AGPS
BConfiguration.UI_MODE_NIGHT_MASK
CBattery only
DRandom
Explanation: resources.configuration.uiMode and NIGHT_YES flag.
12

Semantic properties Compose?

AGPS
BSQL semantics
CcontentDescription, heading, mergeDescendants
DDex
Explanation: Modifier.semantics { contentDescription = "..." }
13

AccessibilityEvent TYPE_VIEW_CLICKED?

ACamera
BNetwork event
CGradle
DFired when view clicked for a11y services
Explanation: Custom views should send accessibility events.
14

Large touch target minimum?

A48dp recommended Material accessibility
B12dp
C1px
DNo minimum
Explanation: Minimum interactive target for motor accessibility.
15

Reduce motion preference?

ABan all UI
BAccessibility setting — minimize animations
CNetwork
DSQL
Explanation: Check Settings Global TRANSITION_ANIMATION_SCALE.
16

ExploreByTouchHelper?

ADeprecated all
BMap only
CCustom View accessibility for complex widgets
DFirebase
Explanation: For custom views needing granular a11y traversal.
17

Bold text accessibility setting?

ACamera
BGradle bold
CAPI ban
DSystem makes text bolder — test layouts
Explanation: Layouts should accommodate thicker glyphs.
18

High contrast text (Android 14+)?

ASystem increases text contrast
BRemoves dark mode
CBan colors
DRoot only
Explanation: Test themes with high contrast enabled.
19

announceForAccessibility?

AToast only
BSpeak message immediately to screen reader
CNotification
DLogcat
Explanation: view.announceForAccessibility("Item deleted") for dynamic updates.
20

Accessibility Scanner?

AVirus scan
BPlay Store scanner
CGoogle tool suggesting a11y improvements
DProGuard
Explanation: Run on app screens to find missing labels etc.
Click an option to select, then check answers.

Dark Mode & Accessibility Interview Q&A

15 topic-focused questions for interviews and revision.

1Enable dark theme in app.easy
Answer: Theme.DayNight parent; values-night/colors.xml; vector drawables support both modes.
2In-app theme toggle.medium
Answer: AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES); save preference in DataStore.
3TalkBack testing steps.easy
Answer: Enable TalkBack in settings; navigate app with swipe; verify all controls spoken.
4contentDescription best practices.easy
Answer: Meaningful for action icons; empty for decorative; don't say "button button".
5Support font scaling.medium
Answer: Use sp for text; avoid fixed height truncating scaled text; test at 200% scale.
6Color contrast check.medium
Answer: Use Material color onSurface on background; WebAIM contrast checker for custom colors.
7Focus order for TalkBack.hard
Answer: Logical traversal — set android:importantForAccessibility and focusable order.
8Dark mode images.medium
Answer: Provide -night drawable variants or tint vectors; avoid harsh white flash images.
9Accessibility Compose.medium
Answer: Modifier.semantics, mergeDescendants, custom actions, test with TalkBack.
10Keyboard navigation.hard
Answer: Focusable views, nextFocusForward, action labels for D-pad and switch access.
11Live region for dynamic content.hard
Answer: accessibilityLiveRegion polite for Snackbar-like updates.
12Don't rely on color alone.easy
Answer: Add icons/text for errors — colorblind users need more than red/green.
13Minimum touch target fix.easy
Answer: Increase padding or minHeight/minWidth 48dp on small icons.
14Test with Accessibility Scanner.medium
Answer: Install scanner app; run on each screen; fix suggested issues.
15Legal/compliance motivation.medium
Answer: ADA, EN 301 549, Play accessibility requirements — inclusive apps reach more users.