Widget Types: declarative presentation hint (full-bleed support)#78209
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @Copilot. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: 0 B Total Size: 7.97 MB ℹ️ View Unchanged
|
|
Flaky tests detected in b610273. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/26023670825
|
|
I've been thinking about this; an alternative could be declaring if the widget wants the header rendered or not, and then leaving the consumer to deal with adding I haven't found a use-case where I'd not want the header and first element to be full bleed, but I have use-cases where I don't want the header, the first element is full bleed, but the following elements aren't full-bleed (e.g. core welcome banner). So my conclusion so far has been to:
But curious for your thoughts! "Full screen widget" and "no header" could also be two separate declarative APIs. I.e. this should be possible: <Card.Content>
<Card.FullBleed>
<div
style={ {
height: 180,
background:
'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
} }
/>
</Card.FullBleed>
<Text>Content outside full bleed.</Text>
</Card.Content> |
|
A couple of related thoughts:
|
Probably a question for @ciampo ! Allowing widgets to hide headers sounds interesting, but don't we intend to place actions in the header (kebab menu in top-right corner), how would they be accessible if the header is hidden? This is one of the reasons I'm a little anxious about allowing too much flexibility in the widget anatomy. I think certain aspects should be locked down otherwise the door is left open to so much randomness. This is just my 2c and potentially a controversial take but I don't think the random design of existing widgets should inform the direction too much. It should be the other way around; the widget system should inform the designs—this is how we produce a consistent and predictable interface. I also suspect that all widgets should be required to include a title for assistive tech, even if it's visually hidden. |
Could be a good idea, I agree on the DevX pros! It would also abstract how internally things are done — ie. if we ever change to a different card or any other UI implementation, it could be simpler to do with a abstraction layer in the middle |
Same boundary as the reply above, applied specifically to the Card API: Card is widget chrome, not content. Proxy: the Card subtree ( The widget's render function produces the content that lives inside No widget imports The DevX argument @ciampo raised is real, but it pays off when there are non-widget callers (extensions composing dashboard chrome directly). Different feature, different call site, worth revisiting then. Multiple |
The actions question pushes on the right spot. Two viable layouts, either of which keeps the action surface independent of header visibility:
In either layout, On a11y: agreed every widget should be announced. The PR already keeps the title in the a11y tree via The stronger property you're pointing at, "every widget must declare a title", is metadata validation at the widget-type level and is orthogonal to this PR. Worth landing as its own pass on the registry, since |
|
I'm honestly a bit lost with the long PR description and comments — is there a shorter version that may help me to gain context on what's being discussed? |
…s/gutenberg into update/widget-presentation-mode
Yeah, fair point. To me, the key question is what the responsibility of these abstractions is in the whole implementation.
This PR defines how a widget can be visualized across its entire dashboard widget chrome. |
|
@copilot resolve the merge conflicts in this pull request |
Merged latest Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
I think that's solvable with the toolbar hovering above the card at the position where it would be with the header too, and with some "badge" area behind toolbar icons to help it stand out. |
|
Trying to catch up here, with the caveat that I have very little context of the whole widget dashboard conversation. Full bleed widget contentI believe the main conversation is around how to better allow (in terms of APIs/contract) a widget to render its contents edge-to-edge within the frame that the dashboard assigns to it. In practical terms, if I understand correctly, that frame is the A few reflections:
I agree with the general idea of avoiding widget settings that are too tied to implementation details / rigid structures, and I also prefer using settings/props that describe the behaviour/intention of the widget without being too prescriptive (such as the We could also mix both Finally, should widgets also have a separate hint about their preferred size? Edit widget actionsI can think of 4 options:
Although, once again, I have little context on what the needs are exactly, and past design explorations. |
Yes, that's correct.
Agreed. A bad balance is when it allows authors to define the chrome padding per instance, for example. We don't want that. The padding and widget-chrome layout settings, in general, need to be consistent across all widget instances in a dashboard layout. That's why we give two options: regular configuration,
👍
Yes, that could work. We want to provide design tools to authors without compromising overall UI consistency.
We are working on a way to customize the widget instances so we can act on this feedback. I suggest accepting this implementation since I think it's flexible enough and can scale properly across other surfaces and scenarios. Eventually, we can keep polishing and tweaking it as it becomes more complex and powerful. |
The widget actions won't be hidden by the widget presentation. It's out of the widget decision.
We don't want to provide free composition for the widget. That's exactly why we want to introduce the
Agreed.
Agree as well. Finally, the widget consumer, in this case a dashboard, is ultimately the one who decides how to render the widget instance: static width, or maybe taking the rest of the width in the same row (
It's correct. Make it required in the widget schema, and just wrap it in a VisuallyHidden component |
|
Let's try this, experiment a bit with how we might have the toolbars look with this (#78060). We may switch to another method for defining layouts, or enforce a strict requirement that widget designs always include a header. We're behind the experimental flag with the dashboard, so we have all the freedom to revert, change or change things up. :-) I thought this was a particularly interesting note from @jameskoster :
We'll still need widget authors to use |
|
Noting that we're also ensuring there's always an accessible title, which is also required for widget picker etc. |



What?
Adds a top-level
presentationfield towidget.jsonso a widget can declare its intended render shape without naming a specific consumer.The dashboard surface honors
'full-bleed'by visually hiding the title header and routing the body throughCard.FullBleed. Other surfaces are free to translate the hint differently or ignore it.Design discussion: #77629.
Part of #77616
Why?
Some widgets are inherently edge-to-edge (image-led hero, embedded canvas) and need to drop the consumer's surrounding chrome unilaterally. The only escape hatch today is for surfaces to special-case widget names, which couples surfaces to specific widgets and defeats the registry.
presentationis an authoring intent expressed by the widget itself: the field describes the widget, not the surface. A surface translates the hint into its own layout model, or ignores it; a surface with no chrome treats both values identically.How?
End-to-end plumbing through the existing widget pipeline:
widget.jsondeclares"presentation": "framed" | "full-bleed"(optional, defaultframed).wp-buildextracts the value into the generated PHP manifest atbuild/widgets/registry.php.gutenberg_register_widget_typespasses it to theWP_Widget_Typeinstance./wp/v2/widget-modulesREST endpoint exposes it (snake_case).WidgetType.presentationshape.WidgetChrometranslatespresentation === 'full-bleed'into:Headerin<VisuallyHidden>so the title stays in the a11y tree and the section'saria-labelledbykeeps pointing to a valid node.<Card.FullBleed>so it spans inline edge-to-edge.Cleanup
Earlier work assumed widget identity types (
WidgetName,WidgetTypeMetadata,WidgetType) would eventually move to a dedicated@wordpress/widget-typespackage, alongside a dedicated store. Widget types now live in a CPT, so that extraction is off the table and the package will never exist. This PR takes the chance to drop the temporary duplication that arrangement implied, and to dedupe thepresentationdeclarations introduced earlier in the same diff:routes/dashboard/widget-dashboard/types.tscollapses to a re-export of the identity types from the canonicalroutes/dashboard/widget-types/types.ts, plus the dashboard-specific types it already owned. TheMIGRATIONcomment block is gone.WP_Widget_Type::PRESENTATION_VALUESis the single PHP source for the allowed values; the REST schema enum and the property docblock both reference it.WidgetModuleRecord.presentationin the resolver derives fromWidgetTypeMetadata['presentation']instead of repeating the union literal.Testing
widgets/hello-world/widget.jsonset to"presentation": "full-bleed": the Hello World tile shows the brand-tinted hero spanning the card's left/right edges; no title header is visible."presentation": "framed"): the title header reappears, and the hero sits inside the card padding with a visible margin around it.Independent of:
widgets/TypeScript scaffolding)Touches
routes/dashboard/widget-types/types.tson a different hunk than the generic-attribute-schemas PR; logically, no conflict.Consumed downstream by the On This Day widget (PR TBD), which declares
"presentation": "full-bleed"in itswidget.json.Follow-ups
schemas/json/widget.jsondoes not yet exist on trunk. When it lands, addpresentationas an optional string property with enum['framed', 'full-bleed'].Card.FullBleedcancels inline padding only. Vertical padding ofCard.Contentremains. Tracked separately (see Remove ZebulanStanphill fromCODEOWNERS#77586).