Class: Unmagic::Color::OKLCH

Inherits:
Unmagic::Color show all
Defined in:
lib/unmagic/color/oklch.rb,
lib/unmagic/color/oklch/gradient/linear.rb

Overview

‘OKLCH` (Lightness, Chroma, Hue) color representation.

## Understanding OKLCH

OKLCH is a modern color space designed to match how humans actually perceive colors. Unlike RGB or even HSL, OKLCH ensures that colors with the same lightness value look equally bright to our eyes, regardless of their hue.

## The Problem with RGB and HSL

In RGB and HSL, pure yellow and pure blue can have the same “lightness” value, but yellow looks much brighter to our eyes. This makes it hard to create consistent-looking color palettes.

OKLCH solves this by being “perceptually uniform” - if you change lightness by ‘0.1`, it looks like the same amount of change whether you’re working with red, green, blue, or any other hue.

## The Three Components

  1. Lightness (‘0.0-1.0`): How bright the color appears

    • ‘0.0` = Black

    • ‘0.5` = Medium brightness

    • ‘1.0` = White

    Unlike HSL, this matches perceived brightness consistently across all hues.

  2. Chroma (‘0.0-0.5`): How colorful/saturated it is

    • ‘0.0` = Gray (no color)

    • ‘0.15` = Moderate color (good for UI)

    • ‘0.3+` = Very vivid (use sparingly)

    Think of it like saturation, but more accurate to perception.

  3. Hue (‘0-360°`): The color itself (same as HSL)

    • ‘0°/360°` = Red

    • ‘120°` = Green

    • ‘240°` = Blue

## Why Use OKLCH?

  • Creating accessible color palettes (ensure consistent contrast)

  • Generating color scales that look evenly spaced

  • Interpolating between colors smoothly

  • Matching colors that “feel” equally bright

## When to Use Each Color Space

  • **RGB**: When working with screens/displays directly

  • **HSL**: When you need intuitive color manipulation

  • OKLCH: When you need perceptually accurate colors (design systems, accessibility)

## Examples

# Parse OKLCH colors
color = Unmagic::Color::OKLCH.parse("oklch(0.65 0.15 240)")  # Medium blue

# Create directly
accessible = Unmagic::Color::OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240)

# Access components
color.lightness  #=> 0.65 (ratio form)
color.chroma.value  #=> 0.15
color.hue.value     #=> 240

# Create perceptually uniform variations
lighter = color.lighten(0.05)  # Looks 5% brighter
less_colorful = color.desaturate(0.03)

# Generate consistent colors
Unmagic::Color::OKLCH.derive("user@example.com".hash)  # Perceptually balanced color

Defined Under Namespace

Modules: Gradient Classes: ParseError

Constant Summary

Constants inherited from Unmagic::Color

Blue, DATA_PATH, Green, Red, VERSION

Constants included from Harmony

Harmony::SCALE_CHROMA_CURVE, Harmony::SCALE_LIGHTNESS_DEFAULT, Harmony::SCALE_LIGHTNESS_SHAPE

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Unmagic::Color

[], #dark?, #light?

Methods included from Harmony

#analogous, #complementary, #monochromatic, #scale, #shades, #split_complementary, #tetradic_rectangle, #tetradic_square, #tints, #tones, #triadic

Constructor Details

#initialize(lightness:, chroma:, hue:, alpha: nil) ⇒ OKLCH

Create a new OKLCH color.

Examples:

Create a medium blue

OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240)

Create a semi-transparent red

OKLCH.new(lightness: 0.60, chroma: 0.25, hue: 30, alpha: 50)

Create a vibrant red

OKLCH.new(lightness: 0.60, chroma: 0.25, hue: 30)

Parameters:

  • lightness (Float)

    Lightness as a ratio (0.0-1.0), clamped to range

  • chroma (Float)

    Chroma intensity (0.0-0.5), clamped to range

  • hue (Numeric)

    Hue in degrees (0-360), wraps around if outside range

  • alpha (Numeric, Color::Alpha, nil) (defaults to: nil)

    Alpha channel (0-100%), defaults to 100 (fully opaque)



97
98
99
100
101
102
103
# File 'lib/unmagic/color/oklch.rb', line 97

def initialize(lightness:, chroma:, hue:, alpha: nil)
  super()
  @lightness = Color::Lightness.new(value: lightness * 100) # Convert 0-1 to percentage
  @chroma = Color::Chroma.new(value: chroma)
  @hue = Color::Hue.new(value: hue)
  @alpha = Color::Alpha.build(alpha) || Color::Alpha::DEFAULT
end

Instance Attribute Details

#alphaObject (readonly)

Returns the value of attribute alpha.



80
81
82
# File 'lib/unmagic/color/oklch.rb', line 80

def alpha
  @alpha
end

#chromaObject (readonly)

Returns the value of attribute chroma.



80
81
82
# File 'lib/unmagic/color/oklch.rb', line 80

def chroma
  @chroma
end

#hueObject (readonly)

Returns the value of attribute hue.



80
81
82
# File 'lib/unmagic/color/oklch.rb', line 80

def hue
  @hue
end

Class Method Details

.build(*args, **kwargs) ⇒ OKLCH

Build an OKLCH color from a string, positional values, or keyword arguments.

Examples:

From string

OKLCH.build("oklch(0.65 0.15 240)")

From positional values

OKLCH.build(0.65, 0.15, 240)

From keyword arguments

OKLCH.build(lightness: 0.65, chroma: 0.15, hue: 240)

Parameters:

  • args (String, Numeric)

    Either a color string or 3 component values

  • kwargs (Hash)

    a customizable set of options

Options Hash (**kwargs):

  • :lightness (Numeric)

    Lightness (0-1)

  • :chroma (Numeric)

    Chroma (0-0.5)

  • :hue (Numeric)

    Hue in degrees (0-360)

Returns:

  • (OKLCH)

    The constructed OKLCH color



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/unmagic/color/oklch.rb', line 207

def build(*args, **kwargs)
  if kwargs.any?
    new(**kwargs)
  elsif args.length == 1
    parse(args[0])
  elsif args.length == 3
    values = args.map { |v| v.is_a?(::String) ? v.to_f : v }
    new(lightness: values[0], chroma: values[1], hue: values[2])
  else
    raise ArgumentError, "Expected 1 or 3 arguments, got #{args.length}"
  end
end

.derive(seed, lightness: 0.58, chroma_range: (0.10..0.18), hue_spread: 997, hue_base: 137.508) ⇒ OKLCH

Generate a deterministic OKLCH color from an integer seed.

Creates perceptually balanced, visually distinct colors. This is particularly effective in OKLCH because the perceptual uniformity ensures all generated colors have consistent perceived brightness and saturation.

The hue distribution uses a golden-angle approach to spread colors evenly and avoid clustering similar hues together.

Examples:

Generate avatar color

OKLCH.derive("user@example.com".hash)

Generate lighter UI colors

OKLCH.derive(12345, lightness: 0.75)

Generate more saturated colors

OKLCH.derive(12345, chroma_range: (0.15..0.25))

Parameters:

  • seed (Integer)

    The seed value (typically from a hash function)

  • lightness (Float) (defaults to: 0.58)

    Fixed lightness value (0.0-1.0, default 0.58)

  • chroma_range (Range) (defaults to: (0.10..0.18))

    Range for chroma variation (default 0.10..0.18)

  • hue_spread (Integer) (defaults to: 997)

    Modulo for hue distribution (default 997)

  • hue_base (Float) (defaults to: 137.508)

    Multiplier for hue calculation (default 137.508 - golden angle)

Returns:

  • (OKLCH)

    A deterministic, perceptually balanced color

Raises:

  • (ArgumentError)

    If seed is not an integer



245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/unmagic/color/oklch.rb', line 245

def derive(seed, lightness: 0.58, chroma_range: (0.10..0.18), hue_spread: 997, hue_base: 137.508)
  raise ArgumentError, "Seed must be an integer" unless seed.is_a?(Integer)

  h32 = seed & 0xFFFFFFFF # Ensure 32-bit

  # Hue: golden-angle style distribution to avoid clusters
  h = (hue_base * (h32 % hue_spread)) % 360

  # Chroma: map a byte into a safe text-friendly range
  c = chroma_range.begin + ((h32 >> 8) & 0xFF) / 255.0 * (chroma_range.end - chroma_range.begin)

  new(lightness: lightness, chroma: c, hue: h)
end

.parse(input) ⇒ OKLCH

Parse an OKLCH color from a string.

Accepts formats:

  • CSS format: “oklch(0.65 0.15 240)” or “oklch(0.65 0.15 240 / 0.5)”

  • Raw values: “0.65 0.15 240”

  • Space-separated values with optional alpha after slash

Examples:

Parse CSS format

OKLCH.parse("oklch(0.65 0.15 240)")

Parse with alpha

OKLCH.parse("oklch(0.65 0.15 240 / 0.5)")

Parse without function wrapper

OKLCH.parse("0.58 0.12 180")

Parameters:

  • input (String)

    The OKLCH color string to parse

Returns:

  • (OKLCH)

    The parsed OKLCH color

Raises:

  • (ParseError)

    If the input format is invalid or values are out of range



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/unmagic/color/oklch.rb', line 140

def parse(input)
  raise ParseError, "Input must be a string" unless input.is_a?(::String)

  # Remove oklch() wrapper if present
  clean = input.gsub(/^oklch\s*\(\s*|\s*\)$/, "").strip

  # Check for alpha with slash separator
  alpha = nil
  if clean.include?("/")
    parts = clean.split("/").map(&:strip)
    raise ParseError, "Invalid format with /: expected 'L C H / alpha'" unless parts.length == 2

    clean = parts[0]
    alpha = Color::Alpha.parse(parts[1])
  end

  # Split values
  parts = clean.split(/\s+/)
  unless parts.length == 3
    raise ParseError, "Expected 3 OKLCH values, got #{parts.length}"
  end

  # Check if all values are numeric
  parts.each_with_index do |v, i|
    unless v.match?(/\A\d+(\.\d+)?\z/)
      component = ["lightness", "chroma", "hue"][i]
      raise ParseError, "Invalid #{component} value: #{v.inspect} (must be a number)"
    end
  end

  # Convert to floats
  l = parts[0].to_f
  c = parts[1].to_f
  h = parts[2].to_f

  # Validate ranges
  if l < 0 || l > 1
    raise ParseError, "Lightness must be between 0 and 1, got #{l}"
  end

  if c < 0 || c > 0.5
    raise ParseError, "Chroma must be between 0 and 0.5, got #{c}"
  end

  if h < 0 || h >= 360
    raise ParseError, "Hue must be between 0 and 360, got #{h}"
  end

  new(lightness: l, chroma: c, hue: h, alpha: alpha)
end

Instance Method Details

#==(other) ⇒ Boolean

Check if two OKLCH colors are equal.

Parameters:

  • other (Object)

    The object to compare with

Returns:

  • (Boolean)

    true if both colors have the same OKLCH values



540
541
542
543
544
545
# File 'lib/unmagic/color/oklch.rb', line 540

def ==(other)
  other.is_a?(Unmagic::Color::OKLCH) &&
    lightness == other.lightness &&
    chroma == other.chroma &&
    hue == other.hue
end

#blend(other, amount = 0.5) ⇒ OKLCH

Blend this color with another color in OKLCH space.

Blending in OKLCH produces perceptually smooth color transitions. Uses shortest-arc hue interpolation to avoid going the long way around the color wheel.

Examples:

Create a perceptually smooth gradient

blue = OKLCH.new(lightness: 0.60, chroma: 0.15, hue: 240)
red = OKLCH.new(lightness: 0.60, chroma: 0.15, hue: 30)
purple = blue.blend(red, 0.5)

Parameters:

  • other (Color)

    The color to blend with (automatically converted to OKLCH)

  • amount (Float) (defaults to: 0.5)

    How much of the other color to mix in (0.0-1.0)

Returns:

  • (OKLCH)

    A new OKLCH color that is a blend of the two



459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/unmagic/color/oklch.rb', line 459

def blend(other, amount = 0.5)
  amount = amount.to_f.clamp(0, 1)
  other_oklch = other.respond_to?(:to_oklch) ? other.to_oklch : other

  # Blend in OKLCH space with shortest-arc hue interpolation
  dh = (((other_oklch.hue.value - @hue.value + 540) % 360) - 180)
  new_hue = (@hue.value + dh * amount) % 360
  new_lightness = lightness + (other_oklch.lightness - lightness) * amount
  new_chroma = @chroma.value + (other_oklch.chroma.value - @chroma.value) * amount

  self.class.new(
    lightness: new_lightness,
    chroma: new_chroma,
    hue: new_hue,
    alpha: @alpha.value * (1 - amount) + other_oklch.alpha.value * amount,
  )
end

#clamp_to_gamutOKLCH

Pull this color into the sRGB gamut by reducing chroma.

Holds lightness and hue fixed and binary-searches chroma downward until the color is displayable. An already-displayable color is returned unchanged. This is the perceptually correct way to map an out-of-gamut OKLCH color — clipping RGB channels instead shifts hue.

Examples:

vivid = OKLCH.new(lightness: 0.95, chroma: 0.3, hue: 140)
vivid.clamp_to_gamut # => a displayable, lower-chroma green

Returns:

  • (OKLCH)

    An in-gamut color with the same lightness and hue



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/unmagic/color/oklch.rb', line 338

def clamp_to_gamut
  return self if in_gamut?

  lo = 0.0
  hi = @chroma.value
  20.times do
    mid = (lo + hi) / 2.0
    candidate = self.class.new(lightness: @lightness.to_ratio, chroma: mid, hue: @hue.value, alpha: @alpha.value)
    if candidate.in_gamut?
      lo = mid
    else
      hi = mid
    end
  end

  self.class.new(lightness: @lightness.to_ratio, chroma: lo, hue: @hue.value, alpha: @alpha.value)
end

#darken(amount = 0.03) ⇒ OKLCH

Create a darker version by decreasing lightness.

Examples:

Make a color perceptually 5% darker

color = OKLCH.new(lightness: 0.70, chroma: 0.15, hue: 120)
darker = color.darken(0.05)

Parameters:

  • amount (Float) (defaults to: 0.03)

    How much to decrease lightness (default 0.03)

Returns:

  • (OKLCH)

    A darker version of this color



401
402
403
404
405
# File 'lib/unmagic/color/oklch.rb', line 401

def darken(amount = 0.03)
  current_lightness = @lightness.to_ratio
  new_lightness = clamp01(current_lightness - amount)
  self.class.new(lightness: new_lightness, chroma: @chroma.value, hue: @hue.value)
end

#desaturate(amount = 0.02) ⇒ OKLCH

Create a less saturated version by decreasing chroma.

Examples:

Make a color more muted

vivid = OKLCH.new(lightness: 0.65, chroma: 0.20, hue: 30)
muted = vivid.desaturate(0.10)

Parameters:

  • amount (Float) (defaults to: 0.02)

    How much to decrease chroma (default 0.02)

Returns:

  • (OKLCH)

    A less saturated version of this color



428
429
430
431
# File 'lib/unmagic/color/oklch.rb', line 428

def desaturate(amount = 0.02)
  new_chroma = [@chroma.value - amount, 0.0].max
  self.class.new(lightness: @lightness.to_ratio, chroma: new_chroma, hue: @hue.value)
end

#in_gamut?(epsilon = 1e-4) ⇒ Boolean

Whether this color can be displayed in the sRGB gamut.

OKLCH can describe colors no sRGB monitor can show — typically high-chroma colors near very light or very dark lightness, where the sRGB gamut narrows. This returns false for those.

Parameters:

  • epsilon (Float) (defaults to: 1e-4)

    Tolerance for channels sitting just past 0 or 1

Returns:

  • (Boolean)

    true if every linear sRGB channel falls within [0, 1]



322
323
324
# File 'lib/unmagic/color/oklch.rb', line 322

def in_gamut?(epsilon = 1e-4)
  to_linear_srgb.all? { |channel| channel >= -epsilon && channel <= 1.0 + epsilon }
end

#lighten(amount = 0.03) ⇒ OKLCH

Create a lighter version by increasing lightness.

In OKLCH, lightness changes are perceptually uniform, so adding 0.05 will look like the same brightness increase regardless of the hue.

Examples:

Make a color perceptually 5% brighter

color = OKLCH.new(lightness: 0.60, chroma: 0.15, hue: 240)
lighter = color.lighten(0.05)

Parameters:

  • amount (Float) (defaults to: 0.03)

    How much to increase lightness (default 0.03)

Returns:

  • (OKLCH)

    A lighter version of this color



387
388
389
390
391
# File 'lib/unmagic/color/oklch.rb', line 387

def lighten(amount = 0.03)
  current_lightness = @lightness.to_ratio
  new_lightness = clamp01(current_lightness + amount)
  self.class.new(lightness: new_lightness, chroma: @chroma.value, hue: @hue.value, alpha: @alpha.value)
end

#lightnessFloat

Get the lightness as a ratio (0.0-1.0).

This overrides the attr_reader to return the ratio form, which is the standard way to work with OKLCH lightness.

Returns:

  • (Float)

    Lightness from 0.0 (black) to 1.0 (white)



111
# File 'lib/unmagic/color/oklch.rb', line 111

def lightness = @lightness.to_ratio

#lightness_percentageFloat

Get the lightness as a percentage (0.0-100.0).

Helper method for when you need the percentage form instead of ratio.

Returns:

  • (Float)

    Lightness from 0.0 to 100.0



118
# File 'lib/unmagic/color/oklch.rb', line 118

def lightness_percentage = @lightness.value

#luminanceFloat

Calculate the relative luminance.

In OKLCH, the lightness value directly represents perceptual luminance, so we can use it as-is.

Returns:

  • (Float)

    Luminance from 0.0 (black) to 1.0 (white)



371
372
373
374
# File 'lib/unmagic/color/oklch.rb', line 371

def luminance
  # OKLCH lightness is perceptually uniform, so we can use it directly
  @lightness.to_ratio # Return 0-1 range
end

#pretty_print(pp) ⇒ Object

Pretty print support with colored swatch in class name.

Outputs standard Ruby object format with a colored block character embedded in the class name area. Note: @lightness is shown via its inspect method since it’s a Lightness percentage object.

Examples:

oklch = OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 30)
pp oklch
# Outputs: #<Unmagic::Color::OKLCH[█] @lightness=... @chroma=0.15 @hue=30>
# (with colored █ block)

