Skip to content

Fire Mode: Themes and styles#8714

Merged
0nko merged 22 commits into
developfrom
feature/ondrej/fire-mode-fire-theme
Jun 12, 2026
Merged

Fire Mode: Themes and styles#8714
0nko merged 22 commits into
developfrom
feature/ondrej/fire-mode-fire-theme

Conversation

@0nko

@0nko 0nko commented May 28, 2026

Copy link
Copy Markdown
Member

Task/Issue URL: https://app.asana.com/1/137249556945/project/1207418217763355/task/1215197767922294?focus=true

Description

Adds themes, styles and resources for use in the new Fire mode UI.

Note
Only applies to the Tab switcher and the normal omnibar UI, NOT the Input screen, native input widget. Fire mode empty screens in the Tab switcher and Browser are also not included.

Figam: https://www.figma.com/design/9S33EK9qIxZdN1FOIFHM5W/%F0%9F%94%A5-Fire-Mode-Tabs--iOS-Android-?node-id=1846-13998&m=dev

Steps to test this PR

Note

Fire-mode surfaces require a build where Fire mode is available (internal build with the feature enabled).

Fire mode disabled

  • With the feature flag disabled, smoke test the app first and make sure everything looks as before, no orange accents or dark input box anywhere

Before starting the next tests, make sure to enable the Fire tabs feature flags in the internal settings and generate some regular tabs and Fire tabs in dev settings.

Fire theme — Tab switcher

  • Open the tab switcher
  • Switch to Fire mode via the browser-mode toggle
  • Verify the active tab has an orange border

Fire theme — Browser

  • Open the tab switcher
  • Switch to Fire mode via the browser-mode toggle
  • Open the browser and then open a web page
  • Verify the omnibar uses the Fire theme (address bar color, orange accent on the omnibar card/text cursor)
  • Verify the tab switch button now has a fire icon in the corner
  • Trigger a page load and confirm the progress bar uses the fire accent color
  • Trigger pull-to-refresh and confirm its color matches the fire accent

Tab titles

  • Open the tab switcher
  • In Regular mode, open a new (blank) tab and confirm the title reads "New Tab" with Dax image
  • In Fire mode, open a new (blank) tab and confirm the title reads "Fire Tab" with a fire image

Undo snackbar action color

  • Open the tab switcher and witch to Fire mode
  • Close one tab
  • Verify the "Undo" action link is orange
  • While still in Fire mode, go the menu -> Select tabs
  • Select a tab
  • Tap on menu -> Tap on Bookmark tab
  • Confirm the action text renders in the regular (blue) accent color

Fire theme — Dark mode

  • Switch to dark mode
  • Smoke test the UI, mainly the accent color is a more subtle orange (see the Figma design)

Note

Medium Risk
Touches core browser/tab-switcher UI and activity recreation on mode changes; regressions could affect theming, scrolling, or mode toggling outside Fire-only surfaces.

Overview
Introduces Fire mode visual theming for the browser and tab switcher: new Theme.DuckDuckGo.*.Fire styles (mandarin accents, dark omnibar card overlay, fire tab icons/placeholders) and theme attrs such as daxColorOmnibarAccent, daxColorSnackbarAction, and daxOmnibarThemeOverlay. DuckDuckGoActivity gains applyFireTheme; Browser and TabSwitcher opt in when BrowserMode.FIRE, and omnibar/progress/pull-to-refresh/cursor/snackbar styling reads those attrs instead of hard-coded blue.

Tab switcher mode switching drops the bitmap overlay in favor of fade-out → recreate() → fade-in, with scroll-anchor preservation across grid/list toggles. Tab titles move to TabTitleResolver (e.g. “Fire Tab” vs “New Tab”) and flow through TabSwitcherItem.title. ViewModel state consolidates layout type and toggle visibility into viewState; bookmark undo snackbars can override action color to stay blue on fire-themed screens.

Reviewed by Cursor Bugbot for commit f3b79d4. Bugbot is set up for automated code reviews on this repo. Configure here.

0nko commented May 28, 2026

Copy link
Copy Markdown
Member Author

Comment thread app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt
Comment thread app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt Outdated
Comment thread app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt
@0nko 0nko force-pushed the feature/ondrej/fire-mode-fire-theme branch from f1373fa to 16acea2 Compare May 29, 2026 12:07
@0nko 0nko force-pushed the feature/ondrej/fire-mode-fire-theme branch from 512e75b to cd5a7f4 Compare June 1, 2026 08:18
@0nko 0nko force-pushed the feature/ondrej/fire-mode-fire-theme branch from cd5a7f4 to 9902db7 Compare June 1, 2026 09:28
Comment thread app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt
@0nko 0nko force-pushed the feature/ondrej/fire-mode-fire-theme branch from 9902db7 to f283aa0 Compare June 2, 2026 15:40
@0nko 0nko mentioned this pull request Jun 2, 2026
10 tasks
@0nko 0nko force-pushed the feature/ondrej/fire-mode-fire-theme branch from f283aa0 to 8adda28 Compare June 5, 2026 20:13
@0nko 0nko force-pushed the feature/ondrej/fire-mode-fire-theme branch from 8adda28 to ab607e5 Compare June 9, 2026 13:58

