Liquid reference
Themes use standard Liquid (tags like if, for, assign, capture, render, and filters like escape, default, date, json…) plus the filters and tags below, and the platform's objects. Always escape text you output in HTML unless it's HTML on purpose (rich text settings, page.content).
Objects
Every object a theme can read (firm, settings, customer, page, section, block and the rest) is documented, property by property, in the Objects reference.
Filters
| Filter | Example | Result |
|---|---|---|
asset_url |
{{ 'theme.css' | asset_url }} |
URL of a file in assets/ (cached for a year, changes with every version) |
stylesheet_tag |
{{ 'theme.css' | asset_url | stylesheet_tag }} |
<link rel="stylesheet"> |
script_tag |
{{ 'theme.js' | asset_url | script_tag }} |
<script defer> with the page's nonce |
t |
{{ 'team.members_count' | t: count: 12 }} |
Text from locales/en.default.json, with {{ count }} filled in |
money |
{{ 49 | money }} |
$49.00 in the firm's currency |
font_face |
{{ settings.body_font | font_face }} |
<link> loading a font_picker font |
font_family |
{{ settings.body_font | font_family }} |
The CSS font-family stack |
color_readable |
{{ settings.color_primary | color_readable }} |
#0f172a or #fff, whichever reads better on the colour |
video_embed_url |
{{ section.settings.video | video_embed_url }} |
The player URL of a YouTube or Vimeo link, or empty |
local_date |
{{ 'now' | local_date: 'F Y' }} |
A date in the firm's timezone (PHP date format) |
handleize |
{{ 'Hello World' | handleize }} |
hello-world |
json |
{{ firm.name | json }} |
JSON, safe inside <script> |
image_url |
{{ section.settings.image | image_url: width: 800 }} |
The image URL (images are already web-sized) |
Tags
| Tag | Use |
|---|---|
{% schema %}…{% endschema %} |
A section's settings, blocks and presets (JSON). |
{% sections 'header-group' %} |
Render a section group. |
{% section 'name' %} |
Render a section with its default settings. |
{% render 'snippet', var: value %} |
Render a snippet. |
{% stylesheet %}…{% endstylesheet %} |
CSS for a section, collected into one <style> in <head>. |
{% javascript %}…{% endjavascript %} |
JavaScript for a section, added to the page with its nonce. |
{% form 'type', class: '…' %}…{% endform %} |
A form posting to the platform (below). |
Forms
{% form %} outputs a <form> with the right action, method and security token. Inside it, a form object has posted_successfully, errors (by field) and values (what was entered, to fill the fields again).
| Type | Fields |
|---|---|
contact |
name, email, message |
intake |
name, email, phone, practice_area_id, answers[{key}] for each of intake_form.questions, and the hidden spam trap company_website |
customer_login |
email, password, remember |
recover_customer_password |
email |
reset_customer_password |
token and email (hidden, from reset_password), password, password_confirmation |
activate_customer_password |
token (hidden, from invitation), password, password_confirmation, accept_terms, marketing_consent (optional) |
resend_verification |
(none) |
customer_logout |
(none) |
{% form 'contact', class: 'grid gap-4' %}
{% if form.posted_successfully %}<p role="status">{{ 'contact.success' | t }}</p>{% endif %}
<label for="contact-email">Email</label>
<input id="contact-email" type="email" name="email" value="{{ form.values.email | escape }}" required>
{% if form.errors.email %}<p>{{ form.errors.email | escape }}</p>{% endif %}
…
<button type="submit">Send</button>
{% endform %}
Counsel's sections/main-*.liquid files show every form in full.
Submit states
Every {% form %} gets a submit state from the platform, with nothing to add to your theme:
- While the form is sent, the
<form>hasaria-busy="true", and the button that sent it gets the classis-loading,aria-disabled="true"and a small spinner (an::beforein the button's text colour). - A second submit while the first is on its way is ignored, so a double click can't send the form twice.
- The button isn't
disabled, so itsnameandvalueare still posted. - Coming back to the page with the browser's Back button resets the form.
Style the state with form[aria-busy='true'] .is-loading (for example, to replace the spinner, set content: none on its ::before). To leave a form out, for instance one your own script submits with fetch(), add data_no_submit_state: true; underscores become dashes, so the form gets data-no-submit-state:
{% form 'contact', data_no_submit_state: true %}…{% endform %}
If your script calls event.preventDefault() on the submit event, the platform leaves that submit alone.
Text and translations
Put your theme's text in locales/en.default.json and output it with t:
{ "team": { "members_count": "{{ count }} lawyers" } }
Text the firm should be able to change belongs in settings instead.
Still need help?
Send a request to the Mattersimple team and we'll reply by email.