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

[BBT-105] Display custom notification when related background and foreground colours fail accessible colour contrast ratios #109

Draft
wants to merge 10 commits into
base: release/1.0.0
Choose a base branch
from
102 changes: 102 additions & 0 deletions src/editor/components/BBContrastChecker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Notice } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

const BBContrastChecker = ( { background, foreground } ) => {
/**
* If either colour is not provided, don't render anything
*/
if ( ! background || ! foreground ) {
return;
}

/**
* Get the luminance of a colour
*
* @param {string} colour The colour to convert to luminance value
*
* @return {number} The luminance value
*/
const getLuminance = ( colour ) => {
const rgb = colour
.match( /\w\w/g )
.map( ( c ) => parseInt( c, 16 ) / 255 );
const [ r, g, b ] = rgb.map( ( c ) => {
return c <= 0.03928
? c / 12.92
: Math.pow( ( c + 0.055 ) / 1.055, 2.4 );
} );
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
};

/**
* Compare two colours and return their contrast ratio.
*
* @param {string} colour1 This is an example function/method parameter description.
* @param {string} colour2 This is a second example.
*
* @return {number} The contrast ratio between the colours
*/
const getContrastRatio = ( colour1, colour2 ) => {
const luminance1 = getLuminance( colour1 );
const luminance2 = getLuminance( colour2 );
return (
( Math.max( luminance1, luminance2 ) + 0.05 ) /
( Math.min( luminance1, luminance2 ) + 0.05 )
);
};

/**
* Determine the colour contrast ratio
*/
const contrastRatio = getContrastRatio( background, foreground );

/**
* Determine the message to output
*/
const displayMessage =
contrastRatio >= 4.5
? __(
'The selected colours do not meet the colour contrast ratio for AAA (7:1) accessibility standards',
'themer'
)
: __(
'The selected colours do not meet the colour contrast ratio for AA (4.5:1) accessibility standards',
'themer'
);

/**
* Set the notice display level based on the contrast ratio
*/
const displayMessageImportance = contrastRatio >= 4.5 ? 'info' : 'warning';

return (
<>
<div className="contrast-checker">
<p className="contrast-checker-title">
{ __( 'WCAG Check:', 'themer' ) }
</p>
<p className="contrast-checker-ratio">
{ contrastRatio.toFixed( 1 ) } : 1
</p>
<div className="contrast-checker-badges">
<p className={ contrastRatio >= 4.5 ? 'pass' : ' fail' }>
&#x2713; { __( 'AA', 'themer' ) }
</p>
<p className={ contrastRatio >= 7 ? 'pass' : ' fail' }>
&#x2713; { __( 'AAA ', 'themer' ) }
</p>
</div>
</div>
{ contrastRatio < 7 && (
<Notice
status={ displayMessageImportance }
isDismissible={ false }
>
{ displayMessage }
</Notice>
) }
</>
);
};

export default BBContrastChecker;
19 changes: 15 additions & 4 deletions src/editor/components/StylesColor.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { set } from 'lodash';
import { set, debounce } from 'lodash';
import { __ } from '@wordpress/i18n';
import { useContext } from '@wordpress/element';
import { ColorPalette } from '@wordpress/components';
Expand All @@ -8,6 +8,7 @@ import getThemeOption from '../../utils/get-theme-option';
import EditorContext from '../context/EditorContext';
import StylesContext from '../context/StylesContext';
import Gradient from './StylesGradient';
import BBContrastChecker from './BBContrastChecker';

/**
* Reusable color control style component
Expand All @@ -24,22 +25,28 @@ const Color = ( { selector } ) => {
themeConfig
);

const onChange = ( newValue, key ) => {
/**
* Function to handle the colour palette changes
*
* @param {string} newValue The value of the setting
* @param {string} key The key of the setting
*/
const onChange = debounce( ( newValue, key ) => {
let config = structuredClone( userConfig );
config = set(
config,
[ selector, key ].join( '.' ),
hexToVar( newValue, themePalette ) ?? ''
);
setUserConfig( config );
};
}, 50 );

const colorPalettes = [ 'background', 'text' ].map( ( key ) => (
<div key={ key } className="themer--styles__item__column">
<span className="themer--styles__item__label">{ key }</span>
<ColorPalette
label={ __( 'Color', 'themer' ) }
colors={ themePalette }
label={ __( 'Color', 'themer' ) }
onChange={ ( value ) => onChange( value, key ) }
value={ varToHex( colorStyles[ key ], themePalette ) }
/>
Expand All @@ -51,6 +58,10 @@ const Color = ( { selector } ) => {
<span className="themer--styles__item__title">
{ __( 'Color', 'themer' ) }
</span>
<BBContrastChecker
background={ varToHex( colorStyles.background, themePalette ) }
foreground={ varToHex( colorStyles.text, themePalette ) }
/>
<div className="themer--styles__item__columns themer--styles__item__columns--2">
{ colorPalettes }
<Gradient selector={ `${ selector }.gradient` } />
Expand Down
82 changes: 82 additions & 0 deletions src/editor/styles/components/bb-contrast-checker.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.themer--styles__item:has(.contrast-checker) {
container-type: inline-size;
container-name: contrast-checker-outer;
}

.contrast-checker {
align-items: center;
border: 1px solid green;
border-radius: 0.5rem;
display: flex;
gap: 1rem;
justify-content: flex-start;
padding: 0.5rem;

transition: border-color 0.25s ease-in-out;
}

.contrast-checker:has(p.fail) {
border-color: blue;
}

.contrast-checker:has(p.fail + p.fail) {
border-color: red;
}

.contrast-checker p {
margin-block: 0;
}

.contrast-checker-title {
font-size: 0.75rem;
font-weight: bold;
text-transform: uppercase;
}

.contrast-checker-ratio {
font-size: 1.25rem;
font-weight: bold;
margin-inline-start: auto;
}

.contrast-checker-badges {
align-items: center;
display: flex;
gap: 0.5rem;
}

.contrast-checker-badges > p {
background-color: green;
border-radius: 0.25rem;
color: #fff;
display: block;
padding: 0.25rem 0.5rem;

transition: background-color 0.25s ease-in-out,
color 0.25s ease-in-out;
}

.contrast-checker-badges > p.fail {
background-color: red;
color: #000;
}


@container contrast-checker-outer (max-width: 350px) {

.contrast-checker {
align-items: center;
flex-wrap: wrap;
justify-content: space-between;
}

.contrast-checker-title {
text-align: center;
width: 100%;
}

.contrast-checker-ratio {
margin-inline-start: 0;
}

}
1 change: 1 addition & 0 deletions src/editor/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
@import "./components/breadcrumbs";
@import "./components/nav-list";
@import "./components/fontPicker.scss";
@import "./components/bb-contrast-checker";