PRC's override/extension of the WordPress core/heading block.
Block Namespace
prc-block/core-heading
Target Block: core/heading
What PRC Customizes
- Changes the default heading level from H2 to H4
- Adds "chapter/section" functionality for table of contents integration
- Adds an alternate TOC text attribute for overriding what appears in the table of contents
- Registers "Heading" (default, H4), "Section" (chapter heading), and "Layout Heading" variations
- Registers "Layout Heading", "Hidden", and Sub-title block styles
- Registers a Sub-title block binding that reads/writes the
sub_titlepost meta field (replaces the deprecatedprc-block/sub-titleblock) - Cleans up auto-generated heading IDs (removes
h-prefix, converts leading numbers to words) - Generates reproducible IDs for headings without anchors
- Integrates chapter headings with
prc-block/table-of-contentsInteractivity API state - Handles legacy chapter detection for posts published before March 2023
- Provides transforms from legacy
[chapter]shortcode andprc-block/chapterblock
Supports Modifications
None directly via supports. However, the default heading level is changed to 4 via block_type_metadata_settings.
Additional Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
isChapter | boolean | false | Marks the heading as a section/chapter heading for TOC inclusion |
altTocText | string | '' | Alternate text to display in the table of contents instead of the heading content |
level | number | 4 | Heading level (H1–H6) |
placeholder | string | '' | Placeholder text in the editor when empty (Sub-title variation: "Add sub-title") |
Registered both server-side (block_type_metadata) and client-side (blocks.registerBlockType).
Additional context consumed:
| Context Key | Description |
|---|---|
postId | Used for legacy chapter heading detection |
prcLegacyChapter | Set to true for posts before March 2023 |
Available Styles
| Style Name | Label | Description |
|---|---|---|
layout-heading | Layout Heading | Black bottom border with 4px padding and 24px margin |
hidden | Hidden | Visually hidden (0px font, 0 opacity, 0 height) on frontend; 50% opacity in editor |
sub-title | Sub-title | Smaller, secondary heading style for post subtitles |
Sub-title block binding
The deprecated prc-block/sub-title block was migrated to a bound core/heading with the Sub-title style. Producers insert a heading, apply the Sub-title style, and bind its content to the sub_title post meta field via the block bindings UI.
Binding source: prc-block/core-heading/sub-title (registered in block-bindings.js and class-core-heading.php).
Post meta: sub_title — registered with show_in_rest and single => true so the binding is editable in the block editor and persists through REST saves (including RTC).
Rendering rules (render_sub_title_heading):
- Bound headings outside
core/post-content(e.g. in single templates) render the livesub_titlemeta value. - Bound headings inside
core/post-contentare suppressed to avoid duplicating the template subtitle. - Empty or whitespace-only meta renders nothing.
Migration: Legacy prc-block/sub-title blocks remain in deprecated/src/sub-title/ for backward compatibility. New content should use the bound heading pattern above.
Style Overrides
From style.scss:
Hidden style: On the frontend, headings with is-style-hidden (excluding editor context) get zero font size, zero opacity, zero height. In the editor, they display at 50% opacity.
Layout heading: is-style-layout-heading gets a 1px solid black bottom border, 4px bottom padding, 24px bottom margin.
Sub-title style: is-style-sub-title applies the secondary heading typography defined in style.scss (used by the sub-title block binding).
Legacy section-header: Same visual treatment as layout-heading (backward compatibility).
Sub-header alt: Black background with white text, display block.
Editor Enhancements
From index.js and controls.jsx:
- Wraps block edit with custom controls via
createHigherOrderComponent - Toolbar: Adds a book-alt icon toggle with labels Make Chapter / Remove Chapter to set
isChapter. When turning a heading into a chapter, ifgetAnchorFromContent(content)yields a slug, the block also sets theanchorattribute so the fragment ID matches the heading text. - Inspector Advanced Controls: When
isChapteris true, shows an Alternate TOC Text text field (placeholder shows current headingcontent). - Anchor sync (H3 and H4): For heading levels 3 and 4, a
useEffectkeeps theanchorattribute aligned withgetAnchorFromContent(content)when the heading text changes or when the level switches into H3/H4 (same slug rules as below). Other levels do not receive automatic anchor updates from content. getAnchorFromContent(src/core-heading/utils.js): derives a URL slug from the raw RichTextcontent— decode HTML entities, strip tags (inserting a space at tag boundaries so words do not run together), collapse whitespace, thencleanForSlug.- Sub-title variation: the bound Sub-title heading variation sets
placeholder: Add sub-titlein the editor. - Registers transforms from legacy blocks
- Registers three block variations
Frontend Interactivity
No dedicated view.js. Interactivity is handled server-side by injecting attributes on chapter headings that integrate with the prc-block/table-of-contents store.
PHP Rendering
Class: Core_Heading
Key behaviors:
- ID cleanup: Removes
h-prefix from auto-generated IDs. Converts leading numbers to words (e.g.,h-5-thingsbecomesfive-things). Falls back to an MD5 hash of block content for reproducible IDs. - Legacy chapter detection: For the first heading in
core/post-content, checks if the post date is before March 2023. If so, setsprcLegacyChaptercontext totrue, which auto-marks H3 headings as chapters. - Table of contents integration: When
isChapteris true:- Registers the heading in
wp_interactivity_state('prc-block/table-of-contents')undersectionsFound - Uses
altTocTextas the TOC label if provided, otherwise strips tags from heading content - Adds
data-is-section="true"attribute - Adds
data-wp-interactiveanddata-wp-contextfor TOC interactivity
- Registers the heading in
- Context filter:
check_if_legacy_chapter_headingruns once per post to determine legacy status, avoiding repeated database lookups.
Block Markup Example
<!-- Standard heading (H4 default) -->
<h4 class="wp-block-heading" id="survey-methodology">Survey Methodology</h4>
<!-- Chapter/section heading with TOC integration -->
<h3
class="wp-block-heading"
id="key-findings"
data-is-section="true"
data-wp-interactive='{"namespace":"prc-block/table-of-contents"}'
data-wp-context='{"id":"key-findings"}'
>
Key Findings
</h3>
<!-- Layout heading -->
<h3 class="wp-block-heading is-style-layout-heading">Related Reports</h3>
<!-- Hidden heading (accessibility only) -->
<h4 class="wp-block-heading is-style-hidden">Screen reader only text</h4>
Variations
| Variation Name | Title | Description | Default Level |
|---|---|---|---|
heading | Heading | Default heading variation | H4 |
section | Section | Chapter/section heading included in TOC | H3 |
layout-heading | Layout Heading | Heading with bottom border styling | H3 |
Transforms
| Source | Type | Description |
|---|---|---|
[chapter] shortcode | Shortcode | Converts [chapter title="..."] to H2 chapter heading |
prc-block/chapter | Block | Converts legacy chapter block to heading with isChapter: true |
core/shortcode containing [chapter] | Block | Extracts title and creates H2 chapter heading |