Config Structure
Configs use a flat structure with promoted common keys at the field level. Type-specific settings go in args.
Full shape
Section titled “Full shape”return [ 'title' => 'Page Title', // Optional. Shown in the header. 'subtitle' => 'A short description', // Optional. Shown below the title. 'header_actions' => [ /* … */ ], // Optional. Buttons beside Save / Reset. 'tabs' => [ [ 'id' => 'tab_id', 'title' => 'Tab Label', 'sections' => [ [ 'id' => 'section_id', 'title' => 'Section Title', 'description' => 'Helpful description text.', 'fields' => [ [ 'id' => 'field_id', 'type' => 'text', 'label' => 'Field Label', 'description' => 'Help text below the field.', 'default' => '', 'required' => false, 'validation' => 'min:3|max:100', 'columns' => 6, 'conditions' => [ 'all' => [ ['field' => 'other_field', 'operator' => 'truthy'], ], ], 'args' => [ 'placeholder' => 'Enter text', ], ], ], ], ], ], ],];Promoted keys (field level)
Section titled “Promoted keys (field level)”These are common to every field type:
| Key | Type | Default | Description |
|---|---|---|---|
id | string | Required | Unique field identifier within the page. |
type | string | 'text' | One of the registered field types. |
label | string | '' | Display label. Pass '' explicitly to render no label (the field will still align with labelled siblings). |
description | string | '' | Help text below the field. Supports {field_id} tokens that interpolate live form values, e.g. 'URL: https://example.com/{slug}'. |
default | mixed | null | Default value, used when no saved value exists. |
required | bool | false | Whether the field must have a value at save time. |
validation | string | '' | Pipe-separated validation rules — e.g. 'min:3|max:100'. |
columns | int (1..12) | 12 | Grid width. 12 = full row; 6 = half; 4 = third; etc. |
new_row | bool | false | Start the field at column 1 of a new grid row, even when the current row has room. No effect on small screens, where fields stack. |
conditions | array | null | Conditional visibility rules evaluated against current values. |
access | string|array | null | Role / capability gating. Strictly opt-in. |
args | array | [] | Type-specific settings — placeholder, rows, options, multiple, mode, etc. |
new_row also applies to repeater subfields. For example, the third field below starts a new row instead of filling the space left by the first two:
['id' => 'a', 'type' => 'text', 'columns' => 4],['id' => 'b', 'type' => 'text', 'columns' => 4],['id' => 'c', 'type' => 'text', 'columns' => 4, 'new_row' => true],Type-specific settings (args)
Section titled “Type-specific settings (args)”Everything that varies between field types lives under args. Some examples:
// Text field['type' => 'text', 'args' => ['placeholder' => 'Optional placeholder', 'copyable' => true]]
// Textarea['type' => 'textarea', 'args' => ['rows' => 5, 'placeholder' => '…']]
// Select['type' => 'select', 'args' => ['options' => ['draft' => 'Draft', 'published' => 'Published']]]
// Repeater['type' => 'repeater', 'args' => ['subfields' => [ /* … */ ], 'add_label' => 'Add row']]See each field type’s page in Field Types for the full set.
Flexible structure — tabs and sections are optional
Section titled “Flexible structure — tabs and sections are optional”The full nested shape (tabs → sections → fields) is the maximum. You can collapse it when you don’t need every level.
No tabs, no sections — just fields
Section titled “No tabs, no sections — just fields”return [ 'fields' => [ ['id' => 'name', 'type' => 'text', 'label' => 'Name'], ['id' => 'email', 'type' => 'email', 'label' => 'Email'], ],];No tabs are rendered. Fields render in a single card.
Sections, no tabs
Section titled “Sections, no tabs”return [ 'sections' => [ [ 'id' => 'main', 'title' => 'Main Settings', 'fields' => [ ['id' => 'name', 'type' => 'text', 'label' => 'Name'], ], ], ],];The tab bar is suppressed; section cards stack vertically.
Schema & validation
Section titled “Schema & validation”A JSON Schema for the config structure ships at schema/config.schema.json. It covers all three input formats, the common field keys, the conditions DSL, the access shapes, and per-type args constraints (e.g. choice fields require args.options, repeaters require args.subfields). Point your editor at it to get autocomplete and inline validation while authoring JSON configs.
In VS Code, map it in .vscode/settings.json:
{ "json.schemas": [ { "fileMatch": ["**/config/*.json", "**/wireframe.config.json"], "url": "./vendor/wp-wireframe/schema/config.schema.json" } ]}Or reference it inline from a JSON config with "$schema": "./vendor/wp-wireframe/schema/config.schema.json".
Runtime check
Section titled “Runtime check”When WP_DEBUG is enabled, every booted config is linted and structural problems — unknown field types, choice fields missing options, duplicate IDs, out-of-range columns, and so on — are written to the PHP error log. Users with manage_options also see them in a warning notice on other admin screens; Wireframe pages themselves suppress admin notices, so check the log there.
It’s a focused sanity check against the live field registry; the JSON Schema remains the full contract. Nothing runs when WP_DEBUG is off, so production is unaffected.