This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
npm run dev # Run Shopify CLI dev + Vite dev server in parallel (hot reload)
npm run build # TypeScript compile + Vite bundle (outputs to assets/)
npm run deploy # Build then push to Shopify store
shopify theme check # Lint Liquid files against theme-check:recommended rulesshopify theme dev, shopify theme push, and vite build are also available individually via the corresponding npm scripts.
Package manager: Bun (bun install to install dependencies). npm scripts still work as wrappers.
These principles define how all code in this theme should be written. Follow them strictly when adding or modifying any feature.
Every distinct feature or component lives in its own file. Do not bundle unrelated logic together.
frontend/entrypoints/js/
global/ # always loaded (variant-picker, modal, etc.) — exists
components/ # feature-specific (cart-drawer, predictive-search, etc.) — not yet created
utils/ # pure utility functions (debounce, fetch-helpers, etc.) — not yet created
- One file = one responsibility. A
cart-drawer.jshandles only the cart drawer. - Global utilities (e.g.
debounce,fetchSection) go injs/utils/and are imported where needed. - Avoid adding logic directly to
theme.js. Keep it a thin entry point that only orchestrates dynamic imports.
Use dynamic imports to load components on demand, not on every page.
// theme.js — only load what the page actually needs
if (document.querySelector('variant-picker')) {
import('./js/global/variant-picker.js');
}- Never statically import a component that isn't guaranteed to be on every page.
- Heavy components (e.g. modals, drawers, video players) must always be lazy-loaded.
- CSS scoped to a section/block belongs in that file's
{% stylesheet %}tag, not in the global bundle.
Build components as extensible base classes so future variants don't require copy-paste.
// Base class with shared logic
class Drawer extends HTMLElement {
open() { ... }
close() { ... }
trapFocus() { ... }
}
// Specialized class extends and overrides what it needs
class CartDrawer extends Drawer {
open() {
super.open();
this.fetchCart();
}
}- Prefer custom elements (
HTMLElementsubclasses) for interactive UI components. They are self-contained and compose cleanly with Liquid. - Shared behaviors (focus trap, overlay, scroll-lock) live in base classes or utility functions — never duplicated.
- Schema settings follow the same principle: prefer a generic
groupblock that composes with other blocks over one-off section-specific layout blocks.
- Component-scoped styles: use
{% stylesheet %}in sections/blocks. Shopify deduplicates them automatically. - Single CSS property from schema → inline CSS variable:
style="--gap: {{ block.settings.gap }}px" - Multiple CSS properties from schema → CSS class:
class="{{ block.settings.layout }}" - Global design tokens live in
theme.cssunder@theme {}. Do not hardcode colors, spacing, or type sizes elsewhere. - Tailwind utilities for layout and spacing;
{% stylesheet %}for structural/component CSS that Tailwind doesn't cover.
This is a Shopify theme built on top of the Shopify Skeleton Theme. It uses Vite (via rolldown-vite — the Rolldown-powered Vite fork, aliased as vite in package.json) for bundling and Tailwind CSS v4 for styling.
frontend/entrypoints/— source files (TypeScript, CSS). Edit these.assets/— Vite output with hashed filenames. Do not edit directly.snippets/vite-tag.liquid— auto-generated byvite-plugin-shopify; maps frontend entrypoints to built assets. Do not edit.
Tailwind CSS v4 is configured via @theme and @source directives directly in frontend/entrypoints/theme.css. There is no tailwind.config.js.
CSS is split into layered entry files:
theme.css— root entry, defines CSS custom properties and global stylescss/typography.css— font sizes and weightscss/spacing.css— spacing scale with responsive breakpointscss/utilities.css— custom Tailwind utilities (scrollbar-hidden,flex-00auto, etc.)
JS is split into per-feature entrypoints. Each file in frontend/entrypoints/ becomes a separate Vite chunk. Loading is controlled by Liquid via {% render 'vite-tag' %} placed directly in the relevant section — not in theme.liquid.
| Entrypoint | Loaded in | Purpose |
|---|---|---|
theme.js |
layout/theme.liquid |
Global: mobile menu, announcement bar, header utils |
product.js |
sections/product.liquid |
Variant picker, image gallery, ATC AJAX |
collection.js |
sections/collection.liquid |
Filters, sort, quick view, infinite scroll |
cart.js |
sections/header.liquid |
Cart drawer (global — triggered from header on any page) |
search.js |
sections/header.liquid |
Predictive search (global — lives in the header) |
Shared classes and utilities live in frontend/entrypoints/js/:
js/global/variant-picker.js—<variant-picker>custom element; fetches updated section HTML on variant change and usespushStateto update the URL.js/global/modal.js—Modalbase class wrapping native<dialog>; imported by whichever entrypoint needs it.
Rule: never add a component to theme.js unless it runs on every single page. Page/section-specific logic always goes in the matching entrypoint.
| Directory | Purpose |
|---|---|
layout/ |
Full-page wrappers (theme.liquid, password.liquid) |
sections/ |
Merchant-customizable full-width components |
blocks/ |
Nestable components used inside sections (text.liquid, group.liquid) |
snippets/ |
Reusable Liquid partials (not customizable by merchants) |
templates/ |
JSON templates wiring sections to page types |
config/ |
settings_schema.json (editable) and settings_data.json (auto-generated, don't edit) |
locales/ |
Translation strings (en.default.json) |
{% render 'vite-tag', with: 'theme.css' %}— renders the correct hashed asset URL{% render 'meta-tags' %}— SEO meta tags in<head>{% render 'image', image: ..., ... %}— responsive image component{% render 'product-variant-picker', product: product %}— variant picker Liquid markup (pairs with<variant-picker>custom element)
The target store is configured in shopify.theme.toml (gitignored). Vite dev server allows CORS from display-xxx.myshopify.com.
Full roadmap and open tasks: GitHub Issue #1
This template uses New Customer Accounts (hosted by Shopify on account.shopify.com), not the legacy Liquid-based system. The customer Liquid object is null in the Online Store when new accounts are enabled. Do not rely on it for conditional rendering. Use {{ routes.account_url }} for account links.
| Area | Component | Notes |
|---|---|---|
| Layout | theme.liquid, password.liquid |
Full-page wrappers |
| SEO | meta-tags.liquid |
Open Graph, Twitter card, schema.org for products |
| CSS | Tailwind v4 + design tokens | Typography, spacing, utilities via @theme |
| Product | Variant picker (<variant-picker>) |
Fetch-based section re-render, pushState URL update |
| Product | Add-to-cart form | Standard Shopify form, no AJAX yet |
| UI | Modal class |
Wraps native <dialog>, manages body scroll-lock |
| Images | image snippet |
Responsive images with aspect ratio and lazy loading |
| Sections | header, footer, product, collection, collections, cart, search, article, blog, 404, page, password, index, custom-section | All wired to corresponding JSON templates |
| Blocks | group, text |
Composable layout blocks with schema settings |
| Customizer | Section-level schemas | Settings per section; no global settings yet |
The following features are pending. When implementing them, follow the Development Philosophy above.
| Area | Feature | Priority |
|---|---|---|
| Cart | AJAX cart (quantity update, remove, totals) | High |
| Cart | Cart Drawer / Mini-cart | High |
| Cart | Add-to-cart AJAX (no redirect) | High |
| Navigation | Mobile Menu (hamburger drawer, focus-trap) | High |
| Search | Predictive Search (<predictive-search>) |
High |
| Collection | Storefront Filtering (price, tags, variants) | High |
| Collection | Sort-by dropdown with URL update | High |
| Product | Image gallery (thumbnails, zoom, swipe) | High |
| Customizer | settings_schema.json global settings (colors, type, spacing) |
High |
| Accessibility | Focus management, ARIA live regions, skip-to-content | High |
| Navigation | Mega Menu (multi-level, desktop columns) | Medium |
| Navigation | Sticky Header | Medium |
| Navigation | Announcement Bar | Medium |
| Collection | Infinite scroll / Load more | Medium |
| Collection | Quick View modal | Medium |
| Product | Sticky Add-to-Cart bar | Medium |
| Product | Related products (Recommendations API) | Medium |
| Product | Media gallery (video, 3D models) | Medium |
| Account | Account/logout links in header | Medium |
| Sections | Homepage sections (Hero, Image+Text, Testimonials, Newsletter) | Medium |
| DX | ESLint + Prettier | Medium |
| DX | Theme Check in CI | Medium |
| DX | README update (feature tracking) | Medium |
| Product | Recently Viewed (localStorage) |
Low |
| Collection | Breadcrumb | Low |
| Customizer | Italian translations (locales/it.default.json) |
Low |