MacOS WKWebView Custom Scrollbars
The Problem
WKWebView on macOS does not support standard CSS scrollbar styling:
::-webkit-scrollbarpseudo-elements are ignoredscrollbar-colorandscrollbar-widthCSS properties don't work reliably- Native scrollbars always render with system appearance
This means CSS-based scrollbar theming that works in browsers will NOT work in the native macOS app.
The Solution: Negative Margin Technique
Hide the native scrollbar using pure CSS layout (not pseudo-elements):
- Outer wrapper:
overflow: hiddenclips the native scrollbar - Inner scrollable div:
overflow-y: scroll+marginRight: -20pxpushes scrollbar outside - Padding compensation:
paddingRight: 20pxensures content isn't cut off - Custom overlay: Render a themed scrollbar as a positioned DOM element
Usage
Use the OverlayScrollbar component from @/components/OverlayScrollbar:
import { OverlayScrollbar } from "@/components/OverlayScrollbar";
// Basic usage
<OverlayScrollbar className="h-full">
<div>Your scrollable content here</div>
</OverlayScrollbar>
// With scroll position persistence
const scrollRef = useTabScrollPersistence(tabId);
<OverlayScrollbar
scrollRef={scrollRef}
className="flex-1 h-full"
style={{ backgroundColor: currentTheme.styles.surfacePrimary }}
>
<div>Content with scroll position saved</div>
</OverlayScrollbar>
Component Props
| Prop | Type | Description |
|---|---|---|
children |
ReactNode |
Scrollable content |
className |
string |
CSS classes for outer wrapper |
style |
CSSProperties |
Inline styles for outer wrapper |
scrollRef |
RefObject<HTMLDivElement> |
Optional ref for scroll position access |
Features
- Theme-aware: Uses
currentTheme.styles.borderDefaultfor scrollbar color - Auto-hide: Scrollbar fades out after 1 second of inactivity
- Hover to show: Scrollbar appears when hovering the container
- Drag support: Click and drag the thumb to scroll
- Track click: Click the track to jump to position
- Resize-aware: Updates when content or container size changes
When to Use
Use OverlayScrollbar instead of native overflow-y-auto when:
- The scroll container needs themed scrollbars
- The component renders in the macOS WKWebView app
- You want consistent scrollbar appearance across web and native
When NOT to Use
- Very small scroll areas (the overlay adds complexity)
- Performance-critical lists with thousands of items (consider virtualization)
- Areas where native scrollbar behavior is preferred
Implementation Details
See the full component at: src/components/OverlayScrollbar.tsx
Key constants:
SCROLLBAR_WIDTH = 20- Margin to hide native scrollbar (macOS scrollbar is ~15-17px)- Thumb minimum height: 30px
- Hide delay: 1000ms after scroll stops
- Fade transition: 150ms