Skip to content

Modernize theme for WordPress 7.1: theme.json, block.json registration, and the missing templates - #1

Merged
mark-iid merged 6 commits into
mainfrom
modernize-theme
Aug 28, 2026
Merged

Modernize theme for WordPress 7.1: theme.json, block.json registration, and the missing templates#1
mark-iid merged 6 commits into
mainfrom
modernize-theme

Conversation

@mark-iid

@mark-iid mark-iid commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Brings the theme up to current WordPress practice. It ran fine on WP 7.1 before this, but architecturally it was a pre-5.9 classic theme: no theme.json, no editor styles, dead block code, and a !important layer fighting the cascade instead of participating in it.

Six commits, each self-contained and reviewable on its own.

The main change: theme.json replaces the nuclear CSS

The theme had no theme.json, so WordPress emitted its default Global Styles and the theme fought back — 186 !important declarations in norton.css, plus norton_nuclear_css(), ~250 lines of duplicated rules injected at wp_head priority 999. Both are gone.

theme.json (v3) now declares the palette, typography, spacing scale, layout sizes and the link/heading/button element styles, and switches off the core presets and custom colour pickers that caused the conflicts. Colours reach CSS as --wp--preset--* custom properties.

The stylesheet splits so the editor can share it:

File Scope
norton-components.css Design tokens + box/alert/table chrome (shared with editor)
norton.css Front-end layout, header, nav, sidebar, footer
editor-style.css Block editor canvas adjustments

Neither stylesheet contains a single !important.

add_theme_support('editor-styles') gives the block editor the theme's look. WordPress 7.0 stopped passing classic themes any post-editor styling, so without this the canvas is plain white.

Blocks: block.json was dead code

blocks/*/block.json and render.php existed but nothing loaded them. inc/blocks.php registered the same three blocks from inline PHP arrays with no render_callback, and blocks.js registered them a third time client-side with its own save(). The three definitions disagreed — that's how blocks fail validation on re-edit.

block.json is now the single source of truth. Blocks are dynamic: save() serialises only inner blocks, render.php emits the wrapper via get_block_wrapper_attributes(), so the align/anchor/spacing supports actually apply — and future markup changes reach existing posts without re-saving them.

Unreachable features

  • Pagination — neither index.php nor archive.php called the_posts_pagination(). Past page one there was no way to navigate.
  • Comments — the theme declared html5 support for comment-form and comment-list but shipped no comments.php and never called comments_template().
  • Search — results fell through to index.php with no heading, so you couldn't see what you'd searched for.

Accessibility

.screen-reader-text was used by the read-more links but never defined in CSS, so post titles rendered visibly as [READ MORE "My Post Title"]. That's a visible rendering bug, not just an a11y one. Fixed, plus a skip link and a :focus-visible ring. Also dropped cursor: default !important on html body *, which was removing the text caret from inputs.

The nav walker is deleted, not patched

It existed only to strip <ul>/<li>, and in doing so dropped submenus entirely, plus aria-current, menu item classes, and target/rel/title. Core's Walker_Nav_Menu provides all of that. The DOS menu-strip look is now CSS — submenus drop on hover and :focus-within, and stack inline on mobile. The hand-rolled fallback became wp_page_menu() so it emits matching markup.

Housekeeping

  • footer.php used PHP date(), which reads the server timezone rather than the site's — now wp_date()
  • error_log() fired on every init, filling the site error log indefinitely; console.log() fired in every editor session
  • style.css declares Requires at least / Tested up to / Requires PHP
  • load_theme_textdomain() + languages/norton-simple.pot, so translation-ready is now true
  • defined( 'ABSPATH' ) || exit; on every PHP file
  • Redundant role="main" / role="complementary" removed
  • CI Node 20 → 24 (Node 20 hit EOL April 2026)

Testing

Verified against a real WordPress 7.1 + MariaDB instance driven by wp-cli and headless Chrome, not just by reading the diff:

  • All templates return 200 with zero PHP diagnostics under WP_DEBUG — home, single, page, paged, search, 404, feed
  • All three blocks register at apiVersion 3 with render callbacks; theme.json parses as v3 and emits every custom property
  • Editor canvas computes navy background, white text, Courier, and .norton-box at 20px padding with the bevel border — editor and front end match
  • The built zip installs into a clean WordPress via wp theme install and renders identically

That testing caught a real bug introduced in this branch: a CSS comment reading blocks/*/render.php had its */ close the comment early, making the parser swallow the next rule and silently drop all padding and margin from .norton-box and .norton-box-invert. Both stylesheets lint clean and every PHP file parses, so nothing static would have flagged it — only the rendered page showed it. Fixed in bf3d336.

Reviewer notes

Two behaviour changes worth a decision:

  1. theme.json restricts the palette to the Norton colourscolor.custom and gradients are off. That's what lets the CSS drop !important. Any existing block where someone picked an arbitrary colour will fall back to the palette. Easy to re-enable if that's not wanted.
  2. Existing posts using the old blocks — saved markup still parses, but worth checking one real post before merging.

