render: percentage padding, box-shadow, external-sprite svg, gradient text

percentage padding/margin: top/bottom percentages resolve against the
containing-block width (the responsive aspect-ratio-box trick, e.g.
padding-top:56.25%), resolved to px in the top-down layout pass so card
media boxes reserve space instead of collapsing.
box-shadow: parse and paint an outset shadow (rounded, offset, spread,
soft-edge approximation) so cards/buttons/menus have depth.
external-sprite svg: resolve <use href="url#id"> to an external sprite
file, inject the referenced symbol so inline-svg icon systems rasterize.
background-clip:text: paint transparent-with-gradient text (the common
gradient hero/button label) instead of leaving it invisible.
This commit is contained in:
SGavrl committed 2026-07-11 21:02:57 +00:00
1 parent 58dee6330e
commit 23594b1f96
6 files changed
+954 -24

No files matched your search

+59 -1
View File
@@ -315,6 +315,12 @@ pub fn layout_dom_with_images(
line_height: crate::LineHeight,
text_transform: crate::TextTransform,
italic: bool,
/// Containing-block width in px for the current element, carried
/// down so percentage padding/margin (which resolve against the
/// containing block WIDTH, all sides) can be turned into px before
/// taffy layout. Not a CSS-inherited property; it is recomputed to
/// the element's own content width for its children.
cb_width: f32,
}
impl Default for Inherited {
fn default() -> Self {
@@ -329,6 +335,7 @@ pub fn layout_dom_with_images(
line_height: crate::LineHeight::Normal,
text_transform: crate::TextTransform::None,
italic: false,
cb_width: 0.0,
}
}
}
@@ -351,8 +358,15 @@ pub fn layout_dom_with_images(
}
};
let mut queue = vec![(root_id, Inherited::default())];
// The root element's containing block is the initial containing block,
// i.e. the viewport width.
let mut root_inh = Inherited::default();
root_inh.cb_width = viewport.0;
let mut queue = vec![(root_id, root_inh)];
while let Some((id, mut inh)) = queue.pop() {
// Default the child containing-block width to this element's own
// (updated to its content width inside the block below).
let mut child_cb_width = inh.cb_width;
if let Some(style) = styles.get_mut(&id) {
match style.color { Some(c) => inh.color = Some(c), None => style.color = inh.color }
// Resolve a relative font-size against the PARENT (em/%) or
@@ -393,7 +407,51 @@ pub fn layout_dom_with_images(
match style.line_height { Some(v) => inh.line_height = v, None => style.line_height = Some(inh.line_height) }
match style.text_transform { Some(v) => inh.text_transform = v, None => style.text_transform = Some(inh.text_transform) }
match style.font_style_italic { Some(v) => inh.italic = v, None => style.font_style_italic = Some(inh.italic) }
// Percentage padding/margin resolve against the containing
// block WIDTH on every side (per CSS: `padding-top:56.25%` in a
// 1000px block is 562.5px, not a fraction of the height). The
// f32 Edges cannot carry a percentage, so bake the resolved px
// back into `padding`/`margin`, which then feed taffy.
let cb_w = inh.cb_width;
for i in 0..4 {
if let Some(frac) = style.padding_percent[i] {
let px = (frac * cb_w).max(0.0);
match i {
0 => style.padding.top = px,
1 => style.padding.right = px,
2 => style.padding.bottom = px,
_ => style.padding.left = px,
}
}
if let Some(frac) = style.margin_percent[i] {
let px = frac * cb_w;
match i {
0 => style.margin.top = px,
1 => style.margin.right = px,
2 => style.margin.bottom = px,
_ => style.margin.left = px,
}
}
}
// Containing-block width handed to this element's children is
// its own content-box width. taffy sizes width as border-box
// (its default box_sizing), so subtract this element's resolved
// padding and border; an auto width fills the containing block.
let used_w = match style.width {
crate::Dimension::Px(w) => w,
crate::Dimension::Percent(p) => p * cb_w,
_ => (cb_w - style.margin.left - style.margin.right).max(0.0),
};
child_cb_width = (used_w
- style.padding.left
- style.padding.right
- style.border.left
- style.border.right)
.max(0.0);
}
inh.cb_width = child_cb_width;
for cid in tree.children(id).into_iter().rev() {
queue.push((cid, inh.clone()));
}