Merge pull request #1767 from egrs/css-value-normalization-gaps
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / zig build release (push) Has been cancelled
wpt / build wpt runner (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
e2e-integration-test / zig build release (push) Has been cancelled
e2e-integration-test / demo-integration-scripts (push) Has been cancelled

extend CSS value normalization to cover more properties
This commit is contained in:
Karl Seguin
2026-03-11 06:28:34 +08:00
committed by GitHub
2 changed files with 135 additions and 1 deletions

View File

@@ -293,6 +293,28 @@
div.style.top = '0';
testing.expectEqual('0px', div.style.top);
// Scroll properties
div.style.scrollMarginTop = '0';
testing.expectEqual('0px', div.style.scrollMarginTop);
div.style.scrollPaddingBottom = '0';
testing.expectEqual('0px', div.style.scrollPaddingBottom);
// Multi-column
div.style.columnWidth = '0';
testing.expectEqual('0px', div.style.columnWidth);
div.style.columnRuleWidth = '0';
testing.expectEqual('0px', div.style.columnRuleWidth);
// Outline shorthand
div.style.outline = '0';
testing.expectEqual('0px', div.style.outline);
// Shapes
div.style.shapeMargin = '0';
testing.expectEqual('0px', div.style.shapeMargin);
// Non-length properties should not be affected
div.style.opacity = '0';
testing.expectEqual('0', div.style.opacity);
@@ -313,6 +335,12 @@
div.style.alignContent = 'first baseline';
testing.expectEqual('baseline', div.style.alignContent);
div.style.alignSelf = 'first baseline';
testing.expectEqual('baseline', div.style.alignSelf);
div.style.justifySelf = 'first baseline';
testing.expectEqual('baseline', div.style.justifySelf);
// "last baseline" should remain unchanged
div.style.alignItems = 'last baseline';
testing.expectEqual('last baseline', div.style.alignItems);
@@ -339,6 +367,16 @@
div.style.gap = '10px 20px';
testing.expectEqual('10px 20px', div.style.gap);
// New shorthands
div.style.overflow = 'hidden hidden';
testing.expectEqual('hidden', div.style.overflow);
div.style.scrollSnapAlign = 'start start';
testing.expectEqual('start', div.style.scrollSnapAlign);
div.style.overscrollBehavior = 'auto auto';
testing.expectEqual('auto', div.style.overscrollBehavior);
}
</script>

View File

@@ -483,6 +483,16 @@ fn isTwoValueShorthand(name: []const u8) bool {
.{ "overflow", {} },
.{ "overscroll-behavior", {} },
.{ "gap", {} },
.{ "grid-gap", {} },
// Scroll
.{ "scroll-padding-block", {} },
.{ "scroll-padding-inline", {} },
.{ "scroll-snap-align", {} },
// Background/Mask
.{ "background-size", {} },
.{ "border-image-repeat", {} },
.{ "mask-repeat", {} },
.{ "mask-size", {} },
});
return shorthands.has(name);
}
@@ -552,7 +562,6 @@ fn isLengthProperty(name: []const u8) bool {
.{ "border-bottom-right-radius", {} },
// Text
.{ "font-size", {} },
.{ "line-height", {} },
.{ "letter-spacing", {} },
.{ "word-spacing", {} },
.{ "text-indent", {} },
@@ -561,17 +570,52 @@ fn isLengthProperty(name: []const u8) bool {
.{ "row-gap", {} },
.{ "column-gap", {} },
.{ "flex-basis", {} },
// Legacy grid aliases
.{ "grid-column-gap", {} },
.{ "grid-row-gap", {} },
// Outline
.{ "outline", {} },
.{ "outline-width", {} },
.{ "outline-offset", {} },
// Multi-column
.{ "column-rule-width", {} },
.{ "column-width", {} },
// Scroll
.{ "scroll-margin", {} },
.{ "scroll-margin-top", {} },
.{ "scroll-margin-right", {} },
.{ "scroll-margin-bottom", {} },
.{ "scroll-margin-left", {} },
.{ "scroll-padding", {} },
.{ "scroll-padding-top", {} },
.{ "scroll-padding-right", {} },
.{ "scroll-padding-bottom", {} },
.{ "scroll-padding-left", {} },
// Shapes
.{ "shape-margin", {} },
// Motion path
.{ "offset-distance", {} },
// Transforms
.{ "translate", {} },
// Animations
.{ "animation-range-end", {} },
.{ "animation-range-start", {} },
// Other
.{ "border-spacing", {} },
.{ "text-shadow", {} },
.{ "box-shadow", {} },
.{ "baseline-shift", {} },
.{ "vertical-align", {} },
.{ "text-decoration-inset", {} },
.{ "block-step-size", {} },
// Grid lanes
.{ "flow-tolerance", {} },
.{ "column-rule-edge-inset", {} },
.{ "column-rule-interior-inset", {} },
.{ "row-rule-edge-inset", {} },
.{ "row-rule-interior-inset", {} },
.{ "rule-edge-inset", {} },
.{ "rule-interior-inset", {} },
});
return length_properties.has(name);
@@ -693,3 +737,55 @@ pub const JsApi = struct {
pub const removeProperty = bridge.function(CSSStyleDeclaration.removeProperty, .{});
pub const cssFloat = bridge.accessor(CSSStyleDeclaration.getFloat, CSSStyleDeclaration.setFloat, .{});
};
const testing = @import("std").testing;
test "normalizePropertyValue: unitless zero to 0px" {
const cases = .{
.{ "width", "0", "0px" },
.{ "height", "0", "0px" },
.{ "scroll-margin-top", "0", "0px" },
.{ "scroll-padding-bottom", "0", "0px" },
.{ "column-width", "0", "0px" },
.{ "column-rule-width", "0", "0px" },
.{ "outline", "0", "0px" },
.{ "shape-margin", "0", "0px" },
.{ "offset-distance", "0", "0px" },
.{ "translate", "0", "0px" },
.{ "grid-column-gap", "0", "0px" },
.{ "grid-row-gap", "0", "0px" },
// Non-length properties should NOT normalize
.{ "opacity", "0", "0" },
.{ "z-index", "0", "0" },
};
inline for (cases) |case| {
const result = try normalizePropertyValue(testing.allocator, case[0], case[1]);
try testing.expectEqualStrings(case[2], result);
}
}
test "normalizePropertyValue: first baseline to baseline" {
const result = try normalizePropertyValue(testing.allocator, "align-items", "first baseline");
try testing.expectEqualStrings("baseline", result);
const result2 = try normalizePropertyValue(testing.allocator, "align-self", "last baseline");
try testing.expectEqualStrings("last baseline", result2);
}
test "normalizePropertyValue: collapse duplicate two-value shorthands" {
const cases = .{
.{ "overflow", "hidden hidden", "hidden" },
.{ "gap", "10px 10px", "10px" },
.{ "scroll-snap-align", "start start", "start" },
.{ "scroll-padding-block", "5px 5px", "5px" },
.{ "background-size", "auto auto", "auto" },
.{ "overscroll-behavior", "auto auto", "auto" },
// Different values should NOT collapse
.{ "overflow", "hidden scroll", "hidden scroll" },
.{ "gap", "10px 20px", "10px 20px" },
};
inline for (cases) |case| {
const result = try normalizePropertyValue(testing.allocator, case[0], case[1]);
try testing.expectEqualStrings(case[2], result);
}
}