diff --git a/Sources/CSS/Fraction.swift b/Sources/CSS/Fraction.swift new file mode 100644 index 0000000..92fad04 --- /dev/null +++ b/Sources/CSS/Fraction.swift @@ -0,0 +1,36 @@ +// +// Fraction.swift +// swift-web-standards +// +// Created by Binary Birds on 2026. 02. 25. + +/// A type that can be rendered as a CSS fractional (`fr`) value. +public protocol FractionRepresentable: Sendable { + /// The rendered fraction string. + var rawValue: String { get } +} + +/// Strongly typed CSS fraction value used by Grid track sizing. +public struct Fraction: FractionRepresentable { + + /// The numeric fraction value. + public var value: T + + /// Creates a new fraction value. + /// - Parameter value: The numeric value. + public init(value: T) { + self.value = value + } + + /// Rendered CSS fraction string. + public var rawValue: String { + "\(value)fr" + } +} + +extension Numeric where Self: Sendable { + /// CSS fraction (`fr`) unit for Grid track sizing. + public var fr: Fraction { + .init(value: self) + } +} diff --git a/Sources/CSS/Properties/GridAutoColumns.swift b/Sources/CSS/Properties/GridAutoColumns.swift index 180104e..6084787 100644 --- a/Sources/CSS/Properties/GridAutoColumns.swift +++ b/Sources/CSS/Properties/GridAutoColumns.swift @@ -21,6 +21,8 @@ public struct GridAutoColumns: Property { // case minmax(min.max) /// Sets the size of the columns, by using a legal length value. case length(UnitRepresentable) + /// Sets the size of the columns using fractional (`fr`) values. + case fraction(FractionRepresentable) var rawValue: String { switch self { @@ -32,6 +34,8 @@ public struct GridAutoColumns: Property { return "min-content" case .length(let value): return value.rawValue + case .fraction(let value): + return value.rawValue } } } diff --git a/Sources/CSS/Properties/GridAutoRows.swift b/Sources/CSS/Properties/GridAutoRows.swift index 0ad78c4..cec83f4 100644 --- a/Sources/CSS/Properties/GridAutoRows.swift +++ b/Sources/CSS/Properties/GridAutoRows.swift @@ -19,6 +19,8 @@ public struct GridAutoRows: Property { case minContent /// Sets the size of the rows, by using a legal length value. case length(UnitRepresentable) + /// Sets the size of the rows using fractional (`fr`) values. + case fraction(FractionRepresentable) var rawValue: String { switch self { @@ -30,6 +32,8 @@ public struct GridAutoRows: Property { return "min-content" case .length(let value): return value.rawValue + case .fraction(let value): + return value.rawValue } } } diff --git a/Sources/CSS/Properties/GridTemplateColumns.swift b/Sources/CSS/Properties/GridTemplateColumns.swift index 1132b00..1f4ce08 100644 --- a/Sources/CSS/Properties/GridTemplateColumns.swift +++ b/Sources/CSS/Properties/GridTemplateColumns.swift @@ -19,6 +19,10 @@ public struct GridTemplateColumns: Property { case minContent /// Sets the size of the columns, by using a legal length value. case length(UnitRepresentable) + /// Sets the size of the columns using fractional (`fr`) values. + case fraction(FractionRepresentable) + /// Repeats a track-size value in a pattern. + case `repeat`(Int, GridTrackSize) /// Sets this property to its default value. case initial /// Inherits this property from its parent element. @@ -36,6 +40,10 @@ public struct GridTemplateColumns: Property { return "min-content" case .length(let value): return value.rawValue + case .fraction(let value): + return value.rawValue + case .repeat(let count, let value): + return "repeat(\(count), \(value.rawValue))" case .initial: return "initial" case .inherit: diff --git a/Sources/CSS/Properties/GridTemplateRows.swift b/Sources/CSS/Properties/GridTemplateRows.swift index 3f524a3..aa6d392 100644 --- a/Sources/CSS/Properties/GridTemplateRows.swift +++ b/Sources/CSS/Properties/GridTemplateRows.swift @@ -19,6 +19,10 @@ public struct GridTemplateRows: Property { case minContent /// Sets the size of the rows, by using a legal length value. case length(UnitRepresentable) + /// Sets the size of the rows using fractional (`fr`) values. + case fraction(FractionRepresentable) + /// Repeats a track-size value in a pattern. + case `repeat`(Int, GridTrackSize) var rawValue: String { switch self { @@ -32,6 +36,10 @@ public struct GridTemplateRows: Property { return "min-content" case .length(let value): return value.rawValue + case .fraction(let value): + return value.rawValue + case .repeat(let count, let value): + return "repeat(\(count), \(value.rawValue))" } } } diff --git a/Sources/CSS/Properties/GridTrackSize.swift b/Sources/CSS/Properties/GridTrackSize.swift new file mode 100644 index 0000000..bdab862 --- /dev/null +++ b/Sources/CSS/Properties/GridTrackSize.swift @@ -0,0 +1,34 @@ +// +// GridTrackSize.swift +// swift-web-standards +// +// Created by Binary Birds on 2026. 02. 25. + +/// Shared Grid track-size value used by repeat() in template properties. +public enum GridTrackSize: Sendable { + /// Automatic track sizing. + case auto + /// Track sized to the largest content contribution. + case maxContent + /// Track sized to the smallest content contribution. + case minContent + /// Track sized with a CSS length/percentage unit. + case length(UnitRepresentable) + /// Track sized with a CSS fraction (`fr`) unit. + case fraction(FractionRepresentable) + + var rawValue: String { + switch self { + case .auto: + return "auto" + case .maxContent: + return "max-content" + case .minContent: + return "min-content" + case .length(let value): + return value.rawValue + case .fraction(let value): + return value.rawValue + } + } +} diff --git a/Tests/CSSTests/FractionTests.swift b/Tests/CSSTests/FractionTests.swift new file mode 100644 index 0000000..3bac8de --- /dev/null +++ b/Tests/CSSTests/FractionTests.swift @@ -0,0 +1,19 @@ +// +// FractionTests.swift +// swift-web-standards +// +// Created by Binary Birds on 2026. 02. 25. + +import Testing + +@testable import CSS + +@Suite +struct FractionTests { + + @Test + func behavior() { + #expect(1.fr.rawValue == "1fr") + #expect(2.5.fr.rawValue == "2.5fr") + } +} diff --git a/Tests/CSSTests/Properties/GridAutoColumnsTestSuite.swift b/Tests/CSSTests/Properties/GridAutoColumnsTestSuite.swift index 6bf53e3..697b150 100644 --- a/Tests/CSSTests/Properties/GridAutoColumnsTestSuite.swift +++ b/Tests/CSSTests/Properties/GridAutoColumnsTestSuite.swift @@ -35,4 +35,14 @@ struct GridAutoColumnsTests { #expect(result == expectation) } + + @Test + func values() { + let length = GridAutoColumns(.length(150.px)) + let fraction = GridAutoColumns(.fraction(3.fr)) + + let renderer = StylesheetRenderer() + #expect(renderer.renderProperty(length) == "grid-auto-columns: 150px") + #expect(renderer.renderProperty(fraction) == "grid-auto-columns: 3fr") + } } diff --git a/Tests/CSSTests/Properties/GridAutoRowsTestSuite.swift b/Tests/CSSTests/Properties/GridAutoRowsTestSuite.swift index b136b29..4c5770e 100644 --- a/Tests/CSSTests/Properties/GridAutoRowsTestSuite.swift +++ b/Tests/CSSTests/Properties/GridAutoRowsTestSuite.swift @@ -35,4 +35,14 @@ struct GridAutoRowsTests { #expect(result == expectation) } + + @Test + func values() { + let length = GridAutoRows(.length(80.px)) + let fraction = GridAutoRows(.fraction(1.5.fr)) + + let renderer = StylesheetRenderer() + #expect(renderer.renderProperty(length) == "grid-auto-rows: 80px") + #expect(renderer.renderProperty(fraction) == "grid-auto-rows: 1.5fr") + } } diff --git a/Tests/CSSTests/Properties/GridTemplateColumnsTestSuite.swift b/Tests/CSSTests/Properties/GridTemplateColumnsTestSuite.swift index eec1a4b..14dec74 100644 --- a/Tests/CSSTests/Properties/GridTemplateColumnsTestSuite.swift +++ b/Tests/CSSTests/Properties/GridTemplateColumnsTestSuite.swift @@ -35,4 +35,28 @@ struct GridTemplateColumnsTests { #expect(result == expectation) } + + @Test + func values() { + let length = GridTemplateColumns(.length(320.px)) + let fraction = GridTemplateColumns(.fraction(1.fr)) + let repeatLength = GridTemplateColumns(.repeat(3, .length(160.px))) + let repeatFraction = GridTemplateColumns(.repeat(3, .fraction(1.fr))) + + let renderer = StylesheetRenderer() + #expect( + renderer.renderProperty(length) == "grid-template-columns: 320px" + ) + #expect( + renderer.renderProperty(fraction) == "grid-template-columns: 1fr" + ) + #expect( + renderer.renderProperty(repeatLength) + == "grid-template-columns: repeat(3, 160px)" + ) + #expect( + renderer.renderProperty(repeatFraction) + == "grid-template-columns: repeat(3, 1fr)" + ) + } } diff --git a/Tests/CSSTests/Properties/GridTemplateRowsTestSuite.swift b/Tests/CSSTests/Properties/GridTemplateRowsTestSuite.swift index 3452cac..513695d 100644 --- a/Tests/CSSTests/Properties/GridTemplateRowsTestSuite.swift +++ b/Tests/CSSTests/Properties/GridTemplateRowsTestSuite.swift @@ -35,4 +35,24 @@ struct GridTemplateRowsTests { #expect(result == expectation) } + + @Test + func values() { + let length = GridTemplateRows(.length(120.px)) + let fraction = GridTemplateRows(.fraction(2.fr)) + let repeatLength = GridTemplateRows(.repeat(3, .length(160.px))) + let repeatFraction = GridTemplateRows(.repeat(3, .fraction(1.fr))) + + let renderer = StylesheetRenderer() + #expect(renderer.renderProperty(length) == "grid-template-rows: 120px") + #expect(renderer.renderProperty(fraction) == "grid-template-rows: 2fr") + #expect( + renderer.renderProperty(repeatLength) + == "grid-template-rows: repeat(3, 160px)" + ) + #expect( + renderer.renderProperty(repeatFraction) + == "grid-template-rows: repeat(3, 1fr)" + ) + } }