Skip to content

refactor: move color extension to MD structure #406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
60 changes: 31 additions & 29 deletions Sources/MarkdownUI/Theme/Theme+DocC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ extension Theme {
/// Table | ![](DocCTable)
public static let docC = Theme()
.text {
ForegroundColor(.text)
ForegroundColor(.MD.DocCTheme.text)
Copy link

@cweider cweider May 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m noticing pervasive change of indentation from 2 to 4 spaces. For consistency (and in the interest of minimizing the change's size) the 2 space indent should be used.

Note: I’d not worry about rushing out a revision. If done, do it at your leisure. It may be some time before maintainers come through to give their review of the patch.

}
.link {
ForegroundColor(.link)
ForegroundColor(.MD.DocCTheme.link)
}
.heading1 { configuration in
configuration.label
Expand Down Expand Up @@ -86,9 +86,9 @@ extension Theme {
.background {
ZStack {
RoundedRectangle.container
.fill(Color.asideNoteBackground)
.fill(Color.MD.DocCTheme.asideNoteBackground)
RoundedRectangle.container
.strokeBorder(Color.asideNoteBorder)
.strokeBorder(Color.MD.DocCTheme.asideNoteBorder)
}
}
.markdownMargin(top: .em(1.6), bottom: .zero)
Expand All @@ -105,7 +105,7 @@ extension Theme {
.padding(.vertical, 8)
.padding(.leading, 14)
}
.background(Color.codeBackground)
.background(Color.MD.DocCTheme.codeBackground)
.clipShape(.container)
.markdownMargin(top: .em(0.8), bottom: .zero)
}
Expand All @@ -126,7 +126,7 @@ extension Theme {
.table { configuration in
configuration.label
.fixedSize(horizontal: false, vertical: true)
.markdownTableBorderStyle(.init(.horizontalBorders, color: .grid))
.markdownTableBorderStyle(.init(.horizontalBorders, color: .MD.DocCTheme.grid))
.markdownMargin(top: .em(1.6), bottom: .zero)
}
.tableCell { configuration in
Expand All @@ -142,7 +142,7 @@ extension Theme {
}
.thematicBreak {
Divider()
.overlay(Color.grid)
.overlay(Color.MD.DocCTheme.grid)
.markdownMargin(top: .em(2.35), bottom: .em(2.35))
}
}
Expand All @@ -153,26 +153,28 @@ extension Shape where Self == RoundedRectangle {
}
}

extension Color {
fileprivate static let text = Color(
light: Color(rgba: 0x1d1d_1fff), dark: Color(rgba: 0xf5f5_f7ff)
)
fileprivate static let secondaryLabel = Color(
light: Color(rgba: 0x6e6e_73ff), dark: Color(rgba: 0x8686_8bff)
)
fileprivate static let link = Color(
light: Color(rgba: 0x0066_ccff), dark: Color(rgba: 0x2997_ffff)
)
fileprivate static let asideNoteBackground = Color(
light: Color(rgba: 0xf5f5_f7ff), dark: Color(rgba: 0x3232_32ff)
)
fileprivate static let asideNoteBorder = Color(
light: Color(rgba: 0x6969_69ff), dark: Color(rgba: 0x9a9a_9eff)
)
fileprivate static let codeBackground = Color(
light: Color(rgba: 0xf5f5_f7ff), dark: Color(rgba: 0x3333_36ff)
)
fileprivate static let grid = Color(
light: Color(rgba: 0xd2d2_d7ff), dark: Color(rgba: 0x4242_45ff)
)
extension Color.MD {
struct DocCTheme {
fileprivate static let text = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0x1d1d_1fff), dark: Color.MD.rgbaColor(rgba: 0xf5f5_f7ff)
)
fileprivate static let secondaryLabel = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0x6e6e_73ff), dark: Color.MD.rgbaColor(rgba: 0x8686_8bff)
)
fileprivate static let link = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0x0066_ccff), dark: Color.MD.rgbaColor(rgba: 0x2997_ffff)
)
fileprivate static let asideNoteBackground = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0xf5f5_f7ff), dark: Color.MD.rgbaColor(rgba: 0x3232_32ff)
)
fileprivate static let asideNoteBorder = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0x6969_69ff), dark: Color.MD.rgbaColor(rgba: 0x9a9a_9eff)
)
fileprivate static let codeBackground = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0xf5f5_f7ff), dark: Color.MD.rgbaColor(rgba: 0x3333_36ff)
)
fileprivate static let grid = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0xd2d2_d7ff), dark: Color.MD.rgbaColor(rgba: 0x4242_45ff)
)
}
}
84 changes: 43 additions & 41 deletions Sources/MarkdownUI/Theme/Theme+GitHub.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ extension Theme {
/// Table | ![](GitHubTable)
public static let gitHub = Theme()
.text {
ForegroundColor(.text)
BackgroundColor(.background)
ForegroundColor(.MD.GithubTheme.text)
BackgroundColor(.MD.GithubTheme.background)
FontSize(16)
}
.code {
FontFamilyVariant(.monospaced)
FontSize(.em(0.85))
BackgroundColor(.secondaryBackground)
BackgroundColor(.MD.GithubTheme.secondaryBackground)
}
.strong {
FontWeight(.semibold)
}
.link {
ForegroundColor(.link)
ForegroundColor(.MD.GithubTheme.link)
}
.heading1 { configuration in
VStack(alignment: .leading, spacing: 0) {
Expand All @@ -41,7 +41,7 @@ extension Theme {
FontWeight(.semibold)
FontSize(.em(2))
}
Divider().overlay(Color.divider)
Divider().overlay(Color.MD.GithubTheme.divider)
}
}
.heading2 { configuration in
Expand All @@ -54,7 +54,7 @@ extension Theme {
FontWeight(.semibold)
FontSize(.em(1.5))
}
Divider().overlay(Color.divider)
Divider().overlay(Color.MD.GithubTheme.divider)
}
}
.heading3 { configuration in
Expand Down Expand Up @@ -90,7 +90,7 @@ extension Theme {
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(0.85))
ForegroundColor(.tertiaryText)
ForegroundColor(.MD.GithubTheme.tertiaryText)
}
}
.paragraph { configuration in
Expand All @@ -102,10 +102,10 @@ extension Theme {
.blockquote { configuration in
HStack(spacing: 0) {
RoundedRectangle(cornerRadius: 6)
.fill(Color.border)
.fill(Color.MD.GithubTheme.border)
.relativeFrame(width: .em(0.2))
configuration.label
.markdownTextStyle { ForegroundColor(.secondaryText) }
.markdownTextStyle { ForegroundColor(.MD.GithubTheme.secondaryText) }
.relativePadding(.horizontal, length: .em(1))
}
.fixedSize(horizontal: false, vertical: true)
Expand All @@ -121,7 +121,7 @@ extension Theme {
}
.padding(16)
}
.background(Color.secondaryBackground)
.background(Color.MD.GithubTheme.secondaryBackground)
.clipShape(RoundedRectangle(cornerRadius: 6))
.markdownMargin(top: 0, bottom: 16)
}
Expand All @@ -132,16 +132,16 @@ extension Theme {
.taskListMarker { configuration in
Image(systemName: configuration.isCompleted ? "checkmark.square.fill" : "square")
.symbolRenderingMode(.hierarchical)
.foregroundStyle(Color.checkbox, Color.checkboxBackground)
.foregroundStyle(Color.MD.GithubTheme.checkbox, Color.MD.GithubTheme.checkboxBackground)
.imageScale(.small)
.relativeFrame(minWidth: .em(1.5), alignment: .trailing)
}
.table { configuration in
configuration.label
.fixedSize(horizontal: false, vertical: true)
.markdownTableBorderStyle(.init(color: .border))
.markdownTableBorderStyle(.init(color: .MD.GithubTheme.border))
.markdownTableBackgroundStyle(
.alternatingRows(Color.background, Color.secondaryBackground)
.alternatingRows(Color.MD.GithubTheme.background, Color.MD.GithubTheme.secondaryBackground)
)
.markdownMargin(top: 0, bottom: 16)
}
Expand All @@ -161,36 +161,38 @@ extension Theme {
.thematicBreak {
Divider()
.relativeFrame(height: .em(0.25))
.overlay(Color.border)
.overlay(Color.MD.GithubTheme.border)
.markdownMargin(top: 24, bottom: 24)
}
}

extension Color {
fileprivate static let text = Color(
light: Color(rgba: 0x0606_06ff), dark: Color(rgba: 0xfbfb_fcff)
)
fileprivate static let secondaryText = Color(
light: Color(rgba: 0x6b6e_7bff), dark: Color(rgba: 0x9294_a0ff)
)
fileprivate static let tertiaryText = Color(
light: Color(rgba: 0x6b6e_7bff), dark: Color(rgba: 0x6d70_7dff)
)
fileprivate static let background = Color(
light: .white, dark: Color(rgba: 0x1819_1dff)
)
fileprivate static let secondaryBackground = Color(
light: Color(rgba: 0xf7f7_f9ff), dark: Color(rgba: 0x2526_2aff)
)
fileprivate static let link = Color(
light: Color(rgba: 0x2c65_cfff), dark: Color(rgba: 0x4c8e_f8ff)
)
fileprivate static let border = Color(
light: Color(rgba: 0xe4e4_e8ff), dark: Color(rgba: 0x4244_4eff)
)
fileprivate static let divider = Color(
light: Color(rgba: 0xd0d0_d3ff), dark: Color(rgba: 0x3334_38ff)
)
fileprivate static let checkbox = Color(rgba: 0xb9b9_bbff)
fileprivate static let checkboxBackground = Color(rgba: 0xeeee_efff)
extension Color.MD {
struct GithubTheme {
fileprivate static let text = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0x0606_06ff), dark: Color.MD.rgbaColor(rgba: 0xfbfb_fcff)
)
fileprivate static let secondaryText = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0x6b6e_7bff), dark: Color.MD.rgbaColor(rgba: 0x9294_a0ff)
)
fileprivate static let tertiaryText = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0x6b6e_7bff), dark: Color.MD.rgbaColor(rgba: 0x6d70_7dff)
)
fileprivate static let background = Color.MD.displayMode(
light: .white, dark: Color.MD.rgbaColor(rgba: 0x1819_1dff)
)
fileprivate static let secondaryBackground = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0xf7f7_f9ff), dark: Color.MD.rgbaColor(rgba: 0x2526_2aff)
)
fileprivate static let link = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0x2c65_cfff), dark: Color.MD.rgbaColor(rgba: 0x4c8e_f8ff)
)
fileprivate static let border = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0xe4e4_e8ff), dark: Color.MD.rgbaColor(rgba: 0x4244_4eff)
)
fileprivate static let divider = Color.MD.displayMode(
light: Color.MD.rgbaColor(rgba: 0xd0d0_d3ff), dark: Color.MD.rgbaColor(rgba: 0x3334_38ff)
)
fileprivate static let checkbox = Color.MD.rgbaColor(rgba: 0xb9b9_bbff)
fileprivate static let checkboxBackground = Color.MD.rgbaColor(rgba: 0xeeee_efff)
}
}
84 changes: 43 additions & 41 deletions Sources/MarkdownUI/Utility/Color+RGBA.swift
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
import SwiftUI

