Class: Sass::Script::Value::Color

Inherits:
Base
  • Object
show all
Defined in:
lib/sass/script/value/color.rb

Overview

A SassScript object representing a CSS color.

A color may be represented internally as RGBA, HSLA, or both. It's originally represented as whatever its input is; if it's created with RGB values, it's represented as RGBA, and if it's created with HSL values, it's represented as HSLA. Once a property is accessed that requires the other representation -- for example, #red for an HSL color -- that component is calculated and cached.

The alpha channel of a color is independent of its RGB or HSL representation. It's always stored, as 1 if nothing else is specified. If only the alpha channel is modified using #with, the cached RGB and HSL values are retained.

Constant Summary collapse

ALTERNATE_COLOR_NAMES =
Sass::Util.map_vals({
    'aqua'                 => 0x00FFFFFF,
    'darkgrey'             => 0xA9A9A9FF,
    'darkslategrey'        => 0x2F4F4FFF,
    'dimgrey'              => 0x696969FF,
    'fuchsia'              => 0xFF00FFFF,
    'grey'                 => 0x808080FF,
    'lightgrey'            => 0xD3D3D3FF,
    'lightslategrey'       => 0x778899FF,
    'slategrey'            => 0x708090FF,
}, &method(:int_to_rgba))
COLOR_NAMES =

A hash from color names to [red, green, blue] value arrays.