Also beyond the original scope, flagged for a call: search.php is new, and package-lock.json is now tracked (CI runs npm install; build.js already excludes it from the shipped theme).

I did not claim the accessibility-ready tag — the work here would likely pass, but that tag carries a formal wordpress.org audit and none was run.

error_log() fired on every init in inc/blocks.php, filling the site
error log indefinitely. console.log() fired in every block editor
session. Neither belongs in shipped code.
The blocks/*/block.json and render.php files were dead code: inc/blocks.php
registered the same three blocks from inline PHP arrays with no
render_callback, and assets/js/blocks.js registered them a third time
client-side with its own save(). The three definitions disagreed, which is
how blocks end up failing validation on re-edit.

Now block.json is the single source of truth. Blocks are registered with
register_block_type() against their directory, making them dynamic: save()
serialises only the inner blocks and render.php emits the wrapper via
get_block_wrapper_attributes(), so the align/anchor/spacing supports
declared in block.json actually apply.

The editor script is registered once as a shared handle referenced by each
block.json "editorScript", replacing the manual enqueue_block_editor_assets
hook. The alert type control moves from a raw <select> in the content area
into an InspectorControls panel.

Alert type classes are namespaced to is-type-* to avoid colliding with
generic .error/.success utility classes; the shortcode emits the same.
The theme had no theme.json, so WordPress emitted its default Global Styles
and the theme fought back: 186 !important declarations in norton.css plus
norton_nuclear_css(), ~250 lines of duplicated rules injected at wp_head
priority 999. Both are gone.

theme.json (v3) now declares the palette, typography, spacing scale, layout
sizes and the link/heading/button element styles, and switches off the core
presets, custom colour pickers and gradients that were the source of the
conflicts. Colours reach CSS as --wp--preset--* custom properties, with the
bevel tokens under settings.custom.

The stylesheet splits in two so the editor can share it:

  norton-components.css  design tokens plus the box/alert/table chrome
  norton.css             front-end layout, header, nav, sidebar, footer

editor-style.css and add_theme_support('editor-styles') give the block
editor the theme's look. WordPress 7.0 stopped passing classic themes any
post-editor styling, so without this the canvas is plain white.

Neither stylesheet contains a single !important.

Also adds .screen-reader-text and .skip-link, a :focus-visible ring, and
drops the `cursor: default !important` on `html body *` that was removing
the text caret from inputs.

Verified against WordPress 7.1: theme.json parses as v3, all custom
properties are emitted, both editor stylesheets register, and the rendered
pages are free of PHP notices under WP_DEBUG.
Past the first page of posts there was previously no way to navigate:
neither index.php nor archive.php called the_posts_pagination(). Single
posts get prev/next navigation too.

Comments were unreachable. The theme declared html5 support for
comment-form and comment-list but shipped no comments.php and never called
comments_template(). Both partials now load it when a post accepts or
already has comments.

Search results fell through to index.php, so a visitor got a bare list with
no confirmation of what they searched for. search.php shows the query and
repeats the form.
.screen-reader-text was used by the read-more and continue-reading links but
never defined in CSS, so post titles rendered visibly: [READ MORE "My Post"].
The class now exists, a skip link targets #main, and there is a
:focus-visible ring.

The custom nav walker emitted a bare <a> per item, dropping submenus,
aria-current, the menu item classes, and target/rel/title. It is gone: core's
Walker_Nav_Menu supplies all of that, and CSS keeps the DOS menu-strip look —
submenus drop on hover or focus-within, and stack inline on mobile. The
hand-rolled fallback becomes wp_page_menu() so it emits matching markup.

Also:
- role="main" / role="complementary" removed; <main> and <aside> imply them.
- footer.php used PHP date(), which reads the server timezone rather than the
  site's; now wp_date(). bloginfo() calls escaped.
- style.css declares Requires at least / Tested up to / Requires PHP.
- load_theme_textdomain() plus languages/norton-simple.pot, so the
  translation-ready tag is now true.
- defined( 'ABSPATH' ) || exit; guards on every PHP file.
- automatic-feed-links, responsive-embeds, align-wide,
  customize-selective-refresh-widgets support added; register_sidebar() moved
  to the widgets_init hook where it belongs.
- CI Node bumped 20 -> 24; Node 20 reached end of life in April 2026.
Documents the blocks, the theme.json styling model and the new file layout,
and drops the "nuclear CSS injection layer" from the feature list — it no
longer exists.

package-lock.json was untracked; running the build generated it, and CI runs
`npm install`, so committing it makes the zip build reproducible. build.js
already excludes it from the shipped theme.
@mark-iid
mark-iid merged commit 31a43d1 into main Aug 28, 2026
1 check passed
@mark-iid
mark-iid deleted the modernize-theme branch August 28, 2026 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant