Breeze Theme Editor - Theme Developer Guide
Version: 1.0
Last Updated: 2026-01-09
This guide explains how to add Theme Editor support to your Breeze theme.
Table of Contents
- Quick Start
- File Structure
- Configuration Format
- Field Types Reference
- Field Properties
- Validation Rules
- Theme Inheritance
- Best Practices
- Examples
- Troubleshooting
Quick Start
Step 1: Create Configuration File
Create etc/theme_editor/settings.json in your theme root:
your-theme/
└── etc/
└── theme_editor/
└── settings.json
Step 2: Basic Configuration
{
"version": "1.0",
"sections": [
{
"id": "colors",
"name": "Colors",
"description": "Customize theme colors",
"icon": "palette",
"order": 1,
"settings": [
{
"id": "primary_color",
"label": "Primary Color",
"type": "color",
"default": "#1979c3",
"css_var": "--primary-color",
"description": "Main brand color"
}
]
}
]
}
Step 3: Test
- Clear Magento cache:
bin/magento cache:clean - Open frontend with
?breeze_theme_editor=1 - Your settings should appear in the panel
File Structure
your-theme/
├── etc/
│ └── theme_editor/
│ ├── settings.json # Main configuration
│ └── presets/ # Optional presets
│ ├── default.json
│ └── dark-mode.json
└── web/
└── css/
└── source/
└── _theme-variables.less # Your CSS variables
Configuration Format
Root Structure
{
"version": "1.0", // Required: Config version
"extends": "Parent/Theme", // Optional: Inherit from parent theme
"sections": [] // Required: Array of sections
}
Section Structure
{
"id": "unique_section_id", // Required: Unique identifier
"name": "Section Name", // Required: Display name
"description": "Description", // Optional: Section description
"icon": "palette", // Optional: Material icon name
"order": 1, // Optional: Display order (default: 999)
"settings": [] // Required: Array of fields
}
Material Icons
Common icons: palette, format_paint, text_fields, view_column, image, settings, code, link
Full list: https://fonts.google.com/icons
Field Types Reference
1. COLOR - Color Picker
Purpose: Select colors with live preview
{
"id": "primary_color",
"label": "Primary Color",
"type": "color",
"default": "#1979c3",
"css_var": "--primary-color",
"description": "Main brand color",
"help_text": "Used for buttons, links, and accents"
}
Features:
- Visual color picker
- HEX input (#RRGGBB or #RGB)
- RGB format support
- Automatic RGB conversion for CSS variables
CSS Output:
:root {
--primary-color: 25, 121, 195; /* Converted to RGB */
}
Notes:
- HEX values are converted to RGB format (Breeze standard)
- Original HEX value added as CSS comment
- Supports both
#FF0000and#F00formats
2. TEXT - Single Line Input
Purpose: Short text values (widths, margins, custom values)
{
"id": "container_width",
"label": "Container Width",
"type": "text",
"default": "1280px",
"css_var": "--container-width",
"placeholder": "e.g. 1280px",
"description": "Maximum content width"
}
Properties:
placeholder- Placeholder textpattern- Regex validation patternvalidationMessage- Custom error messageminLength- Minimum charactersmaxLength- Maximum characters
Example with Validation:
{
"id": "custom_margin",
"type": "text",
"pattern": "^\\d+(px|rem|em|%)$",
"validationMessage": "Must be a valid CSS unit (e.g. 10px, 1rem)",
"placeholder": "10px"
}
3. TEXTAREA - Multi-line Input
Purpose: Custom CSS, scripts, large text blocks
{
"id": "custom_css",
"label": "Custom CSS",
"type": "textarea",
"default": "/* Your custom styles */",
"rows": 10,
"description": "Additional CSS code",
"help_text": "Will be injected into <style> tag"
}
Properties:
rows- Number of visible rows (default: 5)placeholder- Placeholder textmaxLength- Maximum characters
Security:
- CSS comments
/* */are automatically escaped to/ * * / - Prevents breaking CSS output
4. NUMBER - Numeric Input
Purpose: Precise numeric values
{
"id": "grid_columns",
"label": "Grid Columns",
"type": "number",
"default": "4",
"min": "1",
"max": "12",
"step": "1",
"css_var": "--grid-columns",
"description": "Number of product columns"
}
Properties:
min- Minimum valuemax- Maximum valuestep- Increment step (default: 1)
Validation:
- Automatic min/max validation
- Must be numeric
5. RANGE - Slider Input
Purpose: Visual slider for numeric ranges
{
"id": "text_opacity",
"label": "Text Opacity",
"type": "range",
"default": "1",
"min": "0",
"max": "1",
"step": "0.1",
"unit": "",
"css_var": "--text-opacity",
"description": "Transparency level"
}
Properties:
min- Minimum value (required)max- Maximum value (required)step- Slider step (required)unit- Display unit (e.g. “px”, “ms”, “%”)
UI:
- Visual slider with live value display
- Shows: “0.8” or “800ms” (with unit)
6. SELECT - Dropdown
Purpose: Choose from predefined options
{
"id": "layout_type",
"label": "Layout Type",
"type": "select",
"default": "boxed",
"css_var": "--layout-type",
"options": [
{"value": "boxed", "label": "Boxed Layout"},
{"value": "full", "label": "Full Width"},
{"value": "fluid", "label": "Fluid"}
],
"description": "Page layout style"
}
Properties:
options- Array of {value, label} objects (required)
Notes:
- First option is NOT automatically selected
- Must specify
defaultvalue
7. TOGGLE - On/Off Switch
Purpose: Boolean settings
{
"id": "sticky_header",
"label": "Sticky Header",
"type": "toggle",
"default": true,
"css_var": "--header-sticky",
"description": "Fix header on scroll"
}
CSS Output:
:root {
--header-sticky: 1; /* true → 1, false → 0 */
}
Notes:
true→ “1”false→ “0”- Can be used in CSS
calc()oropacity
8. CODE - Code Editor
Purpose: Code snippets with syntax highlighting (future: Monaco editor)
{
"id": "custom_js",
"label": "Custom JavaScript",
"type": "code",
"default": "// Your code here",
"rows": 15,
"description": "Custom JavaScript code",
"help_text": "Use with caution"
}
Properties:
rows- Editor height (default: 10)language- Future: syntax highlighting language
Current Limitations:
- Currently renders as textarea (Monaco integration planned)
- CSS comments are escaped for security
9. FONT_PICKER - Font Selector
Purpose: Choose fonts with preview
{
"id": "body_font",
"label": "Body Font",
"type": "font_picker",
"default": "Open Sans",
"css_var": "--font-family-base",
"options": [
{"value": "Open Sans", "label": "Open Sans"},
{"value": "Roboto", "label": "Roboto"},
{"value": "Georgia", "label": "Georgia"},
{"value": "Courier New", "label": "Courier New"}
],
"description": "Main text font"
}
Features:
- Font preview in dropdown
- Automatic fallback fonts:
- Sans-serif fonts → adds
, sans-serif - Serif fonts → adds
, serif(Georgia, Times, Garamond, etc.) - Monospace fonts → adds
, monospace(Courier, Monaco, Consolas, etc.)
- Sans-serif fonts → adds
CSS Output:
:root {
--font-family-base: "Open Sans", sans-serif;
--font-heading: "Georgia", serif;
--font-code: "Courier New", monospace;
}
Supported Serif Fonts: Georgia, Times New Roman, Times, Garamond, Palatino, Baskerville, Didot, Bodoni, Cambria, serif
Supported Monospace Fonts: Courier New, Courier, Monaco, Consolas, Lucida Console, monospace
10. COLOR_SCHEME - Scheme Selector
Purpose: Predefined color schemes (Light/Dark/Auto)
{
"id": "color_mode",
"label": "Color Scheme",
"type": "color_scheme",
"default": "auto",
"css_var": "--color-scheme",
"schemes": [
{
"value": "light",
"label": "Light Mode",
"colors": ["#ffffff", "#f5f5f5", "#e0e0e0"],
"description": "Light color scheme"
},
{
"value": "dark",
"label": "Dark Mode",
"colors": ["#1a1a1a", "#2d2d2d", "#404040"],
"description": "Dark color scheme"
},
{
"value": "auto",
"label": "Auto",
"colors": ["#ffffff", "#1a1a1a"],
"description": "Follow system preference"
}
]
}
Properties:
schemes- Array of scheme definitions- Each scheme:
value- Scheme IDlabel- Display namecolors- Preview colors (3 swatches)description- Tooltip text
UI:
- Visual cards with color preview
- Radio button selection
11. SOCIAL_LINKS - Social Media URLs
Purpose: Manage social media profile links
{
"id": "social_profiles",
"label": "Social Media Links",
"type": "social_links",
"default": "{}",
"platforms": [
{"code": "facebook", "label": "Facebook", "icon": "f"},
{"code": "twitter", "label": "Twitter/X", "icon": "𝕏"},
{"code": "instagram", "label": "Instagram", "icon": "📷"},
{"code": "linkedin", "label": "LinkedIn", "icon": "in"},
{"code": "youtube", "label": "YouTube", "icon": "▶"}
],
"description": "Your social media profiles"
}
Value Format (JSON):
{
"facebook": "https://facebook.com/yourpage",
"twitter": "https://twitter.com/yourhandle",
"instagram": "https://instagram.com/yourprofile"
}
Features:
- Multiple platform inputs in one field
- Each platform has icon and placeholder
- Stored as JSON object
- Empty fields are omitted
Default Platforms: Facebook, Twitter, Instagram, LinkedIn, YouTube, Pinterest, TikTok
12. ICON_SET_PICKER - Icon Library Selector
Purpose: Choose icon library for theme
{
"id": "icon_library",
"label": "Icon Set",
"type": "icon_set_picker",
"default": "material",
"options": [
{
"value": "material",
"label": "Material Icons",
"preview": "★",
"description": "Google Material Design icons"
},
{
"value": "fontawesome",
"label": "Font Awesome",
"preview": "",
"description": "Font Awesome 6"
},
{
"value": "feather",
"label": "Feather Icons",
"preview": "⚡",
"description": "Simple and clean"
}
]
}
Properties:
options- Array of icon set definitions- Each option:
value- Icon set IDlabel- Display namepreview- Icon preview characterdescription- Tooltip
UI:
- Visual cards with icon preview
- Radio button selection
13. IMAGE_UPLOAD - Image Upload with URL
Purpose: Upload images or provide image URLs
{
"id": "logo",
"label": "Logo Image",
"type": "image_upload",
"default": "",
"css_var": "--logo-url",
"description": "Upload logo or enter URL",
"params": {
"acceptTypes": "image/*",
"maxSize": 2048
}
}
Properties:
params.acceptTypes- Accepted file types (default:"image/*")params.maxSize- Maximum file size in KB (default: 2048)
Features:
- File upload with preview
- URL input option
- Base64 encoding for uploaded files
- Client-side validation
- Remove button to clear image
Value Format:
- URL:
"https://example.com/logo.png" - Data URL:
"data:image/png;base64,iVBORw0KG..."
CSS Output:
:root {
--logo-url: https://example.com/logo.png;
}
Usage in CSS:
.header-logo {
background-image: url(var(--logo-url));
}
Notes:
- Uploaded files are stored as base64 data URLs
- For production, consider using Magento media storage
- Data URLs increase CSS file size
14. SPACING - 4-Sided Spacing Control
Purpose: Control padding or margin with individual sides
{
"id": "container_padding",
"label": "Container Padding",
"type": "spacing",
"default": {
"top": 20,
"right": 20,
"bottom": 20,
"left": 20,
"unit": "px",
"linked": true
},
"css_var": "--container-padding",
"description": "Inner spacing",
"params": {
"unit": "px",
"allowedUnits": ["px", "rem", "em", "%"],
"min": 0,
"max": 100,
"step": 1,
"linkedByDefault": true
}
}
Properties:
params.unit- Default unit (default:"px")params.allowedUnits- Available units (default:["px", "rem", "em", "%"])params.min- Minimum value (default: 0)params.max- Maximum value (default: 100)params.step- Input step (default: 1)params.linkedByDefault- Link all sides initially (default: true)
Features:
- Visual 4-sided input (top, right, bottom, left)
- Link/unlink button to sync all sides
- Unit selector dropdown
- Number inputs with min/max validation
Value Format (JSON):
{
"top": 20,
"right": 20,
"bottom": 20,
"left": 20,
"unit": "px",
"linked": true
}
CSS Output:
:root {
--container-padding: 20px; /* All sides same */
--header-margin: 10px 20px; /* top/bottom, left/right */
--section-padding: 10px 20px 30px 40px; /* All different */
}
Shorthand Logic:
- All same:
20px - Top/Bottom + Left/Right:
10px 20px - Top + Left/Right + Bottom:
10px 20px 30px - All different:
10px 20px 30px 40px
Usage Examples:
.container {
padding: var(--container-padding);
}
.section {
margin: var(--section-margin);
}
15. REPEATER - Dynamic List of Items
Purpose: Create repeating groups of fields (slideshows, testimonials, features)
{
"id": "features",
"label": "Features List",
"type": "repeater",
"default": [],
"description": "Add feature items",
"params": {
"min": 0,
"max": 10,
"addButtonLabel": "Add Feature",
"itemLabel": "Feature",
"collapsible": true,
"sortable": true
},
"fields": [
{
"code": "title",
"label": "Title",
"type": "text",
"required": true,
"placeholder": "Feature name"
},
{
"code": "description",
"label": "Description",
"type": "textarea",
"placeholder": "Describe the feature"
},
{
"code": "icon",
"label": "Icon",
"type": "text",
"placeholder": "Icon name"
},
{
"code": "enabled",
"label": "Enabled",
"type": "toggle",
"default": true
}
]
}
Properties:
fields- Array of sub-field definitions (required)params.min- Minimum items (default: 0)params.max- Maximum items (default: 10)params.addButtonLabel- Button text (default: “Add Item”)params.itemLabel- Item title prefix (default: “Item”)params.collapsible- Allow collapsing items (default: true)params.sortable- Allow reordering (default: true)
Supported Sub-Field Types:
text- Single line inputurl- URL inputtextarea- Multi-line inputnumber- Numeric inputselect- Dropdown with optionstoggle- Checkbox/switch
Features:
- Add/remove items dynamically
- Collapsible items to save space
- Drag-and-drop reordering (basic)
- Each item has numbered header
- Min/max validation
- Remove button per item
Value Format (JSON array):
[
{
"title": "Fast Performance",
"description": "Lightning-fast page loads",
"icon": "speed",
"enabled": true
},
{
"title": "Mobile First",
"description": "Responsive on all devices",
"icon": "mobile",
"enabled": true
}
]
CSS Output:
:root {
--features: [{...}, {...}]; /* JSON string, escaped */
}
Notes:
- Repeater fields typically don’t output to CSS directly
- Use in JavaScript/PHP to render dynamic content
- Access via GraphQL API or custom logic
- Not suitable for CSS variables (use for data storage)
Example Sub-Field Configuration:
{
"fields": [
{
"code": "link_text",
"label": "Link Text",
"type": "text",
"required": true
},
{
"code": "link_url",
"label": "URL",
"type": "url",
"required": true
},
{
"code": "target",
"label": "Open in",
"type": "select",
"options": [
{"value": "_self", "label": "Same Tab"},
{"value": "_blank", "label": "New Tab"}
],
"default": "_self"
}
]
}
Field Properties
Common Properties
All field types support these properties:
{
"id": "field_id", // Required: Unique ID within section
"label": "Field Label", // Required: Display label
"type": "text", // Required: Field type
"default": "value", // Optional: Default value
"css_var": "--css-variable", // Optional: CSS custom property name
"description": "Short text", // Optional: Help text below label
"help_text": "Detailed help", // Optional: Tooltip on (i) icon
"placeholder": "Hint...", // Optional: Input placeholder
"required": false, // Optional: Required field (default: false)
"important": false // Optional: Add !important to CSS (default: false)
}
Property Details
id (string, required)
- Unique identifier within section
- Lowercase, alphanumeric + underscore
- Used in database and GraphQL
- Example:
primary_color,grid_columns
label (string, required)
- Display name in UI
- Can include emoji:
"🎨 Primary Color" - Keep concise (2-4 words)
type (string, required)
- See Field Types Reference
- Supported:
color,text,textarea,number,range,select,toggle,code,font_picker,color_scheme,social_links,icon_set_picker,image_upload,spacing,repeater
default (mixed, optional)
- Default value when field is empty
- Type depends on field type:
color:"#FF0000"or"rgb(255, 0, 0)"text/textarea/code:"string"number/range:"123"or"1.5"toggle:trueorfalseselect/font_picker/icon_set_picker:"option_value"-
color_scheme:"light""dark""auto" social_links:"{}"image_upload:"https://example.com/image.png"or""spacing:{"top": 20, "right": 20, "bottom": 20, "left": 20, "unit": "px", "linked": true}repeater:[](empty array)
css_var (string, optional)
- CSS custom property name
- Must start with
-- - Example:
--primary-color,--font-size-base - If omitted, field value is stored but NOT output to CSS
CSS Output:
:root {
--primary-color: 25, 121, 195;
}
description (string, optional)
- Short help text displayed below label
- 1-2 sentences max
- Example:
"Main brand color for buttons and links"
help_text (string, optional)
- Detailed help text in tooltip
- Click (i) icon to show
- Can be longer, include examples
- Example:
"Accepts HEX (#FF0000), RGB (255, 0, 0), or color names"
placeholder (string, optional)
- Input placeholder text
- For:
text,textarea,code,number - Example:
"1280px","Enter value..."
required (boolean, optional, default: false)
- Mark field as required
- Validation error if empty
- Red asterisk (*) in UI
important (boolean, optional, default: false)
- Add
!importantflag to CSS - Use sparingly (for overrides)
CSS Output:
:root {
--primary-color: 255, 0, 0 !important;
}
16. Presets - Pre-configured Templates
Presets allow theme developers to provide ready-to-use style templates that users can apply with one click. Think of them as “starter kits” for different design variations.
What are Presets?
- Read-only templates defined by theme developer in
settings.json - Quick styling - apply multiple settings at once
- Not saved as separate entities - they update Draft values
- User flow: Select → Preview → Apply → Edit (optional) → Publish
Preset vs Publication
| Feature | Preset | Publication |
|---|---|---|
| Source | Static JSON in theme | Dynamic DB records |
| Created by | Theme developer | User (via UI) |
| Modifiable | No (code only) | Yes (via UI) |
| Versioning | No | Yes (full history) |
| Purpose | Starting templates | Saved versions |
Configuration
Add presets array to your settings.json:
{
"version": "1.0",
"sections": [...],
"presets": [
{
"id": "dark-mode",
"name": "🌙 Dark Mode",
"description": "Complete dark color scheme with high contrast",
"settings": {
"colors.primary_color": "#3b82f6",
"colors.text_color": "rgb(243, 244, 246)",
"colors.background": "#0d0d0d",
"colors.link_color": "rgb(96, 165, 250)"
}
},
{
"id": "minimal-clean",
"name": "✨ Minimal Clean",
"description": "Clean light theme with generous spacing",
"settings": {
"layout.container_width": "1024px",
"layout.grid_gap": "1.5rem",
"colors.primary_color": "#ffffff"
}
}
]
}
Preset Structure
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Unique preset identifier (kebab-case) |
name |
string | Yes | Display name in UI (can include emoji) |
description |
string | No | Short description (1-2 sentences) |
settings |
object | Yes | Key-value pairs of settings to apply |
Settings Format
Use dot notation for setting keys:
"settings": {
"section_code.field_code": "value"
}
Examples:
{
"colors.primary_color": "#1a1a1a",
"typography.font_family": "Roboto",
"layout.sidebar_width": "250px",
"spacing.container_padding": "2rem"
}
Important: Section code must match your sections[].id and field code must match sections[].settings[].id.
User Flow
- User opens Panel → sees “Apply Preset” dropdown
- Selects preset → expanded view shows:
- Description
- Warning: “This will change N settings”
- Clicks “Apply Preset”:
- If no unsaved changes → applies immediately
- If unsaved changes exist → shows dialog:
- Merge: Keep my changes, apply preset to unchanged fields
- Overwrite: Discard my changes, use all preset values
- Values applied to Draft → fields update, live preview refreshes
- User can:
- Continue editing fields
- Save Draft
- Publish changes
Best Practices
1. Provide 2-4 Presets Maximum
Too many options = decision paralysis. Focus on key variations:
- Light/Dark mode
- Minimal/Detailed style
- Accessibility preset
2. Use Descriptive Names
✅ Good: “🌙 Dark Mode”, “♿ High Contrast”, “✨ Minimal Clean” ❌ Bad: “Preset 1”, “Theme 2”, “Option B”
3. Write Clear Descriptions
Explain what it does and why user might want it:
{
"description": "Complete dark color scheme with high contrast for better readability in low-light conditions. Perfect for night browsing."
}
4. Only Include Essential Settings
Don’t change everything - focus on settings that define the preset’s character:
{
"dark-mode": {
"settings": {
// ✅ Core colors that define dark mode
"colors.background": "#0d0d0d",
"colors.text": "rgb(243, 244, 246)",
"colors.primary": "#3b82f6"
// ❌ Don't include unrelated settings like sidebar_width
}
}
}
5. Test All Presets
- Apply each preset on clean Draft
- Verify all values apply correctly
- Check live preview updates
- Test with existing unsaved changes (merge vs overwrite)
Complete Example
{
"version": "1.0",
"sections": [
{
"id": "colors",
"name": "Colors",
"settings": [
{
"id": "background",
"label": "Background Color",
"type": "color",
"default": "#ffffff",
"css_var": "--bg-color"
},
{
"id": "text",
"label": "Text Color",
"type": "color",
"default": "rgb(17, 24, 39)",
"css_var": "--text-color"
},
{
"id": "primary",
"label": "Primary Color",
"type": "color",
"default": "#1979c3",
"css_var": "--primary-color"
}
]
}
],
"presets": [
{
"id": "dark-mode",
"name": "🌙 Dark Mode",
"description": "Dark color scheme for night browsing with reduced eye strain",
"settings": {
"colors.background": "#0d0d0d",
"colors.text": "rgb(243, 244, 246)",
"colors.primary": "#3b82f6"
}
},
{
"id": "high-contrast",
"name": "♿ High Contrast",
"description": "Maximum contrast for accessibility and users with visual impairments",
"settings": {
"colors.background": "#000000",
"colors.text": "rgb(255, 255, 255)",
"colors.primary": "#ffff00"
}
},
{
"id": "light-minimal",
"name": "✨ Light Minimal",
"description": "Clean light theme with soft colors and minimal distractions",
"settings": {
"colors.background": "#fafafa",
"colors.text": "rgb(55, 65, 81)",
"colors.primary": "#6b7280"
}
}
]
}
GraphQL API
Query Presets:
query {
breezeThemeEditorPresets(storeId: 1, themeId: 21) {
id
name
description
}
}
Apply Preset:
mutation {
applyBreezeThemeEditorPreset(input: {
storeId: 1
themeId: 21
presetId: "dark-mode"
status: DRAFT
overwriteExisting: false # Merge mode
}) {
success
message
appliedCount
values {
sectionCode
fieldCode
value
}
}
}
Troubleshooting
Presets not showing in UI
- Check JSON syntax with
python3 -m json.tool settings.json - Verify
presetsis top-level array (same level assections) - Clear Magento cache:
bin/magento cache:clean
Preset applies but fields don’t update
- Check section/field codes match exactly (case-sensitive)
- Verify fields exist in
sections[].settings[] - Check browser console for JavaScript errors
“Setting not found” error
Error: colors.primary_color not found
Fix: Verify colors section exists with primary_color field:
{
"sections": [
{
"id": "colors", // Must match "colors" in preset
"settings": [
{
"id": "primary_color" // Must match "primary_color"
}
]
}
]
}
Validation Rules
Built-in Validation
COLOR Type
{
"type": "color",
"default": "#FF0000"
}
- Rule: Must be HEX format
#RRGGBBor#RGB - Error: “Invalid color format. Expected HEX color (e.g. #FF0000)”
NUMBER/RANGE Type
{
"type": "number",
"min": "1",
"max": "12",
"default": "4"
}
- Rules:
- Must be numeric
- Value ≥
min(if specified) - Value ≤
max(if specified)
- Errors:
- “Value must be a number”
- “Value must be at least {min}”
- “Value must not exceed {max}”
TEXT/TEXTAREA Type
Length Validation:
{
"type": "text",
"minLength": 3,
"maxLength": 50
}
- Rules:
- Length ≥
minLength - Length ≤
maxLength
- Length ≥
- Errors:
- “Text must be at least {minLength} characters”
- “Text must not exceed {maxLength} characters”
Pattern Validation:
{
"type": "text",
"pattern": "^\\d+(px|rem|em|%)$",
"validationMessage": "Must be a valid CSS unit (e.g. 10px, 1rem)"
}
- Rule: Value must match regex
pattern - Error: Custom message from
validationMessage
Required Validation
{
"required": true
}
- Rule: Value cannot be empty
- Error: “Field {fieldCode} is required”
Custom Validation
For complex validation, use pattern + validationMessage:
Example: URL Validation
{
"id": "logo_url",
"type": "text",
"pattern": "^https?:\\/\\/.+",
"validationMessage": "Must be a valid URL starting with http:// or https://"
}
Example: Hex Color (alternative)
{
"id": "custom_hex",
"type": "text",
"pattern": "^#[0-9A-Fa-f]{6}$",
"validationMessage": "Must be a 6-digit HEX color (e.g. #FF0000)"
}
Theme Inheritance
Parent Theme Extension
Extend parent theme configuration:
Parent Theme (Parent/Theme/etc/theme_editor/settings.json):
{
"version": "1.0",
"sections": [
{
"id": "colors",
"name": "Colors",
"settings": [
{
"id": "primary_color",
"type": "color",
"default": "#1979c3",
"css_var": "--primary-color"
}
]
}
]
}
Child Theme (Child/Theme/etc/theme_editor/settings.json):
{
"version": "1.0",
"extends": "Parent/Theme",
"sections": [
{
"id": "colors",
"settings": [
{
"id": "secondary_color",
"type": "color",
"default": "#ff6b6b",
"css_var": "--secondary-color"
}
]
},
{
"id": "custom_section",
"name": "Custom Settings",
"settings": [...]
}
]
}
Result:
- Child inherits ALL parent sections and fields
- Child can ADD new sections
- Child can ADD fields to existing sections
- Child CANNOT override/remove parent fields
Inheritance Rules
- Sections: Merged by
id- Parent sections are included
- Child sections are added
- If
idmatches, settings are merged
- Settings: Merged by
id- Parent settings are included
- Child settings are added
- If
idmatches, child value is used (override)
- Values: Child can override parent defaults
- Even without redefining field
- Stored per theme in database
Best Practices
1. Naming Conventions
Section IDs:
- Lowercase, underscore-separated
- Descriptive:
colors,typography,layout,header,footer - Avoid generic names:
section1,misc
Field IDs:
- Lowercase, underscore-separated
- Be specific:
primary_button_colornotcolor1 - Include context:
header_bg_colornotbg_color
CSS Variables:
- Use semantic names:
--primary-colornot--color-1 - Follow convention:
--component-property-modifier--button-primary-bg--text-base-size--header-height-mobile
2. Section Organization
Logical Grouping:
{
"sections": [
{"id": "colors", "order": 1}, // Visual first
{"id": "typography", "order": 2}, // Then typography
{"id": "layout", "order": 3}, // Then structure
{"id": "header", "order": 4}, // Then components
{"id": "footer", "order": 5},
{"id": "advanced", "order": 99} // Advanced last
]
}
Section Size:
- 5-10 fields per section (ideal)
- Max 20 fields (split if larger)
- Use
descriptionto explain section purpose
3. Default Values
Always Provide Defaults:
{
"id": "primary_color",
"default": "#1979c3" // ✅ Good
}
Match CSS Defaults:
// _theme-variables.less
@primary-color: #1979c3;
{
"id": "primary_color",
"default": "#1979c3" // ✅ Matches LESS
}
Use Real Values:
{"default": "TBD"} // ❌ Bad
{"default": "#1979c3"} // ✅ Good
4. CSS Variables
Use Breeze Variables:
{
"id": "primary_color",
"css_var": "--primary" // ✅ Breeze standard
}
Breeze uses RGB format:
:root {
--primary: 25, 121, 195;
}
.button {
background: rgb(var(--primary));
border-color: rgba(var(--primary), 0.5);
}
Avoid Direct Colors:
{
"css_var": "--button-bg-hex" // ❌ Not Breeze style
}
5. Help Text
Description (short):
{
"description": "Main brand color for buttons and links"
}
Help Text (detailed):
{
"help_text": "This color is used for:\n- Primary buttons (Add to Cart, Checkout)\n- Text links\n- Focus states\n- Hover effects"
}
Examples in Help:
{
"help_text": "Accepts values like: 10px, 1rem, 2em, 5%"
}
6. Field Order
Visual Preview First:
{
"settings": [
{"id": "bg_color", "type": "color"}, // Visual
{"id": "text_color", "type": "color"}, // Visual
{"id": "padding", "type": "range"}, // Semi-visual
{"id": "custom_class", "type": "text"} // Technical last
]
}
7. Testing
Before Release:
- ✅ Test all field types render correctly
- ✅ Verify CSS output with
?breeze_theme_editor=1&debug=1 - ✅ Test default values match existing design
- ✅ Validate inheritance if using parent theme
- ✅ Check mobile/tablet responsiveness
- ✅ Test GraphQL queries work
- ✅ Verify draft/publish workflow
Common Issues:
- Missing
css_var→ field stores but doesn’t output CSS - Wrong
defaulttype → validation errors - Duplicate
id→ only last field shows - Missing
extends→ inheritance doesn’t work
Examples
Example 1: Complete Color Section
{
"id": "colors",
"name": "🎨 Colors",
"description": "Customize your theme colors",
"icon": "palette",
"order": 1,
"settings": [
{
"id": "primary_color",
"label": "Primary Color",
"type": "color",
"default": "#1979c3",
"css_var": "--primary",
"description": "Main brand color",
"help_text": "Used for buttons, links, and accents. Supports HEX format (#FF0000)"
},
{
"id": "secondary_color",
"label": "Secondary Color",
"type": "color",
"default": "#6c757d",
"css_var": "--secondary",
"description": "Secondary actions and elements"
},
{
"id": "success_color",
"label": "Success Color",
"type": "color",
"default": "#28a745",
"css_var": "--success",
"description": "Success messages and states"
},
{
"id": "error_color",
"label": "Error Color",
"type": "color",
"default": "#dc3545",
"css_var": "--error",
"description": "Error messages and validation"
},
{
"id": "text_color",
"label": "Text Color",
"type": "color",
"default": "#212529",
"css_var": "--text-color",
"description": "Main body text color"
},
{
"id": "link_color",
"label": "Link Color",
"type": "color",
"default": "#007bff",
"css_var": "--link-color",
"description": "Hyperlink color"
}
]
}
Example 2: Typography Section
{
"id": "typography",
"name": "✍️ Typography",
"description": "Font settings",
"icon": "text_fields",
"order": 2,
"settings": [
{
"id": "body_font",
"label": "Body Font",
"type": "font_picker",
"default": "Open Sans",
"css_var": "--font-family-base",
"options": [
{"value": "Open Sans", "label": "Open Sans"},
{"value": "Roboto", "label": "Roboto"},
{"value": "Lato", "label": "Lato"},
{"value": "Georgia", "label": "Georgia (Serif)"}
],
"description": "Main text font"
},
{
"id": "heading_font",
"label": "Heading Font",
"type": "font_picker",
"default": "Montserrat",
"css_var": "--font-family-heading",
"options": [
{"value": "Montserrat", "label": "Montserrat"},
{"value": "Playfair Display", "label": "Playfair Display"},
{"value": "Raleway", "label": "Raleway"}
],
"description": "Headings font (H1-H6)"
},
{
"id": "base_font_size",
"label": "Base Font Size",
"type": "range",
"default": "16",
"min": "12",
"max": "20",
"step": "1",
"unit": "px",
"css_var": "--font-size-base",
"description": "Body text size"
},
{
"id": "line_height",
"label": "Line Height",
"type": "range",
"default": "1.5",
"min": "1",
"max": "2",
"step": "0.1",
"css_var": "--line-height-base",
"description": "Text line spacing"
}
]
}
Example 3: Layout Section with Validation
{
"id": "layout",
"name": "📐 Layout",
"description": "Page structure settings",
"icon": "view_column",
"order": 3,
"settings": [
{
"id": "container_width",
"label": "Container Width",
"type": "select",
"default": "1280px",
"css_var": "--container-max-width",
"options": [
{"value": "960px", "label": "Narrow (960px)"},
{"value": "1140px", "label": "Standard (1140px)"},
{"value": "1280px", "label": "Wide (1280px)"},
{"value": "1536px", "label": "Extra Wide (1536px)"},
{"value": "100%", "label": "Full Width"}
],
"description": "Maximum content width"
},
{
"id": "custom_container_width",
"label": "Custom Container Width",
"type": "text",
"placeholder": "e.g. 1400px",
"pattern": "^\\d+(px|rem|em|%)$",
"validationMessage": "Must be a valid CSS unit (e.g. 1400px, 90rem)",
"description": "Override with custom value",
"help_text": "Leave empty to use predefined width from dropdown above"
},
{
"id": "grid_columns",
"label": "Product Grid Columns",
"type": "number",
"default": "4",
"min": "2",
"max": "6",
"step": "1",
"css_var": "--product-grid-columns",
"description": "Products per row (desktop)"
},
{
"id": "grid_gap",
"label": "Grid Gap",
"type": "range",
"default": "24",
"min": "0",
"max": "48",
"step": "4",
"unit": "px",
"css_var": "--product-grid-gap",
"description": "Space between products"
}
]
}
Example 4: Advanced Section
{
"id": "advanced",
"name": "⚙️ Advanced",
"description": "Custom code and advanced settings",
"icon": "code",
"order": 99,
"settings": [
{
"id": "custom_css",
"label": "Custom CSS",
"type": "textarea",
"default": "/* Add your custom styles here */",
"rows": 15,
"description": "Additional CSS code",
"help_text": "Will be injected into theme CSS. Use with caution."
},
{
"id": "enable_animations",
"label": "Enable Animations",
"type": "toggle",
"default": true,
"css_var": "--animations-enabled",
"description": "Smooth transitions and effects"
},
{
"id": "animation_duration",
"label": "Animation Speed",
"type": "range",
"default": "300",
"min": "0",
"max": "1000",
"step": "50",
"unit": "ms",
"css_var": "--animation-duration",
"description": "How fast animations play"
}
]
}
Example 5: Social Links
{
"id": "social",
"name": "🔗 Social Media",
"description": "Social media links",
"icon": "share",
"order": 10,
"settings": [
{
"id": "social_links",
"label": "Social Media Profiles",
"type": "social_links",
"default": "{}",
"platforms": [
{"code": "facebook", "label": "Facebook", "icon": "f"},
{"code": "twitter", "label": "Twitter / X", "icon": "𝕏"},
{"code": "instagram", "label": "Instagram", "icon": "📷"},
{"code": "linkedin", "label": "LinkedIn", "icon": "in"},
{"code": "youtube", "label": "YouTube", "icon": "▶"},
{"code": "pinterest", "label": "Pinterest", "icon": "P"}
],
"description": "Enter your social media profile URLs",
"help_text": "Leave empty to hide a platform. Links will appear in footer."
},
{
"id": "show_social_icons",
"label": "Show Social Icons",
"type": "toggle",
"default": true,
"description": "Display social media icons in footer"
}
]
}
Troubleshooting
Field Not Showing
Problem: Field doesn’t appear in Theme Editor panel
Solutions:
- ✅ Check
idis unique within section - ✅ Verify JSON syntax is valid
- ✅ Clear cache:
bin/magento cache:clean - ✅ Check browser console for JS errors
- ✅ Verify
typeis supported
CSS Not Generated
Problem: Field value doesn’t appear in CSS output
Solutions:
- ✅ Add
css_varproperty - ✅ Verify
css_varstarts with-- - ✅ Check value is not empty
- ✅ Verify value is not same as
default - ✅ View CSS output:
?breeze_theme_editor=1&breeze_theme_editor_debug=1
Validation Errors
Problem: Cannot save field value
Solutions:
- ✅ Check
min/maxfor number/range - ✅ Verify
patternregex is correct (escape backslashes:\\d) - ✅ Check
requiredfields are filled - ✅ For color: use HEX format
#RRGGBB - ✅ Check browser console for detailed error
Inheritance Not Working
Problem: Parent theme fields not showing
Solutions:
- ✅ Verify
extendsvalue matches parent theme path - ✅ Check parent theme has
etc/theme_editor/settings.json - ✅ Clear cache after changing
extends - ✅ Verify parent theme is registered in Magento
Performance Issues
Problem: Panel loads slowly with many fields
Solutions:
- ✅ Split large sections (max 20 fields per section)
- ✅ Use
orderto organize sections logically - ✅ Avoid unnecessary
help_text(loads on demand) - ✅ Optimize field types (use
selectinstead oftextfor predefined values)
FAQ
Can I add custom field types?
No - currently only built-in types are supported. Planned for v2.0.
Can I use media files (images)?
Not yet - image_upload field type is planned (Task 1.1.3).
How do I test my configuration?
- Create
settings.json - Clear cache:
bin/magento cache:clean - Open frontend with
?breeze_theme_editor=1 - Check panel on right side
Where are values stored?
- Database table:
breeze_theme_editor_value - Per theme + per store + per status (draft/published)
- Accessible via GraphQL
Can I export/import settings?
Yes - via GraphQL mutations:
breezeThemeEditorExportSettingsbreezeThemeEditorImportSettings
How do I create presets?
Add presets array to your settings.json:
{
"version": "1.0",
"sections": [...],
"presets": [
{
"id": "dark-mode",
"name": "🌙 Dark Mode",
"description": "Dark color scheme for night browsing",
"settings": {
"colors.primary_color": "#3b82f6",
"colors.text_color": "rgb(243, 244, 246)",
"colors.background": "#0d0d0d"
}
}
]
}
See Section 16: Presets for complete documentation.
Version History
- 1.1 (2026-01-13) - Presets Feature
- Added Section 16: Presets documentation
- 3 new field types: IMAGE_UPLOAD, SPACING, REPEATER (v1.0.5)
- Preset UI implementation
- Updated FAQ with preset examples
- 1.0 (2026-01-09) - Initial documentation
- 12 field types documented
- Validation rules
- Theme inheritance
- Examples and best practices
Support
- Issues: GitHub Issues
- Documentation: This file
- GraphQL API: See
etc/schema.graphqls
Happy theming! 🎨