Skip to content

Additional design revisions to dark mode theme - #43634

Merged
lukeheath merged 10 commits into
mainfrom
hackathon-ui-polish
Apr 23, 2026
Merged

Additional design revisions to dark mode theme#43634
lukeheath merged 10 commits into
mainfrom
hackathon-ui-polish

Conversation

@lukeheath

@lukeheath lukeheath commented Apr 15, 2026

Copy link
Copy Markdown
Member

Design revisions for #42977

Summary by CodeRabbit

  • New Features

    • Anti-flash spinner behavior with option for immediate display; buttons updated to show loading promptly.
    • Dark mode now respects OS preference when no explicit choice and propagates theme changes across the UI.
    • Icons made customizable for color/size.
    • Tab navigation content gains a fade-in transition.
  • Style

    • Updated dark-mode palette, navigation underline, flash message, dropdown, and loading-overlay visuals.
  • Refactor

    • Simplified org logo handling and centralized theme synchronization.
  • Tests

    • Tests updated to await asynchronous spinner/text rendering.

@codecov

codecov Bot commented Apr 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 55.35714% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.90%. Comparing base (7980b63) to head (e0018a0).
⚠️ Report is 194 commits behind head on main.

Files with missing lines Patch % Lines
frontend/utilities/theme.ts 41.66% 14 Missing ⚠️
frontend/components/icons/LowDiskSpaceHosts.tsx 20.00% 4 Missing ⚠️
frontend/components/FlashMessage/FlashMessage.tsx 0.00% 2 Missing ⚠️
frontend/components/icons/MissingHosts.tsx 33.33% 2 Missing ⚠️
frontend/components/icons/TotalHosts.tsx 33.33% 2 Missing ⚠️
...atesCard/UpdateSoftwareItem/UpdateSoftwareItem.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #43634      +/-   ##
==========================================
- Coverage   66.91%   66.90%   -0.01%     
==========================================
  Files        2600     2601       +1     
  Lines      208361   208423      +62     
  Branches     9283     9315      +32     
==========================================
+ Hits       139415   139454      +39     
- Misses      56273    56296      +23     
  Partials    12673    12673              
Flag Coverage Δ
frontend 54.77% <55.35%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@lukeheath lukeheath changed the title Add 250ms delay to async loading indicators to avoid screen flash Additional design revisions to dark mode theme Apr 21, 2026

body.dark-mode .default-fleet-logo,
body.dark-mode .org-logo-icon--invert-dark {
filter: brightness(0) invert(1);

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.

No longer forcing logo white in dark mode.

// should wrap their routed content in this class with `key={location.pathname}`
// so the element remounts on tab change and re-triggers the animation.
.tab-nav-routed-content {
animation: fade-in 250ms ease-out;

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.

This applies the same page load transition to tabbed nav.


&__card-icon {
body.dark-mode & {
opacity: 0.75;

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.

Don't fade icons in dark mode.

vulnFilters: softwareVulnFilters,
onAddFiltersClick: toggleSoftwareFiltersModal,
})}
<div key={location?.pathname} className="tab-nav-routed-content">

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.

Wrapping so we can target the transition.

background-color: #111214;
}

body.dark-mode .data-table-block thead,

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.

These are the targeted dark mode-specific overrides that were surfaced by Mike and in the issue.


