immosquare-colors 0.1.5 → 0.1.6
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 +4 -4
- data/lib/immosquare-colors/version.rb +1 -1
- data/lib/immosquare-colors.rb +131 -13
- metadata +10 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 298cb3ad3c069e4f68cd9de80e71708a7cc96c4dec84809c7f23f4181ad80d1d
|
|
4
|
+
data.tar.gz: 40f23aae65b3b72611898ca2b52a1605781bf345e45bd62b6587c3cb17a8d09f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0d7207b2ff2ef514539cb159e4b05ea5830f38d4aed38bce7bf5f77ebdf5a04efcf1d04c745266b05fcf6904dcfb1843e92651f8d32d6d9863bc8d4967a07c08
|
|
7
|
+
data.tar.gz: e9f0713467cc5cc73ad32c32dd50f896be52cc07bfc836ea066768759e271adcf109143d8e7cddc5d6286e2eece9f5fe1bf454117e4e773ae86f119a2bb075d0
|
data/lib/immosquare-colors.rb
CHANGED
|
@@ -5,26 +5,33 @@ module ImmosquareColors
|
|
|
5
5
|
|
|
6
6
|
##============================================================##
|
|
7
7
|
## To determine whether the complementary color should be
|
|
8
|
-
## black or white
|
|
8
|
+
## black or white.
|
|
9
|
+
##
|
|
10
|
+
## The answer is the overlay that actually reads best, decided by
|
|
11
|
+
## WCAG contrast ratio. A perceived-brightness cutoff does not
|
|
12
|
+
## agree with that ratio around mid luminance: olives, mauves and
|
|
13
|
+
## muted greens sit just under a 127.5 cutoff and are handed white,
|
|
14
|
+
## while black reads better on them. `#6c8539` for instance scores
|
|
15
|
+
## 4.16:1 against white and 5.05:1 against black.
|
|
16
|
+
##
|
|
17
|
+
## Passing `:luminance` keeps the former behaviour, for callers
|
|
18
|
+
## that tuned their own brightness cutoff.
|
|
9
19
|
##============================================================##
|
|
10
20
|
def get_complementary_color(color, options = {})
|
|
11
21
|
begin
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
color_hex = color.start_with?("#") ? color : color_name_to_hex(color)
|
|
22
|
+
black = "#000000"
|
|
23
|
+
white = "#FFFFFF"
|
|
24
|
+
color_hex = color.start_with?("#") ? color : color_name_to_hex(color)
|
|
16
25
|
|
|
17
26
|
raise("Not valid size") if ![7, 9].include?(color_hex.length)
|
|
18
27
|
|
|
28
|
+
if options[:luminance]
|
|
29
|
+
r, g, b = hex_to_rgba(color_hex)
|
|
30
|
+
brightness = Math.sqrt((0.299 * (r**2)) + (0.587 * (g**2)) + (0.114 * (b**2)))
|
|
31
|
+
return brightness > options[:luminance].to_f ? black : white
|
|
32
|
+
end
|
|
19
33
|
|
|
20
|
-
|
|
21
|
-
r, g, b, a = color_rgba
|
|
22
|
-
|
|
23
|
-
##============================================================##
|
|
24
|
-
## luminance calculation
|
|
25
|
-
##============================================================##
|
|
26
|
-
luminance = Math.sqrt((0.299 * (r**2)) + (0.587 * (g**2)) + (0.114 * (b**2)))
|
|
27
|
-
luminance > luminance_std ? black : white
|
|
34
|
+
contrast_ratio(color_hex, white) >= contrast_ratio(color_hex, black) ? white : black
|
|
28
35
|
rescue StandardError => e
|
|
29
36
|
puts("=== Error! ===")
|
|
30
37
|
puts(e.message)
|
|
@@ -33,6 +40,28 @@ module ImmosquareColors
|
|
|
33
40
|
end
|
|
34
41
|
end
|
|
35
42
|
|
|
43
|
+
##============================================================##
|
|
44
|
+
## WCAG 2.1 contrast ratio between two colors, from 1 (identical)
|
|
45
|
+
## to 21 (black on white). Body text wants 4.5, large text and
|
|
46
|
+
## graphical objects 3.
|
|
47
|
+
##============================================================##
|
|
48
|
+
def contrast_ratio(color_one, color_two)
|
|
49
|
+
lighter, darker = [relative_luminance(color_one), relative_luminance(color_two)].minmax.reverse
|
|
50
|
+
(lighter + 0.05) / (darker + 0.05)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##============================================================##
|
|
54
|
+
## WCAG 2.1 relative luminance, between 0 (black) and 1 (white).
|
|
55
|
+
## Channels are linearised before being weighted, which is what
|
|
56
|
+
## separates it from a plain brightness average.
|
|
57
|
+
##============================================================##
|
|
58
|
+
def relative_luminance(color)
|
|
59
|
+
color_hex = color.start_with?("#") ? color : color_name_to_hex(color)
|
|
60
|
+
channels = hex_to_rgba(color_hex).first(3).map {|channel| channel / 255.0 }
|
|
61
|
+
linear = channels.map {|channel| channel <= 0.03928 ? channel / 12.92 : (((channel + 0.055) / 1.055)**2.4) }
|
|
62
|
+
(0.2126 * linear[0]) + (0.7152 * linear[1]) + (0.0722 * linear[2])
|
|
63
|
+
end
|
|
64
|
+
|
|
36
65
|
##============================================================##
|
|
37
66
|
## To transform a hex color to rgba
|
|
38
67
|
##============================================================##
|
|
@@ -88,5 +117,94 @@ module ImmosquareColors
|
|
|
88
117
|
rgba_to_hex([shaded_r, shaded_g, shaded_b, a])
|
|
89
118
|
end
|
|
90
119
|
|
|
120
|
+
##============================================================##
|
|
121
|
+
## Mix two colors in sRGB, `weight` being the share of the second
|
|
122
|
+
## one. Generalises tint (mix with white) and shade (mix with
|
|
123
|
+
## black), and mirrors what CSS `color-mix(in srgb, ...)` computes,
|
|
124
|
+
## so a value derived here matches what a browser would render.
|
|
125
|
+
## Alpha is carried over from the first color.
|
|
126
|
+
##============================================================##
|
|
127
|
+
def mix_colors(color_one, color_two, weight)
|
|
128
|
+
one_hex = color_one.start_with?("#") ? color_one : color_name_to_hex(color_one)
|
|
129
|
+
two_hex = color_two.start_with?("#") ? color_two : color_name_to_hex(color_two)
|
|
130
|
+
r1, g1, b1, a1 = hex_to_rgba(one_hex)
|
|
131
|
+
r2, g2, b2 = hex_to_rgba(two_hex)
|
|
132
|
+
mixed = [[r1, r2], [g1, g2], [b1, b2]].map {|from, to| (((1 - weight) * from) + (weight * to)).round }
|
|
133
|
+
rgba_to_hex(mixed + [a1].compact)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
##============================================================##
|
|
137
|
+
## The color sitting opposite on the color wheel: same saturation
|
|
138
|
+
## and lightness, hue rotated by 180°. Useful to derive a secondary
|
|
139
|
+
## color that cannot be mistaken for a tint of the primary one.
|
|
140
|
+
##============================================================##
|
|
141
|
+
def get_opposite_color(color)
|
|
142
|
+
h, s, l = hex_to_hsl(color)
|
|
143
|
+
hsl_to_hex([h + 180, s, l])
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
##============================================================##
|
|
147
|
+
## Whether two colors are far enough apart to be read together.
|
|
148
|
+
## `:level` accepts :aa (4.5, body text), :aa_large (3, large text
|
|
149
|
+
## and graphical objects such as icons or logos) and :aaa (7).
|
|
150
|
+
##============================================================##
|
|
151
|
+
def accessible_contrast?(color_one, color_two, options = {})
|
|
152
|
+
thresholds = {:aa => 4.5, :aa_large => 3.0, :aaa => 7.0}
|
|
153
|
+
threshold = thresholds[(options[:level] || :aa).to_sym] || thresholds[:aa]
|
|
154
|
+
contrast_ratio(color_one, color_two) >= threshold
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
##============================================================##
|
|
158
|
+
## To transform a hex color to HSL, hue in degrees (0...360),
|
|
159
|
+
## saturation and lightness between 0 and 1.
|
|
160
|
+
##============================================================##
|
|
161
|
+
def hex_to_hsl(color)
|
|
162
|
+
color_hex = color.start_with?("#") ? color : color_name_to_hex(color)
|
|
163
|
+
r, g, b = hex_to_rgba(color_hex).first(3).map {|channel| channel / 255.0 }
|
|
164
|
+
high, low = [r, g, b].minmax.reverse
|
|
165
|
+
lightness = (high + low) / 2.0
|
|
166
|
+
return [0.0, 0.0, lightness.round(4)] if high == low
|
|
167
|
+
|
|
168
|
+
delta = high - low
|
|
169
|
+
saturation = lightness > 0.5 ? delta / (2.0 - high - low) : delta / (high + low)
|
|
170
|
+
hue = case high
|
|
171
|
+
when r then ((g - b) / delta) + (g < b ? 6.0 : 0.0)
|
|
172
|
+
when g then ((b - r) / delta) + 2.0
|
|
173
|
+
else ((r - g) / delta) + 4.0
|
|
174
|
+
end
|
|
175
|
+
[(hue * 60.0).round(2), saturation.round(4), lightness.round(4)]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
##============================================================##
|
|
179
|
+
## To transform an HSL array back to hex. The hue wraps around, so
|
|
180
|
+
## a rotation past 360° needs no clamping by the caller.
|
|
181
|
+
##============================================================##
|
|
182
|
+
def hsl_to_hex(hsl)
|
|
183
|
+
hue, saturation, lightness = hsl
|
|
184
|
+
hue = (hue % 360) / 360.0
|
|
185
|
+
return rgba_to_hex([(lightness * 255).round] * 3) if saturation == 0
|
|
186
|
+
|
|
187
|
+
second = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - (lightness * saturation)
|
|
188
|
+
first = (2 * lightness) - second
|
|
189
|
+
rgba_to_hex([1.0 / 3, 0, -1.0 / 3].map {|offset| (hue_to_channel(first, second, hue + offset) * 255).round })
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
##============================================================##
|
|
196
|
+
## One channel of an HSL → RGB conversion, from the two
|
|
197
|
+
## intermediate values and the hue shifted for that channel.
|
|
198
|
+
##============================================================##
|
|
199
|
+
def hue_to_channel(first, second, hue)
|
|
200
|
+
hue += 1 if hue < 0
|
|
201
|
+
hue -= 1 if hue > 1
|
|
202
|
+
return first + ((second - first) * 6 * hue) if hue < 1.0 / 6
|
|
203
|
+
return second if hue < 1.0 / 2
|
|
204
|
+
return first + ((second - first) * ((2.0 / 3) - hue) * 6) if hue < 2.0 / 3
|
|
205
|
+
|
|
206
|
+
first
|
|
207
|
+
end
|
|
208
|
+
|
|
91
209
|
end
|
|
92
210
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: immosquare-colors
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
8
|
-
autorequire:
|
|
7
|
+
- immosquare
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: immosquare-constants
|
|
@@ -30,8 +29,8 @@ dependencies:
|
|
|
30
29
|
- - ">="
|
|
31
30
|
- !ruby/object:Gem::Version
|
|
32
31
|
version: 0.1.3
|
|
33
|
-
description: Provides methods to
|
|
34
|
-
convert HEX
|
|
32
|
+
description: Provides methods to derive a black/white complementary color from luminance,
|
|
33
|
+
tint or shade a color, convert HEX <-> RGBA, and resolve named colors to HEX.
|
|
35
34
|
email:
|
|
36
35
|
- jules@immosquare.com
|
|
37
36
|
executables: []
|
|
@@ -40,11 +39,10 @@ extra_rdoc_files: []
|
|
|
40
39
|
files:
|
|
41
40
|
- lib/immosquare-colors.rb
|
|
42
41
|
- lib/immosquare-colors/version.rb
|
|
43
|
-
homepage: https://github.com/
|
|
42
|
+
homepage: https://github.com/immosquare/immosquare-colors
|
|
44
43
|
licenses:
|
|
45
44
|
- MIT
|
|
46
45
|
metadata: {}
|
|
47
|
-
post_install_message:
|
|
48
46
|
rdoc_options: []
|
|
49
47
|
require_paths:
|
|
50
48
|
- lib
|
|
@@ -52,15 +50,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
52
50
|
requirements:
|
|
53
51
|
- - ">="
|
|
54
52
|
- !ruby/object:Gem::Version
|
|
55
|
-
version: 2.
|
|
53
|
+
version: 3.2.6
|
|
56
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
55
|
requirements:
|
|
58
56
|
- - ">="
|
|
59
57
|
- !ruby/object:Gem::Version
|
|
60
58
|
version: '0'
|
|
61
59
|
requirements: []
|
|
62
|
-
rubygems_version:
|
|
63
|
-
signing_key:
|
|
60
|
+
rubygems_version: 4.0.20
|
|
64
61
|
specification_version: 4
|
|
65
|
-
summary: Ruby utility for complementary color derivation and color
|
|
62
|
+
summary: Ruby utility for complementary color derivation, tinting, shading and color
|
|
63
|
+
conversions.
|
|
66
64
|
test_files: []
|