diff --git a/packages/block-editor/src/components/colors/color-panel.js b/packages/block-editor/src/components/colors/color-panel.js index 95784826eb302d..75fe0e26238e30 100644 --- a/packages/block-editor/src/components/colors/color-panel.js +++ b/packages/block-editor/src/components/colors/color-panel.js @@ -53,7 +53,11 @@ export default function ColorPanel( { ); @@ -79,7 +83,11 @@ export default function ColorPanel( { { ...contrastCheckers } key={ `${ backgroundColor }-${ textColor }` } backgroundColor={ backgroundColor } - textColor={ textColor } + textColors={ [ + { + color: textColor, + }, + ] } /> ); } ) ) } diff --git a/packages/block-editor/src/components/contrast-checker/contrast-checker-message.js b/packages/block-editor/src/components/contrast-checker/contrast-checker-message.js index 96ea04c7bfb3e9..317f361e822c52 100644 --- a/packages/block-editor/src/components/contrast-checker/contrast-checker-message.js +++ b/packages/block-editor/src/components/contrast-checker/contrast-checker-message.js @@ -1,49 +1,9 @@ /** * WordPress dependencies */ -import { speak } from '@wordpress/a11y'; -import { __ } from '@wordpress/i18n'; import { Notice } from '@wordpress/components'; -import { useEffect } from '@wordpress/element'; - -export default function ContrastCheckerMessage( { - colordBackgroundColor, - colordTextColor, - colordLinkColor, - backgroundColor, - textColor, - linkColor, - shouldShowTransparencyWarning, -} ) { - let msg = ''; - if ( shouldShowTransparencyWarning ) { - msg = __( 'Transparent text may be hard for people to read.' ); - } else { - const backgroundColorBrightness = colordBackgroundColor.brightness(); - msg = - ( colordTextColor && - backgroundColorBrightness < colordTextColor.brightness() ) || - ( colordLinkColor && - backgroundColorBrightness < colordLinkColor.brightness() ) - ? __( - 'This color combination may be hard for people to read. Try using a darker background color and/or a brighter text color.' - ) - : __( - 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.' - ); - } - - // Note: The `Notice` component can speak messages via its `spokenMessage` - // prop, but the contrast checker requires granular control over when the - // announcements are made. Notably, the message will be re-announced if a - // new color combination is selected and the contrast is still insufficient. - useEffect( () => { - const speakMsg = shouldShowTransparencyWarning - ? __( 'Transparent text may be hard for people to read.' ) - : __( 'This color combination may be hard for people to read.' ); - speak( speakMsg ); - }, [ backgroundColor, textColor, linkColor ] ); +export default function ContrastCheckerMessage( { message } ) { return (
- { msg } + { message }
); diff --git a/packages/block-editor/src/components/contrast-checker/index.js b/packages/block-editor/src/components/contrast-checker/index.js index aba4b586f9f524..0925aade73a64e 100644 --- a/packages/block-editor/src/components/contrast-checker/index.js +++ b/packages/block-editor/src/components/contrast-checker/index.js @@ -1,3 +1,8 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { speak } from '@wordpress/a11y'; /** * External dependencies */ @@ -15,50 +20,21 @@ extend( [ namesPlugin, a11yPlugin ] ); function ContrastChecker( { backgroundColor, fallbackBackgroundColor, - fallbackTextColor, - fallbackLinkColor, fontSize, // font size value in pixels isLargeText, - textColor, - linkColor, + textColors, enableAlphaChecker = false, } ) { const currentBackgroundColor = backgroundColor || fallbackBackgroundColor; - // Must have a background color. - if ( ! currentBackgroundColor ) { - return null; - } - - const currentTextColor = textColor || fallbackTextColor; - const currentLinkColor = linkColor || fallbackLinkColor; - - // Must have at least one text color. - if ( ! currentTextColor && ! currentLinkColor ) { + // Must have a background color and some colours to iterate over. + if ( ! currentBackgroundColor || ! textColors || ! textColors.length ) { return null; } const colordBackgroundColor = colord( currentBackgroundColor ); const backgroundColorHasTransparency = colordBackgroundColor.alpha() < 1; - - // If there's only one color passed, store in `singleTextColor`. - const singleTextColor = - currentTextColor && currentLinkColor - ? null - : currentTextColor || currentLinkColor; - - const colordTextColor = singleTextColor - ? colord( singleTextColor ) - : colord( currentTextColor ); - const colordLinkColor = colord( currentLinkColor ); - - // Transparency. - const textColorHasTransparency = - currentTextColor && colordTextColor.alpha() < 1; - const linkColorHasTransparency = - currentLinkColor && colordLinkColor.alpha() < 1; - - // Text size. + const backgroundColorBrightness = colordBackgroundColor.brightness(); const isReadableOptions = { level: 'AA', size: @@ -67,63 +43,75 @@ function ContrastChecker( { : 'small', }; - // Readability. - const isTextColorReadable = - currentTextColor && - colordTextColor.isReadable( colordBackgroundColor, isReadableOptions ); - - const isLinkColorReadable = - currentLinkColor && - colordLinkColor.isReadable( colordBackgroundColor, isReadableOptions ); - - // Flag to warn about transparency only if the text is otherwise readable according to colord - // to ensure the readability warnings take precedence. - let shouldShowTransparencyWarning = false; - - // Don't show the message if the text is readable AND there's no transparency. - // This is the default. - if ( ! textColorHasTransparency && ! linkColorHasTransparency ) { - // If the background has transparency, don't show any contrast warnings. - if ( - backgroundColorHasTransparency || - ( isTextColorReadable && isLinkColorReadable ) || - ( singleTextColor && isTextColorReadable ) - ) { - return null; + let message = ''; + let speakMessage = ''; + for ( const item of textColors ) { + const currentTextColor = item.color || item.fallback; + // If there is no color, go no further. + if ( ! currentTextColor ) { + continue; } - } else { - // If there's text transparency, don't show the message if the alpha checker is disabled. - if ( ! enableAlphaChecker ) { - return null; + const colordTextColor = colord( currentTextColor ); + const isColordTextReadable = colordTextColor.isReadable( + colordBackgroundColor, + isReadableOptions + ); + const textHasTransparency = colordTextColor.alpha() < 1; + + // If the contrast is not readable. + if ( ! isColordTextReadable ) { + // Don't show the message if the background or text is transparent. + if ( backgroundColorHasTransparency || textHasTransparency ) { + continue; + } + const description = item.description || __( 'text color' ); + message = + backgroundColorBrightness < colordTextColor.brightness() + ? sprintf( + // translators: %s is a type of text color, e.g., "text color" or "link color" + __( + 'This color combination may be hard for people to read. Try using a darker background color and/or a brighter %s.' + ), + description + ) + : sprintf( + // translators: %s is a type of text color, e.g., "text color" or "link color" + __( + 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker %s.' + ), + description + ); + speakMessage = __( + 'This color combination may be hard for people to read.' + ); + // Break from the loop when we have a contrast warning. + // These messages take priority over the transparency warning. + break; } - // If the background has transparency, don't show any contrast warnings. - // If both text colors are readable, but transparent show the warning. - if ( - backgroundColorHasTransparency || - ( isTextColorReadable && isLinkColorReadable ) - ) { - shouldShowTransparencyWarning = true; + // If the text color is readable, but transparent, show the transparent warning. + if ( textHasTransparency && true === enableAlphaChecker ) { + message = __( 'Transparent text may be hard for people to read.' ); + speakMessage = __( + 'Transparent text may be hard for people to read.' + ); } + } - // If there is only one text color (text or link) and the color is readable with no transparency. - if ( singleTextColor && isTextColorReadable ) { - if ( ! textColorHasTransparency ) { - return null; - } - shouldShowTransparencyWarning = true; - } + if ( ! message ) { + return null; } + // Note: The `Notice` component can speak messages via its `spokenMessage` + // prop, but the contrast checker requires granular control over when the + // announcements are made. Notably, the message will be re-announced if a + // new color combination is selected and the contrast is still insufficient. + speak( speakMessage ); + return ( ); } diff --git a/packages/block-editor/src/components/contrast-checker/test/index.js b/packages/block-editor/src/components/contrast-checker/test/index.js index 6b5dc9b45eaad2..d4936944b0ee49 100644 --- a/packages/block-editor/src/components/contrast-checker/test/index.js +++ b/packages/block-editor/src/components/contrast-checker/test/index.js @@ -39,9 +39,18 @@ describe( 'ContrastChecker', () => { test( 'should render null when no background or fallback background color is provided', () => { const wrapper = mount( ); @@ -53,11 +62,20 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -69,10 +87,15 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -88,11 +111,20 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -100,7 +132,7 @@ describe( 'ContrastChecker', () => { 'This color combination may be hard for people to read.' ); expect( wrapper.find( Notice ).children().text() ).toBe( - 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.' + 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color, Jack.' ); } ); @@ -108,11 +140,19 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -128,11 +168,18 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -140,14 +187,18 @@ describe( 'ContrastChecker', () => { expect( wrapper.html() ).toBeNull(); } ); - test( 'should render render null if text color contains a transparency', () => { + test( 'should render null if text color contains a transparency', () => { const wrapper = mount( ); @@ -159,11 +210,18 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -177,10 +235,15 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -196,8 +259,13 @@ describe( 'ContrastChecker', () => { const wrapperSmallText = mount( ); @@ -211,8 +279,13 @@ describe( 'ContrastChecker', () => { const wrapperLargeText = mount( ); @@ -223,7 +296,12 @@ describe( 'ContrastChecker', () => { const wrapperSmallFontSize = mount( ); @@ -238,7 +316,12 @@ describe( 'ContrastChecker', () => { const wrapperLargeText = mount( ); @@ -250,7 +333,12 @@ describe( 'ContrastChecker', () => { const wrapper = mount( @@ -262,7 +350,12 @@ describe( 'ContrastChecker', () => { const wrapperNoLargeText = mount( @@ -281,7 +374,12 @@ describe( 'ContrastChecker', () => { ); @@ -292,8 +390,13 @@ describe( 'ContrastChecker', () => { test( 'should render messages when the textColor is valid, but the fallback backgroundColor conflicts.', () => { const wrapper = mount( ); @@ -308,8 +411,13 @@ describe( 'ContrastChecker', () => { test( 'should render messages when the linkColor is valid, but the fallback backgroundColor conflicts.', () => { const wrapper = mount( ); @@ -317,7 +425,7 @@ describe( 'ContrastChecker', () => { 'This color combination may be hard for people to read.' ); expect( wrapper.find( Notice ).children().text() ).toBe( - 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.' + 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color.' ); } ); @@ -327,8 +435,13 @@ describe( 'ContrastChecker', () => { act( () => { render( , appRoot ); @@ -337,7 +450,12 @@ describe( 'ContrastChecker', () => { act( () => { render( , appRoot @@ -352,9 +470,14 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -366,10 +489,18 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -385,10 +516,18 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -404,10 +543,18 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -419,12 +566,20 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -436,12 +591,20 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -457,12 +620,19 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); @@ -470,7 +640,7 @@ describe( 'ContrastChecker', () => { 'This color combination may be hard for people to read.' ); expect( wrapper.find( Notice ).children().text() ).toBe( - 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.' + 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker link color.' ); } ); @@ -478,10 +648,18 @@ describe( 'ContrastChecker', () => { const wrapper = mount( ); diff --git a/packages/block-editor/src/hooks/color-panel.js b/packages/block-editor/src/hooks/color-panel.js index c360ed4e87c6cb..2775eebe66c0de 100644 --- a/packages/block-editor/src/hooks/color-panel.js +++ b/packages/block-editor/src/hooks/color-panel.js @@ -74,9 +74,17 @@ export default function ColorPanel( { { enableContrastChecking && ( ) } diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index 705a46845c1057..0667377f71f947 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -633,13 +633,23 @@ function Navigation( { backgroundColor={ detectedBackgroundColor } - textColor={ detectedColor } + textColors={ [ + { + color: detectedColor, + description: __( 'text color' ), + }, + ] } /> ) } diff --git a/packages/block-library/src/social-links/edit.js b/packages/block-library/src/social-links/edit.js index ee5425174c67af..dd05a199e6844a 100644 --- a/packages/block-library/src/social-links/edit.js +++ b/packages/block-library/src/social-links/edit.js @@ -208,11 +208,14 @@ export function SocialLinksEdit( props ) { /> { ! logosOnly && ( ) }