1
//! User-Agent Default Stylesheet for Azul
2
//!
3
//! This module provides the default CSS styling that browsers apply to HTML elements
4
//! before any author stylesheets are processed. It ensures consistent baseline behavior
5
//! across all applications.
6
//!
7
//! The user-agent stylesheet serves several critical functions:
8
//!
9
//! 1. **Prevents Layout Collapse**: Ensures root elements (`<html>`, `<body>`) have default
10
//!    dimensions so that percentage-based child sizing can work correctly.
11
//!
12
//! 2. **Establishes Display Types**: Defines the default `display` property for all HTML elements
13
//!    (e.g., `<div>` is `block`, `<span>` is `inline`).
14
//!
15
//! 3. **Provides Baseline Typography**: Sets reasonable defaults for font sizes, margins, and text
16
//!    styling for headings, paragraphs, and other text elements.
17
//!
18
//! 4. **Normalizes Browser Behavior**: Incorporates principles from normalize.css to provide
19
//!    consistent rendering across different platforms.
20
//!
21
//! # Licensing
22
//!
23
//! Based on principles from [normalize.css](https://github.com/necolas/normalize.css)
24
//! (MIT License, Copyright Nicolas Gallagher and Jonathan Neal).
25
//! This is NOT a direct copy but incorporates its principles and approach.
26
//!
27
//! # References
28
//!
29
//! - CSS 2.1 Specification: https://www.w3.org/TR/CSS21/
30
//! - HTML Living Standard: https://html.spec.whatwg.org/
31
//! - normalize.css: https://necolas.github.io/normalize.css/
32

            
33
use azul_css::{
34
    css::CssPropertyValue,
35
    dynamic_selector::{
36
        CssPropertyWithConditions,
37
        DynamicSelector, DynamicSelectorContext, OsCondition, ThemeCondition,
38
    },
39
    props::{
40
        basic::{
41
            font::StyleFontWeight, pixel::PixelValue, ColorU,
42
            StyleFontSize,
43
        },
44
        layout::{
45
            dimensions::{LayoutHeight, LayoutWidth},
46
            display::LayoutDisplay,
47
            fragmentation::{BreakInside, PageBreak},
48
            spacing::{
49
                LayoutMarginBottom, LayoutMarginLeft, LayoutMarginRight, LayoutMarginTop,
50
                LayoutPaddingBottom, LayoutPaddingInlineEnd, LayoutPaddingInlineStart,
51
                LayoutPaddingLeft, LayoutPaddingRight, LayoutPaddingTop,
52
            },
53
        },
54
        property::{CssProperty, CssPropertyType},
55
        style::{
56
            border::{
57
                BorderStyle,
58
                LayoutBorderBottomWidth, LayoutBorderLeftWidth, LayoutBorderRightWidth, LayoutBorderTopWidth,
59
                StyleBorderBottomColor, StyleBorderBottomStyle,
60
                StyleBorderLeftColor, StyleBorderLeftStyle,
61
                StyleBorderRightColor, StyleBorderRightStyle,
62
                StyleBorderTopColor, StyleBorderTopStyle,
63
            },
64
            content::CounterReset,
65
            effects::StyleCursor,
66
            lists::StyleListStyleType,
67
            scrollbar::{
68
                LayoutScrollbarWidth, ScrollbarColorCustom, ScrollbarFadeDelay,
69
                ScrollbarFadeDuration, ScrollbarVisibilityMode, StyleScrollbarColor,
70
            },
71
            text::StyleTextDecoration,
72
            StyleTextAlign, StyleVerticalAlign,
73
        },
74
    },
75
};
76

            
77
use crate::dom::NodeType;
78

            
79
/// 100% width
80
static WIDTH_100_PERCENT: CssProperty = CssProperty::Width(CssPropertyValue::Exact(
81
    LayoutWidth::Px(PixelValue::const_percent(100)),
82
));
83

            
84
/// 100% height
85
static HEIGHT_100_PERCENT: CssProperty = CssProperty::Height(CssPropertyValue::Exact(
86
    LayoutHeight::Px(PixelValue::const_percent(100)),
87
));
88

            
89
/// display: block
90
static DISPLAY_BLOCK: CssProperty =
91
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::Block));
92

            
93
/// display: inline
94
static DISPLAY_INLINE: CssProperty =
95
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::Inline));
96

            
97
/// display: inline-block
98
static DISPLAY_INLINE_BLOCK: CssProperty =
99
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::InlineBlock));
100

            
101
/// display: none
102
static DISPLAY_NONE: CssProperty =
103
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::None));
104

            
105
/// display: table
106
static DISPLAY_TABLE: CssProperty =
107
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::Table));
108

            
109
/// display: table-row
110
static DISPLAY_TABLE_ROW: CssProperty =
111
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableRow));
112

            
113
/// display: table-cell
114
static DISPLAY_TABLE_CELL: CssProperty =
115
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableCell));
116

            
117
/// display: table-header-group
118
static DISPLAY_TABLE_HEADER_GROUP: CssProperty =
119
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableHeaderGroup));
120

            
121
/// display: table-row-group
122
static DISPLAY_TABLE_ROW_GROUP: CssProperty =
123
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableRowGroup));
124

            
125
/// display: table-footer-group
126
static DISPLAY_TABLE_FOOTER_GROUP: CssProperty =
127
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableFooterGroup));
128

            
129
/// display: table-caption
130
static DISPLAY_TABLE_CAPTION: CssProperty =
131
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableCaption));
132

            
133
/// display: table-column-group
134
static DISPLAY_TABLE_COLUMN_GROUP: CssProperty =
135
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableColumnGroup));
136

            
137
/// display: table-column
138
static DISPLAY_TABLE_COLUMN: CssProperty =
139
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableColumn));
140

            
141
/// display: list-item
142
static DISPLAY_LIST_ITEM: CssProperty =
143
    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::ListItem));
144

            
145
/// cursor: pointer (for clickable elements like buttons, links)
146
static CURSOR_POINTER: CssProperty =
147
    CssProperty::Cursor(CssPropertyValue::Exact(StyleCursor::Pointer));
148

            
149
/// cursor: text (for selectable text elements)
150
static CURSOR_TEXT: CssProperty =
151
    CssProperty::Cursor(CssPropertyValue::Exact(StyleCursor::Text));
152

            
153
/// margin-top: 0
154
static MARGIN_TOP_ZERO: CssProperty =
155
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
156
        inner: PixelValue::const_px(0),
157
    }));
158

            
159
/// margin-bottom: 0
160
static MARGIN_BOTTOM_ZERO: CssProperty =
161
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
162
        inner: PixelValue::const_px(0),
163
    }));
164

            
165
/// margin-left: 0
166
static MARGIN_LEFT_ZERO: CssProperty =
167
    CssProperty::MarginLeft(CssPropertyValue::Exact(LayoutMarginLeft {
168
        inner: PixelValue::const_px(0),
169
    }));
170

            
171
/// margin-right: 0
172
static MARGIN_RIGHT_ZERO: CssProperty =
173
    CssProperty::MarginRight(CssPropertyValue::Exact(LayoutMarginRight {
174
        inner: PixelValue::const_px(0),
175
    }));
176

            
177
// Chrome User-Agent Stylesheet: body { margin: 8px; }
178
/// margin-top: 8px (Chrome UA default for body)
179
static MARGIN_TOP_8PX: CssProperty =
180
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
181
        inner: PixelValue::const_px(8),
182
    }));
