[WIP] Block Supports: Relocate text and bg color controls to Typography and Background panels#77279
[WIP] Block Supports: Relocate text and bg color controls to Typography and Background panels#77279aaronrobertshaw wants to merge 34 commits into
Conversation
|
@jameskoster I took a quick run at what an interim step might look like in terms of relocating the text color control under the Typography panel, and the background color control under the Background panel. Note that the "element" colors like for Link, Heading, Buttons etc. are all still under optional controls on the Color panel at the moment until we work out a drill down approach or similar. An additional step in this exploration could be to add a new Elements panel that behaves as the Color panel does now for the Group block. That is, all the elements are optional controls that can be toggled on and each has a popover containing text, background etc tabs. Not ideal but a reasonable step until we have the ability to drill down and style more than just colors. We'd keep the current Colors panel so that if blocks add ad hoc color controls into that panel they still show as expected. If there's nothing in the Colors panel it wouldn't render at all. Let me know if you think there might be something here we can pursue in the short term. |
|
Size Change: +1.26 kB (+0.01%) Total Size: 8.46 MB 📦 View Changed
|
This would effectively mean the existing Color panel is hidden most of the time, right? If so I think that might be good to include in this PR. Relatedly I'd consider giving the new Elements panel less prominence in the UI, maybe by positioning it at the bottom of the inspector. |
|
Thanks for the direction @jameskoster 👍 I was already working towards both including the elements panel as well as pushing it further down the sidebar. As soon as I have something more functional. I'll demo again. |
|
This is early days and still buggy but here's a quick glimpse of the elements panel approach discussed. The first pass at this was pushed in 9853127 Screen.Recording.2026-04-16.at.5.54.32.pm.mp4 |
|
Neat, that looks pretty good to me. Time to rope in @WordPress/gutenberg-design for some wider feedback. The only quirk I noticed is the placement of the contrast warning. If that appears due to changing the Text color the placement is a bit unexpected. I wonder if we should move that warning to the color popover. That way it'll appear contextually whether you're currently modifying the background or the text color. Additionally it won't clutter the Inspector in situations where users legitimately want low contrast styling. |
|
Thanks for taking another look at this one 🙇
Good idea. I like it 👍 I'll find some time and make it happen on this PR. |
|
Here's a demo of a quick hack at getting the contrast checker warning within the color popovers. I think we might need to put a little thought into how we could present this better to avoid scrollbar within the popover, the popover extending beyond the bottom of the page, or even just the significant layout jump that it causes. Happy to try out any ideas you might have: Screen.Recording.2026-04-18.at.8.00.06.pm.mp4 |
|
Here's a quick mockup of potentially adding a warning icon and tooltip/popover to the color popover when the contrast is too low. We could also display it in color control in the panel as well if we can find a way that it doesn't look too bad beside existing color swatches and the reset button within the control. For now though maybe this will spark further ideas: Screen.Recording.2026-04-19.at.11.46.28.am.mp4 |
|
Nice! Taking this one for a spin, I think it works well. On the badges for low contrast, it's not a poor idea, but given we have color Items that have very long titles, like "Submenu & overlay background" (which already gets elided), I don't think there's room for a badge there. Could we do an unread dot instead? And regardless, because that may be a rabbit hole and a new feature, might be worth keeping separate from this one? That is, can we just keep the yellow notice that exists in trunk already? I see this is already present, I'm thinking the same but with a blue dot instead of a yellow icon: Also, now that we've changed the Color panel, should we move Typography above it?
|
|
Appreciate all the great feedback here 🙇 I'll try and resolve all the new conflicts over the next few days then explore the options in front of us.
I'm not sure how I feel on this one given the two related colors being compared are split across panels. I'll see how it plays if the contrast warning is displayed in both panels, if that's even possible etc. then update again. |
2c7bfcc to
8be2003
Compare
|
I've finished resolving all the conflicts with the recent state based and responsive styling changes. I believe this is back to a functional state. The suggested tweaks have been made and are present in current state of this PR. These include:
It turns out if a block contains a link element and the link element has styles for the block that also has its contrast checked. Having multiple contrast warnings really creates some dramatic layout shift and/or scroll in the block inspector. It'll be even worse if we add another warning to the elements panel when it is considered low contrast and applicable. (Side note, the element color popovers don't have the low contrast dot and tooltip rendered yet but that can be a follow-up if we keep with that approach) Another issue with the current contrast warning being rendered multiple times is that the warning by default is announced by If we were to display a single warning only, at the block level, within the inspector, would that detach the warning too much from the relevant controls? Could the combination of styles and the resulting lack of contrast be a block level concept? The last issue I noticed, which is also present on trunk is that with state-based styles e.g. hover state, the link element there still has a Here's a quick demo of the latest exploring low contrast selections and warning display: Screen.Recording.2026-06-08.at.1.52.24.pm.mp4/cc @jasmussen and @jameskoster any thoughts on the latest changes? It still feels like we need to polish the contrast warning behaviour and UX. |
| ); | ||
|
|
||
| // Announce to screen readers whenever the warning becomes active. | ||
| speak( message ); |
There was a problem hiding this comment.
This would fire on every render that happens while isPoor is true, right? Is there anything else that could trigger a render besides the contrast check?
There was a problem hiding this comment.
Honestly, not sure off the top of my head but the fact that we're rendering 2 contrast warnings (3 if we add one for elements), means we're going to be announcing redundant warnings. Navigating our editor is hard enough I'd say for those relying on screenreaders etc. without introducing this.
There was a problem hiding this comment.
Actually, I was forgetting that I added the speak call here to meet other accessibility warnings I was getting. There might be a different balance or solution here.
There was a problem hiding this comment.
After adding the warning back to the panels. This speak call could be omitted entirely. If it turns out we go back to just the warning in the color popover, I'll gate this in an effect so it only fires on the change from false to true or when color values change.
| const setTextColor = ( newColor ) => { | ||
| onChange( | ||
| setImmutably( | ||
| value, | ||
| [ 'color', 'text' ], | ||
| encodeColorValue( newColor ) | ||
| ) | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Not sure if this is right but I see a regression and I think it might be related to the way trunk passed newSlug.
If I have two custom colors with the same hex value, e.g.,
"color": {
"palette": [
{ "slug": "brand-a", "name": "Brand A", "color": "#e10000" },
{ "slug": "brand-b", "name": "Brand B", "color": "#e10000" }
]
}They're selected, and only brand-a is assignable.
here's how trunk is working
Kapture.2026-06-09.at.14.24.31.mp4
I think it's important coz if the custom color brand-b is ever updated in the theme, it wouldn't have any effect on stored posts with that var.
There was a problem hiding this comment.
Thanks for the quick testing and flagging this.
I'm sure I've messed up the rebase. I'll get a test in place to cover the regression and implement a fix.
There was a problem hiding this comment.
FWIW I've seen two colors showing as "active" before, when the values are the same. Not ideal, but probably also not the fault of this PR.
There was a problem hiding this comment.
I've seen two colors showing as "active" before, when the values are the same. Not ideal, but probably also not the fault of this PR.
I just retested and still see the two colors selected, but the slug does persist after 71ab383 I think. Nice work!
So here I tested by selected the second B and saved, then updated the value in theme.json to check that the change came through.
| observer.observe( blockEl, { | ||
| attributes: true, | ||
| attributeFilter: [ 'class', 'style' ], | ||
| subtree: true, |
There was a problem hiding this comment.
I guess this is unavoidable. In big containers with lots of deeply-nested blocks I'm wondering what the performance trade off would be of calling a lot of getComputedStyle().
There was a problem hiding this comment.
This is mirroring the existing contrast checker that displays the warning in the different panels. So if it has been accepted there I think that is the baseline.
Now, given this means it might be done multiple times with the warning to be displayed in the color popover, it might need optimising if possible or a different approach to ensure performance is maintained or improved.
I'll look into it.
There was a problem hiding this comment.
No biggie if it was there before. Thanks!
|
Nice work, this is trucking along well. Thanks for changing the dot to blue. I will say it's not entirely clear that it's useful after all. It's fine to keep, but it seems like that dot is serving the same purpose that is covered by the contrast notice itself. It may be best to simply remove it after all. Speaking of, the double warnings is the problem to solve to ship this:
Not a solution near term, but I want to connect this to #76996 and #78880 as having potential to be more generic homes for lint, which I consider contrast issues to be. But near term, can we show the contrast warning only inside the panel that has colors set? E.g. the above image is confusing because it is flagging incompatible colors in a panel that does not have colors set. However the following is probably fine, true, and honest:
Not necessarily for this PR, but we could also potentially improve those contrast warnings with clarity, brevity, and specificity. E.g. instead of:
For the typography panel we could do:
For the background panel we could do:
Relatedly, and possibly not the fault of this PR, but I managed to get myself into this state by clicking first one color, then the other:
The duplicate controls called "Color" will take a little getting used to, insofar as one is typography color and the other is background color, with the panel context revealing which is which. I think this is worth shipping as is, feeling out. If that fails, we can always call change the BG color term to "Fill", or "Solid":
|
|
Didn't we add the dot so that we could remove the notice in the Inspector? I think the yellow dot/icon worked a bit better as it conveys meaning. From that perspective I still think it could be displayed as an Info tip in the
|
|
I would agree that it's either the notice, or the dot. Do we have to decide between them in this PR? I'm softly leaning towards the notice, in part out of inertia/cheese-moving, but also because until we have something like #76996, flagging very visibly contrast issues is something that is unique to the block editor compared to other players in the space. |
|
Yes I think it would be fine to tackle separately, especially without a design we're 100% happy with. Ideally it's something we can prioritise though because obviously the double-notice is taking up a lot of valuable space in the Inspector, arguably a regression. Not essential, but an interim solution could be to make the notice(s) dismissible. |
|
The double notice can hopefully be substantially mitigated by worth-doing-anyway changes to when they fire as suggested here, i.e. only show the notice if a color from the panel has actually been set, and rephrase the notice dependant on which panel it shows up on. WDYT? |
|
It can work, but will still produce double-notices in some cases, no? |
|
If there's a color set in both typography and background panels, both of which are poor, there could be two notices, yes. But each would have a bespoke contextual message, and each would still need separate fixing, yes? I.e. if you fix it in one place and that improves the contrast enough for both checks to pass, the notice would disappear in both places, but you'd get to choose where to fix it. I agree, not necessarily perfect, but it's honest, it's truthful, right? |
|
Appreciate all the continued discussion here 🙇 The commits I pushed yesterday implemented all the feedback from #77279 (comment) but it all needed a little further testing before I was going to record a demo.
I flagged earlier the fact that contrast warnings can be triggered by elements too. That means there might be three locations where a color selection could cause low contrast:
Screen.Recording.2026-06-09.at.5.15.57.pm.mp4This PR now has some conflicts with trunk, so I'll sort those out again, then iterate further. I'll link to two separate commits that will demo both the latest notice in panel behaviour and the alternative dot only approach. Hopefully, that will help guide what feels best before we have the linting error solution mentioned earlier. |
Pull the ColorPanelDropdown component and its supporting LabeledColorIndicators/ColorPanelTab/popoverProps helpers out of color-panel.js into a standalone module so Background, Typography, and Color panels can all render color/gradient ToolsPanelItems consistently without duplicating the dropdown/tabs/reset boilerplate. Pure refactor. No UI change.
Addresses design feedback on the relocated text and background color controls: - Remove the small contrast-warning dot from the color picker popover, along with its portal wiring in the shared dropdown item and its CSS. The panel notice remains the single contrast warning surface. - Only show a panel's contrast warning when that panel actually has a color set: the Typography panel requires a text color, and the Background panel requires a background color. This avoids warning in a panel that has no color selection of its own. - Add a messageOverride prop to the shared ContrastChecker so each panel can provide clearer, panel-specific copy (text-vs-background guidance in Typography, combination guidance in Background).
39661d4 to
75ed850
Compare
|
With the current approach, displaying contrast warnings within the typography or background panels only if those panels contain a color selection, if an element color selection is low contrast against theme inherited values, no warning shows. This probably means that we still need the contrast warning in the third panel, Elements.
I believe there is another issue possibly stemming from the fact the contrast checker previously worked based on the whole set of color selections. If there was any low contrast by the whole set a single contrast warning was shown.
Note that the text vs background colors here are fine, it is the link triggering a contrast warning but each panel with a color selection shows the warning Now if we are showing different warning messages based on the panel, I'll need to tweak the contrast checker in each panel to only check against that color e.g text vs background in the typography panel and not allow elements to impact that check and vice versa |
- Elements panel now surfaces a contrast warning for link color selections, fixing the gap where a low contrast link color went unreported when Typography/Background had no selections. - Typography panel now only warns when the text color pair fails; link-only failures are reported by the Elements panel instead. - Background panel behavior is unchanged: it warns on any failure since both text and link contrast depend on the background.
The Elements panel notice previously had no styles, so it spanned a single grid column with no spacing above it. Consolidate the per-panel rules from background, color, and typography styles into a single shared stylesheet that covers all four panels.
Replaces the in-panel contrast warning notices with an indicator on
the color control toggle row, per design feedback. When a color
selection results in insufficient contrast, the reset button's slot
hosts a small actions dropdown modeled on the Global Styles
inheritance dropdown:
- Trigger: caution icon (exclamation in circle), always visible
(no hover-reveal), labeled '{control label} options', with proper
aria-haspopup/aria-expanded menu semantics and the popover
anchored to the trigger button itself.
- Menu: offers 'Adjust color' (opens the color picker popover, with
the full warning message as its tooltip) and 'Reset'.
When there is no warning, the plain reset button renders exactly as
before, keeping the surface that the upcoming reset-to-dropdown PR
rewrites untouched. That PR's menu can later absorb the warning
state (icon swap on the trigger, extra items in the menu) alongside
inherited-value indicators and push-to-global-styles actions.
Supporting changes:
- Extract getContrastWarning() from the ContrastChecker component so
the warning can be consumed as data; the public component behavior
is unchanged.
- Convert the block-level checker into a useBlockColorContrastWarning
hook that reads computed colors from the block element and returns
the warning message, still announcing it via a11y speak().
- Per-panel attribution is preserved: Background checks both pairs,
Typography checks text only, Elements checks link only.
- Remove the now-unused shared panel notice styles.
|
I have explored the approach to low contrast warnings proposed in #77279 (comment). The initial issue with that is what we do with the "reset" button that is revealed on hover when there is a color selection. For accessibility, we can't nest the button within the color control's button to toggle the color popover. So the current approach is to absolutely position the reset button. This makes it less straightforward to place another warning/icon or interactive element beside that reset button. Below is what it might look like to have the reset button replaced with a dropdown using the warning icon as its toggle button. The dropdown contains an "adjust" item that has the warning icon and tooltip to flag the low contrast issue as well as the "reset" option. This dropdown is inline with the approach being explored for displaying inheritance of global styles values in #77894. In #77894, the blue dot indicating inheritance is a toggle for a dropdown that allows the user to reset to inherited value or push to global styles. This is just an exploration and to provoke further ideas and discussion. Happy for any feedback. Screen.Recording.2026-06-10.at.11.43.43.pm.movTo compare the notice-in-panel approach vs the current approach shown above, checkout bf042da |
|
Thanks for all the explorations. This is trucking along. The shifting notices in your demo video do look jumpy, so not ideal. But to be honest, if I'm comparing to trunk, they feel better and more contextual and actionable. I think we've worked up almost a banner-blindness to what exists in trunk because they are just not quite as useful. Specifically what works well:
These principles are worth maintaining regardless of whether we go with notices or warning icons. Your latest video from just a moment ago is actually quite compelling. What do you prefer yourself at this point: notices or warning icons? If we do go with warning icons, we should consider using the error icon. The only concern I have with the warning icon is that it feels like a one-off pattern as just an icon inside the ItemGroup. I wish we could use an icon-only badge, then at least it'd be a recognisable element, similar to how we use badges for state-bsased indicators:
But I don't think we can. @jameskoster what are your system instincts here? |
In that example, can we rephrase the contrast text to clarify this? Or is that adding needless complexity? |
|
Thanks for the quick feedback here @jasmussen 🙇
Personally, I think I lean toward the icons but feel like the UI and dropdown needs some furtherpolishing. That approach seems forward compatible with the dropdown that is proposed in #77894. With the notices approach, there are cases where you could have one notice get rendered but be outside of the viewport and go unseen unless the user scrolls. The icons approach isn't perfect either but does limit some of that UI jumping.
I'm sure we can refine the text as needed. The real estate available and the required brevity will be determined by the approach we settle on I think. |
|
If we can't refine the text as needed, it makes me also softly lean towards the icons. Ultimately I'll defer to Jay if he has opinions on how we might systematise the icon. That's the main open question for me still. |
|
Coming to say this is great work and the change feels definitely a better experience. Just sharing a thought regarding the latest topic. Regarding informing about the color contrast, I'm drawn to use icon instead of notice component. An icon badge would be ideal, and that sounds like a follow up improvement of the component. For the icon type, I would not use But take the above lightly as Jay and Joen have been actively working here. |
Replaces the contrast warning actions dropdown with a 'Low contrast' badge on the color control toggle row, per design feedback: - A non-interactive Badge (warning intent, caution icon) renders beside the reset button when a color selection has insufficient contrast; clicks pass through to the control toggle. The badge matches the block card's state badge styling. - The reset button stays visible without hover while the badge is shown, so its action remains discoverable. - The full warning message renders inside the color picker popover, between the custom color picker and the palette swatches, so the explanation is visible at the moment the user acts on the warning and disappears live once a passing color is chosen. Supporting changes: - ColorPalette (components) accepts an optional children slot rendered between the custom color picker and the palette. - ColorGradientControl forwards children into the color tab. - Remove the ContrastWarningDropdown and its styles; the warning UI is now purely status (badge) plus contextual advice (popover).
The badge inside the color control toggle inherited the toggle button's 13px font size on its outer span, rendering larger than the same badge in the block card (12px). Pin the badge subtree to $font-size-small so it matches the block card's state badge.
…pover - Blocks with non-standard color attributes (e.g. Social Icons, Navigation) render the public ContrastChecker component directly within the color panel. The rules giving that notice full panel width and top spacing were lost during the contrast warning refactoring; restore them in hooks/color.scss. - Replace the ad hoc icon-and-text contrast warning inside the color picker popover with the Notice component, matching the warning notices shown in the inspector panels (same background, border, and typography) and dropping the icon which wasted horizontal space in the narrow popover.
ramonjd
left a comment
There was a problem hiding this comment.
Great work @aaronrobertshaw and kudos for sticking with this.
From a code perspective this is working well. I tested the following:
- block instance styles (image, gradient, color)
- global styles
- block variations
All are working as expected.
Question - now that root background color moved out of ColorPanel do we need to update useBlockHasGlobalStyles with useHasBackgroundPanel or something?
This LGTM in my testing. If the design direction has the 👍🏻 I think we're in a good place.
Non-blocking observations:
The only things I noticed are related to block instance responsive overrides, and I think this is already a known quantity.
They seem to work fine in the editor, but don't output as expected.
| Editor | Frontend |
|---|---|
![]() |
![]() |
As far as I can tell, global responsive overrides are working okay.
Also the background image control is set up to display the global styles fallback if a block instance isn't set. That's fine until we set a block instance background image to A, then switch to editing "Mobile/Tablet". The control will still show the global styles value instead of the block instance A.
| // control in the Typography panel and under all controls in the | ||
| // Background panel, visible without opening the popovers. | ||
| const typographyWarning = typographyPanel.locator( | ||
| '.block-editor-contrast-checker' |
There was a problem hiding this comment.
Is this still right? I noticed .block-editor-contrast-checker is deleted above in packages/block-editor/src/hooks/color.scss
There was a problem hiding this comment.
This branch is in a bit of a transitional state at the moment. I'm trying several different approaches in the UI. So there's a lot of clean up, polishing, and even sanity checking to be done once we're more settled on a final direction.
Thanks for flagging this, I've added it to a list to circle back to and clean up.
There was a problem hiding this comment.
Ah no worries, I was a bit trigger happy. I'll wait until you're through with the changes.
| const setTextColor = ( newColor ) => { | ||
| onChange( | ||
| setImmutably( | ||
| value, | ||
| [ 'color', 'text' ], | ||
| encodeColorValue( newColor ) | ||
| ) | ||
| ); | ||
| }; |
There was a problem hiding this comment.
I've seen two colors showing as "active" before, when the values are the same. Not ideal, but probably also not the fault of this PR.
I just retested and still see the two colors selected, but the slug does persist after 71ab383 I think. Nice work!
So here I tested by selected the second B and saved, then updated the value in theme.json to check that the change came through.
|
Thanks for circling back to this PR @ramonjd 🙇 I'm working on another alternate approach in the UI that might blend a few of the options we've been considering. So there's a good chance the code might shift again before I can consolidate, polish, and look toward final reviews here. This isn't the UI I'm working towards with the contrast checking, just the current state, so take it with a grain of salt as it is changing rapidly. It might provide food for thought for people though, so I'll share early.
The above:
I'll push the latest as soon as it is demoable or can be played with. |
The Background panel resolved the selected preset slug only from `inheritedValue`, while the displayed color used `value` first (`userBackgroundColor ?? backgroundColor`). For a block instance the selection lives in `value` and `inheritedValue` only holds the global styles fallback, so the slug was missing and ColorPalette fell back to hex matching, marking two same-hex presets as selected at once. Resolve the slug with the same value-first precedence as the indicator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Instead of both the badge and the reset button, here's what omitting the reset button looks like when there is a contrast warning in effect. Even without this reset button, there's two other places to reset the button; ToolsPanel menu + clear button in popover.
Let me know what people think is our best bet from the options floated to date 🙏 |
|
I like that badge, and the notice inside the flyout seems like a good solution. The only problem is that it's an extremely tight space. As mentioned previously, some ItemGroup situations have very long labels. Options: does the badge support tooltip label only, and being icon only? If not, can it just say "Warning"? I'd love it even shorter, but that's hard to do. |
|
Showing the warning title might not scaling well when this is longer and the available space is even smaller. I would keep the indicator just to only show the notice type (warning), and rely purely on the icon. Ideally, using badge in medium |
|
I was thinking the same thing, the only thing is I don't think the badge supports icon-only. |
|
I'm not sure we can do icon only because there's no easy way (that I can see) to make the message accessible to screen readers. To reveal a tooltip the element needs to be focusable, which leads us back to the nested-interactive-elements violation. Maybe we could do something with
When does this occur? I couldn't find an example. Or is it an i18n thing? |
|
Thanks for the continued discussion, I appreciate having so many eyes on this one. My understanding of the feedback so far is that we essentially want an icon-only option, with tooltip, and the notice displayed in the popover. Is that correct? If so, this almost takes us back to an earlier iteration where there was a dropdown menu attached to the icon-only warning. I'll tweak the current version to go back to the icon-only interactive element that is absolutely positioned, similar to the reset button that appears on hover on trunk. That way our icon-only warning can have a tooltip. I'll also play around and see if we can have the reset button beside this icon-only warning too. |
|
Here's one more permutation that we might consider:
|





















What?
See:
This PR is a proof of concept that explores improving the information architecture of the block inspector and global styles sidebars by relocating the root-level text and background color controls to the panels they most naturally belong with:
Why?
Today, every color-related control lives under a single Color panel, regardless of whether the user is thinking about typography, background styling, or element-specific treatments. Grouping controls by the style aspect they affect (typography, background, etc.) should make the sidebars easier to reason about and bring related controls closer to where users are already working.
This PR is deliberately an interim step. The element-level color controls (Links, Headings, Buttons, Captions, etc.) remain under the Color panel because moving those properly is a larger IA question: ideally each element would be presented as a drill-down into its full set of styles (typography, color, spacing, etc.) as a cohesive unit, rather than splitting a single element's controls across multiple top-level panels. That exploration is out of scope here and is best tackled as a follow-up once we have feedback on this simpler change.
The goal of landing this smaller step first is to validate the directional change on the two simplest cases (root text and root background) before committing to a larger restructuring.
How?
Color, Background, and Typography panels. The existing panel-specific classes (block-editor-tools-panel-color-gradient-settings__item,
block-editor-background-panel__item, block-editor-typography-panel__color-item) remain on the DOM for backward compatibility.
visible item in the Color panel when element-level controls were toggled on.
"Color" buttons now exist in the sidebar.
Testing Instructions
Screenshots or screencast
Screen.Recording.2026-04-14.at.11.46.01.am.mp4