Sass::Util.map_vals({
   'aliceblue'            => 0xF0F8FFFF,
   'antiquewhite'         => 0xFAEBD7FF,
   'aquamarine'           => 0x7FFFD4FF,
   'azure'                => 0xF0FFFFFF,
   'beige'                => 0xF5F5DCFF,
   'bisque'               => 0xFFE4C4FF,
   'black'                => 0x000000FF,
   'blanchedalmond'       => 0xFFEBCDFF,
   'blue'                 => 0x0000FFFF,
   'blueviolet'           => 0x8A2BE2FF,
   'brown'                => 0xA52A2AFF,
   'burlywood'            => 0xDEB887FF,
   'cadetblue'            => 0x5F9EA0FF,
   'chartreuse'           => 0x7FFF00FF,
   'chocolate'            => 0xD2691EFF,
   'coral'                => 0xFF7F50FF,
   'cornflowerblue'       => 0x6495EDFF,
   'cornsilk'             => 0xFFF8DCFF,
   'crimson'              => 0xDC143CFF,
   'cyan'                 => 0x00FFFFFF,
   'darkblue'             => 0x00008BFF,
   'darkcyan'             => 0x008B8BFF,
   'darkgoldenrod'        => 0xB8860BFF,
   'darkgray'             => 0xA9A9A9FF,
   'darkgreen'            => 0x006400FF,
   'darkkhaki'            => 0xBDB76BFF,
   'darkmagenta'          => 0x8B008BFF,
   'darkolivegreen'       => 0x556B2FFF,
   'darkorange'           => 0xFF8C00FF,
   'darkorchid'           => 0x9932CCFF,
   'darkred'              => 0x8B0000FF,
   'darksalmon'           => 0xE9967AFF,
   'darkseagreen'         => 0x8FBC8FFF,
   'darkslateblue'        => 0x483D8BFF,
   'darkslategray'        => 0x2F4F4FFF,
   'darkturquoise'        => 0x00CED1FF,
   'darkviolet'           => 0x9400D3FF,
   'deeppink'             => 0xFF1493FF,
   'deepskyblue'          => 0x00BFFFFF,
   'dimgray'              => 0x696969FF,
   'dodgerblue'           => 0x1E90FFFF,
   'firebrick'            => 0xB22222FF,
   'floralwhite'          => 0xFFFAF0FF,
   'forestgreen'          => 0x228B22FF,
   'gainsboro'            => 0xDCDCDCFF,
   'ghostwhite'           => 0xF8F8FFFF,
   'gold'                 => 0xFFD700FF,
   'goldenrod'            => 0xDAA520FF,
   'gray'                 => 0x808080FF,
   'green'                => 0x008000FF,
   'greenyellow'          => 0xADFF2FFF,
   'honeydew'             => 0xF0FFF0FF,
   'hotpink'              => 0xFF69B4FF,
   'indianred'            => 0xCD5C5CFF,
   'indigo'               => 0x4B0082FF,
   'ivory'                => 0xFFFFF0FF,
   'khaki'                => 0xF0E68CFF,
   'lavender'             => 0xE6E6FAFF,
   'lavenderblush'        => 0xFFF0F5FF,
   'lawngreen'            => 0x7CFC00FF,
   'lemonchiffon'         => 0xFFFACDFF,
   'lightblue'            => 0xADD8E6FF,
   'lightcoral'           => 0xF08080FF,
   'lightcyan'            => 0xE0FFFFFF,
   'lightgoldenrodyellow' => 0xFAFAD2FF,
   'lightgreen'           => 0x90EE90FF,
   'lightgray'            => 0xD3D3D3FF,
   'lightpink'            => 0xFFB6C1FF,
   'lightsalmon'          => 0xFFA07AFF,
   'lightseagreen'        => 0x20B2AAFF,
   'lightskyblue'         => 0x87CEFAFF,
   'lightslategray'       => 0x778899FF,
   'lightsteelblue'       => 0xB0C4DEFF,
   'lightyellow'          => 0xFFFFE0FF,
   'lime'                 => 0x00FF00FF,
   'limegreen'            => 0x32CD32FF,
   'linen'                => 0xFAF0E6FF,
   'magenta'              => 0xFF00FFFF,
   'maroon'               => 0x800000FF,
   'mediumaquamarine'     => 0x66CDAAFF,
   'mediumblue'           => 0x0000CDFF,
   'mediumorchid'         => 0xBA55D3FF,
   'mediumpurple'         => 0x9370DBFF,
   'mediumseagreen'       => 0x3CB371FF,
   'mediumslateblue'      => 0x7B68EEFF,
   'mediumspringgreen'    => 0x00FA9AFF,
   'mediumturquoise'      => 0x48D1CCFF,
   'mediumvioletred'      => 0xC71585FF,
   'midnightblue'         => 0x191970FF,
   'mintcream'            => 0xF5FFFAFF,
   'mistyrose'            => 0xFFE4E1FF,
   'moccasin'             => 0xFFE4B5FF,
   'navajowhite'          => 0xFFDEADFF,
   'navy'                 => 0x000080FF,
   'oldlace'              => 0xFDF5E6FF,
   'olive'                => 0x808000FF,
   'olivedrab'            => 0x6B8E23FF,
   'orange'               => 0xFFA500FF,
   'orangered'            => 0xFF4500FF,
   'orchid'               => 0xDA70D6FF,
   'palegoldenrod'        => 0xEEE8AAFF,
   'palegreen'            => 0x98FB98FF,
   'paleturquoise'        => 0xAFEEEEFF,
   'palevioletred'        => 0xDB7093FF,
   'papayawhip'           => 0xFFEFD5FF,
   'peachpuff'            => 0xFFDAB9FF,
   'peru'                 => 0xCD853FFF,
   'pink'                 => 0xFFC0CBFF,
   'plum'                 => 0xDDA0DDFF,
   'powderblue'           => 0xB0E0E6FF,
   'purple'               => 0x800080FF,
   'red'                  => 0xFF0000FF,
   'rebeccapurple'        => 0x663399FF,
   'rosybrown'            => 0xBC8F8FFF,
   'royalblue'            => 0x4169E1FF,
   'saddlebrown'          => 0x8B4513FF,
   'salmon'               => 0xFA8072FF,
   'sandybrown'           => 0xF4A460FF,
   'seagreen'             => 0x2E8B57FF,
   'seashell'             => 0xFFF5EEFF,
   'sienna'               => 0xA0522DFF,
   'silver'               => 0xC0C0C0FF,
   'skyblue'              => 0x87CEEBFF,
   'slateblue'            => 0x6A5ACDFF,
   'slategray'            => 0x708090FF,
   'snow'                 => 0xFFFAFAFF,
   'springgreen'          => 0x00FF7FFF,
   'steelblue'            => 0x4682B4FF,
   'tan'                  => 0xD2B48CFF,
   'teal'                 => 0x008080FF,
   'thistle'              => 0xD8BFD8FF,
   'tomato'               => 0xFF6347FF,
   'transparent'          => 0x00000000,
   'turquoise'            => 0x40E0D0FF,
   'violet'               => 0xEE82EEFF,
   'wheat'                => 0xF5DEB3FF,
   'white'                => 0xFFFFFFFF,
   'whitesmoke'           => 0xF5F5F5FF,
   'yellow'               => 0xFFFF00FF,
   'yellowgreen'          => 0x9ACD32FF
}, &method(:int_to_rgba))
COLOR_NAMES_REVERSE =

