Buttons in WordPress Block Themes Are More Than a Block
When people talk about “buttons” in WordPress, they usually mean the Button block. But as anyone who has styled a Post Comment Form knows, buttons show up in other places too — often rendered with completely different markup yet expected to look the same.
In block themes, those styles aren’t written in CSS. They’re declared in theme.json, a structured schema that orchestrates everything from palette presets to element-level styling rules. Targeting buttons in that system requires understanding which buttons are blocks and which are elements nested inside other blocks.
Two Kinds of Buttons, One Target
From the editor’s perspective, you have two button types:
- The Button block, which exists as a child component inside the Buttons block
- Buttons embedded in other blocks, such as the Post Comments Form block
Both render with the same default look but produce different HTML. What saves you from writing duplicate CSS rules is the shared class name: .wp-block-button and .wp-element-button. These two classes are what the theme’s style engine uses to guarantee visual consistency across the board.
If you were working with plain CSS, you’d write a rule that targets both classes and be done. In a block theme, those styles belong inside the styles section of theme.json, defined against a button element.
Declaring Base Button Styles
WordPress interprets the button element as any markup that acts as a button on the front end — whether it’s the dedicated block or one nested inside a larger component like a comment form. Styling it at the element level means you get consistent defaults everywhere without touching each block individually.
Basic color overrides live inside a color object:
{
"version": 2,
"styles": {
"elements": {
"button": {
"color": {
"background": "#17a2b8",
"text": "#ffffff"
}
}
}
}
}
That single declaration changes the background and text color for both button flavors — the standalone Button block and nested controls like the Post Comment Form’s submit button. If you inspect the generated CSS in DevTools, you’ll see WordPress attaches these rules to the .wp-element-button class, giving you a clean style foundation without a custom stylesheet.
Adding Interaction States
Good button design isn’t just about resting states. CSS gives us pseudo-classes like :hover, :focus, :active, and :visited to reflect user interaction. In theme.json, those states are expressed as nested objects inside the existing button definition:
{
"version": 2,
"styles": {
"elements": {
"button": {
"color": {
"background": "#17a2b8",
"text": "#ffffff"
},
":hover": {
"color": {
"background": "#138496"
}
},
":focus": {
"color": {
"background": "#138496"
}
},
":active": {
"color": {
"background": "#138496"
}
}
}
}
}
}
The schema mirrors how you’d write CSS — an outline that goes from elements down to a property and its value — but it keeps everything structured and WordPress knows exactly where each rule belongs. That approach makes globals easy to maintain, but what happens when one specific button needs a unique look?
Targeting a Nested Button in One Block
Imagine you want all buttons to follow the theme defaults — except the one in the Post Comment Form block, which should stand out in blue. Since that button is an element living inside another block, you move from the elements section into the blocks section of the schema.
You need to nest a button style inside the Post Comment Form block, which is identified as core/post-comments-form:
{
"version": 2,
"styles": {
"elements" {
"button": {
// Default button styles
}
}
"blocks": {
"core/post-comments-form": {
"elements": {
"button": {
"color": {
"background": "#007bff"
}
}
}
}
}
}
}
The resulting CSS has a more explicit, heavily scoped selector that applies only to buttons inside of that block. The main difference: buttons are treated as global elements because they appear across multiple blocks, while the comment form’s button requires descending into its specific block.
Just like with the default styles, interaction states can be added to that nested button too. Declaring a :hover inside the core/post-comments-form block would overwrite the global hover effect for only that block’s submit button.
Covering Non-Block Button Markup
The WordPress Style Engine generates CSS from your theme.json config, but it only applies those styles to markup it recognizes. That’s fine for blocks. What about a classic PHP template, a custom block with hardcoded HTML, or a legacy shortcode that spits out a button?
Those situations simply need the right class attached to them. Adding .wp-element-button to the markup in those templates, shortcodes, or custom blocks lets the style engine find them and apply the generated rules.
If you can’t touch the markup at all because a third-party plugin boxes you out, you can still apply the class on a per-instance basis. Head to the block’s “Advanced” settings panel in the editor and add it there as a custom CSS class.
Why Bother With theme.json?
The mental shift from writing stylesheets to defining schemas takes some getting used to, but the payoff shows up in three key places:
- Your button styles render consistently in both the front-end view and inside the block editor’s preview canvas.
- Declaring styles in
theme.jsonensures version compatibility with future WordPress core updates. - The same configuration powers block themes, classic themes, and hybrid setups — there’s no duplicated CSS to maintain in a separate file.
With the schema handled, a shared set of button styles can be as broad as a site-wide default or as narrow as one control in one form.



