Commmerce Editor
Global settings
Global Settings is the editor-wide configuration surface: theme-level branding, colour, typography, fonts / colors / buttons slot registries, product display defaults, SEO defaults, global head and body code, and the theme's responsive breakpoint configuration. It lives in the right sidebar (replacing the Element Inspector when nothing is selected) and is backed by two stores — useEditorSettingsStore for the persisted EditorSettings payload (saved to the server with each version) and useCommmerceEditorSetupStore for transient editor UI state (device mode, zoom, snap, sidebar toggles, dialog flags). This page is the exhaustive field-by-field reference — every leaf property under every top-level key, with type, default, description, and the merchant-facing use case that motivates each control.
Overview
There are two surfaces. The primary one is the in-sidebar Global Settings panel (settings/GlobalSettingsPanel.tsx) — when no element is selected in the right sidebar, this panel replaces the "No element selected" empty state with a category list. Eight categories drill in inline within the sidebar (Branding, Theme Colour, Typography, Fonts, Color, Button, Product, Global SEO); two "wide" categories (Global Code, Breakpoints) instead pop a floating SettingsExpandedPanel to the left so the Monaco code editor and breakpoint cards have enough room.
The legacy surface is the Global Settings modal (settings/Settings.tsx) — a full Antd Modal with tabs for the same categories. Both share the same draft / save plumbing via useEditorSettingsStore and useSaveSettings, so the data model and validation are identical.
All persisted settings are typed by the EditorSettings interface in common/components/commmerce-editor/types/settings.types.ts. The breakpoint sub-shape lives in types/breakpoint.types.ts. Every default value documented below traces back to a DEFAULT_* constant in those two files — if a field shows — in the Default column, the source did not seed it and the renderer falls back to its own internal default.
How it works
The committed settings payload lives in useEditorSettingsStore.settings (typed as EditorSettings). Each sub-section maintains its own local draft and emits changes via onSettingsChange; GlobalSettingsPanel wires those into setDraftField('branding' | 'themeColour' | 'typography' | ...) on the store. While a draft is non-empty, hasUnsavedDraft() returns true and a sticky Save / Discard footer appears at the bottom of the panel.
Save flow (GlobalSettingsPanel.handleSave / Settings.handleSave): it is intentionally server-first. useSaveSettings inspects the URL params and dispatches to the Dev Portal settings save flow or the Store Editor settings save flow depending on the active project. On success, importSettings(draft) commits the payload into useEditorSettingsStore and runs syncSettingsToWindow() as a side effect so web components and CSS variables pick up the new values immediately. Then importBreakpointSettings(draft.breakpoints) mirrors breakpoint changes into useBreakpointStore and copyBreakpointStyles(from, to) seeds any newly added breakpoints with styles copied from an existing one. Finally, saveThemeBreakpoints(breakpointStorageKey) persists the breakpoint config to local storage as a hot-restart fallback, and clearDraft() wipes the draft.
Persisted vs transient. Two distinct stores back this UI. Persisted theme settings (EditorSettings) ship to the server with every version and live forever — this is what end users see on the live store. Transient editor UI state lives in useCommmerceEditorSetupStore (store/useCommmerceEditorSetupStore.ts), persisted to sessionStorage under the key commmerce-editor-setup-store, with a partialize that explicitly drops isInitializing, initializingMessage, placementMode and quickEditElementId so a stale rehydrate never leaves the canvas in crosshair mode or with a panel pinned to an element that no longer exists.
UI state for the panel itself (which section is drilled in, which sub-view of Fonts / Colors / Buttons is active) lives in a separate transient store useGlobalSettingsUIStore (settings/useGlobalSettingsUIStore.ts) so the right sidebar can swap between Element Inspector and Global Settings without losing or leaking drilled-in state. resetUIState() is called whenever the sidebar swaps to the Element Inspector.
Key features
- In-sidebar panel (
GlobalSettingsPanel.tsx) — renders inline when no element is selected; eight categories drill in within the 320px sidebar, two open a floating expanded panel to the left. - Legacy modal (
Settings.tsx) — same tabs in an AntdModalwithwidth: min(60rem, 90vw);destroyOnHiddenso each open starts with a fresh draft from committed settings. - Draft / commit semantics — every sub-section maintains a local draft (seeded from
storeSettings.*) and emits viaonSettingsChange. Nothing applies to the canvas or to web components until Save. - Server-first save — the API call happens before the store mutation, so a failed save leaves the runtime untouched and the user can retry without local drift.
- Project-aware save dispatch —
useSaveSettingsreads URL params (app_id/app_version_codevsstore_theme_id/store_theme_version_code) and picks the right endpoint with no prop drilling. - Reusable slot registries — Fonts, Color and Button each expose a list / add / edit sub-view inside their inline section, persisted under
settings.fonts,settings.colors,settings.buttons. The Button section also has apatternsshape-gallery sub-view that returns to the edit form it was opened from. - Dynamic breakpoint config (
BreakpointSettings.tsx) — manage up toMAX_CUSTOM_BREAKPOINTS(10) custom breakpoints in addition to the four defaults. Each row controlsminWidth,containedWidth, label and icon; max-width and canvas-width are auto-calculated byrecalculateBreakpointRanges. Adding a new breakpoint can optionally copy every element style from an existing source breakpoint. - Custom attribute registry — theme-wide list of typed HTML attribute definitions (text, number, value-unit, dropdown, radio, checkbox, range, seeker) that elements can opt into via the Element Inspector. Reserved structural keys (
id,class,style,src,href, etc.) are blocked by the picker form. - Global Code editor — Monaco editor with four tabs (Header HTML, Body HTML, CSS, JavaScript). Header HTML and CSS are injected into
<head>; Body HTML and JavaScript are injected before</body>. - Global SEO defaults —
defaultTitle,prefixTitle,suffixTitle,defaultDescription,defaultKeywords,defaultMetaImage. Page-level Block Settings override these per page. - Snap settings for smart guides — managed on
useCommmerceEditorSetupStore.snapSettings: enabled, threshold (5px default), showDistances, snapToElements, snapToContainer. The bottom toolbar has a one-click toggle (toggleSnap). - Default device mode — the canvas always boots at
deviceMode: 'xl'inuseCommmerceEditorSetupStore; the topbar's device switcher callstoggleDeviceMode(id)to switch between any default or custom breakpoint id. - Settings re-seed — sub-components remount on
draftResetVersionchange (incremented after Save / Discard), re-seeding from committedstoreSettings. Otherwise theiruseEffectseed would refire on every keystroke and steal focus.
EditorSettings reference
Top-level keys of the EditorSettings object in types/settings.types.ts. Each key is owned by one sub-section UI; the onSettingsChange from each component writes into setDraftField(key, value) on the store. The rest of this page expands each key into a dedicated section with every leaf field documented.
versionnumber—Schema version for the whole settings payload. Optional on older payloads — importSettings backfills missing branches with defaults.brandingBrandingSettingsBrandingSettings.tsxPrimary logo, favicon, and a structured brand-logo block (image or text logo with full typography controls).themeColourThemeColourSettingsThemeColourSettings.tsxWeb-component colour tokens — primary/secondary text, button, addToCart, see-or-select-variant button, border radius, input box, hyperlink, checkbox, radio button.typographyTypographySettingsTypographySettings.tsxEleven role-keyed typography styles (heading, subHeading, description, button, addToCartButton, seeOrSelectVariantButton, link, caption, label, sellingPrice, originalPrice).productProductSettingsProductSettings.tsxProduct display defaults — background, media width/height, object-fit, short / long description typography.fontsFontsSettingsFontsSettings.tsxFree-form list of named font slots; list / add / edit sub-views inside the inline section.colorsColorsSettingsColorsSettings.tsxFree-form list of named colour slots reusable across elements.buttonsButtonsSettingsButtonsSettings.tsxFree-form list of named button styles, with a shape-pattern gallery sub-view that returns to the entry it was opened from.customAttributesCustomAttributesSettings—Theme-wide registry of user-defined typed HTML attributes that elements can opt into.seoSEOSettingsSEOSettings.tsxTheme-wide SEO defaults: default title (+ prefix/suffix), default description, keywords, default meta image.globalCodeGlobalCodeSettingsGlobalCodeSettings.tsxFree-form code snippets injected into the live page: headerHTML, bodyHTML, css, javascript.breakpointsBreakpointSettingsBreakpointSettings.tsxOrdered list of breakpoints (4 defaults + up to MAX_CUSTOM_BREAKPOINTS custom). Drives the canvas device switcher and every responsive style write.Branding
Owned by BrandingSettings.tsx. Controls the merchant's primary logo and favicon (raw URL strings) plus a structured brandLogo block that the header web component renders directly — either as an <img> (logoType 'image') or as styled text (logoType 'text'). Merchants edit this once when launching a new theme and then return to it whenever they rebrand, add a wordmark, or swap their favicon for a holiday variant.
BrandingSettings (top level)
primaryLogostring''URL of the primary logo image rendered by the header on most pages. Set this when the merchant has an existing image asset they want to drop straight into the header without text styling.faviconIconstring''URL of the favicon shown in browser tabs and on bookmark lists. Set this to brand the tab icon when shoppers pin or share the store.brandLogoBrandLogoSettingsDEFAULT_BRAND_LOGO_SETTINGSStructured logo block with full image-or-text typography controls. Use this instead of primaryLogo when you want the editor to render the logo (sizing, font, transform) rather than ship a baked image.BrandLogoSettings (brandLogo)
logoType'image' | 'text''text'Switches the brand-logo renderer between an image asset and a typographic wordmark. Flip to 'text' when the merchant has no image yet and wants a styled brand name; flip to 'image' once they upload artwork.logoUrlstring''URL of the brand logo image, only read when logoType is 'image'. Update when the merchant uploads a new logo file.logoUrlAltstring''Alt text for the logo image — used by screen readers and crawlers. Set this for accessibility compliance and SEO so that the brand name appears even when images are off.logoWidthnumber150Width of the logo image. Tune this to fit different headers (wider for spacious desktop headers, narrower for compact tablet headers).logoWidthUnitstring'px'Unit for logoWidth (typically px, rem, or %). Switch to % when you want the logo to scale with its container instead of being a fixed size.logoHeightnumber50Height of the logo image. Tune this to align the logo with adjacent header controls without distorting its aspect ratio (combine with logoObjectFit).logoHeightUnitstring'px'Unit for logoHeight. Same trade-off as the width unit — switch to rem for a logo that scales with root font size.logoObjectFitstring'contain'CSS object-fit mode for the logo image (contain / cover / fill / scale-down / none). Use contain to preserve aspect ratio (most common), cover when the logo is a designed banner that should fill its slot.logoTextstring'Brand Name'Text rendered when logoType is 'text'. Set this to the brand wordmark.logoTextFontFamilystring'Roboto, sans-serif'Font family for the text logo. Pick a display or serif face to give the wordmark personality distinct from the rest of the site.logoTextFontWeightstring'700'Font weight (100-900) for the text logo. Use 700+ for the wordmark to make it dominant in the header.logoTextFontSizenumber24Font size value for the text logo. Tune this so the wordmark fits the header height comfortably across breakpoints.logoTextFontSizeUnitstring'px'Unit for logoTextFontSize. Switch to rem for a wordmark that scales with the user's browser font size preference.logoTextLetterSpacingnumber0Letter spacing for the text logo. Use a small positive value (e.g. 1px) to give an uppercase wordmark a more spacious, fashion-brand feel.logoTextLetterSpacingUnitstring'px'Unit for logoTextLetterSpacing (px or em). Use em if you want the spacing to scale with the font size.logoTextColorstring'#000000'Text colour for the wordmark. Set to the brand's primary colour or to a header-on-dark variant for inverted header sections.logoTextTransformstring'none'CSS text-transform for the wordmark (none / uppercase / lowercase / capitalize). Flip to uppercase for a classic minimalist brand mark without re-typing the source.Example — text wordmark
branding: {
primaryLogo: '',
faviconIcon: 'https://cdn.example.com/favicon.svg',
brandLogo: {
logoType: 'text',
logoText: 'ATELIER',
logoTextFontFamily: 'Playfair Display, serif',
logoTextFontWeight: '700',
logoTextFontSize: 28,
logoTextFontSizeUnit: 'px',
logoTextLetterSpacing: 2,
logoTextLetterSpacingUnit: 'px',
logoTextColor: '#111111',
logoTextTransform: 'uppercase'
}
}Theme Colour
Owned by ThemeColourSettings.tsx. These are the colour tokens consumed by the production mmm-* web components — not individual element styles but the system-wide tokens that every PDP, cart, checkout and header reads. Merchants set these once when locking in their brand palette; the renderer wires them to CSS custom properties so a single change repaints buttons, links, inputs and checkboxes everywhere.
text
primaryColorstring'#000000'Primary body text colour. Set this to the brand's strongest readable colour — used by most paragraph and heading text.secondaryColorstring'#6F6F6F'Secondary / muted text colour. Use for captions, meta info, and de-emphasised copy that should still be legible against the background.button
normalFillstring'#000000'Fill colour of the generic button at rest. Set to the brand's accent colour for the primary CTA appearance.normalStrokestring'#000000'Border colour of the generic button at rest. Match normalFill for a solid look, or pick a contrasting colour for an outlined style.hoverFillstring'#000000'Fill colour on hover. Lighten / darken from normalFill to give a clear interactive cue.hoverStrokestring'#000000'Border colour on hover. Match the hover fill for solid buttons; keep the stroke fixed and shift the fill for outline-on-hover effects.addToCartButton
normalFillstring'#000000'Add-to-cart button fill at rest. Make this the most prominent colour in the palette so the conversion CTA always pops.normalStrokestring'#000000'Add-to-cart button border at rest. Match the fill for a solid block, or use a contrasting outline.normalTextColorstring'#FFFFFF'Label colour at rest. Pick a high-contrast value against normalFill for legibility.hoverFillstring'#333333'Add-to-cart button fill on hover. A subtle shade of the rest fill gives a discoverable hover state without changing the meaning.hoverStrokestring'#333333'Add-to-cart button border on hover.hoverTextColorstring'#FFFFFF'Add-to-cart label colour on hover. Re-check contrast against the hover fill.seeOrSelectVariantButton
normalFillstring'#FFFFFF'Fill of the secondary "See / Select Variant" button at rest — defaults to white so the secondary action sits visually below add-to-cart.normalStrokestring'#000000'Border of the variant button at rest. Use a dark stroke against a white fill for a classic outlined secondary CTA.normalTextColorstring'#000000'Label colour at rest.hoverFillstring'#F5F5F5'Variant button fill on hover — defaults to a soft grey so the secondary state remains distinct from the primary CTA.hoverStrokestring'#000000'Variant button border on hover.hoverTextColorstring'#000000'Variant button label colour on hover.border
colorstring'#BFBFBF'Generic border colour for cards, dividers and bordered surfaces. Lighten for a softer interface; darken for a structured editorial feel.radiusnumber0.375Default border radius applied across the theme (default equals 6px at REM_BASE 16). Increase for a friendly pill-heavy feel; set to 0 for a sharp brutalist look.radiusUnitstring'rem'Unit for radius. rem keeps the corners proportional across breakpoints; switch to px for fixed visuals.inputBox
strokeColorstring'#BFBFBF'Border colour for form inputs and selects. Tune so input outlines remain visible against the page background without competing with content.strokeWidthnumber1Border width in px for form inputs. Increase to 2px for an emphasised input look on premium product pages.hyperlink
defaultColorstring'#6F6F6F'Hyperlink colour at rest. Set to a brand-aligned hue distinct from body text so links are visually scannable.hoverColorstring'#000000'Hyperlink colour on hover. Pick a higher-contrast tone to confirm interactivity.checkbox
defaultColorstring'#000000'Default border / box colour for unchecked checkboxes. Tune to match the input stroke so all form chrome reads as a single family.selectedTickColorstring'#000000'Tick colour when the checkbox is selected. Use a high-contrast colour against the box fill so the selection state is unmistakable.radioButton
borderColorstring'#000000'Outer ring colour of radio buttons. Match inputBox.strokeColor for consistent form chrome.selectedDotColorstring'#000000'Inner dot colour when selected. Use a strong accent so the chosen option is visually unmissable.Typography
Owned by TypographySettings.tsx. Eleven role-keyed styles that the web components pick up by role — e.g. the PDP heading reads heading; the cart's selling price reads sellingPrice; the link rendered inside a description reads link. Merchants tune these to set the editorial voice of the store. Every style shares the shape below; per-style defaults vary so each role starts in a sensible place.
TypographyStyle shape (applies to every role key)
fontFamilystring'Roboto, sans-serif'CSS font-family stack for this role. Set to a display face for headings; keep a neutral sans-serif for body and form labels.fontWeightstring'700'CSS font-weight as a string (100-900). Use 600-800 for headings and CTAs; 400-500 for body and captions.fontSizenumber2Numeric font size for the role. Scale up for hero-style heading on luxury themes; scale down for dense editorial layouts.fontSizeUnitstring'rem'Unit for fontSize (rem recommended). Switch to px only when you need fixed typography that ignores root-size changes.letterSpacing?number0Optional letter spacing. Use a small positive value for uppercase headlines; leave at 0 for body text.letterSpacingUnit?string'rem'Unit for letterSpacing. em scales with the font size; px stays fixed.lineHeight?number1.2Optional unitless line-height multiplier. Use 1.2-1.3 for tight headlines, 1.5-1.7 for comfortable body reading.textTransform?string'none'CSS text-transform (none / uppercase / lowercase / capitalize). Apply uppercase to labels and CTAs for a structured look.textDecoration?string—CSS text-decoration at rest. Used by the link and originalPrice roles for underline / line-through; not set for headings.textDecorationHover?string—CSS text-decoration on hover. Combine with textDecoration to flip underline visibility on hover for links.Role-key defaults
All eleven role keys exist on TypographySettings. Their seeded defaults from DEFAULT_TYPOGRAPHY_SETTINGS are listed below (anything omitted from the source remains undefined). Use the role list to decide which control to tune for a given UX outcome.
headingRoboto / 7002 rem, lineHeight 1.2Top-level page and hero headings. Tune size up for marketing themes, down for product-dense list views.subHeadingRoboto / 6001.5 rem, lineHeight 1.3Section / sub-section titles inside a page. Sits one tier below heading in visual weight.descriptionRoboto / 4001 rem, lineHeight 1.5Body / paragraph copy used in product descriptions, cart notes, and content blocks.buttonRoboto / 6000.875 rem, letterSpacing 0.03125 remGeneric button label typography. Slightly tighter than body so CTAs read as actionable.addToCartButtonRoboto / 6000.875 rem, textTransform uppercaseDedicated typography for the primary add-to-cart CTA. Uppercase by default for visual emphasis.seeOrSelectVariantButtonRoboto / 5000.875 remSecondary variant-action button text. Lighter weight than the primary CTA to enforce visual hierarchy.linkRoboto / 4001 rem, textDecoration underlineHyperlink typography. Underlined at rest by default so links are discoverable inside paragraphs.captionRoboto / 4000.75 rem, lineHeight 1.4Small captions / helper text under images and form fields. Tune size to keep secondary info legible without competing.labelRoboto / 5000.875 remForm field labels and chip labels. Slightly heavier than body so it reads as UI chrome.sellingPriceRoboto / 7001.125 remActive selling price on PDP / PLP / cart. Heavier weight than body so the number anchors the product card.originalPriceRoboto / 4001 rem, textDecoration line-throughStrikethrough original price next to a discounted selling price. Visually de-emphasised so the discount is obvious.Product
Owned by ProductSettings.tsx. Theme-wide defaults for how a product is presented — the framing colour around media, the dimensions of product images, and the typography used for short and long descriptions. Adjust these once when designing the look of every product card / PDP, then let element-level overrides handle one-off cases.
Product display
backgroundColorstring'#FFFFFF'Background colour behind product media. Set to a tinted neutral (e.g. #FAFAFA) for an editorial backdrop that lifts product photos off the page.productMediaWidthnumber100Width of the product media frame (the gallery wrapper). Default 100% means the media fills its column; switch to a fixed value for designed grid layouts.productMediaWidthUnitstring'%'Unit for productMediaWidth. % for fluid, px / rem for fixed.productMediaHeightnumber18.75Height of the product media frame (default equals 300px at REM_BASE 16). Tune higher for tall portrait photography, lower for landscape lookbooks.productMediaHeightUnitstring'rem'Unit for productMediaHeight. rem scales with root size; px for pixel-perfect art direction.productObjectFitstring'cover'CSS object-fit for product photos. cover crops to fill (best for uniform card heights), contain keeps the entire photo visible (best for varied aspect ratios).shortDescription (ProductDescriptionStyle)
fontFamilystring'Roboto, sans-serif'Font stack for the short description (PLP / cart line). Pick a tight, readable face for compact contexts.fontWeightstring'400'Weight for the short description. Keep at 400 so it doesn't compete with the product title.fontSizenumber0.875Font size of the short description. Tune down for dense lists, up for editorial PLPs.fontSizeUnitstring'rem'Unit for the short description font size.letterSpacingnumber0Letter spacing for the short description.letterSpacingUnitstring'rem'Unit for the letter spacing.lineHeightnumber1.5Line height for the short description. 1.4-1.5 keeps two-line summaries readable in cards.textTransformstring'none'Text transform for the short description.colorstring'#6F6F6F'Colour for the short description. Defaults to a softer grey so the line reads as supporting copy.description (ProductDescriptionStyle)
fontFamilystring'Roboto, sans-serif'Font stack for the long product description shown on PDP.fontWeightstring'400'Weight for the long description — stays at body weight for readability over multi-paragraph copy.fontSizenumber1Font size of the long description.fontSizeUnitstring'rem'Unit for the long description font size.letterSpacingnumber0Letter spacing for the long description.letterSpacingUnitstring'rem'Unit for the letter spacing.lineHeightnumber1.6Line height for the long description. 1.6 gives the editorial breathing room expected of multi-paragraph PDP copy.textTransformstring'none'Text transform for the long description.colorstring'#333333'Colour of the long description — defaults to a near-black for strong readability over long passages.Fonts
Owned by FontsSettings.tsx. A merchant-managed list of named font slots. Each slot is a fully configured typography preset (family, weight, size, colour, line-height, transform, custom CSS). Once registered, the slot can be referenced anywhere a typography control accepts a slot id — so every element using "Primary Heading" picks up future edits automatically. The default list is empty (DEFAULT_FONTS_SETTINGS.fonts = []); merchants populate it as they design the theme.
FontEntry shape (one per font slot)
idstringclient-generatedStable client-generated id used to reference the slot from elements. Survives renames since name is editable — never reuse or recycle.namestring—Display name shown in pickers (e.g. "Primary", "Special Heading"). Rename freely — references stay intact because they use id.fontFamilystring—CSS font-family stack for the slot. Pick once per slot so every consumer inherits the same typeface.fontWeightstring—Weight string (100-900) for the slot.fontSizenumber—Numeric font size value.fontSizeUnitstring—Unit for fontSize (px, rem, em).fontColorstring—Default colour applied to elements using this slot.letterHeightnumber—"Letter Height" in the UI — maps to CSS line-height with a unit. Use a larger value for more vertical breathing room.letterHeightUnitstring—Unit for letterHeight.letterSpacingnumber—Letter spacing for the slot.letterSpacingUnitstring—Unit for letterSpacing.textTransformstring—CSS text-transform (none / uppercase / lowercase / capitalize).customCSSstring—Free-form CSS appended to the slot's rule. Use this escape hatch for unsupported properties (e.g. font-feature-settings, font-variant-numeric) without losing the slot abstraction.Colors
Owned by ColorsSettings.tsx. Mirrors the Fonts pattern: a merchant-managed list of named colour slots. Define the palette once (Primary, Secondary, Universal, etc.) and reuse the colour by id from anywhere a colour input accepts a slot. Rebrands then ripple through the entire theme by editing one slot. Defaults to an empty list (DEFAULT_COLORS_SETTINGS.colors = []); merchants populate it as they curate the palette.
ColorEntry shape
idstringclient-generatedStable client-generated id used to reference the slot from elements. Survives renames since name is editable.namestring—Display name (e.g. "Primary", "Universal Grey"). Rename freely without breaking references.colorstring—Hex colour value (e.g. #FFC23A). Stored without alpha — the picker is configured with disabledAlpha to keep the model simple. Tune by editing the slot to repaint every consumer across the theme.Buttons
Owned by ButtonsSettings.tsx. The button registry mirrors the Fonts / Colors pattern but is far richer: each entry encodes a complete visual button design (shape, colour, border, radius, padding, font, interaction). Mark one entry isPrimary to use it as the theme-wide primary button; consumers pick the first isPrimary entry when resolving "primary". Defaults to an empty list (DEFAULT_BUTTONS_SETTINGS.buttons = []).
Button shapes
The shape field drives the rendered silhouette. The first four honour the corner-radius controls; tab uses asymmetric corners; chevron and hexagon use clip-paths and ignore border rendering entirely.
rounded Honours the Corner Radius slider / per-corner overrides. Default choice for friendly modern CTAs.square Forces 0 radius. Use for brutalist or editorial themes that want sharp corners everywhere.pill Full half-circle ends. Use for soft, hospitality-feeling CTAs and tag-style buttons.ellipse Oval silhouette (border-radius: 50% / 50%). Use sparingly for hero CTAs.tab Asymmetric — rounded TL + BR only. Use for tabbed UI and category chips.chevron Arrow silhouette via clip-path. Use for "next step" CTAs that should literally point forward (no border rendering).hexagon Six-sided silhouette via clip-path. Use for badge-style CTAs (no border rendering).Button interaction
The interaction field controls the hover / press transition. Stored as a free-form string so future patterns can be added without a type migration; unknown values fall back to none.
none No transition — use for buttons that should feel instant.slide-up Label slides up on hover. Use for buttons with two-line label + arrow combos.slide-down Label slides down on hover. Inverse of slide-up.fade Opacity cross-fade on hover. Use for subtle, premium feels.scale Subtle scale-up on hover. Use for playful storefronts.ButtonEntry shape (one per button slot)
idstringclient-generatedStable client-generated id, used to reference the slot from elements. Survives renames.namestring—Display name (e.g. "Primary", "Secondary", "Special"). Rename freely.isPrimaryboolean—Marks this entry as the theme's primary button. Multiple entries can carry the flag — consumers pick the first one when resolving "primary".enableIconboolean—Toggles a trailing arrow icon. Turn on for "Shop now" / "Read more" CTAs that benefit from a directional cue.buttonColorstring—Background fill of the button. Tune to the brand's accent colour for the primary CTA.textColorstring—Label / foreground colour. Ensure high contrast against buttonColor for legibility.shapeButtonShape—Visual silhouette — see the Button shapes table above.fontSizenumber—Numeric font size of the button label.fontSizeUnitstring—Unit for fontSize.fontFamilystring—Font family of the label. Match the rest of the theme typography, or pick a contrasting display face for hero CTAs.fontWeightstring—Font weight (100-900) of the label. 600-700 for the primary CTA, 500 for secondary buttons.interactionButtonInteraction—Hover / press transition flavour — see the Button interaction table above.borderThicknessnumber—Border thickness in px. Set to 0 for solid buttons, 1-2 for outlined / ghost styles.borderColorstring—Border colour. Match buttonColor for solid, or use a contrasting colour for outlined.cornerRadiusnumber—Uniform corner radius value. Read for rounded shapes; ignored when customCorners is true.cornerRadiusUnitstring—Unit for cornerRadius (px or rem).customCornersboolean—When true, the four per-corner values override cornerRadius. Turn on for asymmetric tabbed or chevron-style corners.cornerTopLeftnumber—Per-corner radius (top-left). Only read when customCorners is true.cornerTopRightnumber—Per-corner radius (top-right). Only read when customCorners is true.cornerBottomLeftnumber—Per-corner radius (bottom-left). Only read when customCorners is true.cornerBottomRightnumber—Per-corner radius (bottom-right). Only read when customCorners is true.paddingnumber—Uniform padding value. Tune for the button height that fits the layout.paddingUnitstring—Unit for padding.customPaddingboolean—When true, the four per-side padding values override the uniform padding. Turn on for tall narrow CTAs or icon-flanked labels.paddingTopnumber—Padding (top). Only read when customPadding is true.paddingRightnumber—Padding (right). Only read when customPadding is true.paddingBottomnumber—Padding (bottom). Only read when customPadding is true.paddingLeftnumber—Padding (left). Only read when customPadding is true.Custom Attributes
Theme-wide registry of typed HTML attributes that elements can opt into. The picker form (rendered from the Element Inspector) consumes this registry to offer a curated set of attributes the user can apply to a node; each definition declares the attribute key, the input flavour, the validation ranges, and an optional default value. Defaults to an empty list (DEFAULT_CUSTOM_ATTRIBUTES_SETTINGS.list = []).
Custom attribute types
text Single-line text. Use for free-form short strings (e.g. data-tracking-label).number Numeric input with optional min/max/step. Use for counts and integer ids.value-unit Number + unit dropdown (e.g. "12px"). Use for layout-style attributes the renderer parses into a CSS value.dropdown Single-select dropdown over options. Use for enumerated states.radio Single-select radio group over options. Use when the options should be visible at a glance.checkbox Boolean toggle, persisted as the strings "true" / "false". Use for on/off flags consumed by web components.range Low + high numeric pair, persisted as "low,high". Use for inclusive numeric ranges.seeker Slider with min / max / step. Replaces the old range slider; step applies here.CustomAttributeDefinition shape
idstringgenerated on first saveStable id kept across edits.namestring—Display name shown in the picker card and as the inspector label.keyNamestring—Actual HTML attribute key written to BaseNode.attributes. Must not collide with reserved structural keys (see Validation rules).typeCustomAttributeType—Input flavour — see the Custom attribute types table above. Drives which primitive InspectorRow renders.options?string[]—List of allowed values for dropdown / radio.units?string[]DEFAULT_CUSTOM_ATTRIBUTE_UNITSList of unit labels offered in the value-unit dropdown. Defaults to ['px', 'rem', 'em', '%', 'vw', 'vh'].min?number—Numeric clamp (number / range / seeker).max?number—Numeric clamp (number / range / seeker).step?number—Step value for number and seeker. range ignores stepping — the pair is two free numbers.defaultValue?string—Value applied to node.attributes[keyName] when the user clicks Add in the picker. Stored as a string regardless of type.description?string—Optional description shown under the title in the picker card. Use to document the attribute's intent for other team members.Global SEO
Owned by SEOSettings.tsx. Theme-wide SEO defaults applied to every page that doesn't override them at the page level. Page-level Block Settings always win — these defaults fill in the gaps so a freshly created page still has a sensible title / description / image without manual setup.
defaultTitlestring''Theme-wide fallback page title (warning thrown when length > 60). Set to the store's tagline so unconfigured pages still rank with a meaningful title.prefixTitlestring''Prefix prepended to every page title. Use for "Store Name | " style prefixes.suffixTitlestring''Suffix appended to every page title. Use for " - Store Name" style suffixes.defaultKeywordsstring''Comma-separated keyword list (warning thrown when count > 20). Use for legacy SEO platforms that still consume meta keywords.defaultMetaImagestring''URL of the default OpenGraph / Twitter card image (URL-validated). Use a hero-style branded image so unconfigured share cards still look intentional.defaultDescriptionstring''Theme-wide fallback meta description (warning thrown when length > 160). Set to a one-sentence elevator pitch for the brand.Global Code
Owned by GlobalCodeSettings.tsx. Free-form code snippets the SSR pipeline injects on every rendered page. Use these for third-party scripts, theme-wide CSS overrides, and additional <head> tags — never hand-edit the rendered HTML in a way that bypasses the theme editor.
headerHTMLstring''Raw HTML injected inside <head>. Use for analytics tags, meta verification, and font preload links.bodyHTMLstring''Raw HTML injected just before </body>. Use for tracking pixels, chat widgets, and late-loaded vendor scripts.cssstring''Global CSS, minified before injection into <head>. Use for theme-wide tweaks that don't belong on a single element.javascriptstring''Global JS, minified before injection before </body>. Use for custom interactivity that's not worth promoting to a WC Function.Breakpoints
Owned by BreakpointSettings.tsx. The ordered list of responsive breakpoints used by the canvas device switcher and by every responsive style write. Four immutable defaults ship with the theme (xl, desktop, tablet, mobile); merchants can add up to MAX_CUSTOM_BREAKPOINTS (10) more. Custom breakpoint ids are custom_{counter} — the counter is monotonic so deleting and re-adding "the same" breakpoint produces a new id.
BreakpointSettings (top level)
versionnumber1Schema version for breakpoint migrations.breakpointsBreakpointConfig[]4 defaults (xl, desktop, tablet, mobile)Ordered list of breakpoints — see the BreakpointConfig table.customBreakpointCounternumber0Monotonic counter for generating custom ids. Never recycles.BreakpointConfig shape (one per breakpoint)
idBreakpointId'xl' | 'desktop' | 'tablet' | 'mobile' | custom_NStable breakpoint id. Defaults are immutable; custom ids increment monotonically.labelstringauto-generatedDisplay label shown in the topbar device switcher and in BreakpointSettings cards. Auto-updated when ranges change unless the merchant customised it.minWidthnumber | nullvariesMinimum viewport width to activate this breakpoint. null only for the lowest (mobile) breakpoint — everything else has a numeric value ≥ 300px (MIN_ALLOWED_WIDTH).maxWidthnumber | nullauto-calculatedMaximum viewport width. Auto-derived as the next breakpoint's minWidth - 1; null for the topmost breakpoint. Do not edit directly.canvasWidthnumberauto-calculated, 320-3840Width shown in the editor canvas for this breakpoint. Defaults to maxWidth (or minWidth + 200 for the topmost). Bounded between 320 and 3840 by validateBreakpointConfig.containedWidthnumber0Side margin per side in px on the rendered page. 0 means full-width; a positive value boxes the page in. Drives the live page's contained layout (distinct from the editor canvas layoutSettings below).iconstring'TvMinimal' / 'Monitor' / 'Tablet' / 'Smartphone'Lucide icon name shown in the device switcher. Defaults vary per breakpoint; custom breakpoints can pick Laptop / Watch / any registered icon.isDefaultbooleantrue for xl/desktop/tablet/mobileMarks the four built-in breakpoints. Custom breakpoints are always false.isDeletablebooleantrue for defaults & customsWhether the row exposes a delete affordance. Default breakpoints currently allow deletion; MIN_BREAKPOINTS (1) is enforced as a floor.ordernumbervariesSort order — higher means a larger screen. Recalculated whenever ranges change.copiedFrom?BreakpointId | nullundefinedMetadata-only flag recording which breakpoint's styles were copied when this one was added. Drives the post-save copyBreakpointStyles call — new breakpoints inherit the source breakpoint's responsive styles for every element.Default breakpoint values
xl1281 / null1920 / TvMinimalExtra-Large desktops (1281px+). Treat as the design canonical — most marketing imagery is composed here.desktop769 / 12801280 / MonitorStandard desktops and small laptops. The mainstream desktop authoring breakpoint.tablet431 / 768768 / TabletTablets and large phones in landscape.mobilenull / 430430 / SmartphonePhones in portrait — the floor of the responsive system. minWidth is locked to null and the InputNumber is hidden.Constants from breakpoint.types.ts: MAX_CUSTOM_BREAKPOINTS = 10, MIN_BREAKPOINTS = 1, MIN_ALLOWED_WIDTH = 300, validateBreakpointConfig.canvasWidth bounds 320-3840.
App-wide editor UI state
Fields on useCommmerceEditorSetupStore that are truly app-wide — not per-block, not per-element, not part of the theme settings payload. The store is persisted to sessionStorage minus isInitializing, initializingMessage, placementMode and quickEditElementId (these are transient by design — see the partialize in useCommmerceEditorSetupStore.ts).
deviceModeBreakpointId'xl'Active canvas breakpoint. Drives which responsiveStyles slot is read/written and which device frame the canvas shows. Switch via the topbar device picker to design for a specific viewport.editorMode'drag-drop' | 'code''drag-drop'Top-level mode toggle between the visual canvas and the Monaco-based code editor. Flip to 'code' for direct HTML/CSS authoring.layoutSettingsRecord<BreakpointId, { mode, containedWidth }>xl 240, desktop 100, tablet 50, mobile 20 (all full-width)Per-breakpoint contained vs full-width preference and side padding in px. Read by getEffectiveContainerBoundary for canvas insert constraints — distinct from the saved breakpoints[].containedWidth which drives live output.layoutSettings[device].mode'full-width' | 'contained''full-width'Toggle between an edge-to-edge canvas and a centred contained layout for the active device. Use 'contained' to preview pages designed for a max content width.layoutSettings[device].containedWidthnumber (px)240/100/50/20 per default deviceSide padding in px when mode is 'contained'. Tune to match the brand's intended content gutter.zoomnumber1Canvas zoom factor; resize / drag math divides DOM rects by this value to stay in document coords. Zoom out to scan dense pages, zoom in to fine-tune small elements.snapSettings.enabledbooleantrueMaster smart-guides toggle. Turn off to drag freely without snap behaviour interfering.snapSettings.thresholdnumber (px)5Pixel distance below which a drag snaps to an alignment guide. Widen to make alignment magnetic; narrow for precision work.snapSettings.showDistancesbooleantrueWhether to render the live distance labels between the dragged element and its neighbours. Turn off for a cleaner canvas while dragging.snapSettings.snapToElementsbooleantrueSnap to nearby sibling element edges and centres. Turn off to ignore siblings when aligning to a guide.snapSettings.snapToContainerbooleantrueSnap to parent container edges and centre. Turn off when working inside a container that you do not want to be magnetic.interactionMode'select' | 'pan' | 'marquee' | 'resize' | 'drag' | 'idle''idle'Mutex flag the drag-drop subsystem uses to prevent event conflicts during a resize / drag. Generally set by subsystems; do not change manually.leftSidebarCollapsebooleanfalseCollapse / expand the left sidebar (Layers / Elements / Blocks). Collapse to reclaim canvas space on small monitors.leftSidebarStatusLeftSidebarKeysLayersWhich left-sidebar tab is active: Layers / Elements / Blocks. Switch via the left sidebar's tab strip.rightSidebarCollapsebooleantrueCollapse / expand the right sidebar (Element Inspector / Global Settings). Collapse when reviewing layout without making style changes.placementModePlacementMode | nullnullClick-to-place mode from the bottom toolbar. Transient — never persisted across reloads so the canvas never boots in crosshair mode.quickEditElementIdstring | nullnullWhich element is allowed to show its QuickEditPanel — set on double-click, cleared on selection change. Transient.activeHotspotIndexnumber | nullnullWhich hotspot in the active image is focused for editing; shared between the QuickEditPanel and the canvas overlay.isInitializingbooleantrueGate flag while the editor shell is still fetching block + version data; consumers render a full-canvas skeleton. Transient.initializingMessagestring | nullnullOptional message shown in the initialization overlay (e.g. "Auto-creating new draft version…"). Transient.isReadOnlybooleanfalseLocks the editor when the active version is published. Drives every store mutation to short-circuit.appSlugstring | nullnullDev Portal preview slug used to build the preview URL. Set after the version loads.appNamestring | nullnullDev Portal display name shown alongside the version in the topbar so devs always see which app + version they are editing.Validation rules
Validation is implemented in types/settings.types.ts and types/breakpoint.types.ts. Most checks produce warnings (the save still proceeds — the user is informed inline) rather than hard errors.
validateSEOSettings
defaultTitle.length > 60— warning: "Default Title should be under 60 characters for optimal SEO".defaultDescription.length > 160— warning: "Default Description should be under 160 characters for optimal SEO".defaultKeywordssplit by,> 20 entries — warning: "Consider limiting keywords to 20 or fewer".defaultMetaImagenon-empty and not a valid URL — warning: "Default Meta Image should be a valid URL".
validateBrandingSettings
primaryLogoset but not a valid URL — warning: "Primary Logo should be a valid URL".faviconIconset but not a valid URL — warning: "Favicon should be a valid URL".primaryLogoempty — recommendation: "Primary Logo is recommended for better branding".faviconIconempty — recommendation: "Favicon is recommended for browser tab identification".
validateGlobalCodeSettings
Currently a no-op — Monaco handles syntax errors and there are no length / pattern checks. The function exists as a hook for future hardening.
Breakpoint validation
validateBreakpointOverlap— flags any pair of breakpoints with overlapping[minWidth, maxWidth]ranges.validateBreakpointConfig— requiresminWidth < maxWidthwhen both are set,canvasWidthbetween320and3840, and a non-emptylabel.validateNewBreakpointMinWidth— requiresminWidth ≥ MIN_ALLOWED_WIDTH(300) and forbids duplicate minWidths.
Reserved custom attribute keys
RESERVED_ATTRIBUTE_KEYS blocks these keyName values in the custom attribute picker form to prevent shadowing structural attributes: id, class, style, src, href, alt, title, name, type, value, data-block-id, data-block-name, data-block-type, data-block-version.
Workflows
1. Edit theme colours and save
- Click on empty canvas to deselect; the right sidebar swaps to Global Settings.
- Click Theme Colour — the section drills in inline and seeds its draft from
storeSettings.themeColour. - Change colour values; each change calls
setDraftField('themeColour', ...)and the sticky Save / Discard footer appears. - Click Save —
useSaveSettingsdispatches to the project's settings save flow; on success,importSettings(draft)commits to the store,syncSettingsToWindow()updates CSS variables, andclearDraft()wipes the draft.
2. Add a custom breakpoint and copy desktop styles
- Open Breakpoints — the floating expanded panel opens to the left.
- Fill in
Min Width(e.g. 900),Margin(0 for full-width or >0 for contained), label and icon. - Tick Copy styles from existing breakpoint and pick
Desktop. - Click Add — the new
custom_Nbreakpoint is added todraftSettings.breakpointswithcopiedFrom: 'desktop'; ranges recalculate viarecalculateBreakpointRanges. - Save — the editor calls
copyBreakpointStyles('desktop', 'custom_N')afterimportBreakpointSettingsso every element gets the desktop responsive slot duplicated under the new id, then persists to local storage as a hot-restart fallback.
3. Add a global tracking pixel via Global Code
- Open Global Code in the expanded panel.
- Switch to Body HTML; paste the pixel's
<script>tag. - Save — the script lands in
settings.globalCode.bodyHTMLand is injected before</body>on every SSR-rendered page.
4. Register a reusable font slot
- Open the Fonts inline section; the panel header swaps to a
+icon. - Click
+— the sub-view switches to{ kind: 'add' }onuseGlobalSettingsUIStore. - Fill in family, weight, size, line height, transform; Save the entry. The back chevron pops back to the Fonts list, not out of the section.
- Reference the font slot by id from anywhere a typography control is used — every element styled with that slot picks up future edits automatically.
5. Tune snap thresholds
- Snap settings are part of the editor UI state, not the saved theme settings — they live on
useCommmerceEditorSetupStore. - Call
setSnapSettings({ threshold: 8, snapToContainer: false })(or use the bottom toolbar's smart-guides controls) to disable container snap and widen the threshold. - The change persists to
sessionStoragewith the rest of the editor setup store; reload the tab and the change survives.
6. Set up Branding for a new theme
- Open Branding inline.
brandLogo.logoTypeseeds to'text'withlogoText"Brand Name". - Replace
logoTextwith the actual brand name; tunelogoTextFontFamily,logoTextFontSize,logoTextLetterSpacing,logoTextColor,logoTextTransform. - If an image asset is available, flip
logoTypeto'image'and paste a CDN URL intologoUrl; setlogoUrlAltfor accessibility. - Upload a square favicon to a CDN and paste the URL into
faviconIcon; save. - Re-run
validateBrandingSettingsif you want the warning list — the form already shows recommendations inline.
7. Register a primary button style
- Open Button inline; click
+to enter the add sub-view. - Set
name("Primary"), tickisPrimary, pick ashape(e.g.'pill'), choosebuttonColor+textColor, setfontSize/fontFamily/fontWeight, and configureinteraction. - Set
cornerRadius/ unit ifshapeis'rounded', or flipcustomCornerson and fill in the four per-corner values. - Set uniform
padding/ unit, or flipcustomPaddingon and tune the four per-side values for tall narrow CTAs. - Save — the slot lands in
settings.buttons.buttons[]; references resolve "primary" to the firstisPrimaryentry.
8. Register a custom attribute
- From Global Settings, define a new
CustomAttributeDefinition: pick aname(e.g. "Track label"), akeyName(e.g.data-track) that is not inRESERVED_ATTRIBUTE_KEYS, atype(e.g.'text'), and an optionaldefaultValueanddescription. - For
'dropdown'/'radio', populateoptions; for'value-unit'populateunits(defaults toDEFAULT_CUSTOM_ATTRIBUTE_UNITS); for'number'/'range'/'seeker'setmin/max/step. - Save — the definition appears in the Element Inspector picker, and applied values are written to
BaseNode.attributes[keyName]as strings.
Tips & gotchas
- The Save button is intentionally hidden until a draft exists. If you change a value and the button doesn't appear, the change didn't reach
setDraftField— usually because the sub-component'suseEffectseed re-ran (check that the section is keyed bydraftResetVersionlike the others). - The modal (
Settings.tsx) and the in-sidebar panel (GlobalSettingsPanel.tsx) share the same store and save hook — opening the modal while a sidebar draft is pending will read from the same draft, not from committed settings. - Breakpoint deletion is destructive — styles for the deleted breakpoint are lost;
BreakpointSettingsshows a Popconfirm before callingdeleteBreakpoint. There is no undo for this through the editor history slice. - The Mobile breakpoint (
minWidth: null) cannot be edited — it is always the "lowest" breakpoint and its row hides the InputNumber. Don't rely on a numeric minWidth for mobile. - Custom breakpoint ids are assigned as
custom_{counter}inBreakpointSettings.handleAddBreakpoint; the counter is monotonic and never recycles, so deleting and re-adding "the same" custom breakpoint produces a new id. - The setup store is persisted to
sessionStorage, notlocalStorage— closing the browser drops zoom, snap settings, and sidebar collapse state. The persisted theme settings are server-side and survive forever. - If
useSaveSettingsreturns"Cannot determine save context — URL is missing version params.", the panel is being rendered outside of a Dev Portal or Store Editor route (e.g. inside a preview iframe with no version params). Wrap consumers in a route that provides them. - Layout settings (
layoutSettingson the setup store) are distinct from thecontainedWidthfield on each breakpoint insettings.breakpoints— the breakpointcontainedWidthdrives the saved theme's responsive output;layoutSettingsdrives the editor's canvas frame only. - Fonts / Colors / Buttons all default to empty lists. Element-level controls fall back to inline values when no slot id is referenced, so an empty registry is not a runtime error — it just means no theme-wide reuse is wired up yet.
- Custom attribute
defaultValueis always a string. For'checkbox'persist"true"/"false"; for'range'persist the pair as"low,high"; consumers must parse on read. - Typography optional fields (
letterSpacing,lineHeight,textTransform,textDecoration,textDecorationHover) are unset for some roles by design — the renderer falls back to user-agent defaults when absent. Only set what you need to override. - Breakpoint
canvasWidthis auto-calculated frommaxWidth(orminWidth + 200for the topmost breakpoint) and clamped to[320, 3840]byvalidateBreakpointConfig— don't try to edit it directly.