Parameters:

  • pp (PrettyPrint)

    The pretty printer instance



585
586
587
# File 'lib/unmagic/color/oklch.rb', line 585

def pretty_print(pp)
  pp.text("#<#{self.class.name}[\x1b[#{to_ansi(mode: :truecolor)}m█\x1b[0m] @lightness=#{@lightness.inspect} @chroma=#{@chroma.value.round(2)} @hue=#{@hue.value.round}>")
end

#rotate(amount = 10) ⇒ OKLCH

Rotate the hue by a specified amount.

Examples:

Shift to an analogous color

blue = OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240)
blue_green = blue.rotate(30)

Parameters:

  • amount (Numeric) (defaults to: 10)

    Degrees to rotate the hue (default 10)

Returns:

  • (OKLCH)

    A color with the hue rotated



441
442
443
444
# File 'lib/unmagic/color/oklch.rb', line 441

def rotate(amount = 10)
  new_hue = (@hue.value + amount) % 360
  self.class.new(lightness: @lightness.to_ratio, chroma: @chroma.value, hue: new_hue)
end

#saturate(amount = 0.02) ⇒ OKLCH

Create a more saturated version by increasing chroma.

Examples:

Make a color more vivid

muted = OKLCH.new(lightness: 0.65, chroma: 0.10, hue: 180)
vivid = muted.saturate(0.05)

