dev-storefront
Coding patterns for Shopify storefront customer-facing UI. Auto-apply when writing Preact components, Theme App Extensions, storefront scripts, or code in extensions/ directory. Focus on lightweight b
Файлы2
Открыть в GitHubSKILL.md.claude/skills/dev-storefront/SKILL.md
GitHubShopify Storefront — Development Conventions
Áp dụng conventions này khi develop code cho storefront (customer-facing UI). Performance là ưu tiên số 1.
Tech Stack
| Layer | Technology | Notes |
|---|---|---|
| UI Framework | Preact | ~3KB gzipped — KHÔNG dùng React (45KB+) |
| Extensions | Theme App Extensions | Liquid + App Blocks |
| Language | TypeScript | Strict mode, tree-shakeable |
| Styling | Vanilla CSS / CSS Modules | KHÔNG Tailwind, KHÔNG CSS-in-JS |
| Bundler | Vite | Tree-shaking, code splitting |
| State | Preact Signals | Lightweight reactive state (~1KB) |
Performance Budget
| Metric | Target | Hard Limit |
|---|---|---|
| JS Bundle | < 15KB gzipped | 30KB gzipped |
| CSS | < 5KB gzipped | 10KB gzipped |
| First Paint | < 100ms | 200ms |
| LCP Impact | < 50ms | 100ms |
| No layout shift | CLS = 0 | CLS < 0.1 |
Merchant không accept app làm chậm storefront. Mọi byte đều quan trọng.
Core Principles
- Performance First: Mỗi KB thêm vào đều phải justify. Prefer vanilla JS nếu Preact không cần thiết
- Progressive Enhancement: Storefront phải work khi JS disabled (basic Liquid), Preact enhance thêm
- Lazy Loading: Chỉ load JS khi component visible (Intersection Observer)
- No External Dependencies: Không thêm npm packages cho storefront — viết tay hoặc inline
- Minimal DOM: Ít DOM nodes nhất có thể, tránh deep nesting
- CSS First: Animation/transition dùng CSS, không JS khi có thể
Directory Structure
extensions/
├── theme-app-extension/
│ ├── assets/
│ │ ├── app-widget.js # Bundled Preact component
│ │ └── app-widget.css # Scoped styles
│ ├── blocks/
│ │ ├── app-block.liquid # App Block definition
│ │ └── promo-banner.liquid # App Block definition
│ ├── snippets/
│ │ └── app-tracking.liquid # Shared snippet
│ └── locales/
│ └── en.default.json # Translations
└── src/ # Preact source (pre-build)
├── components/
│ ├── AppWidget.tsx
│ └── PromoBanner.tsx
├── hooks/
│ └── useTracking.ts
├── utils/
│ └── api.ts
└── index.tsx # Entry point
Code Patterns
Xem chi tiết tại patterns.md bao gồm:
- Preact component pattern (functional, minimal)
- Signals state management pattern
- Lazy loading / code splitting pattern
- Theme App Extension block pattern (Liquid)
- Storefront API fetch pattern
- Tracking pixel pattern (lightweight)
- CSS scoping pattern (no leaks)
- Intersection Observer pattern
Do's and Don'ts
DO
- Dùng Preact
h()hoặc JSX pragma — KHÔNG React - Dùng Preact Signals thay vì useState cho shared state
- Lazy load components bằng
IntersectionObserver - Scope CSS bằng unique prefix (e.g.,
.app-widget__title) - Dùng
<script type="module">cho modern browsers - Test bundle size trước mỗi PR (
npx bundlesize) - Dùng Liquid
{% schema %}settings cho merchant customization - Provide fallback Liquid content khi JS disabled
DON'T
- KHÔNG import React, ReactDOM, hoặc bất kỳ React package nào
- KHÔNG dùng CSS-in-JS (styled-components, emotion) — overhead quá lớn
- KHÔNG dùng Tailwind CSS — utility classes bloat storefront
- KHÔNG fetch Shopify Admin API từ storefront — chỉ Storefront API hoặc App Proxy
- KHÔNG dùng
document.write,eval, hoặc inline scripts - KHÔNG blocking render — async/defer tất cả scripts
- KHÔNG install npm packages > 5KB cho storefront code
- KHÔNG dùng
localStoragecho sensitive data (tokens, secrets) - KHÔNG modify merchant's theme CSS/DOM ngoài app block
Banned Imports (Storefront)
// NEVER import these in storefront code:
// ❌ import React from 'react'
// ❌ import { useState } from 'react'
// ❌ import styled from 'styled-components'
// ❌ import { css } from '@emotion/react'
// ❌ import tw from 'tailwind-styled-components'
// ❌ import axios from 'axios'
// ❌ import lodash from 'lodash'
// ❌ import moment from 'moment'
// ✅ Use these instead:
// import { h, render } from 'preact'
// import { signal, computed } from '@preact/signals'
// import { useEffect, useRef } from 'preact/hooks'
$ARGUMENTS