@razorpay/blade
Version:
The Design System that powers Razorpay
301 lines (289 loc) • 10.8 kB
JavaScript
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import tinycolor from 'tinycolor2';
import overrideTheme from './overrideTheme.js';
import bladeTheme from './bladeTheme.js';
import '../global/index.js';
import '../../utils/logger/index.js';
import { throwBladeError } from '../../utils/logger/logger.js';
import { opacity } from '../global/opacity.js';
import { colors } from '../global/colors.js';
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
// WCAG2ContrastOptions are the options used to determine if a color is readable
var WCAG2ContrastOptions = {
level: 'AAA',
size: 'large'
};
/**
* getColorWithOpacity
* @param color - The color to add opacity to
* @param opacity - The opacity to add to the color
* @returns The color with the opacity added
* @example
* getColorWithOpacity('#fff', 0.5) // returns 'hsla(0, 0%, 100%, 0.5)'
*
**/
var getColorWithOpacity = function getColorWithOpacity(color, opacity) {
return tinycolor(color).setAlpha(opacity).toHslString();
};
/**
*
* @description
* Generates a chromatic color palette based on the base color passed in.
* The base color is used to generate a palette of 11 colors, 5 shades lighter and 5 shades darker than the base color.
* @param baseColorInput - The base color to generate the chromatic color palette from in hex, rgb, or hsl format
* @returns Array of chromatic color palette
*/
var generateChromaticBrandColors = function generateChromaticBrandColors(baseColorInput) {
var baseColor = tinycolor(baseColorInput);
var baseColorHslString = baseColor.toHslString();
if (true) {
if (!baseColor.isValid()) {
throwBladeError({
message: 'Invalid brandColor passed',
moduleName: 'createTheme'
});
}
}
var palette = [baseColorHslString]; // Include the original color
var brightness = tinycolor(baseColor).getBrightness();
// Determine how much to lighten or darken the colors depending on the brightness of the base color
var lightnessFactor = brightness > 150 ? 3 : 6;
var darknessFactor = brightness < 50 ? 3 : 5;
var currentColor = baseColor;
// Generate shades lighter
for (var lightShadeIndex = 0; lightShadeIndex < 6; lightShadeIndex++) {
currentColor = currentColor.brighten(lightnessFactor);
palette.push(currentColor.toHslString());
}
currentColor = tinycolor(baseColorHslString); // Reset to the base color
// Generate shades darker
for (var darkShadeIndex = 0; darkShadeIndex < 4; darkShadeIndex++) {
currentColor = currentColor.darken(darknessFactor);
palette.unshift(currentColor.toHslString()); // Add shades at the beginning of the palette
}
var colorPalette = palette.reverse();
var brandPrimaryColor = colorPalette[6];
var brandColors = {
'50': colorPalette[0],
'100': colorPalette[1],
'200': colorPalette[2],
'300': colorPalette[3],
'400': colorPalette[4],
'500': colorPalette[5],
'600': brandPrimaryColor,
'700': colorPalette[7],
'800': colorPalette[8],
'900': colorPalette[9],
'1000': colorPalette[10],
a50: getColorWithOpacity(brandPrimaryColor, opacity[100]),
a150: getColorWithOpacity(brandPrimaryColor, opacity[100]),
a100: getColorWithOpacity(brandPrimaryColor, opacity[200]),
a200: getColorWithOpacity(brandPrimaryColor, opacity[300]),
a400: getColorWithOpacity(brandPrimaryColor, opacity[400])
};
return brandColors;
};
/**
*
* @param brandColors - The brand colors to use to override the light theme
* @description Returns overrides for the light theme with the brand colors passed in
* @returns Overrides for the light theme with the custom brand colors
*/
var getOnLightOverrides = function getOnLightOverrides(brandColors) {
// Select the most readable color to use as the foreground color on top of surface color
// For example: On Secondary Button where the background color is surface color, the text color should be either the brand color or dark color depending on which is more readable on top of that surface color
var foregroundOnSurface = tinycolor.isReadable(colors.neutral.blueGrayLight[50], brandColors[600], WCAG2ContrastOptions) ? brandColors[600] : colors.neutral.blueGrayLight[1100];
var foregroundOnBrand = tinycolor.mostReadable(brandColors[900], [colors.neutral.white[500], colors.neutral.black[500]], WCAG2ContrastOptions).toHslString();
// Overrides for the light theme with the brand colors passed in
var lightThemeOverrides = {
interactive: {
background: {
primary: {
"default": brandColors[600],
highlighted: brandColors[700],
disabled: brandColors.a100,
faded: brandColors.a100,
fadedHighlighted: brandColors.a150
}
},
border: {
primary: {
"default": brandColors[600],
highlighted: brandColors[700],
disabled: brandColors.a100,
faded: brandColors.a100
}
},
text: {
primary: {
normal: foregroundOnSurface,
disabled: brandColors.a200,
muted: foregroundOnSurface,
subtle: foregroundOnSurface
},
onPrimary: {
normal: foregroundOnBrand,
disabled: foregroundOnBrand,
muted: foregroundOnBrand,
subtle: foregroundOnBrand
}
},
icon: {
primary: {
normal: foregroundOnSurface,
disabled: brandColors.a200,
muted: foregroundOnSurface,
subtle: foregroundOnSurface
},
onPrimary: {
normal: foregroundOnBrand,
disabled: foregroundOnBrand,
muted: foregroundOnBrand,
subtle: foregroundOnBrand
}
}
},
surface: {
background: {
primary: {
intense: brandColors[600],
subtle: brandColors.a150
}
},
border: {
primary: {
normal: brandColors[600],
muted: brandColors.a200
}
},
icon: {
primary: {
normal: brandColors[600]
}
},
text: {
primary: {
normal: brandColors[600]
}
}
}
};
return lightThemeOverrides;
};
/**
*
* @param brandColors - The brand colors to use to override the dark theme
* @description Returns overrides for the dark theme with the brand colors passed in
* @returns Overrides for the dark theme with the custom brand colors
*/
var getOnDarkOverrides = function getOnDarkOverrides(brandColors) {
// Select the most readable color to use as the foreground color on top of surface color
// For example: On Secondary Button where the background color is surface color, the text color should be either the brand color or dark color depending on which is more readable on top of that surface color
var foregroundOnSurface = tinycolor.isReadable(colors.neutral.blueGrayDark[1100], brandColors[400], WCAG2ContrastOptions) ? brandColors[400] : colors.neutral.blueGrayDark[0];
var foregroundOnBrand = tinycolor.mostReadable(brandColors[900], [colors.neutral.white[500], colors.neutral.black[500]], WCAG2ContrastOptions).toHslString();
// Overrides for the dark theme with the brand colors passed in
var darkThemeOverrides = {
interactive: {
background: {
primary: {
"default": brandColors[600],
highlighted: brandColors[700],
disabled: brandColors.a100,
faded: brandColors.a100,
fadedHighlighted: brandColors.a150
}
},
border: {
primary: {
"default": brandColors[600],
highlighted: brandColors[700],
disabled: brandColors.a100,
faded: brandColors.a100
}
},
text: {
primary: {
normal: foregroundOnSurface,
disabled: brandColors.a400,
muted: foregroundOnSurface,
subtle: foregroundOnSurface
},
onPrimary: {
normal: foregroundOnBrand,
disabled: foregroundOnBrand,
muted: foregroundOnBrand,
subtle: foregroundOnBrand
}
},
icon: {
primary: {
normal: foregroundOnSurface,
disabled: brandColors.a400,
muted: foregroundOnSurface,
subtle: foregroundOnSurface
},
onPrimary: {
normal: foregroundOnBrand,
disabled: foregroundOnBrand,
muted: foregroundOnBrand,
subtle: foregroundOnBrand
}
}
},
surface: {
background: {
primary: {
intense: brandColors[600],
subtle: brandColors.a150
}
},
border: {
primary: {
normal: brandColors[600],
muted: brandColors.a200
}
},
icon: {
primary: {
normal: brandColors[600]
}
}
}
};
return darkThemeOverrides;
};
/**
* @param {Object} themeConfig - The brand color and overrides to apply to the theme
* @param {string} themeConfig.brandColor - The brand color to use to generate the theme. Can be in hex, rgb, or hsl format.
* @description
* Creates a Blade Theme based on the custom brand color
* @returns The Theme Tokens with the custom brand colors
* @example
* const { theme, brandColors } = createTheme({ brandColor: '#19BEA2'})
**/
var createTheme = function createTheme(_ref) {
var brandColor = _ref.brandColor;
var chromaticBrandColors = generateChromaticBrandColors(brandColor);
// Get onLight overrides
var brandedLightTheme = getOnLightOverrides(chromaticBrandColors);
// Get onDark overrides
var brandedDarkTheme = getOnDarkOverrides(chromaticBrandColors);
// Override the payment theme with the brand colors
var brandedThemeTokens = overrideTheme({
baseThemeTokens: bladeTheme,
overrides: {
name: "custom-".concat(tinycolor(brandColor).toHex()),
colors: {
onLight: _objectSpread({}, brandedLightTheme),
onDark: _objectSpread({}, brandedDarkTheme)
}
}
});
return {
theme: brandedThemeTokens,
brandColors: chromaticBrandColors
};
};
export { createTheme };
//# sourceMappingURL=createTheme.js.map