A hash from [red, green, blue, alpha] value arrays to color names.

COLOR_NAMES.invert.freeze

Instance Attribute Summary collapse

Attributes inherited from Base

#options, #source_range, #value

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#==, #_perform, #assert_int!, #eql?, #neq, #null?, #separator, #single_eq, #to_a, #to_bool, #to_h, #to_i, #unary_div, #unary_minus, #unary_not, #unary_plus

Constructor Details

#initialize(attrs) ⇒ Color #initialize(rgba, [representation]) ⇒ Color

Constructs an RGB or HSL color object, optionally with an alpha channel.

RGB values are clipped within 0 and 255. Saturation and lightness values are clipped within 0 and 100. The alpha value is clipped within 0 and 1.

Overloads:

  • #initialize(attrs) ⇒ Color

    The attributes are specified as a hash. This hash must contain either :hue, :saturation, and :lightness keys, or :red, :green, and :blue keys. It cannot contain both HSL and RGB keys. It may also optionally contain an :alpha key, and a :representation key indicating the original representation of the color that the user wrote in their stylesheet.

    Raises:

    • (ArgumentError)

      if not enough attributes are specified, or both RGB and HSL attributes are specified

  • #initialize(rgba, [representation]) ⇒ Color

    The attributes are specified as an array. This overload only supports RGB or RGBA colors.

    Raises:

    • (ArgumentError)

      if not enough attributes are specified

Raises:



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/sass/script/value/color.rb', line 227

def initialize(attrs, representation = nil, allow_both_rgb_and_hsl = false)
  super(nil)

  if attrs.is_a?(Array)
    unless (3..4).include?(attrs.size)
      raise ArgumentError.new("Color.new(array) expects a three- or four-element array")
    end

    red, green, blue = attrs[0...3].map {|c| c.to_i}
    @attrs = {:red => red, :green => green, :blue => blue}
    @attrs[:alpha] = attrs[3] ? attrs[3].to_f : 1
    @representation = representation
  else
    attrs = attrs.reject {|k, v| v.nil?}
    hsl = [:hue, :saturation, :lightness] & attrs.keys
    rgb = [:red, :green, :blue] & attrs.keys
    if !allow_both_rgb_and_hsl && !hsl.empty? && !rgb.empty?
      raise ArgumentError.new("Color.new(hash) may not have both HSL and RGB keys specified")
    elsif hsl.empty? && rgb.empty?
      raise ArgumentError.new("Color.new(hash) must have either HSL or RGB keys specified")
    elsif !hsl.empty? && hsl.size != 3
      raise ArgumentError.new("Color.new(hash) must have all three HSL values specified")
    elsif !rgb.empty? && rgb.size != 3
      raise ArgumentError.new("Color.new(hash) must have all three RGB values specified")
    end

    @attrs = attrs
    @attrs[:hue] %= 360 if @attrs[:hue]
    @attrs[:alpha] ||= 1
    @representation = @attrs.delete(:representation)
  end

  [:red, :green, :blue].each do |k|
    next if @attrs[k].nil?
    @attrs[k] = Sass::Util.restrict(@attrs[k].to_i, 0..255)
  end

  [:saturation, :lightness].each do |k|
    next if @attrs[k].nil?
    @attrs[k] = Sass::Util.restrict(@attrs[k], 0..100)
  end

  @attrs[:alpha] = Sass::Util.restrict(@attrs[:alpha], 0..1)
end

Instance Attribute Details

#representationString (readonly)

The user's original representation of the color.



194
195
196
# File 'lib/sass/script/value/color.rb', line 194

def representation
  @representation
end

Class Method Details

.from_hex(hex_string, alpha = nil) ⇒ Color

Create a new color from a valid CSS hex string.

The leading hash is optional.



277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/sass/script/value/color.rb', line 277

