Common Dark Mode Rendering Bugs in Doctor Appointment Apps: Causes and Fixes

All of these stem from mixing static design assets with dynamic theming APIs and from not testing the full user journey under a true dark‑mode environment.

February 24, 2026 · 6 min read · Common Issues

1. What causes dark‑mode rendering bugs in doctor‑appointment apps

Root causeWhy it breaks in a medical‑booking context
Hard‑coded light colorsUI designers often paste HEX values (#FFFFFF, #F5F5F5) directly into layouts. When the system theme switches, those colors stay bright, washing out text on a dark background and making medical details unreadable.
Missing android:forceDarkAllowed="false" (Android) or prefers-color-scheme handling (Web)Android 10+ can automatically invert light‑theme assets. If the app bundles a custom calendar or time‑picker that wasn’t designed for inversion, the widget becomes illegible or mis‑aligned, causing users to mis‑read appointment slots.
Incorrect use of ThemeOverlay / CSS variablesOverlay themes are meant to replace only a subset of attributes. When a developer overrides the primary color but leaves background or surface colors untouched, contrast ratios drop below WCAG 2.1 AA, triggering accessibility violations.
Bitmap assets without dark variantsIcons for “doctor”, “clinic”, or “prescription” are often supplied only as PNGs. In dark mode the system does not recolor them, so they appear as solid white blobs on a black background, confusing users during the “Select a doctor” flow.
Dynamic theming logic tied to user‑profile dataSome apps change UI colors based on the specialty (e.g., cardiology = red). If that logic runs after the dark‑mode check, the resulting color may be a bright red on a dark surface, violating contrast rules.
Third‑party SDKs that ignore the host themeCalendar widgets, payment gateways, or analytics overlays often ship with their own light‑theme resources. When the host app switches to dark, those SDK views remain light, breaking visual continuity during checkout or login.
Improper handling of elevation/shadow in dark modeElevation is expressed as a semi‑transparent overlay in Material Design. When developers force a solid background color, shadows disappear, making the hierarchy of screens (e.g., “Choose a time slot” vs “Confirm”) ambiguous.

All of these stem from mixing static design assets with dynamic theming APIs and from not testing the full user journey under a true dark‑mode environment.

---

2. Real‑world impact

---

3. Concrete manifestations in doctor‑appointment apps

  1. Invisible “Book” button on the time‑slot screen – Light‑colored text (#FFFFFF) on a dark surface (#121212).
  2. Calendar dates rendered as white squares with no contrast – The native Android MaterialCalendarView inherits the system dark theme, but custom day‑cell backgrounds remain #FFFFFF.
  3. Doctor avatar icons appear as solid white blobs – PNG assets without a dark‑mode counterpart.
  4. Form field placeholders disappearandroid:hint color set to @color/white while the background switches to dark, making the hint invisible.
  5. Bottom navigation bar icons lose their tint – The navigation component uses app:itemIconTint="@color/colorPrimary" where colorPrimary is a bright blue that blends into the dark background.
  6. Payment SDK overlay shows a light‑theme credit‑card form – Users see white input fields on a black backdrop, breaking the visual flow and causing data‑entry errors.
  7. Accessibility contrast failures – WCAG 2.1 AA requires a 4.5:1 contrast ratio for normal text. Dark‑mode screenshots often fall to 2.1:1, triggering automated audit failures.

---

4. How to detect dark‑mode rendering bugs

Detection methodWhat to look forTools & SUSA integration
Automated visual regressionPixel‑differences between light and dark screenshots for every screen flow (login → search doctor → checkout).Upload the APK to SUSA, enable the *curious* and *accessibility* personas, and let the platform generate Playwright/Appium scripts that capture both themes.
Contrast analysisRatio < 4.5:1 for any text or UI element in dark mode.SUSA’s WCAG 2.1 AA audit runs automatically for each persona; failures are flagged with element selectors.
Runtime theme toggle testingSwitch system theme while the app is running and observe UI state changes.Use the SUSA CLI (susatest-agent --theme dark) to launch the app in a dark‑mode session and record logs for missing resources.
Resource inspectionSearch the codebase for hard‑coded light colors (#FFF, #FAFAFA).Static analysis integrated into CI (e.g., gradlew lint) can be complemented by SUSA’s “adversarial” persona that deliberately forces dark mode on every screen.
Third‑party SDK screenshotsCapture screenshots of embedded SDK views (payment, calendar) under dark mode.SUSA’s flow tracking will surface “PASS/FAIL” verdicts for each critical flow; a fail in a payment step often indicates an SDK‑theme mismatch.
User‑session analyticsSpike in “session abandoned at checkout” after dark‑mode rollout.Correlate SUSA’s coverage analytics (untapped elements list) with telemetry to pinpoint screens that never rendered correctly.

---

5. Fixes for each example (code‑level guidance)

1. Invisible “Book” button

Android (XML)


<Button
    android:id="@+id/btnBook"
    style="?attr/materialButtonStyle"
    android:text="@string/book"
    android:backgroundTint="?attr/colorPrimary"
    android:textColor="?attr/colorOnPrimary"/>

*Define colorOnPrimary in values-night/colors.xml*


<color name="colorOnPrimary">#FFFFFF</color> <!-- light text on dark primary -->

Web (CSS)


button.book {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

/* dark theme */
@media (prefers-color-scheme: dark) {
  :root {
    --color-primary: #1E1E1E;
    --color-on-primary: #E0E0E0;
  }
}

2. Calendar dates with white background

Android (Kotlin)


val calendar = MaterialCalendarView(this)
calendar.setDayViewDecorator(object : DayViewDecorator {
    override fun shouldDecorate(day: CalendarDay) = true
    override fun decorate(view: DayViewFacade) {
        view.addSpan(object : ForegroundColorSpan(
            ContextCompat.getColor(this@MainActivity, R.color.calendarDayText))
        )
        view.setBackgroundDrawable(
            ContextCompat.getDrawable(this@MainActivity, R.drawable.bg_day_dark))
    }
})

Create bg_day_dark.xml with a semi‑transparent dark drawable.

Web (Playwright test snippet generated by SUSA)


await expect(page.locator('.day-cell')).toHaveCSS('background-color', 'rgb(30,30,30)');

3. Doctor avatar icons

*Convert PNGs to vector assets (.svg or Android VectorDrawable) and provide a dark‑mode tint.*

Android


<ImageView
    android:src="@drawable/ic_doctor"
    android:tint="?attr/colorOnSurface"/>

Define colorOnSurface in values-night/colors.xml (#CCCCCC).

Web


img.avatar {
  filter: invert(80%); /* fallback for raster assets */
}
@media (prefers-color-scheme: dark) {
  img.avatar { filter: none; } /* use SVG with proper fill */
}

4. Missing placeholder text

Android


<EditText
    android:id="@+id/etSymptoms"
    android:hint="@string/hint_symptoms"
    android:textColorHint="?attr/colorOnSurface"/>

Add colorOnSurface dark variant.

Web


input::placeholder {
  color: var(--color-placeholder);
}
@media (prefers-color-scheme: dark) {
  :root { --color-placeholder: #AAAAAA; }
}

5. Bottom navigation icon tint

Android (using Material Components)


<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottomNav"
    app:itemIconTint="@color/bottom_nav_icon_tint"
    app:itemTextColor="@color/bottom_nav_icon_tint"/>

bottom_nav_icon_tint.xml (color state list) with night variant:


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="?attr/colorOnSurface" android:state_checked="true"/>
    <item android:color="?attr/colorOnSurface" android:state_checked="false"/>
</selector>

6. Light‑theme payment SDK

*Most SDKs expose a theme setter.*


PaymentSdk.initialize(this)
PaymentSdk.setTheme(PaymentSdk.Theme.DARK) // force dark

If the SDK lacks a dark mode, wrap it in a DialogFragment with a dark background and apply a custom style:


<style name="DarkPaymentDialog" parent="Theme.MaterialComponents.DayNight.Dialog">
    <item name="android:windowBackground">@color/black</item>
    <item name="android:textColor">@color/white</item>
</style>

7. Accessibility contrast failures

Run SUSA’s WCAG audit; it returns element selectors and the computed contrast ratio. Fix by adjusting the color token in the design system:


$primary-dark: #0A84FF; // meets 4.5:1 on $surface-dark (#121212)

Regenerate both light and dark token files (design-tokens-light.json, design-tokens-dark.json) and commit them to the repo.

---

6. Prevention: catching dark‑mode bugs before release

  1. Design‑system enforcement
  1. Persona‑driven automated testing with SUSA
  1. Night‑mode CI gate
  1. Static resource audit
  1. Third‑party SDK compliance checklist
  1. Continuous accessibility monitoring
  1. User‑feedback loop

By embedding theme‑aware resource management, persona‑driven autonomous testing, and CI gate enforcement, a doctor‑appointment app can ship with confidence that dark‑mode users—whether they are a *curious* teenager checking a pediatrician or an *elderly* patient scheduling a follow‑up—will see a fully functional, accessible UI.

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free