Parameters:

  • amount (Float) (defaults to: 0.02)

    How much to increase chroma (default 0.02)

Returns:

  • (OKLCH)

    A more saturated version of this color



415
416
417
418
# File 'lib/unmagic/color/oklch.rb', line 415

def saturate(amount = 0.02)
  new_chroma = [@chroma.value + amount, 0.4].min
  self.class.new(lightness: @lightness.to_ratio, chroma: new_chroma, hue: @hue.value)
end

#to_ansi(layer: :foreground, mode: :truecolor) ⇒ String

Convert to ANSI SGR color code.

Converts to RGB first, then generates the ANSI code.

Examples:

color = OKLCH.new(lightness: 0.60, chroma: 0.25, hue: 30)
color.to_ansi
# => "38;2;..." (true color format)

Parameters:

  • layer (Symbol) (defaults to: :foreground)

    Whether to generate foreground (:foreground) or background (:background) code

  • mode (Symbol) (defaults to: :truecolor)

    Color format mode (:truecolor, :palette256, :palette16)

Returns:

  • (String)

    ANSI SGR code like “31” or “38;2;255;0;0”



568
569
570
# File 'lib/unmagic/color/oklch.rb', line 568

def to_ansi(layer: :foreground, mode: :truecolor)
  to_rgb.to_ansi(layer: layer, mode: mode)
end

#to_css_color_mix(bg_css = "var(--bg)", a_pct: 72, bg_pct: 28) ⇒ String

Create a CSS color-mix() expression.

Generates a CSS color-mix expression that blends this color with a background color (typically a CSS variable).

Examples:

Mix with background

color = Unmagic::Color::OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240)
color.to_css_color_mix
# => "color-mix(in oklch, oklch(0.6500 0.1500 240.00) 72%, var(--bg) 28%)"

Custom background and percentages

color = Unmagic::Color::OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240)
color.to_css_color_mix("#FFFFFF", a_pct: 50, bg_pct: 50)
# => "color-mix(in oklch, oklch(0.6500 0.1500 240.00) 50%, #FFFFFF 50%)"

Parameters:

  • bg_css (String) (defaults to: "var(--bg)")

    The background color CSS (default “var(–bg)”)

  • a_pct (Integer) (defaults to: 72)

    Percentage of this color (default 72)

  • bg_pct (Integer) (defaults to: 28)

    Percentage of background color (default 28)

Returns:

  • (String)

    CSS color-mix expression



532
533
534
# File 'lib/unmagic/color/oklch.rb', line 532

def to_css_color_mix(bg_css = "var(--bg)", a_pct: 72, bg_pct: 28)
  "color-mix(in oklch, #{to_css_oklch} #{a_pct}%, #{bg_css} #{bg_pct}%)"
