Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Sources/CSS/Fraction.swift
Original file line number Diff line number Diff line change
@@ -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<T: Numeric & Sendable>: 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<Self> {
.init(value: self)
}
}
4 changes: 4 additions & 0 deletions Sources/CSS/Properties/GridAutoColumns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions Sources/CSS/Properties/GridAutoRows.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/CSS/Properties/GridTemplateColumns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions Sources/CSS/Properties/GridTemplateRows.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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))"
}
}
}
Expand Down
34 changes: 34 additions & 0 deletions Sources/CSS/Properties/GridTrackSize.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
}
19 changes: 19 additions & 0 deletions Tests/CSSTests/FractionTests.swift
Original file line number Diff line number Diff line change
@@ -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")
}
}
10 changes: 10 additions & 0 deletions Tests/CSSTests/Properties/GridAutoColumnsTestSuite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
10 changes: 10 additions & 0 deletions Tests/CSSTests/Properties/GridAutoRowsTestSuite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
24 changes: 24 additions & 0 deletions Tests/CSSTests/Properties/GridTemplateColumnsTestSuite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
)
}
}
20 changes: 20 additions & 0 deletions Tests/CSSTests/Properties/GridTemplateRowsTestSuite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
)
}
}
Loading