@edmicro/olkit 2.1.6 → 2.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,310 +1,310 @@
1
- /* =============================================================================
2
- OLKIT COMPONENT ISOLATION & RESILIENCE
3
- Đảm bảo các component của @edmicro/olkit hoạt động độc lập, không bị ảnh hưởng
4
- bởi các CSS Resets unlayered bên ngoài (như *, *::before { padding: 0; })
5
- ============================================================================= */
6
-
7
- /* 1. Button Component Isolation */
8
- ol-button {
9
- display: inline-flex;
10
- vertical-align: middle;
11
- flex-shrink: 0;
12
- }
13
-
14
- ol-button > button {
15
- appearance: button;
16
- -webkit-appearance: button;
17
- box-sizing: border-box !important;
18
- display: inline-flex !important;
19
- align-items: center !important;
20
- justify-content: center !important;
21
- line-height: 1 !important;
22
- font-family: inherit;
23
- cursor: pointer;
24
- border-style: solid;
25
- }
26
-
27
- /* Padding ngang nút có chữ (bảo vệ khỏi `* { padding: 0 }`).
28
- Figma 2026-09-15: xs=12 sm=12 base=16 l=20 xl=24 — KHÔNG cộng bù viền. */
29
- ol-button > button.px-3 {
30
- padding-left: var(--spacing-3, 0.75rem) !important;
31
- padding-right: var(--spacing-3, 0.75rem) !important;
32
- }
33
- ol-button > button.px-4 {
34
- padding-left: var(--spacing-4, 1rem) !important;
35
- padding-right: var(--spacing-4, 1rem) !important;
36
- }
37
- ol-button > button.px-5 {
38
- padding-left: var(--spacing-5, 1.25rem) !important;
39
- padding-right: var(--spacing-5, 1.25rem) !important;
40
- }
41
- ol-button > button.px-6 {
42
- padding-left: var(--spacing-6, 1.5rem) !important;
43
- padding-right: var(--spacing-6, 1.5rem) !important;
44
- }
45
-
46
- ol-button > button.py-1\.5 {
47
- padding-top: var(--spacing-1_5, 0.375rem) !important;
48
- padding-bottom: var(--spacing-1_5, 0.375rem) !important;
49
- }
50
- ol-button > button.py-2 {
51
- padding-top: var(--spacing-2, 0.5rem) !important;
52
- padding-bottom: var(--spacing-2, 0.5rem) !important;
53
- }
54
- ol-button > button.py-2\.5 {
55
- padding-top: var(--spacing-2_5, 0.625rem) !important;
56
- padding-bottom: var(--spacing-2_5, 0.625rem) !important;
57
- }
58
- ol-button > button.py-3 {
59
- padding-top: var(--spacing-3, 0.75rem) !important;
60
- padding-bottom: var(--spacing-3, 0.75rem) !important;
61
- }
62
- ol-button > button.py-3\.5 {
63
- padding-top: var(--spacing-3_5, 0.875rem) !important;
64
- padding-bottom: var(--spacing-3_5, 0.875rem) !important;
65
- }
66
-
67
- /* Chiều cao nút. HAI BẢNG (Figma 2026-09-15):
68
- có chữ 28 · 36 · 40 · 48 · 52
69
- icon-only 32 · 36 · 40 · 44 · 48 (vuông)
70
- Nên `h-8` (32px) và `h-11` (44px) chỉ icon-only dùng. */
71
- ol-button > button.h-7 {
72
- height: 1.75rem !important;
73
- }
74
- ol-button > button.h-8 {
75
- height: 2rem !important;
76
- }
77
- ol-button > button.h-11 {
78
- height: 2.75rem !important;
79
- }
80
- ol-button > button.h-9 {
81
- height: 2.25rem !important;
82
- }
83
- ol-button > button.h-10 {
84
- height: 2.5rem !important;
85
- }
86
- ol-button > button.h-12 {
87
- height: 3rem !important;
88
- }
89
- ol-button > button.h-13 {
90
- height: 3.25rem !important;
91
- }
92
-
93
- /* Icon-only (vuông) — Figma 32 · 36 · 40 · 44 · 48 */
94
- ol-button > button.w-8 {
95
- width: 2rem !important;
96
- }
97
- ol-button > button.w-9 {
98
- width: 2.25rem !important;
99
- }
100
- ol-button > button.w-10 {
101
- width: 2.5rem !important;
102
- }
103
- ol-button > button.w-11 {
104
- width: 2.75rem !important;
105
- }
106
- ol-button > button.w-12 {
107
- width: 3rem !important;
108
- }
109
-
110
- /* Button Gap & Radii
111
- rounded-md là bo góc mặc định của MỌI button không phải pill — trước đây
112
- không được bảo vệ. Fallback lấy đúng thang token (md=12px, xl=24px). */
113
- ol-button > button.gap-1\.5 {
114
- gap: var(--spacing-1_5, 0.375rem) !important;
115
- }
116
- ol-button > button.rounded-md {
117
- border-radius: var(--radius-md, 12px) !important;
118
- }
119
- ol-button > button.rounded-full {
120
- border-radius: var(--radius-full, 9999px) !important;
121
- }
122
-
123
- /* Kích thước icon trong button (Figma: xs 14 · sm/base/l 16 · xl 20; bản
124
- chỉ-icon 14/16/20). Icon vào qua ng-content nên nằm ngoài encapsulation
125
- của ol-button — nếu CSS ngoài đặt lại width/height cho svg thì icon sẽ
126
- lệch, nên chốt lại ở đây giống cách đã làm với padding và height. */
127
- ol-button > button.ol-btn-ico-14 ol-icon,
128
- ol-button > button.ol-btn-ico-14 ol-icon > span {
129
- width: 14px !important;
130
- height: 14px !important;
131
- }
132
- ol-button > button.ol-btn-ico-16 ol-icon,
133
- ol-button > button.ol-btn-ico-16 ol-icon > span {
134
- width: 16px !important;
135
- height: 16px !important;
136
- }
137
- ol-button > button.ol-btn-ico-20 ol-icon,
138
- ol-button > button.ol-btn-ico-20 ol-icon > span {
139
- width: 20px !important;
140
- height: 20px !important;
141
- }
142
-
143
- /* Loading: spinner đứng thay icon. */
144
- ol-button > button.ol-btn-loading ol-icon,
145
- ol-button > button.ol-btn-loading > svg:not(.animate-spin),
146
- ol-button > button.ol-btn-loading > img {
147
- display: none !important;
148
- }
149
-
150
- /* 2. Download App Button Isolation */
151
- ol-download-app-button {
152
- display: inline-block;
153
- vertical-align: middle;
154
- }
155
-
156
- ol-download-app-button > a,
157
- ol-download-app-button > button {
158
- box-sizing: border-box !important;
159
- display: inline-flex !important;
160
- align-items: center !important;
161
- text-decoration: none !important;
162
- }
163
-
164
- ol-download-app-button .px-5 {
165
- padding-left: 1.25rem !important;
166
- padding-right: 1.25rem !important;
167
- }
168
- ol-download-app-button .py-3 {
169
- padding-top: 0.75rem !important;
170
- padding-bottom: 0.75rem !important;
171
- }
172
- ol-download-app-button .rounded-md {
173
- border-radius: var(--radius-md, 12px) !important;
174
- }
175
-
176
- /* ---------------------------------------------------------------------------
177
- `.sr-only` phải NEO, không được trôi theo vị trí tĩnh
178
- ---------------------------------------------------------------------------
179
- Tailwind khai `.sr-only` là `position:absolute` **không kèm `left`/`top`**,
180
- nên phần tử neo tại vị trí tĩnh của nó. Trong một bảng rộng hơn màn hình
181
- (ma trận variant có `min-w-175` = 700px), ô input ẩn rơi vào toạ độ ~615px —
182
- `absolute` nên nó **thoát khỏi khung `overflow-x-auto`** và kéo `<html>`
183
- rộng ra theo.
184
-
185
- Đo thật ở 390px trên `/toggle`: `body.scrollWidth` 390 (đúng) nhưng
186
- `html.scrollWidth` **616** → tràn ngang **226px**. Ẩn 23 `input.sr-only` thì
187
- tràn về **0**, khôi phục lại thành 226 — chứng minh hai chiều.
188
-
189
- Các container toast `fixed right-0` trôi ra 584px chỉ là **hệ quả**: chúng
190
- neo theo initial containing block đã bị kéo rộng. Ẩn chúng đi không sửa được
191
- gì, nên đừng đuổi theo dấu vết đó.
192
-
193
- Ba trang từng tràn: /toggle 226px · /spinner · /skeleton — tất cả đều là
194
- trang có ma trận variant rộng.
195
- --------------------------------------------------------------------------- */
196
- .sr-only {
197
- left: 0 !important;
198
- top: 0 !important;
199
- }
200
-
201
- /* ---------------------------------------------------------------------------
202
- Bo góc của ba component vừa sửa trong đợt rà soát
203
- ---------------------------------------------------------------------------
204
- `ol-card`, `ol-icon-shape`, `ol-alert` đều từng dính bẫy `[class.X]` trùng
205
- tên (Angular gộp binding, cái CUỐI thắng) nên bo góc ra 0px ở vài biến thể.
206
- Sửa xong rồi thì phải re-assert ở đây: consumer có reset kiểu
207
- `* { border-radius: 0 }` sẽ xoá sạch bo góc mà component không biết.
208
-
209
- Giá trị lấy từ Figma, không phải từ thang Tailwind:
210
- ol-card #1149:34153 12px
211
- ol-icon-shape #1149:56578 12px ở MỌI size Square (đọc đủ 84 variant)
212
- ol-alert #1146:15151 12px (20 variant chỉ có {12, 9999, 0})
213
- --------------------------------------------------------------------------- */
214
- ol-card .rounded-md,
215
- ol-icon-shape .rounded-md,
216
- ol-alert .rounded-md {
217
- border-radius: var(--radius-md, 12px) !important;
218
- }
219
-
220
- /* ---------------------------------------------------------------------------
221
- THANH CUỘN — theo token, đổi cùng chế độ sáng/tối
222
-
223
- `color-scheme` trong `tokens.css` đã làm thanh cuộn MẶC ĐỊNH của trình duyệt
224
- chuyển màu đúng. Phần dưới đây đi thêm một bước: cho nó khớp thang màu của
225
- design system thay vì màu hệ điều hành.
226
-
227
- Dùng CẢ HAI cú pháp vì không trình duyệt nào hiểu đủ:
228
- · `scrollbar-color` + `scrollbar-width` — chuẩn CSS, Firefox và Chrome 121+
229
- · `::-webkit-scrollbar-*` — Safari và Chrome cũ
230
-
231
- Không đặt `!important`: đây là kiểu dáng nền, nơi dùng ghi đè được. Khác các
232
- luật trên trong file này — chúng chống CSS reset của consumer nên cần
233
- `!important`.
234
-
235
- KHÔNG đụng tới thanh cuộn của `<select>` gốc và vùng có `scrollbar-width:
236
- none` (thanh ẩn có chủ ý, như dải tab cuộn ngang).
237
- --------------------------------------------------------------------------- */
238
- * {
239
- scrollbar-width: thin;
240
- scrollbar-color: var(--border-default-strong, #d1d5db) transparent;
241
- }
242
-
243
- *::-webkit-scrollbar {
244
- width: 10px;
245
- height: 10px;
246
- }
247
-
248
- *::-webkit-scrollbar-track {
249
- background: transparent;
250
- }
251
-
252
- *::-webkit-scrollbar-thumb {
253
- background-color: var(--border-default-strong, #d1d5db);
254
- border-radius: var(--radius-full, 9999px);
255
- /* Viền trong suốt + `background-clip` để tay cuộn mảnh hơn rãnh, không dính
256
- sát mép. `padding` không có tác dụng trên phần tử giả này. */
257
- border: 2px solid transparent;
258
- background-clip: content-box;
259
- }
260
-
261
- *::-webkit-scrollbar-thumb:hover {
262
- background-color: var(--text-body-subtle, #6b7280);
263
- }
264
-
265
- /* Góc giao giữa thanh dọc và thanh ngang — để trống thì Safari vẽ ô trắng. */
266
- *::-webkit-scrollbar-corner {
267
- background: transparent;
268
- }
269
-
270
- /* ---------------------------------------------------------------------------
271
- ol-app-nav — MỤC ĐANG CHỌN
272
-
273
- Figma `ol-app-sidebar-item` (#5301:338): Active = nền `bg-warning`, chữ đậm.
274
-
275
- Vì sao không viết thẳng `bg-warning` vào class string của component:
276
- `routerLinkActive` gắn class lúc chạy, nên trạng thái active có HAI đường —
277
- qua router và qua input `activeId`. Một tên class cho cả hai đường thì luật
278
- ở đây là chỗ duy nhất định nghĩa màu, không phải hai chỗ.
279
-
280
- Chữ và icon chuyển sang xanh đậm: nền vàng `#ffc022` với chữ trắng cho
281
- tương phản 1,9 — dưới chuẩn AA. Đo với `--text-brand-strong` (brand-900):
282
- **9,4**.
283
-
284
- `!important` ở đây là cần: luật `hover:bg-*` của chính component có cùng độ
285
- đặc hiệu và đứng sau trong tệp Tailwind sinh ra, nên rê chuột vào mục đang
286
- chọn sẽ thấy nó đổi sang màu hover — đúng lỗi đã gặp ở `ol-sidebar-item`.
287
- --------------------------------------------------------------------------- */
288
- .ol-app-nav-item-active {
289
- background-color: var(--bg-warning, #ffc022) !important;
290
- }
291
-
292
- /*
293
- Chữ trên nền vàng dùng `--color-brand-900` — màu GỐC, không phải token ngữ
294
- nghĩa.
295
-
296
- `--text-brand-strong` lật theo chế độ: sáng là brand-900 `#152147`, tối là
297
- brand-300 `#a3baff`. Lật vậy đúng cho chữ brand trên nền TRANG, nhưng nền
298
- mục active luôn VÀNG ở cả hai chế độ — nó ghim `--bg-warning`, không lật.
299
-
300
- Hậu quả đo được ở chế độ tối: chữ `rgb(163,186,255)` trên nền
301
- `rgb(255,176,0)` cho tương phản **1,04** — gần như không đọc được.
302
-
303
- Màu gốc không lật nên đúng ở cả hai chế độ: đo lại được 9,56 (sáng) và
304
- 8,73 (tối, nền vàng đậm hơn một bậc).
305
- */
306
- .ol-app-nav-item-active,
307
- .ol-app-nav-item-active ol-icon,
308
- .ol-app-nav-item-active span {
309
- color: var(--color-brand-900, #152147) !important;
310
- }
1
+ /* =============================================================================
2
+ OLKIT COMPONENT ISOLATION & RESILIENCE
3
+ Đảm bảo các component của @edmicro/olkit hoạt động độc lập, không bị ảnh hưởng
4
+ bởi các CSS Resets unlayered bên ngoài (như *, *::before { padding: 0; })
5
+ ============================================================================= */
6
+
7
+ /* 1. Button Component Isolation */
8
+ ol-button {
9
+ display: inline-flex;
10
+ vertical-align: middle;
11
+ flex-shrink: 0;
12
+ }
13
+
14
+ ol-button > button {
15
+ appearance: button;
16
+ -webkit-appearance: button;
17
+ box-sizing: border-box !important;
18
+ display: inline-flex !important;
19
+ align-items: center !important;
20
+ justify-content: center !important;
21
+ line-height: 1 !important;
22
+ font-family: inherit;
23
+ cursor: pointer;
24
+ border-style: solid;
25
+ }
26
+
27
+ /* Padding ngang nút có chữ (bảo vệ khỏi `* { padding: 0 }`).
28
+ Figma 2026-09-15: xs=12 sm=12 base=16 l=20 xl=24 — KHÔNG cộng bù viền. */
29
+ ol-button > button.px-3 {
30
+ padding-left: var(--spacing-3, 0.75rem) !important;
31
+ padding-right: var(--spacing-3, 0.75rem) !important;
32
+ }
33
+ ol-button > button.px-4 {
34
+ padding-left: var(--spacing-4, 1rem) !important;
35
+ padding-right: var(--spacing-4, 1rem) !important;
36
+ }
37
+ ol-button > button.px-5 {
38
+ padding-left: var(--spacing-5, 1.25rem) !important;
39
+ padding-right: var(--spacing-5, 1.25rem) !important;
40
+ }
41
+ ol-button > button.px-6 {
42
+ padding-left: var(--spacing-6, 1.5rem) !important;
43
+ padding-right: var(--spacing-6, 1.5rem) !important;
44
+ }
45
+
46
+ ol-button > button.py-1\.5 {
47
+ padding-top: var(--spacing-1_5, 0.375rem) !important;
48
+ padding-bottom: var(--spacing-1_5, 0.375rem) !important;
49
+ }
50
+ ol-button > button.py-2 {
51
+ padding-top: var(--spacing-2, 0.5rem) !important;
52
+ padding-bottom: var(--spacing-2, 0.5rem) !important;
53
+ }
54
+ ol-button > button.py-2\.5 {
55
+ padding-top: var(--spacing-2_5, 0.625rem) !important;
56
+ padding-bottom: var(--spacing-2_5, 0.625rem) !important;
57
+ }
58
+ ol-button > button.py-3 {
59
+ padding-top: var(--spacing-3, 0.75rem) !important;
60
+ padding-bottom: var(--spacing-3, 0.75rem) !important;
61
+ }
62
+ ol-button > button.py-3\.5 {
63
+ padding-top: var(--spacing-3_5, 0.875rem) !important;
64
+ padding-bottom: var(--spacing-3_5, 0.875rem) !important;
65
+ }
66
+
67
+ /* Chiều cao nút. HAI BẢNG (Figma 2026-09-15):
68
+ có chữ 28 · 36 · 40 · 48 · 52
69
+ icon-only 32 · 36 · 40 · 44 · 48 (vuông)
70
+ Nên `h-8` (32px) và `h-11` (44px) chỉ icon-only dùng. */
71
+ ol-button > button.h-7 {
72
+ height: 1.75rem !important;
73
+ }
74
+ ol-button > button.h-8 {
75
+ height: 2rem !important;
76
+ }
77
+ ol-button > button.h-11 {
78
+ height: 2.75rem !important;
79
+ }
80
+ ol-button > button.h-9 {
81
+ height: 2.25rem !important;
82
+ }
83
+ ol-button > button.h-10 {
84
+ height: 2.5rem !important;
85
+ }
86
+ ol-button > button.h-12 {
87
+ height: 3rem !important;
88
+ }
89
+ ol-button > button.h-13 {
90
+ height: 3.25rem !important;
91
+ }
92
+
93
+ /* Icon-only (vuông) — Figma 32 · 36 · 40 · 44 · 48 */
94
+ ol-button > button.w-8 {
95
+ width: 2rem !important;
96
+ }
97
+ ol-button > button.w-9 {
98
+ width: 2.25rem !important;
99
+ }
100
+ ol-button > button.w-10 {
101
+ width: 2.5rem !important;
102
+ }
103
+ ol-button > button.w-11 {
104
+ width: 2.75rem !important;
105
+ }
106
+ ol-button > button.w-12 {
107
+ width: 3rem !important;
108
+ }
109
+
110
+ /* Button Gap & Radii
111
+ rounded-md là bo góc mặc định của MỌI button không phải pill — trước đây
112
+ không được bảo vệ. Fallback lấy đúng thang token (md=12px, xl=24px). */
113
+ ol-button > button.gap-1\.5 {
114
+ gap: var(--spacing-1_5, 0.375rem) !important;
115
+ }
116
+ ol-button > button.rounded-md {
117
+ border-radius: var(--radius-md, 12px) !important;
118
+ }
119
+ ol-button > button.rounded-full {
120
+ border-radius: var(--radius-full, 9999px) !important;
121
+ }
122
+
123
+ /* Kích thước icon trong button (Figma: xs 14 · sm/base/l 16 · xl 20; bản
124
+ chỉ-icon 14/16/20). Icon vào qua ng-content nên nằm ngoài encapsulation
125
+ của ol-button — nếu CSS ngoài đặt lại width/height cho svg thì icon sẽ
126
+ lệch, nên chốt lại ở đây giống cách đã làm với padding và height. */
127
+ ol-button > button.ol-btn-ico-14 ol-icon,
128
+ ol-button > button.ol-btn-ico-14 ol-icon > span {
129
+ width: 14px !important;
130
+ height: 14px !important;
131
+ }
132
+ ol-button > button.ol-btn-ico-16 ol-icon,
133
+ ol-button > button.ol-btn-ico-16 ol-icon > span {
134
+ width: 16px !important;
135
+ height: 16px !important;
136
+ }
137
+ ol-button > button.ol-btn-ico-20 ol-icon,
138
+ ol-button > button.ol-btn-ico-20 ol-icon > span {
139
+ width: 20px !important;
140
+ height: 20px !important;
141
+ }
142
+
143
+ /* Loading: spinner đứng thay icon. */
144
+ ol-button > button.ol-btn-loading ol-icon,
145
+ ol-button > button.ol-btn-loading > svg:not(.animate-spin),
146
+ ol-button > button.ol-btn-loading > img {
147
+ display: none !important;
148
+ }
149
+
150
+ /* 2. Download App Button Isolation */
151
+ ol-download-app-button {
152
+ display: inline-block;
153
+ vertical-align: middle;
154
+ }
155
+
156
+ ol-download-app-button > a,
157
+ ol-download-app-button > button {
158
+ box-sizing: border-box !important;
159
+ display: inline-flex !important;
160
+ align-items: center !important;
161
+ text-decoration: none !important;
162
+ }
163
+
164
+ ol-download-app-button .px-5 {
165
+ padding-left: 1.25rem !important;
166
+ padding-right: 1.25rem !important;
167
+ }
168
+ ol-download-app-button .py-3 {
169
+ padding-top: 0.75rem !important;
170
+ padding-bottom: 0.75rem !important;
171
+ }
172
+ ol-download-app-button .rounded-md {
173
+ border-radius: var(--radius-md, 12px) !important;
174
+ }
175
+
176
+ /* ---------------------------------------------------------------------------
177
+ `.sr-only` phải NEO, không được trôi theo vị trí tĩnh
178
+ ---------------------------------------------------------------------------
179
+ Tailwind khai `.sr-only` là `position:absolute` **không kèm `left`/`top`**,
180
+ nên phần tử neo tại vị trí tĩnh của nó. Trong một bảng rộng hơn màn hình
181
+ (ma trận variant có `min-w-175` = 700px), ô input ẩn rơi vào toạ độ ~615px —
182
+ `absolute` nên nó **thoát khỏi khung `overflow-x-auto`** và kéo `<html>`
183
+ rộng ra theo.
184
+
185
+ Đo thật ở 390px trên `/toggle`: `body.scrollWidth` 390 (đúng) nhưng
186
+ `html.scrollWidth` **616** → tràn ngang **226px**. Ẩn 23 `input.sr-only` thì
187
+ tràn về **0**, khôi phục lại thành 226 — chứng minh hai chiều.
188
+
189
+ Các container toast `fixed right-0` trôi ra 584px chỉ là **hệ quả**: chúng
190
+ neo theo initial containing block đã bị kéo rộng. Ẩn chúng đi không sửa được
191
+ gì, nên đừng đuổi theo dấu vết đó.
192
+
193
+ Ba trang từng tràn: /toggle 226px · /spinner · /skeleton — tất cả đều là
194
+ trang có ma trận variant rộng.
195
+ --------------------------------------------------------------------------- */
196
+ .sr-only {
197
+ left: 0 !important;
198
+ top: 0 !important;
199
+ }
200
+
201
+ /* ---------------------------------------------------------------------------
202
+ Bo góc của ba component vừa sửa trong đợt rà soát
203
+ ---------------------------------------------------------------------------
204
+ `ol-card`, `ol-icon-shape`, `ol-alert` đều từng dính bẫy `[class.X]` trùng
205
+ tên (Angular gộp binding, cái CUỐI thắng) nên bo góc ra 0px ở vài biến thể.
206
+ Sửa xong rồi thì phải re-assert ở đây: consumer có reset kiểu
207
+ `* { border-radius: 0 }` sẽ xoá sạch bo góc mà component không biết.
208
+
209
+ Giá trị lấy từ Figma, không phải từ thang Tailwind:
210
+ ol-card #1149:34153 12px
211
+ ol-icon-shape #1149:56578 12px ở MỌI size Square (đọc đủ 84 variant)
212
+ ol-alert #1146:15151 12px (20 variant chỉ có {12, 9999, 0})
213
+ --------------------------------------------------------------------------- */
214
+ ol-card .rounded-md,
215
+ ol-icon-shape .rounded-md,
216
+ ol-alert .rounded-md {
217
+ border-radius: var(--radius-md, 12px) !important;
218
+ }
219
+
220
+ /* ---------------------------------------------------------------------------
221
+ THANH CUỘN — theo token, đổi cùng chế độ sáng/tối
222
+
223
+ `color-scheme` trong `tokens.css` đã làm thanh cuộn MẶC ĐỊNH của trình duyệt
224
+ chuyển màu đúng. Phần dưới đây đi thêm một bước: cho nó khớp thang màu của
225
+ design system thay vì màu hệ điều hành.
226
+
227
+ Dùng CẢ HAI cú pháp vì không trình duyệt nào hiểu đủ:
228
+ · `scrollbar-color` + `scrollbar-width` — chuẩn CSS, Firefox và Chrome 121+
229
+ · `::-webkit-scrollbar-*` — Safari và Chrome cũ
230
+
231
+ Không đặt `!important`: đây là kiểu dáng nền, nơi dùng ghi đè được. Khác các
232
+ luật trên trong file này — chúng chống CSS reset của consumer nên cần
233
+ `!important`.
234
+
235
+ KHÔNG đụng tới thanh cuộn của `<select>` gốc và vùng có `scrollbar-width:
236
+ none` (thanh ẩn có chủ ý, như dải tab cuộn ngang).
237
+ --------------------------------------------------------------------------- */
238
+ * {
239
+ scrollbar-width: thin;
240
+ scrollbar-color: var(--border-default-strong, #d1d5db) transparent;
241
+ }
242
+
243
+ *::-webkit-scrollbar {
244
+ width: 10px;
245
+ height: 10px;
246
+ }
247
+
248
+ *::-webkit-scrollbar-track {
249
+ background: transparent;
250
+ }
251
+
252
+ *::-webkit-scrollbar-thumb {
253
+ background-color: var(--border-default-strong, #d1d5db);
254
+ border-radius: var(--radius-full, 9999px);
255
+ /* Viền trong suốt + `background-clip` để tay cuộn mảnh hơn rãnh, không dính
256
+ sát mép. `padding` không có tác dụng trên phần tử giả này. */
257
+ border: 2px solid transparent;
258
+ background-clip: content-box;
259
+ }
260
+
261
+ *::-webkit-scrollbar-thumb:hover {
262
+ background-color: var(--text-body-subtle, #6b7280);
263
+ }
264
+
265
+ /* Góc giao giữa thanh dọc và thanh ngang — để trống thì Safari vẽ ô trắng. */
266
+ *::-webkit-scrollbar-corner {
267
+ background: transparent;
268
+ }
269
+
270
+ /* ---------------------------------------------------------------------------
271
+ ol-app-nav — MỤC ĐANG CHỌN
272
+
273
+ Figma `ol-app-sidebar-item` (#5301:338): Active = nền `bg-warning`, chữ đậm.
274
+
275
+ Vì sao không viết thẳng `bg-warning` vào class string của component:
276
+ `routerLinkActive` gắn class lúc chạy, nên trạng thái active có HAI đường —
277
+ qua router và qua input `activeId`. Một tên class cho cả hai đường thì luật
278
+ ở đây là chỗ duy nhất định nghĩa màu, không phải hai chỗ.
279
+
280
+ Chữ và icon chuyển sang xanh đậm: nền vàng `#ffc022` với chữ trắng cho
281
+ tương phản 1,9 — dưới chuẩn AA. Đo với `--text-brand-strong` (brand-900):
282
+ **9,4**.
283
+
284
+ `!important` ở đây là cần: luật `hover:bg-*` của chính component có cùng độ
285
+ đặc hiệu và đứng sau trong tệp Tailwind sinh ra, nên rê chuột vào mục đang
286
+ chọn sẽ thấy nó đổi sang màu hover — đúng lỗi đã gặp ở `ol-sidebar-item`.
287
+ --------------------------------------------------------------------------- */
288
+ .ol-app-nav-item-active {
289
+ background-color: var(--bg-warning, #ffc022) !important;
290
+ }
291
+
292
+ /*
293
+ Chữ trên nền vàng dùng `--color-brand-900` — màu GỐC, không phải token ngữ
294
+ nghĩa.
295
+
296
+ `--text-brand-strong` lật theo chế độ: sáng là brand-900 `#152147`, tối là
297
+ brand-300 `#a3baff`. Lật vậy đúng cho chữ brand trên nền TRANG, nhưng nền
298
+ mục active luôn VÀNG ở cả hai chế độ — nó ghim `--bg-warning`, không lật.
299
+
300
+ Hậu quả đo được ở chế độ tối: chữ `rgb(163,186,255)` trên nền
301
+ `rgb(255,176,0)` cho tương phản **1,04** — gần như không đọc được.
302
+
303
+ Màu gốc không lật nên đúng ở cả hai chế độ: đo lại được 9,56 (sáng) và
304
+ 8,73 (tối, nền vàng đậm hơn một bậc).
305
+ */
306
+ .ol-app-nav-item-active,
307
+ .ol-app-nav-item-active ol-icon,
308
+ .ol-app-nav-item-active span {
309
+ color: var(--color-brand-900, #152147) !important;
310
+ }