# Schema and settings

## A section is markup plus a schema

Every `sections/*.html` and `blocks/*.html` file has a separate file with the same name and the `.schema.json` extension. For example: `sections/hero.html` and `sections/hero.schema.json`. Do not place comments, trailing commas or expressions inside schema JSON.

```html
<section>
  <h1 data-text-field="[[ section.settings_path ]].title" gs-text="section.settings.title"></h1>
  <template gs-slot="blocks"></template>
</section>
```

`*.schema.json`:

```json
{
  "name": "Hero",
  "settings": [
    {
      "type": "text",
      "id": "title",
      "label": "Heading",
      "default": "Welcome"
    }
  ],
  "blocks": [
    {
      "type": "@theme"
    }
  ],
  "max_blocks": 12,
  "presets": [
    {
      "name": "Hero"
    }
  ]
}
```

`name` labels the definition. `settings` defines its controls. `blocks` permits child types; it does not create instances. `presets` makes a definition available for adding from the editor. `max_blocks` limits dynamic children. Initial instances belong in page JSON.

## Settings reference

| Type | Value and control | Required details / limitations |
| --- | --- | --- |
| `text` | String, single-line input | `id`, `label`; use `default` |
| `textarea` | String, multiline input | Supports text dynamic sources |
| `url` | URL input | HTTP(S), root-relative, anchor, `mailto:`, `tel:` or empty |
| `image_picker` | Image URL string | Currently a URL input, not a media browser |
| `color` | Custom picker and HEX input | Six-digit HEX or `transparent` default |
| `range` | Slider and numeric input | `min`, `max`, positive `step`, numeric `default`; optional `unit` |
| `number` | Numeric input | Store a number, not a string |
| `select`, `radio` | Custom options menu | `options` with `value` / `label`; default must match an option |
| `checkbox` | Boolean control | Boolean default, not `"true"` |
| `font_picker` | Font selection | Inter, Arial or Georgia |
| `header`, `paragraph` | Sidebar information | `content`; no persisted setting |

```json
[
  {"type":"header","content":"Appearance"},
  {"type":"range","id":"radius","label":"Corner radius","min":0,"max":48,"step":1,"default":16,"unit":"px"},
  {"type":"color","id":"background","label":"Background","default":"#ffffff"},
  {"type":"checkbox","id":"show_link","label":"Show link","default":true},
  {"type":"select","id":"alignment","label":"Alignment","default":"left","options":[{"value":"left","label":"Left"},{"value":"center","label":"Center"}]}
]
```

Apply numeric units in markup, for example `border-radius:{{ block.settings.radius }}px`. Store `16`, not `"16px"`. A unit in the control label does not add CSS automatically.

## Defaults and saved values

Set a meaningful default for every editable field. The editor and renderer use defaults only when a saved value is missing. An empty string, zero and false remain intentional values. Opening an editor should not save a document.

Preset settings are initial values when adding an instance; they are not a migration for existing instances. Changing a default may affect previously unset settings. Explicitly saved values remain. Keep IDs and types stable across releases.

IDs start with a letter or underscore and use letters, digits, underscores and hyphens, up to 80 characters. Do not use `constructor`, `prototype` or `__proto__`. Limit a settings array to 100 entries. Unknown setting IDs are rejected on save.

## Practical editing contract

A schema control does not automatically make every HTML node clickable. Add [editor bindings](/en/editor/) for inline text and card editing. Do not add native color or select controls inside the theme to imitate the editor: the platform supplies consistent controls from schema.
