[[storefront.blocks]] in a manifest. The block
language is published now, in
@tassyir/storefront-blocks,
so you can write and test blocks before the opening. This guide describes it;
details may still change until the opening.
A block is a piece of a store page your app provides: a rating under the
product’s price, a size chart, a trust badge, a delivery estimate. The
merchant places it in the store builder, like any section. Your app ships the
block’s template and stylesheet with its version. Tassyir checks them at
upload, then renders the template on its own servers for every page view,
with the data your app stored.
A block runs no JavaScript and has no form. The store page holds the order
form, with the buyer’s name, phone and address, so a block can only ever be
markup and styles: Tassyir escapes every value, rebuilds the HTML from an
allowlist, and scopes the stylesheet to the block.
Declare a block
Intassyir.app.toml, with the write_storefront scope:
target:productfor the product page (the template readsproduct), orany(the default) for any page but checkout.data: the app data keys the block reads, as<owner>.<key>. Each must be declared withstorefrontvisibility. Owners:product(targetproductonly),store,business. Order and customer data never reach a store page.settings: what the merchant sets in the builder. Types:string,text,boolean,integer,number,select,color,image,url,product,collection. A value that no longer fits the declaration falls back to the default when the block renders.
The template
A block template is a.liquid file in a subset of Liquid. If you know
Shopify’s Liquid, the syntax is the same: {{ value | filter: argument }}
outputs, {% tag %} logic, {{- and -}} to trim whitespace.
Objects
Nothing about the buyer or the cart is ever available. Any value answers
size, first and last; a record answers its own keys only. A name that is
not one of these objects or a variable the template assigns is an error at
upload.
Tags
Not available, each refused at upload with a fix:
include, render,
section, layout, block, raw, liquid, echo, form, schema,
style, stylesheet, javascript, paginate, tablerow, cycle,
increment, decrement. A block is one self-contained template.
Filters
Not available, each refused with a fix:
raw (there is no unescaped output),
date (a block has no clock: store the date as text), json, img_url (use
image_url), money_with_currency (use money), link_to, script_tag,
stylesheet_tag, hashes and base64.
The HTML a block may use
The rendered HTML is parsed and written out again from an allowlist. What the template writes outside the allowlist is an error at upload; what data brings in is removed at render.- Elements:
a,abbr,article,aside,b,bdi,bdo,blockquote,br,caption,cite,code,dd,del,details,dfn,div,dl,dt,em,figcaption,figure,footer,h2toh6,header,hr,i,img,ins,kbd,li,mark,meter,ol,p,pre,progress,q,s,samp,section,small,span,strong,sub,summary,sup, the table elements,time,u,ul,var,wbr. Noh1: the page has its own. - SVG icons:
svgwithg,path,circle,ellipse,line,polygon,polyline,rectand a text-onlytitleinside it, with their shape and paint attributes (fill="currentColor"follows the text colour). - Attributes:
class,title,lang,dir,role,hidden,translate, a set ofaria-*, and each element’s own (hrefandtargeton links;src,alt,width,heighton images;colspan,datetime,open, …). Noidorname: an element with an id becomes a global variable of the page, which can break the store’s own scripts. Nostyle(use the stylesheet), nodata-*, noon…handler. - Links:
https://addresses (Tassyir addsrel="nofollow noopener noreferrer"), paths on the store (/collections/all) and fragments. - Images: an
<img>takes itssrcfrom{{ … | image_url: <width> }}only, so every image comes from Tassyir’s CDN. Images load lazily. - Text: bidirectional override and isolate characters, zero-width and control characters are removed from values; Arabic’s marks and joiners stay.
<{{ x }}>), nor fill an unquoted value.
The stylesheet
The optional.css file is scoped to the block’s section, so its selectors
cannot reach the rest of the page, and the block is drawn inside its section’s
box. At upload it is refused when it holds:
- an at-rule other than
@media,@supportsand@keyframes <plain name>(no@import,@font-face,@layer), or a nested rule; - a
url()outsidehttps://cdn.tassyir.io/, or a function that loads something (image-set(),src()); positionother thanstatic,relative,absoluteorsticky, written as the word itself (neverfixed, never throughvar());behavior,-moz-binding,expression();- a backslash escape outside a quoted string,
<, a bidi or invisible character.
Other
--st-* variables may change without notice and are refused. Each
color setting is published on the block as --block-<key>, underscores as
hyphens: accent_color is var(--block-accent-color).
Limits
A template is at most 16 KB, a stylesheet 8 KB, a version at most 20 blocks and 100 KB of block files. A render reads at most 32 KB of app data and writes at most 32 KB of HTML. Afor loop runs at most 50 times, and at most 3 loops
nest. Render work is counted in steps, never timed: every tag, output, loop
iteration and condition is a step, and every filter or comparison costs one
more per 256 characters it touches, up to 20,000 steps. A block over a limit
renders nothing: the page shows without it.
Test a block on your machine
The package runs the upload check and renders previews with sample data: the store “Dar Amina”, a product with a price, your settings’ defaults, and the app data you pass.validateBlock is the check the API runs on upload: it compiles the template
against the declaration, checks the stylesheet, and renders the block with
sample data in Arabic, French and English and with no app data, since a
block must render before your app has stored anything.
Errors
Every error has a stablecode, a source (template, stylesheet or
render), a 1-based line and column (0 for a render limit), a message
and a fix. On upload, the API refuses a version with an invalid block with
BLOCK_TEMPLATE_INVALID, naming the block and listing these errors. The
package’s reference lists
every code.
The ones you will meet first: