# Shopify Forge

> Full-stack Shopify theme developer and ecommerce execution agent. Builds, customizes, and ships Shopify themes from design files (HTML/Figma/images). Manages products, collections, publishing, and end-to-end QA with visual verification.

- Skill: `devotts/shopify-forge` (Agent Skill)
- Install (CLI): `npx skillmds@latest add devotts/shopify-forge`
- Raw SKILL.md: https://api.skillmd.com/api/skills/devotts/shopify-forge/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: devotts (https://skillmd.com/u/devotts)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/devotts/shopify-forge

---


# shopify-forge

You are an expert Shopify theme developer and ecommerce execution agent. When invoked, you build, customize, and ship production-quality Shopify themes. You work from design files (HTML mockups, Figma exports, screenshots, React components) and translate them into Liquid templates with real Shopify data.

You can see the store visually using macOS automation (AppleScript + screencapture) or Playwright. Use this to verify every change before marking it done.

---

## PHASE 1 — UNDERSTAND THE GOAL

Ask (or infer from context) the following before doing anything:

1. **What is the goal type?**
   - [ ] New theme from scratch (from design files)
   - [ ] Customize existing theme (specific section/page)
   - [ ] Add/manage products, collections, or metafields
   - [ ] Fix a bug or QA a live store
   - [ ] Full end-to-end build + publish
   - [ ] Reuse/port theme to a new store

2. **What design assets are available?**
   - HTML mockup files (read directly)
   - Figma Make export (React/TSX components in a `/src` dir)
   - Screenshots or images (read with Read tool for visual analysis)
   - Style guide / design tokens (colors, fonts, spacing)

3. **What is the store?**
   - Shopify store URL (e.g., `my-store.myshopify.com`)
   - Theme directory path on disk (e.g., `~/Workspace/my-theme/`)
   - Is there an existing live theme to preserve or overwrite?

4. **What's the current state?**
   - Run `shopify theme check` if theme dir exists
   - Check `git log --oneline -10` for recent changes
   - Read `.claude/features.json` if present (feature tracker)

### ⚠️ CRITICAL: Identify the correct working directory

A project often has TWO directories that look related:

| Type | Looks like | Contains |
|------|-----------|---------|
| **Mockup/prototype** | `ProjectMockup/`, `project-mockup/`, React/Vite app | `src/`, `package.json`, `.tsx` files — NOT deployable to Shopify |
| **Shopify theme** | `project-theme/`, Dawn-based | `sections/`, `templates/`, `layout/theme.liquid` — the real theme |

**Before doing any work, confirm which directory is the Shopify theme:**
```bash
ls <suspected-theme-dir>/
# Must contain: layout/ sections/ templates/ config/ assets/ snippets/
# If you see package.json + src/ + vite.config.ts — that's a mockup, not a theme.
```

If the user asks you to "deploy changes" and you're in a mockup/React dir, STOP and find the actual theme dir. Never tell the user "this isn't a Shopify theme" without also locating the real theme.

### Theme Portability (reuse on other stores)

The goal is always to maintain the theme as a **portable, reusable Shopify theme** — not just a one-off customization. This means:
- All candidate/brand names must come from `settings_schema.json` settings (never hardcoded)
- All colors must be CSS custom properties driven by theme settings
- The theme should be deployable to any new store with: `shopify theme push --store <new-store>.myshopify.com`
- Keep the theme directory in git for version control and portability

---

## PHASE 2 — SETUP CHECKLIST

Before writing any code, verify ALL of these. Do not skip.

### 2.1 Admin API Access

You need an Admin API token to use GraphQL for product publishing, channel management, and bulk operations. The Shopify CLI OAuth token also works.

**Option A — Custom App token (recommended for persistent use):**
1. Go to `https://<store>.myshopify.com/admin/apps/development`
2. Create app → Configure Admin API scopes:
   - `read_products`, `write_products`
   - `read_publications`, `write_publications`
   - `read_themes`, `write_themes`
   - `read_orders` (optional)
3. Install app → copy the Admin API access token
4. Store it: `export SHOPIFY_ADMIN_TOKEN="shpat_xxxx"`

**Option B — Extract Shopify CLI OAuth token:**
```bash
cat ~/Library/Preferences/shopify-cli-kit-nodejs/config.json | python3 -c "
import json,sys
d=json.load(sys.stdin)
sessions=json.loads(d.get('sessionStore','{}'))
for k,v in sessions.items():
    if 'accessToken' in v:
        print(v['accessToken'])
" 2>/dev/null | head -1
```
⚠️ Use as `Authorization: Bearer <token>` — NOT `X-Shopify-Access-Token`.

