Skip to content

Commit 7c85735

Browse files
committed
feat(ie-css): add user-agent stylesheet with WHATWG defaults #70
User-agent stylesheet (data/ua.css) based on WHATWG rendering spec: - Display: block for structural elements, none for head/meta/style/ script, inline for span/a/em/strong/code etc. - Body: 8px margin - Headings h1-h6: font sizes (32px-11px), font-weight 700, margins - Paragraphs: 16px vertical margins - Lists: 40px padding-left, 16px vertical margins - Blockquote/figure: 40px horizontal margins - Links: blue color, underline - Bold/italic: font-weight 700, font-style italic - Code: monospace font-family - Table cells: 1px padding - Fieldset: 2px border, padding - HR: 1px solid gray border ua_stylesheet() loads via include_str! and parses once. 5 tests: div/span/head display, h1>p font-size, parses without panic. 72 total ie-css tests.
1 parent e551fc5 commit 7c85735

2 files changed

Lines changed: 281 additions & 0 deletions

File tree

crates/ie-css/data/ua.css

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/* User-Agent Stylesheet — based on WHATWG rendering spec */
2+
3+
/* Block-level elements */
4+
html, body, div, section, article, aside, nav, main,
5+
header, footer, hgroup, address,
6+
h1, h2, h3, h4, h5, h6,
7+
p, pre, blockquote, figure, figcaption,
8+
ul, ol, li, dl, dt, dd,
9+
form, fieldset, legend, details, summary, dialog,
10+
table, caption, thead, tbody, tfoot, tr,
11+
hr, menu, dir, center, listing, search {
12+
display: block;
13+
}
14+
15+
/* Hidden elements */
16+
head, link, meta, style, script, title, base,
17+
noscript, template, datalist {
18+
display: none;
19+
}
20+
21+
/* Inline elements (default, but explicit) */
22+
span, a, em, strong, b, i, u, s, small, big,
23+
code, kbd, samp, var, tt,
24+
abbr, cite, dfn, mark, q, sub, sup,
25+
br, wbr, img, input, select, textarea, button,
26+
label, output, progress, meter {
27+
display: inline;
28+
}
29+
30+
/* Body defaults */
31+
body {
32+
margin-top: 8px;
33+
margin-right: 8px;
34+
margin-bottom: 8px;
35+
margin-left: 8px;
36+
}
37+
38+
/* Headings */
39+
h1 {
40+
font-size: 32px;
41+
font-weight: 700;
42+
margin-top: 21px;
43+
margin-bottom: 21px;
44+
}
45+
46+
h2 {
47+
font-size: 24px;
48+
font-weight: 700;
49+
margin-top: 19px;
50+
margin-bottom: 19px;
51+
}
52+
53+
h3 {
54+
font-size: 19px;
55+
font-weight: 700;
56+
margin-top: 18px;
57+
margin-bottom: 18px;
58+
}
59+
60+
h4 {
61+
font-size: 16px;
62+
font-weight: 700;
63+
margin-top: 21px;
64+
margin-bottom: 21px;
65+
}
66+
67+
h5 {
68+
font-size: 13px;
69+
font-weight: 700;
70+
margin-top: 22px;
71+
margin-bottom: 22px;
72+
}
73+
74+
h6 {
75+
font-size: 11px;
76+
font-weight: 700;
77+
margin-top: 24px;
78+
margin-bottom: 24px;
79+
}
80+
81+
/* Paragraphs */
82+
p {
83+
margin-top: 16px;
84+
margin-bottom: 16px;
85+
}
86+
87+
/* Preformatted */
88+
pre {
89+
font-family: monospace;
90+
white-space: pre;
91+
margin-top: 16px;
92+
margin-bottom: 16px;
93+
}
94+
95+
/* Blockquote */
96+
blockquote {
97+
margin-top: 16px;
98+
margin-bottom: 16px;
99+
margin-left: 40px;
100+
margin-right: 40px;
101+
}
102+
103+
/* Lists */
104+
ul, ol {
105+
margin-top: 16px;
106+
margin-bottom: 16px;
107+
padding-left: 40px;
108+
}
109+
110+
li {
111+
display: list-item;
112+
}
113+
114+
dl {
115+
margin-top: 16px;
116+
margin-bottom: 16px;
117+
}
118+
119+
dd {
120+
margin-left: 40px;
121+
}
122+
123+
/* Horizontal rule */
124+
hr {
125+
border-top-width: 1px;
126+
border-top-style: solid;
127+
border-top-color: gray;
128+
margin-top: 8px;
129+
margin-bottom: 8px;
130+
}
131+
132+
/* Links */
133+
a {
134+
color: blue;
135+
text-decoration: underline;
136+
}
137+
138+
/* Bold / italic */
139+
b, strong {
140+
font-weight: 700;
141+
}
142+
143+
i, em {
144+
font-style: italic;
145+
}
146+
147+
/* Code */
148+
code, kbd, samp, tt {
149+
font-family: monospace;
150+
}
151+
152+
/* Small */
153+
small {
154+
font-size: 13px;
155+
}
156+
157+
/* Table defaults */
158+
table {
159+
border-top-width: 0px;
160+
border-right-width: 0px;
161+
border-bottom-width: 0px;
162+
border-left-width: 0px;
163+
}
164+
165+
td, th {
166+
display: table-cell;
167+
padding-top: 1px;
168+
padding-right: 1px;
169+
padding-bottom: 1px;
170+
padding-left: 1px;
171+
}
172+
173+
th {
174+
font-weight: 700;
175+
text-align: center;
176+
}
177+
178+
/* Fieldset */
179+
fieldset {
180+
margin-left: 2px;
181+
margin-right: 2px;
182+
padding-top: 6px;
183+
padding-right: 12px;
184+
padding-bottom: 12px;
185+
padding-left: 12px;
186+
border-top-width: 2px;
187+
border-right-width: 2px;
188+
border-bottom-width: 2px;
189+
border-left-width: 2px;
190+
border-top-style: solid;
191+
border-right-style: solid;
192+
border-bottom-style: solid;
193+
border-left-style: solid;
194+
border-top-color: gray;
195+
border-right-color: gray;
196+
border-bottom-color: gray;
197+
border-left-color: gray;
198+
}
199+
200+
/* Figure */
201+
figure {
202+
margin-top: 16px;
203+
margin-bottom: 16px;
204+
margin-left: 40px;
205+
margin-right: 40px;
206+
}