183

            
184
/// margin-bottom: 8px (Chrome UA default for body)
185
static MARGIN_BOTTOM_8PX: CssProperty =
186
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
187
        inner: PixelValue::const_px(8),
188
    }));
189

            
190
/// margin-left: 8px (Chrome UA default for body)
191
static MARGIN_LEFT_8PX: CssProperty =
192
    CssProperty::MarginLeft(CssPropertyValue::Exact(LayoutMarginLeft {
193
        inner: PixelValue::const_px(8),
194
    }));
195

            
196
/// margin-right: 8px (Chrome UA default for body)
197
static MARGIN_RIGHT_8PX: CssProperty =
198
    CssProperty::MarginRight(CssPropertyValue::Exact(LayoutMarginRight {
199
        inner: PixelValue::const_px(8),
200
    }));
201

            
202
/// font-size: 2em (for H1)
203
static FONT_SIZE_2EM: CssProperty = CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
204
    inner: PixelValue::const_em(2),
205
}));
206

            
207
/// font-size: 1.5em (for H2)
208
static FONT_SIZE_1_5EM: CssProperty =
209
    CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
210
        inner: PixelValue::const_em_fractional(1, 5),
211
    }));
212

            
213
/// font-size: 1.17em (for H3)
214
static FONT_SIZE_1_17EM: CssProperty =
215
    CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
216
        inner: PixelValue::const_em_fractional(1, 17),
217
    }));
218

            
219
/// font-size: 1em (for H4)
220
static FONT_SIZE_1EM: CssProperty = CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
221
    inner: PixelValue::const_em(1),
222
}));
223

            
224
/// font-size: 0.83em (for H5)
225
static FONT_SIZE_0_83EM: CssProperty =
226
    CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
227
        inner: PixelValue::const_em_fractional(0, 83),
228
    }));
229

            
230
/// font-size: 0.67em (for H6)
231
static FONT_SIZE_0_67EM: CssProperty =
232
    CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
233
        inner: PixelValue::const_em_fractional(0, 67),
234
    }));
235

            
236
/// margin-top: 1em (for P)
237
static MARGIN_TOP_1EM: CssProperty =
238
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
239
        inner: PixelValue::const_em(1),
240
    }));
241

            
242
/// margin-bottom: 1em (for P)
243
static MARGIN_BOTTOM_1EM: CssProperty =
244
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
245
        inner: PixelValue::const_em(1),
246
    }));
247

            
248
/// margin-top: 0.67em (for H1)
249
static MARGIN_TOP_0_67EM: CssProperty =
250
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
251
        inner: PixelValue::const_em_fractional(0, 67),
252
    }));
253

            
254
/// margin-bottom: 0.67em (for H1)
255
static MARGIN_BOTTOM_0_67EM: CssProperty =
256
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
257
        inner: PixelValue::const_em_fractional(0, 67),
258
    }));
259

            
260
/// margin-top: 0.83em (for H2)
261
static MARGIN_TOP_0_83EM: CssProperty =
262
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
263
        inner: PixelValue::const_em_fractional(0, 83),
264
    }));
265

            
266
/// margin-bottom: 0.83em (for H2)
267
static MARGIN_BOTTOM_0_83EM: CssProperty =
268
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
269
        inner: PixelValue::const_em_fractional(0, 83),
270
    }));
271

            
272
/// margin-top: 1.33em (for H4)
273
static MARGIN_TOP_1_33EM: CssProperty =
274
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
275
        inner: PixelValue::const_em_fractional(1, 33),
276
    }));
277

            
278
/// margin-bottom: 1.33em (for H4)
279
static MARGIN_BOTTOM_1_33EM: CssProperty =
280
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
281
        inner: PixelValue::const_em_fractional(1, 33),
282
    }));
283

            
284
/// margin-top: 1.67em (for H5)
285
static MARGIN_TOP_1_67EM: CssProperty =
286
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
287
        inner: PixelValue::const_em_fractional(1, 67),
288
    }));
289

            
290
/// margin-bottom: 1.67em (for H5)
291
static MARGIN_BOTTOM_1_67EM: CssProperty =
292
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
293
        inner: PixelValue::const_em_fractional(1, 67),
294
    }));
295

            
296
/// margin-top: 2.33em (for H6)
297
static MARGIN_TOP_2_33EM: CssProperty =
298
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
299
        inner: PixelValue::const_em_fractional(2, 33),
300
    }));
301

            
302
/// margin-bottom: 2.33em (for H6)
303
static MARGIN_BOTTOM_2_33EM: CssProperty =
304
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
305
        inner: PixelValue::const_em_fractional(2, 33),
306
    }));
307

            
308
/// font-weight: bold (for headings)
309
static FONT_WEIGHT_BOLD: CssProperty =
310
    CssProperty::FontWeight(CssPropertyValue::Exact(StyleFontWeight::Bold));
311

            
312
/// font-weight: bolder
313
static FONT_WEIGHT_BOLDER: CssProperty =
314
    CssProperty::FontWeight(CssPropertyValue::Exact(StyleFontWeight::Bolder));
315

            
316
// Table cell padding - Chrome UA CSS default: 1px
317
static PADDING_TOP_1PX: CssProperty =
318
    CssProperty::PaddingTop(CssPropertyValue::Exact(LayoutPaddingTop {
319
        inner: PixelValue::const_px(1),
320
    }));
321

            
322
static PADDING_BOTTOM_1PX: CssProperty =
323
    CssProperty::PaddingBottom(CssPropertyValue::Exact(LayoutPaddingBottom {
324
        inner: PixelValue::const_px(1),
325
    }));
326

            
327
static PADDING_LEFT_1PX: CssProperty =
328
    CssProperty::PaddingLeft(CssPropertyValue::Exact(LayoutPaddingLeft {
329
        inner: PixelValue::const_px(1),
330
    }));
331

            
332
static PADDING_RIGHT_1PX: CssProperty =
333
    CssProperty::PaddingRight(CssPropertyValue::Exact(LayoutPaddingRight {
334
        inner: PixelValue::const_px(1),
335
    }));
336

            
337
/// text-align: center (for th elements)
338
static TEXT_ALIGN_CENTER: CssProperty =
339
    CssProperty::TextAlign(CssPropertyValue::Exact(StyleTextAlign::Center));
340

            
341
/// vertical-align: middle (for table elements)
342
static VERTICAL_ALIGN_MIDDLE: CssProperty =
343
    CssProperty::VerticalAlign(CssPropertyValue::Exact(StyleVerticalAlign::Middle));
344

            
345
/// list-style-type: disc (default for <ul>)
346
static LIST_STYLE_TYPE_DISC: CssProperty =
347
    CssProperty::ListStyleType(CssPropertyValue::Exact(StyleListStyleType::Disc));
348

            
349
/// list-style-type: decimal (default for <ol>)
350
static LIST_STYLE_TYPE_DECIMAL: CssProperty =
351
    CssProperty::ListStyleType(CssPropertyValue::Exact(StyleListStyleType::Decimal));
352

            
353
// --- HR Element Defaults ---
354
// Per HTML spec, <hr> renders as a horizontal line with inset border style
355

            
356
/// margin-top: 0.5em (for hr)
357
static MARGIN_TOP_0_5EM: CssProperty =
358
    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
359
        inner: PixelValue::const_em_fractional(0, 5),
360
    }));
361

            
362
/// margin-bottom: 0.5em (for hr)
363
static MARGIN_BOTTOM_0_5EM: CssProperty =
364
    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
365
        inner: PixelValue::const_em_fractional(0, 5),
366
    }));