@malmstein malmstein left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did not install or smoke-test the APK — these are static-only observations. solid, well-scoped theming change: the applyFireTheme hook on DuckDuckGoActivity with daxColorOmnibarAccent / daxOmnibarThemeOverlay driving the accent is clean, and pulling title resolution into a tested TabTitleResolver is a nice improvement (the at DuckDuckGo suffix fix even drops the old trailing-space bug). no rule violations and no blockers.

two things I'd want answered before approving: the grid↔list scroll-position behaviour change looks unrelated to theming and may be an accidental regression, and the empty-target-mode fade path looks like it can leave the recycler stuck at alpha 0 now that the fallback timer is gone. one minor nit on Uri.parse. also worth a glance: ic_fire_24.xml lost its trailing newline in this diff, looks accidental.

since I haven't exercised it on a device, I'd recommend smoke-testing the mode toggle (especially switching into an empty mode and toggling grid/list) before merging.

scrollToActiveTab(viewModel.viewState.value.tabSwitcherItems)
attachOnScrolledListener()
}
scrollToActiveTab(viewModel.viewState.value.tabSwitcherItems)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dropped getCurrentCenterOffset / scrollToPreviousCenterOffset here, so toggling grid↔list now jumps to the active tab instead of preserving the scroll position. that's a behaviour change that's unrelated to fire theming — is it intentional to fold it into this PR, or fallout from the recreate refactor?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restored scroll preservation, rebuilt around what was actually breaking. the old getCurrentCenterOffset worked in estimated pixel metrics (computeVerticalScroll*) that differ between grid and list, so it drifted on every toggle. now it records the tab you last rested at the top (on scroll-settle) and re-pins that same tab after the swap, rather than re-deriving the anchor each time.

the grid was the tricky part: its post-swap re-layout (notifyDataSetChanged + the card re-measure) kept re-anchoring it a few rows off, so a one-shot scroll got undone. it now re-asserts the anchor on pre-draw for a short window after the toggle, which holds it. verified on device across repeated grid↔list toggles with 100+ tabs.

if (scrolled) firstTimeLoadingTabsList = false
if (freshAfterModeSwitch) {
tabsRecycler.post(::revealNewMode)
if (fadingInAfterRecreate && !fadeInAnimationStarted && it.tabs.isNotEmpty()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the fade-in is guarded on it.tabs.isNotEmpty(), but configureRecycler already set tabsRecycler.alpha = 0f and itemAnimator = null. if we switch to a mode with no tabs the guard never fires, so the recycler stays invisible and fadingInAfterRecreate stays true until a tab eventually arrives. the old code had FADE_IN_FALLBACK_MS for exactly this "both modes empty" case — how does switching into an empty mode look now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the empty-target case is handled further up the stack: #8748 ("Fire Mode: Empty state pages") adds a dedicated empty-state view that replaces the recycler when a mode has no tabs, and skips the fade-out when switching out of an empty mode. so the recycler sitting at alpha 0 here is moot once that lands, and fire mode isn't user-enabled until the stack completes. leaving this as-is in 8714.

val tabs = tabEntities.map { entity ->
val isActive = entity.tabId == activeTab?.tabId
val title = tabTitleResolver.resolveTitle(entity, browserMode)
val uri = entity.url?.let { Uri.parse(it) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the selection branch above uses entity.url?.toUri() but here we still do entity.url?.let { Uri.parse(it) } — can we use toUri() in both for consistency?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done — both branches now use entity.url?.toUri(), and dropped the now-unused android.net.Uri import.

@0nko 0nko force-pushed the feature/ondrej/fire-mode-fire-theme branch from 136f0b4 to f3b79d4 Compare June 11, 2026 09:25

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 136f0b4. Configure here.

Comment thread app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f3b79d4. Configure here.

Comment thread app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt
@0nko

0nko commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

restored the trailing newline on ic_fire_24.xml so it matches develop again. the grid↔list scroll-position change and the empty-mode fade are addressed in the inline threads.

@0nko 0nko requested a review from malmstein June 11, 2026 09:30

@malmstein malmstein left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works as expected, nice work @0nko !

@0nko 0nko merged commit b323491 into develop Jun 12, 2026
28 checks passed
@0nko 0nko deleted the feature/ondrej/fire-mode-fire-theme branch June 12, 2026 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants