Module: Studio::ColorScale

Defined in:
lib/studio/color_scale.rb

Constant Summary collapse

LIGHT_RATIOS =

Mix ratios: how much white (lighter) or black (darker) to mix

{
  50  => 0.95,
  100 => 0.85,
  200 => 0.70,
  300 => 0.50,
  400 => 0.30
}.freeze
DARK_RATIOS =
{
  600 => 0.15,
  700 => 0.30,
  800 => 0.45,
  900 => 0.60
}.freeze

Class Method Summary collapse

Class Method Details

.contrast_ratio(a, b) ⇒ Object

WCAG contrast ratio between two hex colors (1.0..21.0).



83
84
85
86
# File 'lib/studio/color_scale.rb', line 83

def self.contrast_ratio(a, b)
  hi, lo = [relative_luminance(a), relative_luminance(b)].minmax.reverse
  (hi + 0.05) / (lo + 0.05)
end

.darken(hex, amount) ⇒ Object



53
54
55
56
57
58
59
60
# File 'lib/studio/color_scale.rb', line 53

def self.darken(hex, amount)
  r, g, b = hex_to_rgb(hex)
  rgb_to_hex(
    (r * (1 - amount)).round,
    (g * (1 - amount)).round,
    (b * (1 - amount)).round
  )
end

.generate(hex) ⇒ Object

Generate a full 50-900 shade scale from a base hex color. 500 = base color, lighter shades mix toward white, darker toward black.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/studio/color_scale.rb', line 21

def self.generate(hex)
  r, g, b = hex_to_rgb(hex)
  scale = { 500 => hex.upcase }

  LIGHT_RATIOS.each do |shade, ratio|
    scale[shade] = rgb_to_hex(
      (r + (255 - r) * ratio).round,
      (g + (255 - g) * ratio).round,
      (b + (255 - b) * ratio).round
    )
  end

  DARK_RATIOS.each do |shade, ratio|
    scale[shade] = rgb_to_hex(
      (r * (1 - ratio)).round,
      (g * (1 - ratio)).round,
      (b * (1 - ratio)).round
    )
  end

  scale.sort.to_h
end

.hex_to_rgb(hex) ⇒ Object



62
63
64
65
66
67
68
69
# File 'lib/studio/color_scale.rb', line 62

def self.hex_to_rgb(hex)
  hex = hex.delete("#")
  [
    hex[0..1].to_i(16),
    hex[2..3].to_i(16),
    hex[4..5].to_i(16)
  ]
end

.lighten(hex, amount) ⇒ Object



44
45
46
47
48
49
50
51
# File 'lib/studio/color_scale.rb', line 44

def self.lighten(hex, amount)
  r, g, b = hex_to_rgb(hex)
  rgb_to_hex(
    (r + (255 - r) * amount).round,
    (g + (255 - g) * amount).round,
    (b + (255 - b) * amount).round
  )
end

.relative_luminance(hex) ⇒ Object

WCAG 2.x relative luminance (sRGB linearized).



76
77
78
79
80
# File 'lib/studio/color_scale.rb', line 76

def self.relative_luminance(hex)
  r, g, b = hex_to_rgb(hex).map { |c| c / 255.0 }
  lin = [r, g, b].map { |c| c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055)**2.4 }
  (0.2126 * lin[0]) + (0.7152 * lin[1]) + (0.0722 * lin[2])
end

.rgb_to_hex(r, g, b) ⇒ Object



71
72
73
# File 'lib/studio/color_scale.rb', line 71

def self.rgb_to_hex(r, g, b)
  "#%02X%02X%02X" % [r.clamp(0, 255), g.clamp(0, 255), b.clamp(0, 255)]
end

.with_opacity(hex, opacity) ⇒ Object



88
89
90
91
# File 'lib/studio/color_scale.rb', line 88

def self.with_opacity(hex, opacity)
  r, g, b = hex_to_rgb(hex)
  "rgba(#{r},#{g},#{b},#{opacity})"
end