sass-extras 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +91 -0
- data/Rakefile +8 -0
- data/lib/sass-extras.rb +1 -0
- data/lib/sass/extras.rb +22 -0
- data/lib/sass/extras/contrast.rb +131 -0
- data/lib/sass/extras/inline_color_image.rb +22 -0
- data/lib/sass/extras/svg_gradients.rb +56 -0
- data/lib/sass/extras/version.rb +5 -0
- data/lib/sass/extras/yuv.rb +127 -0
- data/sass-extras.gemspec +28 -0
- data/specs/contrast_spec.rb +63 -0
- data/specs/spec_helper.rb +2 -0
- data/specs/yuv_spec.rb +81 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ac5519a8b4bf64a198c1df9c394825961d3f4662
|
4
|
+
data.tar.gz: 7e8ca39eab7f6200859cd754cd7acfc59227bd31
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a3c396ea1377ad290e2bddf47ad0f17c3c8cb77f14cca7be94376a88e8bcf935777af5d3d99046691759c250a608fcf1682b91f17effa06233d5b2431d96bdd8
|
7
|
+
data.tar.gz: bda6b6651d35c787f57d5b0f8e7cbed500c5c6479fd5cd22c6a601ff6516713980b61755a185baa72b924724adf2cc5b0638c780c9d89a5eb0de6dfe4839cfee
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Tobias Haagen Michaelsen
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Sass::Extras
|
2
|
+
|
3
|
+
[](https://travis-ci.org/tobiashm/sass-extras)
|
4
|
+
|
5
|
+
A collection of miscellaneous SASS extensions. Mostly concerned with colour manipulation.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'sass-extras'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install sass-extras
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Just require this gem in your project, and it will extend the `Sass::Script::Functions` module with a selection of functions that will be available in your `.sass` or `.scss` files.
|
24
|
+
|
25
|
+
Notice: All examples are in SCSS syntax.
|
26
|
+
|
27
|
+
### Contrast Color
|
28
|
+
|
29
|
+
Will adjust a colour so that it conforms to the [WAI](http://www.w3.org/WAI/ER/WD-AERT/#color-contrast) and [WCAG20](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast) recommendations for minimum contrast for text.
|
30
|
+
|
31
|
+
```SCSS
|
32
|
+
span {
|
33
|
+
color: contrast-color($text-color, $background-color);
|
34
|
+
}
|
35
|
+
```
|
36
|
+
|
37
|
+
### Inline Image with a Colour
|
38
|
+
|
39
|
+
Generate a data URL for an image of a given colour. Useful for e.g. backgrounds with an alpha channel in browsers that does not support `rgba`.
|
40
|
+
|
41
|
+
```SCSS
|
42
|
+
a {
|
43
|
+
background: url(inline-color-image(rgba(102, 54, 32, 0.5)));
|
44
|
+
}
|
45
|
+
```
|
46
|
+
|
47
|
+
### SVG gradients
|
48
|
+
|
49
|
+
Generate data URI for SVG images with either a linear or a radial gradient.
|
50
|
+
|
51
|
+
Optional first argument is the colour of the gradient, going from solid to total transparent for the linear and 20% opacity for the radial gradient.
|
52
|
+
|
53
|
+
Optional second argument is the height of the generated image.
|
54
|
+
|
55
|
+
```SCSS
|
56
|
+
.scrollbox {
|
57
|
+
background-image: radial-gradient-image-data-url(black);
|
58
|
+
&:before {
|
59
|
+
background-image: linear-gradient-image-data-url(white);
|
60
|
+
}
|
61
|
+
/* some positioning etc. */
|
62
|
+
}
|
63
|
+
```
|
64
|
+
|
65
|
+
### YUV color space
|
66
|
+
|
67
|
+
Offers two methods for generating colours based on the YUV space: `yuv(y, u, v)` and `yuva(y, u, v, alpha)`.
|
68
|
+
|
69
|
+
```SCSS
|
70
|
+
a {
|
71
|
+
background-color: yuva(10%, 0, 0, 0.5); /* #191919, 50% alpha */
|
72
|
+
}
|
73
|
+
```
|
74
|
+
|
75
|
+
Besides that, a couple of helper functions to adjust brightness based on the YUV colour space, is provided.
|
76
|
+
|
77
|
+
```SCSS
|
78
|
+
set-brightness($color, $amount);
|
79
|
+
increase-brightness($color, $amount);
|
80
|
+
reduce-brightness($color, $amount);
|
81
|
+
add-brightness($color, $amount);
|
82
|
+
detract-brightness($color, $amount);
|
83
|
+
```
|
84
|
+
|
85
|
+
## Contributing
|
86
|
+
|
87
|
+
1. Fork it ( http://github.com/tobiashm/sass-extras/fork )
|
88
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
89
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
90
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
91
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/sass-extras.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "sass/extras"
|
data/lib/sass/extras.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "sass"
|
2
|
+
require "sass/extras/version"
|
3
|
+
require "sass/extras/contrast"
|
4
|
+
require "sass/extras/inline_color_image"
|
5
|
+
require "sass/extras/svg_gradients"
|
6
|
+
require "sass/extras/yuv"
|
7
|
+
|
8
|
+
module Sass
|
9
|
+
module Script
|
10
|
+
class Color
|
11
|
+
include Sass::Extras::Contrast::Color
|
12
|
+
include Sass::Extras::YUV::Color
|
13
|
+
end
|
14
|
+
|
15
|
+
module Functions
|
16
|
+
include Sass::Extras::Contrast::Functions
|
17
|
+
include Sass::Extras::InlineColorImage
|
18
|
+
include Sass::Extras::SvgGradients
|
19
|
+
include Sass::Extras::YUV::Functions
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module Sass
|
2
|
+
module Extras
|
3
|
+
module Contrast
|
4
|
+
BRIGHTNESS_COEFS = [0.299, 0.587, 0.114]
|
5
|
+
LUMINANCE_COEFS = [0.2126, 0.7152, 0.0722]
|
6
|
+
|
7
|
+
module Color
|
8
|
+
def diff(other)
|
9
|
+
# W3C
|
10
|
+
Utils.sum(Utils.abs(rgb, other.rgb))
|
11
|
+
end
|
12
|
+
|
13
|
+
def diff_alt(other)
|
14
|
+
# 3D - Sqrt(dr^2+dg^2+db^2)
|
15
|
+
Math.sqrt(Utils.sum(Utils.sq(Utils.abs(rgb, other.rgb))))
|
16
|
+
end
|
17
|
+
|
18
|
+
def brightness
|
19
|
+
# W3C; Rec. 601 luma
|
20
|
+
Utils.sum(Utils.mul(rgb, BRIGHTNESS_COEFS))
|
21
|
+
end
|
22
|
+
|
23
|
+
def brightness_alt
|
24
|
+
# http://alienryderflex.com/hsp.html
|
25
|
+
Math.sqrt(Utils.sum(Utils.mul(Utils.sq(rgb), [0.241, 0.691, 0.068])))
|
26
|
+
end
|
27
|
+
|
28
|
+
def luminance
|
29
|
+
# http://www.w3.org/TR/WCAG20/#relativeluminancedef
|
30
|
+
norm_rgb = rgb.map { |value| value.to_f / 255 }
|
31
|
+
Utils.sum(Utils.mul(norm_rgb.map { |v| v <= 0.03928 ? v/12.92 : ((v+0.055)/1.055) ** 2.4 }, LUMINANCE_COEFS))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Functions
|
36
|
+
def self.included(base)
|
37
|
+
base.declare :contrast_color, [:color]
|
38
|
+
base.declare :contrast_color, [:color, :seed_color]
|
39
|
+
base.declare :contrast_color, [:color, :seed_color, :wcag20_level]
|
40
|
+
end
|
41
|
+
|
42
|
+
def contrast_color(color, seed_color = nil, wcag20_level = Sass::Script::String.new("aa"))
|
43
|
+
seed_color ||= color
|
44
|
+
assert_type color, :Color, :color
|
45
|
+
assert_type seed_color, :Color, :seed_color
|
46
|
+
assert_type wcag20_level, :String, :wcag20_level
|
47
|
+
direction = color.brightness > 127 ? darken_method : lighten_method
|
48
|
+
new_color = seed_color
|
49
|
+
percentage = 0.0
|
50
|
+
until conform(new_color, color, wcag20_level.value) or percentage > 100.0 do
|
51
|
+
amount = Sass::Script::Number.new percentage, ['%']
|
52
|
+
new_color = self.send direction, seed_color, amount
|
53
|
+
percentage += 0.1
|
54
|
+
end
|
55
|
+
new_color
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
# http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
|
61
|
+
MIN_BRIGHT_DIFF = 125
|
62
|
+
MIN_COLOR_DIFF = 500
|
63
|
+
|
64
|
+
def conform(color1, color2, wcag20_level = "aa")
|
65
|
+
bright_diff = (color1.brightness - color2.brightness).abs
|
66
|
+
color_diff = color1.diff(color2)
|
67
|
+
bright_diff >= MIN_BRIGHT_DIFF && color_diff >= MIN_COLOR_DIFF && wcag20_conform(color1, color2, wcag20_level)
|
68
|
+
end
|
69
|
+
|
70
|
+
# http://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
|
71
|
+
MIN_CONTRAST_RATE_AA = 4.5
|
72
|
+
MIN_CONTRAST_RATE_AA_LARGE_TEXT = 3
|
73
|
+
MIN_CONTRAST_RATE_AAA = 7
|
74
|
+
MIN_CONTRAST_RATE_AAA_LARGE_TEXT = 4.5
|
75
|
+
|
76
|
+
def min_contrast_rate(wcag20_level)
|
77
|
+
case wcag20_level.to_s.downcase
|
78
|
+
when "aaa_large"
|
79
|
+
MIN_CONTRAST_RATE_AAA_LARGE_TEXT
|
80
|
+
when "aaa"
|
81
|
+
MIN_CONTRAST_RATE_AAA
|
82
|
+
when "aa_large"
|
83
|
+
MIN_CONTRAST_RATE_AA_LARGE_TEXT
|
84
|
+
else
|
85
|
+
MIN_CONTRAST_RATE_AA
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def wcag20_conform(color1, color2, wcag20_level = "aa")
|
90
|
+
contrast_ratio(color1, color2) >= min_contrast_rate(wcag20_level)
|
91
|
+
end
|
92
|
+
|
93
|
+
def contrast_ratio(color1, color2)
|
94
|
+
assert_type color1, :Color
|
95
|
+
assert_type color2, :Color
|
96
|
+
l1, l2 = color1.luminance, color2.luminance
|
97
|
+
l2, l1 = l1, l2 if l2 > l1
|
98
|
+
(l1 + 0.05) / (l2 + 0.05)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def lighten_method
|
104
|
+
respond_to?(:add_brightness) ? :add_brightness : :lighten
|
105
|
+
end
|
106
|
+
|
107
|
+
def darken_method
|
108
|
+
respond_to?(:detract_brightness) ? :detract_brightness : :darken
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module Utils
|
113
|
+
def self.abs(array, other)
|
114
|
+
array.zip(other).map { |x, y| (x.to_f - y.to_f).abs }
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.mul(array, other)
|
118
|
+
array.zip(other).map { |x, y| x.to_f * y.to_f }
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.sum(array)
|
122
|
+
array.inject(0) { |sum, value| sum + value.to_f }
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.sq(array)
|
126
|
+
array.map { |e| e ** 2 }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "chunky_png"
|
2
|
+
|
3
|
+
module Sass
|
4
|
+
module Extras
|
5
|
+
module InlineColorImage
|
6
|
+
def self.included(base)
|
7
|
+
base.declare :inline_color_image, [:color]
|
8
|
+
end
|
9
|
+
|
10
|
+
# Generates a data-url for a PNG created from the given color.
|
11
|
+
# Can be used to set a alpha-transparent background for IE8<
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# background: url(inline-color-image(rgba(102, 54, 32, 0.5)));
|
15
|
+
def inline_color_image(color)
|
16
|
+
assert_type color, :Color
|
17
|
+
chunky_color = ChunkyPNG::Color.rgba(color.red, color.green, color.blue, (color.alpha * 255).round)
|
18
|
+
Sass::Script::String.new(ChunkyPNG::Image.new(32, 32, chunky_color).to_data_url)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "rack"
|
3
|
+
|
4
|
+
module Sass
|
5
|
+
module Extras
|
6
|
+
module SvgGradients
|
7
|
+
def self.included(base)
|
8
|
+
base.declare :linear_gradient_image_data_url, [:color, :height]
|
9
|
+
base.declare :radial_gradient_image_data_url, [:color, :height]
|
10
|
+
end
|
11
|
+
|
12
|
+
def radial_gradient_image_data_url(color = Sass::Script::Color.new([0, 0, 0]), height = Sass::Script::Number.new(5))
|
13
|
+
assert_type color, :Color
|
14
|
+
assert_type height, :Number
|
15
|
+
svg_data_url(<<-SVG)
|
16
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
17
|
+
<defs>
|
18
|
+
<radialGradient id="gradient">
|
19
|
+
<stop offset="50%" stop-color="#{svg_color(color)}" stop-opacity="0.2"/>
|
20
|
+
<stop offset="100%" stop-color="#{svg_color(color)}" stop-opacity="0"/>
|
21
|
+
</radialGradient>
|
22
|
+
</defs>
|
23
|
+
<rect width="100%" height="#{Sass::Script::Number.new(2).times(height)}" y="-#{height}" fill="url(#gradient)"></rect>
|
24
|
+
</svg>
|
25
|
+
SVG
|
26
|
+
end
|
27
|
+
|
28
|
+
def linear_gradient_image_data_url(color = Sass::Script::Color.new([255, 255, 255]), height = Sass::Script::Number.new(100, ['%']))
|
29
|
+
assert_type color, :Color
|
30
|
+
assert_type height, :Number
|
31
|
+
svg_data_url(<<-SVG)
|
32
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
33
|
+
<defs>
|
34
|
+
<linearGradient id="gradient" x1="0" x2="0" y1="0" y2="100%">
|
35
|
+
<stop offset="25%" stop-color="#{svg_color(color)}" stop-opacity="1"/>
|
36
|
+
<stop offset="100%" stop-color="#{svg_color(color)}" stop-opacity="0"/>
|
37
|
+
</linearGradient>
|
38
|
+
</defs>
|
39
|
+
<rect width="100%" height="#{height}" fill="url(#gradient)"></rect>
|
40
|
+
</svg>
|
41
|
+
SVG
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def svg_color(color)
|
47
|
+
"rgb(#{color.rgb.join(',')})"
|
48
|
+
end
|
49
|
+
|
50
|
+
def svg_data_url(svg)
|
51
|
+
base64 = Base64.encode64(svg).gsub(/\s+/, "")
|
52
|
+
Sass::Script::String.new("url(data:image/svg+xml;base64,#{Rack::Utils.escape(base64)})")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Sass
|
2
|
+
module Extras
|
3
|
+
module YUV
|
4
|
+
WR = 0.299
|
5
|
+
WG = 0.587
|
6
|
+
WB = 0.114
|
7
|
+
W_SUM = WR + WG + WB # float arithmetics error => 0.9999999999999999
|
8
|
+
U_MAX = 0.436
|
9
|
+
V_MAX = 0.615
|
10
|
+
|
11
|
+
module Color
|
12
|
+
def yuv
|
13
|
+
r, g, b = rgb.map { |k| k / 255.0 }
|
14
|
+
y = r * WR + g * WG + b * WB
|
15
|
+
y = 1.0 if y == W_SUM
|
16
|
+
u = U_MAX*(b - y)/(1 - WB)
|
17
|
+
v = V_MAX*(r - y)/(1 - WR)
|
18
|
+
[y, Utils.restrict(u, -U_MAX..U_MAX), Utils.restrict(v, -V_MAX..V_MAX)]
|
19
|
+
end
|
20
|
+
|
21
|
+
def yuva
|
22
|
+
yuv + [alpha]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Functions
|
27
|
+
def self.included(base)
|
28
|
+
base.declare :yuv, [:y, :u, :v]
|
29
|
+
base.declare :yuva, [:y, :u, :v, :alpha]
|
30
|
+
base.declare :set_brightness, [:color, :amount]
|
31
|
+
base.declare :increase_brightness, [:color, :amount]
|
32
|
+
base.declare :reduce_brightness, [:color, :amount]
|
33
|
+
base.declare :add_brightness, [:color, :amount]
|
34
|
+
base.declare :detract_brightness, [:color, :amount]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Creates a {Color} object from luma (Y), and two chrominance (UV) values.
|
38
|
+
#
|
39
|
+
# @param y [Number]
|
40
|
+
# A number between 0 and 1.0 inclusive
|
41
|
+
# @param u [Number]
|
42
|
+
# A number between -{U_MAX} and +{U_MAX} inclusive
|
43
|
+
# @param v [Number]
|
44
|
+
# A number between -{V_MAX} and +{V_MAX} inclusive
|
45
|
+
# @return [Color]
|
46
|
+
def yuv(y, u, v)
|
47
|
+
assert_type y, :Number, :y
|
48
|
+
assert_type u, :Number, :u
|
49
|
+
assert_type v, :Number, :v
|
50
|
+
|
51
|
+
yv = y.value
|
52
|
+
if y.numerator_units == ["%"] && y.denominator_units.empty?
|
53
|
+
raise ArgumentError.new("Brightness (Y') value #{y} must be between 0% and 100% inclusive") unless (0..100).include?(y.value)
|
54
|
+
yv = yv / 100.0
|
55
|
+
else
|
56
|
+
raise ArgumentError.new("Brightness (Y') value #{y} must be between 0 and 1.0 inclusive") unless (0..1.0).include?(y.value)
|
57
|
+
end
|
58
|
+
raise ArgumentError.new("Chrominance (U) value #{u} must be between -#{U_MAX} and #{U_MAX} inclusive") unless (-U_MAX..U_MAX).include?(u.value)
|
59
|
+
raise ArgumentError.new("Chrominance (V) value #{v} must be between -#{V_MAX} and #{V_MAX} inclusive") unless (-V_MAX..V_MAX).include?(v.value)
|
60
|
+
|
61
|
+
r = yv + v.value * (1 - WR)/(V_MAX)
|
62
|
+
g = yv - u.value * (WB * (1 - WB))/(V_MAX * WG) - v.value * (WR * (1 - WR))/(V_MAX * WG)
|
63
|
+
b = yv + u.value * (1 - WB)/U_MAX
|
64
|
+
|
65
|
+
rgb = [r, g, b].map { |c| [0, [255, c * 255].min].max }
|
66
|
+
Sass::Script::Color.new(rgb)
|
67
|
+
end
|
68
|
+
|
69
|
+
def yuva(y, u, v, a)
|
70
|
+
assert_type a, :Number, :a
|
71
|
+
yuv(y, u, v).with(:alpha => a.value)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param color [Color]
|
75
|
+
# @param amount [Number] between 0 and 1.0, or 0% and 100%
|
76
|
+
def set_brightness(color, amount)
|
77
|
+
adjust_brightness(color, amount) { |c, y| c }
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param color [Color]
|
81
|
+
# @param amount [Number] between 0 and 1.0, or 0% and 100%
|
82
|
+
def increase_brightness(color, amount)
|
83
|
+
adjust_brightness(color, amount) { |c, y| [(1 + c) * y, 1.0].min }
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param color [Color]
|
87
|
+
# @param amount [Number] between 0 and 1.0, or 0% and 100%
|
88
|
+
def reduce_brightness(color, amount)
|
89
|
+
adjust_brightness(color, amount) { |c, y| [(1 - c) * y, 0.0].max }
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param color [Color]
|
93
|
+
# @param amount [Number] between 0 and 1.0, or 0% and 100%
|
94
|
+
def add_brightness(color, amount)
|
95
|
+
adjust_brightness(color, amount) { |c, y| [y + c, 1.0].min }
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param color [Color]
|
99
|
+
# @param amount [Number] between 0 and 1.0, or 0% and 100%
|
100
|
+
def detract_brightness(color, amount)
|
101
|
+
adjust_brightness(color, amount) { |c, y| [y - c, 0.0].max }
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def adjust_brightness(color, amount)
|
107
|
+
y, u, v, a = color.yuva.map { |x| Sass::Script::Number.new x }
|
108
|
+
c = amount.value
|
109
|
+
if amount.numerator_units == ["%"] && amount.denominator_units.empty?
|
110
|
+
raise ArgumentError.new("Amount value #{amount} must be between 0% and 100% inclusive") unless (0..100).include?(c)
|
111
|
+
c = c / 100.0
|
112
|
+
else
|
113
|
+
raise ArgumentError.new("Amount value #{amount} must be between 0 and 1.0 inclusive") unless (0..1.0).include?(c)
|
114
|
+
end
|
115
|
+
y = Sass::Script::Number.new yield(c, y.value)
|
116
|
+
yuva(y, u, v, a)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
module Utils
|
121
|
+
def self.restrict(value, range)
|
122
|
+
[range.min, [value, range.max].min].max
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/sass-extras.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sass/extras/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sass-extras"
|
8
|
+
spec.version = Sass::Extras::VERSION
|
9
|
+
spec.authors = ["Tobias Haagen Michaelsen"]
|
10
|
+
spec.email = ["tobias.michaelsen@gmail.com"]
|
11
|
+
spec.summary = %q{A collection of SASS extensions.}
|
12
|
+
spec.description = %q{A collection of SASS functions and helper methods.}
|
13
|
+
spec.homepage = "https://github.com/tobiashm/sass-extras"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "sass"
|
22
|
+
spec.add_dependency "chunky_png"
|
23
|
+
spec.add_dependency "rack"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "minitest"
|
28
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Sass::Extras::Contrast do
|
4
|
+
it "calculates brightness" do
|
5
|
+
white = Sass::Script::Color.new [255, 255, 255]
|
6
|
+
white.brightness.must_be_within_delta 255.0
|
7
|
+
grey = Sass::Script::Color.new [0xe5, 0xe5, 0xe5]
|
8
|
+
grey.brightness.must_be_within_delta 0.9*255, 1.0
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should generate colors with enough contrast" do
|
12
|
+
data = File.read(__FILE__).split("__END__").last
|
13
|
+
css = Sass::Engine.new(data, :syntax => :scss).to_css
|
14
|
+
rules = css.gsub(/\s+/, ' ').gsub(/\} a/, '}\na').split('\n')
|
15
|
+
rules.each { |rule| rule.must_match(/a \{ color: (#?\w+); background: \1; \}/) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
__END__
|
20
|
+
a {
|
21
|
+
color: contrast-color(white, #ff0000);
|
22
|
+
background: #ee0000;
|
23
|
+
}
|
24
|
+
|
25
|
+
a {
|
26
|
+
color: contrast-color(rgb(127,127,127));
|
27
|
+
background: white;
|
28
|
+
}
|
29
|
+
|
30
|
+
a {
|
31
|
+
color: contrast-color(rgb(128,128,128));
|
32
|
+
background: black;
|
33
|
+
}
|
34
|
+
|
35
|
+
a {
|
36
|
+
color: contrast-color(white);
|
37
|
+
background: #585858;
|
38
|
+
}
|
39
|
+
|
40
|
+
a {
|
41
|
+
color: contrast-color(black);
|
42
|
+
background: #a7a7a7;
|
43
|
+
}
|
44
|
+
|
45
|
+
a {
|
46
|
+
color: contrast-color(red);
|
47
|
+
background: #ffaeb2;
|
48
|
+
}
|
49
|
+
|
50
|
+
a {
|
51
|
+
color: contrast-color(green);
|
52
|
+
background: #b3ffb3;
|
53
|
+
}
|
54
|
+
|
55
|
+
a {
|
56
|
+
color: contrast-color(blue);
|
57
|
+
background: #e1eeff;
|
58
|
+
}
|
59
|
+
|
60
|
+
a {
|
61
|
+
color: contrast-color(#123456);
|
62
|
+
background: #b8dcfc;
|
63
|
+
}
|
data/specs/yuv_spec.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Sass::Extras::YUV::Color do
|
4
|
+
before do
|
5
|
+
@color = Sass::Script::Color.new [255, 255, 255]
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "a beginning" do
|
9
|
+
it "should just be numbers" do
|
10
|
+
@color.rgb.each do |c|
|
11
|
+
c.class.must_equal Fixnum
|
12
|
+
end
|
13
|
+
@color.hue do |c|
|
14
|
+
c.class.must_equal Fixnum
|
15
|
+
end
|
16
|
+
@color.yuv.each do |c|
|
17
|
+
c.class.must_equal Float
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "must have solid aplha chanel" do
|
22
|
+
@color.alpha.must_equal 1
|
23
|
+
@color.yuva.last.must_equal 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "convertion to yuv" do
|
28
|
+
it "must be exected r g b value" do
|
29
|
+
@color.rgb.map { |k| k / 255.0 }.must_equal [1, 1, 1]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "must respond apropriate" do
|
33
|
+
@color.yuv.must_equal [1, 0, 0]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "has aproximated brightness" do
|
37
|
+
color = Sass::Script::Color.new [0x19, 0x19, 0x19]
|
38
|
+
color.inspect.must_equal "#191919"
|
39
|
+
y, u, v = color.yuv
|
40
|
+
y.must_be_within_delta 0.1, 0.002
|
41
|
+
u.must_be_within_delta 0
|
42
|
+
v.must_be_within_delta 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "usage in render" do
|
47
|
+
it "can define color" do
|
48
|
+
(<<CSS).must_equal render(<<SASS)
|
49
|
+
a {
|
50
|
+
b: #191919; }
|
51
|
+
CSS
|
52
|
+
a {
|
53
|
+
b: yuv(10%, 0, 0); }
|
54
|
+
SASS
|
55
|
+
end
|
56
|
+
|
57
|
+
it "converts nicely" do
|
58
|
+
(<<CSS).must_equal render(<<SASS)
|
59
|
+
a {
|
60
|
+
b: #191919; }
|
61
|
+
CSS
|
62
|
+
a {
|
63
|
+
b: set_brightness(#FFFFFF, 10%); }
|
64
|
+
SASS
|
65
|
+
end
|
66
|
+
|
67
|
+
it "reduces brightness" do
|
68
|
+
(<<CSS).must_equal render(<<SASS)
|
69
|
+
a {
|
70
|
+
b: #e5e5e5; }
|
71
|
+
CSS
|
72
|
+
a {
|
73
|
+
b: reduce_brightness(#FFFFFF, 10%); }
|
74
|
+
SASS
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def render(sass)
|
79
|
+
Sass::Engine.new(sass, syntax: :scss).render
|
80
|
+
end
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sass-extras
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tobias Haagen Michaelsen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sass
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: chunky_png
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.5'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.5'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: A collection of SASS functions and helper methods.
|
98
|
+
email:
|
99
|
+
- tobias.michaelsen@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".travis.yml"
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- lib/sass-extras.rb
|
111
|
+
- lib/sass/extras.rb
|
112
|
+
- lib/sass/extras/contrast.rb
|
113
|
+
- lib/sass/extras/inline_color_image.rb
|
114
|
+
- lib/sass/extras/svg_gradients.rb
|
115
|
+
- lib/sass/extras/version.rb
|
116
|
+
- lib/sass/extras/yuv.rb
|
117
|
+
- sass-extras.gemspec
|
118
|
+
- specs/contrast_spec.rb
|
119
|
+
- specs/spec_helper.rb
|
120
|
+
- specs/yuv_spec.rb
|
121
|
+
homepage: https://github.com/tobiashm/sass-extras
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata: {}
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubyforge_project:
|
141
|
+
rubygems_version: 2.2.0
|
142
|
+
signing_key:
|
143
|
+
specification_version: 4
|
144
|
+
summary: A collection of SASS extensions.
|
145
|
+
test_files: []
|