extension Color {
/// Creates a constant color from an RGBA value.
/// - Parameter rgba: A 32-bit value that represents the red, green, blue, and alpha components of the color.
public init(rgba: UInt32) {
self.init(
red: CGFloat((rgba & 0xff00_0000) >> 24) / 255.0,
green: CGFloat((rgba & 0x00ff_0000) >> 16) / 255.0,
blue: CGFloat((rgba & 0x0000_ff00) >> 8) / 255.0,
opacity: CGFloat(rgba & 0x0000_00ff) / 255.0
)
}

/// Creates a context-dependent color with different values for light and dark appearances.
/// - Parameters:
/// - light: The light appearance color value.
/// - dark: The dark appearance color value.
public init(light: @escaping @autoclosure () -> Color, dark: @escaping @autoclosure () -> Color) {
#if os(watchOS)
self = dark()
#elseif canImport(UIKit)
self.init(
uiColor: .init { traitCollection in
switch traitCollection.userInterfaceStyle {
case .unspecified, .light:
return UIColor(light())
case .dark:
return UIColor(dark())
@unknown default:
return UIColor(light())
}
struct MD {
/// Creates a constant color from an RGBA value.
/// - Parameter rgba: A 32-bit value that represents the red, green, blue, and alpha components of the color.
static func rgbaColor(rgba: UInt32) -> Color {
Color.init(
red: CGFloat((rgba & 0xff00_0000) >> 24) / 255.0,
green: CGFloat((rgba & 0x00ff_0000) >> 16) / 255.0,
blue: CGFloat((rgba & 0x0000_ff00) >> 8) / 255.0,
opacity: CGFloat(rgba & 0x0000_00ff) / 255.0
)
}
)
#elseif canImport(AppKit)
self.init(
nsColor: .init(name: nil) { appearance in
if appearance.bestMatch(from: [.aqua, .darkAqua]) == .aqua {
return NSColor(light())
} else {
return NSColor(dark())
}

/// Creates a context-dependent color with different values for light and dark appearances.
/// - Parameters:
/// - light: The light appearance color value.
/// - dark: The dark appearance color value.
static func displayMode(light: @escaping @autoclosure () -> Color, dark: @escaping @autoclosure () -> Color) -> Color {
#if os(watchOS)
return dark()
#elseif canImport(UIKit)
return Color.init(
uiColor: .init { traitCollection in
switch traitCollection.userInterfaceStyle {
case .unspecified, .light:
return UIColor(light())
case .dark:
return UIColor(dark())
@unknown default:
return UIColor(light())
}
}
)
#elseif canImport(AppKit)
return Color.init(
nsColor: .init(name: nil) { appearance in
if appearance.bestMatch(from: [.aqua, .darkAqua]) == .aqua {
return NSColor(light())
} else {
return NSColor(dark())
}
}
)
#endif
}
)
#endif
}
}
}