From d7dd2f77708e4c20f8dcb6cd93fb766fdcd0b352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A1ndor=20Bereczki?= Date: Mon, 23 Feb 2026 21:39:55 +0100 Subject: [PATCH 1/7] Fix parameter order in Margin and Padding initializers for consistency --- Sources/CSS/Properties/Margin.swift | 12 +++++----- Sources/CSS/Properties/Padding.swift | 12 +++++----- .../CSSTests/Properties/MarginTestSuite.swift | 2 +- .../Properties/PaddingTestSuite.swift | 2 +- Tests/CSSTests/SelectorTests.swift | 4 ++-- Tests/CSSTests/StylesheetRendererTests.swift | 2 +- Tests/CSSTests/SwiftCSSTests.swift | 22 +++++++++---------- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Sources/CSS/Properties/Margin.swift b/Sources/CSS/Properties/Margin.swift index 1e7f17c..e8793d4 100644 --- a/Sources/CSS/Properties/Margin.swift +++ b/Sources/CSS/Properties/Margin.swift @@ -59,11 +59,11 @@ public struct Margin: Property { /// - horizontal: The horizontal value. /// - vertical: The vertical value. public init( - horizontal: Value, - vertical: Value + vertical: Value, + horizontal: Value ) { self.name = "margin" - self.value = horizontal.rawValue + " " + vertical.rawValue + self.value = vertical.rawValue + " " + horizontal.rawValue self.isImportant = false } @@ -73,10 +73,10 @@ public struct Margin: Property { /// - horizontal: The horizontal value. /// - vertical: The vertical value. public init( - horizontal: UnitRepresentable = 0, - vertical: UnitRepresentable = 0 + vertical: UnitRepresentable = 0, + horizontal: UnitRepresentable = 0 ) { - self.init(horizontal: .length(horizontal), vertical: .length(vertical)) + self.init(vertical: .length(vertical), horizontal: .length(horizontal)) } /// Creates a `margin` declaration. diff --git a/Sources/CSS/Properties/Padding.swift b/Sources/CSS/Properties/Padding.swift index 3309a9b..384b29f 100644 --- a/Sources/CSS/Properties/Padding.swift +++ b/Sources/CSS/Properties/Padding.swift @@ -55,11 +55,11 @@ public struct Padding: Property { /// - horizontal: The horizontal value. /// - vertical: The vertical value. public init( - horizontal: Value, - vertical: Value + vertical: Value, + horizontal: Value ) { self.name = "padding" - self.value = horizontal.rawValue + " " + vertical.rawValue + self.value = vertical.rawValue + " " + horizontal.rawValue self.isImportant = false } @@ -69,10 +69,10 @@ public struct Padding: Property { /// - horizontal: The horizontal value. /// - vertical: The vertical value. public init( - horizontal: UnitRepresentable = 0, - vertical: UnitRepresentable = 0 + vertical: UnitRepresentable = 0, + horizontal: UnitRepresentable = 0 ) { - self.init(horizontal: .length(horizontal), vertical: .length(vertical)) + self.init(vertical: .length(vertical), horizontal: .length(horizontal)) } /// Creates a `padding` declaration. diff --git a/Tests/CSSTests/Properties/MarginTestSuite.swift b/Tests/CSSTests/Properties/MarginTestSuite.swift index 2bd3ed1..3ae7489 100644 --- a/Tests/CSSTests/Properties/MarginTestSuite.swift +++ b/Tests/CSSTests/Properties/MarginTestSuite.swift @@ -39,7 +39,7 @@ struct MarginTests { @Test func values() { let single = Margin(12.px) - let axis = Margin(horizontal: 8.px, vertical: 16.px) + let axis = Margin(vertical: 8.px, horizontal: 16.px) let sides = Margin(top: 1.px, right: 2.px, bottom: 3.px, left: 4.px) let auto = Margin(.auto) let initial = Margin(.initial) diff --git a/Tests/CSSTests/Properties/PaddingTestSuite.swift b/Tests/CSSTests/Properties/PaddingTestSuite.swift index 2992ab0..19b8988 100644 --- a/Tests/CSSTests/Properties/PaddingTestSuite.swift +++ b/Tests/CSSTests/Properties/PaddingTestSuite.swift @@ -39,7 +39,7 @@ struct PaddingTests { @Test func values() { let single = Padding(12.px) - let axis = Padding(horizontal: 8.px, vertical: 16.px) + let axis = Padding(vertical: 8.px, horizontal: 16.px) let sides = Padding(top: 1.px, right: 2.px, bottom: 3.px, left: 4.px) let inherit = Padding(.inherit) diff --git a/Tests/CSSTests/SelectorTests.swift b/Tests/CSSTests/SelectorTests.swift index 5115928..e76cad4 100644 --- a/Tests/CSSTests/SelectorTests.swift +++ b/Tests/CSSTests/SelectorTests.swift @@ -55,8 +55,8 @@ struct SelectorTests { AllElements { Padding(0) Padding(8.rem) - Padding(horizontal: 8.px) - Padding(horizontal: .length(0), vertical: .inherit) + Padding(vertical: 8.px) + Padding(vertical: .length(0), horizontal: .inherit) } } } diff --git a/Tests/CSSTests/StylesheetRendererTests.swift b/Tests/CSSTests/StylesheetRendererTests.swift index 467a69b..910a22d 100644 --- a/Tests/CSSTests/StylesheetRendererTests.swift +++ b/Tests/CSSTests/StylesheetRendererTests.swift @@ -16,7 +16,7 @@ struct StylesheetRendererTests { let css = Stylesheet { Media { Class("badge") { - Padding(horizontal: 6.px, vertical: 2.px) + Padding(vertical: 6.px, horizontal: 2.px) BackgroundColor(.red) } } diff --git a/Tests/CSSTests/SwiftCSSTests.swift b/Tests/CSSTests/SwiftCSSTests.swift index 131f429..bb7f6f2 100644 --- a/Tests/CSSTests/SwiftCSSTests.swift +++ b/Tests/CSSTests/SwiftCSSTests.swift @@ -17,14 +17,14 @@ struct SwiftCssTests { Charset("UTF-8") Media { Root { - Margin(horizontal: 8.5.px, vertical: 8.px) - Padding(horizontal: 8.px, vertical: 8.px) + Margin(vertical: 8.px, horizontal: 8.5.px) + Padding(vertical: 8.5.px, horizontal: 8.px) } } } #expect( StylesheetRenderer(minify: true, indent: 2).render(css) - == #"@charset "UTF-8";:root{margin:8.5px 8px;padding:8px 8px}"# + == #"@charset "UTF-8";:root{margin:8.5px 8px;padding:8.5px 8px}"# ) } @@ -34,8 +34,8 @@ struct SwiftCssTests { Charset("UTF-8") Media { Root { - Margin(horizontal: 8.5.px, vertical: 8.px) - Padding(horizontal: 8.px, vertical: 8.px) + Margin(vertical: 8.5.px, horizontal: 8.px) + Padding(vertical: 8.5.px, horizontal: 8.px) } } } @@ -45,7 +45,7 @@ struct SwiftCssTests { @charset "UTF-8"; :root { margin: 8.5px 8px; - padding: 8px 8px; + padding: 8.5px 8px; } """# ) @@ -58,8 +58,8 @@ struct SwiftCssTests { Media { Root { - Margin(horizontal: 8.5.px, vertical: 8.px) - Padding(horizontal: 8.px, vertical: 8.px) + Margin(vertical: 8.5.px, horizontal: 8.px) + Padding(vertical: 8.5.px, horizontal: 8.px) } } @@ -70,7 +70,7 @@ struct SwiftCssTests { } Media(.screen && .prefersColorScheme(.dark)) { Universal { - Margin(horizontal: 8.px, vertical: 8.px) + Margin(vertical: 8.5.px, horizontal: 8.px) } } @@ -86,7 +86,7 @@ struct SwiftCssTests { @charset "UTF-8"; :root { margin: 8.5px 8px; - padding: 8px 8px; + padding: 8.5px 8px; } @media screen and (min-width: 600px) { .button { @@ -95,7 +95,7 @@ struct SwiftCssTests { } @media screen and (prefers-color-scheme: dark) { * { - margin: 8px 8px; + margin: 8.5px 8px; } } @media screen and (display-mode: standalone) { From 843ab58ced3b5424b88a0eab517f923b3108840f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A1ndor=20Bereczki?= Date: Mon, 23 Feb 2026 21:46:58 +0100 Subject: [PATCH 2/7] Fix parameter order in DocC comments in Margin and Padding initializers for clarity --- Sources/CSS/Properties/Margin.swift | 4 ++-- Sources/CSS/Properties/Padding.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/CSS/Properties/Margin.swift b/Sources/CSS/Properties/Margin.swift index e8793d4..e14a7b0 100644 --- a/Sources/CSS/Properties/Margin.swift +++ b/Sources/CSS/Properties/Margin.swift @@ -56,8 +56,8 @@ public struct Margin: Property { /// Creates a `margin` declaration. /// Used by `StylesheetRenderer` when emitting CSS. /// - Parameters: - /// - horizontal: The horizontal value. /// - vertical: The vertical value. + /// - horizontal: The horizontal value. public init( vertical: Value, horizontal: Value @@ -70,8 +70,8 @@ public struct Margin: Property { /// Creates a `margin` declaration. /// Used by `StylesheetRenderer` when emitting CSS. /// - Parameters: - /// - horizontal: The horizontal value. /// - vertical: The vertical value. + /// - horizontal: The horizontal value. public init( vertical: UnitRepresentable = 0, horizontal: UnitRepresentable = 0 diff --git a/Sources/CSS/Properties/Padding.swift b/Sources/CSS/Properties/Padding.swift index 384b29f..faf6a24 100644 --- a/Sources/CSS/Properties/Padding.swift +++ b/Sources/CSS/Properties/Padding.swift @@ -52,8 +52,8 @@ public struct Padding: Property { /// Creates a `padding` declaration. /// Used by `StylesheetRenderer` when emitting CSS. /// - Parameters: - /// - horizontal: The horizontal value. /// - vertical: The vertical value. + /// - horizontal: The horizontal value. public init( vertical: Value, horizontal: Value @@ -66,8 +66,8 @@ public struct Padding: Property { /// Creates a `padding` declaration. /// Used by `StylesheetRenderer` when emitting CSS. /// - Parameters: - /// - horizontal: The horizontal value. /// - vertical: The vertical value. + /// - horizontal: The horizontal value. public init( vertical: UnitRepresentable = 0, horizontal: UnitRepresentable = 0 From 24d0028b2263a725991a30c1e4eefe4005e0398f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A1ndor=20Bereczki?= Date: Tue, 24 Feb 2026 10:25:21 +0100 Subject: [PATCH 3/7] Fix `testMinifiedStylesheet` --- Tests/CSSTests/SwiftCSSTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/CSSTests/SwiftCSSTests.swift b/Tests/CSSTests/SwiftCSSTests.swift index bb7f6f2..939bace 100644 --- a/Tests/CSSTests/SwiftCSSTests.swift +++ b/Tests/CSSTests/SwiftCSSTests.swift @@ -17,7 +17,7 @@ struct SwiftCssTests { Charset("UTF-8") Media { Root { - Margin(vertical: 8.px, horizontal: 8.5.px) + Margin(vertical: 8.5.px, horizontal: 8.px) Padding(vertical: 8.5.px, horizontal: 8.px) } } @@ -44,8 +44,8 @@ struct SwiftCssTests { StylesheetRenderer(indent: 2).render(css) == #""" @charset "UTF-8"; :root { - margin: 8.5px 8px; - padding: 8.5px 8px; + margin: 8.5px 8px; + padding: 8.5px 8px; } """# ) From 98cd444abb4e910d53821c766da47dcbd52298a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A1ndor=20Bereczki?= Date: Tue, 24 Feb 2026 10:37:16 +0100 Subject: [PATCH 4/7] Fix indentation for margin and padding in stylesheet rendering test --- Tests/CSSTests/SwiftCSSTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/CSSTests/SwiftCSSTests.swift b/Tests/CSSTests/SwiftCSSTests.swift index 939bace..63926cb 100644 --- a/Tests/CSSTests/SwiftCSSTests.swift +++ b/Tests/CSSTests/SwiftCSSTests.swift @@ -44,8 +44,8 @@ struct SwiftCssTests { StylesheetRenderer(indent: 2).render(css) == #""" @charset "UTF-8"; :root { - margin: 8.5px 8px; - padding: 8.5px 8px; + margin: 8.5px 8px; + padding: 8.5px 8px; } """# ) From 29febeeb373a42402964f85ae1e393d8ca739a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A1ndor=20Bereczki?= Date: Wed, 25 Feb 2026 12:36:11 +0100 Subject: [PATCH 5/7] Add BackdropFilter blur property and corresponding test suite --- Sources/CSS/Properties/BackdropFilter.swift | 91 +++++++++++++++++++ .../Properties/BackdropFilterTestSuite.swift | 69 ++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 Sources/CSS/Properties/BackdropFilter.swift create mode 100644 Tests/CSSTests/Properties/BackdropFilterTestSuite.swift diff --git a/Sources/CSS/Properties/BackdropFilter.swift b/Sources/CSS/Properties/BackdropFilter.swift new file mode 100644 index 0000000..9730b9e --- /dev/null +++ b/Sources/CSS/Properties/BackdropFilter.swift @@ -0,0 +1,91 @@ +// +// BackdropFilter.swift +// swift-web-standards +// +// Created by Binary Birds on 2026. 02. 25. + +/// CSS `backdrop-filter` property. +/// Provides typed values for this declaration. +public struct BackdropFilter: Property { + /// Supported length value for `backdrop-filter: blur(...)`. + public struct BlurLength: UnitRepresentable, Sendable { + public let rawValue: String + + /// Creates a blur length from a CSS `Unit`. + /// Returns `nil` for unsupported units (for example `%`). + public init?( + _ unit: Unit + ) where T: Numeric & Sendable { + switch unit.type { + case .cm, .mm, .in, .px, .pt, .pc, .em, .ex, .ch, .rem, .vw, .vh, + .vmin, .vmax: + self.rawValue = unit.rawValue + case .percent: + return nil + } + } + } + + /// Value options for the `backdrop-filter` property. + public enum Value: Sendable { + /// Default value. Specifies no effects. + case none + /// Applies a blur effect to the backdrop. + case blur(BlurLength) + /// Sets this property to its default value. + case initial + /// Inherits this property from its parent element. + case inherit + + var rawValue: String { + switch self { + case .none: + return "none" + case .blur(let value): + return "blur(\(value.rawValue))" + case .initial: + return "initial" + case .inherit: + return "inherit" + } + } + } + + public let name: String + public let value: String + public var isImportant: Bool + + /// Applies graphical effects such as blurring to the area behind an element. + /// - Parameter value: The property value. + public init( + _ value: Value = .none + ) { + self.name = "backdrop-filter" + self.value = value.rawValue + self.isImportant = false + } + + /// Convenience initializer for `backdrop-filter: blur(...)` using `Unit`. + /// Returns `nil` when the supplied unit is unsupported by blur(). + public init?( + blur unit: Unit + ) where T: Numeric & Sendable { + guard let blurLength = BlurLength(unit) else { + return nil + } + self.init(.blur(blurLength)) + } + + // TODO: add support for the remaining backdrop-filter functions: + // - brightness() + // - contrast() + // - drop-shadow() + // - grayscale() + // - hue-rotate() + // - invert() + // - opacity() + // - saturate() + // - sepia() + // - url() + // - multiple functions in a single declaration +} diff --git a/Tests/CSSTests/Properties/BackdropFilterTestSuite.swift b/Tests/CSSTests/Properties/BackdropFilterTestSuite.swift new file mode 100644 index 0000000..b49a868 --- /dev/null +++ b/Tests/CSSTests/Properties/BackdropFilterTestSuite.swift @@ -0,0 +1,69 @@ +// +// BackdropFilterTestSuite.swift +// swift-web-standards +// +// Created by Binary Birds on 2026. 02. 25. + +import Testing + +@testable import CSS + +@Suite +struct BackdropFilterTests { + + @Test + func initializers() { + let property = BackdropFilter() + + let renderer = StylesheetRenderer() + let result = renderer.renderProperty(property) + + let expectation = "\(property.name): \(property.value)" + + #expect(result == expectation) + } + + @Test + func important() { + let property = BackdropFilter() + .important() + + let renderer = StylesheetRenderer() + let result = renderer.renderProperty(property) + + let expectation = "\(property.name): \(property.value) !important" + + #expect(result == expectation) + } + + @Test + func values() { + let blur = BackdropFilter(blur: 100.px) + let blurRem = BackdropFilter(blur: 2.rem) + let inherit = BackdropFilter(.inherit) + + let renderer = StylesheetRenderer() + if let blur { + #expect(renderer.renderProperty(blur) == "backdrop-filter: blur(100px)") + } else { + Issue.record("Expected px unit to be accepted by BackdropFilter blur.") + } + + if let blurRem { + #expect( + renderer.renderProperty(blurRem) == "backdrop-filter: blur(2rem)" + ) + } else { + Issue.record("Expected rem unit to be accepted by BackdropFilter blur.") + } + + #expect(renderer.renderProperty(inherit) == "backdrop-filter: inherit") + } + + @Test + func unsupportedUnits() { + let invalid = BackdropFilter(blur: 50.percent) + + #expect(invalid == nil) + } +} From 33a07af007972a17cc0ab07c9f8c56ba2419ef88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A1ndor=20Bereczki?= Date: Wed, 25 Feb 2026 12:50:02 +0100 Subject: [PATCH 6/7] Format BackdropFilter test --- .../Properties/BackdropFilterTestSuite.swift | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Tests/CSSTests/Properties/BackdropFilterTestSuite.swift b/Tests/CSSTests/Properties/BackdropFilterTestSuite.swift index b49a868..967b9a4 100644 --- a/Tests/CSSTests/Properties/BackdropFilterTestSuite.swift +++ b/Tests/CSSTests/Properties/BackdropFilterTestSuite.swift @@ -44,17 +44,27 @@ struct BackdropFilterTests { let renderer = StylesheetRenderer() if let blur { - #expect(renderer.renderProperty(blur) == "backdrop-filter: blur(100px)") - } else { - Issue.record("Expected px unit to be accepted by BackdropFilter blur.") + #expect( + renderer.renderProperty(blur) + == "backdrop-filter: blur(100px)" + ) + } + else { + Issue.record( + "Expected px unit to be accepted by BackdropFilter blur." + ) } if let blurRem { #expect( - renderer.renderProperty(blurRem) == "backdrop-filter: blur(2rem)" + renderer.renderProperty(blurRem) + == "backdrop-filter: blur(2rem)" + ) + } + else { + Issue.record( + "Expected rem unit to be accepted by BackdropFilter blur." ) - } else { - Issue.record("Expected rem unit to be accepted by BackdropFilter blur.") } #expect(renderer.renderProperty(inherit) == "backdrop-filter: inherit") From 4e687621ab8b200490eb2e1570e130023c4a219d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A1ndor=20Bereczki?= Date: Wed, 25 Feb 2026 13:07:31 +0100 Subject: [PATCH 7/7] Add HEX opacity support and test --- Sources/CSS/CSSColor.swift | 6 +++--- Tests/CSSTests/CSSColorTests.swift | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Sources/CSS/CSSColor.swift b/Sources/CSS/CSSColor.swift index 6bf3bff..d46234b 100644 --- a/Sources/CSS/CSSColor.swift +++ b/Sources/CSS/CSSColor.swift @@ -19,12 +19,12 @@ public struct CSSColor: Sendable, ExpressibleByStringLiteral { /// Creates a color from a hex string literal. /// - /// Valid lengths are 3, 4, 6, or 7 characters, with an optional `#` prefix. + /// Valid lengths are 3, 4, 6, 7, 8, or 9 characters, with an optional `#` prefix. /// - Parameter value: The hex string literal. public init(stringLiteral value: StringLiteralType) { colorValue = value - /// check if length is valid (000, #000, cafe00, #cafe00). - assert([3, 4, 6, 7].contains(value.count), "Invalid hex string") + /// check if length is valid (000, #000, cafe00, #cafe00, 12345678, #123456789). + assert([3, 4, 6, 7, 8, 9].contains(value.count), "Invalid hex string") /// add # prefix if missing. if !colorValue.hasPrefix("#") { diff --git a/Tests/CSSTests/CSSColorTests.swift b/Tests/CSSTests/CSSColorTests.swift index a030cfa..19e01f1 100644 --- a/Tests/CSSTests/CSSColorTests.swift +++ b/Tests/CSSTests/CSSColorTests.swift @@ -19,6 +19,9 @@ struct CSSColorTests { let short: CSSColor = "#fff" #expect(short.rawValue == "#fff") + let hexOpacity: CSSColor = "#12345678" + #expect(hexOpacity.rawValue == "#12345678") + let rgb = CSSColor(r: 1, g: 2, b: 3) #expect(rgb.rawValue == "rgb(1,2,3)")