diff --git a/Package.swift b/Package.swift
index 1dfbe91..7ff564b 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,28 +1,15 @@
-// swift-tools-version: 5.6
-// The swift-tools-version declares the minimum version of Swift required to build this package.
+// swift-tools-version:5.1
 
 import PackageDescription
 
 let package = Package(
     name: "AttributedFont",
+    platforms: [.iOS(.v13), .macOS(.v10_15), .tvOS(.v13), .watchOS(.v6)],
     products: [
-        // Products define the executables and libraries a package produces, and make them visible to other packages.
-        .library(
-            name: "AttributedFont",
-            targets: ["AttributedFont"]),
-    ],
-    dependencies: [
-        // Dependencies declare other packages that this package depends on.
-        // .package(url: /* package url */, from: "1.0.0"),
+        .library(name: "AttributedFont", targets: ["AttributedFont", "Previews"])
     ],
     targets: [
-        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
-        // Targets can depend on other targets in this package, and on products in packages this package depends on.
-        .target(
-            name: "AttributedFont",
-            dependencies: []),
-        .testTarget(
-            name: "AttributedFontTests",
-            dependencies: ["AttributedFont"]),
+        .target(name: "AttributedFont", dependencies: []),
+        .target(name: "Previews", dependencies: ["AttributedFont"])
     ]
 )