end

#to_css_oklchString

Convert to CSS oklch() function format.

Examples:

Fully opaque

color = OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240)
color.to_css_oklch
# => "oklch(0.6500 0.1500 240.00)"

Semi-transparent

color = OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240, alpha: 50)
color.to_css_oklch
# => "oklch(0.6500 0.1500 240.00 / 0.5)"

Returns:

  • (String)

    CSS string like “oklch(0.6500 0.1500 240.00)” or “oklch(0.6500 0.1500 240.00 / 0.5)”



490
491
492
493
494
495
496
# File 'lib/unmagic/color/oklch.rb', line 490

def to_css_oklch
  if @alpha.value < 100
    format("oklch(%.4f %.4f %.2f / %s)", @lightness.to_ratio, @chroma.value, @hue.value, @alpha.to_css)
  else
    format("oklch(%.4f %.4f %.2f)", @lightness.to_ratio, @chroma.value, @hue.value)
  end
end

#to_css_varsString

Convert to CSS custom properties (variables).

Outputs the color as CSS variables for lightness, chroma, and hue that can be manipulated or mixed at runtime in CSS.

Examples:

color = OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240)
color.to_css_vars
# => "--ul:0.6500;--uc:0.1500;--uh:240.00;"

Returns:

  • (String)

    CSS variables like “–ul:0.6500;–uc:0.1500;–uh:240.00;”



