Skip to content

Commit 7300a36

Browse files
committed
feat(scrollbar): 添加自定义滚动条样式支持
1 parent c7b46ac commit 7300a36

File tree

2 files changed

+85
-27
lines changed

2 files changed

+85
-27
lines changed

src/index.css

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -78,47 +78,63 @@ b, strong, th,
7878
font-weight: calc(var(--font-weight, 400) + 200) !important;
7979
}
8080

81-
/* Global scrollbar styling — thin, semi-transparent, auto-hide on idle */
82-
::-webkit-scrollbar {
81+
/*
82+
* Scrollbar styling — two tiers:
83+
*
84+
* 1. Standard properties (scrollbar-width, scrollbar-color) always apply.
85+
* These provide the baseline thin, semi-transparent scrollbar on all
86+
* platforms (Safari 17.4+, Firefox, Chrome 121+).
87+
*
88+
* 2. ::-webkit-scrollbar pseudo-elements are gated behind the
89+
* .webkit-scrollbar-ok class on <html>. A runtime probe in main.tsx
90+
* adds this class only when WebKit custom scrollbar rendering actually
91+
* works. When active, ::-webkit-scrollbar disables the standard
92+
* properties (browser behaviour), giving us border-radius, transitions,
93+
* and finer control.
94+
*/
95+
96+
/* ── Tier 1: Standard scrollbar properties (always active) ────────── */
97+
* {
98+
scrollbar-width: thin;
99+
scrollbar-color: transparent transparent;
100+
}
101+
*:hover {
102+
scrollbar-color: rgba(0, 0, 0, 0.08) transparent;
103+
}
104+
html[data-theme="dark"] *:hover {
105+
scrollbar-color: rgba(255, 255, 255, 0.08) transparent;
106+
}
107+
108+
/* ── Tier 2: WebKit custom scrollbar (only when probe succeeds) ───── */
109+
.webkit-scrollbar-ok ::-webkit-scrollbar {
83110
width: 5px !important;
84111
height: 5px !important;
85112
}
86-
::-webkit-scrollbar-track {
113+
.webkit-scrollbar-ok ::-webkit-scrollbar-track {
87114
background: transparent !important;
88115
}
89-
::-webkit-scrollbar-thumb {
116+
.webkit-scrollbar-ok ::-webkit-scrollbar-thumb {
90117
background: transparent !important;
91118
border-radius: 3px !important;
92119
transition: background 0.3s ease;
93120
}
94-
::-webkit-scrollbar-corner {
121+
.webkit-scrollbar-ok ::-webkit-scrollbar-corner {
95122
background: transparent !important;
96123
}
97-
:hover::-webkit-scrollbar-thumb {
124+
.webkit-scrollbar-ok :hover::-webkit-scrollbar-thumb {
98125
background: rgba(0, 0, 0, 0.08) !important;
99126
}
100-
:hover::-webkit-scrollbar-thumb:hover {
127+
.webkit-scrollbar-ok :hover::-webkit-scrollbar-thumb:hover {
101128
background: rgba(0, 0, 0, 0.18) !important;
102129
}
103-
html[data-theme="dark"] :hover::-webkit-scrollbar-thumb {
130+
html[data-theme="dark"].webkit-scrollbar-ok :hover::-webkit-scrollbar-thumb {
104131
background: rgba(255, 255, 255, 0.08) !important;
105132
}
106-
html[data-theme="dark"] :hover::-webkit-scrollbar-thumb:hover {
133+
html[data-theme="dark"].webkit-scrollbar-ok :hover::-webkit-scrollbar-thumb:hover {
107134
background: rgba(255, 255, 255, 0.18) !important;
108135
}
109-
/* Firefox scrollbar */
110-
* {
111-
scrollbar-width: thin;
112-
scrollbar-color: transparent transparent;
113-
}
114-
*:hover {
115-
scrollbar-color: rgba(0, 0, 0, 0.08) transparent;
116-
}
117-
html[data-theme="dark"] *:hover {
118-
scrollbar-color: rgba(255, 255, 255, 0.08) transparent;
119-
}
120136

121-
/* Override antd Bubble.List scrollbar — it sets its own CSS-in-JS styles */
137+
/* ── Override antd Bubble.List scrollbar ──────────────────────────── */
122138
.ant-bubble-list-scroll-box {
123139
scrollbar-color: transparent transparent !important;
124140
}
@@ -128,23 +144,23 @@ html[data-theme="dark"] *:hover {
128144
html[data-theme="dark"] .ant-bubble-list-scroll-box:hover {
129145
scrollbar-color: rgba(255, 255, 255, 0.08) transparent !important;
130146
}
131-
.ant-bubble-list-scroll-box::-webkit-scrollbar {
147+
.webkit-scrollbar-ok .ant-bubble-list-scroll-box::-webkit-scrollbar {
132148
width: 5px !important;
133149
}
134-
.ant-bubble-list-scroll-box::-webkit-scrollbar-thumb {
150+
.webkit-scrollbar-ok .ant-bubble-list-scroll-box::-webkit-scrollbar-thumb {
135151
background-color: transparent !important;
136152
border-radius: 3px !important;
137153
}
138-
.ant-bubble-list-scroll-box:hover::-webkit-scrollbar-thumb {
154+
.webkit-scrollbar-ok .ant-bubble-list-scroll-box:hover::-webkit-scrollbar-thumb {
139155
background-color: rgba(0, 0, 0, 0.08) !important;
140156
}
141-
.ant-bubble-list-scroll-box:hover::-webkit-scrollbar-thumb:hover {
157+
.webkit-scrollbar-ok .ant-bubble-list-scroll-box:hover::-webkit-scrollbar-thumb:hover {
142158
background-color: rgba(0, 0, 0, 0.18) !important;
143159
}
144-
html[data-theme="dark"] .ant-bubble-list-scroll-box:hover::-webkit-scrollbar-thumb {
160+
html[data-theme="dark"].webkit-scrollbar-ok .ant-bubble-list-scroll-box:hover::-webkit-scrollbar-thumb {
145161
background-color: rgba(255, 255, 255, 0.08) !important;
146162
}
147-
html[data-theme="dark"] .ant-bubble-list-scroll-box:hover::-webkit-scrollbar-thumb:hover {
163+
html[data-theme="dark"].webkit-scrollbar-ok .ant-bubble-list-scroll-box:hover::-webkit-scrollbar-thumb:hover {
148164
background-color: rgba(255, 255, 255, 0.18) !important;
149165
}
150166

src/main.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,48 @@ import ReactDOM from 'react-dom/client';
33
import AppRoot from './App';
44
import './index.css';
55

6+
// Probe whether ::-webkit-scrollbar custom rendering actually works.
7+
// On macOS, WKWebView compiled against older SDKs may recognise the pseudo-
8+
// element (blocking standard scrollbar-color) without rendering it correctly,
9+
// producing a solid-black native scrollbar. The probe creates an off-screen
10+
// scrollable div, applies a custom scrollbar width via a scoped stylesheet,
11+
// and checks whether the layout width changes. If it does, the custom
12+
// scrollbar pipeline is functional and we enable the ::-webkit-scrollbar rules
13+
// in index.css by adding the `webkit-scrollbar-ok` class to <html>.
14+
(() => {
15+
const outer = document.createElement('div');
16+
Object.assign(outer.style, {
17+
position: 'fixed',
18+
top: '-9999px',
19+
left: '-9999px',
20+
width: '100px',
21+
height: '100px',
22+
overflow: 'scroll',
23+
visibility: 'hidden',
24+
});
25+
const inner = document.createElement('div');
26+
inner.style.height = '200px';
27+
outer.appendChild(inner);
28+
document.documentElement.appendChild(outer);
29+
30+
const defaultWidth = outer.offsetWidth - outer.clientWidth;
31+
32+
const style = document.createElement('style');
33+
style.textContent = '#__scrollbar_probe::-webkit-scrollbar { width: 1px !important; }';
34+
document.head.appendChild(style);
35+
outer.id = '__scrollbar_probe';
36+
// Force re-layout after stylesheet injection
37+
void outer.offsetWidth;
38+
const customWidth = outer.offsetWidth - outer.clientWidth;
39+
40+
outer.remove();
41+
style.remove();
42+
43+
if (customWidth !== defaultWidth) {
44+
document.documentElement.classList.add('webkit-scrollbar-ok');
45+
}
46+
})();
47+
648
// Disable native context menu (reload, inspect element, etc.) in production builds.
749
// Custom context menus (antd Dropdown with trigger={['contextMenu']}) are unaffected
850
// since they use React synthetic events, not the browser's native context menu.

0 commit comments

Comments
 (0)