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

NameHueUsage
Purple285Primary brand color, backgrounds
Orange55Warm accent, bottom-left glow
Blue235Cool accent, top-right glow

Variations

VariationLightnessChromaUsage
Vivid0.680.18Gradients, accents
Standard0.55790.1147Tag-compatible
Deep0.420.14Backgrounds, shadows

Hex Values

ColorOKLCHHex
Purple Vividoklch(0.68 0.18 285)#9083FF
Purple Deepoklch(0.42 0.14 285)#483C95
Orange Vividoklch(0.68 0.18 55)#E97300
Blue Vividoklch(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:

  1. Base layer: Purple Deep (#483C95)
  2. Bottom-left radial: Orange Vivid (#E97300) fading to transparent
  3. 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 utilities
  • TagColorGenerator.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()
    }
}
  1. Extract alphabetic characters from tag name
  2. Use first detected alphabet (Latin priority, then Cyrillic, etc.)
  3. 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
  4. Return midpoint of final range

Supported Alphabets

AlphabetCharactersSizeExample
Latina-z26work, 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ജോലി
Digits0-910123 (fallback)

Fallbacks

  1. If tag has Latin letters → use Latin alphabet
  2. Else if Cyrillic → use Cyrillic
  3. … (check each alphabet in order)
  4. If only digits → use digit alphabet (36° per digit)
  5. 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 NameApprox HueColor Family
admin~0Red
backend~14Orange
development~42Yellow
meeting~173Cyan
personal~214Blue
work~319Purple
работа~165Cyan (Cyrillic)
123~54Yellow (digits)

Backward Compatibility

  • Existing tags keep their stored hex colors
  • New tags get algorithmically generated colors
  • Tag.color field 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 LMS
  • toRGB() - Apply gamma correction
  • toHex() - Format as “#RRGGBB”

Sources

Related