UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

185 lines • 7.51 kB
// Technically this should be shades and tints, but for simplicity we'll call everything a shade. /* This utility module is used with theming. Given a color to shade, whether the theme is inverted (i.e. is a dark color), * and the desired shade enum, this will return an appropriate shade of that color. */ import { MAX_COLOR_RGBA } from './colors'; import * as Colors from './colors'; import { assign } from '../../Utilities'; // Soften: to get closer to the background color's luminance (softening with a white background would be lightening, with black it'd be darkening) // Strongen: opposite of soften // Luminance multiplier constants for generating shades of a given color var WhiteShadeTableBG = [0.027, 0.043, 0.082, 0.145, 0.184, 0.216, 0.349, 0.537]; // white bg var BlackTintTableBG = [0.537, 0.45, 0.349, 0.216, 0.184, 0.145, 0.082, 0.043]; // black bg var WhiteShadeTable = [0.537, 0.349, 0.216, 0.184, 0.145, 0.082, 0.043, 0.027]; // white fg var BlackTintTable = [0.537, 0.45, 0.349, 0.216, 0.184, 0.145, 0.082, 0.043]; // black fg var LumTintTable = [0.88, 0.77, 0.66, 0.55, 0.44, 0.33, 0.22, 0.11]; // light (strongen all) var LumShadeTable = [0.11, 0.22, 0.33, 0.44, 0.55, 0.66, 0.77, 0.88]; // dark (soften all) var ColorTintTable = [0.96, 0.84, 0.7, 0.4, 0.12]; // default soften var ColorShadeTable = [0.1, 0.24, 0.44]; // default strongen // If the given shade's luminance is below/above these values, we'll swap to using the White/Black tables above var LowLuminanceThreshold = 0.2; var HighLuminanceThreshold = 0.8; /** Shades of a given color, from softest to strongest. */ export var Shade; (function (Shade) { Shade[Shade["Unshaded"] = 0] = "Unshaded"; Shade[Shade["Shade1"] = 1] = "Shade1"; Shade[Shade["Shade2"] = 2] = "Shade2"; Shade[Shade["Shade3"] = 3] = "Shade3"; Shade[Shade["Shade4"] = 4] = "Shade4"; Shade[Shade["Shade5"] = 5] = "Shade5"; Shade[Shade["Shade6"] = 6] = "Shade6"; Shade[Shade["Shade7"] = 7] = "Shade7"; Shade[Shade["Shade8"] = 8] = "Shade8"; // remember to update isValidShade()! })(Shade || (Shade = {})); /** * Returns true if the argument is a valid Shade value * @param {Shade} shade The Shade value to validate. */ export function isValidShade(shade) { 'use strict'; return typeof shade === 'number' && shade >= Shade.Unshaded && shade <= Shade.Shade8; } function _isBlack(color) { return color.r === 0 && color.g === 0 && color.b === 0; } function _isWhite(color) { return color.r === MAX_COLOR_RGBA && color.g === MAX_COLOR_RGBA && color.b === MAX_COLOR_RGBA; } function _darken(hsv, factor) { return { h: hsv.h, s: hsv.s, v: _clamp(hsv.v - hsv.v * factor, 0, 100) }; } function _lighten(hsv, factor) { return { h: hsv.h, s: _clamp(hsv.s - hsv.s * factor, 0, 100), v: _clamp(hsv.v + (100 - hsv.v) * factor, 0, 100) }; } function _clamp(n, min, max) { return n; // Math.max(min, Math.min(n, max)); } export function isDark(color) { return Colors.hsv2hsl(color.h, color.s, color.v).l < 50; } /** * Given a color and a shade specification, generates the requested shade of the color. * Logic: * if white * darken via tables defined above * if black * lighten * if light * strongen * if dark * soften * else default * soften or strongen depending on shade# * @param {IColor} color The base color whose shade is to be computed * @param {Shade} shade The shade of the base color to compute * @param {Boolean} isInverted Default false. Whether the given theme is inverted (reverse strongen/soften logic) */ export function getShade(color, shade, isInverted) { 'use strict'; if (isInverted === void 0) { isInverted = false; } if (!color) { return null; } if (shade === Shade.Unshaded || !isValidShade(shade)) { return color; } var hsl = Colors.hsv2hsl(color.h, color.s, color.v); var hsv = { h: color.h, s: color.s, v: color.v }; var tableIndex = shade - 1; var _soften = _lighten; var _strongen = _darken; if (isInverted) { _soften = _darken; _strongen = _lighten; } if (_isWhite(color)) { // white hsv = _darken(hsv, WhiteShadeTable[tableIndex]); } else if (_isBlack(color)) { // black hsv = _lighten(hsv, BlackTintTable[tableIndex]); } else if (hsl.l / 100 > HighLuminanceThreshold) { // light hsv = _strongen(hsv, LumShadeTable[tableIndex]); } else if (hsl.l / 100 < LowLuminanceThreshold) { // dark hsv = _soften(hsv, LumTintTable[tableIndex]); } else { // default if (tableIndex < ColorTintTable.length) { hsv = _soften(hsv, ColorTintTable[tableIndex]); } else { hsv = _strongen(hsv, ColorShadeTable[tableIndex - ColorTintTable.length]); } } return Colors.getColorFromRGBA(assign(Colors.hsv2rgb(hsv.h, hsv.s, hsv.v), { a: color.a })); } // Background shades/tints are generated differently. The provided color will be guaranteed // to be the darkest or lightest one. If it is <50% luminance, it will always be the darkest, // otherwise it will always be the lightest. export function getBackgroundShade(color, shade, isInverted) { 'use strict'; if (isInverted === void 0) { isInverted = false; } if (!color) { return null; } if (shade === Shade.Unshaded || !isValidShade(shade)) { return color; } var hsv = { h: color.h, s: color.s, v: color.v }; var tableIndex = shade - 1; if (!isInverted) { // lightish hsv = _darken(hsv, WhiteShadeTableBG[tableIndex]); } else { // default: if (hsl.l / 100 < .5) { // darkish hsv = _lighten(hsv, BlackTintTableBG[BlackTintTable.length - 1 - tableIndex]); } return Colors.getColorFromRGBA(assign(Colors.hsv2rgb(hsv.h, hsv.s, hsv.v), { a: color.a })); } /* Calculates the contrast ratio between two colors. Used for verifying * color pairs meet minimum accessibility requirements. * See: https://www.w3.org/TR/WCAG20/ section 1.4.3 */ export function getContrastRatio(color1, color2) { // Formula defined by: http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#contrast-ratiodef // relative luminance: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef /* calculate the intermediate value needed to calculating relative luminance */ function _getThing(x) { if (x <= 0.03928) { return x / 12.92; } else { return Math.pow((x + 0.055) / 1.055, 2.4); } } var r1 = _getThing(color1.r / MAX_COLOR_RGBA); var g1 = _getThing(color1.g / MAX_COLOR_RGBA); var b1 = _getThing(color1.b / MAX_COLOR_RGBA); var L1 = 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1; // relative luminance of first color L1 += 0.05; var r2 = _getThing(color2.r / MAX_COLOR_RGBA); var g2 = _getThing(color2.g / MAX_COLOR_RGBA); var b2 = _getThing(color2.b / MAX_COLOR_RGBA); var L2 = 0.2126 * r2 + 0.7152 * g2 + 0.0722 * b2; // relative luminance of second color L2 += 0.05; // return the lighter color divided by darker return L1 / L2 > 1 ? L1 / L2 : L2 / L1; } //# sourceMappingURL=shades.js.map