509
510
511
# File 'lib/unmagic/color/oklch.rb', line 509

def to_css_vars
  format("--ul:%.4f;--uc:%.4f;--uh:%.2f;", @lightness.to_ratio, @chroma.value, @hue.value)
end

#to_hexString

Convert to hex string.

Converts via RGB as an intermediate step.

Returns:

  • (String)

    The color as a hex string (e.g., “#ff5733”)



361
362
363
# File 'lib/unmagic/color/oklch.rb', line 361

def to_hex
  to_rgb.to_hex
end

#to_hslHSL

Convert to HSL color space.

Converts via RGB as an intermediate step.

Returns:

  • (HSL)

    The color in HSL color space



274
275
276
# File 'lib/unmagic/color/oklch.rb', line 274

def to_hsl
  to_rgb.to_hsl
end

#to_oklabArray<Float>

Convert to the OKLab color space as ‘[lightness, a, b]`.

OKLab is the cartesian form of OKLCH: ‘a` is the green–red axis and `b` is the blue–yellow axis. The Euclidean distance between two OKLab triples is a perceptual color difference (ΔE).

Examples:

color = OKLCH.new(lightness: 0.65, chroma: 0.15, hue: 240)
l, a, b = color.to_oklab

Returns:

  • (Array<Float>)

    The ‘[L, a, b]` OKLab coordinates



289
290
291
292
293
# File 'lib/unmagic/color/oklch.rb', line 289

def to_oklab
  h_rad = @hue.value * Math::PI / 180.0
  c = @chroma.value
  [@lightness.to_ratio, c * Math.cos(h_rad), c * Math.sin(h_rad)]
end

#to_oklchOKLCH

Convert to OKLCH color space.

Since this is already an OKLCH color, returns self.

Returns:



265
266
267
# File 'lib/unmagic/color/oklch.rb', line 265

def to_oklch
  self
end

#to_rgbRGB

Convert to RGB color space.

Uses the full OKLab color-science pipeline: OKLCH → OKLab → linear sRGB → gamma-encoded sRGB. Colors outside the sRGB gamut are clipped per channel; call #clamp_to_gamut first for a perceptual fit.

Returns:

  • (RGB)

    The color in RGB color space



302
303
304
305
306
307
308
309
310
311
312
# File 'lib/unmagic/color/oklch.rb', line 302

def to_rgb
  require_relative "rgb"

  r, g, b = to_linear_srgb.map { |channel| linear_to_srgb(channel) }
  Unmagic::Color::RGB.new(
    red: (r * 255).round.clamp(0, 255),
    green: (g * 255).round.clamp(0, 255),
    blue: (b * 255).round.clamp(0, 255),
    alpha: @alpha,
  )
end

#to_sString

Convert to string representation.

Returns the CSS oklch() function format.

Returns:

  • (String)

    CSS string like “oklch(0.6500 0.1500 240.00)”



552
553
554
# File 'lib/unmagic/color/oklch.rb', line 552

def to_s
  to_css_oklch
end