Color Palette
Overview
Colors in Minuta are based on the OKLCH color space for perceptual uniformity. This includes:
- Tag colors: Deterministically generated from tag names
- Brand colors: Fixed palette for app icon, buttons, and gradients
Brand Palette
The brand palette uses three harmonious hues with consistent OKLCH parameters.
Hues
| Name | Hue | Usage |
|---|---|---|
| Purple | 285 | Primary brand color, backgrounds |
| Orange | 55 | Warm accent, bottom-left glow |
| Blue | 235 | Cool accent, top-right glow |
Variations
| Variation | Lightness | Chroma | Usage |
|---|---|---|---|
| Vivid | 0.68 | 0.18 | Gradients, accents |
| Standard | 0.5579 | 0.1147 | Tag-compatible |
| Deep | 0.42 | 0.14 | Backgrounds, shadows |
Hex Values
| Color | OKLCH | Hex |
|---|---|---|
| Purple Vivid | oklch(0.68 0.18 285) | #9083FF |
| Purple Deep | oklch(0.42 0.14 285) | #483C95 |
| Orange Vivid | oklch(0.68 0.18 55) | #E97300 |
| Blue Vivid | oklch(0.68 0.18 235) | #00A6F6 |
Usage in Code
// OKLCH.swift - brand color accessors
OKLCH.purpleVivid // oklch(0.68 0.18 285)
OKLCH.purpleDeep // oklch(0.42 0.14 285)
OKLCH.orangeVivid // oklch(0.68 0.18 55)
OKLCH.blueVivid // oklch(0.68 0.18 235)
// Color+Hex.swift - SwiftUI colors
Color.brandPurpleVivid
Color.brandPurpleDeep
Color.brandOrangeVivid
Color.brandBlueVivid App Icon Gradient
The app icon uses a layered gradient effect:
- Base layer: Purple Deep (
#483C95) - Bottom-left radial: Orange Vivid (
#E97300) fading to transparent - Top-right radial: Blue Vivid (
#00A6F6) fading to transparent
Tag Colors
Tag colors are deterministically generated from tag names using OKLCH color space. The algorithm maps tag names to hues on the color wheel, creating smooth gradients when tags are sorted alphabetically.
Why OKLCH
- Perceptually uniform: Equal steps in OKLCH values produce equal perceived differences
- Predictable: Same tag name always produces same color
- Wide gamut: Supports more colors than sRGB
- Gradient-friendly: Alphabetically sorted tags form smooth color progressions
Implementation
Location: Shared/Sources/MinutaShared/Utilities/
OKLCH.swift- Color space conversion utilitiesTagColorGenerator.swift- Name-to-color algorithm
Base Color
All tag colors use the same lightness and chroma, varying only in hue:
oklch(0.5579 0.1147 H) - Lightness (L): 0.5579 - mid-range, works on both light/dark backgrounds
- Chroma (C): 0.1147 - moderate saturation, not too vibrant
- Hue (H): 0-360 degrees, computed from tag name
Algorithm
public struct TagColorGenerator {
public static func color(for tagName: String) -> String {
let hue = computeHue(from: tagName)
let oklch = OKLCH.tagColor(hue: hue)
return oklch.toHex()
}
} - Extract alphabetic characters from tag name
- Use first detected alphabet (Latin priority, then Cyrillic, etc.)
- Hierarchically subdivide 360-degree hue range:
- First letter picks 1/26th of range (for Latin)
- Second letter subdivides that into 1/26th
- Continue until characters exhausted
- Return midpoint of final range
Supported Alphabets
| Alphabet | Characters | Size | Example |
|---|---|---|---|
| Latin | a-z | 26 | work, meeting |
| Cyrillic | а-я + ё | 33 | работа |
| Greek | α-ω | 24 | εργασία |
| Hebrew | א-ת | 27 | שלום |
| Arabic | ا-ي | 28 | عمل |
| Georgian | ა-ჰ | 33 | სამუშაო |
| Armenian | ա-ֆ | 38 | աdelays |
| Thai | ก-ฮ | 44 | งาน |
| Devanagari | अ-ह | 46 | काम |
| Bengali | অ-হ | 43 | কাজ |
| Tamil | அ-ஹ | 35 | வேலை |
| Telugu | అ-హ | 41 | పని |
| Kannada | ಅ-ಹ | 41 | ಕೆಲಸ |
| Malayalam | അ-ഹ | 41 | ജോലി |
| Digits | 0-9 | 10 | 123 (fallback) |
Fallbacks
- If tag has Latin letters → use Latin alphabet
- Else if Cyrillic → use Cyrillic
- … (check each alphabet in order)
- If only digits → use digit alphabet (36° per digit)
- If no recognized chars → hash-based hue
Usage
Generate color for tag name
let hex = TagColorGenerator.color(for: "work") // "#7A6B8E" (deterministic) Get OKLCH color object
let oklch = TagColorGenerator.oklchColor(for: "work")
let rgb = oklch.toRGB() // (r: 0.48, g: 0.42, b: 0.56) Compute hue only
let hue = TagColorGenerator.computeHue(from: "work") // ~319.5 Text Contrast
When displaying text on colored backgrounds, use Color.contrastingTextColor(for:):
// In Color+Hex.swift
static func contrastingTextColor(for hex: String) -> Color {
// Returns .black or .white based on luminance threshold (0.5)
}
// Or with OKLCH directly
static func contrastingTextColor(for oklch: OKLCH) -> Color {
// Uses OKLCH lightness for more accurate contrast
} Color Examples
| Tag Name | Approx Hue | Color Family |
|---|---|---|
| admin | ~0 | Red |
| backend | ~14 | Orange |
| development | ~42 | Yellow |
| meeting | ~173 | Cyan |
| personal | ~214 | Blue |
| work | ~319 | Purple |
| работа | ~165 | Cyan (Cyrillic) |
| 123 | ~54 | Yellow (digits) |
Backward Compatibility
- Existing tags keep their stored hex colors
- New tags get algorithmically generated colors
Tag.colorfield remains a hex string for storage
OKLCH Conversion Chain
OKLCH → OKLab → Linear RGB → sRGB (gamma) → Hex Key conversions in OKLCH.swift:
toOKLab()- Convert to OKLab (L, a, b)toLinearRGB()- Convert to linear RGB via LMStoRGB()- Apply gamma correctiontoHex()- Format as “#RRGGBB”
Sources
Related
- 201-tag - Tag model (uses color field)
- 306-report - Report charts use tag colors