WordPress 6.9: styling forms with theme.json

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.json for: Background, border, padding, color: the foundational styles for textInput and select.
  • Use style.css for: 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?

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.