Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions packages/block-editor/src/components/block-inspector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,31 @@ function StyleInspectorSlots( {
return (
<>
<InspectorControls.Slot />
<InspectorControls.Slot
group="typography"
label={ __( 'Typography' ) }
/>
<InspectorControls.Slot
group="color"
label={ __( 'Color' ) }
className="color-block-support-panel__inner-wrapper"
/>
<InspectorControls.Slot
group="background"
label={ __( 'Background image' ) }
label={ __( 'Background' ) }
className="background-block-support-panel__inner-wrapper"
/>
<InspectorControls.Slot
group="typography"
label={ __( 'Typography' ) }
/>
<InspectorControls.Slot group="layout" label={ __( 'Layout' ) } />
<InspectorControls.Slot
group="dimensions"
label={ __( 'Dimensions' ) }
/>
<InspectorControls.Slot group="border" label={ borderPanelLabel } />
<InspectorControls.Slot
group="elements"
label={ __( 'Elements' ) }
className="elements-block-support-panel__inner-wrapper"
/>
{ showPositionControls && <PositionControls /> }
<InspectorControls.Slot group="styles" />
{ showBindingsControls && (
Expand All @@ -98,20 +103,20 @@ function StyleStateInspectorSlots( { blockName, selectedBlockStyleState } ) {
! hasPseudoBlockStyleState( selectedBlockStyleState );
return (
<>
<InspectorControls.Slot
group="typography"
label={ __( 'Typography' ) }
/>
<InspectorControls.Slot
group="color"
label={ __( 'Color' ) }
className="color-block-support-panel__inner-wrapper"
/>
<InspectorControls.Slot
group="background"
label={ __( 'Background image' ) }
label={ __( 'Background' ) }
className="background-block-support-panel__inner-wrapper"
/>
<InspectorControls.Slot
group="typography"
label={ __( 'Typography' ) }
/>
{ showLayoutControls && (
<InspectorControls.Slot
group="layout"
Expand All @@ -123,6 +128,11 @@ function StyleStateInspectorSlots( { blockName, selectedBlockStyleState } ) {
label={ __( 'Dimensions' ) }
/>
<InspectorControls.Slot group="border" label={ borderPanelLabel } />
<InspectorControls.Slot
group="elements"
label={ __( 'Elements' ) }
className="elements-block-support-panel__inner-wrapper"
/>
</>
);
}
Expand Down
41 changes: 29 additions & 12 deletions packages/block-editor/src/components/colors-gradients/control.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
__experimentalVStack as VStack,
ColorPalette,
GradientPicker,
Notice,
privateApis as componentsPrivateApis,
} from '@wordpress/components';

Expand Down Expand Up @@ -48,6 +49,7 @@ function ColorGradientControlInner( {
showTitle = true,
enableAlpha,
headingLevel,
noticeProps,
} ) {
const canChooseAColor =
onColorChange &&
Expand All @@ -67,20 +69,35 @@ function ColorGradientControlInner( {
}
: ( newColor, _index, newSlug ) => onColorChange( newColor, newSlug );

const colorPalette = (
<ColorPalette
value={ colorValue }
selectedSlug={ colorSlug }
onChange={ colorPaletteOnChange }
{ ...{ colors, disableCustomColors } }
__experimentalIsRenderedInSidebar={
__experimentalIsRenderedInSidebar
}
clearable={ clearable }
enableAlpha={ enableAlpha }
headingLevel={ headingLevel }
/>
);

const tabPanels = {
// The `ColorPalette` must stay at a stable position in the tree whether
// or not a notice is present. Wrapping it in a `VStack` only when a
// notice appears remounts it, which resets the custom color picker back
// to the swatch view mid-edit. Keep `ColorPalette` last and toggle only
// the notice ahead of it; the notice's own bottom margin provides the
// spacing the wrapper used to.
[ TAB_IDS.color ]: (
<ColorPalette
value={ colorValue }
selectedSlug={ colorSlug }
onChange={ colorPaletteOnChange }
{ ...{ colors, disableCustomColors } }
__experimentalIsRenderedInSidebar={
__experimentalIsRenderedInSidebar
}
clearable={ clearable }
enableAlpha={ enableAlpha }
headingLevel={ headingLevel }
/>
<>
{ noticeProps && (
<Notice isDismissible={ false } { ...noticeProps } />
) }
{ colorPalette }
</>
),
[ TAB_IDS.gradient ]: (
<GradientPicker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ const WithToolsPanelItem = ( { setting, children, panelId, ...props } ) => {
: true
}
{ ...props }
className="block-editor-tools-panel-color-gradient-settings__item"
className={ clsx(
'block-editor-color-gradient-item',
'block-editor-tools-panel-color-gradient-settings__item'
) }
panelId={ panelId }
// Pass resetAllFilter if supplied due to rendering via SlotFill
// into parent ToolsPanel.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,23 @@ $swatch-gap: 12px;
background: linear-gradient(-45deg, transparent 48%, $gray-300 48%, $gray-300 52%, transparent 52%);
}

// Space the in-popover contrast notice away from the color palette below it.
.block-editor-panel-color-gradient-settings__contrast-notice {
margin-bottom: $grid-unit-20;
}

/**
* The following styles replicate the separated border of the
* `ItemGroup` component but allows for hidden items. This is because
* to maintain the order of `ToolsPanel` controls, each `ToolsPanelItem`
* must at least render a placeholder which would otherwise interfere
* with the `:last-child` styles.
*
* Applied via the shared `block-editor-color-gradient-item` class on
* color/gradient dropdown items across the Color, Background, and
* Typography panels.
*/
.block-editor-tools-panel-color-gradient-settings__item {
.block-editor-color-gradient-item {
padding: 0;
max-width: 100%;
position: relative;
Expand All @@ -87,7 +96,6 @@ $swatch-gap: 12px;

// Identify the first visible instance as placeholder items will not have this class.
&:nth-child(1 of &) {
margin-top: $grid-unit-30;
border-top-left-radius: $radius-small;
border-top-right-radius: $radius-small;
border-top: 1px solid $gray-300;
Expand Down Expand Up @@ -127,6 +135,12 @@ $swatch-gap: 12px;
text-overflow: ellipsis;
max-width: calc(100% - ($button-size-next-default-40px + $grid-unit-05));
}

// Reserve extra space for the always-visible contrast warning icon,
// which sits alongside the hover-revealed reset button.
> button.has-contrast-warning .block-editor-panel-color-gradient-settings__color-name {
max-width: calc(100% - ($button-size-next-default-40px + $button-size-small + $grid-unit));
}
}

.block-editor-panel-color-gradient-settings__dropdown {
Expand Down Expand Up @@ -161,4 +175,27 @@ $swatch-gap: 12px;
// Show reset button on devices that do not support hover.
opacity: 1;
}

// While a contrast warning is in effect the warning icon occupies the
// far-right slot, so the reset button shifts left to sit beside it.
.block-editor-panel-color-gradient-settings__dropdown.has-contrast-warning + & {
right: $button-size-small + $grid-unit-05;
}
}

// Icon-only "Low contrast" warning shown to the right of the color
// control toggle whenever the selection has insufficient contrast.
// Unlike the reset button it stays visible without hover. It is not a
// menu; activating it opens the color picker popover (where the full
// warning notice lives), so it doubles as a shortcut to the fix. The
// accessible name and hover/focus tooltip come from the button label.
.block-editor-panel-color-gradient-settings__contrast-warning {
position: absolute;
right: 0;
top: $grid-unit;
margin: auto $grid-unit auto;

&.block-editor-panel-color-gradient-settings__contrast-warning {
border-radius: $radius-small;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,12 @@ The text color to check the contrast of the background against.

- Type: `String`
- Required: No

#### messageOverride

Custom warning message to display (and announce) instead of the default
contrast guidance when the color combination has insufficient contrast. Useful
for providing panel-specific, more concise copy.

- Type: `String`
- Required: No
45 changes: 42 additions & 3 deletions packages/block-editor/src/components/contrast-checker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,28 @@ import { speak } from '@wordpress/a11y';

extend( [ namesPlugin, a11yPlugin ] );

function ContrastChecker( {
/**
* Computes a contrast warning for the given color combination, if any.
*
* Shared between the `ContrastChecker` component and the block inspector
* contrast warning indicators, which surface the result without rendering
* a notice.
*
* @param {Object} props
* @param {string} [props.backgroundColor] Background color.
* @param {string} [props.fallbackBackgroundColor] Fallback background color.
* @param {string} [props.fallbackTextColor] Fallback text color.
* @param {string} [props.fallbackLinkColor] Fallback link color.
* @param {number} [props.fontSize] Font size value in pixels.
* @param {boolean} [props.isLargeText] Whether the text is large.
* @param {string} [props.textColor] Text color.
* @param {string} [props.linkColor] Link color.
* @param {string} [props.messageOverride] Caller-provided copy used in place of the generic guidance.
* @param {boolean} [props.enableAlphaChecker] Whether to warn about transparent text.
*
* @return {?Object} `{ message, speakMessage }` when contrast is insufficient, otherwise `null`.
*/
export function getContrastWarning( {
backgroundColor,
fallbackBackgroundColor,
fallbackTextColor,
Expand All @@ -23,6 +44,7 @@ function ContrastChecker( {
isLargeText,
textColor,
linkColor,
messageOverride,
enableAlphaChecker = false,
} ) {
const currentBackgroundColor = backgroundColor || fallbackBackgroundColor;
Expand Down Expand Up @@ -81,6 +103,13 @@ function ContrastChecker( {
if ( backgroundColorHasTransparency || textHasTransparency ) {
continue;
}
// A caller can provide panel-specific copy that is clearer and
// more concise than the generic brighter/darker guidance.
if ( messageOverride ) {
message = messageOverride;
speakMessage = messageOverride;
break;
}
message =
backgroundColorBrightness < colordTextColor.brightness()
? sprintf(
Expand Down Expand Up @@ -119,11 +148,21 @@ function ContrastChecker( {
return null;
}

return { message, speakMessage };
}

function ContrastChecker( props ) {
const warning = getContrastWarning( props );

if ( ! warning ) {
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 );
speak( warning.speakMessage );

return (
<div className="block-editor-contrast-checker">
Expand All @@ -132,7 +171,7 @@ function ContrastChecker( {
status="warning"
isDismissible={ false }
>
{ message }
{ warning.message }
</Notice>
</div>
);
Expand Down
Loading
Loading