1ConsentConsentDocs
Custom consent designs

1CL syntax and control flow

1CL adds bindings, conditions, loops, and components to HTML. It is not a JavaScript expression language: function calls, calculations, filters, ternaries, else, and compound and/or conditions are unsupported. Use runtime data and structure your markup around it.

Text and attribute bindings

<h2>{{ ui.title }}</h2>
<p>{{ ui.description }}</p>
<div data-framework="{{ config.framework.type }}">{{ ui.title }}</div>

Bindings read dot-separated paths. Missing/null text renders no text. Strings become text nodes: bound markup displays as text, not inserted HTML. Keep ordinary content in localized settings rather than hard-coding one language.

Inside tags, bindings must be in quoted attributes. Mixed values work in ordinary HTML attributes:

<div class="category category-{{ category.id }}" aria-label="{{ category.name }}">
  {{ category.name }}
</div>

Bare bindings such as <div {{ attributes }}> and attribute spreading are unsupported. A whole-attribute binding removes the attribute for false, null, or undefined; true creates a boolean attribute. Empty strings remove ordinary attributes. Classes are whitespace-normalized; zero remains an attribute value.

Conditions

[[if appearance.showClose]]
<button type="button" data-1c="command:close">{{ ui.button.close }}</button>
[[/if]]
[[if not categories]]
<p>{{ ui.description }}</p>
[[/if]]

False values are false, zero, empty strings, null/undefined, and empty arrays. Non-empty arrays and empty objects are true. Close both condition forms with [[/if]].

Nested conditions need an intervening HTML element; directly nested sibling markers are rejected:

[[if categories]]
<div>
  [[if appearance.showCategoriesInNotice]]
  <p>{{ ui.title }}</p>
  [[/if]]
</div>
[[/if]]

Loops

<ul>
  [[each category in categories]]
  <li>
    <h3>{{ category.name }}</h3>
    <ul>
      [[each service in category.services]]
      <li>{{ service.name }}</li>
      [/each]
    </ul>
  </li>
  [/each]
</ul>

Loops require arrays; missing/non-array values render no items. Each item extends the caller scope, so ui remains available. Close with [/each], one bracket pair. Separate nested loops with an HTML element so markers pair correctly. There is no built-in index variable, filtering, sorting, or object enumeration.

Views

Start a view file with @view main, @view details, or @view trigger as the first non-empty line at column zero. Reusable partials must not start with @view. Canonical file names and kinds determine published views; a directive does not rename a protected file. See components.

Security boundaries

The compiler rejects script, iframe, object, and embed elements, inline on* handlers, and literal javascript: attribute values. Use commands instead of scripts. Bind trusted configuration URLs and retain the disclosures and choices required by your configured framework.

On this page