Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color function updates - #RRGGBBAA and CSS Variables #3291

Merged
merged 4 commits into from
Jul 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 84 additions & 41 deletions lib/less/functions/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@ var Dimension = require('../tree/dimension'),
function clamp(val) {
return Math.min(1, Math.max(0, val));
}
function hsla(color) {
return colorFunctions.hsla(color.h, color.s, color.l, color.a);
function hsla(origColor, hsl) {
var color = colorFunctions.hsla(hsl.h, hsl.s, hsl.l, hsl.a);
if (color) {
if (origColor.value &&
/^(rgb|hsl)/.test(origColor.value)) {
color.value = origColor.value;
} else {
color.value = 'rgb';
}
return color;
}
}
function number(n) {
if (n instanceof Dimension) {
Expand All @@ -32,46 +41,79 @@ function scaled(n, size) {
}
colorFunctions = {
rgb: function (r, g, b) {
return colorFunctions.rgba(r, g, b, 1.0);
var color = colorFunctions.rgba(r, g, b, 1.0);
if (color) {
color.value = 'rgb';
return color;
}
},
rgba: function (r, g, b, a) {
var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
a = number(a);
return new Color(rgb, a);
try {
if (r instanceof Color) {
if (g) {
a = number(g);
} else {
a = r.alpha;
}
return new Color(r.rgb, a, 'rgba');
}
var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
a = number(a);
return new Color(rgb, a, 'rgba');
}
catch (e) {}
},
hsl: function (h, s, l) {
return colorFunctions.hsla(h, s, l, 1.0);
var color = colorFunctions.hsla(h, s, l, 1.0);
if (color) {
color.value = 'hsl';
return color;
}
},
hsla: function (h, s, l, a) {
try {
if (h instanceof Color) {
if (s) {
a = number(s);
} else {
a = h.alpha;
}
return new Color(h.rgb, a, 'hsla');
}

var m1, m2;
var m1, m2;

function hue(h) {
h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
if (h * 6 < 1) {
return m1 + (m2 - m1) * h * 6;
}
else if (h * 2 < 1) {
return m2;
}
else if (h * 3 < 2) {
return m1 + (m2 - m1) * (2 / 3 - h) * 6;
}
else {
return m1;
function hue(h) {
h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
if (h * 6 < 1) {
return m1 + (m2 - m1) * h * 6;
}
else if (h * 2 < 1) {
return m2;
}
else if (h * 3 < 2) {
return m1 + (m2 - m1) * (2 / 3 - h) * 6;
}
else {
return m1;
}
}
}

h = (number(h) % 360) / 360;
s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
h = (number(h) % 360) / 360;
s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));

m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
m1 = l * 2 - m2;
m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
m1 = l * 2 - m2;

return colorFunctions.rgba(hue(h + 1 / 3) * 255,
hue(h) * 255,
hue(h - 1 / 3) * 255,
a);
var rgb = [
hue(h + 1 / 3) * 255,
hue(h) * 255,
hue(h - 1 / 3) * 255
];
a = number(a);
return new Color(rgb, a, 'hsla');
}
catch (e) {}
},

hsv: function(h, s, v) {
Expand Down Expand Up @@ -159,7 +201,7 @@ colorFunctions = {
hsl.s += amount.value / 100;
}
hsl.s = clamp(hsl.s);
return hsla(hsl);
return hsla(color, hsl);
},
desaturate: function (color, amount, method) {
var hsl = color.toHSL();
Expand All @@ -171,7 +213,7 @@ colorFunctions = {
hsl.s -= amount.value / 100;
}
hsl.s = clamp(hsl.s);
return hsla(hsl);
return hsla(color, hsl);
},
lighten: function (color, amount, method) {
var hsl = color.toHSL();
Expand All @@ -183,7 +225,7 @@ colorFunctions = {
hsl.l += amount.value / 100;
}
hsl.l = clamp(hsl.l);
return hsla(hsl);
return hsla(color, hsl);
},
darken: function (color, amount, method) {
var hsl = color.toHSL();
Expand All @@ -195,7 +237,7 @@ colorFunctions = {
hsl.l -= amount.value / 100;
}
hsl.l = clamp(hsl.l);
return hsla(hsl);
return hsla(color, hsl);
},
fadein: function (color, amount, method) {
var hsl = color.toHSL();
Expand All @@ -207,7 +249,7 @@ colorFunctions = {
hsl.a += amount.value / 100;
}
hsl.a = clamp(hsl.a);
return hsla(hsl);
return hsla(color, hsl);
},
fadeout: function (color, amount, method) {
var hsl = color.toHSL();
Expand All @@ -219,22 +261,22 @@ colorFunctions = {
hsl.a -= amount.value / 100;
}
hsl.a = clamp(hsl.a);
return hsla(hsl);
return hsla(color, hsl);
},
fade: function (color, amount) {
var hsl = color.toHSL();

hsl.a = amount.value / 100;
hsl.a = clamp(hsl.a);
return hsla(hsl);
return hsla(color, hsl);
},
spin: function (color, amount) {
var hsl = color.toHSL();
var hue = (hsl.h + amount.value) % 360;

hsl.h = hue < 0 ? 360 + hue : hue;

return hsla(hsl);
return hsla(color, hsl);
},
//
// Copyright (c) 2006-2009 Hampton Catlin, Natalie Weizenbaum, and Chris Eppstein
Expand Down Expand Up @@ -338,16 +380,17 @@ colorFunctions = {
},
color: function(c) {
if ((c instanceof Quoted) &&
(/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) {
return new Color(c.value.slice(1));
(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3,4})$/i.test(c.value))) {
var val = c.value.slice(1);
return new Color(val, undefined, '#' + val);
}
if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
c.value = undefined;
return c;
}
throw {
type: 'Argument',
message: 'argument must be a color keyword or 3/6 digit hex e.g. #FFF'
message: 'argument must be a color keyword or 3|4|6|8 digit hex e.g. #FFF'
};
},
tint: function(color, amount) {
Expand Down
11 changes: 2 additions & 9 deletions lib/less/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -636,15 +636,8 @@ var Parser = function Parser(context, imports, fileInfo) {
color: function () {
var rgb;

if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
// strip colons, brackets, whitespaces and other characters that should not
// definitely be part of color string
var colorCandidateString = rgb.input.match(/^#([\w]+).*/);
colorCandidateString = colorCandidateString[1];
if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
error('Invalid HEX color code');
}
return new(tree.Color)(rgb[1], undefined, '#' + colorCandidateString);
if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3,4})/))) {
return new(tree.Color)(rgb[1], undefined, rgb[0]);
}
},