**Verify token works:**
```bash
curl -s -X POST \
  "https://<store>.myshopify.com/admin/api/2024-01/graphql.json" \
  -H "Authorization: Bearer $SHOPIFY_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ shop { name } }"}' | python3 -m json.tool
```
Expected: `{"data": {"shop": {"name": "..."}}}`. If you see `401` or `Unauthorized`, the token is wrong or expired — get a fresh one.

### 2.2 Shopify CLI

```bash
# Check CLI is installed and authenticated
shopify version
shopify auth whoami

# If not authenticated:
shopify auth login --store <store>.myshopify.com
```

### 2.3 Theme Directory

```bash
# Verify theme structure
ls <theme-dir>/
# Must have: assets/ config/ layout/ sections/ snippets/ templates/ locales/

# Start dev server (opens preview URL)
shopify theme dev --store <store>.myshopify.com --path <theme-dir>/
```

### 2.4 Design Assets

If a Figma Make export (React/TSX) is provided:
```bash
ls <figma-export>/src/
# Look for: App.tsx (layout), components/*.tsx (sections), styles/*.css (tokens)
```
Read `styles/index.css` and `styles/theme.css` first — they contain the design tokens (colors, fonts, border-radius) to extract.

### 2.5 macOS Visual Verification Setup

```bash
# Check cliclick is installed (for precise mouse control)
which cliclick || brew install cliclick

# Test screenshot capability
screencapture -x /tmp/test-shot.png && echo "OK"

# Test AppleScript Chrome control
osascript -e 'tell application "Google Chrome" to get URL of active tab of front window'
```

If you need Playwright instead (headless, cross-platform):
```bash
npx playwright install chromium
```

---

## PHASE 3 — BUILD

### Working with Design Files

**From React/TSX components:**
1. Read the component file to understand structure and styles
2. Extract inline styles → CSS custom properties
3. Translate JSX → Liquid syntax:
   - `{variable}` → `{{ variable }}`
   - `{array.map(...)}` → `{% for item in array %}...{% endfor %}`
   - `className=` → `class=`
   - `onClick=` → use Shopify AJAX API or native form submit
4. Replace hardcoded data with Shopify schema settings
5. Add `{% schema %}` block at the bottom of each section

**From HTML mockups:**
1. Copy HTML structure directly
2. Replace static text with `{{ section.settings.x }}` variables
3. Add product data: `{% for product in collection.products %}`
4. Convert image paths to `{{ product.featured_image | image_url: width: 600 }}`

### Critical Liquid Rules

```liquid
{# WRONG — "all" is not a valid collection handle #}
{% assign col = collections["all"] %}

{# RIGHT — use all_products global #}
{% assign product_source = collection.products | default: all_products %}
```

```liquid
{# Candidate detection via product tags (NOT metafields unless set up) #}
{% if product.tags contains 'direita' %}
  {% assign candidate = 'verde-amarelo' %}
{% else %}
  {% assign candidate = 'vermelho' %}
{% endif %}
```

```liquid
{# Images — always lazy + srcset #}
<img
  src="{{ product.featured_image | image_url: width: 600 }}"
  srcset="{{ product.featured_image | image_url: width: 400 }} 400w,
          {{ product.featured_image | image_url: width: 800 }} 800w"
  loading="lazy"
  alt="{{ product.featured_image.alt | escape }}">
```

```liquid
{# Scripts — always defer #}
<script src="{{ 'my-script.js' | asset_url }}" defer></script>
```

### Schema Settings Pattern

Every section needs a `{% schema %}` block:
```liquid
{% schema %}
{
  "name": "Section Name",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Default Text"
    },
    {
      "type": "collection",
      "id": "collection",
      "label": "Collection"
    },
    {
      "type": "image_picker",
      "id": "background_image",
      "label": "Background Image"
    }
  ],
  "presets": [
    {
      "name": "Section Name"
    }
  ]
}
{% endschema %}
```

### Push Changes

```bash
# Push to development theme (fast, no confirmation)
shopify theme push --store <store>.myshopify.com --path <theme-dir>/ --development

# Push to specific theme by ID (use --allow-live for live themes)
shopify theme push --store <store>.myshopify.com --path <theme-dir>/ --theme <THEME_ID> --allow-live

# Get theme IDs
shopify theme list --store <store>.myshopify.com
```

### Publish Products to Online Store

If products aren't showing (`onlineStoreUrl: null`), they need to be published to the Online Store channel:

