Skip to content

fix(ui): use native platform scrollbars instead of custom ::-webkit-scrollbar rules - #230

Open
bsneed wants to merge 1 commit into
AtomicBot-ai:mainfrom
bsneed:fix/native-scrollbars
Open

fix(ui): use native platform scrollbars instead of custom ::-webkit-scrollbar rules#230
bsneed wants to merge 1 commit into
AtomicBot-ai:mainfrom
bsneed:fix/native-scrollbars

Conversation

@bsneed

@bsneed bsneed commented Aug 9, 2026

Copy link
Copy Markdown

Describe Your Changes

On macOS in dark mode, scrollbars render as bright white bars that are permanently visible. This happens because the custom ::-webkit-scrollbar pseudo-element rules opt out of native overlay scrollbar behavior. Once any ::-webkit-scrollbar style is defined, macOS stops using its native overlay scrollbars and switches to a permanently visible custom-rendered track.

This PR removes the ::-webkit-scrollbar rules and adds color-scheme: light dark to :root, which tells the native scrollbar to follow the OS light/dark theme automatically. The standards-based scrollbar-width: thin and scrollbar-color properties are preserved. These hint at thinner scrollbars with appropriate colors without interfering with native behavior on any platform.

The old /* Firefox scrollbar */ comment was removed because those properties (scrollbar-width, scrollbar-color) are standards-based CSS, not Firefox-specific. The properties themselves are unchanged.

What changed:

  • Removed all ::-webkit-scrollbar, ::-webkit-scrollbar-track, and ::-webkit-scrollbar-thumb rules (these disable native scrollbar behavior)
  • Added color-scheme: light dark on :root (enables theme-aware native scrollbars)
  • Kept scrollbar-width: thin and scrollbar-color (standards-based, no side effects)

Tested on:

  • macOS (WKWebView), native overlay scrollbars restored, dark mode respected
  • Linux / Ubuntu 26.04 with Wayland/GNOME (WebKitGTK), builds and renders correctly

Screenshots below.

Fixes Issues

  • N/A

Self Checklist

  • Added relevant comments, esp in complex areas
  • Updated docs (for bug fixes / features)
  • Created issues for follow-up changes or refactoring needed

@bsneed

bsneed commented Aug 9, 2026

Copy link
Copy Markdown
Author

macOS Settings, "Automatic":

image

macOS before:

image

macOS after:

image

Gnome 50/Ubuntu 26.04:

image

@bsneed
bsneed marked this pull request as ready for review August 13, 2026 00:07
@bsneed
bsneed requested a review from Vect0rM as a code owner August 13, 2026 00:07
@Vect0rM

Vect0rM commented Aug 14, 2026

Copy link
Copy Markdown
Member

Thanks for contributing to Atomic Chat, and for tracking this one down with
testing on both macOS and Ubuntu. The direction is right: the custom
::-webkit-scrollbar styling has been fighting the platform here, and getting
rid of it is the outcome we want.

One thing needs to change before we can merge, plus a question and an optional
suggestion.

1. color-scheme should follow the app theme, not the OS.

Our theme is an independent user setting (light / dark / auto), applied as
a dark class on <html> in web-app/src/providers/ThemeProvider.tsx. With
color-scheme: light dark the native scrollbars follow the OS instead, so a
user running the app in Light on a dark macOS (or the reverse) gets scrollbars
inverted relative to the UI. The same applies to native controls: we still use
real <select> elements in a few places (settings/general.tsx,
settings/attachments.tsx, settings/mcp-servers.tsx, the two dflash dialogs),
and their OS-rendered dropdowns would follow the system theme too.

Could you bind it to the class instead?

:root {
  color-scheme: light;
}

:root.dark {
  color-scheme: dark;
}

Note it has to be :root.dark rather than .dark: the project defines
@custom-variant dark (&:is(.dark *)), so the existing .dark * rule never
matched <html> itself. Using :root.dark also fixes the root scroller, which
never actually picked up the dark thumb color.

2. A question about the mechanism.

