@fluid-app/fluid-cli-theme-dev 0.1.62 → 0.1.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/dist-cli-preview/.vite/manifest.json +225 -225
- package/dist/dist-cli-preview/account-screens.js +1 -1
- package/dist/dist-cli-preview/chunks/{Breadcrumb-CZ-skiUy.js → Breadcrumb-BQKo8hPc.js} +1 -1
- package/dist/dist-cli-preview/chunks/{Combobox-BGiCYa56.js → Combobox-cf11L_T-.js} +4 -4
- package/dist/dist-cli-preview/chunks/{ContactsScreen-vsNgy_gn.js → ContactsScreen-gIiy4u7c.js} +1160 -1155
- package/dist/dist-cli-preview/chunks/{MemberManagementProviders-DMwkh3JW.js → MemberManagementProviders-C1bwc3tB.js} +258 -224
- package/dist/dist-cli-preview/chunks/{MessagingScreen-DzJOwPGH.js → MessagingScreen-B8ZxAE0L.js} +3904 -3927
- package/dist/dist-cli-preview/chunks/{MobileActionSheet-DqVPgFKI.js → MobileActionSheet-DMeTsLlK.js} +100 -100
- package/dist/dist-cli-preview/chunks/{MySiteScreen-BSmWlCKA.js → MySiteScreen-Dtw1rsF9.js} +69 -69
- package/dist/dist-cli-preview/chunks/OrdersScreen-C-b6i5jB.js +9 -0
- package/dist/dist-cli-preview/chunks/{OrdersScreen-CVEl9FnN.js → OrdersScreen-D9HfcTaa.js} +247 -249
- package/dist/dist-cli-preview/chunks/{ProfileScreen-BC5gyJDY.js → ProfileScreen-DwXozaLs.js} +1051 -965
- package/dist/dist-cli-preview/chunks/{SubscriptionsScreen-CFM4Pufg.js → SubscriptionsScreen-CIdYNpWT.js} +3 -3
- package/dist/dist-cli-preview/chunks/{account-screen-runtime-DxhJm6Qd.js → account-screen-runtime-DYMunv0A.js} +2482 -2407
- package/dist/dist-cli-preview/chunks/ar-DaN3xEy4.js +351 -0
- package/dist/dist-cli-preview/chunks/{arrow-left-UFKI-PQf.js → arrow-left-Bd7dJtIV.js} +1 -1
- package/dist/dist-cli-preview/chunks/bg-D3Np0de3.js +351 -0
- package/dist/dist-cli-preview/chunks/bn-BDSehnZ3.js +351 -0
- package/dist/dist-cli-preview/chunks/{cli-preview-DunPfnjU.js → cli-preview-BT_ARKjk.js} +13 -13
- package/dist/dist-cli-preview/chunks/{client.gen-CSTBgfxj.js → client.gen-Ck6Pm2jV.js} +246 -236
- package/dist/dist-cli-preview/chunks/contacts-ChSWj70z.js +4 -0
- package/dist/dist-cli-preview/chunks/{countries-api-adapter-B3usO25w.js → countries-api-adapter-lxSE748x.js} +2 -2
- package/dist/dist-cli-preview/chunks/{create-card-entry-container-DHgsbSyr.js → create-card-entry-container-D6kSC2Aw.js} +20726 -20509
- package/dist/dist-cli-preview/chunks/cs-CrmATj4L.js +351 -0
- package/dist/dist-cli-preview/chunks/da-CmJdErjd.js +351 -0
- package/dist/dist-cli-preview/chunks/de-C0KTNBZq.js +351 -0
- package/dist/dist-cli-preview/chunks/el-BOQs49GU.js +351 -0
- package/dist/dist-cli-preview/chunks/es-Dt3VSQPu.js +351 -0
- package/dist/dist-cli-preview/chunks/fi-B8UZ7eJL.js +351 -0
- package/dist/dist-cli-preview/chunks/fr-BomEiNL0.js +351 -0
- package/dist/dist-cli-preview/chunks/he-CfmnXjsr.js +351 -0
- package/dist/dist-cli-preview/chunks/{hey-api-client-DSvKWGPq.js → hey-api-client-DGRBsZ7Y.js} +3 -3
- package/dist/dist-cli-preview/chunks/hi-CTk5zGoJ.js +351 -0
- package/dist/dist-cli-preview/chunks/hr-DaNToqEm.js +351 -0
- package/dist/dist-cli-preview/chunks/hu-lcAuIwez.js +351 -0
- package/dist/dist-cli-preview/chunks/id-CEThcKZG.js +351 -0
- package/dist/dist-cli-preview/chunks/it-BCRCoDOg.js +351 -0
- package/dist/dist-cli-preview/chunks/ja-BIkFdIfq.js +351 -0
- package/dist/dist-cli-preview/chunks/ko-CD3bk6gv.js +351 -0
- package/dist/dist-cli-preview/chunks/{log-out-CBUq5Hd-.js → log-out-CSPZjXTK.js} +1 -1
- package/dist/dist-cli-preview/chunks/{map-pin-DHfjSyoR.js → map-pin-B2ScoijR.js} +1 -1
- package/dist/dist-cli-preview/chunks/messaging-Bq5UPkvG.js +4 -0
- package/dist/dist-cli-preview/chunks/{mount-account-screen-pc1VISwF.js → mount-account-screen-C_CdoGir.js} +2453 -2392
- package/dist/dist-cli-preview/chunks/ms-DI4mGa5X.js +351 -0
- package/dist/dist-cli-preview/chunks/mysite-DcpuKsuW.js +4 -0
- package/dist/dist-cli-preview/chunks/nl-B-XAD3Ra.js +351 -0
- package/dist/dist-cli-preview/chunks/no-C_NmYGbe.js +351 -0
- package/dist/dist-cli-preview/chunks/orders-B_57xKkq.js +4 -0
- package/dist/dist-cli-preview/chunks/pl-mQRzJZEW.js +351 -0
- package/dist/dist-cli-preview/chunks/{plus-C-lo0pFM.js → plus-CocoE5gC.js} +1 -1
- package/dist/dist-cli-preview/chunks/profile-BfM2KxKe.js +4 -0
- package/dist/dist-cli-preview/chunks/pt-BYMWPjog.js +351 -0
- package/dist/dist-cli-preview/chunks/ro-B98EuFQI.js +351 -0
- package/dist/dist-cli-preview/chunks/ru-DBO9IasC.js +351 -0
- package/dist/dist-cli-preview/chunks/schemas-D94nZk5x.js +5374 -0
- package/dist/dist-cli-preview/chunks/{screen-route-slug-utils-B254xI_J.js → screen-route-slug-utils-BDjSpDoe.js} +1 -1
- package/dist/dist-cli-preview/chunks/{sheet-haptics-context-D2WUiXJm.js → sheet-haptics-context-iOjKcNDj.js} +1 -1
- package/dist/dist-cli-preview/chunks/{sidebar-CMSWZCPS.js → sidebar-Ckzom4Xc.js} +12 -12
- package/dist/dist-cli-preview/chunks/sk-hRvNJfiN.js +351 -0
- package/dist/dist-cli-preview/chunks/{store-api-context-BIdILNWL.js → store-api-context-B96Ip2FB.js} +1 -1
- package/dist/dist-cli-preview/chunks/subscriptions-8VtIA4w9.js +4 -0
- package/dist/dist-cli-preview/chunks/sv-noYtTQbO.js +351 -0
- package/dist/dist-cli-preview/chunks/th-CJCAKsR-.js +351 -0
- package/dist/dist-cli-preview/chunks/tl-Bx8pJ7p7.js +351 -0
- package/dist/dist-cli-preview/chunks/tr-C4tCNiV7.js +351 -0
- package/dist/dist-cli-preview/chunks/{trash-2-Bq4IqH0j.js → trash-2-wVweQZMw.js} +4 -4
- package/dist/dist-cli-preview/chunks/uk-xFjXQDP3.js +351 -0
- package/dist/dist-cli-preview/chunks/{use-optional-store-Csor1Z3z.js → use-optional-store-lZUz976C.js} +2 -2
- package/dist/dist-cli-preview/chunks/use-store-DVmgsDXy.js +103 -0
- package/dist/dist-cli-preview/chunks/{useInfiniteQuery-BpFtQ7DH.js → useInfiniteQuery-Dtzov9uh.js} +1 -1
- package/dist/dist-cli-preview/chunks/{user-BGrQJ1GJ.js → user-B81IrSgp.js} +1 -1
- package/dist/dist-cli-preview/chunks/{users-DBL0isYy.js → users-BXTjoCUz.js} +1 -1
- package/dist/dist-cli-preview/chunks/vi-BAvhRTgb.js +351 -0
- package/dist/dist-cli-preview/chunks/zh_CN-Cyq9zeex.js +351 -0
- package/dist/dist-cli-preview/chunks/zh_TW-DFM5MhMA.js +351 -0
- package/dist/index.mjs +346 -32
- package/dist/index.mjs.map +1 -1
- package/dist/skills/themes-cart-feedback/SKILL.md +9 -14
- package/dist/skills/themes-review/SKILL.md +40 -34
- package/dist/skills/themes-review/references/blocks-vs-sections.md +2 -2
- package/dist/skills/themes-review/references/dead-code.md +1 -1
- package/dist/skills/themes-review/references/dynamism.md +9 -3
- package/dist/skills/themes-review/references/editor-attributes.md +25 -26
- package/dist/skills/themes-review/references/examples.md +3 -4
- package/dist/skills/themes-review/references/fairshare-attributes.md +5 -5
- package/dist/skills/themes-review/references/global-settings.md +56 -67
- package/dist/skills/themes-review/references/liquid-correctness.md +10 -15
- package/dist/skills/themes-review/references/media-tag.md +8 -7
- package/dist/skills/themes-review/references/navigation.md +18 -13
- package/dist/skills/themes-review/references/security-accessibility.md +11 -6
- package/dist/skills/themes-review/references/setting-types.md +136 -77
- package/dist/skills/themes-settings-schema/SKILL.md +15 -2
- package/package.json +2 -2
- package/dist/dist-cli-preview/chunks/OrdersScreen-d4fJE_dm.js +0 -9
- package/dist/dist-cli-preview/chunks/ar-B7NQmkZ1.js +0 -341
- package/dist/dist-cli-preview/chunks/bg-CxfL8rZN.js +0 -341
- package/dist/dist-cli-preview/chunks/bn-CdhHCK--.js +0 -341
- package/dist/dist-cli-preview/chunks/contacts-CpYlEgAq.js +0 -4
- package/dist/dist-cli-preview/chunks/cs-DTe-xGeI.js +0 -341
- package/dist/dist-cli-preview/chunks/da-B8Cl_vkl.js +0 -341
- package/dist/dist-cli-preview/chunks/de-D8cYy8G-.js +0 -341
- package/dist/dist-cli-preview/chunks/el-Chjin2JW.js +0 -341
- package/dist/dist-cli-preview/chunks/es-mp8RwFN4.js +0 -341
- package/dist/dist-cli-preview/chunks/fi-91gcpxoy.js +0 -341
- package/dist/dist-cli-preview/chunks/fr-ekvUCKTH.js +0 -341
- package/dist/dist-cli-preview/chunks/he-B7QiuquT.js +0 -341
- package/dist/dist-cli-preview/chunks/hi-DNNhi7ud.js +0 -341
- package/dist/dist-cli-preview/chunks/hr-CAwxMKX_.js +0 -341
- package/dist/dist-cli-preview/chunks/hu-D_oQwH4Q.js +0 -341
- package/dist/dist-cli-preview/chunks/id-oeAszBoo.js +0 -341
- package/dist/dist-cli-preview/chunks/isAfter-BUGgByEf.js +0 -1414
- package/dist/dist-cli-preview/chunks/it-Dme6GXSG.js +0 -341
- package/dist/dist-cli-preview/chunks/ja-CRnvQNCa.js +0 -341
- package/dist/dist-cli-preview/chunks/ko-8mk8A3_I.js +0 -341
- package/dist/dist-cli-preview/chunks/messaging-DkyH4CPP.js +0 -4
- package/dist/dist-cli-preview/chunks/ms-Bs0GvdOw.js +0 -341
- package/dist/dist-cli-preview/chunks/mysite-wvfITV7T.js +0 -4
- package/dist/dist-cli-preview/chunks/nl-tsI6lLxl.js +0 -341
- package/dist/dist-cli-preview/chunks/no-DMot6qX0.js +0 -341
- package/dist/dist-cli-preview/chunks/orders-c13r499A.js +0 -4
- package/dist/dist-cli-preview/chunks/pl-DmTxONNg.js +0 -341
- package/dist/dist-cli-preview/chunks/profile-CoWo9Sxe.js +0 -4
- package/dist/dist-cli-preview/chunks/pt-DgmwnAV-.js +0 -341
- package/dist/dist-cli-preview/chunks/ro-B4M17oRc.js +0 -341
- package/dist/dist-cli-preview/chunks/ru-BS2gZnbs.js +0 -341
- package/dist/dist-cli-preview/chunks/schemas-9_ZwC5nC.js +0 -3977
- package/dist/dist-cli-preview/chunks/sk-BdDjjHfn.js +0 -341
- package/dist/dist-cli-preview/chunks/subscriptions-BHALQrcU.js +0 -4
- package/dist/dist-cli-preview/chunks/sv-CW7FL5HA.js +0 -341
- package/dist/dist-cli-preview/chunks/th-BYBMKmSh.js +0 -341
- package/dist/dist-cli-preview/chunks/tl-CDigaLsK.js +0 -341
- package/dist/dist-cli-preview/chunks/tr-PxiurNCr.js +0 -341
- package/dist/dist-cli-preview/chunks/uk-B4aYnJ8t.js +0 -341
- package/dist/dist-cli-preview/chunks/vi-BoKSdzH3.js +0 -341
- package/dist/dist-cli-preview/chunks/zh_CN-DrCrXsZC.js +0 -341
- package/dist/dist-cli-preview/chunks/zh_TW-JmyC1W5K.js +0 -341
|
@@ -46,7 +46,7 @@ Everything here depends on exactly one `<script id="fluid-cdn-script">` with `da
|
|
|
46
46
|
<script
|
|
47
47
|
id="fluid-cdn-script"
|
|
48
48
|
src="https://assets.fluid.app/scripts/fluid-sdk/latest/web-widgets/index.js"
|
|
49
|
-
data-fluid-shop="{{
|
|
49
|
+
data-fluid-shop="{{ company.subdomain }}"
|
|
50
50
|
defer
|
|
51
51
|
></script>
|
|
52
52
|
```
|
|
@@ -95,7 +95,7 @@ Add attributes to the same `<script id="fluid-cdn-script">` tag:
|
|
|
95
95
|
<script
|
|
96
96
|
id="fluid-cdn-script"
|
|
97
97
|
src="https://assets.fluid.app/scripts/fluid-sdk/latest/web-widgets/index.js"
|
|
98
|
-
data-fluid-shop="{{
|
|
98
|
+
data-fluid-shop="{{ company.subdomain }}"
|
|
99
99
|
data-fluid-toast="true"
|
|
100
100
|
data-fluid-toast-position="bottom-right"
|
|
101
101
|
defer
|
|
@@ -147,7 +147,7 @@ In web-widgets 0.16.0 and later, any button carrying `data-fluid-add-to-cart` or
|
|
|
147
147
|
```liquid
|
|
148
148
|
<button
|
|
149
149
|
type="button"
|
|
150
|
-
data-fluid-add-to-cart="{{ product.
|
|
150
|
+
data-fluid-add-to-cart="{{ product.selected_or_first_available_variant.id }}"
|
|
151
151
|
data-fluid-loading-text="{{ 'product.adding' | t }}"
|
|
152
152
|
>
|
|
153
153
|
{{ 'product.add_to_cart' | t }}
|
|
@@ -292,29 +292,24 @@ window.addEventListener("CART_OPERATION_ERROR", (e) => {
|
|
|
292
292
|
|
|
293
293
|
### A. Live cart-count badge (product cards, shop grids, navbar)
|
|
294
294
|
|
|
295
|
-
The single most common need
|
|
295
|
+
The single most common need, and it needs no JavaScript. The SDK keeps the text of every element with `id="fluid-cart-count"` in sync with the cart — whenever the cart is created, refreshed, cleared, or its items change from anywhere on the page:
|
|
296
296
|
|
|
297
297
|
```html
|
|
298
|
-
<span id="fluid-cart-count">
|
|
299
|
-
|
|
300
|
-
<script>
|
|
301
|
-
window.addEventListener("CART_OPERATION_SUCCESS", () => {
|
|
302
|
-
const badge = document.getElementById("fluid-cart-count");
|
|
303
|
-
if (badge) badge.textContent = String(window.FairShareSDK?.getCartItemCount() ?? "0");
|
|
304
|
-
});
|
|
305
|
-
</script>
|
|
298
|
+
<span id="fluid-cart-count">0</span>
|
|
306
299
|
```
|
|
307
300
|
|
|
301
|
+
Do not seed it with `{{ cart.item_count }}`: `cart` exists only in `cart_page` templates, so everywhere else it renders empty until the SDK fills it. To drive a different element, listen for the cart state events (`UPDATE_CART_ITEM`, `ADD_TO_CART`, `REMOVE_FROM_CART`) and read `window.FairShareSDK.getCartItemCount()`.
|
|
302
|
+
|
|
308
303
|
### B. Inline "Added ✓" on the exact card clicked (`variantIds` match)
|
|
309
304
|
|
|
310
305
|
When a grid of product cards shares one listener, use `variantIds` to light up only the card the shopper acted on. Reveal a **separate** confirmation element rather than overwriting the button text — the button's own label is managed by `withButtonLoading`, so leave it alone:
|
|
311
306
|
|
|
312
307
|
```html
|
|
313
308
|
{%- comment -%} Each card carries its variant id; a hidden confirmation sits alongside the button {%- endcomment -%}
|
|
314
|
-
<div class="product-card" data-variant-id="{{ product.
|
|
309
|
+
<div class="product-card" data-variant-id="{{ product.selected_or_first_available_variant.id }}">
|
|
315
310
|
<button
|
|
316
311
|
type="button"
|
|
317
|
-
onclick="window.FairShareSDK?.withButtonLoading(this, () => window.FairShareSDK.addCartItems({{ product.
|
|
312
|
+
onclick="window.FairShareSDK?.withButtonLoading(this, () => window.FairShareSDK.addCartItems({{ product.selected_or_first_available_variant.id }}, { quantity: 1 }))"
|
|
318
313
|
>
|
|
319
314
|
{{ 'product.add_to_cart' | t }}
|
|
320
315
|
</button>
|
|
@@ -35,7 +35,7 @@ Don't run end to end silently. **Once in a while — after finishing a section,
|
|
|
35
35
|
|
|
36
36
|
## Where themes actually live
|
|
37
37
|
|
|
38
|
-
Themes are **separate Git repos**, one repo per theme. The reference starter is `git@github.com:fluid-commerce/base-theme.git`. They are not part of
|
|
38
|
+
Themes are **separate Git repos**, one repo per theme. The reference starter is `git@github.com:fluid-commerce/base-theme.git`. They are not part of this monorepo — this monorepo only ships the _tooling_ (CLI + validator) that operates on them.
|
|
39
39
|
|
|
40
40
|
A typical theme repo looks like this (verified against the `base-theme` starter cloned by `fluid theme init`):
|
|
41
41
|
|
|
@@ -152,28 +152,29 @@ If you feel the urge to nest, rename: `assets/icon-social-twitter.svg`, `compone
|
|
|
152
152
|
|
|
153
153
|
Every layout file under `layouts/` **must emit both magic drops** for the engine to inject head content and page content:
|
|
154
154
|
|
|
155
|
-
- `{{ content_for_header }}` — placed inside `<head>`. The engine injects meta tags,
|
|
155
|
+
- `{{ content_for_header }}` — placed inside `<head>`. The engine injects the page `<title>`, meta and Open Graph tags, canonical and `hreflang` links, structured data, the CSRF tag, Fluid's JavaScript globals (`window.fluid_shop`, …), theme stylesheets, and head Global Embeds here. It does **not** load the FairShare SDK — the layout or a Global Embed does.
|
|
156
156
|
- `{{ content_for_layout }}` — placed where the page content goes (inside `<body>`, typically inside the main container). The selected page template renders into this slot.
|
|
157
157
|
|
|
158
158
|
A layout missing either one is broken. Missing `content_for_header` breaks editor mode, analytics, and head-injected assets. Missing `content_for_layout` renders an empty page.
|
|
159
159
|
|
|
160
|
+
Do not write your own `<title>` (or `og:*` tags): `content_for_header` already emits them, so a second one duplicates or overrides Fluid's. `page_title` is not a Fluid variable — it renders empty. Liquid comments are `{% comment %}…{% endcomment %}` or `{% # … %}`; Jinja-style `{# … #}` is printed as literal text.
|
|
161
|
+
|
|
160
162
|
```liquid
|
|
161
163
|
<!doctype html>
|
|
162
|
-
<html lang="{{
|
|
164
|
+
<html lang="{{ localization.language.iso_code | default: 'en' }}">
|
|
163
165
|
<head>
|
|
164
166
|
<meta charset="utf-8">
|
|
165
167
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
166
|
-
<title>{{ page_title }}</title>
|
|
167
168
|
|
|
168
169
|
{{ 'theme.css' | asset_url | stylesheet_tag }}
|
|
169
170
|
|
|
170
|
-
{{ content_for_header }}
|
|
171
|
+
{{ content_for_header }}
|
|
171
172
|
</head>
|
|
172
173
|
<body>
|
|
173
174
|
{% section 'main_navbar' %}
|
|
174
175
|
|
|
175
176
|
<main>
|
|
176
|
-
{{ content_for_layout }}
|
|
177
|
+
{{ content_for_layout }}
|
|
177
178
|
</main>
|
|
178
179
|
|
|
179
180
|
{% section 'main_footer' %}
|
|
@@ -267,7 +268,7 @@ The `fluid theme` command group is how a theme repo syncs with the Fluid API. Th
|
|
|
267
268
|
|
|
268
269
|
| Tag | Meaning | Examples |
|
|
269
270
|
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
270
|
-
| `blocker` | The validator would reject it on `fluid theme push`, OR it renders wrong / breaks a page / exposes user data. Must be fixed before merge. | Invalid `type:` value,
|
|
271
|
+
| `blocker` | The validator would reject it on `fluid theme push`, OR it renders wrong / breaks a page / exposes user data. Must be fixed before merge. | Invalid `type:` value, duplicate `id`, missing `id` (lint misses it, the editor can't save it), missing `type:` on a block, settings shape is object not array, referenced section not on disk, `escape` on a section/block `text`/`textarea` value, unclosed `{% if %}` |
|
|
271
272
|
| `should` | Works today but will hurt later: perf cliff, DRY violation, missing default, missing whitespace control in a hot loop. | `asset_url` inside a `for` loop, 3+ identical `{% render %}` snippets, multiple `product` settings playing the same role, missing `default:` on a user-facing setting |
|
|
272
273
|
| `nit` | Cosmetic, style, or low-impact. Leave one comment, do not block. | Comment style, optional `default` on internal-only setting, naming nits |
|
|
273
274
|
|
|
@@ -288,8 +289,8 @@ Decision rule: if `fluid theme lint --json` would error on it, it's `blocker`. I
|
|
|
288
289
|
|
|
289
290
|
For each entry in `"settings": [...]`:
|
|
290
291
|
|
|
291
|
-
- `id
|
|
292
|
-
- `id` must be unique across all settings in the same
|
|
292
|
+
- `id`, when present, must be non-empty and non-whitespace. The validator does **not** flag a *missing* `id`, but a value-producing setting without one cannot be saved by the editor — treat it as a `blocker` yourself. `type: "header"` settings carry no `id` and use `content:` instead.
|
|
293
|
+
- `id` must be unique across all settings in the same array.
|
|
293
294
|
- `type` is required.
|
|
294
295
|
- `type` must be one of the canonical types — see the [full list below](references/setting-types.md).
|
|
295
296
|
|
|
@@ -308,6 +309,10 @@ For each entry in `"blocks": [...]`:
|
|
|
308
309
|
|
|
309
310
|
- Every `{% section 'name' %}` in a Liquid template must have a matching section file on disk. Missing sections produce errors.
|
|
310
311
|
|
|
312
|
+
### Liquid block tags
|
|
313
|
+
|
|
314
|
+
- Paired block tags (`if`/`endif`, `for`/`endfor`, `case`, `capture`, `unless`, …) must balance. The validator reports an unbalanced tag with its line. The server accepts the upload anyway, and the storefront stops rendering that file correctly, so treat it as a `blocker`.
|
|
315
|
+
|
|
311
316
|
### Template vs section block shape
|
|
312
317
|
|
|
313
318
|
- **Section files** (`sections/{name}/index.liquid`): `blocks` is an **array** `[]`.
|
|
@@ -329,7 +334,7 @@ touches them. Read the matching file when you hit its topic:
|
|
|
329
334
|
- **[Global settings](references/global-settings.md)** — typography/color/spacing tokens, dark mode, `config/settings_schema.json` + `theme.liquid` wiring.
|
|
330
335
|
- **[Blocks vs. sections](references/blocks-vs-sections.md)** — when a setting belongs in a block; settings-panel ergonomics.
|
|
331
336
|
- **[Liquid correctness](references/liquid-correctness.md)** — variable scope, balanced tags, whitespace control, typoed ids.
|
|
332
|
-
- **[Editor attributes](references/editor-attributes.md)** — `
|
|
337
|
+
- **[Editor attributes](references/editor-attributes.md)** — `block.fluid_attributes` (sections are wrapped automatically).
|
|
333
338
|
- **[FairShare attributes](references/fairshare-attributes.md)** — `data-fluid-*` cart / add-to-cart behavioral attributes + the CDN script.
|
|
334
339
|
- **[Performance](references/performance.md)** — `asset_url` in loops, render hygiene.
|
|
335
340
|
- **[Media rendering](references/media-tag.md)** — the `media_tag` filter for responsive, format-negotiated images and video; when raw `<img>`/`<video>` is wrong.
|
|
@@ -347,7 +352,7 @@ eyeballing the text.
|
|
|
347
352
|
|
|
348
353
|
This is one of the most common smells.
|
|
349
354
|
|
|
350
|
-
**If a section or block has two or more singular
|
|
355
|
+
**If a section or block has two or more singular `product` pickers playing the _same role_, collapse them to one `product_list` setting.** For every other resource, collapse to **blocks** instead — see the table below.
|
|
351
356
|
|
|
352
357
|
The test: would a user reasonably want N+1 items in the same role? If yes, it's a list. If the section needs _exactly one_ hero product _and separately_ one upsell product, those are two different roles — keep them singular.
|
|
353
358
|
|
|
@@ -395,18 +400,19 @@ The test: would a user reasonably want N+1 items in the same role? If yes, it's
|
|
|
395
400
|
{%- endfor -%}
|
|
396
401
|
```
|
|
397
402
|
|
|
398
|
-
###
|
|
403
|
+
### Other resources: blocks, not lists
|
|
404
|
+
|
|
405
|
+
`product_list` is the **only** list type Fluid resolves into objects. `collection_list`, `category_list`, `posts_list`, `enrollment_packs_list`, and the rest hand Liquid an array of **IDs**, so a loop that reads `item.title` or `item.url` renders nothing. Do not "fix" repeated singular pickers into those lists.
|
|
399
406
|
|
|
400
|
-
| Singular smell |
|
|
401
|
-
| ------------------------------------------------------- |
|
|
402
|
-
| 2+ `product`
|
|
403
|
-
| 2+ `collection` settings, same role | `
|
|
404
|
-
| 2+ `category` settings, same role | `
|
|
405
|
-
| 2+ `
|
|
406
|
-
| 2+ `
|
|
407
|
-
| 2+ `blog` settings, same role | `blog_list` |
|
|
407
|
+
| Singular smell | Fix |
|
|
408
|
+
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
409
|
+
| 2+ `product` settings, same role | One `product_list` (resolved) |
|
|
410
|
+
| 2+ `collection` settings, same role | One block type with a single `collection` setting; the merchant adds a block per collection |
|
|
411
|
+
| 2+ `category` settings, same role | One block type with a single `category` setting |
|
|
412
|
+
| 2+ `enrollment_pack` settings, same role | One block type with a single `enrollment_pack` setting |
|
|
413
|
+
| 2+ `post` / `blog` settings, same role | One block type with a single `post` setting (still an unresolved ID — look it up in `posts`) |
|
|
408
414
|
|
|
409
|
-
|
|
415
|
+
`product_list` lets the merchant **add or remove** products without a code change, but it does **not** give per-item drag-and-drop reorder — that's a *blocks* feature. If the merchant needs to reorder products, model them as blocks with a `product` setting instead.
|
|
410
416
|
|
|
411
417
|
---
|
|
412
418
|
|
|
@@ -539,7 +545,7 @@ For a fast first pass: `fluid theme lint --json` from the theme repo root.
|
|
|
539
545
|
**Only after explicit approval** (`open the fix PR`, `yes do it`, `apply the fixes`):
|
|
540
546
|
|
|
541
547
|
1. Branch from the PR's head (not main): `git checkout -b fix/<short-slug>`
|
|
542
|
-
2. Apply ONE fix at a time, one commit per finding. Commit messages must follow Conventional Commits (this
|
|
548
|
+
2. Apply ONE fix at a time, one commit per finding. Commit messages must follow Conventional Commits (this monorepo's style, and what most theme repos use):
|
|
543
549
|
|
|
544
550
|
```
|
|
545
551
|
|
|
@@ -621,15 +627,15 @@ When reviewing any theme file, run through this in order. Anything unchecked is
|
|
|
621
627
|
- [ ] No user-visible literal text in markup (use `text`/`textarea`/`richtext`)
|
|
622
628
|
- [ ] No hardcoded colors in styles (use `color` / `color_background` or a global token)
|
|
623
629
|
- [ ] No hardcoded image URLs (use `image_picker` or upload to `assets/`)
|
|
624
|
-
- [ ] Setting/resource images and videos render through `| media_tag` (not hand-rolled `<img>`/`<video>`)
|
|
630
|
+
- [ ] Setting/resource images and videos render through `| media_tag` (not hand-rolled `<img>`/`<video>`); a `media_picker` with `fluid_media_id > 0` renders `<fluid-media-widget>` instead
|
|
625
631
|
- [ ] No external CDN URLs for theme-owned assets
|
|
626
632
|
- [ ] No hardcoded `href` to fixed paths the company might want to change (use `url`)
|
|
627
633
|
|
|
628
634
|
**Global settings (config + layout)**
|
|
629
635
|
|
|
630
|
-
- [ ] `config/settings_schema.json` defines `typography
|
|
631
|
-
- [ ] `layouts/theme.liquid` reads those settings and emits CSS
|
|
632
|
-
- [ ] Sections consume
|
|
636
|
+
- [ ] `config/settings_schema.json` defines `typography` and `color_schema` groups, plus `padding` and `corner_radius` when sections use those controls — the editor builds presets only from these exact group names
|
|
637
|
+
- [ ] `layouts/theme.liquid` reads those settings and emits a `--<setting_id>` CSS variable for each on `:root`
|
|
638
|
+
- [ ] Sections consume those variables rather than re-reading `settings.*` directly
|
|
633
639
|
- [ ] No section-level setting duplicates a global token (per-section overrides are the exception, default `unset`)
|
|
634
640
|
- [ ] Schema is grouped by `name:` (not a flat array)
|
|
635
641
|
- [ ] If dark mode is in schema, it's wired in `theme.liquid` (`@media (prefers-color-scheme: dark)` or `[data-theme="dark"]`)
|
|
@@ -644,13 +650,13 @@ When reviewing any theme file, run through this in order. Anything unchecked is
|
|
|
644
650
|
**Settings ergonomics**
|
|
645
651
|
|
|
646
652
|
- [ ] Section settings panel under ~15 fields, OR repeated variations moved to blocks
|
|
647
|
-
- [ ] No 2+ singular
|
|
653
|
+
- [ ] No 2+ singular `product` pickers playing the same role (collapse to `product_list`); other repeated resource pickers become blocks
|
|
648
654
|
- [ ] No `X_1` / `X_2` / `X_3` parallel-named settings (use blocks)
|
|
649
|
-
- [ ] No
|
|
655
|
+
- [ ] No `products_list` / `products` where resolved products were meant (`product_list` is the only resolved list)
|
|
650
656
|
|
|
651
657
|
**Dead code**
|
|
652
658
|
|
|
653
|
-
- [ ] No section files with zero `{% section 'name' %}` references
|
|
659
|
+
- [ ] No section files with zero `{% section 'name' %}` references in a freshly pulled theme (editor-added sections are saved into page templates)
|
|
654
660
|
- [ ] No component files with zero `{% render 'name' %}` references
|
|
655
661
|
- [ ] No `{% schema %}` `blocks:` declaring a `type:` the template never handles
|
|
656
662
|
- [ ] No `assets/` files with zero references in any `.liquid` or `.css` (greppable from the theme root)
|
|
@@ -661,7 +667,7 @@ When reviewing any theme file, run through this in order. Anything unchecked is
|
|
|
661
667
|
- [ ] No identical `{% render %}` snippet appearing 3+ times
|
|
662
668
|
- [ ] No `asset_url` inside a `for` loop
|
|
663
669
|
- [ ] All `for`/`if` in markup context use `{%-` / `-%}` whitespace control
|
|
664
|
-
- [ ] `{% if %}` / `{% endif %}` (and `for`/`case`/`capture`) tags balance
|
|
670
|
+
- [ ] `{% if %}` / `{% endif %}` (and `for`/`case`/`capture`) tags balance — `fluid theme lint` reports any that don't
|
|
665
671
|
|
|
666
672
|
**Variable scope**
|
|
667
673
|
|
|
@@ -683,9 +689,9 @@ When reviewing any theme file, run through this in order. Anything unchecked is
|
|
|
683
689
|
- [ ] JS tags have `defer` (or a justification for not)
|
|
684
690
|
- [ ] No dead assets under `assets/`
|
|
685
691
|
|
|
686
|
-
**Editor attributes (`
|
|
692
|
+
**Editor attributes (`block.fluid_attributes`)**
|
|
687
693
|
|
|
688
|
-
- [ ]
|
|
694
|
+
- [ ] Sections carry no editor attributes of their own — Fluid wraps each section with `data-fluid-section` automatically; `section.fluid_attributes` does not exist and renders empty
|
|
689
695
|
- [ ] Every block iterated via `{% for block in section.blocks %}` carries `{{ block.fluid_attributes }}` on its own root element
|
|
690
696
|
- [ ] No typos (`fluid_attribute`, `fluid_attr`, `fluidAttributes`)
|
|
691
697
|
- [ ] No hand-rolled `data-fluid-section-*` attributes — always go through the helper
|
|
@@ -699,11 +705,11 @@ When reviewing any theme file, run through this in order. Anything unchecked is
|
|
|
699
705
|
- [ ] `data-fluid-subscription-plan-id` carries a single integer (never comma-separated)
|
|
700
706
|
- [ ] JSON-valued attributes (`bundled-items`, `bundle-selections`) are valid JSON with numeric IDs and quantities
|
|
701
707
|
- [ ] Attributes live on `<button>` or `<a>` (not `<div>` without semantics)
|
|
702
|
-
- [ ] Exactly one `<script id="fluid-cdn-script">` with `data-fluid-shop`
|
|
708
|
+
- [ ] Exactly one `<script id="fluid-cdn-script">` with `data-fluid-shop="{{ company.subdomain }}"` is loaded by the layout (or by a head Global Embed — not both)
|
|
703
709
|
|
|
704
710
|
**Security & a11y**
|
|
705
711
|
|
|
706
|
-
- [ ]
|
|
712
|
+
- [ ] Plain strings escaped (`| escape`): `plaintext` settings, global `settings.*` of type `text` (a plain input in the Theme panel), and resource fields such as `product.title`. HTML-shaped section/block settings — `text`, `textarea`, `richtext`/`rich_text`, `html`/`html_textarea` — render raw and never sit inside an attribute
|
|
707
713
|
- [ ] Images have `alt`; below-the-fold images have `loading="lazy"`
|
|
708
714
|
- [ ] Clickable elements use `<button>` (action) or `<a href>` (nav) — never `<div>`
|
|
709
715
|
|
|
@@ -103,9 +103,9 @@ Rendering:
|
|
|
103
103
|
<div class="features features--cols-{{ section.settings.columns }}">
|
|
104
104
|
{%- for block in section.blocks -%}
|
|
105
105
|
<article class="feature" {{ block.fluid_attributes }}>
|
|
106
|
-
{{ block.settings.icon | media_tag: alt: block.settings.
|
|
106
|
+
{{ block.settings.icon | media_tag: alt: block.settings.icon_alt }}
|
|
107
107
|
<h3>{{ block.settings.title }}</h3>
|
|
108
|
-
<
|
|
108
|
+
<div>{{ block.settings.body }}</div>
|
|
109
109
|
</article>
|
|
110
110
|
{%- endfor -%}
|
|
111
111
|
</div>
|
|
@@ -41,7 +41,7 @@ for d in sections/*/; do
|
|
|
41
41
|
done
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
**False-positive guard:**
|
|
44
|
+
**False-positive guard:** sections a merchant adds in the Page Editor are saved into the page template itself — as a `{% section 'name', id: "…" %}` tag plus an entry in that template's `{% schema %}` `"sections"` map — so they only show up in a theme you have pulled recently. Run `fluid theme pull` before the audit, and grep the page templates' schemas for `"type": "name"` as well. If you cannot pull, drop the severity to `nit` and frame it as "appears unused in templates on disk — confirm in the editor before removing".
|
|
45
45
|
|
|
46
46
|
### Components — unused
|
|
47
47
|
|
|
@@ -51,10 +51,16 @@ Every one of those values — background, padding, heading text, body text, imag
|
|
|
51
51
|
|
|
52
52
|
```liquid
|
|
53
53
|
{%- assign s = section.settings -%}
|
|
54
|
-
|
|
54
|
+
{%- assign pt = s.section_pad.top -%}
|
|
55
|
+
{%- assign pb = s.section_pad.bottom -%}
|
|
56
|
+
<section style="
|
|
57
|
+
background: {{ s.bg_color }};
|
|
58
|
+
padding-top: {% if pt contains 'var' %}{{ pt }}{% else %}{{ pt | default: 0 }}px{% endif %};
|
|
59
|
+
padding-bottom: {% if pb contains 'var' %}{{ pb }}{% else %}{{ pb | default: 0 }}px{% endif %};
|
|
60
|
+
">
|
|
55
61
|
<h2 style="color:{{ s.text_color }};">{{ s.heading | default: 'Our Bestsellers' }}</h2>
|
|
56
|
-
<
|
|
57
|
-
{{ s.banner_image | media_tag: alt: s.
|
|
62
|
+
<div>{{ s.body }}</div>
|
|
63
|
+
{{ s.banner_image | media_tag: alt: s.banner_image_alt }}
|
|
58
64
|
{%- if s.show_cta -%}
|
|
59
65
|
<a href="{{ s.cta_url }}" class="btn">{{ s.cta_label }}</a>
|
|
60
66
|
{%- endif -%}
|
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
## Contents
|
|
6
6
|
|
|
7
7
|
- What `fluid_attributes` emits
|
|
8
|
-
-
|
|
8
|
+
- Sections are wrapped for you
|
|
9
9
|
- Correct usage
|
|
10
10
|
- Findings to surface
|
|
11
11
|
- Examples from the reference theme
|
|
12
12
|
- Quick audit
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Blocks have a Liquid value called `fluid_attributes` that emits the `data-fluid-section-*` attributes the visual editor uses to find, select, click-to-edit, and drag-to-reorder the block. **Without these attributes on the block's root element, the editor cannot interact with the block** — click handlers don't bind, inspector panels don't open, drag-and-drop is dead.
|
|
16
16
|
|
|
17
|
-
> **Note:** Despite the `data-fluid-*` prefix, these are _editor_ attributes — not the same as the runtime [FairShare behavioral attributes](
|
|
17
|
+
> **Note:** Despite the `data-fluid-*` prefix, these are _editor_ attributes — not the same as the runtime [FairShare behavioral attributes](fairshare-attributes.md) (which wire up cart, checkout, enrollment behavior). Keep them straight: `block.fluid_attributes` is emitted by the _theme engine_ in editor mode; FairShare attributes are _hand-written_ HTML attributes that the FairShare runtime SDK reads.
|
|
18
18
|
|
|
19
19
|
These attributes are only populated in editor preview mode; in production rendering, the drops emit an empty string. So a missing call has zero visible effect at runtime — the bug only surfaces in the editor. That's why reviewers must catch it.
|
|
20
20
|
|
|
@@ -32,25 +32,26 @@ In editor mode, `{{ block.fluid_attributes }}` expands to the full set of `data-
|
|
|
32
32
|
></div>
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
###
|
|
35
|
+
### Sections are wrapped for you
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
| -------------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------- |
|
|
39
|
-
| `{{ section.fluid_attributes }}` | Inside a section file (`sections/{name}/index.liquid`) | On the **root** wrapper element of the section |
|
|
40
|
-
| `{{ block.fluid_attributes }}` | Inside a `{% for block in section.blocks %}` loop, or when a block context is passed to a render | On the **root** wrapper element of _each_ block |
|
|
37
|
+
Fluid renders every section inside a wrapper element carrying `data-fluid-section="<id>"` and `data-fluid-section-type="<name>"`, in every render, editor or not. **A section needs no editor attributes of its own.** `section.fluid_attributes` does not exist — `{{ section.fluid_attributes }}` renders an empty string, so adding it changes nothing.
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
| Value | Where it's available | Where to emit it |
|
|
40
|
+
| ------------------------------ | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------- |
|
|
41
|
+
| `{{ block.fluid_attributes }}` | Inside a `{% for block in section.blocks %}` loop, a standalone block template, or a render that receives the block | On the **root** wrapper element of _each_ block |
|
|
42
|
+
|
|
43
|
+
**This is the only one.** There is no `section.fluid_attributes`, `component.fluid_attributes`, dropzone drop, slot drop, or inline-editing helper. Components are pure partials — they don't have editor presence on their own; the block that _renders_ them carries the attributes.
|
|
43
44
|
|
|
44
45
|
### Correct usage
|
|
45
46
|
|
|
46
47
|
```liquid
|
|
47
|
-
{%- comment -%} Section root:
|
|
48
|
-
<section class="featured-products"
|
|
48
|
+
{%- comment -%} Section root: no attributes needed — Fluid wraps the section {%- endcomment -%}
|
|
49
|
+
<section class="featured-products">
|
|
49
50
|
{%- for block in section.blocks -%}
|
|
50
51
|
{%- case block.type -%}
|
|
51
52
|
{%- when 'heading' -%}
|
|
52
53
|
<h2 class="featured-products__heading" {{ block.fluid_attributes }}>
|
|
53
|
-
{{ block.settings.text
|
|
54
|
+
{{ block.settings.text }}
|
|
54
55
|
</h2>
|
|
55
56
|
|
|
56
57
|
{%- when 'product_card' -%}
|
|
@@ -76,19 +77,19 @@ When a block's content is fully delegated to a component, the standard pattern i
|
|
|
76
77
|
|
|
77
78
|
| You see | Severity | Fix |
|
|
78
79
|
| ---------------------------------------------------------------------------------------------------------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
79
|
-
| Section file
|
|
80
|
+
| Section file emits `{{ section.fluid_attributes }}` | `nit` | Remove it. It renders nothing; Fluid already wraps the section with `data-fluid-section`. |
|
|
80
81
|
| Section uses `{% for block in section.blocks %}` and the block's root element has no `{{ block.fluid_attributes }}` | `blocker` | Each block's outer element must carry its own `block.fluid_attributes`. Without it the editor can't click-to-edit individual blocks. |
|
|
81
|
-
| Typo: `{{
|
|
82
|
-
| Typo: `{{ block.fluid_attr }}` / `{{
|
|
83
|
-
| Attributes applied on a child element instead of the root
|
|
84
|
-
| Same `{{
|
|
85
|
-
| Hardcoded
|
|
82
|
+
| Typo: `{{ block.fluid_attribute }}` (singular) | `blocker` | The name is `fluid_attributes` (plural). The singular form renders empty. |
|
|
83
|
+
| Typo: `{{ block.fluid_attr }}` / `{{ block.fluidAttributes }}` / `{{ block.fluid_attribute_set }}` | `blocker` | Only `fluid_attributes` exists. |
|
|
84
|
+
| Attributes applied on a child element instead of the block's root | `blocker` | Move to the outermost element of the block. The editor walks ancestors looking for the closest match; placing it on a child means the editor picks up _that_ element as the block bounds, not the real one. |
|
|
85
|
+
| Same `{{ block.fluid_attributes }}` applied to multiple elements | `should` | Apply it once — to the root. Duplicates render the same `data-*` attributes twice in the DOM. |
|
|
86
|
+
| Hardcoded editor attributes (`data-fluid-section-block-id`, `data-fluid-section="{{ section.id }}"`) instead of the helper | `blocker` | Remove them. Use `{{ block.fluid_attributes }}` for blocks; sections are wrapped automatically. Hand-rolling drops most of the set. |
|
|
86
87
|
| Block-aware component (renders inside `{% for block %}`) has no `attr` parameter to receive `block.fluid_attributes` | `should` | Add an `attr` argument to the component's signature and apply it on the component's root element. |
|
|
87
|
-
| Conditional render without protecting against `nil`: `<div {{
|
|
88
|
+
| Conditional render without protecting against `nil`: `<div {{ header_block.fluid_attributes }}>` where the block may be absent | `should` (rarely `blocker`) | Wrap: `{% if header_block %}{{ header_block.fluid_attributes }}{% endif %}`. Only relevant where the value can legitimately be nil. |
|
|
88
89
|
|
|
89
90
|
### Examples from the reference theme
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
Block root with attributes — `sections/main_product/index.liquid:49-51` in the reference fluid theme:
|
|
92
93
|
|
|
93
94
|
```liquid
|
|
94
95
|
<div
|
|
@@ -133,10 +134,8 @@ Multiple blocks each carrying their own attrs — `sections/main_category/index.
|
|
|
133
134
|
From the theme repo root:
|
|
134
135
|
|
|
135
136
|
```bash
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
grep -q 'section\.fluid_attributes' "$f" || echo "MISSING section.fluid_attributes $f"
|
|
139
|
-
done
|
|
137
|
+
# Leftover section.fluid_attributes (renders nothing; remove)
|
|
138
|
+
grep -rn 'section\.fluid_attributes' sections/
|
|
140
139
|
|
|
141
140
|
# Sections that iterate blocks but never emit block.fluid_attributes
|
|
142
141
|
for f in sections/*/index.liquid; do
|
|
@@ -150,8 +149,8 @@ done
|
|
|
150
149
|
# Common typos
|
|
151
150
|
grep -rE 'fluid_attribute[^s]|fluidAttributes|fluid_attribute_set' sections/ components/
|
|
152
151
|
|
|
153
|
-
# Hardcoded
|
|
154
|
-
grep -rE 'data-fluid-section-id|data-fluid-section-block-id|data-fluid-parent-section-type' sections/ components/ \
|
|
152
|
+
# Hardcoded editor attributes (likely should use the helper)
|
|
153
|
+
grep -rE 'data-fluid-section=|data-fluid-section-id|data-fluid-section-block-id|data-fluid-parent-section-type' sections/ components/ \
|
|
155
154
|
| grep -v 'fluid_attributes'
|
|
156
155
|
```
|
|
157
156
|
|
|
@@ -47,16 +47,15 @@
|
|
|
47
47
|
```
|
|
48
48
|
## Theme review — featured_products.liquid
|
|
49
49
|
|
|
50
|
-
**
|
|
50
|
+
**7 findings** — 2 blocker, 4 should, 1 nit
|
|
51
51
|
|
|
52
52
|
Reproduce locally with `fluid theme lint --json`.
|
|
53
53
|
|
|
54
54
|
| Severity | Line | Finding |
|
|
55
55
|
|---|---|---|
|
|
56
56
|
| blocker | 9 | Invalid type `text_area` — use `textarea`. Validator rejects. |
|
|
57
|
-
| blocker |
|
|
58
|
-
|
|
|
59
|
-
| should | 10 | Type `image` is valid but `image_picker` is clearer for new code |
|
|
57
|
+
| blocker | 18 | `{% for product in products %}` — `products` is Fluid's global product collection, not the products picked in this section, so the loop renders the store's catalog above the picked cards. Remove it; render the picked products through the `product_list` fix below (`section.settings.products`). |
|
|
58
|
+
| should | 10 | Type `image` works in sections only (blocks reject it) — use `image_picker` |
|
|
60
59
|
| should | 5-8 | Four `product` settings, same role — collapse to one `product_list` with `limit: 4` |
|
|
61
60
|
| should | 19 | `asset_url` resolved inside loop — hoist above |
|
|
62
61
|
| should | 16-21 | Missing whitespace control on `{% for %}` / `{% endfor %}` |
|
|
@@ -17,7 +17,7 @@ These are the **runtime behavioral attributes** consumed by the FairShare web-wi
|
|
|
17
17
|
|
|
18
18
|
> These attributes **trigger** cart mutations. The **result** — confirming the change with a toast, spinning the button, or reacting in custom UI via the `CART_OPERATION_SUCCESS` / `CART_OPERATION_ERROR` events — is the `themes-cart-feedback` skill (installed as a sibling: [`../../themes-cart-feedback/SKILL.md`](../../themes-cart-feedback/SKILL.md), when present). Reach for it when reviewing product cards, shop grids, cart sections, or enrollment flows that add to cart.
|
|
19
19
|
|
|
20
|
-
**This is different from `
|
|
20
|
+
**This is different from `block.fluid_attributes`** (see [Editor attributes](editor-attributes.md)). The editor attributes are emitted by the theme engine in editor mode. These are static HTML attributes you write into the template that the runtime SDK scans for on `DOMContentLoaded`.
|
|
21
21
|
|
|
22
22
|
### Naming convention
|
|
23
23
|
|
|
@@ -29,12 +29,12 @@ All FairShare runtime attributes are `data-fluid-*` (kebab-case, `data-` prefix)
|
|
|
29
29
|
<script
|
|
30
30
|
id="fluid-cdn-script"
|
|
31
31
|
src="https://assets.fluid.app/scripts/fluid-sdk/latest/web-widgets/index.js"
|
|
32
|
-
data-fluid-shop="{{
|
|
32
|
+
data-fluid-shop="{{ company.subdomain }}"
|
|
33
33
|
defer
|
|
34
34
|
></script>
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
`data-fluid-shop` is required. Optional script-level attributes: `data-fluid-api-base-url`, `data-fluid-country` (2-letter ISO), `data-fluid-language` (2–3 letter), `data-fluid-rep-id`, `data-share-guid`, `data-debug`.
|
|
37
|
+
`data-fluid-shop` is required — use `{{ company.subdomain }}`. There is no `shop` global in Fluid, so `{{ shop.handle }}` (a Shopify habit) renders an empty value and the SDK cannot resolve the store. `content_for_header` sets `window.fluid_shop` but does **not** load the SDK: add this tag to the layout once, unless the company already injects it as a head Global Embed — never both. Optional script-level attributes: `data-fluid-api-base-url`, `data-fluid-country` (2-letter ISO), `data-fluid-language` (2–3 letter), `data-fluid-rep-id`, `data-share-guid`, `data-debug`.
|
|
38
38
|
|
|
39
39
|
### The attributes
|
|
40
40
|
|
|
@@ -55,10 +55,10 @@ All FairShare runtime attributes are `data-fluid-*` (kebab-case, `data-` prefix)
|
|
|
55
55
|
|
|
56
56
|
```html
|
|
57
57
|
{%- comment -%} Open cart drawer {%- endcomment -%}
|
|
58
|
-
<button data-fluid-cart="open">Cart
|
|
58
|
+
<button data-fluid-cart="open">Cart</button>
|
|
59
59
|
|
|
60
60
|
{%- comment -%} Add one item {%- endcomment -%}
|
|
61
|
-
<button data-fluid-add-to-cart="{{ product.
|
|
61
|
+
<button data-fluid-add-to-cart="{{ product.selected_or_first_available_variant.id }}">
|
|
62
62
|
Add to cart
|
|
63
63
|
</button>
|
|
64
64
|
|