Static files
Theme assets
Theme assets are the templates, snippets, sections and such which make up the core of your theme. Most of these are copied directly from your project into the final theme with only minor changes by the compiler.
Theme assets are stored in static/ and follow roughly the same structure as a typical Shopify theme:
Directoryassets
- …
Directorysrc
- …
Directorystatic
Directoryassets Uncompiled assets (i.e. images)
- my-font.woff2
- ...
Directoryconfig Theme settings
- settings_data.json
- settings_schema.json
Directorylayout Layouts
- theme.liquid
Directorylocales Translations
- en.default.json
- ...
Directorysections Sections
- product-main.liquid
- ...
Directorysnippets Snippets
- contact-form.liquid
- ...
Directorytemplates Templates
- index.json
- product.json
- ...
- package.json
- salvo.config.js
- ...
Files inside static/assets/, static/snippets/, and static/sections/ can be nested to provide better organization.
Compiler macros
The framework supports various macros in .liquid files including in static files and in component templates.
{! component !}
Renders a component template snippet.
- Input
- Compiled Liquid
- Output HTML
html{! component 'SayHello', name: 'World' !}
html{! component 'SayHello', name: 'World' !}
html{% render 'component_SayHello', name: 'World' %}
html{% render 'component_SayHello', name: 'World' %}
html<say-hello name="World">...</say-hello>
html<say-hello name="World">...</say-hello>
{! icon !}
Renders a SVG icon snippet.
- Input
- Compiled Liquid
- Output HTML
html{! icon 'cart-lg', class: 'icon-cart' !}
html{! icon 'cart-lg', class: 'icon-cart' !}
html<svg class="icon-cart" viewBox="0 0 50 50">...</svg>
html<svg class="icon-cart" viewBox="0 0 50 50">...</svg>
html<svg class="icon-cart" viewBox="0 0 50 50">...</svg>
html<svg class="icon-cart" viewBox="0 0 50 50">...</svg>
{{{ ... }}} (escape)
Shorthand for {{ ... | escape }}, see escape filter liquid docs.
- Input Liquid
- Compiled Liquid
- Output HTML
html<img src="..." alt="{{{ image.alt }}}">
html<img src="..." alt="{{{ image.alt }}}">
html<img src="..." alt="{{ image.alt | escape }}">
html<img src="..." alt="{{ image.alt | escape }}">
html<img src="..." alt="An image described "pretty alright".">
html<img src="..." alt="An image described "pretty alright".">
{# ... #} (comment)
Shorthand for {% comment %}...{% endcomment %}, see comment tag liquid docs.
- Input Liquid
- Compiled Liquid
- Output HTML
html<span>Blue</span>{#<span>Red</span>#}<span>Green</span>
html<span>Blue</span>{#<span>Red</span>#}<span>Green</span>
html<span>Blue</span>{% comment %}<span>Red</span>{% endcomment %}<span>Green</span>
html<span>Blue</span>{% comment %}<span>Red</span>{% endcomment %}<span>Green</span>
html<span>Blue</span><span>Green</span>
html<span>Blue</span><span>Green</span>
Deprecated macros
{! schema_blocks !}
Schema blocks allowed you to re-use common section settings schema between settings files. This is deprecated and should not be used in new themes. Existing themes making use of this macro should be updated to avoid it by simply duplicating the referenced 'schema block' into each instance where the macro was used.
Escaping
The SALVO-TS compiler tries to promote proper handling of unsafe values in templates by enforcing the escaping of values in Liquid templates. This stance is the default in most sane templating languages, but differs from Liquid's usually behavior of outputting all values raw by default (unless you explicitly use the |escape filter.)
Here is basic example to illustrate the issue:
- Liquid
- Output HTML
html<div>Size:</div>{% for value in size_option.values %}<label><input type="radio" value="{{ value }}" name="size">{{ value }}</label>{% endfor %}
html<div>Size:</div>{% for value in size_option.values %}<label><input type="radio" value="{{ value }}" name="size">{{ value }}</label>{% endfor %}
html<div>Size:</div><label><input type="radio" value="Small (1')" name="size">Small (1')</label><label>// The value attribute is incorrect because the " character in the value has broken it<input type="radio" value="Medium (1'6")" name="size">Medium (1'6")</label><label><input type="radio" value="Large (2')" name="size">Large (2')</label>
html<div>Size:</div><label><input type="radio" value="Small (1')" name="size">Small (1')</label><label>// The value attribute is incorrect because the " character in the value has broken it<input type="radio" value="Medium (1'6")" name="size">Medium (1'6")</label><label><input type="radio" value="Large (2')" name="size">Large (2')</label>
Escaping the values avoids the issue:
- Liquid
- Output HTML
html<div>Size:</div>{% for value in size_option.values %}<label><input type="radio" value="{{ value|escape }}" name="size">{{ value|escape }}</label>{% endfor %}
html<div>Size:</div>{% for value in size_option.values %}<label><input type="radio" value="{{ value|escape }}" name="size">{{ value|escape }}</label>{% endfor %}
html<div>Size:</div><label><input type="radio" value="Small (1')" name="size">Small (1')</label><label>// Quote character properly escaped, so will not break any HTML output<input type="radio" value="Medium (1'6")" name="size">Medium (1'6&")</label><label><input type="radio" value="Large (2')" name="size">Large (2')</label>
html<div>Size:</div><label><input type="radio" value="Small (1')" name="size">Small (1')</label><label>// Quote character properly escaped, so will not break any HTML output<input type="radio" value="Medium (1'6")" name="size">Medium (1'6&")</label><label><input type="radio" value="Large (2')" name="size">Large (2')</label>
This is a minor example - it only gets worse when you start including values which unintentionally contain HTML code, user input, and values which need to be later parsed handled as JSON.
Any sane framework would default to doing the safe thing by default here, but Liquid is not sane. Therefore, the compiler will highlight possible issues and encourage you to add |escape and/or |json filters to the values as appropriate.