render: table vertical-align and specified column/row sizing

Cells now vertical-align the way tables actually do: middle by default
(the effective UA behavior), with author top/middle/bottom parsed and
applied to whichever shape the cell took: main-axis alignment on the
flex-column stand-in, line alignment on a promoted inline context, or a
pinned-origin offset for the pure-text leaf path where the shaped text
height is known at finalize.

Specified column widths finally apply. <col>/<colgroup> widths (style
or attribute, with span) and colspan-1 cell widths are recorded per
column at build time and pinned inside the column-balancing pass: px
directly, percent against the table's content width, with each column's
content min-content still flooring it. The cell's own width is
neutralized once it has fed the column, so the cell box fills its grid
area instead of shrinking to a fraction of its own already-sized track
(a width:50% cell previously ended up at 25 percent). Unspecified
columns keep content sizing but stretch to fill a definite table width
instead of leaving a dead strip of table background. Row and cell
heights act as row minimums.

Verified against Chromium on a 5-case repro (column from cell width,
row backgrounds, vertical alignment, col element width, percent cell in
a fixed table): all match. Wikipedia and the prose spot set unchanged;
obstacle course 33/33.
This commit is contained in:
SGavrl committed 2026-07-16 12:47:47 +00:00
1 parent 1d96c316ca
commit 953292563d
3 files changed
+221 -20

No files matched your search

+13
View File
@@ -130,6 +130,10 @@ pub fn ua_style(tag: &str) -> LayoutStyle {
style.align_items = Some(taffy::AlignItems::FLEX_START);
style.padding = Edges { top: 0.0, right: 5.0, bottom: 0.0, left: 0.0 };
style.min_width = crate::Dimension::Px(0.0);
// Cells are effectively middle-aligned by default in browsers (the
// HTML UA sheet's row-group vertical-align inherited into cells);
// an author `vertical-align` on the cell overrides this.
style.vertical_align = Some(crate::VerticalAlign::Middle);
if tag == "th" {
style.font_weight = Some("bold".to_string());
}
@@ -438,6 +442,15 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) {
}
"visibility" => style.visibility_hidden = Some(value.eq_ignore_ascii_case("hidden")),
"opacity" => style.opacity = value.trim().parse::<f32>().ok(),
"vertical-align" => {
style.vertical_align = match value.trim().to_ascii_lowercase().as_str() {
"top" | "baseline" | "text-top" => Some(crate::VerticalAlign::Top),
"middle" => Some(crate::VerticalAlign::Middle),
"bottom" | "text-bottom" => Some(crate::VerticalAlign::Bottom),
// sub/super/lengths are text-level; leave the cell default.
_ => style.vertical_align,
};
}
"list-style-type" => {
style.list_style = Some(list_style_keyword(value.trim()).unwrap_or(crate::ListStyle::Disc));
}