Skip to main content

azul_core/
ua_css.rs

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//! This user-agent stylesheet integrates principles from normalize.css v8.0.1:
24//!
25//! - **normalize.css License**: MIT License Copyright (c) Nicolas Gallagher and
26//    Jonathan Neal https://github.com/necolas/normalize.css
27//!
28//! The normalize.css project is licensed under the MIT License, which permits
29//! commercial use, modification, distribution, and private use. The full license
30//! text is as follows:
31//!
32//! ```text
33//! MIT License
34//!
35//! Copyright (c) Nicolas Gallagher and Jonathan Neal
36//!
37//! Permission is hereby granted, free of charge, to any person obtaining a copy
38//! of this software and associated documentation files (the "Software"), to deal
39//! in the Software without restriction, including without limitation the rights
40//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41//! copies of the Software, and to permit persons to whom the Software is
42//! furnished to do so, subject to the following conditions:
43//!
44//! The above copyright notice and this permission notice shall be included in all
45//! copies or substantial portions of the Software.
46//!
47//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
53//! SOFTWARE.
54//! ```
55//!
56//! This implementation is NOT a direct copy of normalize.css but incorporates its
57//! principles and approach. The Azul project's overall license applies to this
58//! implementation.
59//!
60//! # References
61//!
62//! - CSS 2.1 Specification: https://www.w3.org/TR/CSS21/
63//! - HTML Living Standard: https://html.spec.whatwg.org/
64//! - normalize.css: https://necolas.github.io/normalize.css/
65
66use azul_css::{
67    css::CssPropertyValue,
68    props::{
69        basic::{
70            font::StyleFontWeight, length::PercentageValue, pixel::PixelValue, ColorU,
71            StyleFontSize,
72        },
73        layout::{
74            dimensions::{LayoutHeight, LayoutWidth},
75            display::LayoutDisplay,
76            fragmentation::{BreakInside, PageBreak},
77            spacing::{
78                LayoutMarginBottom, LayoutMarginLeft, LayoutMarginRight, LayoutMarginTop,
79                LayoutPaddingBottom, LayoutPaddingInlineEnd, LayoutPaddingInlineStart,
80                LayoutPaddingLeft, LayoutPaddingRight, LayoutPaddingTop,
81            },
82        },
83        property::{CssProperty, CssPropertyType},
84        style::{
85            border::{BorderStyle, LayoutBorderTopWidth, StyleBorderTopColor, StyleBorderTopStyle},
86            content::CounterReset,
87            effects::StyleCursor,
88            lists::StyleListStyleType,
89            text::StyleTextDecoration,
90            StyleTextAlign, StyleVerticalAlign,
91        },
92    },
93};
94
95use crate::dom::NodeType;
96
97/// 100% width
98static WIDTH_100_PERCENT: CssProperty = CssProperty::Width(CssPropertyValue::Exact(
99    LayoutWidth::Px(PixelValue::const_percent(100)),
100));
101
102/// 100% height
103static HEIGHT_100_PERCENT: CssProperty = CssProperty::Height(CssPropertyValue::Exact(
104    LayoutHeight::Px(PixelValue::const_percent(100)),
105));
106
107/// display: block
108static DISPLAY_BLOCK: CssProperty =
109    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::Block));
110
111/// display: inline
112static DISPLAY_INLINE: CssProperty =
113    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::Inline));
114
115/// display: inline-block
116static DISPLAY_INLINE_BLOCK: CssProperty =
117    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::InlineBlock));
118
119/// display: none
120static DISPLAY_NONE: CssProperty =
121    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::None));
122
123/// display: table
124static DISPLAY_TABLE: CssProperty =
125    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::Table));
126
127/// display: table-row
128static DISPLAY_TABLE_ROW: CssProperty =
129    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableRow));
130
131/// display: table-cell
132static DISPLAY_TABLE_CELL: CssProperty =
133    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableCell));
134
135/// display: table-header-group
136static DISPLAY_TABLE_HEADER_GROUP: CssProperty =
137    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableHeaderGroup));
138
139/// display: table-row-group
140static DISPLAY_TABLE_ROW_GROUP: CssProperty =
141    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableRowGroup));
142
143/// display: table-footer-group
144static DISPLAY_TABLE_FOOTER_GROUP: CssProperty =
145    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableFooterGroup));
146
147/// display: table-caption
148static DISPLAY_TABLE_CAPTION: CssProperty =
149    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableCaption));
150
151/// display: table-column-group
152static DISPLAY_TABLE_COLUMN_GROUP: CssProperty =
153    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableColumnGroup));
154
155/// display: table-column
156static DISPLAY_TABLE_COLUMN: CssProperty =
157    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::TableColumn));
158
159/// display: list-item
160static DISPLAY_LIST_ITEM: CssProperty =
161    CssProperty::Display(CssPropertyValue::Exact(LayoutDisplay::ListItem));
162
163/// cursor: pointer (for clickable elements like buttons, links)
164static CURSOR_POINTER: CssProperty =
165    CssProperty::Cursor(CssPropertyValue::Exact(StyleCursor::Pointer));
166
167/// cursor: text (for selectable text elements)
168static CURSOR_TEXT: CssProperty =
169    CssProperty::Cursor(CssPropertyValue::Exact(StyleCursor::Text));
170
171/// margin-top: 0
172static MARGIN_TOP_ZERO: CssProperty =
173    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
174        inner: PixelValue::const_px(0),
175    }));
176
177/// margin-bottom: 0
178static MARGIN_BOTTOM_ZERO: CssProperty =
179    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
180        inner: PixelValue::const_px(0),
181    }));
182
183/// margin-left: 0
184static MARGIN_LEFT_ZERO: CssProperty =
185    CssProperty::MarginLeft(CssPropertyValue::Exact(LayoutMarginLeft {
186        inner: PixelValue::const_px(0),
187    }));
188
189/// margin-right: 0
190static MARGIN_RIGHT_ZERO: CssProperty =
191    CssProperty::MarginRight(CssPropertyValue::Exact(LayoutMarginRight {
192        inner: PixelValue::const_px(0),
193    }));
194
195// Chrome User-Agent Stylesheet: body { margin: 8px; }
196/// margin-top: 8px (Chrome UA default for body)
197static MARGIN_TOP_8PX: CssProperty =
198    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
199        inner: PixelValue::const_px(8),
200    }));
201
202/// margin-bottom: 8px (Chrome UA default for body)
203static MARGIN_BOTTOM_8PX: CssProperty =
204    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
205        inner: PixelValue::const_px(8),
206    }));
207
208/// margin-left: 8px (Chrome UA default for body)
209static MARGIN_LEFT_8PX: CssProperty =
210    CssProperty::MarginLeft(CssPropertyValue::Exact(LayoutMarginLeft {
211        inner: PixelValue::const_px(8),
212    }));
213
214/// margin-right: 8px (Chrome UA default for body)
215static MARGIN_RIGHT_8PX: CssProperty =
216    CssProperty::MarginRight(CssPropertyValue::Exact(LayoutMarginRight {
217        inner: PixelValue::const_px(8),
218    }));
219
220/// font-size: 2em (for H1)
221static FONT_SIZE_2EM: CssProperty = CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
222    inner: PixelValue::const_em(2),
223}));
224
225/// font-size: 1.5em (for H2)
226static FONT_SIZE_1_5EM: CssProperty =
227    CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
228        inner: PixelValue::const_em_fractional(1, 5),
229    }));
230
231/// font-size: 1.17em (for H3)
232static FONT_SIZE_1_17EM: CssProperty =
233    CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
234        inner: PixelValue::const_em_fractional(1, 17),
235    }));
236
237/// font-size: 1em (for H4)
238static FONT_SIZE_1EM: CssProperty = CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
239    inner: PixelValue::const_em(1),
240}));
241
242/// font-size: 0.83em (for H5)
243static FONT_SIZE_0_83EM: CssProperty =
244    CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
245        inner: PixelValue::const_em_fractional(0, 83),
246    }));
247
248/// font-size: 0.67em (for H6)
249static FONT_SIZE_0_67EM: CssProperty =
250    CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
251        inner: PixelValue::const_em_fractional(0, 67),
252    }));
253
254/// margin-top: 1em (for P)
255static MARGIN_TOP_1EM: CssProperty =
256    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
257        inner: PixelValue::const_em(1),
258    }));
259
260/// margin-bottom: 1em (for P)
261static MARGIN_BOTTOM_1EM: CssProperty =
262    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
263        inner: PixelValue::const_em(1),
264    }));
265
266/// margin-top: 0.67em (for H1)
267static MARGIN_TOP_0_67EM: CssProperty =
268    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
269        inner: PixelValue::const_em_fractional(0, 67),
270    }));
271
272/// margin-bottom: 0.67em (for H1)
273static MARGIN_BOTTOM_0_67EM: CssProperty =
274    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
275        inner: PixelValue::const_em_fractional(0, 67),
276    }));
277
278/// margin-top: 0.83em (for H2)
279static MARGIN_TOP_0_83EM: CssProperty =
280    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
281        inner: PixelValue::const_em_fractional(0, 83),
282    }));
283
284/// margin-bottom: 0.83em (for H2)
285static MARGIN_BOTTOM_0_83EM: CssProperty =
286    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
287        inner: PixelValue::const_em_fractional(0, 83),
288    }));
289
290/// margin-top: 1.33em (for H4)
291static MARGIN_TOP_1_33EM: CssProperty =
292    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
293        inner: PixelValue::const_em_fractional(1, 33),
294    }));
295
296/// margin-bottom: 1.33em (for H4)
297static MARGIN_BOTTOM_1_33EM: CssProperty =
298    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
299        inner: PixelValue::const_em_fractional(1, 33),
300    }));
301
302/// margin-top: 1.67em (for H5)
303static MARGIN_TOP_1_67EM: CssProperty =
304    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
305        inner: PixelValue::const_em_fractional(1, 67),
306    }));
307
308/// margin-bottom: 1.67em (for H5)
309static MARGIN_BOTTOM_1_67EM: CssProperty =
310    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
311        inner: PixelValue::const_em_fractional(1, 67),
312    }));
313
314/// margin-top: 2.33em (for H6)
315static MARGIN_TOP_2_33EM: CssProperty =
316    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
317        inner: PixelValue::const_em_fractional(2, 33),
318    }));
319
320/// margin-bottom: 2.33em (for H6)
321static MARGIN_BOTTOM_2_33EM: CssProperty =
322    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
323        inner: PixelValue::const_em_fractional(2, 33),
324    }));
325
326/// font-weight: bold (for headings)
327static FONT_WEIGHT_BOLD: CssProperty =
328    CssProperty::FontWeight(CssPropertyValue::Exact(StyleFontWeight::Bold));
329
330/// font-weight: bolder
331static FONT_WEIGHT_BOLDER: CssProperty =
332    CssProperty::FontWeight(CssPropertyValue::Exact(StyleFontWeight::Bolder));
333
334// Table cell padding - Chrome UA CSS default: 1px
335static PADDING_1PX: CssProperty =
336    CssProperty::PaddingTop(CssPropertyValue::Exact(LayoutPaddingTop {
337        inner: PixelValue::const_px(1),
338    }));
339
340static PADDING_TOP_1PX: CssProperty =
341    CssProperty::PaddingTop(CssPropertyValue::Exact(LayoutPaddingTop {
342        inner: PixelValue::const_px(1),
343    }));
344
345static PADDING_BOTTOM_1PX: CssProperty =
346    CssProperty::PaddingBottom(CssPropertyValue::Exact(LayoutPaddingBottom {
347        inner: PixelValue::const_px(1),
348    }));
349
350static PADDING_LEFT_1PX: CssProperty =
351    CssProperty::PaddingLeft(CssPropertyValue::Exact(LayoutPaddingLeft {
352        inner: PixelValue::const_px(1),
353    }));
354
355static PADDING_RIGHT_1PX: CssProperty =
356    CssProperty::PaddingRight(CssPropertyValue::Exact(LayoutPaddingRight {
357        inner: PixelValue::const_px(1),
358    }));
359
360/// text-align: center (for th elements)
361static TEXT_ALIGN_CENTER: CssProperty =
362    CssProperty::TextAlign(CssPropertyValue::Exact(StyleTextAlign::Center));
363
364/// vertical-align: middle (for table elements)
365static VERTICAL_ALIGN_MIDDLE: CssProperty =
366    CssProperty::VerticalAlign(CssPropertyValue::Exact(StyleVerticalAlign::Middle));
367
368/// list-style-type: disc (default for <ul>)
369static LIST_STYLE_TYPE_DISC: CssProperty =
370    CssProperty::ListStyleType(CssPropertyValue::Exact(StyleListStyleType::Disc));
371
372/// list-style-type: decimal (default for <ol>)
373static LIST_STYLE_TYPE_DECIMAL: CssProperty =
374    CssProperty::ListStyleType(CssPropertyValue::Exact(StyleListStyleType::Decimal));
375
376// --- HR Element Defaults ---
377// Per HTML spec, <hr> renders as a horizontal line with inset border style
378
379/// margin-top: 0.5em (for hr)
380static MARGIN_TOP_0_5EM: CssProperty =
381    CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
382        inner: PixelValue::const_em_fractional(0, 5),
383    }));
384
385/// margin-bottom: 0.5em (for hr)
386static MARGIN_BOTTOM_0_5EM: CssProperty =
387    CssProperty::MarginBottom(CssPropertyValue::Exact(LayoutMarginBottom {
388        inner: PixelValue::const_em_fractional(0, 5),
389    }));
390
391/// border-top-style: inset (for hr - default browser style)
392static BORDER_TOP_STYLE_INSET: CssProperty =
393    CssProperty::BorderTopStyle(CssPropertyValue::Exact(StyleBorderTopStyle {
394        inner: BorderStyle::Inset,
395    }));
396
397/// border-top-width: 1px (for hr)
398static BORDER_TOP_WIDTH_1PX: CssProperty =
399    CssProperty::BorderTopWidth(CssPropertyValue::Exact(LayoutBorderTopWidth {
400        inner: PixelValue::const_px(1),
401    }));
402
403/// border-top-color: gray (for hr - default visible color)
404static BORDER_TOP_COLOR_GRAY: CssProperty =
405    CssProperty::BorderTopColor(CssPropertyValue::Exact(StyleBorderTopColor {
406        inner: ColorU {
407            r: 128,
408            g: 128,
409            b: 128,
410            a: 255,
411        },
412    }));
413
414/// height: 0 (for hr - the line comes from the border, not height)
415static HEIGHT_ZERO: CssProperty = CssProperty::Height(CssPropertyValue::Exact(LayoutHeight::Px(
416    PixelValue::const_px(0),
417)));
418
419/// counter-reset: list-item 0 (default for <ul>, <ol>)
420/// Per CSS Lists Module Level 3, list containers automatically reset the list-item counter
421static COUNTER_RESET_LIST_ITEM: CssProperty =
422    CssProperty::CounterReset(CssPropertyValue::Exact(CounterReset::list_item()));
423
424// CSS Fragmentation (Page Breaking) Properties
425//
426// Per CSS Fragmentation Level 3 and paged media best practices,
427// certain elements should avoid page breaks inside them
428
429/// break-inside: avoid
430/// Used for elements that should not be split across page boundaries
431/// Applied to: h1-h6, table, thead, tbody, tfoot, figure, figcaption
432static BREAK_INSIDE_AVOID: CssProperty = CssProperty::break_inside(BreakInside::Avoid);
433
434/// break-before: page
435/// Forces a page break before the element
436static BREAK_BEFORE_PAGE: CssProperty = CssProperty::break_before(PageBreak::Page);
437
438/// break-after: page
439/// Forces a page break after the element
440static BREAK_AFTER_PAGE: CssProperty = CssProperty::break_after(PageBreak::Page);
441
442/// break-before: avoid
443/// Avoids a page break before the element
444static BREAK_BEFORE_AVOID: CssProperty = CssProperty::break_before(PageBreak::Avoid);
445
446/// break-after: avoid
447/// Avoids a page break after the element (useful for headings)
448static BREAK_AFTER_AVOID: CssProperty = CssProperty::break_after(PageBreak::Avoid);
449
450/// padding-inline-start: 40px (default for <li>)
451///
452/// Creates space for list markers in the inline-start direction (left in LTR, right in RTL)
453/// padding-inline-start: 40px for list items per CSS Lists Module Level 3
454/// Applied to <li> items to create gutter space for ::marker pseudo-elements
455///
456/// NOTE: This should be on the list items, not the container, because:
457///
458/// 1. ::marker pseudo-elements are children of <li>, not <ul>/<ol>
459/// 2. The marker needs to be positioned relative to the list item's content box
460/// 3. Padding on <li> creates space between the marker and the text content
461/// TODO: Change to PaddingInlineStart once logical property resolution is implemented
462static PADDING_INLINE_START_40PX: CssProperty =
463    CssProperty::PaddingLeft(CssPropertyValue::Exact(LayoutPaddingLeft {
464        inner: PixelValue::const_px(40),
465    }));
466
467/// Text decoration: underline - used for <a> and <u> elements
468static TEXT_DECORATION_UNDERLINE: CssProperty = CssProperty::TextDecoration(
469    CssPropertyValue::Exact(StyleTextDecoration::Underline),
470);
471
472/*
473const LINE_HEIGHT_1_15: CssProperty = CssProperty::LineHeight(LayoutLineHeightValue::Exact(
474    LayoutLineHeight {
475        inner: PercentageValue::const_new(115), // 1.15 = 115%
476    },
477));
478*/
479
480/// Returns the default user-agent CSS property value for a given node type and property.
481///
482/// This function provides the baseline styling that should be applied before any author
483/// styles. It ensures that elements have sensible defaults that prevent layout issues.
484///
485/// # Arguments
486///
487/// * `node_type` - The type of DOM node (e.g., `Body`, `H1`, `Div`)
488/// * `property_type` - The specific CSS property to query (e.g., `Width`, `Display`)
489///
490/// # Returns
491///
492/// `Some(CssProperty)` if a default value is defined for this combination, otherwise `None`.
493pub fn get_ua_property(
494    node_type: &NodeType,
495    property_type: CssPropertyType,
496) -> Option<&'static CssProperty> {
497    use CssPropertyType as PT;
498    use NodeType as NT;
499
500    let result = match (node_type, property_type) {
501        // HTML Element
502        // (Html, PT::LineHeight) => Some(&LINE_HEIGHT_1_15),
503
504        // Body Element - CRITICAL for preventing layout collapse
505        (NT::Body, PT::Display) => Some(&DISPLAY_BLOCK),
506        // NOTE: Body does NOT have width: 100% in standard UA CSS - it inherits from ICB
507        // (NT::Body, PT::Height) => Some(&HEIGHT_100_PERCENT),
508        (NT::Body, PT::MarginTop) => Some(&MARGIN_TOP_8PX),
509        (NT::Body, PT::MarginBottom) => Some(&MARGIN_BOTTOM_8PX),
510        (NT::Body, PT::MarginLeft) => Some(&MARGIN_LEFT_8PX),
511        (NT::Body, PT::MarginRight) => Some(&MARGIN_RIGHT_8PX),
512
513        // Block-level Elements
514        // NOTE: Do NOT set width: 100% here! Block elements have width: auto by default
515        // in CSS spec. width: auto for blocks means "fill available width" but it's NOT
516        // the same as width: 100%. The difference is critical for flexbox: width: auto
517        // allows flex-grow/flex-shrink to control sizing, while width: 100% prevents it.
518        (NT::Div, PT::Display) => Some(&DISPLAY_BLOCK),
519        (NT::P, PT::Display) => Some(&DISPLAY_BLOCK),
520        // REMOVED - blocks have width: auto by default
521        // (NT::Div, PT::Width) => Some(&WIDTH_100_PERCENT),
522        // REMOVED - blocks have width: auto by default
523        // (NT::P, PT::Width) => Some(&WIDTH_100_PERCENT),
524        (NT::P, PT::MarginTop) => Some(&MARGIN_TOP_1EM),
525        (NT::P, PT::MarginBottom) => Some(&MARGIN_BOTTOM_1EM),
526        (NT::Main, PT::Display) => Some(&DISPLAY_BLOCK),
527        (NT::Header, PT::Display) => Some(&DISPLAY_BLOCK),
528        (NT::Footer, PT::Display) => Some(&DISPLAY_BLOCK),
529        (NT::Section, PT::Display) => Some(&DISPLAY_BLOCK),
530        (NT::Article, PT::Display) => Some(&DISPLAY_BLOCK),
531        (NT::Aside, PT::Display) => Some(&DISPLAY_BLOCK),
532        (NT::Nav, PT::Display) => Some(&DISPLAY_BLOCK),
533
534        // Headings - Chrome UA CSS values
535        // Per CSS Fragmentation Level 3: headings should avoid page breaks inside
536        // and after them (to keep heading with following content)
537        (NT::H1, PT::Display) => Some(&DISPLAY_BLOCK),
538        (NT::H1, PT::FontSize) => Some(&FONT_SIZE_2EM),
539        (NT::H1, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
540        (NT::H1, PT::MarginTop) => Some(&MARGIN_TOP_0_67EM),
541        (NT::H1, PT::MarginBottom) => Some(&MARGIN_BOTTOM_0_67EM),
542        (NT::H1, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
543        (NT::H1, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
544
545        (NT::H2, PT::Display) => Some(&DISPLAY_BLOCK),
546        (NT::H2, PT::FontSize) => Some(&FONT_SIZE_1_5EM),
547        (NT::H2, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
548        (NT::H2, PT::MarginTop) => Some(&MARGIN_TOP_0_83EM),
549        (NT::H2, PT::MarginBottom) => Some(&MARGIN_BOTTOM_0_83EM),
550        (NT::H2, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
551        (NT::H2, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
552
553        (NT::H3, PT::Display) => Some(&DISPLAY_BLOCK),
554        (NT::H3, PT::FontSize) => Some(&FONT_SIZE_1_17EM),
555        (NT::H3, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
556        (NT::H3, PT::MarginTop) => Some(&MARGIN_TOP_1EM),
557        (NT::H3, PT::MarginBottom) => Some(&MARGIN_BOTTOM_1EM),
558        (NT::H3, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
559        (NT::H3, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
560
561        (NT::H4, PT::Display) => Some(&DISPLAY_BLOCK),
562        (NT::H4, PT::FontSize) => Some(&FONT_SIZE_1EM),
563        (NT::H4, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
564        (NT::H4, PT::MarginTop) => Some(&MARGIN_TOP_1_33EM),
565        (NT::H4, PT::MarginBottom) => Some(&MARGIN_BOTTOM_1_33EM),
566        (NT::H4, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
567        (NT::H4, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
568
569        (NT::H5, PT::Display) => Some(&DISPLAY_BLOCK),
570        (NT::H5, PT::FontSize) => Some(&FONT_SIZE_0_83EM),
571        (NT::H5, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
572        (NT::H5, PT::MarginTop) => Some(&MARGIN_TOP_1_67EM),
573        (NT::H5, PT::MarginBottom) => Some(&MARGIN_BOTTOM_1_67EM),
574        (NT::H5, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
575        (NT::H5, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
576
577        (NT::H6, PT::Display) => Some(&DISPLAY_BLOCK),
578        (NT::H6, PT::FontSize) => Some(&FONT_SIZE_0_67EM),
579        (NT::H6, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
580        (NT::H6, PT::MarginTop) => Some(&MARGIN_TOP_2_33EM),
581        (NT::H6, PT::MarginBottom) => Some(&MARGIN_BOTTOM_2_33EM),
582        (NT::H6, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
583        (NT::H6, PT::BreakAfter) => Some(&BREAK_AFTER_AVOID),
584
585        // Lists - padding on container creates gutter for markers
586        (NT::Ul, PT::Display) => Some(&DISPLAY_BLOCK),
587        (NT::Ul, PT::ListStyleType) => Some(&LIST_STYLE_TYPE_DISC),
588        (NT::Ul, PT::CounterReset) => Some(&COUNTER_RESET_LIST_ITEM),
589        (NT::Ul, PT::PaddingLeft) => Some(&PADDING_INLINE_START_40PX),
590        (NT::Ol, PT::Display) => Some(&DISPLAY_BLOCK),
591        (NT::Ol, PT::ListStyleType) => Some(&LIST_STYLE_TYPE_DECIMAL),
592        (NT::Ol, PT::CounterReset) => Some(&COUNTER_RESET_LIST_ITEM),
593        (NT::Ol, PT::PaddingLeft) => Some(&PADDING_INLINE_START_40PX),
594        (NT::Li, PT::Display) => Some(&DISPLAY_LIST_ITEM),
595        (NT::Dl, PT::Display) => Some(&DISPLAY_BLOCK),
596        (NT::Dt, PT::Display) => Some(&DISPLAY_BLOCK),
597        (NT::Dd, PT::Display) => Some(&DISPLAY_BLOCK),
598
599        // Inline Elements
600        (NT::Span, PT::Display) => Some(&DISPLAY_INLINE),
601        (NT::A, PT::Display) => Some(&DISPLAY_INLINE),
602        (NT::A, PT::TextDecoration) => Some(&TEXT_DECORATION_UNDERLINE),
603        (NT::Strong, PT::Display) => Some(&DISPLAY_INLINE),
604        (NT::Strong, PT::FontWeight) => Some(&FONT_WEIGHT_BOLDER),
605        (NT::Em, PT::Display) => Some(&DISPLAY_INLINE),
606        (NT::B, PT::Display) => Some(&DISPLAY_INLINE),
607        (NT::B, PT::FontWeight) => Some(&FONT_WEIGHT_BOLDER),
608        (NT::I, PT::Display) => Some(&DISPLAY_INLINE),
609        (NT::U, PT::Display) => Some(&DISPLAY_INLINE),
610        (NT::U, PT::TextDecoration) => Some(&TEXT_DECORATION_UNDERLINE),
611        (NT::Small, PT::Display) => Some(&DISPLAY_INLINE),
612        (NT::Code, PT::Display) => Some(&DISPLAY_INLINE),
613        (NT::Kbd, PT::Display) => Some(&DISPLAY_INLINE),
614        (NT::Samp, PT::Display) => Some(&DISPLAY_INLINE),
615        (NT::Sub, PT::Display) => Some(&DISPLAY_INLINE),
616        (NT::Sup, PT::Display) => Some(&DISPLAY_INLINE),
617
618        // Text Content
619        (NT::Pre, PT::Display) => Some(&DISPLAY_BLOCK),
620        (NT::BlockQuote, PT::Display) => Some(&DISPLAY_BLOCK),
621        (NT::Hr, PT::Display) => Some(&DISPLAY_BLOCK),
622        (NT::Hr, PT::Width) => Some(&WIDTH_100_PERCENT),
623        (NT::Hr, PT::Height) => Some(&HEIGHT_ZERO),
624        (NT::Hr, PT::MarginTop) => Some(&MARGIN_TOP_0_5EM),
625        (NT::Hr, PT::MarginBottom) => Some(&MARGIN_BOTTOM_0_5EM),
626        (NT::Hr, PT::BorderTopStyle) => Some(&BORDER_TOP_STYLE_INSET),
627        (NT::Hr, PT::BorderTopWidth) => Some(&BORDER_TOP_WIDTH_1PX),
628        (NT::Hr, PT::BorderTopColor) => Some(&BORDER_TOP_COLOR_GRAY),
629
630        // Table Elements
631        // Per CSS Fragmentation Level 3: table ROWS should avoid breaks inside
632        // Tables themselves should NOT have break-inside: avoid (they can span pages)
633        (NT::Table, PT::Display) => Some(&DISPLAY_TABLE),
634        // NOTE: Removed break-inside: avoid from Table - tables CAN break across pages
635        (NT::THead, PT::Display) => Some(&DISPLAY_TABLE_HEADER_GROUP),
636        (NT::THead, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
637        (NT::THead, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
638        (NT::TBody, PT::Display) => Some(&DISPLAY_TABLE_ROW_GROUP),
639        (NT::TBody, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
640        // NOTE: Removed break-inside: avoid from TBody - tbody CAN break across pages
641        (NT::TFoot, PT::Display) => Some(&DISPLAY_TABLE_FOOTER_GROUP),
642        (NT::TFoot, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
643        (NT::TFoot, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
644        (NT::Tr, PT::Display) => Some(&DISPLAY_TABLE_ROW),
645        (NT::Tr, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
646        (NT::Tr, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
647        (NT::Th, PT::Display) => Some(&DISPLAY_TABLE_CELL),
648        (NT::Th, PT::TextAlign) => Some(&TEXT_ALIGN_CENTER),
649        (NT::Th, PT::FontWeight) => Some(&FONT_WEIGHT_BOLD),
650        (NT::Th, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
651        (NT::Th, PT::PaddingTop) => Some(&PADDING_TOP_1PX),
652        (NT::Th, PT::PaddingBottom) => Some(&PADDING_BOTTOM_1PX),
653        (NT::Th, PT::PaddingLeft) => Some(&PADDING_LEFT_1PX),
654        (NT::Th, PT::PaddingRight) => Some(&PADDING_RIGHT_1PX),
655        (NT::Td, PT::Display) => Some(&DISPLAY_TABLE_CELL),
656        (NT::Td, PT::VerticalAlign) => Some(&VERTICAL_ALIGN_MIDDLE),
657        (NT::Td, PT::PaddingTop) => Some(&PADDING_TOP_1PX),
658        (NT::Td, PT::PaddingBottom) => Some(&PADDING_BOTTOM_1PX),
659        (NT::Td, PT::PaddingLeft) => Some(&PADDING_LEFT_1PX),
660        (NT::Td, PT::PaddingRight) => Some(&PADDING_RIGHT_1PX),
661
662        // Form Elements
663        (NT::Form, PT::Display) => Some(&DISPLAY_BLOCK),
664        (NT::Input, PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
665        (NT::Button, PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
666        (NT::Button, PT::Cursor) => Some(&CURSOR_POINTER),
667        // Text nodes get I-beam cursor for text selection
668        // The cursor resolution algorithm ensures that explicit cursor properties
669        // on parent elements (e.g., cursor:pointer on button) take precedence
670        (NT::Text(_), PT::Cursor) => Some(&CURSOR_TEXT),
671        (NT::Select, PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
672        (NT::TextArea, PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
673        // TextArea gets I-beam cursor since it's an editable text field
674        (NT::TextArea, PT::Cursor) => Some(&CURSOR_TEXT),
675        (NT::Label, PT::Display) => Some(&DISPLAY_INLINE),
676        // Hidden Elements
677        (NT::Head, PT::Display) => Some(&DISPLAY_NONE),
678        (NT::Title, PT::Display) => Some(&DISPLAY_NONE),
679        (NT::Script, PT::Display) => Some(&DISPLAY_NONE),
680        (NT::Style, PT::Display) => Some(&DISPLAY_NONE),
681        (NT::Link, PT::Display) => Some(&DISPLAY_NONE),
682
683        // Special Elements
684        (NT::Br, PT::Display) => Some(&DISPLAY_BLOCK),
685        // Images are replaced elements - inline-block so they respect width/height
686        (NT::Image(_), PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
687
688        // Media Elements
689        (NT::Video, PT::Display) => Some(&DISPLAY_INLINE),
690        (NT::Audio, PT::Display) => Some(&DISPLAY_INLINE),
691        (NT::Canvas, PT::Display) => Some(&DISPLAY_INLINE),
692        (NT::Svg, PT::Display) => Some(&DISPLAY_INLINE),
693        (NT::IFrame(_), PT::Display) => Some(&DISPLAY_INLINE),
694
695        // Icon Elements - inline-block so they have width/height but flow inline
696        (NT::Icon(_), PT::Display) => Some(&DISPLAY_INLINE_BLOCK),
697
698        // Form Input Elements (inline-block behavior approximated as inline)
699        (NT::Input, PT::Display) => Some(&DISPLAY_INLINE),
700        (NT::Button, PT::Display) => Some(&DISPLAY_INLINE),
701        (NT::Select, PT::Display) => Some(&DISPLAY_INLINE),
702        (NT::TextArea, PT::Display) => Some(&DISPLAY_INLINE),
703        (NT::SelectOption, PT::Display) => Some(&DISPLAY_NONE),
704        (NT::OptGroup, PT::Display) => Some(&DISPLAY_NONE),
705
706        // Other Inline Elements
707        (NT::Abbr, PT::Display) => Some(&DISPLAY_INLINE),
708        (NT::Cite, PT::Display) => Some(&DISPLAY_INLINE),
709        (NT::Del, PT::Display) => Some(&DISPLAY_INLINE),
710        (NT::Ins, PT::Display) => Some(&DISPLAY_INLINE),
711        (NT::Mark, PT::Display) => Some(&DISPLAY_INLINE),
712        (NT::Q, PT::Display) => Some(&DISPLAY_INLINE),
713        (NT::Dfn, PT::Display) => Some(&DISPLAY_INLINE),
714        (NT::Var, PT::Display) => Some(&DISPLAY_INLINE),
715        (NT::Time, PT::Display) => Some(&DISPLAY_INLINE),
716        (NT::Data, PT::Display) => Some(&DISPLAY_INLINE),
717        (NT::Wbr, PT::Display) => Some(&DISPLAY_INLINE),
718        (NT::Bdi, PT::Display) => Some(&DISPLAY_INLINE),
719        (NT::Bdo, PT::Display) => Some(&DISPLAY_INLINE),
720        (NT::Rp, PT::Display) => Some(&DISPLAY_INLINE),
721        (NT::Rt, PT::Display) => Some(&DISPLAY_INLINE),
722        (NT::Rtc, PT::Display) => Some(&DISPLAY_INLINE),
723        (NT::Ruby, PT::Display) => Some(&DISPLAY_INLINE),
724
725        // Block Container Elements
726        // Per CSS Fragmentation Level 3: figures should avoid page breaks inside
727        (NT::FieldSet, PT::Display) => Some(&DISPLAY_BLOCK),
728        (NT::Figure, PT::Display) => Some(&DISPLAY_BLOCK),
729        (NT::Figure, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
730        (NT::FigCaption, PT::Display) => Some(&DISPLAY_BLOCK),
731        (NT::FigCaption, PT::BreakInside) => Some(&BREAK_INSIDE_AVOID),
732        (NT::Details, PT::Display) => Some(&DISPLAY_BLOCK),
733        (NT::Summary, PT::Display) => Some(&DISPLAY_BLOCK),
734        (NT::Dialog, PT::Display) => Some(&DISPLAY_BLOCK),
735
736        // Table Caption
737        (NT::Caption, PT::Display) => Some(&DISPLAY_TABLE_CAPTION),
738        (NT::ColGroup, PT::Display) => Some(&DISPLAY_TABLE_COLUMN_GROUP),
739        (NT::Col, PT::Display) => Some(&DISPLAY_TABLE_COLUMN),
740
741        // Legacy/Deprecated Elements
742        (NT::Menu, PT::Display) => Some(&DISPLAY_BLOCK),
743        (NT::Dir, PT::Display) => Some(&DISPLAY_BLOCK),
744
745        // Html (root) Element
746        //
747        // In browsers, the viewport itself provides scrolling when <html> overflows.
748        // Since Azul has no separate viewport scroll mechanism, we set `height: 100%`
749        // on the <html> element so it fills the Initial Containing Block (the viewport).
750        // This constrains child elements like <body> to the viewport height, enabling
751        // overflow:scroll on <body> to create scrollable content areas.
752        //
753        // Without this, <html> has height:auto and grows to fit all content,
754        // making container_size == content_size, which results in a useless 100% scrollbar.
755        (NT::Html, PT::Display) => Some(&DISPLAY_BLOCK),
756        (NT::Html, PT::Height) => Some(&HEIGHT_100_PERCENT),
757
758        // Universal fallback for display property
759        // Per CSS spec, unknown/custom elements should default to inline
760        // Text nodes will be filtered out before this function is called
761        (_, PT::Display) => Some(&DISPLAY_INLINE),
762
763        // No default defined for other combinations
764        _ => None,
765    };
766
767    result
768}