Since Safari 18.2, setting scrollbar-width makes WebKit ignore
::-webkit-scrollbar customizations entirely
(https://webkit.org/blog/16301/webkit-features-in-safari-18-2/, same conclusion
in home-assistant/frontend#25442). We keep
* { scrollbar-width: thin } in this PR, and scrollbar-color only landed in
Safari 26.2. So on a recent WKWebView the pseudo-element rules should already
have been inert, which suggests the white bars came from the standard-property
path and that the actual fix here is color-scheme, not the removal.

Which macOS / WebKit version did you test on? If the trigger really is
scrollbar-width: thin, users on older WKWebView would still see the bug after
this change. Trying the two edits separately would settle it.

3. Optional: keep a fallback for older engines.

scrollbar-color is only Baseline as of Dec 2025, so on a non-evergreen
WebView2 or an older WebKitGTK the removal means a full-width default scrollbar.
Wrapping the old rules in @supports not (scrollbar-color: auto) keeps them as
a fallback without conflicting with the standard properties on modern engines.

Minor: the six-line comment reads as a changelog of the change; a line or two on
why color-scheme is needed would age better. Also, CI has not run on this
branch yet - it is CSS-only, but let's get a green run before merging.

@bsneed
bsneed force-pushed the fix/native-scrollbars branch from 1ebabaa to 05508f1 Compare August 18, 2026 17:53
@bsneed

bsneed commented Aug 18, 2026

Copy link
Copy Markdown
Author

Thanks for the review @Vect0rM, good catches.

1. color-scheme bound to app theme:
Done. Split into :root { color-scheme: light; } and :root.dark { color-scheme: dark; } so it tracks the app's theme class instead of the OS. Good call on :root.dark vs .dark — the @custom-variant descendant selector wouldn't have matched <html> itself, so the dark scrollbar colors were never actually reaching the root scroller.

2. The mechanism:
I tested on macOS 15.7.3 (Sequoia) with the bundled WKWebView. You're right that Safari 18.2+ ignores ::-webkit-scrollbar when scrollbar-width is set, so the pseudo-element rules were likely already inert on my test system. That means color-scheme is doing the real work here. I didn't test the two changes independently — I'll do that to confirm, but the logic tracks: the white bars were coming from scrollbar-color rendering against the wrong color-scheme, not from the webkit pseudo-elements.

3. Fallback:
Added. The old ::-webkit-scrollbar rules are now wrapped in @supports not (scrollbar-color: auto) so they only activate on engines where the standards-based path isn't available. On modern engines they're fully inert.

I've amended my commit. Lemme know what you think, thanks so much!

@Vect0rM

Vect0rM commented Aug 19, 2026

Copy link
Copy Markdown
Member

Thanks for the turnaround, @bsneed — points 1 and 3 are both in, and the rewritten comment now explains why color-scheme has to track the class rather than narrating the diff. That one's closed. 🙏

I built and inspected the output rather than eyeballing the CSS, since there's still no CI on this branch:

  • vite build on your branch with current main merged in — exit 0.

  • The theme binding is correct. dark goes on document.documentElement (ThemeProvider.tsx:16), so :root.dark matches. color-scheme isn't declared anywhere else in the project, so nothing competes with it.

  • The compiled CSS comes out in the right order and survives minification:

    :root{color-scheme:light}:root.dark{color-scheme:dark}
    *{scrollbar-width:thin;scrollbar-color:oklch(.708 0 0/30%)transparent}
    .dark *{scrollbar-color:oklch(.985 0 0/20%)transparent}
    @supports not (scrollbar-color:auto){::-webkit-scrollbar{width:8px;...}}

    Lightning CSS even downgrades oklch to hex inside the @supports branch and leaves it as oklch outside — so the fallback really is compiled for old engines. Mechanically the patch does what it says.

That's the good news. One thing I'd like changed, and the question from last round has become more load-bearing rather than less.

1. The fallback is gated on the wrong feature

@supports not (scrollbar-color: auto) doesn't describe the condition we care about. What opts a page out of native overlay scrollbars is defining ::-webkit-scrollbar at all — that's the premise of your own PR description. And what makes WebKit ignore those pseudo-elements is scrollbar-width, from Safari 18.2 (the link from my last comment).

So on any WebKit that lacks scrollbar-color, this @supports branch is true and the pseudo-element rules come back. That includes the machine you reported the bug from: macOS 15.7.3. Two possibilities, and they lead to opposite conclusions:

  • If the Safari 18.2 precedence holds, the rules are inert there — the entire block is dead CSS on every WebKit from 18.2 up, and it only ever activates on genuinely old engines.
  • If it doesn't hold, we've re-enabled the exact rules this PR set out to remove, on the exact platform the bug was reported on.

Either way the condition should be the one that actually matters:

@supports not (scrollbar-width: thin) {
  /* ...old rules... */
}

That targets engines which can't do the standards path at all, and it can never re-enable the pseudo-elements on WebKit ≥ 18.2. Same fallback intent, no chance of undoing the fix.

2. The A/B test, again — and what the title promises

Last round you offered to test the two edits separately and then amended a single commit, so we still don't have that data point. It matters more now, because of something in your own answer: if the pseudo-elements were already inert on 18.x and scrollbar-color isn't supported there either, then neither custom path was styling anything — the white bars were the UA's own rendering under color-scheme: light, and the permanently visible part is coming from scrollbar-width: thin, which this PR keeps.

If that's right, this change fixes the colour but not the persistence, while the title and description promise native overlay behaviour is restored. Your "after" screenshot can't settle it — a still frame can't show auto-hide.

Could you run it on the 15.7.3 box:

  • (a) color-scheme only, old ::-webkit-scrollbar rules left in place;
  • (b) this PR as it stands;

and say whether the bars auto-hide in each? If they don't auto-hide in (b), the honest fix probably involves dropping scrollbar-width: thin too, and the description needs adjusting.

Worth saying plainly: neither of us can see this. I'm on macOS 26.5.2 / Safari 26.5.2, where scrollbar-color is supported and the fallback branch is dead — the platform where this could misfire is the one no reviewer here has in front of them. That's why I'm leaning on you for the measurement rather than guessing.

3. Two small ones

  • The root scroller still gets the light thumb in dark mode. You fixed this for color-scheme with :root.dark, but scrollbar-color is still on .dark *, which doesn't match <html> — the same descendant-combinator gap. :root.dark, .dark * would make the two consistent. Low impact in practice, since body is overflow-hidden and the root scroller rarely shows, so treat it as tidiness.
  • Same gap inside the fallback: .dark ::-webkit-scrollbar-thumb doesn't reach the root scroller either.

Change 1 is what I'd like before merging; 2 is a question I need answered rather than a diff; 3 is optional. None of it is a knock on the direction — fighting the platform for scrollbar styling was the wrong trade, and you're the one who noticed. Thanks for staying with this through a second round. 🖱️

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