@augeo/smelt 1.2.2

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.
Files changed (152) hide show
  1. package/.claude/settings.json +11 -0
  2. package/.github/workflows/verify.yml +64 -0
  3. package/.gitmodules +3 -0
  4. package/.prettierignore +25 -0
  5. package/.prettierrc.cjs +9 -0
  6. package/.zed/settings.json +21 -0
  7. package/AGENTS.md +232 -0
  8. package/LICENSE +21 -0
  9. package/README.md +266 -0
  10. package/biome.json +58 -0
  11. package/dist/cli.d.mts +1 -0
  12. package/dist/cli.mjs +350 -0
  13. package/dist/schema.d.mts +265 -0
  14. package/dist/schema.mjs +21 -0
  15. package/docs/TESTING.md +293 -0
  16. package/docs/assets-plan.md +197 -0
  17. package/docs/build-spec.md +466 -0
  18. package/docs/library-conversion-plan.md +419 -0
  19. package/example/.gitattributes +7 -0
  20. package/example/.shopifyignore +28 -0
  21. package/example/.theme-check.yml +7 -0
  22. package/example/blocks/_built--sections--hero--blocks--feature.liquid +52 -0
  23. package/example/config/settings_schema.json +10 -0
  24. package/example/layout/theme.liquid +25 -0
  25. package/example/locales/en.default.json +1 -0
  26. package/example/package-lock.json +51 -0
  27. package/example/package.json +20 -0
  28. package/example/sections/built--sections--hero.liquid +83 -0
  29. package/example/snippets/built--components--button.liquid +38 -0
  30. package/example/snippets/built--components--card.liquid +33 -0
  31. package/example/src/components/button/button.css +13 -0
  32. package/example/src/components/card/card.css +16 -0
  33. package/example/src/components/card/card.liquid +9 -0
  34. package/example/src/sections/hero/blocks/feature/feature.css +11 -0
  35. package/example/src/sections/hero/blocks/feature/feature.liquid +9 -0
  36. package/example/src/sections/hero/blocks/feature/feature.schema.ts +14 -0
  37. package/example/src/sections/hero/hero.css +15 -0
  38. package/example/src/sections/hero/hero.liquid +16 -0
  39. package/example/src/sections/hero/hero.schema.ts +26 -0
  40. package/example/src/sections/hero/hero.test.ts +43 -0
  41. package/example/src/utilities/labels.ts +5 -0
  42. package/example/templates/index.liquid +1 -0
  43. package/example/tsconfig.json +10 -0
  44. package/example/vitest.config.ts +6 -0
  45. package/lib/build/build.test.ts +475 -0
  46. package/lib/build/build.ts +314 -0
  47. package/lib/build/command.ts +27 -0
  48. package/lib/build/index.ts +1 -0
  49. package/lib/cli.ts +17 -0
  50. package/lib/dev/command.ts +25 -0
  51. package/lib/dev/index.ts +1 -0
  52. package/lib/dev/watch.ts +52 -0
  53. package/lib/resolver.test.ts +275 -0
  54. package/lib/resolver.ts +156 -0
  55. package/lib/schema.ts +37 -0
  56. package/package.json +59 -0
  57. package/scripts/codegen-schema.ts +66 -0
  58. package/src/components/button/button.css +13 -0
  59. package/src/components/button/button.liquid +5 -0
  60. package/src/components/button/button.ts +5 -0
  61. package/src/tsconfig.json +10 -0
  62. package/tests/example.test.ts +101 -0
  63. package/tsconfig.json +20 -0
  64. package/tsdown.config.ts +14 -0
  65. package/vendor/theme-liquid-docs/.gitattributes +10 -0
  66. package/vendor/theme-liquid-docs/.github/CODEOWNERS +1 -0
  67. package/vendor/theme-liquid-docs/.github/CODE_OF_CONDUCT.md +73 -0
  68. package/vendor/theme-liquid-docs/.github/ISSUE_TEMPLATE/bug_report.md +17 -0
  69. package/vendor/theme-liquid-docs/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  70. package/vendor/theme-liquid-docs/.github/dependabot.yaml +6 -0
  71. package/vendor/theme-liquid-docs/.github/workflows/ci.yml +33 -0
  72. package/vendor/theme-liquid-docs/.github/workflows/cla.yml +27 -0
  73. package/vendor/theme-liquid-docs/.github/workflows/shopify-dev-preview-automation.yml +86 -0
  74. package/vendor/theme-liquid-docs/.github/workflows/update-latest.yml +56 -0
  75. package/vendor/theme-liquid-docs/.prettierrc.json +16 -0
  76. package/vendor/theme-liquid-docs/.vscode/settings.json +28 -0
  77. package/vendor/theme-liquid-docs/LICENSE.md +7 -0
  78. package/vendor/theme-liquid-docs/README.md +48 -0
  79. package/vendor/theme-liquid-docs/ai/claude/CLAUDE.md +1485 -0
  80. package/vendor/theme-liquid-docs/ai/cursor/rules/assets.mdc +15 -0
  81. package/vendor/theme-liquid-docs/ai/cursor/rules/blocks.mdc +339 -0
  82. package/vendor/theme-liquid-docs/ai/cursor/rules/examples/block-example-group.mdc +103 -0
  83. package/vendor/theme-liquid-docs/ai/cursor/rules/examples/block-example-text.mdc +59 -0
  84. package/vendor/theme-liquid-docs/ai/cursor/rules/examples/section-example.mdc +61 -0
  85. package/vendor/theme-liquid-docs/ai/cursor/rules/examples/snippet-example.mdc +72 -0
  86. package/vendor/theme-liquid-docs/ai/cursor/rules/liquid.mdc +837 -0
  87. package/vendor/theme-liquid-docs/ai/cursor/rules/locales.mdc +100 -0
  88. package/vendor/theme-liquid-docs/ai/cursor/rules/localization.mdc +67 -0
  89. package/vendor/theme-liquid-docs/ai/cursor/rules/mcp.mdc +2 -0
  90. package/vendor/theme-liquid-docs/ai/cursor/rules/schemas.mdc +184 -0
  91. package/vendor/theme-liquid-docs/ai/cursor/rules/sections.mdc +84 -0
  92. package/vendor/theme-liquid-docs/ai/cursor/rules/settings-schema.mdc +51 -0
  93. package/vendor/theme-liquid-docs/ai/cursor/rules/snippets.mdc +119 -0
  94. package/vendor/theme-liquid-docs/ai/github/copilot-instructions.md +1485 -0
  95. package/vendor/theme-liquid-docs/ai/liquid.mdc +638 -0
  96. package/vendor/theme-liquid-docs/data/filters.json +6148 -0
  97. package/vendor/theme-liquid-docs/data/latest.json +2 -0
  98. package/vendor/theme-liquid-docs/data/objects.json +20594 -0
  99. package/vendor/theme-liquid-docs/data/shopify_system_translations.json +2586 -0
  100. package/vendor/theme-liquid-docs/data/tags.json +1276 -0
  101. package/vendor/theme-liquid-docs/package.json +20 -0
  102. package/vendor/theme-liquid-docs/schemas/manifest_schema.json +31 -0
  103. package/vendor/theme-liquid-docs/schemas/manifest_theme.json +19 -0
  104. package/vendor/theme-liquid-docs/schemas/manifest_theme_app_extension.json +10 -0
  105. package/vendor/theme-liquid-docs/schemas/theme/app_block_entry.json +13 -0
  106. package/vendor/theme-liquid-docs/schemas/theme/default_setting_values.json +24 -0
  107. package/vendor/theme-liquid-docs/schemas/theme/local_block_entry.json +25 -0
  108. package/vendor/theme-liquid-docs/schemas/theme/preset.json +72 -0
  109. package/vendor/theme-liquid-docs/schemas/theme/preset_blocks.json +91 -0
  110. package/vendor/theme-liquid-docs/schemas/theme/section.json +208 -0
  111. package/vendor/theme-liquid-docs/schemas/theme/setting.json +1413 -0
  112. package/vendor/theme-liquid-docs/schemas/theme/settings.json +10 -0
  113. package/vendor/theme-liquid-docs/schemas/theme/targetted_block_entry.json +15 -0
  114. package/vendor/theme-liquid-docs/schemas/theme/theme_block.json +91 -0
  115. package/vendor/theme-liquid-docs/schemas/theme/theme_block_entry.json +14 -0
  116. package/vendor/theme-liquid-docs/schemas/theme/theme_settings.json +83 -0
  117. package/vendor/theme-liquid-docs/schemas/theme/translations.json +63 -0
  118. package/vendor/theme-liquid-docs/schemas/update/update_extension_schema_v1.json +186 -0
  119. package/vendor/theme-liquid-docs/tests/fixtures/section-nested-blocks.json +18 -0
  120. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-1.json +90 -0
  121. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-2.json +201 -0
  122. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-3.json +29 -0
  123. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-4.json +315 -0
  124. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-5.json +114 -0
  125. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-6.json +63 -0
  126. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-conditional-settings.json +145 -0
  127. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-preset-blocks-as-hash.json +60 -0
  128. package/vendor/theme-liquid-docs/tests/fixtures/section-schema-static-block-preset.json +76 -0
  129. package/vendor/theme-liquid-docs/tests/fixtures/section-settings.json +34 -0
  130. package/vendor/theme-liquid-docs/tests/fixtures/theme-block-1.json +234 -0
  131. package/vendor/theme-liquid-docs/tests/fixtures/theme-block-2.json +253 -0
  132. package/vendor/theme-liquid-docs/tests/fixtures/theme-block-basics.json +48 -0
  133. package/vendor/theme-liquid-docs/tests/fixtures/theme-block-conditional-settings.json +202 -0
  134. package/vendor/theme-liquid-docs/tests/fixtures/theme-block-presets-as-hash.json +50 -0
  135. package/vendor/theme-liquid-docs/tests/fixtures/theme-block-settings.json +34 -0
  136. package/vendor/theme-liquid-docs/tests/fixtures/theme-settings-all-settings.json +313 -0
  137. package/vendor/theme-liquid-docs/tests/fixtures/theme-settings-dawn.json +1469 -0
  138. package/vendor/theme-liquid-docs/tests/fixtures/theme-settings-metadata.json +10 -0
  139. package/vendor/theme-liquid-docs/tests/fixtures/translations-1.json +14 -0
  140. package/vendor/theme-liquid-docs/tests/section.spec.ts +367 -0
  141. package/vendor/theme-liquid-docs/tests/test-constants.ts +58 -0
  142. package/vendor/theme-liquid-docs/tests/test-helpers.ts +104 -0
  143. package/vendor/theme-liquid-docs/tests/theme-settings/color_palette.spec.ts +184 -0
  144. package/vendor/theme-liquid-docs/tests/theme-settings/color_scheme_group.spec.ts +143 -0
  145. package/vendor/theme-liquid-docs/tests/theme-settings/general.spec.ts +192 -0
  146. package/vendor/theme-liquid-docs/tests/theme-settings/metaobject.spec.ts +94 -0
  147. package/vendor/theme-liquid-docs/tests/theme-settings/resource_list.spec.ts +58 -0
  148. package/vendor/theme-liquid-docs/tests/theme-settings/theme-metadata.spec.ts +59 -0
  149. package/vendor/theme-liquid-docs/tests/theme_block.spec.ts +266 -0
  150. package/vendor/theme-liquid-docs/tests/translations_schema.spec.ts +31 -0
  151. package/vendor/theme-liquid-docs/yarn.lock +543 -0
  152. package/vitest.config.ts +7 -0