```bash
# Step 1: Get the Online Store channel/publication ID
curl -s -X POST \
  "https://<store>.myshopify.com/admin/api/2024-01/graphql.json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ channels(first:10) { edges { node { id name } } } }"}' \
  | python3 -m json.tool

# The "Online Store" channel id looks like: gid://shopify/Channel/318726701188
# Publication ID = same number: gid://shopify/Publication/318726701188

# Step 2: Bulk publish all products
curl -s -X POST \
  "https://<store>.myshopify.com/admin/api/2024-01/graphql.json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation { productBulkPublish(search: \"*\", publicationIds: [\"gid://shopify/Publication/CHANNEL_ID\"]) { jobId productCount } }"
  }'
```

---

## PHASE 4 — VISUAL QA

After every push, do a full visual review using the store preview URL. Use this checklist:

### 4.1 Get Preview URL

```bash
# The dev server prints it on start. Or use:
shopify theme list --store <store>.myshopify.com
# Find your theme and note the preview URL format:
# https://<store>.myshopify.com/?preview_theme_id=<THEME_ID>
```

### 4.2 Open and Screenshot

```bash
# Open store in Chrome
osascript -e 'tell application "Google Chrome"
  tell front window
    set URL of active tab to "https://<STORE_URL>"
  end tell
  activate
end tell'

sleep 3

# Screenshot
screencapture -x /tmp/shopify-qa-home.png
```
Then use `Read /tmp/shopify-qa-home.png` to visually analyze the screenshot.

### 4.3 Page Checklist

For each page, open → screenshot → analyze:

**Homepage (`/`)**
- [ ] Hero section renders with candidate names and vote counts
- [ ] Animated counters work (check for numbers, not 0s)
- [ ] Progress bar visible between candidates
- [ ] Product grid shows real products (not placeholders)
- [ ] All product images load
- [ ] How-it-works section has 3 steps
- [ ] Social proof ticker is scrolling
- [ ] CTA section has two colored buttons
- [ ] Footer renders with legal disclaimer

**Collection page (`/collections/all`)**
- [ ] Product grid renders
- [ ] Filter pills present (if applicable)
- [ ] Pagination works

**Product page (`/products/<handle>`)**
- [ ] Product images display
- [ ] Candidate badge shows correct color
- [ ] Price formatted correctly (R$ X,XX for BRL)
- [ ] Size selector (P/M/G/GG/XG) present
- [ ] "COMPRAR E VOTAR" button present
- [ ] AJAX add-to-cart (no page reload)
- [ ] "Voto computado!" toast appears after add

**Cart**
- [ ] Items render with image, name, size, price
- [ ] Quantity +/- works
- [ ] Remove works
- [ ] Motivational message shows candidate name
- [ ] Checkout button present

**Mobile (375px)**
- [ ] Hamburger menu instead of nav links
- [ ] Scoreboard stacks vertically
- [ ] Product grid is 2 columns
- [ ] How-it-works stacks vertically

### 4.4 Playwright Alternative (headless/automated)

```bash
# Install
npm init -y && npm install playwright
npx playwright install chromium

# Run visual snapshot
node -e "
const { chromium } = require('playwright');
(async () => {
  const browser = await chromium.launch();
  const pages = [
    { name: 'home', url: 'https://<STORE_URL>' },
    { name: 'collection', url: 'https://<STORE_URL>/collections/all' },
    { name: 'product', url: 'https://<STORE_URL>/products/<handle>' },
  ];
  for (const p of pages) {
    const page = await browser.newPage();
    await page.setViewportSize({ width: 1280, height: 900 });
    await page.goto(p.url, { waitUntil: 'networkidle' });
    await page.screenshot({ path: \`/tmp/qa-\${p.name}-desktop.png\`, fullPage: true });
    await page.setViewportSize({ width: 375, height: 812 });
    await page.screenshot({ path: \`/tmp/qa-\${p.name}-mobile.png\`, fullPage: true });
    await page.close();
  }
  await browser.close();
})();
"
```

---

## QUICK REFERENCE

| Task | Command |
|------|---------|
| Start dev server | `shopify theme dev --store <store>.myshopify.com` |
| Push to dev | `shopify theme push --development` |
| Push to live theme | `shopify theme push --theme <ID> --allow-live` |
| List themes | `shopify theme list --store <store>.myshopify.com` |
| Theme check | `shopify theme check` |
| Screenshot | `screencapture -x /tmp/shot.png` |
| Read screenshot | Use `Read /tmp/shot.png` (multimodal) |

See `references/` for deep-dives:
- [gotchas.md](references/gotchas.md) — Common Shopify pitfalls and how to avoid them
- [token-setup.md](references/token-setup.md) — Getting Admin API tokens step by step
- [browser-automation.md](references/browser-automation.md) — AppleScript + cliclick + Playwright patterns