def self.from_hex(hex_string, alpha = nil)
  unless hex_string =~ /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i ||
         hex_string =~ /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i
    raise ArgumentError.new("#{hex_string.inspect} is not a valid hex color.")
  end
  red   = $1.ljust(2, $1).to_i(16)
  green = $2.ljust(2, $2).to_i(16)
  blue  = $3.ljust(2, $3).to_i(16)

  hex_string = '##{hex_string}' unless hex_string[0] == ?#
  attrs = {:red => red, :green => green, :blue => blue, :representation => hex_string}
  attrs[:alpha] = alpha if alpha
  new(attrs)
end

Instance Method Details

#alphaFixnum

The alpha channel (opacity) of the color. This is 1 unless otherwise defined.



344
345
346
# File 'lib/sass/script/value/color.rb', line 344

def alpha
  @attrs[:alpha].to_f
end

#alpha?Boolean

Returns whether this color object is translucent; that is, whether the alpha channel is non-1.



352
353
354
# File 'lib/sass/script/value/color.rb', line 352

def alpha?
  alpha < 1
end

#blueFixnum

The blue component of the color.



311
312
313
314
# File 'lib/sass/script/value/color.rb', line 311

def blue
  hsl_to_rgb!
  @attrs[:blue]
end

#div(other) ⇒ Color

The SassScript / operation. Its functionality depends on the type of its argument:

Number : Divides each of the RGB color channels by the number.

Sass::Script::Value::Color : Divides each of this color's RGB color channels by the other color's.

Sass::Script::Value : See Base#div.

Raises:



526
527
528
529
530
531
532
533
# File 'lib/sass/script/value/color.rb', line 526

def div(other)
  if other.is_a?(Sass::Script::Value::Number) ||
      other.is_a?(Sass::Script::Value::Color)
    piecewise(other, :/)
  else
    super
  end
end

#eq(other) ⇒ Bool

The SassScript == operation. Note that this returns a Bool object, not a Ruby boolean.



395
396
397
398
# File 'lib/sass/script/value/color.rb', line 395

def eq(other)
  Sass::Script::Value::Bool.new(
    other.is_a?(Color) && rgb == other.rgb && alpha == other.alpha)
end

#greenFixnum

The green component of the color.



303
304
305
306
# File 'lib/sass/script/value/color.rb', line 303

def green
  hsl_to_rgb!
  @attrs[:green]
end

#hash



400
401
402
# File 'lib/sass/script/value/color.rb', line 400

def hash
  [rgb, alpha].hash
end

#hslArray<Fixnum>

Returns the hue, saturation, and lightness components of the color.



376
377
378
# File 'lib/sass/script/value/color.rb', line 376

def hsl
  [hue, saturation, lightness].freeze
end

#hslaArray<Fixnum>

Returns the hue, saturation, lightness, and alpha components of the color.



384
385
386
# File 'lib/sass/script/value/color.rb', line 384

def hsla
  [hue, saturation, lightness].freeze
end

#hueNumeric

The hue component of the color.



319
320
321
322
# File 'lib/sass/script/value/color.rb', line 319

def hue
  rgb_to_hsl!
  @attrs[:hue]
end

#inspectString

Returns a string representation of the color.



572
573
574
# File 'lib/sass/script/value/color.rb', line 572

def inspect
  alpha? ? rgba_str : hex_str
end

#lightnessNumeric

The lightness component of the color.



335
336
337
338
# File 'lib/sass/script/value/color.rb', line 335

def lightness
  rgb_to_hsl!
  @attrs[:lightness]
end

#minus(other) ⇒ Color

The SassScript - operation. Its functionality depends on the type of its argument:

Number : Subtracts the number from each of the RGB color channels.

Sass::Script::Value::Color : Subtracts each of the other color's RGB color channels from this color's.

Sass::Script::Value : See Base#minus.

Raises:



483
484
485
486
487
488
489
# File 'lib/sass/script/value/color.rb', line 483

def minus(other)
  if other.is_a?(Sass::Script::Value::Number) || other.is_a?(Sass::Script::Value::Color)
    piecewise(other, :-)
  else
    super
  end
end

#mod(other) ⇒ Color

The SassScript % operation. Its functionality depends on the type of its argument:

Number : Takes each of the RGB color channels module the number.

Sass::Script::Value::Color : Takes each of this color's RGB color channels modulo the other color's.

Raises:



547
548
549
550
551
552
553
554
# File 'lib/sass/script/value/color.rb', line 547