367

            
368
/// border-top-style: inset (for hr - default browser style)
369
static BORDER_TOP_STYLE_INSET: CssProperty =
370
    CssProperty::BorderTopStyle(CssPropertyValue::Exact(StyleBorderTopStyle {
371
        inner: BorderStyle::Inset,
372
    }));
373

            
374
/// border-top-width: 1px (for hr)
375
static BORDER_TOP_WIDTH_1PX: CssProperty =
376
    CssProperty::BorderTopWidth(CssPropertyValue::Exact(LayoutBorderTopWidth {
377
        inner: PixelValue::const_px(1),
378
    }));
379

            
380
/// border-top-color: gray (for hr - default visible color)
381
static BORDER_TOP_COLOR_GRAY: CssProperty =
382
    CssProperty::BorderTopColor(CssPropertyValue::Exact(StyleBorderTopColor {
383
        inner: ColorU {
384
            r: 128,
385
            g: 128,
386
            b: 128,
387
            a: 255,
388
        },
389
    }));
390

            
391
/// height: 0 (for hr - the line comes from the border, not height)
392
static HEIGHT_ZERO: CssProperty = CssProperty::Height(CssPropertyValue::Exact(LayoutHeight::Px(
393
    PixelValue::const_px(0),
394
)));
395

            
396
/// counter-reset: list-item 0 (default for <ul>, <ol>)
397
/// Per CSS Lists Module Level 3, list containers automatically reset the list-item counter
398
static COUNTER_RESET_LIST_ITEM: CssProperty =
399
    CssProperty::CounterReset(CssPropertyValue::Exact(CounterReset::list_item()));
400

            
401
// CSS Fragmentation (Page Breaking) Properties
402
//
403
// Per CSS Fragmentation Level 3 and paged media best practices,
404
// certain elements should avoid page breaks inside them
405

            
406
/// break-inside: avoid
407
/// Used for elements that should not be split across page boundaries
408
/// Applied to: h1-h6, table, thead, tbody, tfoot, figure, figcaption
409
static BREAK_INSIDE_AVOID: CssProperty = CssProperty::break_inside(BreakInside::Avoid);
410

            
411
/// break-after: avoid
412
/// Avoids a page break after the element (useful for headings)
413
static BREAK_AFTER_AVOID: CssProperty = CssProperty::break_after(PageBreak::Avoid);
414

            
415
/// padding-inline-start: 40px (default for <li>)
416
///
417
/// Creates space for list markers in the inline-start direction (left in LTR, right in RTL)
418
/// padding-inline-start: 40px for list items per CSS Lists Module Level 3
419
/// Applied to <li> items to create gutter space for ::marker pseudo-elements
420
///
421
/// NOTE: This should be on the list items, not the container, because:
422
///
423
/// 1. ::marker pseudo-elements are children of <li>, not <ul>/<ol>
424
/// 2. The marker needs to be positioned relative to the list item's content box
425
/// 3. Padding on <li> creates space between the marker and the text content
426
/// TODO: Change to PaddingInlineStart once logical property resolution is implemented
427
static PADDING_INLINE_START_40PX: CssProperty =
428
    CssProperty::PaddingLeft(CssPropertyValue::Exact(LayoutPaddingLeft {
429
        inner: PixelValue::const_px(40),
430
    }));
431

            
432
/// Text decoration: underline - used for <a> and <u> elements
433
static TEXT_DECORATION_UNDERLINE: CssProperty = CssProperty::TextDecoration(
434
    CssPropertyValue::Exact(StyleTextDecoration::Underline),
435
);
436

            
437
// --- Button Element Defaults ---
438
// Per browser UA CSS, <button> has padding, border, and a system font size.
439
// These ensure a button is visible even without author CSS.
440

            
441
/// font-size: 13px (standard button font size on macOS/Linux)
442
static FONT_SIZE_13PX: CssProperty = CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
443
    inner: PixelValue::const_px(13),
444
}));
445

            
446
/// padding-top: 5px (button)
447
static PADDING_TOP_5PX: CssProperty =
448
    CssProperty::PaddingTop(CssPropertyValue::Exact(LayoutPaddingTop {
449
        inner: PixelValue::const_px(5),
450
    }));
451

            
452
/// padding-bottom: 5px (button)
453
static PADDING_BOTTOM_5PX: CssProperty =
454
    CssProperty::PaddingBottom(CssPropertyValue::Exact(LayoutPaddingBottom {
455
        inner: PixelValue::const_px(5),
456
    }));
457

            
458
/// padding-left: 10px (button)
459
static PADDING_LEFT_10PX: CssProperty =
460
    CssProperty::PaddingLeft(CssPropertyValue::Exact(LayoutPaddingLeft {
461
        inner: PixelValue::const_px(10),
462
    }));
463

            
464
/// padding-right: 10px (button)
465
static PADDING_RIGHT_10PX: CssProperty =
466
    CssProperty::PaddingRight(CssPropertyValue::Exact(LayoutPaddingRight {
467
        inner: PixelValue::const_px(10),
468
    }));
469

            
470
/// Border color for button: #c8c8c8 (light gray)
471
static BUTTON_BORDER_COLOR: ColorU = ColorU { r: 200, g: 200, b: 200, a: 255 };
472

            
473
static BUTTON_BORDER_TOP_COLOR: CssProperty =
474
    CssProperty::BorderTopColor(CssPropertyValue::Exact(StyleBorderTopColor {
475
        inner: BUTTON_BORDER_COLOR,
476
    }));
477
static BUTTON_BORDER_BOTTOM_COLOR: CssProperty =
478
    CssProperty::BorderBottomColor(CssPropertyValue::Exact(StyleBorderBottomColor {
479
        inner: BUTTON_BORDER_COLOR,
480
    }));
481
static BUTTON_BORDER_LEFT_COLOR: CssProperty =
482
    CssProperty::BorderLeftColor(CssPropertyValue::Exact(StyleBorderLeftColor {
483
        inner: BUTTON_BORDER_COLOR,
484
    }));
485
static BUTTON_BORDER_RIGHT_COLOR: CssProperty =
486
    CssProperty::BorderRightColor(CssPropertyValue::Exact(StyleBorderRightColor {
487
        inner: BUTTON_BORDER_COLOR,
488
    }));
489

            
490
static BUTTON_BORDER_TOP_STYLE: CssProperty =
491
    CssProperty::BorderTopStyle(CssPropertyValue::Exact(StyleBorderTopStyle {
492
        inner: BorderStyle::Solid,
493
    }));
494
static BUTTON_BORDER_BOTTOM_STYLE: CssProperty =
495
    CssProperty::BorderBottomStyle(CssPropertyValue::Exact(StyleBorderBottomStyle {
496
        inner: BorderStyle::Solid,
497
    }));
498
static BUTTON_BORDER_LEFT_STYLE: CssProperty =
499
    CssProperty::BorderLeftStyle(CssPropertyValue::Exact(StyleBorderLeftStyle {
500
        inner: BorderStyle::Solid,
501
    }));
502
static BUTTON_BORDER_RIGHT_STYLE: CssProperty =
503
    CssProperty::BorderRightStyle(CssPropertyValue::Exact(StyleBorderRightStyle {
504
        inner: BorderStyle::Solid,
505
    }));
506

            
507
static BUTTON_BORDER_TOP_WIDTH: CssProperty =
508
    CssProperty::BorderTopWidth(CssPropertyValue::Exact(LayoutBorderTopWidth {
509
        inner: PixelValue::const_px(1),
510
    }));
511
static BUTTON_BORDER_BOTTOM_WIDTH: CssProperty =
512
    CssProperty::BorderBottomWidth(CssPropertyValue::Exact(LayoutBorderBottomWidth {
513
        inner: PixelValue::const_px(1),
514
    }));
