class Dvl::Color::Generator

Constants

DARK_BACKGROUND_CUTOFF

Attributes

background_color[RW]
dropdown_background[RW]
dropdown_color[RW]
input_background[RW]
input_background_focus[RW]
input_color[RW]

Public Class Methods

new(background_color) click to toggle source

@param background_color [Chroma::Color or String]

# File lib/dvl/color/generator.rb, line 12
def initialize(background_color)
  self.background_color = background_color

  unless background_color.is_a?(Chroma::Color)
    self.background_color = Chroma.paint(background_color)
  end

  self.input_background, self.input_background_focus, self.input_color = generate_input_colors
  self.dropdown_background, self.dropdown_color = generate_dropdown_colors
end

Public Instance Methods

base_color() click to toggle source
# File lib/dvl/color/generator.rb, line 84
def base_color
  @base_color ||= begin
    text = background_color.dup

    # If the background is dark, we lighten it.
    # If the background is light, we darken it.
    amt = if background_color.brightness < DARK_BACKGROUND_CUTOFF # Dark
            1
          else
            -1
          end

    target = target_contrast_ratio

    while contrast(background_color, text) < target
      new_text = text.lighten(amt)

      # prevent infinite loop
      break if text == new_text

      text = new_text
    end

    # If the background is a conservative color, use a conservative
    # (desaturated) text color.

    if conservative_background_dark? || conservative_background_light?
      hsl = text.hsl
      hsl.s = hsl.s * 0.15
      text = Chroma.paint(hsl)
    end

    text
  end
end
black() click to toggle source
# File lib/dvl/color/generator.rb, line 195
def black
  Chroma.paint('#000')
end
conservative_background_dark?() click to toggle source
# File lib/dvl/color/generator.rb, line 66
def conservative_background_dark?
  background_color.brightness < 52
end
conservative_background_light?() click to toggle source
# File lib/dvl/color/generator.rb, line 70
def conservative_background_light?
  background_color.brightness > 229
end
error_bubble_background() click to toggle source
# File lib/dvl/color/generator.rb, line 171
def error_bubble_background
  if error_color.brightness > 200
    white
  else
    error_color
  end
end
error_bubble_color() click to toggle source
# File lib/dvl/color/generator.rb, line 179
def error_bubble_color
  if error_bubble_background == white
    background_color
  else
    white
  end
end
error_color() click to toggle source
# File lib/dvl/color/generator.rb, line 120
def error_color
  @error_color ||= begin
    error = background_color.dup

    # Spin the color wheel until we land on "red"
    amt = if error.hsl.h > 180
            15
          else
            -15
          end

    while !red?(error)
      error = error.spin(amt)
    end

    # White
    if background_color == white
      error = Chroma.paint('d95b76')
    # Black
    elsif background_color == black
      error = Chroma.paint('d95b76')
    # Grayscale
    elsif error.hsl.s == 0
      error = error.saturate(100)
    # Otherwise, ensure saturation is at least 0.4, or else it's not really red...
    elsif error.hsl.s < 0.4
      hsl = error.hsl
      hsl.s = 0.4
      error = Chroma.paint(hsl)
    elsif conservative_background_light?
      hsl = error.hsl
      hsl.l = hsl.l - 0.4
      error = Chroma.paint(hsl)
    end

    amt = if background_color.brightness < 100
            2
          else
            -2
          end

    while contrast(error, background_color) < 2.5
      new_error = error.lighten(amt)
      break if error == new_error
      error = new_error
    end

    error
  end
end
generate_dropdown_colors() click to toggle source
# File lib/dvl/color/generator.rb, line 187
def generate_dropdown_colors
  if background_color.brightness < DARK_BACKGROUND_CUTOFF
    [base_color, background_color]
  else
    [background_color, base_color]
  end
end
target_contrast_ratio() click to toggle source
# File lib/dvl/color/generator.rb, line 74
def target_contrast_ratio
  if conservative_background_dark?
    13
  elsif conservative_background_light?
    10
  else
    7
  end
end
to_debug_s() click to toggle source
# File lib/dvl/color/generator.rb, line 46
def to_debug_s
  str = []

  to_h.each do |k, v|
    str << "#{k}: #{v}"
  end

  str << "Background brightness: #{background_color.brightness}"

  str << "Ratio: #{contrast(background_color, base_color).round(2)}"
  str << "Error Ratio: #{contrast(background_color, error_color).round(2)}"
  str << "Error Bubble Ratio: #{contrast(error_bubble_background, error_bubble_color).round(2)}"
  str << "Input ratio: #{contrast(input_background, input_color).round(2)}"
  str << "Error satur: #{error_color.hsl.s.round(2)}"
  str << "Conservative light: #{conservative_background_light?}"
  str << "Conservative dark: #{conservative_background_dark?}"

  str.join("<br>")
end
to_h() click to toggle source
# File lib/dvl/color/generator.rb, line 23
def to_h
  {
    backgroundColor: background_color.to_hex,
    baseColor: base_color.to_hex,
    errorColor: error_color.to_hex,
    errorBubbleBackground: error_bubble_background.to_hex,
    errorBubbleColor: error_bubble_color.to_hex,
    inputBackground: input_background.to_hex,
    inputBackgroundFocus: input_background_focus.to_hex,
    inputColor: input_color.to_hex,
    dropdownBackground: dropdown_background.to_hex,
    dropdownColor: dropdown_color.to_hex
  }
end
to_scss() click to toggle source
# File lib/dvl/color/generator.rb, line 38
def to_scss
  ''.tap do |str|
    to_h.each do |k, v|
      str << "$#{k}:#{v};"
    end
  end
end
white() click to toggle source
# File lib/dvl/color/generator.rb, line 199
def white
  Chroma.paint('#fff')
end

Private Instance Methods

contrast(c1, c2) click to toggle source
# File lib/dvl/color/generator.rb, line 233
def contrast(c1, c2)
  ChromaWcagContrast.ratio(c1, c2)
end
generate_input_colors() click to toggle source
# File lib/dvl/color/generator.rb, line 205
def generate_input_colors
  input_background = Dvl::Color::FakeRgba.calculate(background_color, white, 0.4)
  input_background_focus = Dvl::Color::FakeRgba.calculate(background_color, white, 0.7)
  input_color = base_color

  if contrast(input_background, input_color) > 6
    [input_background, input_background_focus, input_color]
  else
    alpha = 0.2
    input_background = Dvl::Color::FakeRgba.calculate(background_color, black, alpha)
    input_background_focus = Dvl::Color::FakeRgba.calculate(background_color, black, alpha + 0.4)

    while contrast(input_background, input_color) < 6
      alpha += 0.01
      break if alpha == 1
      input_background = Dvl::Color::FakeRgba.calculate(background_color, black, alpha)
      input_background_focus = Dvl::Color::FakeRgba.calculate(background_color, black, alpha + 0.4)
    end

    [input_background, input_background_focus, input_color]
  end
end
red?(color) click to toggle source
# File lib/dvl/color/generator.rb, line 228
def red?(color)
  color.hsl.h > 355 ||
  color.hsl.h < 10
end