diff --git a/CHANGELOG.md b/CHANGELOG.md index d8c543c..f69fe2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,28 @@ All notable changes follow [Keep a Changelog](https://keepachangelog.com/) and t ## [Unreleased] +### Added + +- Shopify-native component CSS and JavaScript in the reference storefront through `{% stylesheet %}` and `{% javascript %}` tags. +- Theme-block-first product composition with merchant-reorderable vendor, title, price, purchase, description, `@theme`, and `@app` blocks. +- Shopify standard storefront view events for product, collection, recommendation, and cart surfaces, plus cart mutation through `Shopify.actions.updateCart()` with the native product form retained as the no-JavaScript fallback. +- Shopify `color_palette` design tokens and `` customer-account integration in the fresh reference theme. +- A documented compiled-branch/deployment-repository workflow and opt-in GitHub Actions recipe for Shopify GitHub theme integration. +- A complete Shopify 2026 modernization plan covering architecture, compatibility constraints, implementation order, and real-store acceptance criteria. + +### Changed + +- Fresh projects now default to Shopify-native static/component assets with `viteConfig: false` instead of requiring a Vite/Tailwind storefront bundle. +- Vite and Tailwind remain supported framework integrations but are now documented as explicit optional asset strategies rather than the framework's product identity. +- The reference product, header, predictive search, collection, featured collection, and cart surfaces now demonstrate current Shopify-native platform primitives. +- The embedded `create-liquid-loom` starter stays byte-for-byte synchronized with the modernized reference storefront and targets Shopify CLI 4.8+ for new projects. +- README, recipes, and roadmap now describe Liquid Loom as a deterministic engineering layer for Shopify Liquid themes and sequence platform modernization after the `0.3` project-intelligence work. + +### Removed + +- The fresh starter's default global Vite entrypoint, Tailwind stylesheet source, and bundled `theme.js`/`style.css` architecture. +- The obsolete default Vite configuration from the reference project and embedded starter. Existing projects that explicitly enable Vite remain supported. + ## [0.2.0] - 2026-09-09 ### Added diff --git a/README.md b/README.md index f09408c..3ffe3f4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Liquid Loom

-

A deterministic engineering layer for Shopify themes, from legacy adoption to agent-readable architecture.

+

A deterministic engineering layer for Shopify Liquid themes, from legacy adoption to agent-readable architecture.

npm version @@ -15,9 +15,9 @@ Liquid Loom gives Shopify theme development a safer, source-first engineering layer while keeping Shopify's runtime completely standard. -Use it to organize new code by feature, add modern tooling to an existing client theme without a rewrite, migrate gradually when it is worth it, understand source ownership before an agent edits the project, and still ship a normal Shopify theme through Shopify CLI. +Use it to organize theme code by feature, adopt better tooling inside an existing client theme without a rewrite, migrate source gradually, inspect project ownership before a developer or coding agent edits it, and still ship a conventional Shopify theme through Shopify CLI. -It does **not** replace Liquid, Online Store 2.0, Shopify CLI, or Shopify's runtime model. Vite and Tailwind are first-class integrations for fresh projects, not abstractions that Liquid Loom depends on for its identity. +Liquid Loom does **not** replace Liquid, Online Store 2.0, Shopify CLI, the Theme Editor, or Shopify's storefront runtime. ## Start with the theme you already have @@ -29,7 +29,7 @@ npx liquid-loom@latest init Liquid Loom shows the complete setup plan before writing anything. Existing Shopify source is not moved. -After setup, keep the current theme exactly as it is: +You can keep the theme exactly as it is: ```text assets/ @@ -39,19 +39,19 @@ snippets/ templates/ ``` -and put only new work into organized source when you want to: +and put only new work into organized source when that helps: ```text src/theme/sections/product/upsell.liquid src/theme/snippets/product/upsell-price.liquid ``` -Both become one normal deployable theme: +Both source layers become one normal deployable theme: ```text existing Shopify source ----+ | -organized Liquid Loom source +--> deterministic ownership plan --> dist/theme --> Shopify CLI +organized Liquid Loom source +--> ownership plan --> dist/theme --> Shopify CLI ``` Build with the namespaced script added by `init`: @@ -60,9 +60,9 @@ Build with the namespaced script added by `init`: npm run loom:build ``` -Use your preferred package manager. `init` detects npm, pnpm, Yarn, or Bun and supports `--package-manager`, `--no-install`, `--dry-run`, `--theme-dir`, and `--yes`. +`init` detects npm, pnpm, Yarn, or Bun and supports `--package-manager`, `--no-install`, `--dry-run`, `--theme-dir`, and `--yes`. -Read the full [existing-theme adoption guide](docs/EXISTING_THEME_ADOPTION.md). +Read [Existing-theme adoption](docs/EXISTING_THEME_ADOPTION.md) for the full zero-migration workflow. ## Or start a new theme @@ -71,101 +71,136 @@ corepack enable pnpm create liquid-loom@latest my-storefront cd my-storefront pnpm build -``` - -Then preview against a Shopify development store: - -```bash pnpm dev ``` -The scaffolder supports `--package-manager npm|pnpm|yarn|bun` and `--no-install`. +A fresh project uses Shopify-native component CSS and JavaScript by default. Vite and Tailwind remain supported integrations when a project actually needs bundling, module graphs, or utility-first CSS. ## Why Liquid Loom -Liquid Loom targets the parts of custom theme development that become painful as projects, teams, and coding agents grow: +Liquid Loom targets the engineering problems that become painful as custom themes, teams, and coding agents grow: - **Adopt incrementally.** Existing themes do not need an up-front rewrite. - **Feature-oriented source.** Organize authored Liquid by feature without inventing a new runtime. - **Dual-source builds.** Native Shopify source and organized Liquid Loom source can coexist safely. -- **No silent precedence.** Every Shopify output path has exactly one owner. -- **No silent overwrites.** Destination collisions fail before a new build is published. -- **Last-known-good output.** Failed builds do not replace the previous working theme. -- **Transactional migration.** Preview first, preserve Shopify output identity, and roll back completed moves if a later move fails. +- **One output, one owner.** No silent overlay or last-write-wins behavior. +- **Transactional builds.** Failed builds never replace the last-known-good theme. +- **Safe migration.** Preview ownership transfers before moving source and preserve Shopify output identity. - **Project intelligence.** `explain` turns ownership and provable static relationships into deterministic human- and machine-readable data. -- **Preview-aware, not preview-dependent.** Shopify developer-preview Liquid is explicit and observable instead of silently becoming a production requirement. -- **Optional Vite + Tailwind.** Fresh projects get the modern asset workflow; existing themes can keep their current pipeline. -- **Portable behavior.** Collision checks are case-insensitive and Unicode-normalized across filesystems. +- **Shopify-native defaults.** Theme blocks, app blocks, component asset tags, color palettes, customer accounts, and standard storefront interoperability stay visible as Shopify concepts. +- **Optional advanced assets.** Vite and Tailwind remain available without defining the framework's architecture. +- **Portable behavior.** Destination collisions are case-insensitive and Unicode-normalized across filesystems. - **Useful diagnostics.** `doctor`, `check`, `analyze`, Theme Check, and performance budgets make failures explainable. -- **Standard Shopify output.** `dist/theme` remains inspectable, pushable, and debuggable with Shopify's own tooling. +- **Standard output.** `dist/theme` remains inspectable, pushable, and debuggable with Shopify's own tools. -## Understand the theme before editing it +## Shopify-native fresh-project architecture -Liquid Loom can describe the source model without requiring a build first: +The reference storefront now demonstrates Shopify's 2026-native theme model instead of loading one application bundle for ordinary theme behavior. -```bash -liquid-loom explain -liquid-loom explain product -``` - -Coding agents and CI can request deterministic JSON: +A fresh source tree can look like: -```bash -liquid-loom explain --json -liquid-loom explain product --json +```text +src/ +├── public/ +│ └── theme.css +└── theme/ + ├── blocks/ + │ ├── product-title.liquid + │ ├── product-price.liquid + │ └── product-purchase.liquid + ├── sections/ + │ ├── products/main-product.liquid + │ └── global/header.liquid + ├── snippets/ + ├── templates/ + └── layout/theme.liquid ``` -The model reports source ownership, semantic feature groups, static snippet/section/block/asset relationships, JSON-template section references, preview partial regions, preview-tag usage, and unresolved static references. +Component-local behavior lives with the Liquid component when practical: -It does not evaluate Liquid or guess dynamic dependencies. The goal is a smaller truthful architecture model, not a second Liquid interpreter. +```liquid +{% stylesheet %} + .product-purchase { ... } +{% endstylesheet %} -Read [Project model](docs/PROJECT_MODEL.md) for the complete contract and recommended agent workflow. +{% javascript %} + customElements.define(...) +{% endjavascript %} +``` -## The source model +Shopify can then process component assets through its own render-aware pipeline. Global CSS is reserved for genuinely global concerns such as base styles, design tokens, accessibility defaults, and shared layout primitives. -A fresh Liquid Loom project can look like this: +The output stays conventional: ```text -src/theme/sections/home/hero.liquid -src/theme/sections/product/recommendations.liquid -src/theme/snippets/product/price.liquid -src/entrypoints/theme.js -src/styles/theme.css +dist/theme/ +├── assets/theme.css +├── blocks/product-title.liquid +├── blocks/product-price.liquid +├── blocks/product-purchase.liquid +├── sections/main-product.liquid +├── snippets/ +├── templates/product.json +└── layout/theme.liquid ``` -Liquid Loom produces: +## Theme-block-first composition + +The reference product page is composed from Shopify theme blocks rather than hard-coding all product information into one monolithic section. ```text -dist/theme/sections/hero.liquid -dist/theme/sections/recommendations.liquid -dist/theme/snippets/price.liquid -dist/theme/assets/theme.js -dist/theme/assets/style.css +Main product +├── Product media +└── Merchant-reorderable blocks + ├── Vendor + ├── Title + ├── Price + ├── Purchase controls + ├── Description + ├── @theme + └── @app ``` -An adopted project may simultaneously contain: +This keeps customization inside Shopify's Theme Editor and makes the structure understandable to merchants, developers, apps, and coding agents without adding a Liquid Loom component runtime. -```text -sections/header.liquid -assets/legacy-theme.css -src/theme/sections/product/upsell.liquid +## Storefront interoperability + +The reference storefront adopts Shopify's standard storefront events and actions where the theme owns the interaction. + +Examples include: + +- standardized product, collection, recommendation, and cart view events through `s-view-event` and `standard_event_data`; +- cart mutation through `Shopify.actions.updateCart()` with the native product form retained as the no-JavaScript fallback; +- merchant-configurable `` integration in the header; +- `color_palette` as the source for semantic CSS design tokens. + +The goal is interoperability with Shopify, apps, and agents, not a Liquid Loom-specific browser protocol. + +## Understand the theme before editing it + +Liquid Loom can describe the project without evaluating Liquid or requiring a build first: + +```bash +liquid-loom explain +liquid-loom explain product ``` -and produce: +Coding agents and CI can request deterministic JSON: -```text -dist/theme/sections/header.liquid -dist/theme/sections/upsell.liquid -dist/theme/assets/legacy-theme.css +```bash +liquid-loom explain --json +liquid-loom explain product --json ``` -Shopify still receives the conventional structure it expects. +The model reports source ownership, semantic feature groups, static snippet/section/block/asset relationships, JSON-template section references, preview partial regions, preview-tag usage, and unresolved static references. + +It intentionally does not guess dynamic Liquid relationships. Read [Project model](docs/PROJECT_MODEL.md) for the complete contract. ## One output, one owner There is intentionally no "new source wins" rule. -This fails: +This is invalid: ```text sections/hero.liquid @@ -178,13 +213,13 @@ because both map to: sections/hero.liquid ``` -Liquid Loom validates the global ownership plan before changing the last-known-good output and tells you which sources collide. +Liquid Loom plans the complete ownership map before publishing a build and reports the conflicting owners instead of silently overwriting one of them. -The same ownership model includes generated asset reservations when Vite is enabled. +Generated Vite outputs participate in the same ownership map when Vite is enabled. ## Migrate only when it helps -Preview migration of one file: +Preview a migration: ```bash liquid-loom migrate sections/hero.liquid @@ -196,7 +231,7 @@ Apply it: liquid-loom migrate sections/hero.liquid --apply ``` -Organize it during migration: +Organize it while preserving its Shopify identity: ```bash liquid-loom migrate sections/hero.liquid \ @@ -204,141 +239,85 @@ liquid-loom migrate sections/hero.liquid \ --apply ``` -The Shopify output must remain `sections/hero.liquid`. If the requested destination would change the deployable path, Liquid Loom refuses the migration. +The deployable path must remain `sections/hero.liquid`. Liquid Loom rejects a migration that would silently rename Shopify output. -Migrate a directory: +Bulk migration remains optional: ```bash liquid-loom migrate sections --apply -``` - -Or preview/apply all remaining native theme source: - -```bash liquid-loom migrate --all liquid-loom migrate --all --apply ``` -Liquid Loom never invents your feature taxonomy during bulk migration. +Liquid Loom never invents a feature taxonomy for existing source. -## Existing asset pipelines are allowed +## Asset strategies -Existing-theme initialization defaults to: +### Shopify-native default -```js -import { defineConfig } from "liquid-loom"; +Fresh projects default to `viteConfig: false` and use static global assets plus `{% stylesheet %}` / `{% javascript %}` for component-local code. -export default defineConfig({ - shopifySourceDir: ".", - sourceDir: "src", - outputDir: "dist/theme", - viteConfig: false, - performance: false -}); -``` - -That means native `assets/*` pass through unchanged and your existing Sass/PostCSS/Webpack/Vite process can keep running independently. - -Fresh projects still use Liquid Loom's Vite + Tailwind workflow by default. Tailwind remains an integration choice rather than part of the source-ownership contract. - -If `src/entrypoints` exists while Vite is disabled, `doctor` warns that those files are not being compiled. +### Existing pipeline -## Shopify mapping contract - -| Authoring source | Deployable Shopify path | Rule | -| ----------------------------------------------- | -------------------------------------- | ------------------------------------- | -| `sections/hero.liquid` | `sections/hero.liquid` | Native Shopify identity mapping | -| `src/theme/sections/home/hero.liquid` | `sections/hero.liquid` | Organized source flattens by filename | -| `src/theme/snippets/product/price.liquid` | `snippets/price.liquid` | Organized source flattens by filename | -| `src/theme/config/editor/settings_schema.json` | `config/settings_schema.json` | Flatten by filename | -| `src/theme/locales/markets/en.default.json` | `locales/en.default.json` | Flatten by filename | -| `src/theme/templates/catalog/product.json` | `templates/product.json` | Feature folders flatten | -| `templates/customers/account.json` | `templates/customers/account.json` | Preserve Shopify-supported nesting | -| `src/theme/templates/metaobject/book.json` | `templates/metaobject/book.json` | Preserve Shopify-supported nesting | -| `src/public/icons/cart.svg` | `assets/cart.svg` | Flatten passthrough assets | -| `src/entrypoints/theme.js` + `src/styles/*.css` | `assets/theme.js` + `assets/style.css` | Vite-generated outputs when enabled | - -Framework validation checks Shopify's upload minimum from the final ownership plan: `layout/theme.liquid` must exist in deployable output, regardless of which source layer owns it. - -For Shopify's canonical directory rules, see the official [theme architecture documentation](https://shopify.dev/docs/storefronts/themes/architecture). - -## Build guarantees +An adopted theme can keep Sass, PostCSS, Webpack, Vite, Tailwind, or another existing pipeline unchanged. Native `assets/*` pass through as Shopify assets. -### Complete planning before publish +### Optional Vite and Tailwind -Source discovery, Shopify path validation, traversal guards, collision detection, and generated-output reservations happen before a new output is promoted. +Projects that need bundling can explicitly enable Vite and reserve stable generated Shopify asset names. Tailwind remains available through the native Tailwind Vite plugin. -### Last-known-good output +The framework still validates static and generated outputs in one collision map. -Static mapping and optional Vite bundling happen in isolated staging paths. Output and cache are promoted together only after the build succeeds. +See [Recipes](docs/RECIPES.md) for setup examples. -### Safe concurrent builds +## Shopify GitHub deployment -Independent CLI processes serialize through a recoverable project lock. An abandoned lock from a terminated process is detected and removed. +A Liquid Loom source branch contains development files that Shopify's GitHub theme integration does not treat as the deployable theme root. -### Migration-safe caching - -Hybrid manifests track source-layer identity. Moving ownership from: - -```text -sections/hero.liquid -``` - -to: +For GitHub-connected themes, use a generated `shopify-production` branch or a separate deployment repository containing only the canonical Shopify theme directories: ```text -src/theme/sections/home/hero.liquid +main + | + | validate + build + v +shopify-production + | + v +Shopify GitHub integration ``` -does not cause `sections/hero.liquid` to be removed as stale. - -### Consumer-shaped release validation +The repository includes an opt-in deployment recipe that publishes compiled output without force-pushing and avoids deployment loops from Shopify-originated commits. -CI packs the actual npm artifacts, installs both CLIs into independent temporary projects, validates `explain` from the packed package, validates a fresh scaffold, and validates an existing-theme `init -> hybrid build -> migrate -> build` flow. +Read [Shopify GitHub deployment](docs/GITHUB_DEPLOYMENT.md). -Releases use npm Trusted Publishing with GitHub OIDC and signed provenance. +## Shopify Liquid preview mode -## Configuration +Shopify's July 2026 `{% block %}` / `{% partial %}` developer preview is never enabled implicitly. -Fresh project example: +Projects intentionally targeting it can declare: ```ts -import { defineConfig } from "liquid-loom"; - export default defineConfig({ - sourceDir: "src", - outputDir: "dist/theme", - performance: { - maxBuildMs: 10_000, - maxThemeBytes: 5_000_000, - maxAssetBytes: 500_000 - } + shopifyLiquidMode: "july-2026-preview" }); ``` -Existing theme in a monorepo: +This records project intent for diagnostics and project intelligence. It does not enable the preview on a Shopify store. -```ts -export default defineConfig({ - shopifySourceDir: "theme", - sourceDir: "src", - outputDir: "dist/theme", - viteConfig: false, - performance: false -}); -``` +Read [Shopify Liquid July 2026 developer preview](docs/LIQUID_JULY_2026_PREVIEW.md). -All paths remain project-relative and portable. +## Build guarantees -Shopify's July 2026 Liquid developer-preview syntax is never enabled implicitly. Projects intentionally targeting that preview can declare: +Liquid Loom keeps the existing engineering guarantees regardless of asset strategy: -```ts -export default defineConfig({ - shopifyLiquidMode: "july-2026-preview" -}); -``` +1. discover all managed source; +2. map the complete Shopify ownership plan; +3. validate paths, collisions, and Shopify's upload minimum; +4. build in isolated staging; +5. optionally run Vite and performance budgets; +6. atomically promote output and cache together. -This only declares project intent to Liquid Loom; it does not enable the preview on a Shopify development store. See [Shopify Liquid July 2026 developer preview](docs/LIQUID_JULY_2026_PREVIEW.md). +A failed build leaves the previous deployable output intact. Independent CLI processes serialize through a recoverable project lock. ## Commands @@ -358,58 +337,64 @@ This only declares project intent to Liquid Loom; it does not enable the preview Scaffolded projects expose package-manager scripts for the same commands. Existing-theme `init` uses namespaced `loom:*` scripts so it does not replace a project's existing `build` or `dev` commands. +## Reference storefront + +The fresh-project scaffold is a merchant-neutral reference theme that demonstrates: + +- Shopify theme blocks and app blocks; +- merchant-reorderable product information and purchase controls; +- Shopify-native component CSS and JavaScript; +- standard storefront view events and cart actions; +- `color_palette` design tokens; +- `` customer-account integration; +- JSON templates and editable header/footer section groups; +- storefront filtering and predictive search; +- variant URL state, selling plans, quantities, and progressive enhancement; +- responsive images, semantic navigation, skip links, visible focus states, reduced-motion support, and no-JavaScript fallbacks. + +It is a reference implementation. The product is Liquid Loom's engineering layer and workflow. + ## Who it is for Liquid Loom is a good fit when you: -- maintain a custom or client Shopify theme and want better tooling without rewriting it; +- maintain a custom or client Shopify theme and want stronger tooling without rewriting it; - want new theme work organized by feature rather than only by Shopify's flat directories; -- want deterministic build ownership and explicit collision behavior; -- want coding agents to query a conservative architectural model before editing source; -- expect modern frontend build and CI guarantees from the rest of your stack. +- need deterministic ownership and collision behavior; +- want coding agents to inspect a conservative architectural model before editing source; +- want Shopify-native theme capabilities without giving up modern engineering guarantees. It is probably not the right starting point when: - the theme is tiny and needs no build pipeline; - you are building a headless storefront rather than a Shopify Liquid theme; -- your primary goal is Shopify Theme Store submission, where Shopify's official starting points and policies should remain authoritative. - -## Reference storefront - -The fresh-project scaffolder includes a merchant-neutral Online Store 2.0 reference theme with theme blocks, JSON templates, editable header/footer groups, storefront filtering, predictive search, variant URL state, selling plans, responsive images, semantic navigation, reduced-motion support, visible focus states, and no-JavaScript fallbacks. - -It is a reference implementation. The product is the tooling and workflow. - -## Validation and feedback - -Liquid Loom is being validated against real Shopify workflows, not just repository metrics. +- your primary goal is Shopify Theme Store submission, where Shopify's official starting points and policies remain authoritative. -Useful evidence includes: +## Validation -- a successful public-package install; -- a real Shopify development-store preview; -- existing-theme adoption without source migration; -- incremental migration on a real client-style repository; -- coding-agent use of the deterministic project model; -- developers independently choosing the workflow again. +Protected CI validates: -If you try it, feedback about what is confusing, slower, or worse is especially useful. +- unit and integration tests with coverage thresholds; +- transactional clean builds; +- zero-offense Shopify Theme Check; +- Linux/Node 22, Windows/Node 24, and macOS/Node 24 portability; +- packed-package installation and fresh-scaffold smoke tests; +- Dependency Review and CodeQL; +- a non-blocking Node 26 canary. -- [Run the first-project test](docs/FIRST_PROJECT.md) -- [Read the existing-theme adoption guide](docs/EXISTING_THEME_ADOPTION.md) -- [Share first-project feedback](https://github.com/yotamon/Liquid-Loom/issues/new?template=early_adopter_feedback.yml) -- [Report a bug](https://github.com/yotamon/Liquid-Loom/issues/new?template=bug_report.yml) -- [Start a discussion](https://github.com/yotamon/Liquid-Loom/discussions) +Real-store validation remains separate from public CI so contributors do not need Shopify credentials. ## Documentation +- [Shopify 2026 platform modernization](docs/SHOPIFY_2026_MODERNIZATION.md) - architecture decisions and acceptance criteria - [Existing-theme adoption](docs/EXISTING_THEME_ADOPTION.md) - zero-migration setup, hybrid source, and safe migration - [Project model](docs/PROJECT_MODEL.md) - deterministic architecture queries for developers, agents, and CI - [Shopify Liquid July 2026 developer preview](docs/LIQUID_JULY_2026_PREVIEW.md) - explicit preview-mode contract and safety boundaries +- [Shopify GitHub deployment](docs/GITHUB_DEPLOYMENT.md) - compiled deployment branches and write-back constraints - [First project](docs/FIRST_PROJECT.md) - short evaluation from public install to a real source edit - [Architecture](docs/ARCHITECTURE.md) - ownership, transactions, cache, and migration invariants - [Validation](docs/VALIDATION.md) - real-user and real-store validation plan -- [Recipes](docs/RECIPES.md) - entrypoints, static assets, private-term policies, and budgets +- [Recipes](docs/RECIPES.md) - native assets, optional bundling, private-term policies, and budgets - [Troubleshooting](docs/TROUBLESHOOTING.md) - collisions, locks, Shopify CLI, and build failures - [Benchmarks](docs/BENCHMARKS.md) - reproducible build measurements - [Releasing](docs/RELEASING.md) - provenance-backed npm release process @@ -421,12 +406,12 @@ If you try it, feedback about what is confusing, slower, or worse is especially - Adopt tooling before forcing migration. - Source ownership must be explicit. - Shopify's runtime contract stays visible. -- Integrate with Shopify primitives instead of duplicating them. +- Prefer Shopify primitives over competing abstractions. - Generated output is disposable; authored source is the product. - Prefer provable project intelligence over guessed relationships. - Fail early, explain specifically, and preserve the last-known-good build. - Treat Vite and Tailwind as replaceable integrations, not the framework's durable moat. -- Prefer a dependable core over speculative abstractions. +- Keep preview APIs explicit until Shopify stabilizes them. - Add framework surface only after real users demonstrate the need.

diff --git a/ROADMAP.md b/ROADMAP.md index 6791d29..c5b3987 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -73,7 +73,38 @@ Now proving: See [Project model](docs/PROJECT_MODEL.md) and [Shopify Liquid July 2026 developer preview](docs/LIQUID_JULY_2026_PREVIEW.md). -## 0.4 - Extension surface, if earned +## 0.4 - Shopify 2026 platform modernization + +Modernize fresh-project defaults and the reference storefront around Shopify's current native theme capabilities without weakening the `0.2` ownership/migration guarantees or the `0.3` project-intelligence model. + +Implemented in the modernization PR: + +- [x] make Shopify-native `{% stylesheet %}` / `{% javascript %}` component assets the preferred fresh-project path where practical; +- [x] keep Vite and Tailwind as explicit, tested optional asset strategies rather than Liquid Loom's product identity; +- [x] make theme blocks and merchant-reorderable composition central to the reference storefront, especially product information and purchase surfaces; +- [x] implement relevant Shopify standard storefront view events and cart actions in the reference theme; +- [x] move the reference color system to `color_palette` plus semantic CSS custom properties; +- [x] adopt the `` component with a merchant-configurable account menu; +- [x] add a documented compiled branch/deployment-repository workflow and opt-in GitHub Actions recipe for Shopify GitHub integration; +- [x] target Shopify CLI 4.8+ in fresh scaffolds and document disposable dev-store validation; +- [x] preserve explicit `stable` / `july-2026-preview` modes and keep preview syntax outside the stable release gate; +- [x] preserve zero-migration adoption and existing asset pipelines for established themes; +- [x] add automated capability tests and keep the reference storefront byte-for-byte synchronized with the embedded starter. + +Still requiring real Shopify evidence before calling the milestone complete: + +- [ ] preview the modernized fresh scaffold on a Shopify development store; +- [ ] verify product-block reordering and app-block placement in Theme Editor; +- [ ] verify `` with customer accounts enabled and disabled; +- [ ] verify palette editing through Theme Editor; +- [ ] verify standard storefront event/action behavior in a real browser session; +- [ ] verify the compiled deployment branch against Shopify GitHub integration and confirm Shopify-originated commits do not create a deployment loop; +- [ ] run the modernization against at least one established client-style theme without changing its existing asset pipeline; +- [ ] extend project intelligence only if those real workflows expose new statically provable relationships worth modeling. + +See [Shopify 2026 platform modernization](docs/SHOPIFY_2026_MODERNIZATION.md) for the full architecture decisions, compatibility constraints, implementation order, and acceptance criteria. + +## 0.5 - Extension surface, if earned Only after repeated consumer needs justify a public extension API: @@ -97,5 +128,6 @@ Only after repeated consumer needs justify a public extension API: - Requiring migration before an existing theme can receive value from Liquid Loom. - Guessing dynamic Liquid relationships in order to make the project model look more complete. - Making developer-preview Liquid syntax the default before Shopify stabilizes it. +- Introducing a second storefront runtime to imitate headless frameworks. See [Validation](docs/VALIDATION.md) for the evidence expected before broadening the public API. diff --git a/docs/GITHUB_DEPLOYMENT.md b/docs/GITHUB_DEPLOYMENT.md new file mode 100644 index 0000000..34c2f53 --- /dev/null +++ b/docs/GITHUB_DEPLOYMENT.md @@ -0,0 +1,62 @@ +# Shopify GitHub deployment + +Liquid Loom intentionally keeps authored source separate from deployable Shopify output. Shopify's GitHub theme integration, however, expects the connected branch to contain Shopify's canonical theme directories. + +For repositories that use `src`, tests, and build tooling, connect Shopify to a **compiled deployment branch** or a separate deployment repository rather than to the authored branch. + +## Recommended topology + +```text +main +├── src/ +├── tests/ +├── liquid-loom.config.ts +└── package.json + | + | CI: install -> validate -> build + v +shopify-production +├── assets/ +├── blocks/ +├── config/ +├── layout/ +├── locales/ +├── sections/ +├── snippets/ +└── templates/ + | + v +Shopify GitHub integration +``` + +The deployment branch is generated output. `main` remains the source of truth. + +## Safe CI policy + +Use a workflow triggered only by authored branches. Never trigger the build from `shopify-production` itself; that prevents a Shopify-originated commit on the connected branch from creating a deployment loop. + +When publishing: + +1. fetch the current `shopify-production` head; +2. create a worktree from that head so the new deployment is a fast-forward commit; +3. replace only the canonical Shopify theme directories with `dist/theme`; +4. commit only when the compiled output changed; +5. push without `--force`. + +A concurrent Shopify write will make the push fail rather than silently overwrite history. Re-run only after reviewing the branch state. + +See [`docs/examples/shopify-deploy.yml`](examples/shopify-deploy.yml) for an opt-in workflow recipe. + +## Theme Editor writes + +A compiled branch can't automatically translate Theme Editor changes back into organized source. Choose one of these policies explicitly: + +- **Source-authoritative:** Theme Editor changes on the deployment branch are temporary and the next successful source deployment may replace them. Keep history so drift can be reviewed first. +- **Reconciliation workflow:** review Shopify-originated commits and manually port intentional changes into authored source before the next deployment. +- **Separate deployment repository:** use this when you want stronger operational separation between authored source and generated theme output. + +Do not pretend two-way synchronization is lossless when a build step changes source shape. + +## Development stores + +For protected integration CI, Shopify CLI 4.8+ can create a disposable dev store, preview the built theme, run browser smoke tests, and remove the store afterwards. Public CI should continue to run without Shopify credentials. diff --git a/docs/RECIPES.md b/docs/RECIPES.md index bb3ffdb..7c10150 100644 --- a/docs/RECIPES.md +++ b/docs/RECIPES.md @@ -8,21 +8,53 @@ Place a file anywhere under `src/public/`. Its basename becomes the Shopify asse src/public/icons/cart.svg → dist/theme/assets/cart.svg ``` -Basenames must be unique across the entire public tree and cannot be `theme.js` or `style.css` unless generated-output configuration changes with matching Vite output. +Basenames must be unique across the entire public tree. When Vite is enabled, static assets also cannot collide with any configured generated output. -## Add a section or snippet +## Add component CSS or JavaScript -Organize by feature; Shopify’s destination remains flat: +Fresh Liquid Loom projects prefer Shopify-native component assets: + +```liquid +{% stylesheet %} + .product-card { + display: grid; + } +{% endstylesheet %} + +{% javascript %} + customElements.define('product-card', class extends HTMLElement {}); +{% endjavascript %} +``` + +Keep truly global styles in a static file such as `src/public/theme.css`. Liquid doesn't render inside `{% stylesheet %}` or `{% javascript %}` tags, so pass dynamic values through markup, data attributes, or CSS custom properties. + +## Add a section, block, or snippet + +Organize by feature; Shopify's destination remains flat: ```text src/theme/sections/editorial/story.liquid → dist/theme/sections/story.liquid +src/theme/blocks/product/title.liquid → dist/theme/blocks/title.liquid ``` Run `pnpm build` immediately after adding the file. Portable collisions fail before the output changes. -## Add another Vite entrypoint +## Opt into Vite or Tailwind + +The fresh starter uses Shopify-native assets by default, but the framework still supports Vite and Tailwind. Add a Vite config and enable it explicitly: + +```ts +export default defineConfig({ + viteConfig: "vite.config.js", + reservedOutputs: ["assets/theme.js", "assets/style.css"] +}); +``` + +Install the asset tooling your project needs and configure stable output names. Generated outputs participate in the same ownership/collision plan as authored Shopify assets. + +## Deploy compiled output through Shopify GitHub integration -Extend `rollupOptions.input` and give its output a stable name in `vite.config.js`. Add that output to `reservedOutputs` in `liquid-loom.config.ts` so a public asset cannot overwrite it. +If your source repository contains `src`, tests, and build tooling, connect Shopify to a generated branch or separate deployment repository that contains only canonical Shopify theme directories. See [GitHub deployment](GITHUB_DEPLOYMENT.md). ## Scan for organization-specific terms diff --git a/docs/SHOPIFY_2026_MODERNIZATION.md b/docs/SHOPIFY_2026_MODERNIZATION.md new file mode 100644 index 0000000..ac675a2 --- /dev/null +++ b/docs/SHOPIFY_2026_MODERNIZATION.md @@ -0,0 +1,404 @@ +# Shopify 2026 platform modernization + +Liquid Loom's core architecture still fits Shopify theme development in 2026: it keeps Liquid and Shopify CLI authoritative, produces a canonical Shopify theme, makes output ownership explicit, and lets established themes adopt tooling without an up-front rewrite. + +The next modernization step is therefore not a framework rewrite. It is a change in **defaults and reference architecture** so that fresh Liquid Loom projects demonstrate Shopify's strongest current theme primitives instead of treating Vite + Tailwind as the center of the product. + +This plan builds on the `0.3` project-intelligence work. The deterministic project model, `explain`, explicit `shopifyLiquidMode`, and preview-aware diagnostics remain part of the foundation. + +## Decision + +Liquid Loom should be described as: + +> A deterministic engineering layer for Shopify Liquid themes: safer source architecture, incremental adoption, agent-readable project understanding, modern Shopify-native composition, and optional advanced asset tooling. + +Vite and Tailwind stay supported. They stop being the default explanation for why Liquid Loom exists. + +## Core invariants that do not change + +1. Liquid remains the storefront runtime. +2. Shopify CLI remains the canonical preview/deploy tool. +3. `dist/theme` remains a normal, inspectable Shopify theme. +4. Every deployable Shopify path has exactly one source owner. +5. Ownership is planned and validated before publish. +6. Failed builds never replace the last-known-good output. +7. Existing themes can adopt Liquid Loom without moving source. +8. Migration remains preview-first and preserves Shopify output identity. +9. Existing Sass/PostCSS/Webpack/Vite/Tailwind pipelines remain valid for adopted themes. +10. The project model reports provable relationships rather than evaluating or guessing Liquid. +11. Developer-preview Shopify syntax never becomes a stable requirement implicitly. + +## Target architecture + +```text +authored Shopify/Liquid source + | + +-- native component assets + +-- theme blocks / app blocks + +-- optional Vite or Tailwind assets + +-- explicit stable/preview Liquid mode + | + v +Liquid Loom + | + +-- ownership plan + +-- project intelligence + +-- diagnostics + +-- transactional build + +-- migration safety + | + v +canonical Shopify theme + | + +-- Shopify CLI preview/push + +-- compiled GitHub deployment branch/repo +``` + +## 1. Shopify-native component assets become the fresh-project default + +Shopify supports `{% stylesheet %}` and `{% javascript %}` in sections, blocks, and snippets. Shopify now subsets stylesheet-tag CSS by the current page render tree, making component-local CSS a real platform feature rather than just a source-organization convention. + +### Target + +Fresh projects should prefer component-local assets for component-local behavior: + +```liquid +{% stylesheet %} + .product-card { ... } +{% endstylesheet %} + +{% javascript %} + customElements.define(...) +{% endjavascript %} +``` + +Global CSS should remain only for genuinely global concerns such as base styles, typography, design tokens, shared layout primitives, accessibility defaults, and intentionally global utilities. + +### Vite and Tailwind + +Vite and Tailwind remain first-class supported integrations, but they become explicit asset strategies instead of the architectural default. + +A future scaffolder can expose a small choice such as: + +```text +native Shopify-native component assets + minimal global assets +vite Vite for projects that need bundling/modules + tailwind Tailwind + Vite for teams that intentionally choose it +``` + +Do not introduce a generic asset plugin API merely to support these three paths. + +### Acceptance criteria + +- a default fresh scaffold builds without requiring a storefront Vite bundle; +- component CSS/JS is colocated with its Liquid component where practical; +- global assets are intentionally small and documented; +- Vite and Tailwind remain tested supported paths; +- existing-theme `init` continues to preserve the current asset pipeline. + +Reference: + +## 2. Theme blocks become the primary reference composition model + +Liquid Loom already understands root `blocks/*.liquid`, block references, app blocks, and preview block relationships. The reference storefront should now demonstrate that model deeply rather than treating blocks as a small flexible-content example. + +### Product page target + +Move the product page toward merchant-reorderable components: + +```text +Main product +| ++-- Product media ++-- Product information group +| +-- Vendor +| +-- Title +| +-- Price +| +-- @app ++-- Variant picker ++-- Purchase group +| +-- Quantity +| +-- Buy buttons +| +-- @app ++-- Description +``` + +The decomposition should stay intentionally small and Shopify-native. Liquid Loom should not invent its own component runtime or schema abstraction. + +### Acceptance criteria + +- merchants can reorder the supported product information blocks in Theme Editor; +- expected surfaces accept `@app` blocks; +- reusable content primitives are theme blocks where reuse creates merchant value; +- source continues to map through the existing organized-source ownership contract; +- no-JavaScript fallbacks and accessible markup remain part of the reference theme. + +## 3. Standard storefront events and actions + +Shopify now provides a standard communication layer between Liquid storefronts, apps, and agents. The reference storefront should speak that protocol rather than forcing integrations to inspect custom DOM or intercept requests. + +### Stable scope + +Implement the relevant documented events for interactions owned by the reference storefront, including appropriate page, product, collection, search, and cart events. + +Use Shopify standard actions as the integration contract. Override an action only when the reference theme can provide a better local UI than Shopify's safe default behavior. + +These events are not an analytics system. Analytics stays on Shopify Web Pixels. + +### Acceptance criteria + +- events follow Shopify's documented payload contracts; +- action overrides retain Shopify-compatible fallback behavior; +- integrations do not depend on undocumented DOM conventions or global `fetch` interception; +- event/action behavior is included in real-store/browser smoke coverage. + +References: + +- +- +- + +## 4. Adopt `color_palette` for the reference design-token model + +Shopify recommends `color_palette` for new themes and is focusing future color-system development there. + +### Target + +The reference theme should define one semantic palette in `settings_schema.json`, for example: + +```json +{ + "type": "color_palette", + "id": "colors", + "default": { + "background": "#F8F7F2", + "foreground": "#111820", + "surface": "#FFFFFF", + "accent": "#176C5B", + "accent_contrast": "#FFFFFF" + } +} +``` + +Section/block color controls can reference palette entries as defaults while remaining locally overrideable. + +CSS custom properties remain the runtime design-token bridge. This keeps the rendered theme inspectable and works equally well with native CSS, Vite, or Tailwind. + +Reference: + +## 5. Modern customer accounts + +The reference header should use Shopify's `` storefront component whenever customer accounts are enabled. + +### Target + +- render the component in a location available on desktop and mobile; +- expose the menu through a merchant setting with `customer-account-main-menu` as the default; +- customize only through supported CSS variables, parts, and slots; +- render nothing account-specific when customer accounts are disabled. + +Reference: + +## 6. First-class compiled GitHub deployment workflow + +Liquid Loom repositories can contain authored `src`, tests, package metadata, configuration, and build tooling. Shopify's GitHub theme integration only connects correctly to a branch that already has canonical Shopify theme structure. + +That constraint should become a supported Liquid Loom workflow instead of tribal knowledge. + +### Target model + +```text +main / feature branches +| ++-- authored source ++-- Liquid Loom config ++-- tests and tooling +| ++-- CI: validate + build + | + v +shopify-production branch (or deployment repo) +| ++-- assets/ ++-- blocks/ ++-- config/ ++-- layout/ ++-- locales/ ++-- sections/ ++-- snippets/ ++-- templates/ + | + v +Shopify GitHub integration +``` + +### Deliverables + +- documented deployment-branch and deployment-repository strategies; +- an opt-in generated GitHub Actions workflow or maintained recipe; +- validation before publishing compiled output; +- loop protection for Shopify-originated commits on a connected branch; +- explicit guidance for teams that allow Theme Editor/code-editor changes to write back into GitHub; +- no hosted Liquid Loom deployment service. + +Reference: + +## 7. Shopify CLI 4.8+ development-store workflow + +Shopify CLI 4.8 added `shopify store` commands for creating, listing, inspecting, and deleting dev stores. Liquid Loom should align its real-store validation guidance with that workflow. + +### Target + +Document and optionally script an opt-in flow such as: + +```text +shopify store create dev --demo-data + | + v +liquid-loom build + | + v +shopify theme dev --path dist/theme + | + v +browser smoke + | + v +shopify store delete +``` + +Public CI must continue to work without Shopify credentials. Real-store automation belongs in protected/opt-in workflows. + +Reference: + +## 8. Agent-friendly contracts build on `0.3`, not around Shopify + +PR #25 added the deterministic project model, `explain`, JSON output, preview-aware relationships, and explicit `shopifyLiquidMode`. The modernization should extend that strategy rather than create a separate agent framework. + +### Target + +- keep `doctor`, `check`, and `explain` deterministic and script-friendly; +- ensure new block/component/deployment concepts appear truthfully in the project model where statically provable; +- add architectural diff/report features only if real agent or CI workflows demonstrate the need; +- prefer standard Shopify primitives so agents can use Shopify's own documentation and AI tooling; +- keep generated deployment output visibly disposable and authored source visibly authoritative. + +A Liquid Loom-specific AI runtime or agent SDK is out of scope. + +## 9. July 2026 Liquid preview remains explicit and experimental + +Liquid Loom already supports explicit `stable` and `july-2026-preview` project modes and can report preview block/partial usage. + +That is the correct policy for the modernization release. + +### Policy + +- do not scaffold `{% block %}` or `{% partial %}` by default; +- do not make preview syntax a stable build requirement; +- continue allowing a project to declare preview intent through `shopifyLiquidMode`; +- keep preview relationships visible to `explain` and diagnostics; +- validate preview recipes separately from stable release gates; +- reconsider the default only after Shopify publishes a stable contract. + +Reference: + +## 10. Existing-theme adoption must remain boring and safe + +None of the platform modernization should force an established theme to refactor. + +For existing themes: + +- `liquid-loom init` still moves zero Shopify source files; +- current Vite/Tailwind/Sass/PostCSS/Webpack pipelines remain valid; +- native component asset tags can be adopted file-by-file; +- palettes remain optional; +- block refactors are never automatic; +- standard event/action integration is opt-in per interaction the project owns; +- migration continues to preserve output identity; +- stable mode remains the default unless preview intent is explicit. + +This compatibility rule is non-negotiable. + +## Implementation order for one modernization PR + +The implementation should land as one coherent PR, but be built internally in dependency order so every commit remains understandable: + +1. update platform-alignment docs, roadmap, and acceptance criteria; +2. update the supported Shopify CLI development-tool floor; +3. modernize palette/design-token and customer-account primitives; +4. refactor the reference storefront toward theme-block-first composition; +5. move appropriate component CSS/JS to Shopify-native tags; +6. preserve Vite/Tailwind as explicit tested asset paths; +7. implement standard storefront events/actions; +8. add compiled GitHub deployment workflow/recipe; +9. update project intelligence where new static relationships need representation; +10. expand tests, package smoke, and opt-in real-store/browser validation; +11. keep July-preview validation separate from the stable release gate. + +## Compatibility rules for that PR + +The implementation must not: + +- change the deployable Shopify path for existing organized source; +- invalidate `0.2`/`0.3` projects merely because they use Vite or Tailwind; +- require migration in adopted themes; +- require Shopify credentials for normal unit/package CI; +- depend on preview-only Liquid syntax for stable fixtures; +- change source ownership precedence silently; +- introduce a second storefront runtime; +- hide the generated canonical Shopify theme; +- regress the deterministic project model or `explain --json` contract without an explicit versioning decision. + +Any unavoidable breaking public API change requires an explicit migration note before merge. + +## Validation matrix + +### Public/local automated validation + +- default fresh scaffold builds and passes Theme Check; +- optional Vite path builds and participates in generated-output collision checks; +- optional Tailwind path builds and participates in generated-output collision checks; +- reference and embedded starter remain synchronized; +- existing-theme `init -> hybrid build -> migrate -> build` remains green; +- packed-package smoke exercises the actual published artifact shape; +- native `{% stylesheet %}` / `{% javascript %}` content survives organized-source mapping unchanged; +- `explain --json` remains deterministic across repeated runs; +- stable fixtures require no Shopify developer preview. + +### Real Shopify validation + +- fresh scaffold previews against a dev store; +- product, cart, collection, and search flows work in the browser; +- required no-JavaScript fallbacks remain usable; +- product blocks can be reordered in Theme Editor; +- app blocks work in the intended surfaces; +- `` works when accounts are enabled and disappears cleanly when disabled; +- palette edits propagate through semantic CSS tokens; +- standard storefront event payloads match Shopify contracts; +- the compiled deployment branch/repo is accepted by Shopify GitHub integration; +- Shopify-originated writes do not cause deployment loops. + +### Adoption validation + +Run the modernization against at least one established client-style theme while leaving its current asset pipeline untouched. The upgrade must provide value without requiring block, palette, native-asset, or preview migrations. + +## Success criteria + +The modernization is successful if a developer can accurately describe the project as: + +> Shopify-native Liquid theme development with deterministic source ownership, safe adoption, agent-readable architecture, and modern platform defaults. + +The next release should feel **more native to Shopify**, not more dependent on Liquid Loom-specific abstractions. + +## Deferred work + +Keep these deferred unless real usage earns them: + +- broad plugin/hook systems; +- arbitrary source/mapping plugins; +- hosted deployment infrastructure; +- headless/Hydrogen support; +- React/Vue/Svelte storefront runtimes; +- a custom analytics layer; +- a Liquid Loom-specific AI agent runtime; +- stable defaults based on developer-preview Liquid syntax. diff --git a/docs/examples/shopify-deploy.yml b/docs/examples/shopify-deploy.yml new file mode 100644 index 0000000..7ddf210 --- /dev/null +++ b/docs/examples/shopify-deploy.yml @@ -0,0 +1,61 @@ +name: Build Shopify deployment branch + +on: + workflow_dispatch: + push: + branches: [main] + +permissions: + contents: write + +concurrency: + group: shopify-theme-deployment + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - run: corepack enable + - run: pnpm install --frozen-lockfile + - run: pnpm validate + + - name: Prepare compiled branch + shell: bash + run: | + set -euo pipefail + deploy_dir="$RUNNER_TEMP/shopify-production" + if git ls-remote --exit-code --heads origin shopify-production >/dev/null 2>&1; then + git fetch origin shopify-production:refs/remotes/origin/shopify-production + git worktree add --detach "$deploy_dir" origin/shopify-production + else + git worktree add --detach "$deploy_dir" HEAD + find "$deploy_dir" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} + + fi + + for directory in assets blocks config layout locales sections snippets templates; do + rm -rf "$deploy_dir/$directory" + if [ -d "dist/theme/$directory" ]; then + cp -R "dist/theme/$directory" "$deploy_dir/$directory" + fi + done + + git -C "$deploy_dir" checkout -B shopify-production + git -C "$deploy_dir" config user.name "github-actions[bot]" + git -C "$deploy_dir" config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git -C "$deploy_dir" add -A + if git -C "$deploy_dir" diff --cached --quiet; then + echo "No deployable theme changes." + exit 0 + fi + git -C "$deploy_dir" commit -m "Deploy Shopify theme from ${GITHUB_SHA}" + git -C "$deploy_dir" push origin HEAD:shopify-production diff --git a/liquid-loom.config.ts b/liquid-loom.config.ts index a5790ef..16c3c11 100644 --- a/liquid-loom.config.ts +++ b/liquid-loom.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "liquid-loom"; export default defineConfig({ + viteConfig: false, performance: { maxAssetBytes: 500_000, maxBuildMs: 10_000, diff --git a/package.json b/package.json index 494bf81..678b360 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "liquid-loom", "version": "0.2.0", - "description": "A deterministic engineering layer for Shopify themes: source architecture, safe incremental adoption, Vite, and Tailwind CSS.", + "description": "A deterministic engineering layer for Shopify Liquid themes: safe adoption, explicit ownership, project intelligence, and modern platform defaults.", "type": "module", "license": "MIT", "bin": { diff --git a/packages/create-liquid-loom/template/README.md b/packages/create-liquid-loom/template/README.md index 79041d9..8bf3b65 100644 --- a/packages/create-liquid-loom/template/README.md +++ b/packages/create-liquid-loom/template/README.md @@ -2,6 +2,8 @@ A Shopify storefront built with [Liquid Loom](https://github.com/yotamon/Liquid-Loom). +The starter uses Shopify-native component CSS/JavaScript by default and keeps Vite/Tailwind optional. + ## Start ```bash @@ -9,4 +11,4 @@ pnpm install pnpm dev ``` -Use `pnpm build` for a deployable theme in `dist/theme`, `pnpm doctor` for environment diagnostics, and `pnpm validate` before shipping. +Use `pnpm build` for a deployable theme in `dist/theme`, `pnpm explain -- --json` for the deterministic project model, `pnpm doctor` for environment diagnostics, and `pnpm validate` before shipping. diff --git a/packages/create-liquid-loom/template/liquid-loom.config.ts b/packages/create-liquid-loom/template/liquid-loom.config.ts index a5790ef..16c3c11 100644 --- a/packages/create-liquid-loom/template/liquid-loom.config.ts +++ b/packages/create-liquid-loom/template/liquid-loom.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "liquid-loom"; export default defineConfig({ + viteConfig: false, performance: { maxAssetBytes: 500_000, maxBuildMs: 10_000, diff --git a/packages/create-liquid-loom/template/package.json b/packages/create-liquid-loom/template/package.json index 4dfc899..fb7abc0 100644 --- a/packages/create-liquid-loom/template/package.json +++ b/packages/create-liquid-loom/template/package.json @@ -15,6 +15,7 @@ "clean": "liquid-loom clean", "dev": "liquid-loom dev", "doctor": "liquid-loom doctor", + "explain": "liquid-loom explain", "format": "prettier --write .", "format:check": "prettier --check .", "shopify:push": "shopify theme push --path dist/theme", @@ -24,12 +25,10 @@ "watch": "liquid-loom watch" }, "devDependencies": { - "@shopify/cli": "^4.6.0", + "@shopify/cli": "^4.8.0", "@shopify/prettier-plugin-liquid": "^1.11.0", - "@tailwindcss/vite": "^4.3.3", "liquid-loom": "^0.2.0", "prettier": "^3.9.6", - "tailwindcss": "^4.3.3", "typescript": "^7.0.2" } } diff --git a/packages/create-liquid-loom/template/src/entrypoints/theme.js b/packages/create-liquid-loom/template/src/entrypoints/theme.js deleted file mode 100644 index fd16943..0000000 --- a/packages/create-liquid-loom/template/src/entrypoints/theme.js +++ /dev/null @@ -1,118 +0,0 @@ -import "../styles/theme.css"; - -class MenuDisclosure extends HTMLElement { - connectedCallback() { - this.button = this.querySelector("button"); - this.panel = this.querySelector("[data-menu-panel]"); - if (!this.button || !this.panel) return; - - this.button.addEventListener("click", () => this.toggle()); - this.addEventListener("keydown", (event) => { - if (event.key === "Escape" && this.isOpen()) { - this.close(); - this.button.focus(); - } - }); - } - - isOpen() { - return this.button.getAttribute("aria-expanded") === "true"; - } - - toggle() { - this.isOpen() ? this.close() : this.open(); - } - - open() { - this.button.setAttribute("aria-expanded", "true"); - this.panel.removeAttribute("hidden"); - document.documentElement.classList.add("menu-open"); - } - - close() { - this.button.setAttribute("aria-expanded", "false"); - this.panel.setAttribute("hidden", ""); - document.documentElement.classList.remove("menu-open"); - } -} - -class QuantityInput extends HTMLElement { - connectedCallback() { - this.input = this.querySelector("input[type='number']"); - this.querySelectorAll("button").forEach((button) => { - button.addEventListener("click", () => this.change(button.dataset.quantityAction)); - }); - } - - change(action) { - if (!this.input) return; - const step = Number(this.input.step) || 1; - const minimum = Number(this.input.min) || 1; - const current = Number(this.input.value) || minimum; - this.input.value = String(Math.max(minimum, current + (action === "increase" ? step : -step))); - this.input.dispatchEvent(new Event("change", { bubbles: true })); - } -} - -class PredictiveSearch extends HTMLElement { - connectedCallback() { - this.input = this.querySelector("input[type='search']"); - this.results = this.querySelector("[data-predictive-search-results]"); - if (!this.input || !this.results) return; - this.input.addEventListener("input", () => { - clearTimeout(this.timer); - this.timer = setTimeout(() => this.search(), 180); - }); - } - - async search() { - const query = this.input.value.trim(); - this.controller?.abort(); - if (query.length < 2) { - this.results.replaceChildren(); - this.input.setAttribute("aria-expanded", "false"); - return; - } - - this.controller = new AbortController(); - try { - const url = `/search/suggest?q=${encodeURIComponent(query)}&resources[type]=product§ion_id=predictive-search`; - const response = await fetch(url, { signal: this.controller.signal }); - if (!response.ok) throw new Error(`Search request failed: ${response.status}`); - const document = new DOMParser().parseFromString(await response.text(), "text/html"); - const content = document.querySelector(".predictive-search__results"); - this.results.replaceChildren(...(content ? [content] : [])); - this.input.setAttribute("aria-expanded", content ? "true" : "false"); - } catch (error) { - if (error.name !== "AbortError") console.error(error); - } - } -} - -class ProductForm extends HTMLElement { - connectedCallback() { - this.select = this.querySelector("[data-variant-select]"); - this.submit = this.querySelector("button[type='submit']"); - const data = this.querySelector("[data-product-variants]"); - if (!this.select || !data) return; - this.variants = JSON.parse(data.textContent); - this.select.addEventListener("change", () => this.variantChanged()); - } - - variantChanged() { - const variant = this.variants.find(({ id }) => String(id) === this.select.value); - if (!variant) return; - this.submit?.toggleAttribute("disabled", !variant.available); - const url = new URL(window.location.href); - url.searchParams.set("variant", variant.id); - window.history.replaceState({}, "", url); - } -} - -if (!customElements.get("menu-disclosure")) customElements.define("menu-disclosure", MenuDisclosure); -if (!customElements.get("quantity-input")) customElements.define("quantity-input", QuantityInput); -if (!customElements.get("predictive-search")) customElements.define("predictive-search", PredictiveSearch); -if (!customElements.get("product-form")) customElements.define("product-form", ProductForm); - -document.documentElement.classList.remove("no-js"); -document.documentElement.classList.add("js"); diff --git a/src/styles/theme.css b/packages/create-liquid-loom/template/src/public/theme.css similarity index 72% rename from src/styles/theme.css rename to packages/create-liquid-loom/template/src/public/theme.css index 3ee152c..69995dd 100644 --- a/src/styles/theme.css +++ b/packages/create-liquid-loom/template/src/public/theme.css @@ -1,6 +1,3 @@ -@import "tailwindcss"; -@source "../theme/**/*.liquid"; - :root { --color-background: #f8f7f2; --color-foreground: #111820; @@ -185,80 +182,6 @@ textarea { text-align: center; } -.site-header { - position: sticky; - top: 0; - z-index: 40; - border-bottom: 1px solid color-mix(in srgb, var(--color-border) 75%, transparent); - background: color-mix(in srgb, var(--color-background) 88%, transparent); - backdrop-filter: blur(18px); -} - -.site-header__inner { - display: grid; - min-height: 4.8rem; - grid-template-columns: 1fr auto 1fr; - align-items: center; - gap: 1rem; -} - -.site-header__brand { - justify-self: center; - font-size: 1.1rem; - font-weight: 800; - letter-spacing: -0.03em; - text-decoration: none; -} - -.site-header__logo { - width: auto; - max-height: 2.3rem; -} - -.site-header__nav, -.site-header__actions { - display: flex; - align-items: center; - gap: 1.25rem; -} - -.site-header__nav a, -.site-header__actions a { - font-size: 0.9rem; - font-weight: 650; - text-decoration: none; -} - -.site-header__actions { - justify-self: end; -} - -.site-header__menu-button { - display: none; - border: 0; - background: transparent; - cursor: pointer; - padding: 0.5rem; -} - -.mobile-menu { - border-top: 1px solid var(--color-border); - background: var(--color-background); - padding: 1rem; -} - -.mobile-menu[hidden] { - display: none; -} - -.mobile-menu a { - display: block; - padding: 0.8rem; - font-size: 1.15rem; - font-weight: 700; - text-decoration: none; -} - .hero { position: relative; overflow: hidden; @@ -394,80 +317,32 @@ textarea { text-decoration: line-through; } -.product { - display: grid; - grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr); - gap: clamp(2rem, 7vw, 7rem); -} - -.product__gallery { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 1rem; -} - -.product__gallery img { - width: 100%; - border-radius: var(--radius-md); -} - -.product__details { - align-self: start; -} - -.product__details .price { - display: block; - margin-block: 1rem; - font-size: 1.2rem; -} - -.field { - display: grid; - gap: 0.45rem; - margin-block: 1rem; +.collection-header { + max-width: 48rem; + margin-bottom: 3rem; } -.field label { - font-size: 0.85rem; - font-weight: 700; +.facets { + display: flex; + flex-wrap: wrap; + align-items: start; + gap: 0.75rem; + margin-bottom: 2rem; } -.field input, -.field select, -.field textarea { - width: 100%; - min-height: 3rem; +.facet { + position: relative; border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface); - padding: 0.7rem 0.85rem; + padding: 0.65rem 0.8rem; } -.quantity { +.facet__options { display: grid; - width: 9rem; - grid-template-columns: 2.6rem 1fr 2.6rem; - border: 1px solid var(--color-border); - border-radius: 999px; - background: var(--color-surface); -} - -.quantity button, -.quantity input { - min-height: 2.8rem; - border: 0; - background: transparent; - text-align: center; -} - -.quantity input { - width: 100%; - appearance: textfield; -} - -.collection-header { - max-width: 48rem; - margin-bottom: 3rem; + min-width: 14rem; + gap: 0.55rem; + padding-top: 0.75rem; } .cart-table { @@ -525,53 +400,6 @@ textarea { border-radius: var(--radius-lg); } -.facets { - display: flex; - flex-wrap: wrap; - align-items: start; - gap: 0.75rem; - margin-bottom: 2rem; -} - -.facet { - position: relative; - border: 1px solid var(--color-border); - border-radius: var(--radius-sm); - background: var(--color-surface); - padding: 0.65rem 0.8rem; -} - -.facet__options { - display: grid; - min-width: 14rem; - gap: 0.55rem; - padding-top: 0.75rem; -} - -.predictive-search__results { - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - margin-top: 0.5rem; - background: var(--color-surface); - box-shadow: var(--shadow-soft); - padding: 1rem; -} - -.predictive-search__results ul { - display: grid; - gap: 0.65rem; - margin: 0; - padding: 0; - list-style: none; -} - -.predictive-search__results a { - display: flex; - justify-content: space-between; - gap: 1rem; - text-decoration: none; -} - .empty-state { max-width: 38rem; margin-inline: auto; @@ -626,24 +454,8 @@ textarea { } @media (max-width: 900px) { - .site-header__inner { - grid-template-columns: 1fr auto 1fr; - } - - .site-header__nav { - display: none; - } - - .site-header__menu-button { - display: inline-flex; - } - - .hero__grid, - .product { - grid-template-columns: 1fr; - } - .hero__grid { + grid-template-columns: 1fr; padding-block: 4rem; } @@ -669,12 +481,7 @@ textarea { width: min(calc(100% - 1.25rem), var(--page-width)); } - .site-header__actions a:not([href*="cart"]) { - display: none; - } - .product-grid, - .product__gallery, .site-footer__grid { grid-template-columns: 1fr; } diff --git a/packages/create-liquid-loom/template/src/theme/blocks/product-description.liquid b/packages/create-liquid-loom/template/src/theme/blocks/product-description.liquid new file mode 100644 index 0000000..fd4725d --- /dev/null +++ b/packages/create-liquid-loom/template/src/theme/blocks/product-description.liquid @@ -0,0 +1,4 @@ +{%- if product.description != blank -%} +

{{ product.description }}
+{%- endif -%} +{% schema %}{"name":"Product description","presets":[{"name":"Product description"}]}{% endschema %} diff --git a/packages/create-liquid-loom/template/src/theme/blocks/product-price.liquid b/packages/create-liquid-loom/template/src/theme/blocks/product-price.liquid new file mode 100644 index 0000000..475a02f --- /dev/null +++ b/packages/create-liquid-loom/template/src/theme/blocks/product-price.liquid @@ -0,0 +1,2 @@ +
{% render 'price', product: product %}
+{% schema %}{"name":"Product price","presets":[{"name":"Product price"}]}{% endschema %} diff --git a/packages/create-liquid-loom/template/src/theme/blocks/product-purchase.liquid b/packages/create-liquid-loom/template/src/theme/blocks/product-purchase.liquid new file mode 100644 index 0000000..49962f7 --- /dev/null +++ b/packages/create-liquid-loom/template/src/theme/blocks/product-purchase.liquid @@ -0,0 +1,192 @@ + + {%- form 'product', product, id: 'ProductForm-' | append: block.id, novalidate: 'novalidate', data-product-form: '' -%} + {%- unless product.has_only_default_variant -%} +
+ +
+ {%- else -%} + + {%- endunless -%} + {%- if product.selling_plan_groups.size > 0 -%} +
+ +
+ {%- endif -%} +
+ +
+ +

+ {%- endform -%} + +
+ +{% stylesheet %} + .field { + display: grid; + gap: 0.45rem; + margin-block: 1rem; + } + .field label { + font-size: 0.85rem; + font-weight: 700; + } + .field input, + .field select, + .field textarea { + width: 100%; + min-height: 3rem; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: var(--color-surface); + padding: 0.7rem 0.85rem; + } + .quantity { + display: grid; + width: 9rem; + grid-template-columns: 2.6rem 1fr 2.6rem; + border: 1px solid var(--color-border); + border-radius: 999px; + background: var(--color-surface); + } + .quantity button, + .quantity input { + min-height: 2.8rem; + border: 0; + background: transparent; + text-align: center; + } + .quantity input { + width: 100%; + appearance: textfield; + } + .product-form__status { + min-height: 1.5em; + margin-block: 0.75rem 0; + } +{% endstylesheet %} + +{% javascript %} + class QuantityInput extends HTMLElement { + connectedCallback() { + this.input = this.querySelector("input[type='number']"); + this.querySelectorAll('button').forEach((button) => + button.addEventListener('click', () => this.change(button.dataset.quantityAction)) + ); + } + change(action) { + if (!this.input) return; + const step = Number(this.input.step) || 1; + const minimum = Number(this.input.min) || 1; + const current = Number(this.input.value) || minimum; + this.input.value = String(Math.max(minimum, current + (action === 'increase' ? step : -step))); + this.input.dispatchEvent(new Event('change', { bubbles: true })); + } + } + + class ProductForm extends HTMLElement { + connectedCallback() { + this.form = this.querySelector('form'); + this.select = this.querySelector('[data-variant-select]'); + this.submit = this.querySelector("button[type='submit']"); + this.status = this.querySelector('[data-product-form-status]'); + const data = this.querySelector('[data-product-variants]'); + if (data) this.variants = JSON.parse(data.textContent); + this.select?.addEventListener('change', () => this.variantChanged()); + this.form?.addEventListener('submit', (event) => this.submitWithStandardAction(event)); + } + variantChanged() { + const variant = this.variants?.find(({ id }) => String(id) === this.select.value); + if (!variant) return; + this.submit?.toggleAttribute('disabled', !variant.available); + const url = new URL(window.location.href); + url.searchParams.set('variant', variant.id); + window.history.replaceState({}, '', url); + } + async submitWithStandardAction(event) { + if (!window.Shopify?.actions?.updateCart || !this.form) return; + event.preventDefault(); + const formData = new FormData(this.form); + const merchandiseId = formData.get('id'); + if (!merchandiseId) return; + const line = { + merchandiseId: String(merchandiseId), + quantity: Math.max(1, Number(formData.get('quantity')) || 1) + }; + const sellingPlanId = formData.get('selling_plan'); + if (sellingPlanId) line.sellingPlanId = String(sellingPlanId); + this.submit?.setAttribute('disabled', ''); + if (this.status) this.status.textContent = ''; + try { + const { cart, userErrors, warnings } = await window.Shopify.actions.updateCart( + { lines: [line] }, + { event: { context: 'product' } } + ); + if (userErrors?.length) { + if (this.status) this.status.textContent = userErrors[0].message; + return; + } + if (this.status) this.status.textContent = warnings?.[0]?.message || 'Added to cart.'; + if (cart) + document.querySelectorAll('[data-cart-count]').forEach((node) => { + node.textContent = String(cart.totalQuantity); + }); + } catch (error) { + console.error(error); + if (this.status) this.status.textContent = 'Unable to update the cart. Try again.'; + } finally { + const variant = this.variants?.find(({ id }) => String(id) === String(formData.get('id'))); + this.submit?.toggleAttribute('disabled', variant ? !variant.available : false); + } + } + } + if (!customElements.get('quantity-input')) customElements.define('quantity-input', QuantityInput); + if (!customElements.get('product-form')) customElements.define('product-form', ProductForm); +{% endjavascript %} + +{% schema %}{"name":"Product purchase","presets":[{"name":"Product purchase"}]}{% endschema %} diff --git a/packages/create-liquid-loom/template/src/theme/blocks/product-title.liquid b/packages/create-liquid-loom/template/src/theme/blocks/product-title.liquid new file mode 100644 index 0000000..2be29a4 --- /dev/null +++ b/packages/create-liquid-loom/template/src/theme/blocks/product-title.liquid @@ -0,0 +1,2 @@ +

{{ product.title | escape }}

+{% schema %}{"name":"Product title","presets":[{"name":"Product title"}]}{% endschema %} diff --git a/packages/create-liquid-loom/template/src/theme/blocks/product-vendor.liquid b/packages/create-liquid-loom/template/src/theme/blocks/product-vendor.liquid new file mode 100644 index 0000000..5af67ea --- /dev/null +++ b/packages/create-liquid-loom/template/src/theme/blocks/product-vendor.liquid @@ -0,0 +1,4 @@ +{%- if product.vendor != blank -%} +

{{ product.vendor | escape }}

+{%- endif -%} +{% schema %}{"name":"Product vendor","presets":[{"name":"Product vendor"}]}{% endschema %} diff --git a/packages/create-liquid-loom/template/src/theme/config/settings_data.json b/packages/create-liquid-loom/template/src/theme/config/settings_data.json index 5f90608..03b5e60 100644 --- a/packages/create-liquid-loom/template/src/theme/config/settings_data.json +++ b/packages/create-liquid-loom/template/src/theme/config/settings_data.json @@ -1,19 +1,9 @@ { "current": { - "color_background": "#f8f7f2", - "color_foreground": "#111820", - "color_surface": "#ffffff", - "color_accent": "#176c5b", - "color_accent_contrast": "#ffffff", "page_width": 1200 }, "presets": { "Default": { - "color_background": "#f8f7f2", - "color_foreground": "#111820", - "color_surface": "#ffffff", - "color_accent": "#176c5b", - "color_accent_contrast": "#ffffff", "page_width": 1200 } } diff --git a/packages/create-liquid-loom/template/src/theme/config/settings_schema.json b/packages/create-liquid-loom/template/src/theme/config/settings_schema.json index f13f9bf..285db0c 100644 --- a/packages/create-liquid-loom/template/src/theme/config/settings_schema.json +++ b/packages/create-liquid-loom/template/src/theme/config/settings_schema.json @@ -17,11 +17,19 @@ { "name": "Colors", "settings": [ - { "type": "color", "id": "color_background", "label": "Background", "default": "#f8f7f2" }, - { "type": "color", "id": "color_foreground", "label": "Text", "default": "#111820" }, - { "type": "color", "id": "color_surface", "label": "Surface", "default": "#ffffff" }, - { "type": "color", "id": "color_accent", "label": "Accent", "default": "#176c5b" }, - { "type": "color", "id": "color_accent_contrast", "label": "Accent text", "default": "#ffffff" } + { + "type": "color_palette", + "id": "colors", + "default": { + "background": "#F8F7F2", + "foreground": "#111820", + "surface": "#FFFFFF", + "muted": "#66717C", + "border": "#DCE1DD", + "accent": "#176C5B", + "accent_contrast": "#FFFFFF" + } + } ] } ] diff --git a/packages/create-liquid-loom/template/src/theme/layout/theme.liquid b/packages/create-liquid-loom/template/src/theme/layout/theme.liquid index af4d7ba..640a028 100644 --- a/packages/create-liquid-loom/template/src/theme/layout/theme.liquid +++ b/packages/create-liquid-loom/template/src/theme/layout/theme.liquid @@ -1,24 +1,25 @@ - + - + {% render 'meta-tags' %} - {{ 'style.css' | asset_url | stylesheet_tag: preload: true }} - + {{ 'theme.css' | asset_url | stylesheet_tag: preload: true }} @@ -28,13 +29,8 @@ - {% sections 'header-group' %} - -
- {{ content_for_layout }} -
- +
{{ content_for_layout }}
{% sections 'footer-group' %} diff --git a/packages/create-liquid-loom/template/src/theme/sections/cart/main-cart.liquid b/packages/create-liquid-loom/template/src/theme/sections/cart/main-cart.liquid index 2e98701..e833524 100644 --- a/packages/create-liquid-loom/template/src/theme/sections/cart/main-cart.liquid +++ b/packages/create-liquid-loom/template/src/theme/sections/cart/main-cart.liquid @@ -1,64 +1,58 @@ -
-
-

{{ 'cart.title' | t }}

- - {%- if cart.empty? -%} -
-

{{ 'cart.empty' | t }}

- {{ 'actions.continue_shopping' | t }} -
- {%- else -%} -
- - - - - - - - - - {%- for item in cart.items -%} + +
+
+

{{ 'cart.title' | t }}

+ {%- if cart.empty? -%} +
+

{{ 'cart.empty' | t }}

+ {{ 'actions.continue_shopping' | t }} +
+ {%- else -%} + +
ProductQuantityTotal
+ - - - + + + - {%- endfor -%} - -
-
- {%- if item.image -%} - {{ item.image | image_url: width: 200 | image_tag: loading: 'lazy' }} - {%- endif -%} -
- {{ item.product.title | escape }}
- {{ item.variant.title }} -
-
-
- - {{ item.final_line_price | money }}ProductQuantityTotal
- - -
- {%- endif -%} -
-
- -{% schema %} -{ - "name": "Main cart", - "settings": [] -} -{% endschema %} + + + {%- for item in cart.items -%} + + +
+ {%- if item.image -%} + {{ item.image | image_url: width: 200 | image_tag: loading: 'lazy' }} + {%- endif -%} +
+ {{ item.product.title | escape }}
+ {{ item.variant.title }} +
+
+ + + + + {{ item.final_line_price | money }} + + {%- endfor -%} + + + + + {%- endif -%} + + + +{% schema %}{"name":"Main cart","settings":[]}{% endschema %} diff --git a/packages/create-liquid-loom/template/src/theme/sections/collections/main-collection.liquid b/packages/create-liquid-loom/template/src/theme/sections/collections/main-collection.liquid index c77edf1..f3d096a 100644 --- a/packages/create-liquid-loom/template/src/theme/sections/collections/main-collection.liquid +++ b/packages/create-liquid-loom/template/src/theme/sections/collections/main-collection.liquid @@ -1,27 +1,28 @@
-
-

Collection

-

{{ collection.title | escape }}

- {%- if collection.description != blank -%} -
{{ collection.description }}
- {%- endif -%} -
- + +
+

Collection

+

{{ collection.title | escape }}

+ {%- if collection.description != blank -%} +
{{ collection.description }}
+ {%- endif -%} +
+
{%- if collection.filters.size > 0 -%}
{%- for filter in collection.filters -%}
- {{ filter.label | escape -}} + {{ filter.label | escape }} {%- if filter.active_values.size > 0 %} ({{ filter.active_values.size }}){% endif %}
{%- case filter.type -%} {%- when 'boolean', 'list' -%} {%- for value in filter.values -%} - + {{- value.label | escape }} ({{ value.count }}) {%- endfor -%} {%- when 'price_range' -%} -
{%- endfor -%} - - Clear + Clear
{%- endif -%} - {%- paginate collection.products by section.settings.products_per_page -%} - {%- if paginate.pages > 1 -%}
{{ paginate | default_pagination }}
{%- endif -%} {%- endpaginate -%}
- -{% schema %} -{ - "name": "Main collection", - "settings": [ - { - "type": "range", - "id": "products_per_page", - "min": 8, - "max": 32, - "step": 4, - "label": "Products per page", - "default": 16 - } - ] -} -{% endschema %} +{% schema %}{"name":"Main collection","settings":[{"type":"range","id":"products_per_page","min":8,"max":32,"step":4,"label":"Products per page","default":16}]}{% endschema %} diff --git a/packages/create-liquid-loom/template/src/theme/sections/global/header.liquid b/packages/create-liquid-loom/template/src/theme/sections/global/header.liquid index 6248eaa..71ff71e 100644 --- a/packages/create-liquid-loom/template/src/theme/sections/global/header.liquid +++ b/packages/create-liquid-loom/template/src/theme/sections/global/header.liquid @@ -6,7 +6,6 @@ {{ link.title | escape }} {%- endfor -%} - - {%- if section.settings.logo != blank -%} {{ @@ -33,13 +31,16 @@ {{ shop.name | escape }} {%- endif -%} -
{{ 'actions.search' | t }} - {{ 'cart.title' | t }} ({{ cart.item_count }}) + {%- if shop.customer_accounts_enabled -%} + + {%- endif -%} + + {{- 'cart.title' | t }} ({{ cart.item_count }})
- - - {%- if section.settings.logo != blank -%} {{ @@ -33,13 +31,16 @@ {{ shop.name | escape }} {%- endif -%} -
{{ 'actions.search' | t }} - {{ 'cart.title' | t }} ({{ cart.item_count }}) + {%- if shop.customer_accounts_enabled -%} + + {%- endif -%} + + {{- 'cart.title' | t }} ({{ cart.item_count }})
-