Guide 04 · Developer docs

Input types, layouts, components and images

The platform's central abstraction, in one line: an input type defines what an editor can enter, a layout area binds one input type to a named slot, a layout groups areas and points at a template, a record or component holds the content for each area, and the template reads it back. Read this book straight after the template languages.

L1–L3
Unit 02 · Input types: what an editor can enter

Select options, keys and design tokens

A select stores the option's key, not its label, so the key is what templates and stylesheets depend on. Writing options, changing them safely, one select type per design choice, and finding the duplicate and unused types a mature site collects.

Who this is for. Content modellers who create select and multiselect input types, and the theme developers whose templates and CSS depend on them. After this page you can add, relabel and retire options without breaking pages, and keep a site's list of input types short.

Possible values: key and label

A select or multiselect lists its options under Possible values, one per entry, written as key::Label. The author sees the label; the record stores the key; templates receive the key. Without ::, the same text is both. This site's Content Width type has two:

Possible values of the Content Width input type
full-bleed::Full width (edge to edge)
layout single::Standard width

A select area returns its key from getAreaText(); a multiselect returns its keys as a list from getAreaValues(). The second key above holds a space on purpose: printed into a class attribute it becomes two classes. To show the author's label on the page instead, look it up on the area's input type:

Template code
{$key = $component->getAreaText('width')}
{$label = $key}
{foreach $component->getAreaType('width')->getPossibleValues() as $option}
  {$parts = explode('::', $option->getValue(), 2)}
  {if $parts[0] == $key && count($parts) == 2}{$label = $parts[1]}{/if}
{/foreach}
<div class="{$key}" data-width-label="{$label|escape}">...</div>
{% set key = component.getAreaText('width') %}
{% set label = key %}
{% for option in component.getAreaType('width').getPossibleValues() %}
  {% set parts = option.getValue()|split('::', 2) %}
  {% if parts[0] == key and parts|length == 2 %}{% set label = parts[1] %}{% endif %}
{% endfor %}
<div class="{{ key }}" data-width-label="{{ label|e }}">...</div>

Changing options safely

  • Relabelling is safe. Change the part after ::; stored keys are untouched and the templates see no difference.
  • Adding is safe. The new option appears everywhere the type is used. It can be chosen as the type's default only after the input type has been saved once with it.
  • Changing or removing a key is not. Records that hold the old key keep it: the template still receives it, and nothing in the stylesheet matches it any more. A select's value is checked against the current keys whenever a record is saved, so an API write that keeps the old key is refused with "This value is not valid.", and in the admin the old key is no longer among the choices.

To retire a key, add its replacement, move every record that uses the old key to the new one (in the admin, or through the API, where a select takes the key alone), check that nothing holds the old key, and only then remove it. The value an API write must send for each input type is on Writing areas.

Design tokens as select types

On a mature site about half the input types are selects that each control one styling choice: a section's background, text alignment, column width, a button's variant. The pattern that keeps them manageable:

  • One input type per style axis, named for the axis, not for the first place it was used: Section background, not Homepage hero background.
  • Keys are the CSS classes, with a prefix per axis: bg-light::Light, bg-dark::Dark, bg-brand::Brand colour. The template prints the key into class and the stylesheet does the branching.
  • Reuse the type on every layout that offers the choice, so the options are identical everywhere and one new option reaches every layout at once.
  • Make the area an attribute, so the setting sits behind the Attributes control instead of between content fields: see Attributes: settings, not content.
Template code
<section class="section {$component->getAreaText('background')} {$component->getAreaText('align')}">
  {$component->body}
</section>
<section class="section {{ component.getAreaText('background') }} {{ component.getAreaText('align') }}">
  {{ component.body }}
</section>

The classes are read with getAreaText() and the body with the shorthand. Inside an attribute you need the plain value: in live preview, the shorthand and getAreaHtml() wrap a text value in the editing span that lets the author change it in place, while getAreaText() adds that span only to rich text. A helper that turns any list of such areas into one class string, in both engines, is Pattern: attributes to CSS classes.

Reuse versus proliferation

Left alone, a site collects near-identical types (ColumnWidth, columnSize, Column Width Medium), each with slightly different options, and the author's experience and every later developer suffer. Create a new type only when its options or its rules genuinely differ from every existing one; otherwise reuse. Before creating one, list the existing types of the same editor and compare their options.

The list, with what uses each type, comes from the API:

Select input types with their options and what uses them
GET /cdna-api/common/input-types?editor=select&limit=100&properties={id,name,possibleValues{value},layoutAreas{id},customFields{id}} HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Response 200 OKThree of the 56 select types on this site
{
  "result": "success",
  "totalCount": 56,
  "data": [
    {
      "id": 32,
      "name": "Content Width",
      "possibleValues": [
        {"value": "full-bleed::Full width (edge to edge)"},
        {"value": "layout single::Standard width"}
      ],
      "layoutAreas": [{"id": 84}, {"id": 89}],
      "customFields": []
    },
    {
      "id": 88,
      "name": "backgroundPosition",
      "possibleValues": [],
      "layoutAreas": [],
      "customFields": []
    },
    {
      "id": 96,
      "name": "alignContent",
      "possibleValues": [],
      "layoutAreas": [],
      "customFields": []
    }
  ]
}
Real response from this documentation site, shortened to three of its entries

On this documentation site the full answer is telling: of 56 select types, 50 are used by no layout area and no custom field, and 40 have no options at all. A type with empty layoutAreas and customFields is unused and can be deleted; the admin refuses to delete a type that is still used ("Types with associated areas or custom fields cannot be deleted"). Two types with the same editor and the same keys are one type too many: move the areas of one onto the other, then delete it. The quarterly routine that includes this check is on Naming conventions and the site audit.

Go deeper

Was this page helpful?

Your answer goes to the docs team.