Had a client call the other day, really happy with the new block theme we built. Everything looked great, he said, except the forms. The comment form, the little search bar in the header, they looked like they were styled in 2003. Pure default browser look. “It just feels… broken,” he said. And he was right. For years, getting consistent form styling in WordPress has been a nightmare of CSS overrides and !important tags.
WordPress 6.9 finally takes a real swing at this with theme.json form styling. It’s not a home run yet, more like a solid double, but it’s a move in the right direction. We can now style some core form elements properly, straight from the theme’s configuration file. As the official WordPress Developer Blog notes, this has been a long-requested feature.
The old way vs. the “proper” way
My first thought was, “Fine, I’ll just write some global CSS.” Slap some styles on input, select, and textarea in the theme’s style.css and call it a day. And yeah, that works. Until it doesn’t. You quickly end up in specificity wars with every plugin that drags its own forms and stylesheets to the party. It’s a brittle, frustrating game of whack-a-mole.
The right approach now is to define these styles in theme.json. That treats form elements as first-class citizens of the global styling system, the same as buttons and headings. With 6.9 we get two new elements to work with: select and textInput.
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"styles": {
"elements": {
"textInput": {
"border": {
"radius": "0.5rem",
"width": "1px"
},
"color": {
"background": "#f0f0f0"
},
"spacing": {
"padding": "0.75rem"
}
},
"select": {
"border": {
"radius": "0.5rem",
"width": "1px"
},
"color": {
"background": "#f0f0f0"
},
"spacing": {
"padding": "0.75rem"
}
}
}
}
}
So, what’s the catch?
There’s a catch: this is only the first pass. It covers basic text inputs and select dropdowns, but it leaves a few important things out. The biggest gap is pseudo-class support. You can’t style the :focus state in theme.json. Not yet, anyway. That matters for accessibility and usability, since a clear focus indicator is essential.
So for now we’re in a hybrid setup. You define the base styles in theme.json to keep things clean and integrated with the block theme system, then add a little CSS for what isn’t supported yet. It still beats going back to doing everything in a stylesheet.
- Use
theme.jsonfor: Background, border, padding, color: the foundational styles fortextInputandselect. - Use
style.cssfor: Focus states (:focus), styling labels, and handling unsupported input types like checkboxes and radios.
This stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
It’s a step forward, and a welcome one. It makes themes cleaner and easier to maintain, even if it isn’t the whole picture yet. What other elements do you think need theme.json support next?