crates/ie-css/src/lib.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,78 @@ pub use selector::{
1919
pub use style::ComputedStyle;
2020
pub use tokenizer::{CssToken, CssTokenizer};
2121
pub use values::{CssColor, CssValue, LengthUnit, PropertyId};
22+
23+
/// The built-in user-agent stylesheet (WHATWG default rendering).
24+
pub fn ua_stylesheet() -> Stylesheet {
25+
parse_stylesheet(include_str!("../data/ua.css"))
26+
}
27+
28+
#[cfg(test)]
29+
mod tests {
30+
use super::*;
31+
use cascade::Origin;
32+
33+
fn ua_value_for(tag: &str, prop: PropertyId) -> Option<CssValue> {
34+
let ua = ua_stylesheet();
35+
let mut doc = ie_dom::Document::new();
36+
let html = doc.create_element("html");
37+
let _ = doc.append_child(doc.root, html);
38+
let body = doc.create_element("body");
39+
let _ = doc.append_child(html, body);
40+
let el = doc.create_element(tag);
41+
let _ = doc.append_child(body, el);
42+
let styles = cascade(&[(ua, Origin::UserAgent)], el, &doc);
43+
styles.get(&prop).cloned()
44+
}
45+
46+
#[test]
47+
fn ua_div_display_block() {
48+
let val = ua_value_for("div", PropertyId::Display);
49+
assert_eq!(val, Some(CssValue::Keyword("block".into())));
50+
}
51+
52+
#[test]
53+
fn ua_span_display_inline() {
54+
let val = ua_value_for("span", PropertyId::Display);
55+
assert_eq!(val, Some(CssValue::Keyword("inline".into())));
56+
}
57+
58+
#[test]
59+
fn ua_head_display_none() {
60+
// head is child of html, not body
61+
let ua = ua_stylesheet();
62+
let mut doc = ie_dom::Document::new();
63+
let html = doc.create_element("html");
64+
let _ = doc.append_child(doc.root, html);
65+
let head = doc.create_element("head");
66+
let _ = doc.append_child(html, head);
67+
let styles = cascade(&[(ua, Origin::UserAgent)], head, &doc);
68+
assert_eq!(styles.get(&PropertyId::Display), Some(&CssValue::None));
69+
}
70+
71+
#[test]
72+
fn ua_h1_larger_than_p() {
73+
let h1_size = ua_value_for("h1", PropertyId::FontSize);
74+
let p_size = ua_value_for("p", PropertyId::FontSize);
75+
// h1 should have an explicit font-size, p should not (or smaller)
76+
match h1_size {
77+
Some(CssValue::Length(h1_px, _)) => {
78+
let p_px = match p_size {
79+
Some(CssValue::Length(px, _)) => px,
80+
_ => 16.0, // default
81+
};
82+
assert!(
83+
h1_px > p_px,
84+
"h1 ({h1_px}px) should be larger than p ({p_px}px)"
85+
);
86+
}
87+
other => panic!("expected h1 font-size as Length, got {other:?}"),
88+
}
89+
}
90+
91+
#[test]
92+
fn ua_stylesheet_parses_without_panic() {
93+
let ua = ua_stylesheet();
94+
assert!(!ua.rules.is_empty(), "UA stylesheet should have rules");
95+
}
96+
}

0 commit comments

Comments
 (0)