diff --git a/Sources/AttributedFont/AttributedFont+Font.swift b/Sources/AttributedFont/AttributedFont+Font.swift
new file mode 100644
index 0000000..8bd061e
--- /dev/null
+++ b/Sources/AttributedFont/AttributedFont+Font.swift
@@ -0,0 +1,74 @@
+import SwiftUI
+
+// MARK: Creating Custom Fonts
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedFont {
+    
+    @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
+    public static func custom(_ name: String, fixedSize: CGFloat, attributes: Attributes) -> Self {
+        return .init(name: name, fixedSize: fixedSize, attributes: attributes)
+    }
+}
+
+// MARK: Styling a Font
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedFont {
+    
+    public func italic() -> Self {
+        var modified = self
+        modified.font = modified.font.italic()
+        return modified
+    }
+    
+    public func smallCaps() -> Self {
+        var modified = self
+        modified.font = modified.font.smallCaps()
+        return modified
+    }
+    
+    public func lowercaseSmallCaps() -> Self {
+        var modified = self
+        modified.font = modified.font.lowercaseSmallCaps()
+        return modified
+    }
+    
+    public func uppercaseSmallCaps() -> Self {
+        var modified = self
+        modified.font = modified.font.uppercaseSmallCaps()
+        return modified
+    }
+    
+    public func monospacedDigit() -> Self {
+        var modified = self
+        modified.font = modified.font.monospacedDigit()
+        return modified
+    }
+    
+    public func weight(_ weight: Font.Weight) -> Self {
+        var modified = self
+        modified.font = modified.font.weight(weight)
+        return modified
+    }
+    
+    public func bold() -> Self {
+        var modified = self
+        modified.font = modified.font.bold()
+        return modified
+    }
+    
+    @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+    public func monospaced() -> Self {
+        var modified = self
+        modified.font = modified.font.monospaced()
+        return modified
+    }
+    
+    @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
+    public func leading(_ leading: Font.Leading) -> Self {
+        var modified = self
+        modified.font = modified.font.leading(leading)
+        return modified
+    }
+}
diff --git a/Sources/AttributedFont/AttributedFont.swift b/Sources/AttributedFont/AttributedFont.swift
index 277469f..5af2216 100644
--- a/Sources/AttributedFont/AttributedFont.swift
+++ b/Sources/AttributedFont/AttributedFont.swift
@@ -1,6 +1,57 @@
-public struct AttributedFont {
-    public private(set) var text = "Hello, World!"
+import SwiftUI
 
-    public init() {
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public struct AttributedFont: Hashable {
+    
+    public internal(set) var name: String
+    public internal(set) var size: CGFloat
+    public internal(set) var attributes: Attributes
+    
+    public internal(set) var font: Font
+    public internal(set) var ctFont: CTFont
+    
+    @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
+    internal init(name: String, fixedSize: CGFloat, attributes: Attributes) {
+        self.name = name
+        self.size = fixedSize
+        self.attributes = attributes
+        
+        self.font = .custom(name, fixedSize: fixedSize)
+        self.ctFont = CTFontCreateWithName(name as CFString, size, nil)
+    }
+}
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedFont {
+    
+    public struct Attributes: Hashable {
+        
+        public internal(set) var kerning: CGFloat?
+        public internal(set) var tracking: CGFloat?
+        public internal(set) var lineHeightMultiple: CGFloat?
+        
+        public init(kerning: CGFloat? = nil, tracking: CGFloat? = nil, lineHeightMultiple: CGFloat? = nil) {
+            self.kerning = kerning
+            self.tracking = tracking
+            self.lineHeightMultiple = lineHeightMultiple
+        }
+    }
+}
+
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedFont {
+    
+    public var lineSpacing: CGFloat? {
+        guard let lineHeightMultiple = attributes.lineHeightMultiple else {
+            return nil
+        }
+        let originalLineHeight = CTFontGetLineHeight(ctFont)
+        let customLineHeight = originalLineHeight * lineHeightMultiple
+        guard customLineHeight > originalLineHeight else {
+            return nil
+        }
+        let lineSpacing = customLineHeight - originalLineHeight
+        return lineSpacing
     }
 }
diff --git a/Sources/AttributedFont/AttributedText+Text.swift b/Sources/AttributedFont/AttributedText+Text.swift
new file mode 100644
index 0000000..ca6cb26
--- /dev/null
+++ b/Sources/AttributedFont/AttributedText+Text.swift
@@ -0,0 +1,259 @@
+import SwiftUI
+
+// MARK: Creating a Text View from a String
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedText {
+    
+    public init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil) {
+        self.init(text: Text(key, tableName: tableName, bundle: bundle, comment: comment))
+    }
+    
+    @inlinable public init(verbatim content: String) {
+        self.init(text: Text(verbatim: content))
+    }
+    
+    @_disfavoredOverload public init<S: StringProtocol>(_ content: S) {
+        self.init(text: Text(content))
+    }
+}
+
+// MARK: Creating a Text View from an Attributed String
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+extension AttributedText {
+    
+    public init(_ attributedContent: AttributedString) {
+        self.init(text: Text(attributedContent))
+    }
+}
+
+// MARK: Creating a Text View for a Date
+
+@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
+extension AttributedText {
+    
+    public init(_ dates: ClosedRange<Date>) {
+        self.init(text: Text(dates))
+    }
+    
+    public init(_ interval: DateInterval) {
+        self.init(text: Text(interval))
+    }
+    
+    public init(_ date: Date, style: Text.DateStyle) {
+        self.init(text: Text(date, style: style))
+    }
+}
+
+// MARK: Creating a Text View with Formatting
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+extension AttributedText {
+    
+    public init<F: FormatStyle>(_ input: F.FormatInput, format: F) where F.FormatInput : Equatable, F.FormatOutput == String {
+        self.init(text: Text(input, format: format))
+    }
+}
+
+@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
+extension AttributedText {
+    
+    public init<Subject: ReferenceConvertible>(_ subject: Subject, formatter: Formatter) {
+        self.init(text: Text(subject, formatter: formatter))
+    }
+    
+    public init<Subject: NSObject>(_ subject: Subject, formatter: Formatter) {
+        self.init(text: Text(subject, formatter: formatter))
+    }
+}
+
+// MARK: Creating a Text View from an Image
+
+@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
+extension AttributedText {
+    
+    public init(_ image: Image) {
+        self.init(text: Text(image))
+    }
+}
+
+// MARK: Choosing a Font
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedText {
+    
+    public func attributedFont(_ attributedFont: AttributedFont?) -> Self {
+        var modified = self
+        modified.text = text
+            .font(attributedFont?.font)
+            .kerning(attributedFont?.attributes.kerning ?? 0)
+        modified.modifiedAttributedFont = attributedFont
+        return modified
+    }
+    
+    public func font(_ font: Font?) -> Self {
+        var modified = self
+        modified.text = text.font(font)
+        return modified
+    }
+    
+    public func fontWeight(_ weight: Font.Weight?) -> Self {
+        var modified = self
+        modified.text = text.fontWeight(weight)
+        return modified
+    }
+}
+
+// MARK: Styling the View’s Text
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedText {
+    
+    public func foregroundColor(_ color: Color?) -> Self {
+        var modified = self
+        modified.text = text.foregroundColor(color)
+        return modified
+    }
+    
+    public func bold() -> Self {
+        var modified = self
+        modified.text = text.bold()
+        return modified
+    }
+    
+    public func italic() -> Self {
+        var modified = self
+        modified.text = text.italic()
+        return modified
+    }
+    
+    @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+    public func monospacedDigit() -> Self {
+        var modified = self
+        modified.text = text.monospacedDigit()
+        return modified
+    }
+    
+    public func strikethrough(_ active: Bool = true, color: Color? = nil) -> Self {
+        var modified = self
+        modified.text = text.strikethrough(active, color: color)
+        return modified
+    }
+    
+    public func underline(_ active: Bool = true, color: Color? = nil) -> Self {
+        var modified = self
+        modified.text = text.underline(active, color: color)
+        return modified
+    }
+    
+    public func kerning(_ kerning: CGFloat) -> Self {
+        var modified = self
+        modified.text = text.kerning(kerning)
+        return modified
+    }
+    
+    public func tracking(_ tracking: CGFloat) -> Self {
+        var modified = self
+        modified.text = text.tracking(tracking)
+        return modified
+    }
+    
+    public func baselineOffset(_ baselineOffset: CGFloat) -> Self {
+        var modified = self
+        modified.text = text.baselineOffset(baselineOffset)
+        return modified
+    }
+}
+
+// MARK: Configuring VoiceOver
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+extension AttributedText {
+    
+    public func speechAlwaysIncludesPunctuation(_ value: Bool = true) -> Self {
+        var modified = self
+        modified.text = text.speechAlwaysIncludesPunctuation(value)
+        return modified
+    }
+    
+    public func speechSpellsOutCharacters(_ value: Bool = true) -> Self {
+        var modified = self
+        modified.text = text.speechSpellsOutCharacters(value)
+        return modified
+    }
+    
+    public func speechAdjustedPitch(_ value: Double) -> Self {
+        var modified = self
+        modified.text = text.speechAdjustedPitch(value)
+        return modified
+    }
+    
+    public func speechAnnouncementsQueued(_ value: Bool = true) -> Self {
+        var modified = self
+        modified.text = text.speechAnnouncementsQueued(value)
+        return modified
+    }
+}
+
+// MARK: Providing Accessibility Information
+
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+extension AttributedText {
+    public func accessibilityTextContentType(_ value: AccessibilityTextContentType) -> Self {
+        var modified = self
+        modified.text = text.accessibilityTextContentType(value)
+        return modified
+    }
+    
+    public func accessibilityHeading(_ level: AccessibilityHeadingLevel) -> Self {
+        var modified = self
+        modified.text = text.accessibilityHeading(level)
+        return modified
+    }
+    
+    public func accessibilityLabel(_ label: Text) -> Self {
+        var modified = self
+        modified.text = text.accessibilityLabel(label)
+        return modified
+    }
+    
+    public func accessibilityLabel(_ labelKey: LocalizedStringKey) -> Self {
+        var modified = self
+        modified.text = text.accessibilityLabel(labelKey)
+        return modified
+    }
+    
+    public func accessibilityLabel<S: StringProtocol>(_ label: S) -> Self {
+        var modified = self
+        modified.text = text.accessibilityLabel(label)
+        return modified
+    }
+}
+
+// MARK: Combining Text Views
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedText {
+    
+    public static func concatenate(_ views: [AttributedText], attributedFont: AttributedFont) -> Self {
+        let text = views.map(\.text).reduce(Text(verbatim: ""), +)
+        var attributedText: AttributedText = .init(text: text)
+        attributedText.modifiedAttributedFont = attributedFont
+        return attributedText
+    }
+    
+    public static func concatenate(_ views: AttributedText..., attributedFont: AttributedFont) -> Self {
+        return Self.concatenate(views, attributedFont: attributedFont)
+    }
+}
+
+// MARK: Comparing Text Views
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension AttributedText: Equatable {
+    
+    public static func == (lhs: AttributedText, rhs: AttributedText) -> Bool {
+        return lhs.text == rhs.text
+    }
+}
diff --git a/Sources/AttributedFont/AttributedText.swift b/Sources/AttributedFont/AttributedText.swift
new file mode 100644
index 0000000..f5267c6
--- /dev/null
+++ b/Sources/AttributedFont/AttributedText.swift
@@ -0,0 +1,27 @@
+import SwiftUI
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public struct AttributedText: View {
+    
+    @Environment(\.attributedFont) private var environmentAttributedFont: AttributedFont?
+    
+    internal var text: Text
+    internal var modifiedAttributedFont: AttributedFont?
+    
+    internal var attributedFont: AttributedFont? {
+        return modifiedAttributedFont ?? environmentAttributedFont
+    }
+    
+    public init(text: Text) {
+        self.text = text
+    }
+    
+    public var body: some View {
+        text
+            .font(attributedFont?.font)
+            .kerning(attributedFont?.attributes.kerning ?? 0)
+            .tracking(attributedFont?.attributes.tracking ?? 0)
+            .lineSpacing(attributedFont?.lineSpacing ?? 0)
+            .padding(.vertical, (attributedFont?.lineSpacing?.rounded()).flatMap { $0 / 2 })
+    }
+}
diff --git a/Sources/AttributedFont/CTFontGetLineHeight.swift b/Sources/AttributedFont/CTFontGetLineHeight.swift
new file mode 100644
index 0000000..e04d7e3
--- /dev/null
+++ b/Sources/AttributedFont/CTFontGetLineHeight.swift
@@ -0,0 +1,6 @@
+import CoreText
+
+@available(iOS 3.2, macOS 10.5, tvOS 9.0, watchOS 2.0, *)
+public func CTFontGetLineHeight(_ font: CTFont) -> CGFloat {
+    return CTFontGetAscent(font) + CTFontGetDescent(font) + CTFontGetLeading(font)
+}
diff --git a/Sources/AttributedFont/EnvironmentValues+attributedFont.swift b/Sources/AttributedFont/EnvironmentValues+attributedFont.swift
new file mode 100644
index 0000000..941bf41
--- /dev/null
+++ b/Sources/AttributedFont/EnvironmentValues+attributedFont.swift
@@ -0,0 +1,24 @@
+import SwiftUI
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+private struct AttributedFontKey: EnvironmentKey {
+    
+    static let defaultValue: AttributedFont? = nil
+}
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension EnvironmentValues {
+    
+    public var attributedFont: AttributedFont? {
+        get { self[AttributedFontKey.self] }
+        set { self[AttributedFontKey.self] = newValue }
+    }
+}
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+extension View {
+    
+    public func attributedFont(_ attributedFont: AttributedFont?) -> some View {
+        return self.environment(\.attributedFont, attributedFont)
+    }
+}
diff --git a/Sources/Previews/Previews.swift b/Sources/Previews/Previews.swift
new file mode 100644
index 0000000..6bcbc20
--- /dev/null
+++ b/Sources/Previews/Previews.swift
@@ -0,0 +1,65 @@
+import SwiftUI
+import AttributedFont
+
+@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
+struct AttributedText_Previews: PreviewProvider {
+    
+    static let body =
+    "Here's to the crazy ones. The misfits. The rebels. "
+    + "The troublemakers. The round pegs in the square "
+    + "holes. The ones who see things differently. They're "
+    + "not fond of rules. And they have no respect for the "
+    + "status quo. You can praise them, disagree with them, "
+    + "quote them, disbelieve them, glorify or vilify them. "
+    + "About the only thing you can't do is ignore them. "
+    + "Because they change things."
+    
+    static var previews: some View {
+        VStack {
+            AttributedText(body)
+            Button<AttributedText>("Read More", action: {})
+                .buttonStyle(FilledButtonStyle())
+        }
+        .attributedFont(
+            .custom(
+                "Avenir Next",
+                fixedSize: 16,
+                attributes: .init(
+                    kerning: 0,
+                    lineHeightMultiple: 1.5
+                )
+            )
+        )
+        .padding()
+        .previewLayout(.sizeThatFits)
+    }
+}
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+private extension Button where Label == AttributedText {
+    
+    init(_ titleKey: LocalizedStringKey, action: @escaping () -> Void) {
+        self.init(action: action) {
+            AttributedText(titleKey)
+        }
+    }
+    
+    init<S: StringProtocol>(_ title: S, action: @escaping () -> Void) {
+        self.init(action: action) {
+            AttributedText(title)
+        }
+    }
+}
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+private struct FilledButtonStyle: ButtonStyle {
+    func makeBody(configuration: Configuration) -> some View {
+        configuration.label
+            .foregroundColor(.white)
+            .padding(8)
+            .frame(maxWidth: .infinity)
+            .background(
+                RoundedRectangle(cornerRadius: 12).fill(.blue)
+            )
+    }
+}
diff --git a/Tests/AttributedFontTests/AttributedFontTests.swift b/Tests/AttributedFontTests/AttributedFontTests.swift
deleted file mode 100644
index f69b7e1..0000000
--- a/Tests/AttributedFontTests/AttributedFontTests.swift
+++ /dev/null
@@ -1,11 +0,0 @@
-import XCTest
-@testable import AttributedFont
-
-final class AttributedFontTests: XCTestCase {
-    func testExample() throws {
-        // This is an example of a functional test case.
-        // Use XCTAssert and related functions to verify your tests produce the correct
-        // results.
-        XCTAssertEqual(AttributedFont().text, "Hello, World!")
-    }
-}