export const isDarkMode = (): boolean => {
return localStorage.getItem(THEME_KEY) === "true";
const systemPrefersDark = (): boolean => {

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 logic in place in this file is:

  1. On first load, set the Fleet UI theme to whatever the system default is.
  2. If the system default changes, change the Fleet theme.
  3. Unless they go to the "My profile" page and manually toggle the theme. In that case, we store the preference in localStorage and will always default to their selection.

@lukeheath
lukeheath marked this pull request as ready for review April 21, 2026 18:03
@lukeheath
lukeheath requested a review from a team as a code owner April 21, 2026 18:03
Copilot AI review requested due to automatic review settings April 21, 2026 18:03

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@lukeheath

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

Copilot AI 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.

Pull request overview

This PR refines the frontend dark mode experience by improving theming behavior (including OS preference following), polishing dark-mode styling across common UI elements, and reducing spinner “flash” by introducing a built-in spinner delay (with corresponding test updates).

Changes:

  • Update theme initialization/toggling to respect system prefers-color-scheme when no explicit user preference is stored and emit a global theme-change event.
  • Introduce anti-flash spinner delay behavior (default 250ms) and update tests/components to account for delayed rendering.
  • Apply dark-mode styling/token improvements across nav, tables, dropdowns, flash toasts, and multiple icons/components.

Reviewed changes

Copilot reviewed 38 out of 38 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
frontend/utilities/theme.ts System preference fallback + live OS theme change listener + centralized DOM apply helper.
frontend/styles/var/colors.ts Add static (un-themed) color tokens for always-colored surfaces.
frontend/styles/var/colors.scss Add nav underline token; refine dark-mode palette; add new SCSS aliases/tokens.
frontend/styles/global/_global.scss Dark-mode global overrides for tables, nav hover, react-select, checkboxes, etc.
frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx Make assertions resilient to async rendering.
frontend/pages/hosts/details/modals/MDMStatusModal/MDMStatusModal.tests.tsx Adjust spinner test for delayed spinner rendering.
frontend/pages/hosts/details/cards/Software/SelfService/components/UpdatesCard/UpdatesCard.tests.tsx Adjust spinner test for delayed spinner rendering.
frontend/pages/hosts/details/cards/Software/SelfService/components/UpdatesCard/UpdateSoftwareItem/UpdateSoftwareItem.tsx Ensure “pending/updating” status spinners show immediately (delay={0}).
frontend/pages/hosts/details/cards/Software/SelfService/components/TileActionStatus/TileActionStatus.tsx Ensure action status spinner shows immediately (delay={0}).
frontend/pages/hosts/details/cards/Software/SelfService/SelfServiceCard/SelfServiceCard.tests.tsx Adjust spinner test for delayed spinner rendering.
frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx Ensure pending status spinner shows immediately (delay={0}).
frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx Wrap routed tab content to re-trigger fade-in animation on route change.
frontend/pages/admin/IntegrationsPage/cards/ConditionalAccess/ConditionalAccess.tests.tsx Adjust spinner test for delayed spinner rendering.
frontend/pages/admin/AdminWrapper.tsx Wrap routed tab content to re-trigger fade-in animation on route change.
frontend/pages/SoftwarePage/SoftwarePage.tsx Wrap routed tab content (and cloned children) to re-trigger fade-in animation on route change.
frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAddPage.tsx Wrap routed tab content (and cloned children) to re-trigger fade-in animation on route change.
frontend/pages/ManageControlsPage/SetupExperience/cards/RunScript/RunScript.tests.tsx Update expectations for spinner anti-flash behavior + async rendering.
frontend/pages/ManageControlsPage/OSSettings/cards/Certificates/components/AddCertificateModal/AddCertificateModal.tests.tsx Use async queries for inputs that may render after loading completes.
frontend/pages/ManageControlsPage/ManageControlsPage.tsx Wrap routed tab content (and cloned children) to re-trigger fade-in animation on route change.
frontend/pages/DashboardPage/cards/HostCountCard/_styles.scss Remove dark-mode icon opacity tweak.
frontend/pages/AccountPage/AccountSidePanel/AccountSidePanel.tsx Subscribe to fleet-theme-change event to keep UI toggle state in sync.
frontend/components/top_nav/SiteTopNav/_styles.scss Use themed nav-active-underline token.
frontend/components/top_nav/SiteTopNav/SiteTopNav.tsx Remove logo inversion prop usage.
frontend/components/icons/TotalHosts.tsx Make icon color themeable via COLORS and optional color prop.
frontend/components/icons/OrgLogoIcon/_styles.scss Remove dark-mode inversion filter styles.
frontend/components/icons/OrgLogoIcon/OrgLogoIcon.jsx Remove invertDark prop and related class logic.
frontend/components/icons/MissingHosts.tsx Make icon color themeable via COLORS and optional color prop.
frontend/components/icons/LowDiskSpaceHosts.tsx Make icon color themeable via COLORS and optional color prop.
frontend/components/icons/Calendar.tsx Add optional size/color props and use shared sizing/tokens.
frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx Use themed colors for active/hover/focus styling; tweak open-state styling logic.
frontend/components/forms/fields/Dropdown/_styles.scss Override react-select hardcoded colors to keep dropdown themed in dark mode.
frontend/components/buttons/Button/Button.tsx Ensure loading spinner on buttons shows immediately (delay={0}).
frontend/components/TargetsInput/_styles.scss Use themed loading overlay variable.
frontend/components/TabNav/_styles.scss Add routed-content fade-in helper class + themed underline color token.
frontend/components/Spinner/Spinner.tsx Add anti-flash delay behavior (render null until delay elapsed).
frontend/components/FlashMessage/_styles.scss Use static (un-themed) foreground colors for always-colored toast surfaces.
frontend/components/FlashMessage/FlashMessage.tsx Use static icon colors for toast surfaces (esp. warning-filled).
frontend/components/AddHostsModal/AddHostsModal.tests.tsx Adjust spinner test for delayed spinner rendering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread frontend/styles/var/colors.scss
Comment thread frontend/components/Spinner/Spinner.tsx
@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR adds an optional anti-flash delay?: number to Spinner and makes it render null until the delay elapses. Many components were updated to pass delay={0} where immediate spinner rendering is required. Tests were converted from synchronous getBy* queries to asynchronous findBy* where they assert spinner or other asynchronously rendered UI. Theme logic was refactored to prefer an explicit localStorage setting then fall back to the OS preference, centralize DOM updates and dispatch a "fleet-theme-change" event, and several icons/styles were converted to use static or themed color tokens. Several routed-content areas are wrapped in a keyed tab-nav-routed-content container.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description provides only a reference to the related issue (42977) but is missing comprehensive information about the changes, testing, and required checklist items from the template. Complete the description template by adding details about what was changed, how it was tested, and checking appropriate boxes from the testing, migrations, and other required sections.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and specifically describes the main change: design revisions focused on the dark mode theme, which aligns with the core objective and changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hackathon-ui-polish

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/components/Spinner/Spinner.tsx (1)

28-42: ⚠️ Potential issue | 🟠 Major

Default delay={250} is a behavior change for every existing call site that didn't explicitly opt in.

The only site updated to use the new delay prop is Button component (with delay={0}). This means approximately 90+ other <Spinner /> usages across the codebase now have a 250ms delay applied without modification. While this prevents flash during quick loads, the component documentation explicitly acknowledges that scenarios representing ongoing work (like pending install/uninstall states) should use delay={0} for immediate feedback.

Either confirm that all remaining call sites are genuinely short-load scenarios where 250ms delay improves UX, or reconsider defaulting delay to 0 and explicitly opting into delay where the anti-flash behavior is desired.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/Spinner/Spinner.tsx` around lines 28 - 42, The default
delay=250 on the Spinner component (ISpinnerProps / Spinner) changes behavior
for all existing call sites; revert the Spinner default to delay = 0 and instead
opt individual callers into the delayed anti-flash behavior by passing
delay={250} where desired (e.g., update the Button usage to explicitly pass
delay={250} or other components that should defer showing the spinner); ensure
ISpinnerProps still declares delay?: number and adjust any docs/comments to
reflect the new default.
🧹 Nitpick comments (1)
frontend/components/top_nav/SiteTopNav/SiteTopNav.tsx (1)

153-180: Optional: hoist logo URL computation out of renderNavItem.

darkLogoURL, lightLogoURL, hasDarkLogo, and orgLogoURL are computed on every renderNavItem call, but only consumed when iconName === "logo". Consider moving these into a useMemo keyed on [config.org_info, darkMode] (or computing inline only within the logo branch) to avoid unnecessary work per nav item and to make the data dependency clearer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/top_nav/SiteTopNav/SiteTopNav.tsx` around lines 153 -
180, The logo URL computation is being repeated on every renderNavItem call;
hoist the logic out of renderNavItem by using a useMemo at the component level
(keyed on [config.org_info, darkMode]) to compute darkLogoURL, lightLogoURL,
hasDarkLogo and orgLogoURL once, or alternatively move those variable
assignments inside the iconName === "logo" branch so they are only computed when
needed; update renderNavItem to use the memoized orgLogoURL (or the inline
values) and keep the rendering of OrgLogoIcon unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/components/FlashMessage/_styles.scss`:
- Around line 20-22: Add a blank line before the double-slash comment blocks
that precede the color declaration using $static-white (the comment starting
"Use static (un-themed) white: the flash toast...") and the later comment block
around lines 43–45 so the scss/double-slash-comment-empty-line-before rule is
satisfied; edit the _styles.scss where the color: $static-white; declaration and
the second comment occur and insert one empty line immediately above each `//`
comment.

In `@frontend/components/forms/fields/Dropdown/_styles.scss`:
- Around line 11-13: Add a blank line before each `//` comment block in the
Dropdown styles to satisfy Stylelint's
scss/double-slash-comment-empty-line-before rule; specifically, insert an empty
line immediately above the comment that begins with "// Override react-select's
hardcoded `.Select-value { color: `#aaa`; }`..." (the block that sets color:
$core-fleet-black) and likewise add an empty line before the later comment
around line ~176-177 so each double-slash comment is preceded by a blank line.

In `@frontend/components/icons/TotalHosts.tsx`:
- Around line 14-15: The SVG currently uses static hex values via COLORS[color]
and COLORS["core-fleet-white"] so it won’t respond to dark-mode overrides;
update the TotalHosts component to use CSS custom properties instead (e.g.,
derive fill and background as CSS variable strings rather than hex), replace
usages of fillColor and bgColor with those CSS variable references, and ensure
the SVG elements use those variables (so dark-mode values from colors.scss
apply).

In `@frontend/components/Spinner/Spinner.tsx`:
- Around line 41-51: The spinner can remain hidden if delay changes from >0 to 0
because the useEffect early-returns and visible stays false; in the Spinner
component update the effect (and/or state init) so that when delay === 0 you
call setVisible(true) immediately instead of returning early, and otherwise
always schedule a timeout (const id = setTimeout(() => setVisible(true), delay))
and clear it in the cleanup (clearTimeout(id)); reference useState(visible,
setVisible), useEffect, and the delay prop/ISpinnerProps when applying this
change.

In `@frontend/styles/var/colors.scss`:
- Around line 105-113: The SCSS variable --ui-fleet-black-25 is empty and must
be assigned a valid color to avoid broken dark-mode styles; update the
declaration for --ui-fleet-black-25 in frontend/styles/var/colors.scss (the same
token referenced by $ui-fleet-black-25/var(--ui-fleet-black-25)) to a sensible
mid-dark hex between --ui-fleet-black-33 and --ui-fleet-black-10 (for example,
set it to a hex like `#565A66`) so components using that token render correctly in
dark mode.

In `@frontend/styles/var/colors.ts`:
- Around line 50-53: The color token "static-white" is incorrectly set to
"#e8eaf0" (a light grey-blue) which mismatches its name and reduces contrast for
components like FlashMessage that expect pure white; either rename the token
(e.g., "static-off-white" or "static-foreground-light") and update all callers
(references to "static-white" in FlashMessage/_styles.scss) to the new name, or
change the value of "static-white" to "#FFFFFF" and then re-check WCAG contrast
for toast backgrounds ("$core-vibrant-blue", "$ui-error", "$ui-success"); also
ensure any documentation or usage comments near "static-black" and
"static-white" are updated to reflect the chosen fix.

In `@frontend/utilities/theme.ts`:
- Around line 52-57: The MediaQueryList returned by window.matchMedia may not
support addEventListener, so update the listener registration in initTheme (the
block where you call window.matchMedia("(prefers-color-scheme: dark)") and
reference media) to first check for media.addEventListener and use it if
available, otherwise fall back to media.addListener; ensure the handler still
checks localStorage.getItem(THEME_KEY) and calls applyDarkMode(e.matches, true)
(or the legacy event signature) so behavior remains identical across
environments.
- Around line 12-20: Guard all direct localStorage accesses with try-catch and
fall back to systemPrefersDark() (or no-op on writes). Specifically, wrap the
localStorage.getItem(THEME_KEY) call inside isDarkMode() in a try-catch and
return systemPrefersDark() on any exception; likewise wrap any
localStorage.setItem/removeItem calls used by initTheme and the theme
persistence functions (e.g., setTheme/toggleTheme) so they silently fail (no-op)
when storage is unavailable. Ensure you reference THEME_KEY consistently and do
not change the external behavior except for the safe fallback when storage
throws.

---

Outside diff comments:
In `@frontend/components/Spinner/Spinner.tsx`:
- Around line 28-42: The default delay=250 on the Spinner component
(ISpinnerProps / Spinner) changes behavior for all existing call sites; revert
the Spinner default to delay = 0 and instead opt individual callers into the
delayed anti-flash behavior by passing delay={250} where desired (e.g., update
the Button usage to explicitly pass delay={250} or other components that should
defer showing the spinner); ensure ISpinnerProps still declares delay?: number
and adjust any docs/comments to reflect the new default.

---

Nitpick comments:
In `@frontend/components/top_nav/SiteTopNav/SiteTopNav.tsx`:
- Around line 153-180: The logo URL computation is being repeated on every
renderNavItem call; hoist the logic out of renderNavItem by using a useMemo at
the component level (keyed on [config.org_info, darkMode]) to compute
darkLogoURL, lightLogoURL, hasDarkLogo and orgLogoURL once, or alternatively
move those variable assignments inside the iconName === "logo" branch so they
are only computed when needed; update renderNavItem to use the memoized
orgLogoURL (or the inline values) and keep the rendering of OrgLogoIcon
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 20b7077e-a19d-481b-b120-1818fc62b490

📥 Commits

Reviewing files that changed from the base of the PR and between 3ef0a3f and 61f5e5c.

📒 Files selected for processing (38)
  • frontend/components/AddHostsModal/AddHostsModal.tests.tsx
  • frontend/components/FlashMessage/FlashMessage.tsx
  • frontend/components/FlashMessage/_styles.scss
  • frontend/components/Spinner/Spinner.tsx
  • frontend/components/TabNav/_styles.scss
  • frontend/components/TargetsInput/_styles.scss
  • frontend/components/buttons/Button/Button.tsx
  • frontend/components/forms/fields/Dropdown/_styles.scss
  • frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx
  • frontend/components/icons/Calendar.tsx
  • frontend/components/icons/LowDiskSpaceHosts.tsx
  • frontend/components/icons/MissingHosts.tsx
  • frontend/components/icons/OrgLogoIcon/OrgLogoIcon.jsx
  • frontend/components/icons/OrgLogoIcon/_styles.scss
  • frontend/components/icons/TotalHosts.tsx
  • frontend/components/top_nav/SiteTopNav/SiteTopNav.tsx
  • frontend/components/top_nav/SiteTopNav/_styles.scss
  • frontend/pages/AccountPage/AccountSidePanel/AccountSidePanel.tsx
  • frontend/pages/DashboardPage/cards/HostCountCard/_styles.scss
  • frontend/pages/ManageControlsPage/ManageControlsPage.tsx
  • frontend/pages/ManageControlsPage/OSSettings/cards/Certificates/components/AddCertificateModal/AddCertificateModal.tests.tsx
  • frontend/pages/ManageControlsPage/SetupExperience/cards/RunScript/RunScript.tests.tsx
  • frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAddPage.tsx
  • frontend/pages/SoftwarePage/SoftwarePage.tsx
  • frontend/pages/admin/AdminWrapper.tsx
  • frontend/pages/admin/IntegrationsPage/cards/ConditionalAccess/ConditionalAccess.tests.tsx
  • frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx
  • frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx
  • frontend/pages/hosts/details/cards/Software/SelfService/SelfServiceCard/SelfServiceCard.tests.tsx
  • frontend/pages/hosts/details/cards/Software/SelfService/components/TileActionStatus/TileActionStatus.tsx
  • frontend/pages/hosts/details/cards/Software/SelfService/components/UpdatesCard/UpdateSoftwareItem/UpdateSoftwareItem.tsx
  • frontend/pages/hosts/details/cards/Software/SelfService/components/UpdatesCard/UpdatesCard.tests.tsx
  • frontend/pages/hosts/details/modals/MDMStatusModal/MDMStatusModal.tests.tsx
  • frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx
  • frontend/styles/global/_global.scss
  • frontend/styles/var/colors.scss
  • frontend/styles/var/colors.ts
  • frontend/utilities/theme.ts
💤 Files with no reviewable changes (2)
  • frontend/components/icons/OrgLogoIcon/_styles.scss
  • frontend/pages/DashboardPage/cards/HostCountCard/_styles.scss

Comment thread frontend/components/FlashMessage/_styles.scss
Comment thread frontend/components/forms/fields/Dropdown/_styles.scss
Comment thread frontend/components/icons/TotalHosts.tsx
Comment thread frontend/components/Spinner/Spinner.tsx
Comment thread frontend/styles/var/colors.scss
Comment thread frontend/styles/var/colors.ts
Comment thread frontend/utilities/theme.ts
Comment thread frontend/utilities/theme.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx (2)

269-276: ⚠️ Potential issue | 🟡 Minor

Behavior change: placeholder/indicator overrides now gated on menuIsOpen instead of isFocused.

Per the PR summary, this block previously applied under state.isFocused and a duplicated state.menuIsOpen block was removed. As a result, for non-button dropdowns, when the control is tab-focused but the menu is closed, the placeholder and indicator no longer receive the ui-fleet-black-75 override — they'll fall back to whatever color the base placeholder/dropdownIndicator styles define. Please confirm this is intentional (e.g., matches updated Figma for focus vs. open states); otherwise keep it under isFocused or add both conditions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx` around
lines 269 - 276, The recent change moved the placeholder/indicator color
override from being applied when state.isFocused to only when state.menuIsOpen,
causing tab-focused (but closed) non-button dropdowns to lose the
ui-fleet-black-75 styling; update DropdownWrapper.tsx to apply the override when
either state.isFocused or state.menuIsOpen is true (i.e., use a combined
condition) so the ".dropdown-wrapper__placeholder" and
".dropdown-wrapper__indicator path" rules still get the
COLORS["ui-fleet-black-75"] styling on focus as well as when the menu is open.

202-224: ⚠️ Potential issue | 🟡 Minor

Add & prefix to the selector on line 202 to match the correct CSS-in-JS pattern.

The selector .react-select__control--is-focused (line 202) is missing the & prefix and should be &.react-select__control--is-focused. Without it, the styles target descendant elements rather than the control itself, so the backgroundColor and color overrides won't apply when the control is focused.

This is inconsistent with the non-button variant at line 258, which correctly uses &.react-select__control--is-focused. The bug is partially masked in the button variant because the identical styles are applied via state.menuIsOpen (line 212), which covers the "menu open" case but not "tab-focused with closed menu."

Proposed fix
-          ".react-select__control--is-focused": {
+          "&.react-select__control--is-focused": {

Additionally, note that the button variant applies focus-state overrides only when state.menuIsOpen, whereas the non-button variant applies them when state.isFocused. This semantic difference may cause the button dropdown to lack focus-state styling when navigated to via Tab but not yet opened.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx` around
lines 202 - 224, Update the focused-control selector in DropdownWrapper to use
the ampersand prefix so the styles apply to the control itself: change the
selector `.react-select__control--is-focused` to
`&.react-select__control--is-focused` inside the style object that currently
sets backgroundColor, boxShadow, placeholder and indicator colors; also align
the focus condition with the non-button variant by using state.isFocused
(instead of only state.menuIsOpen) for the button variant so keyboard tab focus
receives the same overrides as the open-menu state (adjust the block that
currently spreads ...(state.menuIsOpen && { ... }) or add a similar
...(state.isFocused && { ... }) branch as appropriate).
♻️ Duplicate comments (6)
frontend/styles/var/colors.scss (1)

111-111: ⚠️ Potential issue | 🟠 Major

Fill in the dark-mode --ui-fleet-black-25 token.

An empty custom property resolves to an empty var(...) substitution, so any dark-mode declaration using $ui-fleet-black-25 can become invalid at computed-value time.

🎨 Proposed fix
-  --ui-fleet-black-25: ;
+  --ui-fleet-black-25: `#555a66`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/styles/var/colors.scss` at line 111, The dark-mode custom property
--ui-fleet-black-25 in frontend/styles/var/colors.scss is empty and must be
assigned a valid color value; update the declaration to a concrete color (for
example rgba(0,0,0,0.25) or a corresponding hex/rgba from your design tokens) so
any references to var(--ui-fleet-black-25) resolve correctly and don't produce
invalid computed values.
frontend/styles/var/colors.ts (1)

50-53: ⚠️ Potential issue | 🟡 Minor

Clarify whether static-white should be true white or an off-white token.

static-white is currently #e8eaf0, which is not white and can subtly reduce foreground contrast for callers expecting a theme-independent white. Either rename it to an off-white/foreground token or set it to #FFFFFF and re-check toast contrast.

Possible fixes
-  "static-white": "#e8eaf0",
+  "static-white": "#FFFFFF",
   "static-black": "#192147",

or rename the token and update call sites:

-  "static-white": "#e8eaf0",
+  "static-foreground-light": "#e8eaf0",
   "static-black": "#192147",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/styles/var/colors.ts` around lines 50 - 53, The token "static-white"
currently uses `#e8eaf0` but is misleadingly named; either change the value of the
"static-white" token to true white (`#FFFFFF`) and then re-check contrast in
toast/tooltip components (update any visual tests), or rename the token to a
clear off-white name like "static-offwhite" (or "static-gray-100") and then
update all call sites that reference "static-white" to the new token name
(search for the "static-white" identifier in the codebase and replace usages in
toast/tooltip/foreground styles). Ensure whichever path you choose leaves a
semantically correct token name/value and that contrast checks for
toasts/tooltips are validated.
frontend/components/forms/fields/Dropdown/_styles.scss (1)

11-13: ⚠️ Potential issue | 🟡 Minor

Add blank lines before these // comment blocks.

Stylelint still reports scss/double-slash-comment-empty-line-before here. Insert an empty line before the comments on Line 11 and Line 176.

Proposed fix
     &-label {
       line-height: 38px;
       font-size: $x-small;
+
       // Override react-select's hardcoded `.Select-value { color: `#aaa`; }` so
       // the selected value uses themed text color (esp. needed for dark mode).
       color: $core-fleet-black;
@@
     > .Select-control {
       border-radius: $border-radius;
+
       // Override react-select's hardcoded white background when open.
       background-color: $core-fleet-white;
     }

Also applies to: 174-177

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/forms/fields/Dropdown/_styles.scss` around lines 11 - 13,
Add a blank line before the double-slash comment blocks that start with "//
Override react-select's hardcoded `.Select-value { color: `#aaa`; }`..." and the
other similar comment near the bottom (the two occurrences flagged by
scss/double-slash-comment-empty-line-before) so Stylelint no longer reports the
rule; simply insert an empty line immediately above each of those // comment
lines in _styles.scss.
frontend/components/FlashMessage/_styles.scss (1)

20-22: ⚠️ Potential issue | 🟡 Minor

Add blank lines before these // comment blocks.

Stylelint still reports scss/double-slash-comment-empty-line-before here. Insert an empty line before the comments on Line 20 and Line 43.

Proposed fix
 .flash-message {
   display: flex;
   align-items: center;
   justify-content: center;
+
   // Use static (un-themed) white: the flash toast is always a colored
   // surface, so foreground should stay light regardless of dark mode.
   color: $static-white;
@@
   &--warning-filled {
     background-color: $ui-warning;
+
     // Yellow is light enough that foreground should be dark in BOTH modes.
     // Use static (un-themed) tokens so dark mode doesn't flip to light text.
     color: $static-black;

Also applies to: 41-45

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/FlashMessage/_styles.scss` around lines 20 - 22, Add a
blank line before each double-slash comment block to satisfy
scss/double-slash-comment-empty-line-before: insert an empty line immediately
above the comment that precedes the color: $static-white; declaration (the "//
Use static (un-themed) white..." comment) and likewise add a blank line before
the comment block around the other flash toast comments referenced (the block
covering the lines near the second occurrence). No other changes—just ensure
there is an empty line above each "// ..." comment so stylelint stops reporting
the rule.
frontend/utilities/theme.ts (2)

52-57: ⚠️ Potential issue | 🟠 Major

Check MediaQueryList.addEventListener before calling it.

window.matchMedia can exist while the returned MediaQueryList lacks addEventListener (older Safari / some test environments using jsdom mocks), which would throw during initTheme(). The codebase already uses the addEventListener/addListener fallback pattern in useIsMobileWidth.tsx — mirror it here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/utilities/theme.ts` around lines 52 - 57, The MediaQueryList
returned from window.matchMedia may not support addEventListener; in the
initTheme logic where you create const media =
window.matchMedia("(prefers-color-scheme: dark)"), guard the listener
registration by checking for media.addEventListener and falling back to
media.addListener if absent (and pass the same handler that calls
applyDarkMode(e.matches, true) only when localStorage.getItem(THEME_KEY) is
null). Update the listener registration around the media variable to use this
addEventListener/addListener fallback pattern so older Safari/jsdom environments
don't throw.

15-15: ⚠️ Potential issue | 🟠 Major

Guard localStorage access to prevent startup crashes.

initTheme() runs before React mounts, and localStorage.getItem/setItem can throw when storage is disabled or unavailable (e.g., strict privacy modes, sandboxed iframes). The unguarded calls at lines 15, 39, and 55 will crash app initialization. Wrap access in try/catch and fall back to systemPrefersDark() on read failure; no-op on write failure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/utilities/theme.ts` at line 15, The unguarded localStorage access in
initTheme (e.g., the stored = localStorage.getItem(THEME_KEY) read and
subsequent localStorage.setItem writes) can throw and crash startup; wrap reads
and writes in try/catch: for reads (getItem) catch errors and treat as "no
stored value" so you fall back to calling systemPrefersDark(); for writes
(setItem) catch and swallow the error (no-op) so failures to persist do not
break initTheme. Update all localStorage touches in initTheme (and any helpers
that reference THEME_KEY) to follow this pattern.
🧹 Nitpick comments (2)
frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx (1)

737-737: Minor: findByText is redundant after waitFor on spinner removal, but harmless.

Since the preceding waitFor already ensures the spinner is gone and the content has rendered, screen.getByText would also work. Using findByText is fine and arguably more resilient — no change needed.

Also applies to: 782-784

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx`
at line 737, The test uses await screen.findByText(/Creating a new report for/i)
after a waitFor that ensures the spinner is gone, which is redundant; change
these assertions to synchronous lookups (screen.getByText(/Creating a new report
for/i)) to remove the unnecessary async call — update the specific occurrences
of await screen.findByText in EditQueryForm.tests.tsx (including the shown call
and the similar instances around the later block) to use screen.getByText (or
keep findByText if you prefer the more resilient async form).
frontend/utilities/theme.ts (1)

24-35: Overlapping toggles can prematurely strip the transition class.

Each applyDarkMode(_, true) call schedules a setTimeout that unconditionally removes theme-transition after 300ms. If the OS fires a change event (or the user toggles) while an earlier transition is still in flight, the first timer will remove the class mid-transition on the second change. Consider tracking the pending timeout and clearing it before scheduling a new one.

♻️ Proposed fix
+let transitionTimeout: ReturnType<typeof setTimeout> | null = null;
+
 const applyDarkMode = (dark: boolean, animate: boolean): void => {
   if (animate) {
     document.body.classList.add("theme-transition");
-    setTimeout(() => {
+    if (transitionTimeout !== null) {
+      clearTimeout(transitionTimeout);
+    }
+    transitionTimeout = setTimeout(() => {
       document.body.classList.remove("theme-transition");
+      transitionTimeout = null;
     }, TRANSITION_MS);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/utilities/theme.ts` around lines 24 - 35, applyDarkMode currently
schedules a setTimeout that unconditionally removes the "theme-transition" class
(using TRANSITION_MS), which allows overlapping calls to clear the class
mid-transition; modify applyDarkMode to track the pending timeout (e.g., a
module-scoped variable like pendingTransitionTimeout) and
clearTimeout(pendingTransitionTimeout) before scheduling a new one so only the
most recent timer removes "theme-transition"; keep the existing behavior of
adding the class when animate is true, toggling "dark-mode", and dispatching the
"fleet-theme-change" CustomEvent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/components/Spinner/Spinner.tsx`:
- Line 42: The Spinner component's signature now returns JSX.Element | null
which breaks callers expecting JSX.Element (e.g., the PolicyDetailsPage
function). Fix by either updating the return type of affected components (e.g.,
change PolicyDetailsPage's declared return from JSX.Element to JSX.Element |
null) or update call sites that directly return <Spinner /> to handle the
nullable case (wrap the call in a null-coalescing fallback or conditional render
so the returned expression is always a JSX.Element). Locate the Spinner
component (ISpinnerProps -> Spinner) and the PolicyDetailsPage function and
apply one consistent approach across callers.

In `@frontend/styles/global/_global.scss`:
- Around line 285-290: In the dark-mode card button hover rules, replace the
themed color variable $core-fleet-white with the unthemed $static-white so hover
backgrounds remain visible; update the selector block that targets
body.dark-mode .card .button--inverse:hover, .button--inverse-alert:hover,
.button--text-icon:hover, and .button--icon:hover to use $static-white for
background-color instead of $core-fleet-white.

---

Outside diff comments:
In `@frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx`:
- Around line 269-276: The recent change moved the placeholder/indicator color
override from being applied when state.isFocused to only when state.menuIsOpen,
causing tab-focused (but closed) non-button dropdowns to lose the
ui-fleet-black-75 styling; update DropdownWrapper.tsx to apply the override when
either state.isFocused or state.menuIsOpen is true (i.e., use a combined
condition) so the ".dropdown-wrapper__placeholder" and
".dropdown-wrapper__indicator path" rules still get the
COLORS["ui-fleet-black-75"] styling on focus as well as when the menu is open.
- Around line 202-224: Update the focused-control selector in DropdownWrapper to
use the ampersand prefix so the styles apply to the control itself: change the
selector `.react-select__control--is-focused` to
`&.react-select__control--is-focused` inside the style object that currently
sets backgroundColor, boxShadow, placeholder and indicator colors; also align
the focus condition with the non-button variant by using state.isFocused
(instead of only state.menuIsOpen) for the button variant so keyboard tab focus
receives the same overrides as the open-menu state (adjust the block that
currently spreads ...(state.menuIsOpen && { ... }) or add a similar
...(state.isFocused && { ... }) branch as appropriate).

---

Duplicate comments:
In `@frontend/components/FlashMessage/_styles.scss`:
- Around line 20-22: Add a blank line before each double-slash comment block to
satisfy scss/double-slash-comment-empty-line-before: insert an empty line
immediately above the comment that precedes the color: $static-white;
declaration (the "// Use static (un-themed) white..." comment) and likewise add
a blank line before the comment block around the other flash toast comments
referenced (the block covering the lines near the second occurrence). No other
changes—just ensure there is an empty line above each "// ..." comment so
stylelint stops reporting the rule.

In `@frontend/components/forms/fields/Dropdown/_styles.scss`:
- Around line 11-13: Add a blank line before the double-slash comment blocks
that start with "// Override react-select's hardcoded `.Select-value { color:
`#aaa`; }`..." and the other similar comment near the bottom (the two occurrences
flagged by scss/double-slash-comment-empty-line-before) so Stylelint no longer
reports the rule; simply insert an empty line immediately above each of those //
comment lines in _styles.scss.

In `@frontend/styles/var/colors.scss`:
- Line 111: The dark-mode custom property --ui-fleet-black-25 in
frontend/styles/var/colors.scss is empty and must be assigned a valid color
value; update the declaration to a concrete color (for example rgba(0,0,0,0.25)
or a corresponding hex/rgba from your design tokens) so any references to
var(--ui-fleet-black-25) resolve correctly and don't produce invalid computed
values.

In `@frontend/styles/var/colors.ts`:
- Around line 50-53: The token "static-white" currently uses `#e8eaf0` but is
misleadingly named; either change the value of the "static-white" token to true
white (`#FFFFFF`) and then re-check contrast in toast/tooltip components (update
any visual tests), or rename the token to a clear off-white name like
"static-offwhite" (or "static-gray-100") and then update all call sites that
reference "static-white" to the new token name (search for the "static-white"
identifier in the codebase and replace usages in toast/tooltip/foreground
styles). Ensure whichever path you choose leaves a semantically correct token
name/value and that contrast checks for toasts/tooltips are validated.

In `@frontend/utilities/theme.ts`:
- Around line 52-57: The MediaQueryList returned from window.matchMedia may not
support addEventListener; in the initTheme logic where you create const media =
window.matchMedia("(prefers-color-scheme: dark)"), guard the listener
registration by checking for media.addEventListener and falling back to
media.addListener if absent (and pass the same handler that calls
applyDarkMode(e.matches, true) only when localStorage.getItem(THEME_KEY) is
null). Update the listener registration around the media variable to use this
addEventListener/addListener fallback pattern so older Safari/jsdom environments
don't throw.
- Line 15: The unguarded localStorage access in initTheme (e.g., the stored =
localStorage.getItem(THEME_KEY) read and subsequent localStorage.setItem writes)
can throw and crash startup; wrap reads and writes in try/catch: for reads
(getItem) catch errors and treat as "no stored value" so you fall back to
calling systemPrefersDark(); for writes (setItem) catch and swallow the error
(no-op) so failures to persist do not break initTheme. Update all localStorage
touches in initTheme (and any helpers that reference THEME_KEY) to follow this
pattern.

---

Nitpick comments:
In
`@frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx`:
- Line 737: The test uses await screen.findByText(/Creating a new report for/i)
after a waitFor that ensures the spinner is gone, which is redundant; change
these assertions to synchronous lookups (screen.getByText(/Creating a new report
for/i)) to remove the unnecessary async call — update the specific occurrences
of await screen.findByText in EditQueryForm.tests.tsx (including the shown call
and the similar instances around the later block) to use screen.getByText (or
keep findByText if you prefer the more resilient async form).

In `@frontend/utilities/theme.ts`:
- Around line 24-35: applyDarkMode currently schedules a setTimeout that
unconditionally removes the "theme-transition" class (using TRANSITION_MS),
which allows overlapping calls to clear the class mid-transition; modify
applyDarkMode to track the pending timeout (e.g., a module-scoped variable like
pendingTransitionTimeout) and clearTimeout(pendingTransitionTimeout) before
scheduling a new one so only the most recent timer removes "theme-transition";
keep the existing behavior of adding the class when animate is true, toggling
"dark-mode", and dispatching the "fleet-theme-change" CustomEvent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2d027842-5410-4013-ae4e-749940e3c726

📥 Commits

Reviewing files that changed from the base of the PR and between 3ef0a3f and 61f5e5c.

📒 Files selected for processing (38)
  • frontend/components/AddHostsModal/AddHostsModal.tests.tsx
  • frontend/components/FlashMessage/FlashMessage.tsx
  • frontend/components/FlashMessage/_styles.scss
  • frontend/components/Spinner/Spinner.tsx
  • frontend/components/TabNav/_styles.scss
  • frontend/components/TargetsInput/_styles.scss
  • frontend/components/buttons/Button/Button.tsx
  • frontend/components/forms/fields/Dropdown/_styles.scss
  • frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx
  • frontend/components/icons/Calendar.tsx
  • frontend/components/icons/LowDiskSpaceHosts.tsx
  • frontend/components/icons/MissingHosts.tsx
  • frontend/components/icons/OrgLogoIcon/OrgLogoIcon.jsx
  • frontend/components/icons/OrgLogoIcon/_styles.scss
  • frontend/components/icons/TotalHosts.tsx
  • frontend/components/top_nav/SiteTopNav/SiteTopNav.tsx
  • frontend/components/top_nav/SiteTopNav/_styles.scss
  • frontend/pages/AccountPage/AccountSidePanel/AccountSidePanel.tsx
  • frontend/pages/DashboardPage/cards/HostCountCard/_styles.scss
  • frontend/pages/ManageControlsPage/ManageControlsPage.tsx
  • frontend/pages/ManageControlsPage/OSSettings/cards/Certificates/components/AddCertificateModal/AddCertificateModal.tests.tsx
  • frontend/pages/ManageControlsPage/SetupExperience/cards/RunScript/RunScript.tests.tsx
  • frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAddPage.tsx
  • frontend/pages/SoftwarePage/SoftwarePage.tsx
  • frontend/pages/admin/AdminWrapper.tsx
  • frontend/pages/admin/IntegrationsPage/cards/ConditionalAccess/ConditionalAccess.tests.tsx
  • frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx
  • frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx
  • frontend/pages/hosts/details/cards/Software/SelfService/SelfServiceCard/SelfServiceCard.tests.tsx
  • frontend/pages/hosts/details/cards/Software/SelfService/components/TileActionStatus/TileActionStatus.tsx
  • frontend/pages/hosts/details/cards/Software/SelfService/components/UpdatesCard/UpdateSoftwareItem/UpdateSoftwareItem.tsx
  • frontend/pages/hosts/details/cards/Software/SelfService/components/UpdatesCard/UpdatesCard.tests.tsx
  • frontend/pages/hosts/details/modals/MDMStatusModal/MDMStatusModal.tests.tsx
  • frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx
  • frontend/styles/global/_global.scss
  • frontend/styles/var/colors.scss
  • frontend/styles/var/colors.ts
  • frontend/utilities/theme.ts
💤 Files with no reviewable changes (2)
  • frontend/components/icons/OrgLogoIcon/_styles.scss
  • frontend/pages/DashboardPage/cards/HostCountCard/_styles.scss

Comment thread frontend/components/Spinner/Spinner.tsx
Comment thread frontend/styles/global/_global.scss

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx (1)

202-224: ⚠️ Potential issue | 🟡 Minor

Reserve the “down” colors for the open state.

Because state.isFocused can remain true after the menu closes, Line 202 can leave the closed button styled with the active/down placeholder and chevron colors until blur. Keep the focus background, but let the existing placeholder/indicator focus styling use the “over” color and only apply “down” when menuIsOpen.

🎨 Proposed fix
           ...(state.isFocused && {
             backgroundColor: COLORS["ui-fleet-black-5"],
             boxShadow: "none",
-            ".dropdown-wrapper__placeholder": {
-              color: COLORS["ui-fleet-black-75-down"],
-            },
-            ".dropdown-wrapper__indicator path": {
-              stroke: COLORS["ui-fleet-black-75-down"],
-            },
           }),
           ...(state.menuIsOpen && {
             backgroundColor: COLORS["ui-fleet-black-5"],
             ".dropdown-wrapper__placeholder": {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx` around
lines 202 - 224, The focus block (state.isFocused) currently applies the “down”
colors to .dropdown-wrapper__placeholder and .dropdown-wrapper__indicator path
which can persist after the menu closes; change the state.isFocused style to
only set the focus backgroundColor (keep boxShadow: "none") and set
placeholder/indicator colors to the “over” variant instead, and reserve the
“down” color overrides for the state.menuIsOpen block (also keep the
.dropdown-wrapper__indicator svg rotation/transition inside menuIsOpen). Target
the style objects referenced (state.isFocused, state.menuIsOpen,
".dropdown-wrapper__placeholder", ".dropdown-wrapper__indicator path",
".dropdown-wrapper__indicator svg") in DropdownWrapper.tsx to implement this
swap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx`:
- Around line 202-224: The focus block (state.isFocused) currently applies the
“down” colors to .dropdown-wrapper__placeholder and .dropdown-wrapper__indicator
path which can persist after the menu closes; change the state.isFocused style
to only set the focus backgroundColor (keep boxShadow: "none") and set
placeholder/indicator colors to the “over” variant instead, and reserve the
“down” color overrides for the state.menuIsOpen block (also keep the
.dropdown-wrapper__indicator svg rotation/transition inside menuIsOpen). Target
the style objects referenced (state.isFocused, state.menuIsOpen,
".dropdown-wrapper__placeholder", ".dropdown-wrapper__indicator path",
".dropdown-wrapper__indicator svg") in DropdownWrapper.tsx to implement this
swap.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 29680cab-76e7-4f54-8a80-7438aac03355

📥 Commits

Reviewing files that changed from the base of the PR and between 61f5e5c and e0018a0.

📒 Files selected for processing (2)
  • frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx
  • frontend/styles/var/colors.scss

@lukeheath

Copy link
Copy Markdown
Member Author

@sgress454 Also tagging you if you want to get your review on.

@RachelElysia RachelElysia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for all this! Sorry empty states will not go in 4.85.0. I've had to prioritize on-call + current sprint work this week <3

@lukeheath
lukeheath merged commit 255be3f into main Apr 23, 2026
19 checks passed
@lukeheath
lukeheath deleted the hackathon-ui-polish branch April 23, 2026 15:13
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.

4 participants