diff --git a/Sources/TokamakCore/Tokens/AnyTokenBox.swift b/Sources/TokamakCore/Tokens/AnyTokenBox.swift new file mode 100644 index 000000000..1c0cc44a2 --- /dev/null +++ b/Sources/TokamakCore/Tokens/AnyTokenBox.swift @@ -0,0 +1,22 @@ +// Copyright 2018-2020 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Created by Carson Katri on 10/24/2020. +// + +/// Allows "late-binding tokens" to be resolved in an environment by a `Renderer` (or `TokamakCore`) +public protocol AnyTokenBox: AnyObject, Hashable { + associatedtype ResolvedValue + func resolve(in environment: EnvironmentValues) -> ResolvedValue +} diff --git a/Sources/TokamakCore/Tokens/Color.swift b/Sources/TokamakCore/Tokens/Color.swift index 87180f13b..adc6495ff 100644 --- a/Sources/TokamakCore/Tokens/Color.swift +++ b/Sources/TokamakCore/Tokens/Color.swift @@ -15,38 +15,186 @@ // Created by Max Desiatov on 16/10/2018. // -public struct Color: Hashable, Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - var lightEnv = EnvironmentValues() - lightEnv.colorScheme = .light - var darkEnv = EnvironmentValues() - darkEnv.colorScheme = .dark - return lhs._evaluate(lightEnv) == rhs._evaluate(lightEnv) && - lhs._evaluate(darkEnv) == rhs._evaluate(darkEnv) +/// Override `TokamakCore`'s default `Color` resolvers with a Renderer-specific one. +/// You can override a specific color box +/// (such as `_SystemColorBox`, or all boxes with `AnyColorBox`). +/// +/// This extension makes all system colors red: +/// +/// extension _SystemColorBox: AnyColorBoxDeferredToRenderer { +/// public func deferredResolve( +/// in environment: EnvironmentValues +/// ) -> AnyColorBox.ResolvedValue { +/// return .init( +/// red: 1, +/// green: 0, +/// blue: 0, +/// opacity: 1, +/// space: .sRGB +/// ) +/// } +/// } +/// +public protocol AnyColorBoxDeferredToRenderer: AnyColorBox { + func deferredResolve(in environment: EnvironmentValues) -> AnyColorBox.ResolvedValue +} + +public class AnyColorBox: AnyTokenBox { + public struct _RGBA: Hashable, Equatable { + public let red: Double + public let green: Double + public let blue: Double + public let opacity: Double + public let space: Color.RGBColorSpace + public init( + red: Double, + green: Double, + blue: Double, + opacity: Double, + space: Color.RGBColorSpace + ) { + self.red = red + self.green = green + self.blue = blue + self.opacity = opacity + self.space = space + } } + public static func == (lhs: AnyColorBox, rhs: AnyColorBox) -> Bool { false } public func hash(into hasher: inout Hasher) { - hasher.combine(evaluator(EnvironmentValues())) + fatalError("implement \(#function) in subclass") } - public enum RGBColorSpace { - case sRGB - case sRGBLinear - case displayP3 + public func resolve(in environment: EnvironmentValues) -> _RGBA { + fatalError("implement \(#function) in subclass") } +} - public struct _RGBA: Hashable, Equatable { - public let red: Double - public let green: Double - public let blue: Double - public let opacity: Double - public let space: RGBColorSpace +public class _ConcreteColorBox: AnyColorBox { + public let rgba: AnyColorBox._RGBA + + public static func == (lhs: _ConcreteColorBox, rhs: _ConcreteColorBox) -> Bool { + lhs.rgba == rhs.rgba + } + + override public func hash(into hasher: inout Hasher) { + hasher.combine(rgba) + } + + init(_ rgba: AnyColorBox._RGBA) { + self.rgba = rgba } - let evaluator: (EnvironmentValues) -> _RGBA + override public func resolve(in environment: EnvironmentValues) -> ResolvedValue { + rgba + } +} - private init(_ evaluator: @escaping (EnvironmentValues) -> _RGBA) { - self.evaluator = evaluator +public class _EnvironmentDependentColorBox: AnyColorBox { + public let resolver: (EnvironmentValues) -> Color + + public static func == (lhs: _EnvironmentDependentColorBox, + rhs: _EnvironmentDependentColorBox) -> Bool + { + lhs.resolver(EnvironmentValues()) == rhs.resolver(EnvironmentValues()) + } + + override public func hash(into hasher: inout Hasher) { + hasher.combine(resolver(EnvironmentValues())) + } + + init(_ resolver: @escaping (EnvironmentValues) -> Color) { + self.resolver = resolver + } + + override public func resolve(in environment: EnvironmentValues) -> ResolvedValue { + resolver(environment).provider.resolve(in: environment) + } +} + +public class _SystemColorBox: AnyColorBox { + public enum SystemColor: Equatable, Hashable { + case clear + case black + case white + case gray + case red + case green + case blue + case orange + case yellow + case pink + case purple + case primary + case secondary + } + + public let value: SystemColor + + public static func == (lhs: _SystemColorBox, rhs: _SystemColorBox) -> Bool { + lhs.value == rhs.value + } + + override public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + fileprivate init(_ value: SystemColor) { + self.value = value + } + + override public func resolve(in environment: EnvironmentValues) -> ResolvedValue { + switch environment.colorScheme { + case .light: + switch value { + case .clear: return .init(red: 0, green: 0, blue: 0, opacity: 0, space: .sRGB) + case .black: return .init(red: 0, green: 0, blue: 0, opacity: 1, space: .sRGB) + case .white: return .init(red: 1, green: 1, blue: 1, opacity: 1, space: .sRGB) + case .gray: return .init(red: 0.55, green: 0.55, blue: 0.57, opacity: 1, space: .sRGB) + case .red: return .init(red: 1, green: 0.23, blue: 0.19, opacity: 1, space: .sRGB) + case .green: return .init(red: 0.21, green: 0.78, blue: 0.35, opacity: 1, space: .sRGB) + case .blue: return .init(red: 0.01, green: 0.48, blue: 1, opacity: 1, space: .sRGB) + case .orange: return .init(red: 1, green: 0.58, blue: 0, opacity: 1, space: .sRGB) + case .yellow: return .init(red: 1, green: 0.8, blue: 0, opacity: 1, space: .sRGB) + case .pink: return .init(red: 1, green: 0.17, blue: 0.33, opacity: 1, space: .sRGB) + case .purple: return .init(red: 0.69, green: 0.32, blue: 0.87, opacity: 1, space: .sRGB) + case .primary: return .init(red: 0, green: 0, blue: 0, opacity: 1, space: .sRGB) + case .secondary: return .init(red: 0.55, green: 0.55, blue: 0.57, opacity: 1, space: .sRGB) + } + case .dark: + switch value { + case .clear: return .init(red: 0, green: 0, blue: 0, opacity: 0, space: .sRGB) + case .black: return .init(red: 0, green: 0, blue: 0, opacity: 1, space: .sRGB) + case .white: return .init(red: 1, green: 1, blue: 1, opacity: 1, space: .sRGB) + case .gray: return .init(red: 0.55, green: 0.55, blue: 0.57, opacity: 1, space: .sRGB) + case .red: return .init(red: 1, green: 0.27, blue: 0.23, opacity: 1, space: .sRGB) + case .green: return .init(red: 0.19, green: 0.82, blue: 0.35, opacity: 1, space: .sRGB) + case .blue: return .init(red: 0.04, green: 0.52, blue: 1.00, opacity: 1, space: .sRGB) + case .orange: return .init(red: 1, green: 0.62, blue: 0.04, opacity: 1, space: .sRGB) + case .yellow: return .init(red: 1, green: 0.84, blue: 0.04, opacity: 1, space: .sRGB) + case .pink: return .init(red: 1, green: 0.22, blue: 0.37, opacity: 1, space: .sRGB) + case .purple: return .init(red: 0.75, green: 0.35, blue: 0.95, opacity: 1, space: .sRGB) + case .primary: return .init(red: 1, green: 1, blue: 1, opacity: 1, space: .sRGB) + case .secondary: return .init(red: 0.55, green: 0.55, blue: 0.57, opacity: 1, space: .sRGB) + } + } + } +} + +public struct Color: Hashable, Equatable { + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.provider == rhs.provider + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(provider) + } + + let provider: AnyColorBox + + private init(_ provider: AnyColorBox) { + self.provider = provider } public init( @@ -56,9 +204,9 @@ public struct Color: Hashable, Equatable { blue: Double, opacity: Double = 1 ) { - self.init { _ in - _RGBA(red: red, green: green, blue: blue, opacity: opacity, space: colorSpace) - } + self.init(_ConcreteColorBox( + .init(red: red, green: green, blue: blue, opacity: opacity, space: colorSpace) + )) } public init(_ colorSpace: RGBColorSpace = .sRGB, white: Double, opacity: Double = 1) { @@ -77,43 +225,56 @@ public struct Color: Hashable, Equatable { } /// Create a `Color` dependent on the current `ColorScheme`. - public static func _withScheme(_ evaluator: @escaping (ColorScheme) -> Self) -> Self { - .init { - evaluator($0.colorScheme)._evaluate($0) - } + public static func _withScheme(_ resolver: @escaping (ColorScheme) -> Self) -> Self { + .init(_EnvironmentDependentColorBox { + resolver($0.colorScheme) + }) } +} - public func _evaluate(_ environment: EnvironmentValues) -> _RGBA { - evaluator(environment) +public struct _ColorProxy { + let subject: Color + public init(_ subject: Color) { self.subject = subject } + public func resolve(in environment: EnvironmentValues) -> AnyColorBox.ResolvedValue { + if let deferred = subject.provider as? AnyColorBoxDeferredToRenderer { + return deferred.deferredResolve(in: environment) + } else { + return subject.provider.resolve(in: environment) + } } } extension Color { - public static let clear: Self = .init(red: 0, green: 0, blue: 0, opacity: 0) - public static let black: Self = .init(white: 0) - public static let white: Self = .init(white: 1) - public static let gray: Self = .init(white: 0.6) - public static let red: Self = .init(red: 1.00, green: 0.27, blue: 0.23) - public static let green: Self = .init(red: 0.20, green: 0.84, blue: 0.29) - public static let blue: Self = .init(red: 0.04, green: 0.52, blue: 1.00) - public static let orange: Self = .init(red: 1.00, green: 0.62, blue: 0.04) - public static let yellow: Self = .init(red: 1.00, green: 0.84, blue: 0.04) - public static let pink: Self = .init(red: 1.00, green: 0.22, blue: 0.37) - public static let purple: Self = .init(red: 0.75, green: 0.36, blue: 0.95) - public static let primary: Self = .init { - switch $0.colorScheme { - case .light: - return .init(red: 0, green: 0, blue: 0, opacity: 1, space: .sRGB) - case .dark: - return .init(red: 1, green: 1, blue: 1, opacity: 1, space: .sRGB) - } + public enum RGBColorSpace { + case sRGB + case sRGBLinear + case displayP3 } +} - public static let secondary: Self = .gray - public static let accentColor: Self = .init { - ($0.accentColor ?? Self.blue)._evaluate($0) +extension Color { + private init(systemColor: _SystemColorBox.SystemColor) { + self.init(_SystemColorBox(systemColor)) } + public static let clear: Self = .init(systemColor: .clear) + public static let black: Self = .init(systemColor: .black) + public static let white: Self = .init(systemColor: .white) + public static let gray: Self = .init(systemColor: .gray) + public static let red: Self = .init(systemColor: .red) + public static let green: Self = .init(systemColor: .green) + public static let blue: Self = .init(systemColor: .blue) + public static let orange: Self = .init(systemColor: .orange) + public static let yellow: Self = .init(systemColor: .yellow) + public static let pink: Self = .init(systemColor: .pink) + public static let purple: Self = .init(systemColor: .purple) + public static let primary: Self = .init(systemColor: .primary) + + public static let secondary: Self = .init(systemColor: .secondary) + public static let accentColor: Self = .init(_EnvironmentDependentColorBox { + ($0.accentColor ?? Self.blue) + }) + public init(_ color: UIColor) { self = color.color } diff --git a/Sources/TokamakCore/Tokens/Font.swift b/Sources/TokamakCore/Tokens/Font.swift index 451cab270..9206e149c 100644 --- a/Sources/TokamakCore/Tokens/Font.swift +++ b/Sources/TokamakCore/Tokens/Font.swift @@ -12,43 +12,178 @@ // See the License for the specific language governing permissions and // limitations under the License. +/// Override `TokamakCore`'s default `Font` resolvers with a Renderer-specific one. +/// You can override a specific font box +/// (such as `_SystemFontBox`, or all boxes with `AnyFontBox`). +/// +/// This extension makes all fonts monospaced: +/// +/// extension AnyFontBox: AnyFontBoxDeferredToRenderer { +/// public func deferredResolve( +/// in environment: EnvironmentValues +/// ) -> AnyFontBox.ResolvedValue { +/// var font = resolve(in: environment) +/// font._design = .monospaced +/// return font +/// } +/// } +/// +public protocol AnyFontBoxDeferredToRenderer: AnyFontBox { + func deferredResolve(in environment: EnvironmentValues) -> AnyFontBox.ResolvedValue +} + +public class AnyFontBox: AnyTokenBox, Hashable, Equatable { + public struct _Font: Hashable, Equatable { + public var _name: String + public var _size: CGFloat + public var _design: Font.Design + public var _weight: Font.Weight + public var _smallCaps: Bool + public var _italic: Bool + public var _bold: Bool + public var _monospaceDigit: Bool + public var _leading: Font.Leading + + public init( + name: _FontNames, + size: CGFloat, + design: Font.Design = .default, + weight: Font.Weight = .regular, + smallCaps: Bool = false, + italic: Bool = false, + bold: Bool = false, + monospaceDigit: Bool = false, + leading: Font.Leading = .standard + ) { + _name = name.rawValue + _size = size + _design = design + _weight = weight + _smallCaps = smallCaps + _italic = italic + _bold = bold + _monospaceDigit = monospaceDigit + _leading = leading + } + } + + public static func == (lhs: AnyFontBox, rhs: AnyFontBox) -> Bool { false } + public func hash(into hasher: inout Hasher) { + fatalError("implement \(#function) in subclass") + } + + public func resolve(in environment: EnvironmentValues) -> _Font { + fatalError("implement \(#function) in subclass") + } +} + +public class _ConcreteFontBox: AnyFontBox { + public let font: ResolvedValue + + public static func == (lhs: _ConcreteFontBox, rhs: _ConcreteFontBox) -> Bool { + lhs.font == rhs.font + } + + override public func hash(into hasher: inout Hasher) { + hasher.combine(font) + } + + init(_ font: ResolvedValue) { + self.font = font + } + + override public func resolve(in environment: EnvironmentValues) -> ResolvedValue { + font + } +} + +public class _ModifiedFontBox: AnyFontBox { + public let provider: AnyFontBox + public let modifier: (inout ResolvedValue) -> () + + public static func == (lhs: _ModifiedFontBox, rhs: _ModifiedFontBox) -> Bool { + lhs.resolve(in: EnvironmentValues()) == rhs.resolve(in: EnvironmentValues()) + } + + override public func hash(into hasher: inout Hasher) { + hasher.combine(provider.resolve(in: EnvironmentValues())) + } + + init(previously provider: AnyFontBox, modifier: @escaping (inout ResolvedValue) -> ()) { + self.provider = provider + self.modifier = modifier + } + + override public func resolve(in environment: EnvironmentValues) -> ResolvedValue { + var font = provider.resolve(in: environment) + modifier(&font) + return font + } +} + +public class _SystemFontBox: AnyFontBox { + public enum SystemFont: Equatable, Hashable { + case largeTitle + case title + case title2 + case title3 + case headline + case subheadline + case body + case callout + case footnote + case caption + case caption2 + } + + public let value: SystemFont + + public static func == (lhs: _SystemFontBox, rhs: _SystemFontBox) -> Bool { + lhs.value == rhs.value + } + + override public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + init(_ value: SystemFont) { + self.value = value + } + + override public func resolve(in environment: EnvironmentValues) -> ResolvedValue { + switch value { + case .largeTitle: return .init(name: .system, size: 34) + case .title: return .init(name: .system, size: 28) + case .title2: return .init(name: .system, size: 22) + case .title3: return .init(name: .system, size: 20) + case .headline: return .init(name: .system, size: 17, design: .default, weight: .semibold) + case .subheadline: return .init(name: .system, size: 15) + case .body: return .init(name: .system, size: 17) + case .callout: return .init(name: .system, size: 16) + case .footnote: return .init(name: .system, size: 13) + case .caption: return .init(name: .system, size: 12) + case .caption2: return .init(name: .system, size: 11) + } + } +} + public struct Font: Hashable { - public let _name: String - public let _size: CGFloat - public let _design: Design - public let _weight: Weight - public let _smallCaps: Bool - public let _italic: Bool - public let _bold: Bool - public let _monospaceDigit: Bool - public let _leading: Leading + let provider: AnyFontBox + + fileprivate init(_ provider: AnyFontBox) { + self.provider = provider + } public func italic() -> Self { - .init( - _name: _name, - _size: _size, - _design: _design, - _weight: _weight, - _smallCaps: _smallCaps, - _italic: true, - _bold: _bold, - _monospaceDigit: _monospaceDigit, - _leading: _leading - ) + .init(_ModifiedFontBox(previously: provider) { + $0._italic = true + }) } public func smallCaps() -> Self { - .init( - _name: _name, - _size: _size, - _design: _design, - _weight: _weight, - _smallCaps: true, - _italic: _italic, - _bold: _bold, - _monospaceDigit: _monospaceDigit, - _leading: _leading - ) + .init(_ModifiedFontBox(previously: provider) { + $0._smallCaps = true + }) } public func lowercaseSmallCaps() -> Self { @@ -60,59 +195,27 @@ public struct Font: Hashable { } public func monospacedDigit() -> Self { - .init( - _name: _name, - _size: _size, - _design: _design, - _weight: _weight, - _smallCaps: _smallCaps, - _italic: _italic, - _bold: _bold, - _monospaceDigit: true, - _leading: _leading - ) + .init(_ModifiedFontBox(previously: provider) { + $0._monospaceDigit = true + }) } public func weight(_ weight: Weight) -> Self { - .init( - _name: _name, - _size: _size, - _design: _design, - _weight: weight, - _smallCaps: _smallCaps, - _italic: _italic, - _bold: _bold, - _monospaceDigit: _monospaceDigit, - _leading: _leading - ) + .init(_ModifiedFontBox(previously: provider) { + $0._weight = weight + }) } public func bold() -> Self { - .init( - _name: _name, - _size: _size, - _design: _design, - _weight: _weight, - _smallCaps: _smallCaps, - _italic: _italic, - _bold: true, - _monospaceDigit: _monospaceDigit, - _leading: _leading - ) + .init(_ModifiedFontBox(previously: provider) { + $0._bold = true + }) } public func leading(_ leading: Leading) -> Self { - .init( - _name: _name, - _size: _size, - _design: _design, - _weight: _weight, - _smallCaps: _smallCaps, - _italic: _italic, - _bold: true, - _monospaceDigit: _monospaceDigit, - _leading: leading - ) + .init(_ModifiedFontBox(previously: provider) { + $0._leading = leading + }) } } @@ -149,15 +252,19 @@ extension Font { design: Design = .default) -> Self { .init( - _name: _FontNames.system.rawValue, - _size: size, - _design: design, - _weight: weight, - _smallCaps: false, - _italic: false, - _bold: false, - _monospaceDigit: false, - _leading: .standard + _ConcreteFontBox( + .init( + name: .system, + size: size, + design: design, + weight: weight, + smallCaps: false, + italic: false, + bold: false, + monospaceDigit: false, + leading: .standard + ) + ) ) } @@ -170,20 +277,22 @@ extension Font { } extension Font { - public static let largeTitle: Self = .system(size: 34) - public static let title: Self = .system(size: 28) - public static let title2: Self = .system(size: 22) - public static let title3: Self = .system(size: 20) - public static let headline: Font = .system(size: 17, weight: .semibold, design: .default) - public static let subheadline: Self = .system(size: 15) - public static let body: Self = .system(size: 17) - public static let callout: Self = .system(size: 16) - public static let footnote: Self = .system(size: 13) - public static let caption: Self = .system(size: 12) - public static let caption2: Font = .system(size: 11) + public static let largeTitle: Self = .init(_SystemFontBox(.largeTitle)) + public static let title: Self = .init(_SystemFontBox(.title)) + public static let title2: Self = .init(_SystemFontBox(.title2)) + public static let title3: Self = .init(_SystemFontBox(.title3)) + public static let headline: Font = .init(_SystemFontBox(.headline)) + public static let subheadline: Self = .init(_SystemFontBox(.subheadline)) + public static let body: Self = .init(_SystemFontBox(.body)) + public static let callout: Self = .init(_SystemFontBox(.callout)) + public static let footnote: Self = .init(_SystemFontBox(.footnote)) + public static let caption: Self = .init(_SystemFontBox(.caption)) + public static let caption2: Self = .init(_SystemFontBox(.caption2)) public static func system(_ style: TextStyle, design: Design = .default) -> Self { - .system(size: style.font._size, weight: style.font._weight, design: design) + .init(_ModifiedFontBox(previously: style.font.provider) { + $0._design = design + }) } public enum TextStyle: Hashable, CaseIterable { @@ -217,6 +326,18 @@ extension Font { } } +public struct _FontProxy { + let subject: Font + public init(_ subject: Font) { self.subject = subject } + public func resolve(in environment: EnvironmentValues) -> AnyFontBox.ResolvedValue { + if let deferred = subject.provider as? AnyFontBoxDeferredToRenderer { + return deferred.deferredResolve(in: environment) + } else { + return subject.provider.resolve(in: environment) + } + } +} + struct FontKey: EnvironmentKey { static let defaultValue: Font? = nil } diff --git a/Sources/TokamakStaticHTML/Tokens/Tokens.swift b/Sources/TokamakStaticHTML/Tokens/Tokens.swift index 23deaa1cd..ecab78948 100644 --- a/Sources/TokamakStaticHTML/Tokens/Tokens.swift +++ b/Sources/TokamakStaticHTML/Tokens/Tokens.swift @@ -16,7 +16,7 @@ import TokamakCore extension Color { func cssValue(_ environment: EnvironmentValues) -> String { - let rgba = _evaluate(environment) + let rgba = _ColorProxy(self).resolve(in: environment) return "rgba(\(rgba.red * 255), \(rgba.green * 255), \(rgba.blue * 255), \(rgba.opacity))" } } diff --git a/Sources/TokamakStaticHTML/Views/Text/Text.swift b/Sources/TokamakStaticHTML/Views/Text/Text.swift index 346fe6afa..d5be3c9c5 100644 --- a/Sources/TokamakStaticHTML/Views/Text/Text.swift +++ b/Sources/TokamakStaticHTML/Views/Text/Text.swift @@ -76,15 +76,17 @@ extension Font.Leading: CustomStringConvertible { } } -extension Font: StylesConvertible { - public var styles: [String: String] { - [ - "font-family": _name == _FontNames.system.rawValue ? _design.description : _name, - "font-weight": "\(_bold ? Font.Weight.bold.value : _weight.value)", - "font-style": _italic ? "italic" : "normal", - "font-size": "\(_size)", - "line-height": _leading.description, - "font-variant": _smallCaps ? "small-caps" : "normal", +extension Font { + public func styles(in environment: EnvironmentValues) -> [String: String] { + let proxy = _FontProxy(self).resolve(in: environment) + return [ + "font-family": proxy._name == _FontNames.system.rawValue ? proxy._design.description : proxy + ._name, + "font-weight": "\(proxy._bold ? Font.Weight.bold.value : proxy._weight.value)", + "font-style": proxy._italic ? "italic" : "normal", + "font-size": "\(proxy._size)", + "line-height": proxy._leading.description, + "font-variant": proxy._smallCaps ? "small-caps" : "normal", ] } } @@ -175,13 +177,16 @@ extension Text { ?? underline?.1?.cssValue(environment) ?? "inherit" + let resolvedFont = font == nil ? nil : _FontProxy(font!).resolve(in: environment) + return [ "style": """ - \(font?.styles.filter { weight != nil ? $0.key != "font-weight" : true }.inlineStyles ?? "") + \(font?.styles(in: environment).filter { weight != nil ? $0.key != "font-weight" : true } + .inlineStyles ?? "") \(font == nil ? "font-family: \(Font.Design.default.description);" : "") color: \((color ?? .primary).cssValue(environment)); font-style: \(italic ? "italic" : "normal"); - font-weight: \(weight?.value ?? font?._weight.value ?? 400); + font-weight: \(weight?.value ?? resolvedFont?._weight.value ?? 400); letter-spacing: \(kerning); vertical-align: \(baseline == nil ? "baseline" : "\(baseline!)em"); text-decoration: \(textDecoration);