UNPKG

@wordpress/components

Version:
143 lines (118 loc) 3.97 kB
/** * External dependencies */ import { colord, extend } from 'colord'; import a11yPlugin from 'colord/plugins/a11y'; import namesPlugin from 'colord/plugins/names'; /** * WordPress dependencies */ import warning from '@wordpress/warning'; /** * Internal dependencies */ import type { ThemeInputValues, ThemeOutputValues } from './types'; import { COLORS } from '../utils'; extend( [ namesPlugin, a11yPlugin ] ); export function generateThemeVariables( inputs: ThemeInputValues ): ThemeOutputValues { validateInputs( inputs ); const generatedColors = { ...generateAccentDependentColors( inputs.accent ), ...generateBackgroundDependentColors( inputs.background ), }; warnContrastIssues( checkContrasts( inputs, generatedColors ) ); return { colors: generatedColors }; } function validateInputs( inputs: ThemeInputValues ) { for ( const [ key, value ] of Object.entries( inputs ) ) { if ( typeof value !== 'undefined' && ! colord( value ).isValid() ) { warning( `wp.components.Theme: "${ value }" is not a valid color value for the '${ key }' prop.` ); } } } export function checkContrasts( inputs: ThemeInputValues, outputs: ThemeOutputValues[ 'colors' ] ) { const background = inputs.background || COLORS.white; const accent = inputs.accent || '#3858e9'; const foreground = outputs.foreground || COLORS.gray[ 900 ]; const gray = outputs.gray || COLORS.gray; return { accent: colord( background ).isReadable( accent ) ? undefined : `The background color ("${ background }") does not have sufficient contrast against the accent color ("${ accent }").`, foreground: colord( background ).isReadable( foreground ) ? undefined : `The background color provided ("${ background }") does not have sufficient contrast against the standard foreground colors.`, grays: colord( background ).contrast( gray[ 600 ] ) >= 3 && colord( background ).contrast( gray[ 700 ] ) >= 4.5 ? undefined : `The background color provided ("${ background }") cannot generate a set of grayscale foreground colors with sufficient contrast. Try adjusting the color to be lighter or darker.`, }; } function warnContrastIssues( issues: ReturnType< typeof checkContrasts > ) { for ( const error of Object.values( issues ) ) { if ( error ) { warning( 'wp.components.Theme: ' + error ); } } } function generateAccentDependentColors( accent?: string ) { if ( ! accent ) { return {}; } return { accent, accentDarker10: colord( accent ).darken( 0.1 ).toHex(), accentDarker20: colord( accent ).darken( 0.2 ).toHex(), accentInverted: getForegroundForColor( accent ), }; } function generateBackgroundDependentColors( background?: string ) { if ( ! background ) { return {}; } const foreground = getForegroundForColor( background ); return { background, foreground, foregroundInverted: getForegroundForColor( foreground ), gray: generateShades( background, foreground ), }; } function getForegroundForColor( color: string ) { return colord( color ).isDark() ? COLORS.white : COLORS.gray[ 900 ]; } export function generateShades( background: string, foreground: string ) { // How much darkness you need to add to #fff to get the COLORS.gray[n] color const SHADES = { 100: 0.06, 200: 0.121, 300: 0.132, 400: 0.2, 600: 0.42, 700: 0.543, 800: 0.821, }; // Darkness of COLORS.gray[ 900 ], relative to #fff const limit = 0.884; const direction = colord( background ).isDark() ? 'lighten' : 'darken'; // Lightness delta between the background and foreground colors const range = Math.abs( colord( background ).toHsl().l - colord( foreground ).toHsl().l ) / 100; const result: Record< number, string > = {}; Object.entries( SHADES ).forEach( ( [ key, value ] ) => { result[ parseInt( key ) ] = colord( background ) [ direction ]( ( value / limit ) * range ) .toHex(); } ); return result as NonNullable< ThemeOutputValues[ 'colors' ][ 'gray' ] >; }