Block Type Contracts
Purpose And Scope
This reference describes the shipped published core block types, their editable fields, translations, child rules and public rendering. The Block Types administration screen exposes the same contract as read-only information; it does not define a custom schema or build new editing forms.
Plugins support declaration-only plugin block hooks through PluginBlockTypeDefinition, PluginBlockPackDefinition, and PluginBlockRegistry. Enabled plugins can expose plugin-owned handles such as analytics-tools::score-card for discovery, but these declarations do not replace the shipped core block contracts, core block views, core seeders, or block editing services. Unqualified core-style handles such as hero remain core-owned and are rejected for plugin declarations.
Definition
A Block Type Contract is the current technical agreement for how one block type behaves across CMS layers:
- catalog identity: slug, label, category, status, and system or container metadata
- admin form source: which Blade partial currently edits the block and which fields it exposes
- validation and request handling: how
WebBlocks\Cms\Http\Requests\Admin\BlockRequestcurrently normalizes and validates block payloads - storage ownership: which values live on
blocks, dedicated translation rows,block_media, or related records - translation ownership: which user-facing fields are locale-owned
- shared ownership: which settings or relationships remain shared across locales
- media or relationship ownership: direct
media_id, orderedblock_media, navigation lookups, or same-page relationships - child support: whether the block is a container and whether child types are restricted
- public renderer source: which public Blade partial renders the block today
- renderer root contract: whether the block owns its public root markup or relies on the generic wrapper path
- portability and revisions: whether the current storage shape should continue to travel through revisions, clone, export/import, and promotion
- tests and gaps: known focused coverage, unclear behavior, or compatibility debt
Current Contract Terms
Status terms used below:
clear: admin form, request handling, storage, and renderer mostly line upmostly clear: current contract is understandable but has one notable caveattransitional: current contract intentionally carries a compatibility path or mixed ownership patternneeds review: the installed contract needs confirmation before relying on the behaviorlegacy/fallback: published behavior depends on a fallback or compatibility path
Storage Ownership Rules
Current and future contract work should keep these ownership rules explicit:
- user-facing copy belongs in translation rows when the block is locale-owned
- shared operational or settings data belongs in shared block settings or explicit relationships
- media should use
media_idorblock_mediaownership paths where applicable - avoid moving user-facing content into arbitrary shared settings
- keep compatibility storage paths documented when they still affect public output or imports
From CMS 1.98.6, the compatible settings model/API field is backed by relational
records. Known core fields use finite typed appearance, media, navigation and
content columns. Content bindings, collection configuration and application
instance values have separate records; typed nodes retain structure, ordering
and historical/plugin extensions. CMS 1.98.8 removed the temporary JSON rollback
copies; SQL is authoritative. The editor, translation ownership, API payloads and renderer methods
keep their existing contracts; storage migration does not move locale-owned copy
into shared settings.
Authoring Settings Validation
CMS 1.105.9 adds revisioned authoring_settings_schema discovery for all 55
published core blocks. Read the separate create, PATCH and admin write flags
before supplying a field. New or changed values must satisfy the declared types,
enums and bounds; unsupported fields return validation errors instead of being
silently accepted or cleared. This remains a code-owned contract, not an editable
schema or a generated form builder.
Unchanged historical values survive unrelated edits and trusted cloning, staging and revisions. New blocks and complete replacements must pass current validation. Plugins without a schema retain their existing behavior; optional plugin scalar schemas do not replace media, application or Dynamic Content validators. HTML remains human-only.
See Per-block authoring settings schemas
for operation metadata, compatibility rules and the read-only
webblocks:blocks:audit-settings compatibility command. The separate catalog
audit below checks shipped forms and renderers, rather than stored settings.
Shipped Sources
The reference uses the shipped catalog, forms and renderers.
- catalog source:
src/Support/Blocks/CoreBlockTypeCatalogSyncer.php - block edit request normalization:
src/Http/Requests/Admin/BlockRequest.php - persistence helpers:
src/Support/Blocks/BlockPayloadWriter.php,src/Support/Blocks/BlockTranslationWriter.php,src/Support/Blocks/BlockTranslationResolver.php - translation registry:
src/Support/Blocks/BlockTranslationRegistry.php - admin forms:
resources/views/admin/blocks/types/*.blade.php - public renderers:
resources/views/pages/partials/blocks/*.blade.php - coverage references: block catalog, rendering, translation, and slug-specific tests under
tests/Feature/
Published core block types currently documented here: 55.
Catalog reconciliation: WebBlocks CMS 1.105.8. The table covers all 55 published core definitions; seven additional definitions remain unpublished. Later phase notes below are historical records, not a second current catalog count.
Audit Command
Operators and developers can run this read-only catalog check:
php artisan block-types:contracts-audit
php artisan block-types:contracts-audit --json
The command is read-only.
- it does not modify the database
- it does not depend on installed site content
- it reads the shipped core catalog definitions
- it verifies shipped admin form and public renderer file presence
- it reports translation-family metadata and basic container support
The command is a freshness aid for catalog and file-presence drift. It is not a substitute for the fuller contract notes in this document.
Read-Only Admin View
Open contract details read-only in Admin -> System -> Block Types.
- each row can open a
Block Type Contractmodal - the modal is informational only and does not submit updates
- it shows catalog, admin form, storage, translation, media or relationship, child, renderer, and gap details from shipped code
- it does not make Block Types admin a schema editor or form builder
- custom or draft block types can still open the modal, but may show
No shipped contract is documented for this block type yet.when no core contract is defined
Published Block Contract Matrix
Content
Header — header
Content and behavior
| Field | Contract |
|---|---|
| Slug | header |
| Label | Header |
| Category | content |
| Translatable fields | title via text translation rows |
| Shared/settings fields | variant heading level; settings.alignment; shared anchor in settings.anchor with legacy url fallback |
| Media/relationship fields | Same-slot TOC reads anchored Header blocks |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Canonical heading block; shared anchor contract is clear but still has legacy url fallback behavior. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/header.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/header.blade.php |
| Renderer root contract | Owns its root heading element |
| Tests / coverage | SyncCoreBlockTypesCommandTest, PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest |
Plain Text — plain_text
Content and behavior
| Field | Contract |
|---|---|
| Slug | plain_text |
| Label | Plain Text |
| Category | content |
| Translatable fields | content via text translation rows |
| Shared/settings fields | settings.alignment |
| Media/relationship fields | None |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Simple translated body-copy primitive. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/plain_text.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/plain_text.blade.php |
| Renderer root contract | Owns its root <p> |
| Tests / coverage | PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest |
Rich Text — rich-text
Content and behavior
| Field | Contract |
|---|---|
| Slug | rich-text |
| Label | Rich Text |
| Category | content |
| Translatable fields | content via text translation rows |
| Shared/settings fields | None |
| Media/relationship fields | None |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Safe HTML storage and translation ownership are clear. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/rich-text.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/rich-text.blade.php |
| Renderer root contract | Owns its .wb-rich-text root when content exists |
| Tests / coverage | RichTextBlockTest, PublicRichContentTest, BlockTranslationIntegrityTest |
Code — code
Content and behavior
| Field | Contract |
|---|---|
| Slug | code |
| Label | Code |
| Category | content |
| Translatable fields | title, subtitle, content via text translation rows |
| Shared/settings fields | settings.language |
| Media/relationship fields | None |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | Aligns translated code title, label, and snippet body with the existing text-translation architecture and now ignores arbitrary historical child trees in public output. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/code.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/code.blade.php |
| Renderer root contract | Owns its <pre><code> root |
| Tests / coverage | PublicRichContentTest, BlockTypePhaseThreeContractsTest, PageBuilderExperienceTest |
Button Link — button_link
Content and behavior
| Field | Contract |
|---|---|
| Slug | button_link |
| Label | Button Link |
| Category | content |
| Translatable fields | title label via text translation rows |
| Shared/settings fields | settings.url; settings.target; shared variant |
| Media/relationship fields | None |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Shared URL and target with translated label are consistent. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/button_link.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/button_link.blade.php |
| Renderer root contract | Owns its <a.wb-btn> root |
| Tests / coverage | PublicEditorialBlocksRenderingTest |
Card — card
Content and behavior
| Field | Contract |
|---|---|
| Slug | card |
| Label | Card |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | Shared media_id; settings.layout_name; settings.background_position; settings.background_overlay; optional settings.url and settings.target |
| Media/relationship fields | Optional background image via direct media_id; child region blocks define structure |
| Child/container behavior | Container; allowed direct children are card_header, card_body, and card_footer only |
| Current status | clear |
| Known gaps / notes | Card is a composable shell. Its optional whole-card URL is safe and semantic; editors must not place nested interactive controls in linked Cards. It supports Media Library backgrounds and keeps a minimal legacy fallback for older rows without Card regions. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/card.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/card.blade.php |
| Renderer root contract | Owns its <article class="wb-card"> root or one semantic <a class="wb-card wb-no-decoration"> root when linked |
| Tests / coverage | CardClickableRenderTest, PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest, BlockTranslationIntegrityTest, MediaVisualBlockContractsTest |
Card Header — card_header
Content and behavior
| Field | Contract |
|---|---|
| Slug | card_header |
| Label | Card Header |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name, settings.icon_slug, settings.icon_tone, settings.icon_size |
| Media/relationship fields | Parent card relationship only |
| Child/container behavior | Container; may only be placed under card; card-region children are not allowed |
| Current status | clear |
| Known gaps / notes | Composable Card region block for header content. Optional icons use active content catalog slugs and render only through safe icon, tone, and size classes. Card Header has no badge copy and therefore exposes no badge tone. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/card_header.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/card_header.blade.php |
| Renderer root contract | Owns its <div class="wb-card-header"> root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest, PublicIconBadgeSupportTest |
Card Body — card_body
Content and behavior
| Field | Contract |
|---|---|
| Slug | card_body |
| Label | Card Body |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name |
| Media/relationship fields | Parent card relationship only |
| Child/container behavior | Container; may only be placed under card; card-region children are not allowed |
| Current status | clear |
| Known gaps / notes | Composable Card region block for main body content. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/card_body.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/card_body.blade.php |
| Renderer root contract | Owns its <div class="wb-card-body"> root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Card Footer — card_footer
Content and behavior
| Field | Contract |
|---|---|
| Slug | card_footer |
| Label | Card Footer |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name |
| Media/relationship fields | Parent card relationship only |
| Child/container behavior | Container; may only be placed under card; card-region children are not allowed |
| Current status | clear |
| Known gaps / notes | Composable Card region block for footer or action content. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/card_footer.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/card_footer.blade.php |
| Renderer root contract | Owns its <div class="wb-card-footer"> root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Stat Card — stat-card
Content and behavior
| Field | Contract |
|---|---|
| Slug | stat-card |
| Label | Stat Card |
| Category | content |
| Translatable fields | title, subtitle, content via text translation rows |
| Shared/settings fields | Canonical url remains shared on the block row |
| Media/relationship fields | None |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Keeps the existing optional URL field and now renders a simple public link when present. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/stat-card.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/stat-card.blade.php |
| Renderer root contract | Owns its stat-card root |
| Tests / coverage | StatCardTest, BlockTranslationIntegrityTest |
Image — image
Content and behavior
| Field | Contract |
|---|---|
| Slug | image |
| Label | Image |
| Category | content |
| Translatable fields | caption, alt text via image translation rows |
| Shared/settings fields | Shared media_id; shared canonical url; settings.viewer_enabled; settings.viewer_group |
| Media/relationship fields | Direct image media relation via media_id |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | Caption and alt text are locale-owned while media, optional link URL, and viewer grouping remain shared. Explicit links take precedence; otherwise enabled Image blocks with the same group share the existing CMS Gallery Viewer without changing page composition. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/image.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/image.blade.php |
| Renderer root contract | Owns its semantic <figure> root when media exists and may register with one grouped Gallery Viewer |
| Tests / coverage | MediaVisualBlockContractsTest, PublicMediaBlocksTest, BlockTranslationIntegrityTest, PublicOverlayRegistryTest |
Gallery — gallery
Content and behavior
| Field | Contract |
|---|---|
| Slug | gallery |
| Label | Gallery |
| Category | content |
| Translatable fields | Per-gallery-item alt_text, caption, overlay_title, and overlay_text via block_gallery_item_translations |
| Shared/settings fields | Shared gallery presentation settings plus optional viewer title and ordered block_media gallery-item relations |
| Media/relationship fields | Ordered block_media rows with role gallery_item; locale-owned gallery item copy lives on block_gallery_item_translations; legacy stored block title/subtitle values may still exist but are ignored by public rendering |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | Gallery is now a media-collection block. The normal admin form uses a compact list-row editor instead of the older selected-assets grid. Its nested Add Gallery Items picker stays on the shared admin #wb-overlay-root contract so WebBlocks UI owns the stacked modal lifecycle, and long compact result lists keep natural row height while the modal body remains the scroll container. Public variants are distinct: grid keeps equal cells, masonry uses CSS columns with natural image height, and collage keeps the featured-first composition. Legacy saved masonary values are still accepted and normalized to the canonical masonry path. Gallery no longer owns intro heading or paragraph output; editors should place content_header plus plain_text or rich-text before Gallery when section copy is needed. The optional viewer title labels only the lightbox modal header. Legacy settings-based fallback items remain readable for older content. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/gallery.blade.php plus resources/views/admin/blocks/types/partials/gallery-items-editor.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/gallery.blade.php |
| Renderer root contract | Owns its gallery wrapper root and registers one viewer modal under #wb-overlay-root when lightbox is enabled |
| Tests / coverage | MediaVisualBlockContractsTest, PublicMediaBlocksTest, PageBuilderExperienceTest, SiteCloneServiceTest, SiteExportImportTest, SitePromotionTest, ReconstructionIntegrityTest, SharedSlotRevisionTest |
Download — download
Content and behavior
| Field | Contract |
|---|---|
| Slug | download |
| Label | Download |
| Category | content |
| Translatable fields | title, subtitle via text translation rows |
| Shared/settings fields | Shared media_id; shared variant |
| Media/relationship fields | Direct download media relation via media_id |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | Keeps the selected download media and button variant shared while moving visible label and helper copy into the existing text-translation path. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/download.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/download.blade.php |
| Renderer root contract | Owns its CTA wrapper root when media exists |
| Tests / coverage | MediaVisualBlockContractsTest, PublicMediaBlocksTest, AudioContentApiTest |
File — file
Content and behavior
| Field | Contract |
|---|---|
| Slug | file |
| Label | File |
| Category | content |
| Translatable fields | title, content via text translation rows |
| Shared/settings fields | Shared media_id; shared canonical url |
| Media/relationship fields | Direct media relation via media_id with external URL fallback |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | Provides the shipped File renderer with a dedicated admin form and keeps shared file-source ownership explicit between Media and external URL fallback. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/file.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/file.blade.php |
| Renderer root contract | Owns its file-card root |
| Tests / coverage | MediaVisualBlockContractsTest, PublicMediaBlocksTest, AudioContentApiTest |
Video — video
Content and behavior
| Field | Contract |
|---|---|
| Slug | video |
| Label | Video |
| Category | content |
| Translatable fields | title, content via text translation rows |
| Shared/settings fields | Shared media_id; shared canonical url |
| Media/relationship fields | Direct media relation via media_id with safe external URL fallback |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | Provides the missing admin form and save path so uploaded video media, safe external provider URLs, translated visible copy, and public fallback behavior now describe the same contract. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/video.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/video.blade.php |
| Renderer root contract | Owns its video-card root |
| Tests / coverage | MediaVisualBlockContractsTest, PublicMediaBlocksTest, AudioContentApiTest |
Audio — audio
Content and behavior
| Field | Contract |
|---|---|
| Slug | audio |
| Label | Audio |
| Category | content |
| Translatable fields | title, content via text translation rows |
| Shared/settings fields | Shared media_id; shared canonical url |
| Media/relationship fields | Direct media relation via media_id with external URL fallback |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | Admin, Content Plan and incremental block writes accept audio media with kind other and an audio/* MIME type; incompatible media is rejected. Localized copy and safe no-empty-controls rendering remain native. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/audio.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/audio.blade.php |
| Renderer root contract | Owns its audio-card root |
| Tests / coverage | MediaVisualBlockContractsTest, PublicMediaBlocksTest, AudioContentApiTest |
Table — table
Content and behavior
| Field | Contract |
|---|---|
| Slug | table |
| Label | Table |
| Category | content |
| Translatable fields | title, content via text translation rows |
| Shared/settings fields | Shared variant; renderer also checks settings.rows fallback |
| Media/relationship fields | None |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | mostly clear |
| Known gaps / notes | Aligns translated title and row-copy ownership with the existing text-translation architecture and now ignores arbitrary historical child trees in public output; the legacy settings.rows fallback path remains documented. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/table.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/table.blade.php |
| Renderer root contract | Owns its table wrapper root |
| Tests / coverage | PublicRichContentTest, BlockTypePhaseThreeContractsTest, PageBuilderExperienceTest |
Quote — quote
Content and behavior
| Field | Contract |
|---|---|
| Slug | quote |
| Label | Quote |
| Category | content |
| Translatable fields | None in the registry today |
| Shared/settings fields | Canonical title, subtitle, content, variant |
| Media/relationship fields | None |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | Quote is not a layout/container wrapper in public output while preserving existing saved child rows in admin block trees. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/quote.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/quote.blade.php |
| Renderer root contract | Owns its quote root |
| Tests / coverage | PublicRichContentTest, PageBuilderExperienceTest |
Hero — hero
Content and behavior
| Field | Contract |
|---|---|
| Slug | hero |
| Label | Hero |
| Category | content |
| Translatable fields | title, subtitle, content via text translation rows |
| Shared/settings fields | Shared media_id; shared variant; settings.layout; settings.title_tag; settings.background_position; settings.background_overlay |
| Media/relationship fields | Optional background image via direct media_id; child button_link blocks for actions |
| Child/container behavior | Container; button_link children (legacy button also allowed), no fixed count |
| Current status | transitional |
| Known gaps / notes | Hero is now a published source-backed core contract. Intro copy is locale-owned, action buttons are ordinary button_link children that own their own labels and URLs, and optional Media Library background images render through the native WebBlocks UI background-media primitive. Legacy content fallbacks remain readable where canonical translated fields are empty. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/hero.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/hero.blade.php |
| Renderer root contract | Owns its promo <section> root |
| Tests / coverage | PublicHeroBlockRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Columns — columns
Content and behavior
| Field | Contract |
|---|---|
| Slug | columns |
| Label | Columns |
| Category | content |
| Translatable fields | title, subtitle, content via text translation rows |
| Shared/settings fields | Shared variant |
| Media/relationship fields | Child column_item blocks |
| Child/container behavior | Container; only column_item children |
| Current status | clear |
| Known gaps / notes | Columns is now a published first-class structured content block. Intro copy is locale-owned while variant, child order, and child URLs stay shared. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/columns.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/columns.blade.php |
| Renderer root contract | Owns its structured-content <section> root |
| Tests / coverage | PublicColumnsRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Column Item — column_item
Content and behavior
| Field | Contract |
|---|---|
| Slug | column_item |
| Label | Column Item |
| Category | content |
| Translatable fields | title, eyebrow as badge label, subtitle, content via text translation rows |
| Shared/settings fields | Shared canonical url, settings.icon_slug, settings.icon_tone, settings.icon_size, settings.badge_tone |
| Media/relationship fields | Parent columns relationship |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Column Item uses the shipped WebBlocks UI wb-icon-card composition for icon-led plain and card variants. Locale edits update item copy without overwriting shared URLs, icon slug, icon tone, icon size, or badge tone. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/column_item.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/column_item.blade.php |
| Renderer root contract | Parent-driven item root varies by Columns variant |
| Tests / coverage | PublicColumnsRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest, PublicIconBadgeSupportTest |
CTA — cta
Content and behavior
| Field | Contract |
|---|---|
| Slug | cta |
| Label | CTA |
| Category | content |
| Translatable fields | title, subtitle, content via text translation rows |
| Shared/settings fields | Shared media_id; shared variant; settings.background_position; settings.background_overlay |
| Media/relationship fields | Optional background image via direct media_id; child button_link blocks for actions |
| Child/container behavior | Container; button_link children (legacy button also allowed), no fixed count |
| Current status | clear |
| Known gaps / notes | CTA is now a published promo contract with locale-owned copy, action buttons as ordinary button_link children, optional Media Library background images, and a root-owning public renderer. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/cta.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/cta.blade.php |
| Renderer root contract | Owns its promo <section> root |
| Tests / coverage | PublicHeroBlockRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Feature Grid — feature-grid
Content and behavior
| Field | Contract |
|---|---|
| Slug | feature-grid |
| Label | Feature Grid |
| Category | content |
| Translatable fields | title, subtitle, content via text translation rows; child feature-item title, badge label, and content |
| Shared/settings fields | Shared child structure, order, links, icon slugs, icon tones, and badge tones |
| Media/relationship fields | Child feature-item blocks with legacy-compatible column_item support |
| Child/container behavior | Container; allowed children are feature-item and column_item |
| Current status | transitional |
| Known gaps / notes | Feature Grid is now a published compatibility alias because it has real shipped admin and render paths, but it still intentionally delegates to the Columns cards contract. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/feature-grid.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/feature-grid.blade.php |
| Renderer root contract | Delegates to the Columns cards presentation and still uses the generic public wrapper |
| Tests / coverage | PublicHeroBlockRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest, PublicIconBadgeSupportTest |
Feature Item — feature-item
Content and behavior
| Field | Contract |
|---|---|
| Slug | feature-item |
| Label | Feature Item |
| Category | content |
| Translatable fields | title, eyebrow as badge label, content via text translation rows |
| Shared/settings fields | Shared canonical url, settings.icon_slug, settings.icon_tone, settings.icon_size, settings.badge_tone |
| Media/relationship fields | Parent feature-grid relationship |
| Child/container behavior | Not a container |
| Current status | transitional |
| Known gaps / notes | Feature Item is now a published supporting child contract for Feature Grid while still sharing the existing Column Item cards and content-icon rendering. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/feature-item.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/feature-item.blade.php |
| Renderer root contract | Delegates to the Column Item cards presentation |
| Tests / coverage | PublicHeroBlockRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest, PublicIconBadgeSupportTest |
Layout
Section — section
Content and behavior
| Field | Contract |
|---|---|
| Slug | section |
| Label | Section |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | Shared media_id; settings.layout_name; settings.spacing; settings.background_position; settings.background_overlay |
| Media/relationship fields | Optional background image via direct media_id; child blocks remain structural content |
| Child/container behavior | Container; no explicit child whitelist |
| Current status | clear |
| Known gaps / notes | Keeps Section layout-oriented: shared layout settings stay canonical, optional background media uses the standard Media Library contract, it owns the semantic <section> root, and it does not move user-facing copy into arbitrary settings. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/section.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/section.blade.php |
| Renderer root contract | Owns its root <section> |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PublicLayoutStructureTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Container — container
Content and behavior
| Field | Contract |
|---|---|
| Slug | container |
| Label | Container |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name; settings.width; settings.flow |
| Media/relationship fields | Child blocks only |
| Child/container behavior | Container; no explicit child whitelist |
| Current status | clear |
| Known gaps / notes | Container is layout-neutral when flow is unset or none; only explicit Flow: Stack adds vertical child flow. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/container.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/container.blade.php |
| Renderer root contract | Owns its root container <div> |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PublicLayoutStructureTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Stack — stack
Content and behavior
| Field | Contract |
|---|---|
| Slug | stack |
| Label | Stack |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name; settings.spacing |
| Media/relationship fields | Child blocks only |
| Child/container behavior | Container; no explicit child whitelist |
| Current status | clear |
| Known gaps / notes | Vertical child flow maps only to shipped wb-stack and wb-stack-* classes. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/stack.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/stack.blade.php |
| Renderer root contract | Owns its root stack <div> |
| Tests / coverage | StackSplitCatalogTest, StackSplitContractTest |
Split — split
Content and behavior
| Field | Contract |
|---|---|
| Slug | split |
| Label | Split |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name; settings.gap; settings.items_alignment; settings.width |
| Media/relationship fields | Exactly two direct child blocks |
| Child/container behavior | Container; no explicit child-type whitelist, but direct child count is limited to two |
| Current status | clear |
| Known gaps / notes | The first direct child grows and the second remains content-sized. A Stack may be nested on either side. The pinned UI has no responsive split-to-stack modifier yet. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/split.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/split.blade.php |
| Renderer root contract | Owns its root split <div> |
| Tests / coverage | StackSplitCatalogTest, StackSplitContractTest |
Cluster — cluster
Content and behavior
| Field | Contract |
|---|---|
| Slug | cluster |
| Label | Cluster |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name; settings.gap; settings.alignment; settings.items_alignment; settings.wrap; settings.width |
| Media/relationship fields | Child blocks only |
| Child/container behavior | Container; no explicit child whitelist |
| Current status | clear |
| Known gaps / notes | Shared layout settings stay explicit and renderer-owned, including the existing navbar-compatible full-width, between, center, nowrap composition path without adding navbar-specific logic. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/cluster.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/cluster.blade.php |
| Renderer root contract | Owns its root cluster <div> |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PublicLayoutStructureTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Grid — grid
Content and behavior
| Field | Contract |
|---|---|
| Slug | grid |
| Label | Grid |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name; settings.columns; settings.gap; settings.alternate_media_text_sections; settings.alternate_start |
| Media/relationship fields | Child blocks only |
| Child/container behavior | Container; no explicit child whitelist |
| Current status | clear |
| Known gaps / notes | Shared grid settings stay canonical. Alternating media/text grids keep the shipped wb-grid-* wrapper and reorder direct media/text child pairs, including Slider plus text Section structures, by detected visual content. Sibling alternating Grids under the same parent share one sequence even when other blocks appear between them, with the first alternating Grid controlling the start direction. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/grid.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/grid.blade.php |
| Renderer root contract | Owns its root grid <div> |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PublicLayoutStructureTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest |
Pattern
Content Header — content_header
Content and behavior
| Field | Contract |
|---|---|
| Slug | content_header |
| Label | Content Header |
| Category | pattern |
| Translatable fields | title, eyebrow as badge label, subtitle, meta via text translation rows |
| Shared/settings fields | Shared media_id; settings.alignment, settings.icon_slug, settings.icon_tone, settings.icon_size, settings.badge_tone, settings.background_position, settings.background_overlay |
| Media/relationship fields | Optional background image via direct media_id; meta is stored as structured list content |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Content Header is a content-shell pattern block. It keeps locale-owned title, badge label, intro, and meta copy, always renders its title as H1, ignores any legacy saved heading-level values at render time, and keeps alignment, icon slug, icon tone, icon size, badge tone, and optional background media shared. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/content_header.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/content_header.blade.php |
| Renderer root contract | Owns its root <header> |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest, BlockTypePhaseThreeContractsTest, BlockTranslationIntegrityTest, PublicIconBadgeSupportTest |
Alert — alert
Content and behavior
| Field | Contract |
|---|---|
| Slug | alert |
| Label | Alert |
| Category | pattern |
| Translatable fields | title, content via text translation rows |
| Shared/settings fields | settings.variant |
| Media/relationship fields | None |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Shared variant and translated copy line up cleanly. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/alert.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/alert.blade.php |
| Renderer root contract | Owns its alert root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest |
Navigation
Link List — link-list
Content and behavior
| Field | Contract |
|---|---|
| Slug | link-list |
| Label | Link List |
| Category | navigation |
| Translatable fields | title, subtitle, content via text translation rows |
| Shared/settings fields | Optional settings.row_layout (index/stacked) and settings.list_frame (joined/cards) |
| Media/relationship fields | Child link-list-item blocks |
| Child/container behavior | Container; only link-list-item children |
| Current status | clear |
| Known gaps / notes | renders the existing translated intro copy above the public link list without changing child item behavior. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/link-list.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/link-list.blade.php |
| Renderer root contract | Owns its .wb-link-list root when child rows exist, adding wb-link-list--stacked and wb-link-list--cards for the selected styles |
| Tests / coverage | LinkListBlockTest, PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest, BlockTypePhaseThreeContractsTest |
Link List Item — link-list-item
Content and behavior
| Field | Contract |
|---|---|
| Slug | link-list-item |
| Label | Link List Item |
| Category | navigation |
| Translatable fields | Required title, optional eyebrow as badge label, optional subtitle, optional content via text translation rows |
| Shared/settings fields | Required shared url, optional shared media_id thumbnail, optional settings.icon_slug, settings.icon_tone, settings.icon_size, settings.badge_tone |
| Media/relationship fields | Optional thumbnail media through the direct block media_id relation; parent link-list relationship |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Shared URL with translated row copy is consistent. Optional icons use active content catalog slugs and render only through safe icon, tone, and size classes. An optional image thumbnail renders as wb-link-list-thumb in the row's single leading column and wins over the icon when both are set. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/link-list-item.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/link-list-item.blade.php |
| Renderer root contract | Owns its row link root, adds wb-link-list-item--media when a thumbnail or icon renders, and omits the description element when content is blank |
| Tests / coverage | LinkListBlockTest, LinkListThumbnailTest, PageBuilderExperienceTest, PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest, PublicIconBadgeSupportTest |
TOC — toc
Content and behavior
| Field | Contract |
|---|---|
| Slug | toc |
| Label | TOC |
| Category | navigation |
| Translatable fields | None |
| Shared/settings fields | Canonical title only |
| Media/relationship fields | Same-slot published header blocks with valid anchors |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | clear |
| Known gaps / notes | TOC is a system block type. It scans only the slot it is placed in — not the whole page — walked in document order, and no longer treats arbitrary child blocks as public TOC content. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/toc.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/toc.blade.php |
| Renderer root contract | Owns its generated TOC wrapper when headings exist |
| Tests / coverage | PublicRichContentTest, PageBuilderExperienceTest, TocHeadingScanTest |
Breadcrumb — breadcrumb
Content and behavior
| Field | Contract |
|---|---|
| Slug | breadcrumb |
| Label | Breadcrumb |
| Category | navigation |
| Translatable fields | None |
| Shared/settings fields | Shared settings.home_label; settings.include_current |
| Media/relationship fields | Current page, site, and locale breadcrumb context |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Preserves the exposed breadcrumb settings through the shared BlockRequest path so form behavior now matches persistence and rendering. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/breadcrumb.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/breadcrumb.blade.php |
| Renderer root contract | Owns its <nav.wb-breadcrumb> root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest |
Header Actions — header-actions
Content and behavior
| Field | Contract |
|---|---|
| Slug | header-actions |
| Label | Header Actions |
| Category | navigation |
| Translatable fields | None |
| Shared/settings fields | Shared settings.show_mode_toggle; settings.show_accent_toggle; settings.show_search; settings.show_language_switcher |
| Media/relationship fields | Search route plus client-side UI hooks |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | System utility block; no translation ownership by design. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/header-actions.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/header-actions.blade.php |
| Renderer root contract | Owns its inner action cluster only |
| Tests / coverage | PublicEditorialBlocksRenderingTest |
Navbar — sticky-navbar
Content and behavior
| Field | Contract |
|---|---|
| Slug | sticky-navbar |
| Label | Navbar |
| Category | navigation |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name; settings.sticky_mode |
| Media/relationship fields | Nested navbar child blocks |
| Child/container behavior | Container; allowed children are container, cluster, header, plain_text, rich-text, button_link, navbar-brand, navbar-navigation, header-actions, search-form |
| Current status | clear |
| Known gaps / notes | Aligns the persisted sticky-navbar slug with Block::ownsPublicRoot() so Navbar no longer receives an extra generic public block wrapper. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/sticky-navbar.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/sticky-navbar.blade.php |
| Renderer root contract | Public renderer owns the outer <nav.wb-navbar> root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PublicLayoutStructureTest, BlockTypePhaseThreeContractsTest |
Navbar Brand — navbar-brand
Content and behavior
| Field | Contract |
|---|---|
| Slug | navbar-brand |
| Label | Navbar Brand |
| Category | navigation |
| Translatable fields | title, subtitle via text translation rows |
| Shared/settings fields | Shared settings.url; settings.target; settings.aria_label |
| Media/relationship fields | Shared logo media via media_id; site home URL fallback |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Aligns admin save behavior with the shipped renderer contract: explicit saved URL wins, otherwise the current site home path is used when available, then / as the final safe fallback. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/navbar-brand.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/navbar-brand.blade.php |
| Renderer root contract | Owns the inner brand link only |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest, BlockTranslationIntegrityTest, BlockTypePhaseThreeContractsTest |
Navbar Navigation — navbar-navigation
Content and behavior
| Field | Contract |
|---|---|
| Slug | navbar-navigation |
| Label | Navbar Navigation |
| Category | navigation |
| Translatable fields | None |
| Shared/settings fields | Canonical title as shared ARIA label; settings.menu_key; settings.active_indicator; settings.active_matching |
| Media/relationship fields | Shared NavigationItem menu tree |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Shared menu binding, ARIA label, active indicator, and active matching are current product-owned behavior. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/navbar-navigation.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/navbar-navigation.blade.php |
| Renderer root contract | Owns the inner navigation wrapper only and maps active indicator settings to WebBlocks UI navbar active classes |
| Tests / coverage | PublicEditorialBlocksRenderingTest |
Sidebar Brand — sidebar-brand
Content and behavior
| Field | Contract |
|---|---|
| Slug | sidebar-brand |
| Label | Sidebar Brand |
| Category | navigation |
| Translatable fields | title, subtitle via text translation rows |
| Shared/settings fields | Shared settings.url; settings.target; settings.aria_label |
| Media/relationship fields | Shared logo media via media_id; site home URL fallback |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Sidebar Brand uses the same logo-only accessible-name fallback order as Navbar Brand and the same conservative shared URL fallback contract. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/sidebar-brand.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/sidebar-brand.blade.php |
| Renderer root contract | Owns the inner brand link only |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest, BlockTranslationIntegrityTest, BlockTypePhaseThreeContractsTest |
Sidebar Navigation — sidebar-navigation
Content and behavior
| Field | Contract |
|---|---|
| Slug | sidebar-navigation |
| Label | Sidebar Navigation |
| Category | navigation |
| Translatable fields | title via text translation rows |
| Shared/settings fields | Shared settings.menu_key; settings.layout_name; settings.show_icons; settings.active_matching |
| Media/relationship fields | Either CMS NavigationItem tree or manual child blocks |
| Child/container behavior | Container; only sidebar-nav-item and sidebar-nav-group children |
| Current status | clear |
| Known gaps / notes | Shared menu-mode settings and manual child mode are both explicit. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/sidebar-navigation.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/sidebar-navigation.blade.php |
| Renderer root contract | Owns its <nav.wb-sidebar-nav> root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest |
Sidebar Nav Item — sidebar-nav-item
Content and behavior
| Field | Contract |
|---|---|
| Slug | sidebar-nav-item |
| Label | Sidebar Nav Item |
| Category | navigation |
| Translatable fields | title via text translation rows |
| Shared/settings fields | Shared settings.url; settings.target; settings.icon; settings.active_mode; settings.manual_active |
| Media/relationship fields | Shared icon catalog slug; parent sidebar relationship |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Shared link behavior plus translated label line up well. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/sidebar-nav-item.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/sidebar-nav-item.blade.php |
| Renderer root contract | Owns its sidebar link root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest |
Sidebar Nav Group — sidebar-nav-group
Content and behavior
| Field | Contract |
|---|---|
| Slug | sidebar-nav-group |
| Label | Sidebar Nav Group |
| Category | navigation |
| Translatable fields | title via text translation rows |
| Shared/settings fields | Shared settings.icon; settings.initially_open; settings.layout_name |
| Media/relationship fields | Child sidebar-nav-item blocks; icon catalog slug |
| Child/container behavior | Container; only sidebar-nav-item children |
| Current status | clear |
| Known gaps / notes | Keeps the shipped WebBlocks UI nav-group wrapper contract while nested manual child links now reuse the same sidebar item semantics for href, target, icon, and active-state output. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/sidebar-nav-group.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/sidebar-nav-group.blade.php |
| Renderer root contract | Owns its .wb-nav-group root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PageBuilderExperienceTest, BlockTranslationIntegrityTest, BlockTypePhaseThreeContractsTest |
Search Form — search-form
Content and behavior
| Field | Contract |
|---|---|
| Slug | search-form |
| Label | Search Form |
| Category | navigation |
| Translatable fields | title, subtitle, content via text translation rows |
| Shared/settings fields | Shared variant; settings.show_button |
| Media/relationship fields | Search route, site, and locale context |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Shared button-display settings and translated labels are explicit. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/search-form.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/search-form.blade.php |
| Renderer root contract | Owns its <form role="search"> root |
| Tests / coverage | SearchFormTest, PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest |
Sidebar Footer — sidebar-footer
Content and behavior
| Field | Contract |
|---|---|
| Slug | sidebar-footer |
| Label | Sidebar Footer |
| Category | navigation |
| Translatable fields | title, subtitle, content via text translation rows |
| Shared/settings fields | Shared settings.variant |
| Media/relationship fields | None |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | Shared variant with translated copy is straightforward. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/sidebar-footer.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/sidebar-footer.blade.php |
| Renderer root contract | Owns its inner footer block root |
| Tests / coverage | PublicEditorialBlocksRenderingTest, BlockTranslationIntegrityTest |
Forms
Contact Form — contact_form
Content and behavior
| Field | Contract |
|---|---|
| Slug | contact_form |
| Label | Contact Form |
| Category | form |
| Translatable fields | title, content, submit_label, success_message via contact form translation rows |
| Shared/settings fields | Shared settings.recipient_email; settings.send_email_notification; settings.store_submissions |
| Media/relationship fields | contact_messages submissions relate back to the block and page |
| Child/container behavior | Not a container |
| Current status | clear |
| Known gaps / notes | The renderer emits a CMS-owned hidden generated anti-spam check field that is not normal visitor input and should not be created manually. Filled check-field submissions return generic success without storage or notification. Legitimate messages are stored before notification, scored spam is retained for admin review, recipient fallback is block, site, CONTACT_RECIPIENT_EMAIL, then MAIL_FROM_ADDRESS, and Contact pages should not use Trusted HTML/raw forms/mailto: as substitutes. Internal Content API content plans preserve submit_label and success_message, and German locale rendering uses German default form labels when custom copy is absent. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/contact_form.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/contact_form.blade.php |
| Renderer root contract | Owns its section.wb-card root and emits a native CSRF-protected form posting to /contact-messages |
| Tests / coverage | ContactFormModuleTest, InternalContentApiTest, ContactMailDiagnoseCommandTest |
Advanced
HTML (Trusted) — html
Content and behavior
| Field | Contract |
|---|---|
| Slug | html |
| Label | HTML (Trusted) |
| Category | advanced |
| Translatable fields | None |
| Shared/settings fields | Canonical trusted HTML content |
| Media/relationship fields | Public overlay and body-end registries can receive extracted fragments |
| Child/container behavior | Not a container; historical child rows are preserved but new child placement is not supported |
| Current status | mostly clear |
| Known gaps / notes | Treats Trusted HTML as a public child-container wrapper. Trusted markup can still affect shared overlay or body-end output beyond the visible root. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/html.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/html.blade.php |
| Renderer root contract | Owns a wrapper <div> around trusted markup and can also emit out-of-band overlay or body-end content |
| Tests / coverage | PublicEditorialBlocksRenderingTest, PublicRichContentTest, PageBuilderExperienceTest |
Additional published contracts
Slider — slider
Content and behavior
| Field | Contract |
|---|---|
| Slug | slider |
| Label | Slider |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | settings.layout_name; settings.height; settings.min_height; settings.aspect_ratio; settings.transition; settings.interval_ms; settings.autoplay; settings.pause_on_hover; settings.show_arrows; settings.show_dots; settings.loop; settings.swipe; settings.keyboard; settings.overlay; settings.content_position; settings.content_width; settings.text_color; settings.background_fit |
| Media/relationship fields | Child slide blocks are the primary relationship. Slider itself does not support direct media ownership. |
| Child/container behavior | Container-capable. Allowed child type is slide. |
| Current status | clear |
| Known gaps / notes | None |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/slider.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/slider.blade.php |
| Renderer root contract | Owns its public <section data-wb-slider> root, internal track, controls, and dot navigation. |
| Tests / coverage | SliderContentPositionTest |
Slide — slide
Content and behavior
| Field | Contract |
|---|---|
| Slug | slide |
| Label | Slide |
| Category | layout |
| Translatable fields | None |
| Shared/settings fields | media_id; settings.layout_name; settings.aria_label; settings.background_position; settings.background_overlay; settings.content_position; settings.content_width; settings.text_color; settings.background_fit; mobile_media_id |
| Media/relationship fields | Optional slide background media is owned through the direct block media_id relation. Child blocks are the visible content relationship.; mobile_media_id assigns an image Media Library record through block_media.mobile_image. Send null to clear the override. |
| Child/container behavior | Container-capable. Child types are not explicitly restricted in the current helper. |
| Current status | clear |
| Known gaps / notes | None |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/slide.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/slide.blade.php |
| Renderer root contract | Owns its public <article class="wb-slide"> root and delegates visible copy/UI to nested child blocks. At viewport widths up to 768px, a public mobile image overrides the default visual through picture/source or the background image; absent or unavailable overrides use the default image. |
| Tests / coverage | SliderContentPositionTest |
Navigation Auto — navigation-auto
Content and behavior
| Field | Contract |
|---|---|
| Slug | navigation-auto |
| Label | Navigation Auto |
| Category | navigation |
| Translatable fields | None |
| Shared/settings fields | settings.menu_key |
| Media/relationship fields | Current site and locale context resolve the rendered menu tree at render time. |
| Child/container behavior | Not a container. Menu items come from Navigation records, not from child blocks. |
| Current status | clear |
| Known gaps / notes | None |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/navigation-auto.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/navigation-auto.blade.php |
| Renderer root contract | Owns its public <nav class="wb-stack wb-gap-2" data-wb-menu-key="..."> root and renders the visible menu tree as nested <ul> lists of wb-link anchors. |
| Tests / coverage | ManagedCtaApiTest |
Rating — rating
Content and behavior
| Field | Contract |
|---|---|
| Slug | rating |
| Label | Rating |
| Category | system |
| Translatable fields | title; subtitle |
| Shared/settings fields | settings.scale; settings.allow_change; settings.show_summary; settings.data_scope |
| Media/relationship fields | Current page, site, block, visitor hash, and source URL are recorded on rating rows. |
| Child/container behavior | Not a container in the current contract. |
| Current status | clear |
| Known gaps / notes | None |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/rating.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/rating.blade.php |
| Renderer root contract | Owns its public section.wb-card.wb-rating root, renders optional localized title/subtitle copy, a read-only wb-rating-stars average, and a no-JS wb-rating-input star form that posts each submit to /content-ratings. |
| Tests / coverage | EngagementManagementTest |
Comments — comments
Content and behavior
| Field | Contract |
|---|---|
| Slug | comments |
| Label | Comments |
| Category | system |
| Translatable fields | None |
| Shared/settings fields | settings.form_enabled; settings.show_approved; settings.show_author_name; settings.sort_order; settings.data_scope |
| Media/relationship fields | Current page, site, block, visitor hash, and source URL are recorded on comment rows. |
| Child/container behavior | Not a container in the current contract. |
| Current status | clear |
| Known gaps / notes | None |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/comments.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/comments.blade.php |
| Renderer root contract | Owns its public section.wb-card root, renders approved comments, and emits a native CSRF-protected comment form that posts to /comment-entries. |
| Tests / coverage | EngagementManagementTest |
Page List — page-list
Content and behavior
| Field | Contract |
|---|---|
| Slug | page-list |
| Label | Page List |
| Category | content |
| Translatable fields | None |
| Shared/settings fields | settings.scope; settings.page_type; settings.path_prefix; settings.sort; settings.limit; settings.layout; settings.columns; settings.show_thumbnail; settings.show_description; settings.exclude_current; settings.clickable_card |
| Media/relationship fields | No block media. Thumbnails resolve from each listed page translation at render time. |
| Child/container behavior | Not a container. Rows come from a page query, not from child blocks. |
| Current status | clear |
| Known gaps / notes | Card descriptions and thumbnails reuse the SEO description and Open Graph image, which are authored for search and social rather than for a card. Dedicated list_excerpt and list_image page settings are planned. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/page-list.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/page-list.blade.php |
| Renderer root contract | Owns its public root: a wb-grid of wb-card articles or whole-card anchors in the cards layout, or a wb-link-list of wb-link-list-item anchors in the links layout. Renders nothing when the query returns no pages. |
| Tests / coverage | PageListBlockRenderTest |
Application — application
Content and behavior
| Field | Contract |
|---|---|
| Slug | application |
| Label | Application |
| Category | content |
| Translatable fields | None |
| Shared/settings fields | settings.application_handle; settings.application_settings; settings.width; settings.loading; settings.aspect_ratio; settings.min_height; settings.show_loading_state; settings.show_failure_state |
| Media/relationship fields | No direct CMS Media relationship. Executable application assets are registry-owned files, not editorial media. |
| Child/container behavior | Not a container. The application runtime owns its internal DOM. |
| Current status | clear |
| Known gaps / notes | Application code and registry management remain separate from editorial placement; the block selects an enabled definition and validates its settings. |
Sources and tests
| Source | Contract |
|---|---|
| Admin form source | resources/views/admin/blocks/types/application.blade.php |
| Public renderer source | resources/views/pages/partials/blocks/application.blade.php |
| Renderer root contract | Owns a .wb-application root. Inline definitions receive a safe mount element; iframe definitions receive a CMS-owned iframe wrapper. Missing or invalid definitions never break the surrounding page. |
| Tests / coverage | EmbeddedApplicationsTest |
Validation And Persistence Overview
Published block types do not each have their own dedicated request class today.
WebBlocks\Cms\Http\Requests\Admin\BlockRequestis the shared edit request path- slug-specific branches inside that request normalize fields into current canonical storage
WebBlocks\Cms\Support\Blocks\BlockPayloadWriterpersists the normalized block payloadWebBlocks\Cms\Support\Blocks\BlockTranslationWritermoves locale-owned fields into translation rows for registered familiesWebBlocks\Cms\Support\Blocks\BlockTranslationResolverresolves translated or fallback values back onto a renderable block instance
Compatibility Behavior
Legacy Gallery and Table settings can remain readable when canonical translated content or ordered media is absent. Historical field fallbacks do not make those fields the preferred authoring format. Use the fields documented in the current block matrix.
Draft catalog rows and renderer-only compatibility handles are not published core authoring contracts. Discovery and the read-only contract modal may report that no shipped contract is available for such a row.
Media And Editor Additions Through 1.94.2
From CMS 1.91.0, Slide, Image, Hero, Section, Card, CTA, Content Header, and Link List Item support optional mobile_media_id in plans and existing-block updates. The relationship uses block_media.role = mobile_image, is shared across locales, and replaces the default image up to 768 px with fallback when unavailable. It does not introduce separate mobile alt text or captions. See Media Image Variants.
From 1.91.1, Slide and Slider offer nine content positions: center, center-left, center-right, top-left, top-center, top-right, bottom-left, bottom-center, and bottom-right. These values are shared by the admin, API, and renderer.
From 1.93.0, the Rich Text editor has per-field undo/redo, selection restoration, focus mode, and word count. Pasted headings and table-cell text remain as safe body paragraphs; semantic headings, tables, and media belong in dedicated blocks. This does not add a new stored Rich Text variant or permit executable markup. See Rich Text.