515
static BUTTON_BORDER_LEFT_WIDTH: CssProperty =
516
    CssProperty::BorderLeftWidth(CssPropertyValue::Exact(LayoutBorderLeftWidth {
517
        inner: PixelValue::const_px(1),
518
    }));
519
static BUTTON_BORDER_RIGHT_WIDTH: CssProperty =
520
    CssProperty::BorderRightWidth(CssPropertyValue::Exact(LayoutBorderRightWidth {
521
        inner: PixelValue::const_px(1),
522
    }));
523

            
524
/// Returns the default user-agent CSS property value for a given node type and property.
525
///
526
/// This function provides the baseline styling that should be applied before any author
527
/// styles. It ensures that elements have sensible defaults that prevent layout issues.
528
///
529
/// # Arguments
530
///
531
/// * `node_type` - The type of DOM node (e.g., `Body`, `H1`, `Div`)
532
/// * `property_type` - The specific CSS property to query (e.g., `Width`, `Display`)
533
///
534
/// # Returns
535
///
536
/// `Some(CssProperty)` if a default value is defined for this combination, otherwise `None`.
537
4210273
pub fn get_ua_property(
538
4210273
    node_type: &NodeType,
539
4210273
    property_type: CssPropertyType,
540
4210273
) -> Option<&'static CssProperty> {
541
    use CssPropertyType as PT;
542
    use NodeType as NT;
543

            
544
4210273
    let result = match (node_type, property_type) {
545
        // Body Element - CRITICAL for preventing layout collapse
546
10375
        (NT::Body, PT::Display) => Some(&DISPLAY_BLOCK),
547
        // NOTE: Body does NOT have width: 100% in standard UA CSS - it inherits from ICB
548
        // (NT::Body, PT::Height) => Some(&HEIGHT_100_PERCENT),
549
9761
        (NT::Body, PT::MarginTop) => Some(&MARGIN_TOP_8PX),
550
9744
        (NT::Body, PT::MarginBottom) => Some(&MARGIN_BOTTOM_8PX),
551
9744
        (NT::Body, PT::MarginLeft) => Some(&MARGIN_LEFT_8PX),
552
9744
        (NT::Body, PT::MarginRight) => Some(&MARGIN_RIGHT_8PX),
553

            
554
        // Block-level Elements
555
        // NOTE: Do NOT set width: 100% here! Block elements have width: auto by default
556
        // in CSS spec. width: auto for blocks means "fill available width" but it's NOT
557
        // the same as width: 100%. The difference is critical for flexbox: width: auto
558
        // allows flex-grow/flex-shrink to control sizing, while width: 100% prevents it.
559
19163
        (NT::Div, PT::Display) => Some(&DISPLAY_BLOCK),
560
2445
        (NT::P, PT::Display) => Some(&DISPLAY_BLOCK),
561
        // REMOVED - blocks have width: auto by default
562
        // (NT::Div, PT::Width) => Some(&WIDTH_100_PERCENT),
563
        // REMOVED - blocks have width: auto by default
564
        // (NT::P, PT::Width) => Some(&WIDTH_100_PERCENT),
565
1798
        (NT::P, PT::MarginTop) => Some(&MARGIN_TOP_1EM),
566
1840
        (NT::P, PT::MarginBottom) => Some(&MARGIN_BOTTOM_1EM),
567
        (NT::Main, PT::Display) => Some(&DISPLAY_BLOCK),
568
        (NT::Header, PT::Display) => Some(&DISPLAY_BLOCK),
569
        (NT::Footer, PT::Display) => Some(&DISPLAY_BLOCK),
570
34
        (NT::Section, PT::Display) => Some(&DISPLAY_BLOCK),
571
34
        (NT::Article, PT::Display) => Some(&DISPLAY_BLOCK),
572
        (NT::Aside, PT::Display) => Some(&DISPLAY_BLOCK),
573
        (NT::Nav, PT::Display) => Some(&DISPLAY_BLOCK),
574

            
575
        // Headings - Chrome UA CSS values
576
        // Per CSS Fragmentation Level 3: headings should avoid page breaks inside
577
        // and after them (to keep heading with following content)
578
254
        (NT::H1, PT::Display) => Some(&DISPLAY_BLOCK),
579
254
        (NT::H1, PT::FontSize) => Some(&FONT_SIZE_2EM),
580
254
        (NT::H1, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
581
212
        (NT::H1, PT::MarginTop) => Some(&MARGIN_TOP_0_67EM),
582
212
        (NT::H1, PT::MarginBottom) => Some(&MARGIN_BOTTOM_0_67EM),
583
127
        (NT::H1, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
584
127
        (NT::H1, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
585

            
586
        (NT::H2, PT::Display) => Some(&DISPLAY_BLOCK),
587
        (NT::H2, PT::FontSize) => Some(&FONT_SIZE_1_5EM),
588
        (NT::H2, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
589
        (NT::H2, PT::MarginTop) => Some(&MARGIN_TOP_0_83EM),
590
        (NT::H2, PT::MarginBottom) => Some(&MARGIN_BOTTOM_0_83EM),
591
        (NT::H2, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
592
        (NT::H2, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
593

            
594
        (NT::H3, PT::Display) => Some(&DISPLAY_BLOCK),
595
        (NT::H3, PT::FontSize) => Some(&FONT_SIZE_1_17EM),
596
        (NT::H3, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
597
        (NT::H3, PT::MarginTop) => Some(&MARGIN_TOP_1EM),
598
        (NT::H3, PT::MarginBottom) => Some(&MARGIN_BOTTOM_1EM),
599
        (NT::H3, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
600
        (NT::H3, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
601

            
602
        (NT::H4, PT::Display) => Some(&DISPLAY_BLOCK),
603
        (NT::H4, PT::FontSize) => Some(&FONT_SIZE_1EM),
604
        (NT::H4, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
605
        (NT::H4, PT::MarginTop) => Some(&MARGIN_TOP_1_33EM),
606
        (NT::H4, PT::MarginBottom) => Some(&MARGIN_BOTTOM_1_33EM),
607
        (NT::H4, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
608
        (NT::H4, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
609

            
610
        (NT::H5, PT::Display) => Some(&DISPLAY_BLOCK),
611
        (NT::H5, PT::FontSize) => Some(&FONT_SIZE_0_83EM),
612
        (NT::H5, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
613
        (NT::H5, PT::MarginTop) => Some(&MARGIN_TOP_1_67EM),
614
        (NT::H5, PT::MarginBottom) => Some(&MARGIN_BOTTOM_1_67EM),
615
        (NT::H5, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
616
        (NT::H5, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
617

            
618
        (NT::H6, PT::Display) => Some(&DISPLAY_BLOCK),
619
        (NT::H6, PT::FontSize) => Some(&FONT_SIZE_0_67EM),
620
        (NT::H6, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
621
        (NT::H6, PT::MarginTop) => Some(&MARGIN_TOP_2_33EM),
622
        (NT::H6, PT::MarginBottom) => Some(&MARGIN_BOTTOM_2_33EM),
623
        (NT::H6, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
624
        (NT::H6, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
625

            
626
        // Lists - padding on container creates gutter for markers
627
        (NT::Ul, PT::Display) => Some(&DISPLAY_BLOCK),
628
        (NT::Ul, PT::ListStyleType) => Some(&LIST_STYLE_TYPE_DISC),
629
        (NT::Ul, PT::CounterReset) => Some(&COUNTER_RESET_LIST_ITEM),
630
        (NT::Ul, PT::PaddingLeft) => Some(&PADDING_INLINE_START_40PX),
631
        (NT::Ol, PT::Display) => Some(&DISPLAY_BLOCK),
632
        (NT::Ol, PT::ListStyleType) => Some(&LIST_STYLE_TYPE_DECIMAL),
633
        (NT::Ol, PT::CounterReset) => Some(&COUNTER_RESET_LIST_ITEM),
634
        (NT::Ol, PT::PaddingLeft) => Some(&PADDING_INLINE_START_40PX),
635
        (NT::Li, PT::Display) => Some(&DISPLAY_LIST_ITEM),
636
        (NT::Dl, PT::Display) => Some(&DISPLAY_BLOCK),
637
        (NT::Dt, PT::Display) => Some(&DISPLAY_BLOCK),
638
        (NT::Dd, PT::Display) => Some(&DISPLAY_BLOCK),
639

            
640
        // Inline Elements
641
4808
        (NT::Span, PT::Display) => Some(&DISPLAY_INLINE),
642
4116
        (NT::A, PT::Display) => Some(&DISPLAY_INLINE),
643
4116
        (NT::A, PT::TextDecoration) => Some(&TEXT_DECORATION_UNDERLINE),
644
        (NT::Strong, PT::Display) => Some(&DISPLAY_INLINE),
645
        (NT::Strong, PT::FontWeight) => Some(&FONT_WEIGHT_BOLDER),
646
        (NT::Em, PT::Display) => Some(&DISPLAY_INLINE),
647
        (NT::B, PT::Display) => Some(&DISPLAY_INLINE),
648
        (NT::B, PT::FontWeight) => Some(&FONT_WEIGHT_BOLDER),
649
        (NT::I, PT::Display) => Some(&DISPLAY_INLINE),
650
        (NT::U, PT::Display) => Some(&DISPLAY_INLINE),
651
        (NT::U, PT::TextDecoration) => Some(&TEXT_DECORATION_UNDERLINE),
652
        (NT::Small, PT::Display) => Some(&DISPLAY_INLINE),
653
        (NT::Code, PT::Display) => Some(&DISPLAY_INLINE),
654
        (NT::Kbd, PT::Display) => Some(&DISPLAY_INLINE),
655
        (NT::Samp, PT::Display) => Some(&DISPLAY_INLINE),
656
        (NT::Sub, PT::Display) => Some(&DISPLAY_INLINE),
657
        (NT::Sup, PT::Display) => Some(&DISPLAY_INLINE),
658

            
659
        // Text Content
660
252
        (NT::Pre, PT::Display) => Some(&DISPLAY_BLOCK),
661
        (NT::BlockQuote, PT::Display) => Some(&DISPLAY_BLOCK),
662
        (NT::Hr, PT::Display) => Some(&DISPLAY_BLOCK),
663
        (NT::Hr, PT::Width) => Some(&WIDTH_100_PERCENT),
664
        (NT::Hr, PT::Height) => Some(&HEIGHT_ZERO),
665
        (NT::Hr, PT::MarginTop) => Some(&MARGIN_TOP_0_5EM),
666
        (NT::Hr, PT::MarginBottom) => Some(&MARGIN_BOTTOM_0_5EM),
667
        (NT::Hr, PT::BorderTopStyle) => Some(&BORDER_TOP_STYLE_INSET),
668
        (NT::Hr, PT::BorderTopWidth) => Some(&BORDER_TOP_WIDTH_1PX),
669
        (NT::Hr, PT::BorderTopColor) => Some(&BORDER_TOP_COLOR_GRAY),
670

            
671
        // Table Elements
672
        // Per CSS Fragmentation Level 3: table ROWS should avoid breaks inside
673
        // Tables themselves should NOT have break-inside: avoid (they can span pages)
674
2688
        (NT::Table, PT::Display) => Some(&DISPLAY_TABLE),
675
        // NOTE: Removed break-inside: avoid from Table - tables CAN break across pages
676
        (NT::THead, PT::Display) => Some(&DISPLAY_TABLE_HEADER_GROUP),
677
        (NT::THead, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
678
        (NT::THead, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
679
        (NT::TBody, PT::Display) => Some(&DISPLAY_TABLE_ROW_GROUP),
680
        (NT::TBody, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
681
        // NOTE: Removed break-inside: avoid from TBody - tbody CAN break across pages
682
        (NT::TFoot, PT::Display) => Some(&DISPLAY_TABLE_FOOTER_GROUP),
683
        (NT::TFoot, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
684
        (NT::TFoot, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
685
2772
        (NT::Tr, PT::Display) => Some(&DISPLAY_TABLE_ROW),
686
2772
        (NT::Tr, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
687
1386
        (NT::Tr, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
688
        (NT::Th, PT::Display) => Some(&DISPLAY_TABLE_CELL),
689
        (NT::Th, PT::TextAlign) => Some(&TEXT_ALIGN_CENTER),
690
        (NT::Th, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
691
        (NT::Th, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
692
        (NT::Th, PT::PaddingTop) => Some(&PADDING_TOP_1PX),
693
        (NT::Th, PT::PaddingBottom) => Some(&PADDING_BOTTOM_1PX),
694
        (NT::Th, PT::PaddingLeft) => Some(&PADDING_LEFT_1PX),
695
        (NT::Th, PT::PaddingRight) => Some(&PADDING_RIGHT_1PX),
696
6972
        (NT::Td, PT::Display) => Some(&DISPLAY_TABLE_CELL),
697
9240
        (NT::Td, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
698
6972
        (NT::Td, PT::PaddingTop) => Some(&PADDING_TOP_1PX),
699
6972
        (NT::Td, PT::PaddingBottom) => Some(&PADDING_BOTTOM_1PX),
700
6972
        (NT::Td, PT::PaddingLeft) => Some(&PADDING_LEFT_1PX),
701
6972
        (NT::Td, PT::PaddingRight) => Some(&PADDING_RIGHT_1PX),
702

            
703
        // Form Elements
704
        (NT::Form, PT::Display) => Some(&DISPLAY_BLOCK),
705
        (NT::Input, PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
706
        (NT::Button, PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
707
        (NT::Button, PT::Cursor) => Some(&CURSOR_POINTER),
708
        (NT::Button, PT::FontSize) => Some(&FONT_SIZE_13PX),
709
        (NT::Button, PT::PaddingTop) => Some(&PADDING_TOP_5PX),
710
        (NT::Button, PT::PaddingBottom) => Some(&PADDING_BOTTOM_5PX),
711
        (NT::Button, PT::PaddingLeft) => Some(&PADDING_LEFT_10PX),
712
        (NT::Button, PT::PaddingRight) => Some(&PADDING_RIGHT_10PX),
713
        (NT::Button, PT::BorderTopWidth) => Some(&BUTTON_BORDER_TOP_WIDTH),
714
        (NT::Button, PT::BorderBottomWidth) => Some(&BUTTON_BORDER_BOTTOM_WIDTH),
715
        (NT::Button, PT::BorderLeftWidth) => Some(&BUTTON_BORDER_LEFT_WIDTH),
716
        (NT::Button, PT::BorderRightWidth) => Some(&BUTTON_BORDER_RIGHT_WIDTH),
717
        (NT::Button, PT::BorderTopStyle) => Some(&BUTTON_BORDER_TOP_STYLE),
718
        (NT::Button, PT::BorderBottomStyle) => Some(&BUTTON_BORDER_BOTTOM_STYLE),
719
        (NT::Button, PT::BorderLeftStyle) => Some(&BUTTON_BORDER_LEFT_STYLE),
720
        (NT::Button, PT::BorderRightStyle) => Some(&BUTTON_BORDER_RIGHT_STYLE),
721
        (NT::Button, PT::BorderTopColor) => Some(&BUTTON_BORDER_TOP_COLOR),
722
        (NT::Button, PT::BorderBottomColor) => Some(&BUTTON_BORDER_BOTTOM_COLOR),
723
        (NT::Button, PT::BorderLeftColor) => Some(&BUTTON_BORDER_LEFT_COLOR),
724
        (NT::Button, PT::BorderRightColor) => Some(&BUTTON_BORDER_RIGHT_COLOR),
725
        // Text nodes get I-beam cursor for text selection
726
        // The cursor resolution algorithm ensures that explicit cursor properties
727
        // on parent elements (e.g., cursor:pointer on button) take precedence
728
25612
        (NT::Text(_), PT::Cursor) => Some(&CURSOR_TEXT),
729
        (NT::Select, PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
730
        (NT::TextArea, PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
731
        // TextArea gets I-beam cursor since it's an editable text field
732
        (NT::TextArea, PT::Cursor) => Some(&CURSOR_TEXT),
733
        (NT::Label, PT::Display) => Some(&DISPLAY_INLINE),
734
        // Hidden Elements
735
        (NT::Head, PT::Display) => Some(&DISPLAY_NONE),
736
        (NT::Title, PT::Display) => Some(&DISPLAY_NONE),
737
        (NT::Script, PT::Display) => Some(&DISPLAY_NONE),
738
        (NT::Style, PT::Display) => Some(&DISPLAY_NONE),
739
        (NT::Link, PT::Display) => Some(&DISPLAY_NONE),
740

            
741
        // Special Elements
742
84
        (NT::Br, PT::Display) => Some(&DISPLAY_BLOCK),
743
        // Images are replaced elements - inline-block so they respect width/height
744
        (NT::Image(_), PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
745

            
746
        // Media Elements
747
        (NT::Video, PT::Display) => Some(&DISPLAY_INLINE),
748
        (NT::Audio, PT::Display) => Some(&DISPLAY_INLINE),
749
        (NT::Canvas, PT::Display) => Some(&DISPLAY_INLINE),
750
1596
        (NT::Svg, PT::Display) => Some(&DISPLAY_INLINE),
751
        // VirtualView is a block-level replaced element (like div) — must be block
752
        // so it participates in flex layout (flex-grow, etc.)
753
        (NT::VirtualView, PT::Display) => Some(&DISPLAY_BLOCK),
754

            
755
        // Icon Elements - inline-block so they have width/height but flow inline
756
        (NT::Icon(_), PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
757

            
758
        (NT::SelectOption, PT::Display) => Some(&DISPLAY_NONE),
759
        (NT::OptGroup, PT::Display) => Some(&DISPLAY_NONE),
760

            
761
        // Other Inline Elements
762
        (NT::Abbr, PT::Display) => Some(&DISPLAY_INLINE),
763
        (NT::Cite, PT::Display) => Some(&DISPLAY_INLINE),
764
        (NT::Del, PT::Display) => Some(&DISPLAY_INLINE),
765
        (NT::Ins, PT::Display) => Some(&DISPLAY_INLINE),
766
        (NT::Mark, PT::Display) => Some(&DISPLAY_INLINE),
767
        (NT::Q, PT::Display) => Some(&DISPLAY_INLINE),
768
        (NT::Dfn, PT::Display) => Some(&DISPLAY_INLINE),
769
        (NT::Var, PT::Display) => Some(&DISPLAY_INLINE),
770
        (NT::Time, PT::Display) => Some(&DISPLAY_INLINE),
771
        (NT::Data, PT::Display) => Some(&DISPLAY_INLINE),
772
        (NT::Wbr, PT::Display) => Some(&DISPLAY_INLINE),
773
        (NT::Bdi, PT::Display) => Some(&DISPLAY_INLINE),
774
        (NT::Bdo, PT::Display) => Some(&DISPLAY_INLINE),
775
        (NT::Rp, PT::Display) => Some(&DISPLAY_INLINE),
776
        (NT::Rt, PT::Display) => Some(&DISPLAY_INLINE),
777
        (NT::Rtc, PT::Display) => Some(&DISPLAY_INLINE),
778
        (NT::Ruby, PT::Display) => Some(&DISPLAY_INLINE),
779

            
780
        // Block Container Elements
781
        // Per CSS Fragmentation Level 3: figures should avoid page breaks inside
782
        (NT::FieldSet, PT::Display) => Some(&DISPLAY_BLOCK),
783
        (NT::Figure, PT::Display) => Some(&DISPLAY_BLOCK),
784
        (NT::Figure, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
785
        (NT::FigCaption, PT::Display) => Some(&DISPLAY_BLOCK),
786
        (NT::FigCaption, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
787
        (NT::Details, PT::Display) => Some(&DISPLAY_BLOCK),
788
        (NT::Summary, PT::Display) => Some(&DISPLAY_BLOCK),
789
        (NT::Dialog, PT::Display) => Some(&DISPLAY_BLOCK),
790

            
791
        // Table Caption
792
        (NT::Caption, PT::Display) => Some(&DISPLAY_TABLE_CAPTION),
793
        (NT::ColGroup, PT::Display) => Some(&DISPLAY_TABLE_COLUMN_GROUP),
794
        (NT::Col, PT::Display) => Some(&DISPLAY_TABLE_COLUMN),
795

            
796
        // Legacy/Deprecated Elements
797
        (NT::Menu, PT::Display) => Some(&DISPLAY_BLOCK),
798
        (NT::Dir, PT::Display) => Some(&DISPLAY_BLOCK),
799

            
800
        // Html (root) Element
801
        //
802
        // In browsers, the viewport itself provides scrolling when <html> overflows.
803
        // Since Azul has no separate viewport scroll mechanism, we set `height: 100%`
804
        // on the <html> element so it fills the Initial Containing Block (the viewport).
805
        // This constrains child elements like <body> to the viewport height, enabling
806
        // overflow:scroll on <body> to create scrollable content areas.
807
        //
808
        // Without this, <html> has height:auto and grows to fit all content,
809
        // making container_size == content_size, which results in a useless 100% scrollbar.
810
7524
        (NT::Html, PT::Display) => Some(&DISPLAY_BLOCK),
811
7524
        (NT::Html, PT::Height) => Some(&HEIGHT_100_PERCENT),
812

            
813
        // Universal fallback for display property
814
        // Per CSS spec, unknown/custom elements should default to inline
815
        // Text nodes will be filtered out before this function is called
816
27628
        (_, PT::Display) => Some(&DISPLAY_INLINE),
817

            
818
        // No default defined for other combinations
819
3997173
        _ => None,
820
    };
821

            
822
4210273
    result
823
4210273
}
824

            
825
// ============================================================================
826
// UA Scrollbar Defaults — individual CssPropertyWithConditions
827
// ============================================================================
828
//
829
// These rules define the default scrollbar appearance per OS and theme,
830
// using the same `@os` / `@theme` condition system as author CSS.
831
// Each entry is a single CSS property (scrollbar-color or scrollbar-width)
832
// with its conditions.  Rules are evaluated first-match-wins per property type.
833
//
834
// Conceptually equivalent to:
835
//
836
//   @os macos                { scrollbar-width: thin; }
837
//   @os ios                  { scrollbar-width: thin; }
838
//   @os android              { scrollbar-width: thin; }
839
//   /* default */            { scrollbar-width: auto; }
840
//
841
//   @os macos                { -azul-scrollbar-visibility: when-scrolling; }
842
//   @os ios                  { -azul-scrollbar-visibility: when-scrolling; }
843
//   @os android              { -azul-scrollbar-visibility: when-scrolling; }
844
//   /* default */            { -azul-scrollbar-visibility: always; }
845
//
846
//   @os macos                { -azul-scrollbar-fade-delay: 500ms; }
847
//   @os ios                  { -azul-scrollbar-fade-delay: 500ms; }
848
//   @os android              { -azul-scrollbar-fade-delay: 300ms; }
849
//   /* default */            { -azul-scrollbar-fade-delay: 0; }
850
//
851
//   @os macos                { -azul-scrollbar-fade-duration: 200ms; }
852
//   @os ios                  { -azul-scrollbar-fade-duration: 200ms; }
853
//   @os android              { -azul-scrollbar-fade-duration: 150ms; }
854
//   /* default */            { -azul-scrollbar-fade-duration: 0; }
855
//
856
//   @os macos @theme dark    { scrollbar-color: rgba(180,180,180,0.78) rgba(40,40,40,0.31); }
857
//   @os macos @theme light   { scrollbar-color: rgba(80,80,80,0.78) rgba(200,200,200,0.31); }
858
//   @os windows @theme dark  { scrollbar-color: #6e6e6e #202020; }
859
//   @os windows @theme light { scrollbar-color: #828282 #f1f1f1; }
860
//   @os ios @theme dark      { scrollbar-color: rgba(255,255,255,0.4) transparent; }
861
//   @os ios @theme light     { scrollbar-color: rgba(0,0,0,0.4) transparent; }
862
//   @os android @theme dark  { scrollbar-color: rgba(255,255,255,0.3) transparent; }
863
//   @os android @theme light { scrollbar-color: rgba(0,0,0,0.3) transparent; }
864
//   @theme dark              { scrollbar-color: #646464 #2d2d2d; }
865
//   /* default */            { scrollbar-color: #c1c1c1 #f1f1f1; }
866

            
867
/// Helper to create a const `scrollbar-color` `CssProperty`.
868
const fn scrollbar_color(thumb: ColorU, track: ColorU) -> CssProperty {
869
    CssProperty::ScrollbarColor(CssPropertyValue::Exact(
870
        StyleScrollbarColor::Custom(ScrollbarColorCustom { thumb, track }),
871
    ))
872
}
873

            
874
/// Helper to create a const `scrollbar-width` `CssProperty`.
875
const fn scrollbar_width(w: LayoutScrollbarWidth) -> CssProperty {
876
    CssProperty::ScrollbarWidth(CssPropertyValue::Exact(w))
877
}
878

            
879
/// Helper to create a const `-azul-scrollbar-visibility` `CssProperty`.
880
const fn scrollbar_visibility(v: ScrollbarVisibilityMode) -> CssProperty {
881
    CssProperty::ScrollbarVisibility(CssPropertyValue::Exact(v))
882
}
883

            
884
/// Helper to create a const `-azul-scrollbar-fade-delay` `CssProperty`.
885
const fn scrollbar_fade_delay(ms: u32) -> CssProperty {
886
    CssProperty::ScrollbarFadeDelay(CssPropertyValue::Exact(ScrollbarFadeDelay::new(ms)))
887
}
888

            
889
/// Helper to create a const `-azul-scrollbar-fade-duration` `CssProperty`.
890
const fn scrollbar_fade_duration(ms: u32) -> CssProperty {
891
    CssProperty::ScrollbarFadeDuration(CssPropertyValue::Exact(ScrollbarFadeDuration::new(ms)))
892
}
893

            
894
/// UA scrollbar CSS properties with `@os` / `@theme` conditions.
895
///
896
/// Ordered most-specific first.  The evaluation function picks the
897
/// first matching entry for each property type (`scrollbar-color`,
898
/// `scrollbar-width`, `-azul-scrollbar-visibility`,
899
/// `-azul-scrollbar-fade-delay`, `-azul-scrollbar-fade-duration`).
900
pub(crate) static UA_SCROLLBAR_CSS: &[CssPropertyWithConditions] = &[
901
    // ── scrollbar-width per OS ──────────────────────────────────────────
902
    // macOS → thin (overlay)
903
    CssPropertyWithConditions::with_single_condition(
904
        scrollbar_width(LayoutScrollbarWidth::Thin),
905
        &[DynamicSelector::Os(OsCondition::MacOS)],
906
    ),
907
    // iOS → thin
908
    CssPropertyWithConditions::with_single_condition(
909
        scrollbar_width(LayoutScrollbarWidth::Thin),
910
        &[DynamicSelector::Os(OsCondition::IOS)],
911
    ),
912
    // Android → thin
913
    CssPropertyWithConditions::with_single_condition(
914
        scrollbar_width(LayoutScrollbarWidth::Thin),
915
        &[DynamicSelector::Os(OsCondition::Android)],
916
    ),
917
    // default → auto (classic)
918
    CssPropertyWithConditions::simple(
919
        scrollbar_width(LayoutScrollbarWidth::Auto),
920
    ),
921

            
922
    // ── scrollbar-visibility per OS ─────────────────────────────────────
923
    // macOS → overlay (show only when scrolling)
924
    CssPropertyWithConditions::with_single_condition(
925
        scrollbar_visibility(ScrollbarVisibilityMode::WhenScrolling),
926
        &[DynamicSelector::Os(OsCondition::MacOS)],
927
    ),
928
    // iOS → overlay
929
    CssPropertyWithConditions::with_single_condition(
930
        scrollbar_visibility(ScrollbarVisibilityMode::WhenScrolling),
931
        &[DynamicSelector::Os(OsCondition::IOS)],
932
    ),
933
    // Android → overlay
934
    CssPropertyWithConditions::with_single_condition(
935
        scrollbar_visibility(ScrollbarVisibilityMode::WhenScrolling),
936
        &[DynamicSelector::Os(OsCondition::Android)],
937
    ),
938
    // default → always visible (classic)
939
    CssPropertyWithConditions::simple(
940
        scrollbar_visibility(ScrollbarVisibilityMode::Always),
941
    ),
942

            
943
    // ── scrollbar-fade-delay per OS ─────────────────────────────────────
944
    CssPropertyWithConditions::with_single_condition(
945
        scrollbar_fade_delay(500),
946
        &[DynamicSelector::Os(OsCondition::MacOS)],
947
    ),
948
    CssPropertyWithConditions::with_single_condition(
949
        scrollbar_fade_delay(500),
950
        &[DynamicSelector::Os(OsCondition::IOS)],
951
    ),
952
    CssPropertyWithConditions::with_single_condition(
953
        scrollbar_fade_delay(300),
954
        &[DynamicSelector::Os(OsCondition::Android)],
955
    ),
956
    // default → 0 (no fade)
957
    CssPropertyWithConditions::simple(
958
        scrollbar_fade_delay(0),
959
    ),
960

            
961
    // ── scrollbar-fade-duration per OS ──────────────────────────────────
962
    CssPropertyWithConditions::with_single_condition(
963
        scrollbar_fade_duration(200),
964
        &[DynamicSelector::Os(OsCondition::MacOS)],
965
    ),
966
    CssPropertyWithConditions::with_single_condition(
967
        scrollbar_fade_duration(200),
968
        &[DynamicSelector::Os(OsCondition::IOS)],
969
    ),
970
    CssPropertyWithConditions::with_single_condition(
971
        scrollbar_fade_duration(150),
972
        &[DynamicSelector::Os(OsCondition::Android)],
973
    ),
974
    // default → 0 (instant)
975
    CssPropertyWithConditions::simple(
976
        scrollbar_fade_duration(0),
977
    ),
978

            
979
    // ── scrollbar-color per OS + theme ──────────────────────────────────
980
    // macOS dark: light grey thumb on dark semi-transparent track
981
    CssPropertyWithConditions::with_single_condition(
982
        scrollbar_color(
983
            ColorU { r: 180, g: 180, b: 180, a: 200 },
984
            ColorU { r: 40, g: 40, b: 40, a: 80 },
985
        ),
986
        &[DynamicSelector::Os(OsCondition::MacOS), DynamicSelector::Theme(ThemeCondition::Dark)],
987
    ),
988
    // macOS light: dark grey thumb on light semi-transparent track
989
    CssPropertyWithConditions::with_single_condition(
990
        scrollbar_color(
991
            ColorU { r: 80, g: 80, b: 80, a: 200 },
992
            ColorU { r: 200, g: 200, b: 200, a: 80 },
993
        ),
994
        &[DynamicSelector::Os(OsCondition::MacOS), DynamicSelector::Theme(ThemeCondition::Light)],
995
    ),
996
    // Windows dark
997
    CssPropertyWithConditions::with_single_condition(
998
        scrollbar_color(
999
            ColorU { r: 110, g: 110, b: 110, a: 255 },
            ColorU { r: 32, g: 32, b: 32, a: 255 },
        ),
        &[DynamicSelector::Os(OsCondition::Windows), DynamicSelector::Theme(ThemeCondition::Dark)],
    ),
    // Windows light
    CssPropertyWithConditions::with_single_condition(
        scrollbar_color(
            ColorU { r: 130, g: 130, b: 130, a: 255 },
            ColorU { r: 241, g: 241, b: 241, a: 255 },
        ),
        &[DynamicSelector::Os(OsCondition::Windows), DynamicSelector::Theme(ThemeCondition::Light)],
    ),
    // iOS dark
    CssPropertyWithConditions::with_single_condition(
        scrollbar_color(
            ColorU { r: 255, g: 255, b: 255, a: 100 },
            ColorU::TRANSPARENT,
        ),
        &[DynamicSelector::Os(OsCondition::IOS), DynamicSelector::Theme(ThemeCondition::Dark)],
    ),
    // iOS light
    CssPropertyWithConditions::with_single_condition(
        scrollbar_color(
            ColorU { r: 0, g: 0, b: 0, a: 100 },
            ColorU::TRANSPARENT,
        ),
        &[DynamicSelector::Os(OsCondition::IOS), DynamicSelector::Theme(ThemeCondition::Light)],
    ),
    // Android dark
    CssPropertyWithConditions::with_single_condition(
        scrollbar_color(
            ColorU { r: 255, g: 255, b: 255, a: 77 },
            ColorU::TRANSPARENT,
        ),
        &[DynamicSelector::Os(OsCondition::Android), DynamicSelector::Theme(ThemeCondition::Dark)],
    ),
    // Android light
    CssPropertyWithConditions::with_single_condition(
        scrollbar_color(
            ColorU { r: 0, g: 0, b: 0, a: 77 },
            ColorU::TRANSPARENT,
        ),
        &[DynamicSelector::Os(OsCondition::Android), DynamicSelector::Theme(ThemeCondition::Light)],
    ),
    // Linux / unknown dark fallback
    CssPropertyWithConditions::with_single_condition(
        scrollbar_color(
            ColorU { r: 100, g: 100, b: 100, a: 255 },
            ColorU { r: 45, g: 45, b: 45, a: 255 },
        ),
        &[DynamicSelector::Theme(ThemeCondition::Dark)],
    ),
    // Unconditional fallback (classic light)
    CssPropertyWithConditions::simple(
        scrollbar_color(
            ColorU { r: 193, g: 193, b: 193, a: 255 },
            ColorU { r: 241, g: 241, b: 241, a: 255 },
        ),
    ),
];
/// Resolved UA scrollbar defaults after evaluating conditions.
///
/// All fields are guaranteed to resolve because `UA_SCROLLBAR_CSS`
/// contains unconditional fallback entries for every property type.
pub struct ResolvedUaScrollbar {
    pub color: StyleScrollbarColor,
    pub width: LayoutScrollbarWidth,
    pub visibility: ScrollbarVisibilityMode,
    pub fade_delay: ScrollbarFadeDelay,
    pub fade_duration: ScrollbarFadeDuration,
}
/// Evaluate UA scrollbar CSS rules against a `DynamicSelectorContext`.
///
/// Iterates `UA_SCROLLBAR_CSS` and picks the first matching entry per
/// property type.  Unconditional fallback entries in the table guarantee
/// that every field resolves.
32424
pub fn evaluate_ua_scrollbar_css(ctx: &DynamicSelectorContext) -> ResolvedUaScrollbar {
32424
    let mut color: Option<StyleScrollbarColor> = None;
32424
    let mut width: Option<LayoutScrollbarWidth> = None;
32424
    let mut visibility: Option<ScrollbarVisibilityMode> = None;
32424
    let mut fade_delay: Option<ScrollbarFadeDelay> = None;
32424
    let mut fade_duration: Option<ScrollbarFadeDuration> = None;
843024
    for prop in UA_SCROLLBAR_CSS {
843024
        if !prop.matches(ctx) {
680904
            continue;
162120
        }
32424
        match &prop.property {
32424
            CssProperty::ScrollbarColor(CssPropertyValue::Exact(c)) => {
32424
                if color.is_none() {
32424
                    color = Some(*c);
32424
                }
            }
32424
            CssProperty::ScrollbarWidth(CssPropertyValue::Exact(w)) => {
32424
                if width.is_none() {
32424
                    width = Some(*w);
32424
                }
            }
32424
            CssProperty::ScrollbarVisibility(CssPropertyValue::Exact(v)) => {
32424
                if visibility.is_none() {
32424
                    visibility = Some(*v);
32424
                }
            }
32424
            CssProperty::ScrollbarFadeDelay(CssPropertyValue::Exact(d)) => {
32424
                if fade_delay.is_none() {
32424
                    fade_delay = Some(*d);
32424
                }
            }
32424
            CssProperty::ScrollbarFadeDuration(CssPropertyValue::Exact(d)) => {
32424
                if fade_duration.is_none() {
32424
                    fade_duration = Some(*d);
32424
                }
            }
            _ => {}
        }
162120
        if color.is_some() && width.is_some() && visibility.is_some()
32424
            && fade_delay.is_some() && fade_duration.is_some()
        {
32424
            break;
129696
        }
    }
    // Unconditional `simple` entries in UA_SCROLLBAR_CSS guarantee all
    // fields resolve; these defaults match those entries as a safety net.
32424
    ResolvedUaScrollbar {
32424
        color: color.unwrap_or(StyleScrollbarColor::Custom(ScrollbarColorCustom {
32424
            thumb: ColorU { r: 193, g: 193, b: 193, a: 255 },
32424
            track: ColorU { r: 241, g: 241, b: 241, a: 255 },
32424
        })),
32424
        width: width.unwrap_or(LayoutScrollbarWidth::Auto),
32424
        visibility: visibility.unwrap_or(ScrollbarVisibilityMode::Always),
32424
        fade_delay: fade_delay.unwrap_or(ScrollbarFadeDelay { ms: 0 }),
32424
        fade_duration: fade_duration.unwrap_or(ScrollbarFadeDuration { ms: 0 }),
32424
    }
32424
}