11import { parseStyle } from '../utils/styles' ;
22
3- export function scrollbarStyle ( {
4- scrollbar,
5- overflow,
6- } : {
3+ interface ScrollbarStyleProps {
74 scrollbar ?: string | boolean | number ;
85 overflow ?: string ;
9- } ) {
6+ }
7+
8+ /**
9+ * Creates cross-browser compatible scrollbar styles
10+ *
11+ * Supports both Firefox (scrollbar-width, scrollbar-color) and
12+ * WebKit/Chromium browsers (::-webkit-scrollbar)
13+ */
14+ export function scrollbarStyle ( { scrollbar, overflow } : ScrollbarStyleProps ) {
1015 // Check if scrollbar is defined
1116 if ( ! scrollbar && scrollbar !== 0 ) return ;
1217
1318 // Support true as alias for thin
14- let value = scrollbar === true ? 'thin' : scrollbar ;
19+ const value = scrollbar === true || scrollbar === '' ? 'thin' : scrollbar ;
1520 const { mods, colors, values } = parseStyle ( String ( value ) ) ;
1621 const style = { } ;
1722
18- style [ 'scrollbar-color' ] = 'var(--scrollbar-thumb-color) transparent' ;
23+ // Default colors for scrollbar
24+ const defaultThumbColor = 'var(--scrollbar-thumb-color)' ;
25+ const defaultTrackColor = 'var(--scrollbar-track-color)' ;
26+
27+ // Setup default Firefox scrollbar style
28+ style [ 'scrollbar-color' ] = `${ defaultThumbColor } transparent` ;
1929
20- // Modifiers
30+ // Default scrollbar size
31+ const defaultSize = '8px' ;
32+ const sizeValue = values [ 0 ] || defaultSize ;
33+
34+ // Process modifiers
2135 if ( mods . includes ( 'thin' ) ) {
2236 style [ 'scrollbar-width' ] = 'thin' ;
23- }
24- if ( mods . includes ( 'none' ) ) {
37+ } else if ( values . includes ( 'none' ) ) {
2538 style [ 'scrollbar-width' ] = 'none' ;
2639 style [ 'scrollbar-color' ] = 'transparent transparent' ;
27- }
28- if ( mods . includes ( 'auto' ) ) {
40+ // Also hide WebKit scrollbars
41+ style [ '&::-webkit-scrollbar' ] = {
42+ width : '0px' ,
43+ height : '0px' ,
44+ display : 'none' ,
45+ } ;
46+
47+ return style ;
48+ } else if ( mods . includes ( 'auto' ) ) {
2949 style [ 'scrollbar-width' ] = 'auto' ;
3050 }
51+
52+ // Handle scrollbar gutter behavior
3153 if ( mods . includes ( 'stable' ) || mods . includes ( 'both-edges' ) ) {
54+ // scrollbar-gutter is supported in newer browsers only
3255 style [ 'scrollbar-gutter' ] = mods . includes ( 'both-edges' )
3356 ? 'stable both-edges'
3457 : 'stable' ;
3558 }
3659
37- // Custom size (all values are sizes)
38- const sizeValue = values [ 0 ] ;
60+ // Custom size setup for WebKit
3961 if ( sizeValue ) {
4062 style [ '&::-webkit-scrollbar' ] = {
4163 ...( style [ '&::-webkit-scrollbar' ] || { } ) ,
@@ -44,25 +66,57 @@ export function scrollbarStyle({
4466 } ;
4567 }
4668
47- // Colors (support up to 3: thumb, track, corner)
69+ // Extract colors (support up to 3: thumb, track, corner)
70+ // These will be used in various places throughout the function
71+ const thumbColor = colors && colors [ 0 ] ? colors [ 0 ] : defaultThumbColor ;
72+ const trackColor = colors && colors [ 1 ] ? colors [ 1 ] : defaultTrackColor ;
73+ const cornerColor = colors && colors [ 2 ] ? colors [ 2 ] : trackColor ;
74+
75+ // Apply colors if they are specified
4876 if ( colors && colors . length ) {
49- const thumb = colors [ 0 ] || 'var(--scrollbar-thumb-color)' ;
50- const track = colors [ 1 ] || 'var(-- scrollbar-track- color)' ;
51- const corner = colors [ 2 ] || track ;
52- style [ 'scrollbar-color' ] = ` ${ thumb } ${ track } ` ;
53- if ( ! style [ '&::-webkit-scrollbar-corner ' ] ) {
54- style [ '&::-webkit-scrollbar-corner ' ] = { } ;
77+ // Firefox
78+ style [ ' scrollbar-color' ] = ` ${ thumbColor } ${ trackColor } ` ;
79+
80+ // WebKit - always set these for consistency
81+ if ( ! style [ '&::-webkit-scrollbar' ] ) {
82+ style [ '&::-webkit-scrollbar' ] = { } ;
5583 }
56- style [ '&::-webkit-scrollbar-corner' ] . background = corner ;
84+ style [ '&::-webkit-scrollbar' ] [ 'background' ] = trackColor ;
85+
86+ style [ '&::-webkit-scrollbar-track' ] = {
87+ ...( style [ '&::-webkit-scrollbar-track' ] || { } ) ,
88+ background : trackColor ,
89+ } ;
90+
91+ style [ '&::-webkit-scrollbar-thumb' ] = {
92+ ...( style [ '&::-webkit-scrollbar-thumb' ] || { } ) ,
93+ background : thumbColor ,
94+ } ;
95+
96+ style [ '&::-webkit-scrollbar-corner' ] = {
97+ ...( style [ '&::-webkit-scrollbar-corner' ] || { } ) ,
98+ background : cornerColor ,
99+ } ;
57100 }
58101
59- // always: force scrollbars to show (requires overflow)
102+ // Handle ' always' mode : force scrollbars to show
60103 if ( mods . includes ( 'always' ) ) {
61104 style [ 'overflow' ] = overflow || 'scroll' ;
62- style [ 'scrollbar-gutter' ] = style [ 'scrollbar-gutter' ] || 'always' ;
105+
106+ // Use auto for WebKit browsers since they don't support 'always'
107+ // This is closer to the expected behavior
108+ if ( ! style [ 'scrollbar-gutter' ] ) {
109+ style [ 'scrollbar-gutter' ] = 'stable' ;
110+ }
111+
112+ // Ensure scrollbars appear in WebKit even with little content
113+ if ( ! style [ '&::-webkit-scrollbar' ] ) {
114+ style [ '&::-webkit-scrollbar' ] = { } ;
115+ }
116+ style [ '&::-webkit-scrollbar' ] [ 'display' ] = 'block' ;
63117 }
64118
65- // Legacy styled mod
119+ // Enhanced ' styled' mode with better transitions and appearance
66120 if ( mods . includes ( 'styled' ) ) {
67121 const baseTransition = [
68122 'background var(--transition)' ,
@@ -72,27 +126,39 @@ export function scrollbarStyle({
72126 'height var(--transition)' ,
73127 'border var(--transition)' ,
74128 ] . join ( ', ' ) ;
129+
130+ // Firefox
75131 style [ 'scrollbar-width' ] = style [ 'scrollbar-width' ] || 'thin' ;
76132 style [ 'scrollbar-color' ] =
77- style [ 'scrollbar-color' ] ||
78- 'var(--scrollbar-thumb-color) var(--scrollbar-track-color)' ;
133+ style [ 'scrollbar-color' ] || `${ defaultThumbColor } ${ defaultTrackColor } ` ;
134+
135+ // WebKit
79136 style [ '&::-webkit-scrollbar' ] = {
80- ...( style [ '&::-webkit-scrollbar' ] || { } ) ,
81- width : sizeValue || '8px' ,
82- height : sizeValue || '8px' ,
83- background : 'var(--scrollbar-track-color)' ,
137+ width : sizeValue ,
138+ height : sizeValue ,
84139 transition : baseTransition ,
140+ background : defaultTrackColor ,
141+ ...( style [ '&::-webkit-scrollbar' ] || { } ) ,
85142 } ;
143+
86144 style [ '&::-webkit-scrollbar-thumb' ] = {
87- background : 'var(--scrollbar-thumb-color)' ,
88- borderRadius : '8px' ,
89- minHeight : '24px' ,
145+ 'border-radius' : '8px' ,
146+ 'min-height' : '24px' ,
90147 transition : baseTransition ,
148+ background : defaultThumbColor ,
149+ ...( style [ '&::-webkit-scrollbar-thumb' ] || { } ) ,
91150 } ;
151+
152+ style [ '&::-webkit-scrollbar-track' ] = {
153+ background : defaultTrackColor ,
154+ transition : baseTransition ,
155+ ...( style [ '&::-webkit-scrollbar-track' ] || { } ) ,
156+ } ;
157+
92158 style [ '&::-webkit-scrollbar-corner' ] = {
93- ...( style [ '&::-webkit-scrollbar-corner' ] || { } ) ,
94- background : 'var(--scrollbar-track-color)' ,
159+ background : defaultTrackColor ,
95160 transition : baseTransition ,
161+ ...( style [ '&::-webkit-scrollbar-corner' ] || { } ) ,
96162 } ;
97163 }
98164
0 commit comments