Build tooling for Shopify themes using Vite, Sass, and a flat assets/ output
compatible with Shopify's CDN.
- Sass compilation through
sass-embeddedand native CSS cascade layers. - Flat, unhashed assets with Shopify
?v=version query parameters. - Image, font, and SVG optimization.
- Automatic SCSS entrypoints for component, section, and snippet styles.
- A generated
snippets/vite-tag.liquidthat maps each entrypoint to its asset. - ESLint, Stylelint, Prettier, TypeScript checks, and asset-name verification.
yarn build compiles frontend/ into flat, unhashed files under assets/ and
regenerates snippets/vite-tag.liquid. Stale output from the previous build is
removed first, and versionNumbers keeps the ?v= query that Shopify's
asset_url appends so browsers revalidate on change. Liquid templates never
hard-code asset paths; they call the snippet:
{% render 'vite-tag', entry: 'base.scss' %}Use the Yarn version pinned in package.json:
yarn install
yarn build:verifyTo run the dev server, authenticate Shopify CLI separately with
shopify theme login, then start Vite and the CLI together:
yarn dev| Command | Description |
|---|---|
yarn dev |
Runs Vite and Shopify CLI together. |
yarn dev:vite |
Runs Vite only. |
yarn build |
Builds assets and regenerates vite-tag.liquid. |
yarn build:verify |
Builds and validates asset names and flat output. |
yarn lint |
Runs Stylelint and ESLint, including Node utilities, scripts, and ESLint configuration. |
yarn lint:fix |
Applies available lint fixes. |
yarn format |
Formats source, utility, root configuration, and documentation files. |
yarn check:types |
Checks frontend and Node/Vite configuration types without emitting files. |
yarn check:entrypoints |
Tests generated-entrypoint naming rules. |
yarn check:sass |
Compiles Sass fixtures to validate function and mixin arguments (27 tests). |
yarn test |
Runs the full Vitest suite (Sass validation, entrypoints, utilities). |
yarn test:watch |
Runs the suite in watch mode. |
yarn test:coverage |
Runs the suite with a V8 coverage report. |
yarn clean |
Removes dependencies, build artifacts, and caches. |
yarn clean:build |
Removes build artifacts and Vite caches only. |
yarn fresh |
Cleans, installs, builds, and verifies. |
frontend/
├── entrypoints/ # base.scss/base.mts plus generated SCSS entrypoints
├── styles/
│ ├── component/ # reusable UI styles
│ ├── section/ # Shopify section styles
│ ├── snippet/ # Shopify snippet styles
│ └── … # settings, tools, generic, elements, objects, utilities
├── images/
├── fonts/
└── svg/
assets/ # generated files published to Shopify
snippets/vite-tag.liquid # generated at build time
The core is framework-agnostic: no framework-specific Vite plugin, ESLint plugin, or component/testing harness is installed. To add one:
- Install the framework and its Vite plugin.
- Import the plugin in
vite.config.jsand add it to thepluginsarray. - If the framework compiles JSX/TSX, set
compilerOptions.jsxintsconfig.jsonto the value its tooling expects (for example,react-jsxfor React). - Optionally install the framework's ESLint plugin and add it to
eslint.config.js. - Create a component under
frontend/components/and an entrypoint underfrontend/entrypoints/that imports and mounts it. - Load the entrypoint from Liquid:
{% render 'vite-tag', entry: 'counter.js' %}Framework plugins (verified on npm):
| Framework | Vite plugin |
|---|---|
| React | @vitejs/plugin-react |
| Vue | @vitejs/plugin-vue |
| Svelte | @sveltejs/vite-plugin-svelte |
Create a Sass partial under frontend/styles/component/, section/, or
snippet/. Partial names must use a leading Sass underscore followed by
lowercase kebab-case using letters only:
_button-group.scss # valid → component-button-group.scss
_rich-text.scss # valid → section-rich-text.scss
_button_group.scss # invalid
_button-group-2.scss # invalid
The generator creates the matching entrypoint in frontend/entrypoints/.
Reference it from Liquid with the generated snippet:
{% render 'vite-tag', entry: 'component-button.scss' %}Do not edit generated entrypoints or snippets/vite-tag.liquid directly.
Styles follow ITCSS and use this layer order:
@layer settings, generic, elements, objects, components, sections, snippets, utilities;The declaration lives in frontend/styles/generic/_layer-order.scss and is
loaded first by frontend/entrypoints/base.scss. See
frontend/styles/README.md for folder-specific
conventions and the merchant color-token bridge.
CI (.github/workflows/ci.yml) runs the same checks you can run locally:
yarn lint— ESLint and Stylelint, including Node utilities and scripts.yarn format:check— Prettier over source, configuration, and documentation.yarn check:typesandyarn check:types:coverage— type checks plus type coverage for frontend and Node/Vite configuration.yarn check:sass— compiles Sass fixtures to validate function and mixin arguments (27 tests).yarn check:entrypoints— generated-entrypoint naming rules.yarn testandyarn test:coverage— Vitest suite; coverage fails below the thresholds invitest.config.mjs: 44% statements, 39% branches, 70% functions, 46% lines.yarn build:verify— flat names, no hashes, and manifest parity.yarn check:themeand a dependency audit (yarn audit, CI at--severity high); workflows are linted with actionlint.
This project targets the last 2 versions of evergreen browsers (Chrome, Edge, Firefox, Safari). This baseline is informed by the use of modern CSS features including CSS Cascade Layers (@layer) and CSS custom properties, which require:
- Chrome 99+ or Edge 99+
- Firefox 97+
- Safari 15.4+
Internet Explorer is not supported.
DEPLOYMENT.md covers environments and rollback steps. RELEASING.md documents the release runbook. Version history lives in CHANGELOG.md.
MIT — see LICENSE.