Plang reference
Objects
Every global a Plang template can read: section, settings, theme, site, page, entry, forloop and editing, plus link and collection objects.
A template can read these globals, and nothing else besides the variables it makes itself with assign, capture and for. They are read-only, and they are the same inside snippets.
| Global | What it is |
|---|---|
section | The section being rendered: its id, handle, type, position and settings |
settings | The theme settings (config/settings_schema.json) |
theme | Colours, fonts and style options chosen in the editor's Theme tab |
site | Business name, WhatsApp number, language and language list |
page | The page being shown |
entry | The item of a detail page, or nil |
forloop | Inside a for loop |
editing | true only inside the editor |
A missing property is nil and prints nothing, so you can't break a page by reading something that isn't there.
section
| Property | Value |
|---|---|
section.id | The section's unique id, short lowercase letters and digits such as k3j9x1. Handy for unique HTML ids: id="menu-{{ section.id }}" |
section.handle | The file name without .plang: promo-banner for sections/promo-banner.plang |
section.type | code for a section that is added from the library, or the built-in type (hero, header, faq...) when your file replaces a built-in section |
section.index | The section's position on the page, counting from 0. The header and footer are rendered on their own, so they get 0 |
section.settings | The values of the settings declared in the section's {% schema %}, by setting id |
Most templates start by shortening the settings:
{%- assign s = section.settings -%}
<h2>{{ s.heading }}</h2>How each setting type arrives
Setting types are declared in the {% schema %} (see sections and settings). What the template receives depends on the type:
| Schema type | You get | Notes |
|---|---|---|
text, textarea, richtext | Text | '' when empty. Output is escaped. For paragraphs use the paragraphs or lines filters, or markdown |
select, radio | The chosen option's value as text | |
icon | An icon name such as Truck | Draw it with icon: {{ s.icon | icon }} |
number, range | A number | 0 when not a valid number |
checkbox, toggle | true or false | |
color | A hex colour like #D62828, or '' | Only valid #rgb or #rrggbb values come through |
image, image_picker | A safe image address (/uploads/... or https://...), or '' | Anything else is dropped. Use the image snippet to get a placeholder when empty |
link, url | A link object, or '' when empty | Prints as the address |
collection | A collection object, or nil | Up to 48 visible items |
list | A list of objects, one per item, with the item fields as properties | At most the list's max items. Each item field arrives by the rules in this table |
Settings the schema doesn't describe (for example the style options every section gets: bg, pad, anchor) are copied as plain values. The platform already applies those to the section wrapper, so you rarely need them.
{%- assign s = section.settings -%}
{% for plan in s.plans %}
<h3>{{ plan.name }}</h3>
{% if plan.price > 0 %}{{ plan.price | money }}{% endif %}
{% render 'button', label: plan.button_label, link: plan.button_link %}
{% endfor %}settings
settings holds the theme settings: the values behind config/settings_schema.json, which owners change in the editor's Theme tab. It is not the same as section.settings. A value is the owner's choice, or the schema's default if they haven't changed it.
[
{
"name": "Accent",
"settings": [
{ "type": "color", "id": "accent_color", "label": "Accent colour", "default": "#D62828" },
{ "type": "checkbox", "id": "show_top_bar", "label": "Show top bar", "default": true }
]
}
]{% if settings.show_top_bar %}<div class="top-bar">{{ site.name }}</div>{% endif %}
<span style="color: {{ settings.accent_color }}">Promo</span>The same types as section settings work, except collection. Colours, numbers, ranges, selects and checkboxes are also written as CSS variables on the website, named --ts-<id> with _ turned into -. accent_color becomes --ts-accent-color. Ranges get their unit appended, and checkboxes become 1 or 0. See theme settings and styling.
theme
The choices made in the editor's Theme tab.
| Property | Value |
|---|---|
theme.colors.brand, .accent, .base, .surface, .ink, .inverse | The six theme colours, as hex |
theme.heading_font, theme.body_font | Font family names, such as Poppins |
theme.heading_weight | 500, 600, 700 or 800 (as text) |
theme.radius | none, sm, md, lg or xl |
theme.button_shape | square, rounded or pill |
theme.heading_case | normal or upper |
theme.heading_style | plain or bar |
theme.button_style | solid, soft, outline or bold |
theme.card_style | flat, border or shadow |
theme.image_style | plain, arch or frame |
Usually CSS variables are the better way to use the theme (var(--c-brand), see styling), because they follow each section's background. Use theme when the template itself has to decide something, such as {% if theme.button_shape == 'pill' %}.
site
| Property | Value |
|---|---|
site.name | The business name |
site.whatsapp | The WhatsApp number as digits in international format, such as 6281234567890. For a chat link use the whatsapp_url filter instead |
site.lang | The language code of the page being shown: id, en, ... |
site.languages | The language switcher: a list of { code, label, href, current }. label is the native name (English, 日本語), href is the same page in that language and current is true for the one being shown. The built-in header only shows a switcher when the list has more than one entry; do the same |
site.home | The address of the home page in the current language: / or /en |
site.credit | true when the "made with Plangku" credit should be shown |
site.app_url | The address of Plangku itself, for the credit link |
{% if site.languages.size > 1 %}
<nav aria-label="{{ 'language' | ui }}">
{% for l in site.languages %}
<a href="{{ l.href }}" hreflang="{{ l.code }}"{% if l.current %} aria-current="true"{% endif %}>{{ l.label }}</a>
{% endfor %}
</nav>
{% endif %}page
The page being shown, or nil if there isn't one.
| Property | Value |
|---|---|
page.title | The page title |
page.slug | The page's address part, '' for the home page |
page.url | The page's address in the current language, such as /tentang or /en/tentang |
entry
On a detail page (the page that is shown for each item of a data collection) entry is the item being shown. On every other page it is nil, so test it first: {% if entry %}...{% endif %}. In the editor, a detail page shows a sample item so you can design it before real data exists.
Properties are listed on the data page: title, url, collection, images, image, price, old_price, badge, summary, excerpt, body, facts and fields.
forloop
Available inside {% for %}. See the full table on the language page: index, index0, rindex, rindex0, first, last, length and parentloop.
When you use {% render 'card' for items as item %}, the snippet gets a smaller forloop with index, index0, first, last and length.
editing
true while the template is rendered inside the editor canvas, false on the live site and in previews. Use it to show a hint that visitors should never see:
{% if s.products == nil and editing %}
<p>Pilih koleksi produk di pengaturan section ini.</p>
{% endif %}Keep the hint harmless: the markup that editing adds is the only difference between the editor and the live site.
Link objects
A link setting (and the output of the link filter) is a link object. Written out, it is the finished address, so <a href="{{ s.button_link }}"> just works. Behind it is a value the owner picks in the editor: a page, a WhatsApp message, a phone number, an email address, a section anchor or any web address.
| Property | Value |
|---|---|
{{ link }} and link.href | The address to put in href |
link.kind | What the owner chose: url, whatsapp, page, anchor, phone or email |
link.external | true for WhatsApp chats and for web addresses that start with http:// or https://, so you know to open a new tab |
What each kind turns into:
| Stored value | kind | Address written out |
|---|---|---|
wa:Halo, saya mau pesan | whatsapp | https://wa.me/6281234567890?text=Halo%2C%20saya%20mau%20pesan (the site's number) |
page:tentang | page | /tentang (or /en/tentang on the English version). page: alone is the home page |
#kontak | anchor | #kontak |
tel:0812-3456-7890 | phone | tel:081234567890 |
mailto:[email protected] | email | mailto:[email protected] |
https://tokopedia.com/kopi | url | https://tokopedia.com/kopi |
/produk | url | /produk on this website |
An empty link setting is the empty text '', which is false. Anything that can't be understood becomes #.
{%- assign to = s.button_link -%}
<a href="{{ to | default: '#' }}"
{%- if to.external %} target="_blank" rel="noopener noreferrer"{% endif %}>
{%- if to.kind == 'whatsapp' %}{{ 'whatsapp' | icon: class: 'size-5' }}{% endif -%}
{{ s.button_label }}
</a>You rarely write this by hand: {% render 'button', label: ..., link: ... %} does exactly this (see snippets). The link filter makes a link object from any stored value, such as a text you built yourself: {% assign order = 'wa:Halo, mau pesan ' | append: p.title | link %}.
Collection objects
A collection setting gives a collection object, which lists a data collection's items (Produk, Menu, Artikel...). It is nil until the owner picks one.
| Property | Value |
|---|---|
collection.id | The collection's id |
collection.name | Its name, such as Produk |
collection.slug | Its address part, such as produk |
collection.url | The address of its list page, such as /produk |
collection.items | A list of at most 48 items, in the order set in the Data tab. Hidden (draft) items are left out |
Each item has id, title, url, image, price, old_price, badge, subtitle, summary and fields. The data page explains them with a product grid example.
{% for p in s.products.items limit: 8 %}
<a href="{{ p.url | default: '#' }}">{{ p.title }} {{ p.price }}</a>
{% endfor %}