def mod(other)
  if other.is_a?(Sass::Script::Value::Number) ||
      other.is_a?(Sass::Script::Value::Color)
    piecewise(other, :%)
  else
    raise NoMethodError.new(nil, :mod)
  end
end

#nameString?

Returns the color's name, if it has one.



579
580
581
# File 'lib/sass/script/value/color.rb', line 579

def name
  COLOR_NAMES_REVERSE[rgba]
end

#plus(other) ⇒ Color

The SassScript + operation. Its functionality depends on the type of its argument:

Number : Adds the number to each of the RGB color channels.

Sass::Script::Value::Color : Adds each of the RGB color channels together.

Sass::Script::Value : See Base#plus.

Raises:



460
461
462
463
464
465
466
# File 'lib/sass/script/value/color.rb', line 460

def plus(other)
  if other.is_a?(Sass::Script::Value::Number) || other.is_a?(Sass::Script::Value::Color)
    piecewise(other, :+)
  else
    super
  end
end

#redFixnum

The red component of the color.



295
296
297
298
# File 'lib/sass/script/value/color.rb', line 295

def red
  hsl_to_rgb!
  @attrs[:red]
end

#rgbArray<Fixnum>

Returns the red, green, and blue components of the color.



360
361
362
# File 'lib/sass/script/value/color.rb', line 360

def rgb
  [red, green, blue].freeze
end

#rgbaArray<Fixnum>

Returns the red, green, blue, and alpha components of the color.



368
369
370
# File 'lib/sass/script/value/color.rb', line 368

def rgba
  [red, green, blue, alpha].freeze
end

#saturationNumeric

The saturation component of the color.



327
328
329
330
# File 'lib/sass/script/value/color.rb', line 327

def saturation
  rgb_to_hsl!
  @attrs[:saturation]
end

#times(other) ⇒ Color

The SassScript * operation. Its functionality depends on the type of its argument:

Number : Multiplies the number by each of the RGB color channels.

Sass::Script::Value::Color : Multiplies each of the RGB color channels together.

Raises:



503
504
505
506
507
508
509
# File 'lib/sass/script/value/color.rb', line 503

def times(other)
  if other.is_a?(Sass::Script::Value::Number) || other.is_a?(Sass::Script::Value::Color)
    piecewise(other, :*)
  else
    raise NoMethodError.new(nil, :times)
  end
end

#to_s(opts = {}) ⇒ String Also known as: to_sass

Returns a string representation of the color. This is usually the color's hex value, but if the color has a name that's used instead.



561
562
563
564
565
566
# File 'lib/sass/script/value/color.rb', line 561

def to_s(opts = {})
  return smallest if options[:style] == :compressed
  return representation if representation
  return name if name
  alpha? ? rgba_str : hex_str
end

#with(attrs) ⇒ Color

Returns a copy of this color with one or more channels changed. RGB or HSL colors may be changed, but not both at once.

For example:

Color.new([10, 20, 30]).with(:blue => 40)
  #=> rgb(10, 40, 30)
Color.new([126, 126, 126]).with(:red => 0, :green => 255)
  #=> rgb(0, 255, 126)
Color.new([255, 0, 127]).with(:saturation => 60)
  #=> rgb(204, 51, 127)
Color.new([1, 2, 3]).with(:alpha => 0.4)
  #=> rgba(1, 2, 3, 0.4)

Raises:

  • (ArgumentError)

    if both RGB and HSL keys are specified



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/sass/script/value/color.rb', line 423

def with(attrs)
  attrs = attrs.reject {|k, v| v.nil?}
  hsl = !([:hue, :saturation, :lightness] & attrs.keys).empty?
  rgb = !([:red, :green, :blue] & attrs.keys).empty?
  if hsl && rgb
    raise ArgumentError.new("Cannot specify HSL and RGB values for a color at the same time")
  end

  if hsl
    [:hue, :saturation, :lightness].each {|k| attrs[k] ||= send(k)}
  elsif rgb
    [:red, :green, :blue].each {|k| attrs[k] ||= send(k)}
  else
    # If we're just changing the alpha channel,
    # keep all the HSL/RGB stuff we've calculated
    attrs = @attrs.merge(attrs)
  end
  attrs[:alpha] ||= alpha

  Color.new(attrs, nil, :allow_both_rgb_and_hsl)
end