110 lines
4.2 KiB
Swift
110 lines
4.2 KiB
Swift
import SwiftUI
|
|
|
|
enum ClaudeTheme {
|
|
|
|
enum Palette {
|
|
static let canvas = Color(red: 0.965, green: 0.953, blue: 0.929)
|
|
static let surface = Color(red: 0.984, green: 0.976, blue: 0.957)
|
|
static let surfaceRaised = Color.white
|
|
static let border = Color(red: 0.890, green: 0.871, blue: 0.831)
|
|
static let divider = Color(red: 0.918, green: 0.902, blue: 0.867)
|
|
|
|
static let textPrimary = Color(red: 0.122, green: 0.118, blue: 0.110)
|
|
static let textSecondary = Color(red: 0.412, green: 0.392, blue: 0.357)
|
|
static let textTertiary = Color(red: 0.612, green: 0.588, blue: 0.541)
|
|
|
|
static let accent = Color(red: 0.851, green: 0.459, blue: 0.341)
|
|
static let accentSoft = Color(red: 0.961, green: 0.882, blue: 0.847)
|
|
static let success = Color(red: 0.380, green: 0.604, blue: 0.471)
|
|
static let danger = Color(red: 0.776, green: 0.286, blue: 0.286)
|
|
}
|
|
|
|
enum Spacing {
|
|
static let xs: CGFloat = 4
|
|
static let sm: CGFloat = 8
|
|
static let md: CGFloat = 12
|
|
static let lg: CGFloat = 16
|
|
static let xl: CGFloat = 24
|
|
static let xxl: CGFloat = 32
|
|
}
|
|
|
|
enum Radius {
|
|
static let sm: CGFloat = 8
|
|
static let md: CGFloat = 12
|
|
static let lg: CGFloat = 16
|
|
static let pill: CGFloat = 999
|
|
}
|
|
|
|
enum Typography {
|
|
static let display = Font.system(size: 28, weight: .semibold, design: .serif)
|
|
static let title = Font.system(size: 22, weight: .semibold, design: .serif)
|
|
static let headline = Font.system(size: 17, weight: .semibold, design: .default)
|
|
static let body = Font.system(size: 16, weight: .regular, design: .default)
|
|
static let callout = Font.system(size: 15, weight: .regular, design: .default)
|
|
static let caption = Font.system(size: 13, weight: .regular, design: .default)
|
|
static let mono = Font.system(size: 13, weight: .regular, design: .monospaced)
|
|
}
|
|
}
|
|
|
|
struct ClaudeBackground: ViewModifier {
|
|
func body(content: Content) -> some View {
|
|
content
|
|
.background(ClaudeTheme.Palette.canvas.ignoresSafeArea())
|
|
}
|
|
}
|
|
|
|
struct ClaudeCard: ViewModifier {
|
|
var padding: CGFloat = ClaudeTheme.Spacing.lg
|
|
func body(content: Content) -> some View {
|
|
content
|
|
.padding(padding)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md, style: .continuous)
|
|
.fill(ClaudeTheme.Palette.surface)
|
|
)
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md, style: .continuous)
|
|
.strokeBorder(ClaudeTheme.Palette.border, lineWidth: 0.5)
|
|
)
|
|
}
|
|
}
|
|
|
|
struct ClaudePrimaryButton: ButtonStyle {
|
|
func makeBody(configuration: Configuration) -> some View {
|
|
configuration.label
|
|
.font(ClaudeTheme.Typography.headline)
|
|
.foregroundStyle(Color.white)
|
|
.frame(maxWidth: .infinity)
|
|
.padding(.vertical, 14)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md, style: .continuous)
|
|
.fill(ClaudeTheme.Palette.accent.opacity(configuration.isPressed ? 0.85 : 1))
|
|
)
|
|
}
|
|
}
|
|
|
|
struct ClaudeSecondaryButton: ButtonStyle {
|
|
func makeBody(configuration: Configuration) -> some View {
|
|
configuration.label
|
|
.font(ClaudeTheme.Typography.headline)
|
|
.foregroundStyle(ClaudeTheme.Palette.textPrimary)
|
|
.frame(maxWidth: .infinity)
|
|
.padding(.vertical, 14)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md, style: .continuous)
|
|
.fill(ClaudeTheme.Palette.surfaceRaised.opacity(configuration.isPressed ? 0.7 : 1))
|
|
)
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: ClaudeTheme.Radius.md, style: .continuous)
|
|
.strokeBorder(ClaudeTheme.Palette.border, lineWidth: 1)
|
|
)
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
func claudeBackground() -> some View { modifier(ClaudeBackground()) }
|
|
func claudeCard(padding: CGFloat = ClaudeTheme.Spacing.lg) -> some View {
|
|
modifier(ClaudeCard(padding: padding))
|
|
}
|
|
}
|