Expand Down
76 changes: 58 additions & 18 deletions lib/less/tree/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var Node = require('./node'),
// RGB Colors - #ff0014, #eee
//
var Color = function (rgb, a, originalForm) {
var self = this;
//
// The end goal here, is to parse the arguments
// into an integer triplet, such as `128, 255, 0`
Expand All @@ -13,16 +14,26 @@ var Color = function (rgb, a, originalForm) {
//
if (Array.isArray(rgb)) {
this.rgb = rgb;
} else if (rgb.length == 6) {
this.rgb = rgb.match(/.{2}/g).map(function (c) {
return parseInt(c, 16);
} else if (rgb.length >= 6) {
this.rgb = [];
rgb.match(/.{2}/g).map(function (c, i) {
if (i < 3) {
self.rgb.push(parseInt(c, 16));
} else {
self.alpha = (parseInt(c, 16)) / 255;
}
});
} else {
this.rgb = rgb.split('').map(function (c) {
return parseInt(c + c, 16);
this.rgb = [];
rgb.split('').map(function (c, i) {
if (i < 3) {
self.rgb.push(parseInt(c + c, 16));
} else {
self.alpha = (parseInt(c + c, 16)) / 255;
}
});
}
this.alpha = typeof a === 'number' ? a : 1;
this.alpha = this.alpha || (typeof a === 'number' ? a : 1);
if (typeof originalForm !== 'undefined') {
this.value = originalForm;
}
Expand Down Expand Up @@ -57,25 +68,54 @@ Color.prototype.genCSS = function (context, output) {
output.add(this.toCSS(context));
};
Color.prototype.toCSS = function (context, doNotCompress) {
var compress = context && context.compress && !doNotCompress, color, alpha;
var compress = context && context.compress && !doNotCompress, color, alpha,
colorFunction, args = [];

// `value` is set if this color was originally
// converted from a named color string so we need
// to respect this and try to output named color too.
alpha = this.fround(context, this.alpha);

if (this.value) {
return this.value;
if (this.value.indexOf('rgb') === 0) {
if (alpha < 1) {
colorFunction = 'rgba';
}
} else if (this.value.indexOf('hsl') === 0) {
if (alpha === 1) {
colorFunction = 'hsl';
} else {
colorFunction = 'hsla';
}
} else {
return this.value;
}
} else {
if (alpha < 1) {
colorFunction = 'rgba';
}
}

// If we have some transparency, the only way to represent it
// is via `rgba`. Otherwise, we use the hex representation,
// which has better compatibility with older browsers.
// Values are capped between `0` and `255`, rounded and zero-padded.
alpha = this.fround(context, this.alpha);
if (alpha < 1) {
return 'rgba(' + this.rgb.map(function (c) {
return clamp(Math.round(c), 255);
}).concat(clamp(alpha, 1))
.join(',' + (compress ? '' : ' ')) + ')';
switch (colorFunction) {
case 'rgba':
args = this.rgb.map(function (c) {
return clamp(Math.round(c), 255);
}).concat(clamp(alpha, 1));
break;
case 'hsla':
args.push(clamp(alpha, 1));
case 'hsl':
color = this.toHSL();
args = [
this.fround(context, color.h),
this.fround(context, color.s * 100) + '%',
this.fround(context, color.l * 100) + '%'
].concat(args);
}

if (colorFunction) {
// Values are capped between `0` and `255`, rounded and zero-padded.
return colorFunction + '(' + args.join(',' + (compress ? '' : ' ')) + ')';
}

color = this.toRGB();
Expand Down
21 changes: 18 additions & 3 deletions test/css/colors.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
color: #1a0000ff;
}
#alpha #hsla {
color: rgba(61, 45, 41, 0.6);
color: hsla(11, 20%, 20%, 0.6);
}
#overflow .a {
color: #000000;
Expand All @@ -47,10 +47,10 @@
color: #333333;
}
#808080 {
color: #808080;
color: hsl(0, 0%, 50%);
}
#00ff00 {
color: #00ff00;
color: hsl(120, 100%, 50%);
}
.lightenblue {
color: #3333ff;
Expand Down Expand Up @@ -85,3 +85,18 @@
color: 255;
border-color: rgba(255, 0, 0, 0.5);
}
#rrggbbaa {
test-1: #55FF5599;
test-2: #5F59;
test-3: rgba(136, 255, 136, 0.6);
test-4: rgba(85, 255, 85, 0.1);
test-5: rgba(85, 255, 85, 0.6);
test-6: rgba(85, 255, 85, 0.6);
test-7: rgba(85, 255, 85, 0.5);
test-8: rgba(var(--color-accent), 0.2);
test-9: rgb(var(--color-accent));
test-9: hsla(var(--color-accent));
test-10: #55FF5599;
test-11: hsla(120, 100%, 66.66666667%, 0.6);
test-12: hsla(120, 100%, 66.66666667%, 0.5);
}
Loading