@@ -0,0 +1,638 @@
1
+ ---
2
+ description: Liquid development rules for Shopify themes
3
+ globs:
4
+ alwaysApply: true
5
+ ---
6
+
7
+ <liquid-development>
8
+ <liquid-rules>
9
+ valid_filters = [
10
+
11
+ // array
12
+ { name: "compact", usage: "array | compact" },
13
+ { name: "concat", usage: "array | concat: array" },
14
+ { name: "find", usage: "array | find: string, string" },
15
+ { name: "find_index", usage: "array | find_index: string, string" },
16
+ { name: "first", usage: "array | first" },
17
+ { name: "has", usage: "array | has: string, string" },
18
+ { name: "join", usage: "array | join" },
19
+ { name: "last", usage: "array | last" },
20
+ { name: "map", usage: "array | map: string" },
21
+ { name: "reject", usage: "array | reject: string, string" },
22
+ { name: "reverse", usage: "array | reverse" },
23
+ { name: "size", usage: "variable | size" },
24
+ { name: "sort", usage: "array | sort" },
25
+ { name: "sort_natural", usage: "array | sort_natural" },
26
+ { name: "sum", usage: "array | sum" },
27
+ { name: "uniq", usage: "array | uniq" },
28
+ { name: "where", usage: "array | where: string, string" },
29
+
30
+ // cart
31
+ { name: "item_count_for_variant", usage: "cart | item_count_for_variant: {variant_id}" },
32
+ { name: "line_items_for", usage: "cart | line_items_for: object" },
33
+
34
+ // collection
35
+ { name: "link_to_type", usage: "string | link_to_type" },
36
+ { name: "link_to_vendor", usage: "string | link_to_vendor" },
37
+ { name: "sort_by", usage: "string | sort_by: string" },
38
+ { name: "url_for_type", usage: "string | url_for_type" },
39
+ { name: "url_for_vendor", usage: "string | url_for_vendor" },
40
+ { name: "within", usage: "string | within: collection" },
41
+ { name: "highlight_active_tag", usage: "string | highlight_active_tag" },
42
+
43
+ // color
44
+ { name: "brightness_difference", usage: "string | brightness_difference: string" },
45
+ { name: "color_brightness", usage: "string | color_brightness" },
46
+ { name: "color_contrast", usage: "string | color_contrast: string" },
47
+ { name: "color_darken", usage: "string | color_darken: number" },
48
+ { name: "color_desaturate", usage: "string | color_desaturate: number" },
49
+ { name: "color_difference", usage: "string | color_difference: string" },
50
+ { name: "color_extract", usage: "string | color_extract: string" },
51
+ { name: "color_lighten", usage: "string | color_lighten: number" },
52
+ { name: "color_mix", usage: "string | color_mix: string, number" },
53
+ { name: "color_modify", usage: "string | color_modify: string, number" },
54
+ { name: "color_saturate", usage: "string | color_saturate: number" },
55
+ { name: "color_to_hex", usage: "string | color_to_hex" },
56
+ { name: "color_to_hsl", usage: "string | color_to_hsl" },
57
+ { name: "color_to_oklch", usage: "string | color_to_oklch" },
58
+ { name: "color_to_rgb", usage: "string | color_to_rgb" },
59
+ { name: "hex_to_rgba", usage: "string | hex_to_rgba" },
60
+
61
+ // customer
62
+ { name: "customer_login_link", usage: "string | customer_login_link" },
63
+ { name: "customer_logout_link", usage: "string | customer_logout_link" },
64
+ { name: "customer_register_link", usage: "string | customer_register_link" },
65
+ { name: "avatar", usage: "customer | avatar" },
66
+ { name: "login_button", usage: "shop | login_button" },
67
+
68
+ // date
69
+ { name: "date", usage: "date | date: string" },
70
+
71
+ // default
72
+ { name: "default_errors", usage: "string | default_errors" },
73
+ { name: "default", usage: "variable | default: variable" },
74
+ { name: "default_pagination", usage: "paginate | default_pagination" },
75
+
76
+ // font
77
+ { name: "font_face", usage: "font | font_face" },
78
+ { name: "font_modify", usage: "font | font_modify: string, string" },
79
+ { name: "font_url", usage: "font | font_url" },
80
+
81
+ // format
82
+ { name: "date", usage: "string | date: string" },
83
+ { name: "json", usage: "variable | json" },
84
+ { name: "structured_data", usage: "variable | structured_data" },
85
+ { name: "unit_price_with_measurement", usage: "number | unit_price_with_measurement: unit_price_measurement" },
86
+ { name: "weight_with_unit", usage: "number | weight_with_unit" },
87
+
88
+ // hosted_file
89
+ { name: "asset_img_url", usage: "string | asset_img_url" },
90
+ { name: "asset_url", usage: "string | asset_url" },
91
+ { name: "file_img_url", usage: "string | file_img_url" },
92
+ { name: "file_url", usage: "string | file_url" },
93
+ { name: "global_asset_url", usage: "string | global_asset_url" },
94
+ { name: "shopify_asset_url", usage: "string | shopify_asset_url" },
95
+
96
+ // html
97
+ { name: "class_list", usage: "settings.layout | class_list" },
98
+ { name: "time_tag", usage: "string | time_tag: string" },
99
+ { name: "inline_asset_content", usage: "asset_name | inline_asset_content" },
100
+ { name: "highlight", usage: "string | highlight: string" },
101
+ { name: "link_to", usage: "string | link_to: string" },
102
+ { name: "placeholder_svg_tag", usage: "string | placeholder_svg_tag" },
103
+ { name: "preload_tag", usage: "string | preload_tag: as: string" },
104
+ { name: "script_tag", usage: "string | script_tag" },
105
+ { name: "stylesheet_tag", usage: "string | stylesheet_tag" },
106
+
107
+ // localization
108
+ { name: "currency_selector", usage: "form | currency_selector" },
109
+ { name: "translate", usage: "string | t" },
110
+ { name: "format_address", usage: "address | format_address" },
111
+
112
+ // math
113
+ { name: "abs", usage: "number | abs" },
114
+ { name: "at_least", usage: "number | at_least" },
115
+ { name: "at_most", usage: "number | at_most" },
116
+ { name: "ceil", usage: "number | ceil" },
117
+ { name: "divided_by", usage: "number | divided_by: number" },
118
+ { name: "floor", usage: "number | floor" },
119
+ { name: "minus", usage: "number | minus: number" },
120
+ { name: "modulo", usage: "number | modulo: number" },
121
+ { name: "plus", usage: "number | plus: number" },
122
+ { name: "round", usage: "number | round" },
123
+ { name: "times", usage: "number | times: number" },
124
+
125
+ // media
126
+ { name: "external_video_tag", usage: "variable | external_video_tag" },
127
+ { name: "external_video_url", usage: "media | external_video_url: attribute: string" },
128
+ { name: "image_tag", usage: "string | image_tag" },
129
+ { name: "media_tag", usage: "media | media_tag" },
130
+ { name: "model_viewer_tag", usage: "media | model_viewer_tag" },
131
+ { name: "video_tag", usage: "media | video_tag" },
132
+ { name: "article_img_url", usage: "variable | article_img_url" },
133
+ { name: "collection_img_url", usage: "variable | collection_img_url" },
134
+ { name: "image_url", usage: "variable | image_url: width: number, height: number" },
135
+ { name: "img_tag", usage: "string | img_tag" },
136
+ { name: "img_url", usage: "variable | img_url" },
137
+ { name: "product_img_url", usage: "variable | product_img_url" },
138
+
139
+ // metafield
140
+ { name: "metafield_tag", usage: "metafield | metafield_tag" },
141
+ { name: "metafield_text", usage: "metafield | metafield_text" },
142
+
143
+ // money
144
+ { name: "money", usage: "number | money" },
145
+ { name: "money_with_currency", usage: "number | money_with_currency" },
146
+ { name: "money_without_currency", usage: "number | money_without_currency" },
147
+ { name: "money_without_trailing_zeros", usage: "number | money_without_trailing_zeros" },
148
+
149
+ // payment
150
+ { name: "payment_button", usage: "form | payment_button" },
151
+ { name: "payment_terms", usage: "form | payment_terms" },
152
+ { name: "payment_type_img_url", usage: "string | payment_type_img_url" },
153
+ { name: "payment_type_svg_tag", usage: "string | payment_type_svg_tag" },
154
+
155
+ // string
156
+ { name: "hmac_sha1", usage: "string | hmac_sha1: string" },
157
+ { name: "hmac_sha256", usage: "string | hmac_sha256: string" },
158
+ { name: "md5", usage: "string | md5" },
159
+ { name: "sha1", usage: "string | sha1: string" },
160
+ { name: "sha256", usage: "string | sha256: string" },
161
+ { name: "append", usage: "string | append: string" },
162
+ { name: "base64_decode", usage: "string | base64_decode" },
163
+ { name: "base64_encode", usage: "string | base64_encode" },
164
+ { name: "base64_url_safe_decode", usage: "string | base64_url_safe_decode" },
165
+ { name: "base64_url_safe_encode", usage: "string | base64_url_safe_encode" },
166
+ { name: "capitalize", usage: "string | capitalize" },
167
+ { name: "downcase", usage: "string | downcase" },
168
+ { name: "escape", usage: "string | escape" },
169
+ { name: "escape_once", usage: "string | escape_once" },
170
+ { name: "lstrip", usage: "string | lstrip" },
171
+ { name: "newline_to_br", usage: "string | newline_to_br" },
172
+ { name: "prepend", usage: "string | prepend: string" },
173
+ { name: "remove", usage: "string | remove: string" },
174
+ { name: "remove_first", usage: "string | remove_first: string" },
175
+ { name: "remove_last", usage: "string | remove_last: string" },
176
+ { name: "replace", usage: "string | replace: string, string" },
177
+ { name: "replace_first", usage: "string | replace_first: string, string" },
178
+ { name: "replace_last", usage: "string | replace_last: string, string" },
179
+ { name: "rstrip", usage: "string | rstrip" },
180
+ { name: "slice", usage: "string | slice" },
181
+ { name: "split", usage: "string | split: string" },
182
+ { name: "strip", usage: "string | strip" },
183
+ { name: "strip_html", usage: "string | strip_html" },
184
+ { name: "strip_newlines", usage: "string | strip_newlines" },
185
+ { name: "truncate", usage: "string | truncate: number" },
186
+ { name: "truncatewords", usage: "string | truncatewords: number" },
187
+ { name: "upcase", usage: "string | upcase" },
188
+ { name: "url_decode", usage: "string | url_decode" },
189
+ { name: "url_encode", usage: "string | url_encode" },
190
+ { name: "camelize", usage: "string | camelize" },
191
+ { name: "handleize", usage: "string | handleize" },
192
+ { name: "url_escape", usage: "string | url_escape" },
193
+ { name: "url_param_escape", usage: "string | url_param_escape" },
194
+ { name: "pluralize", usage: "number | pluralize: string, string" },
195
+
196
+ // tag
197
+ { name: "link_to_add_tag", usage: "string | link_to_add_tag" },
198
+ { name: "link_to_remove_tag", usage: "string | link_to_remove_tag" },
199
+ { name: "link_to_tag", usage: "string | link_to_tag" },
200
+
201
+ ]
202
+
203
+ valid_tags = [
204
+ "content_for",
205
+ "form",
206
+ "layout",
207
+ "assign",
208
+ "break",
209
+ "capture",
210
+ "case",
211
+ "comment",
212
+ "continue",
213
+ "cycle",
214
+ "decrement",
215
+ "doc",
216
+ "echo",
217
+ "for",
218
+ "if",
219
+ "include",
220
+ "increment",
221
+ "raw",
222
+ "render",
223
+ "tablerow",
224
+ "unless",
225
+ "paginate",
226
+ "javascript",
227
+ "section",
228
+ "stylesheet",
229
+ "sections",
230
+ "style",
231
+ "else",
232
+ "else",
233
+ "liquid",
234
+ ]
235
+
236
+ valid_objects = [
237
+ "collections",
238
+ "pages",
239
+ "all_products",
240
+ "articles",
241
+ "blogs",
242
+ "cart",
243
+ "closest",
244
+ "content_for_header",
245
+ "customer",
246
+ "images",
247
+ "linklists",
248
+ "localization",
249
+ "metaobjects",
250
+ "request",
251
+ "routes",
252
+ "shop",
253
+ "theme",
254
+ "settings",
255
+ "template",
256
+ "additional_checkout_buttons",
257
+ "all_country_option_tags",
258
+ "canonical_url",
259
+ "content_for_additional_checkout_buttons",
260
+ "content_for_index",
261
+ "content_for_layout",
262
+ "country_option_tags",
263
+ "current_page",
264
+ "handle",
265
+ "page_description",
266
+ "page_image",
267
+ "page_title",
268
+ "powered_by_link",
269
+ "scripts",
270
+ ]
271
+
272
+ validation_rules = {
273
+ syntax: {
274
+ - Use {% liquid %} for multiline code
275
+ - Use {% # comments %} for inline comments
276
+ - Never invent new filters, tags, or objects
277
+ - Follow proper tag closing order
278
+ - Use proper object dot notation
279
+ - Respect object scope and availability
280
+ },
281
+
282
+ theme_structure: {
283
+ - Place files in appropriate directories
284
+ - Follow naming conventions
285
+ - Respect template hierarchy
286
+ - Maintain proper section/block structure
287
+ - Use appropriate schema settings
288
+ }
289
+ }
290
+
291
+ ∀ liquid_code ∈ theme:
292
+ validate_syntax(liquid_code) ∧
293
+ validate_filters(liquid_code.filters ∈ valid_filters) ∧
294
+ validate_tags(liquid_code.tags ∈ valid_tags) ∧
295
+ validate_objects(liquid_code.objects ∈ valid_objects) ∧
296
+ validate_structure(liquid_code.location ∈ theme_structure)
297
+
298
+ </liquid-rules>
299
+ <theme-architecture>
300
+ folder_structure = {
301
+ sections: theme_sections(),
302
+ blocks: theme_blocks(),
303
+ layout: theme_layout(),
304
+ snippets: theme_snippets(),
305
+ config: theme_config(),
306
+ assets: theme_assets(),
307
+ locales: theme_locales(),
308
+ templates: theme_templates(),
309
+ templates/customers: theme_templates/customers(),
310
+ templates/metaobject: theme_templates/metaobject()
311
+ }
312
+ theme_sections = {
313
+ - Liquid files that define customizable sections of a page
314
+ - They include blocks and settings defined via a schema, allowing merchants to modify them in the theme editor
315
+ - Should occupy the full width of the page container and be self-contained layout units
316
+ - Can be added to any JSON template and reordered via the theme editor
317
+ - Must include a {% schema %} tag to define settings and presets
318
+ - Examples: hero banners, product grids, testimonials, featured collections
319
+ }
320
+ theme_blocks = {
321
+ - Configurable elements within sections that can be added, removed, or reordered
322
+ - They are defined with a schema tag for merchant customization in the theme editor
323
+ - Allow merchants to build dynamic content without code changes
324
+ - Each block has a type and can contain settings for text, images, groups, links, etc.
325
+ - Limited to specific block types defined in the section's schema
326
+ - Blocks can be nested within other blocks to create hierarchical content structures
327
+ - Examples: individual testimonials, slides in a carousel, feature items
328
+ }
329
+ theme_layout = {
330
+ - Defines the structure for repeated content such as headers and footers, wrapping other template files
331
+ - It's the frame that holds the page together, but it's not the content
332
+ - Contains the HTML document structure (head, body tags)
333
+ - Includes global elements like navigation, cart drawer, and footer
334
+ - Typically includes global CSS/JS assets and meta tags or render a snippet that do that
335
+ }
336
+ theme_snippets = {
337
+ - Reusable code fragments included in templates, sections, and layouts via the render tag
338
+ - Ideal for logic that needs to be reused but not directly edited in the theme editor
339
+ - Can accept parameters when rendered for dynamic behavior
340
+ - Perfect for repetitive UI components like product cards, buttons, or form elements
341
+ - Help maintain DRY (Don't Repeat Yourself) principles in theme development
342
+ - Examples: product-card.liquid, icon.liquid, price.liquid
343
+ }
344
+ theme_config = {
345
+ - Holds settings data and schema for theme customization options, accessible through the Admin theme editor
346
+ - Stores global theme context like typography, colors, spacing, and branding
347
+ - Contains theme-wide settings that affect multiple pages (global variables)
348
+ - Includes presets for different theme variations or demo content
349
+ - Settings are accessible via settings object in all Liquid files
350
+ - Examples: brand colors, font choices, layout options, feature toggles
351
+ }
352
+ theme_assets = {
353
+ - Contains static files such as CSS, JavaScript, and images referenced via asset_url filter
354
+ - Includes compiled stylesheets, JavaScript bundles, and media files
355
+ - Supports asset optimization and minification for performance
356
+ - Images should be optimized and include responsive variants when possible
357
+ - Examples: theme.css, theme.js, logo.png, icon sprites
358
+ }
359
+ theme_locales = {
360
+ - Stores translation files for localizing theme editor and storefront content
361
+ - Organized by language code (en.default.json, fr.json, etc.)
362
+ - Contains translations for static text, labels, and theme editor strings
363
+ - Enables multi-language support and proper internationalization
364
+ - Accessed via translation filters like {{ 'key' | t }} in Liquid
365
+ - Should cover all user-facing text for complete localization
366
+ }
367
+ theme_templates = {
368
+ - JSON files that specify which sections appear on each page type (e.g., product, collection, blog)
369
+ - They are wrapped by layout files for consistent header/footer content
370
+ - Templates can be Liquid files as well, but JSON is preferred as best practice for flexibility
371
+ - Define the page structure and section ordering for different page types
372
+ - Allow merchants to customize page layouts without code changes
373
+ - Support alternate templates for A/B testing or different page variants
374
+ }
375
+ theme_templates/customers = {
376
+ - Templates for customer-related pages such as login, register, account overview, and order history
377
+ - Handle customer authentication flows and account management interfaces
378
+ - Include forms for login, registration, password reset, and profile updates
379
+ - Must follow Shopify's customer data handling and security requirements
380
+ - Examples: login.json, register.liquid, account.json, order.liquid
381
+ }
382
+ theme_templates/metaobject = {
383
+ - Templates for rendering custom content types defined as metaobjects in Shopify Admin
384
+ - Enable custom content structures beyond standard product/collection/page types
385
+ - Allow merchants to create structured content like team members, locations, or custom landing pages
386
+ - Must handle dynamic field rendering based on metaobject definition
387
+ - Examples: team-member.json, location.liquid, custom-page.json
388
+ }
389
+ ∀ file ∈ theme:
390
+ validate(file.location) ∈ folder_structure;
391
+
392
+ </theme-architecture>
393
+ <ux-principles>
394
+ <translations>
395
+ - Keep every piece of text in the theme translated.
396
+ - Update the locale files with sensible keys and text.
397
+ - Just add english text, not other languages, as we have translators on staff who handle other languages
398
+ </translations>
399
+ <settings>
400
+ <general_guidance>
401
+ Keep it simple, clear, and non-repetitive.
402
+
403
+ - The setting type can provide context that the setting label doesn't need to provide. Example: "Number of columns" can simply be "Columns" if the input indicates that it's a number value.
404
+ - Assume all settings to be device-agnostic, with graceful scaling between breakpoints. Only mention mobile or desktop if there is a unique setting required.
405
+ - Use common shorthand where it makes sense. Example: Max/Min to mean Maximum and Minimum. Caveat: ensure these values are translated/localized correctly
406
+ - Help text: Minimize use as much as possible. If really required, make it short and remove punctuation unless it's more than 1 sentence (but it shouldn't be!)
407
+ </general_guidance>
408
+
409
+ <information_architecture>
410
+ <ordering>
411
+ The order of theme settings greatly impacts the merchant's ability to understand and configure the section/block.
412
+
413
+ - List settings to reflect the order of elements they control in the preview. Top to bottom, left to right, background to foreground.
414
+ - List resource pickers first, if they're needed, followed by customization settings. Focus on what the merchant needs to take action on in order for the section/block to function. Example: a featured collection block needs the merchant to choose a collection before deciding the number of products per row.
415
+ - List settings in order of visual impact, example: Number of products per row should come before the product card settings.
416
+ </ordering>
417
+
418
+ <groupings>
419
+ Consider grouping settings under a heading if there are more than 1 related setting. List ungrouped settings at the top of the section/block.
420
+
421
+ Common groupings:
422
+
423
+ - Layout
424
+ - Typography
425
+ - Colors
426
+ - Padding
427
+ </groupings>
428
+
429
+ <naming>
430
+ Remove word duplication in the heading and nested labels. When a word appears in a heading (e.g. "Color"), it should not be repeated in nested setting labels or help text. The hierarchy of information provides sufficient context.
431
+ </naming>
432
+
433
+ <conditional>
434
+ Use conditional settings when it:
435
+
436
+ - simplifies decision-making for merchants via progressive disclosure
437
+ - avoids duplication of settings
438
+ - avoids visual clutter and reduces cognitive load
439
+
440
+ Conditional settings should appear in the information architecture wherever they're most relevant. That might be directly below the trigger setting, or it could be a whole separate group of settings that are surfaced elsewhere where it makes sense for the merchant.
441
+
442
+ Tradeoffs and considerations of conditional settings:
443
+
444
+ - They hide functionality/options that help merchants decide how style their website, so be judicious in what concepts you tie together. For example, don't make a Product card's "Swatch display" setting conditional on a "Quick buy" setting. They are both related to variant selection, but they serve different purposes.
445
+ - Limit conditions to 2 levels deep to avoid complex logic (up for discussion!)
446
+ - Even when not shown, a conditional setting's value is evaluated in the Liquid code. Code defensively, never assume a theme setting's value is nil.
447
+ </conditional>
448
+
449
+ <input_type>
450
+ **Checkbox**: Treat checkbox as an on/off switch. Avoid using verb-based labels, example: use "Language selector" and not "Enable language selector". The presence of the verb may inadvertently suggest the direction to toggle to enable or disable it.
451
+
452
+ **Select**: Keep select option labels as short as possible so they can be dynamically displayed as segmented controls
453
+ </input_type>
454
+ </information_architecture>
455
+ </settings>
456
+
457
+ <server_side_rendering>
458
+ Storefronts are to be rendered server-side with Liquid as a first principle. As opposed to client-side JavaScript.
459
+
460
+ When using JavaScript to render part of the page, fetch the new HTML from the server wherever possible.
461
+ <optimistic_ui>
462
+ This is the exception to the rule of server-side rendering
463
+
464
+ "Optimistic UI" is the idea that we can update part of the UI before the server response is received in the name of **perceived performance**.
465
+
466
+ <criteria>
467
+ Key factors to consider when deciding whether to use optimistic UI:
468
+
469
+ 1. You are updating a **small** portion of the UI on the client (with JavaScript) before the server response is received.
470
+ 2. The API request has a high degree of certainty of being successful.
471
+
472
+ Examples of appropriate use cases:
473
+
474
+ When filtering a collection page, we can update the a list of applied filters client-side as a Buyer chooses them, i.e. "Color: Red" or "Size: Medium". However, we do not know how many products will be returned that match the filters, so we can't update the product grid or a count of products.
475
+
476
+ When a Buyer attempts to add an item to their cart, we can update the cart item count client-side. Assuming our product form's "add to cart" button is already checking the item's availability, we can have a reasonably high degree of certainty that the item will be added to the cart (API request is successful). However, we do not know what the new cart total will be, nor do we know what the line items will look like, so we can't update those in a cart drawer without waiting for the server response.
477
+ </criteria>
478
+ </optimistic_ui>
479
+ </server_side_rendering>
480
+ </ux-principles>
481
+ <html>
482
+ <structure>
483
+ - Use semantic HTML
484
+ - Prefer `<details>` and `<summary>` over JS for show/hide
485
+ - Use `CamelCase` for IDs. Append `-{{ block.id }}` or `-{{ section.id }}`
486
+ </structure>
487
+
488
+ <accessibility>
489
+ - Make interactive elements focusable with `tabindex="0"`
490
+ - Only use `tabindex="0"` - avoid hijacking tab flow
491
+ </accessibility>
492
+
493
+ </html>
494
+ <css>
495
+ <specificity>
496
+ - Never use IDs as selectors for styles
497
+ - Avoid element selectors
498
+ - Avoid !important (comment why if absolutely necessary)
499
+ - Use 0 1 0 specificity (single .class selector)
500
+ - Maximum 0 4 0 specificity for parent/child relationships
501
+ - Keep selectors simple and readable
502
+ - Use pseudo selectors sparingly (:has, :where, :nth-child, et )
503
+ </specificity>
504
+
505
+ <variables>
506
+ - Use CSS variables to reduce redundancy
507
+ - Hardcode values as variables first (e.g. --touch-target-size: 45px)
508
+ - Never hardcode colors, use color schemes
509
+ - Scope variables to components unless global
510
+ - Global variables go in :root (in may live in a file like snippets/theme-styles-variables.liquid)
511
+ - Scoped variables can reference global variables
512
+ </variables>
513
+
514
+ <scoping>
515
+ - Use {% stylesheet %} tags in sections, blocks, and snippets
516
+ - Reset CSS variables inline with style attributes for settings
517
+ - Avoid {% style %} tags with block/section ID selectors
518
+ - Use variables to reduce redundancy
519
+ </scoping>
520
+
521
+ <bem>
522
+ - Block: component name
523
+ - Element: block__element
524
+ - Modifier: block--modifier, block__element--modifier
525
+ - Use dashes to separate words
526
+ </bem>
527
+
528
+ <media-queries>
529
+ - Mobile first (min-width queries)
530
+ - Use screen for all media queries
531
+ </media-queries>
532
+
533
+ <nesting>
534
+ - No & operator
535
+ - Never nest beyond first level
536
+ - Exceptions: media queries, parent-child with multiple states
537
+ - Keep nesting simple
538
+ </nesting>
539
+
540
+ </css>
541
+ <javascript>
542
+ <general_principles>
543
+ - Use zero external dependencies
544
+ - Use native browser features over JS ("popover", "details")
545
+ - Never use "var"
546
+ - Use "const" over "let" - avoid mutation
547
+ - Use "for (const thing of things)" over "things.forEach()"
548
+ - Add new lines before blocks with "{" and "}"
549
+ </general_principles>
550
+
551
+ <file_structure>
552
+ - Group scripts by feature area
553
+ - Co-locate related classes (e.g. "collection.js" contains all collection page classes)
554
+ </file_structure>
555
+
556
+ <modules>
557
+ - Use the module pattern to avoid global scope pollution
558
+
559
+ <privacy_and_instance_methods>
560
+ - Keep public APIs minimal
561
+ - Prefix private methods with "#"
562
+ - Don't use instance methods for functions that don't need the class instance
563
+
564
+ ```
565
+ class MyClass {
566
+ constructor() {
567
+ this.cache = new Map();
568
+ }
569
+
570
+ // Public method for external use
571
+ myPublicMethod() {
572
+ this.#myPrivateMethod();
573
+ }
574
+
575
+ // Private method requiring instance access
576
+ #myPrivateMethod() {
577
+ this.cache.set('key', 'value');
578
+ }
579
+ }
580
+
581
+ // Module-scoped utility - no instance access needed
582
+ const someUtilityFunction = (num1, num2) => num1 + num2;
583
+ ```
584
+ </privacy_and_instance_methods>
585
+ </modules>
586
+
587
+ <asynchronous_code>
588
+ - Use "async/await" syntax
589
+ - Use "await" over chaining ".then()"
590
+ </asynchronous_code>
591
+
592
+ <events>
593
+ - Use events for custom element communication to avoid explicit dependencies
594
+ </events>
595
+
596
+ <web_components>
597
+ - Initialize JS components as custom elements for seamless DOM updates
598
+ - Use shadow DOM and slots
599
+ </web_components>
600
+
601
+ <early_returns>
602
+ - Use early returns over nested conditionals
603
+
604
+ <optional_chaining>
605
+ Multiple optional chains require early returns. Single chains are acceptable.
606
+
607
+ ```
608
+ // Multiple chains - use early return
609
+ const button = this.querySelector('ref="button"');
610
+ if (!button) return;
611
+ button.enable();
612
+ button.textContent = 'Add to cart';
613
+
614
+ // Single chain is fine
615
+ const button = this.querySelector('ref="button"');
616
+ button?.enable();
617
+ ```
618
+ </optional_chaining>
619
+ </early_returns>
620
+
621
+ <simplification>
622
+ <ternaries>
623
+ Use ternaries for simple if/else blocks:
624
+ `simpleCondition ? this.doAThing() : this.doAnotherThing();`
625
+ </ternaries>
626
+
627
+ <one_liners>
628
+ Write simple conditional returns on one line:
629
+ `if (simpleCondition) return;`
630
+ </one_liners>
631
+
632
+ <returning_boolean>
633
+ Return boolean comparisons directly:
634
+ `return simpleCondition;`
635
+ </returning_boolean>
636
+ </simplification>
637
+ </javascript>
638
+ </liquid-development>