Build a Spark storefront theme with agents
v0.4Updated Sep 4, 2026
Use this workflow to turn an approved design into a new, unpublished Spark theme. next-theme-figma prepares the design handoff, next-theme-dev guides the build, and Theme Kit uploads the result for review.
Purpose
This guide covers a new Spark build on an existing store. It does not cover a redesign of the active theme. If the design targets Intro Bootstrap or a custom theme, use next-theme-dev to preserve that theme family instead of switching it to Spark.
The workflow
Each step has a check. Do not continue when that check fails.
| Step | Action | Check | If it fails |
|---|---|---|---|
| 1. Install the skills | Install or update next-theme-figma and next-theme-dev, then restart the agent. | Both status commands report current. | Update the stale copy. |
| 2. Prepare the design | Run next-theme-figma in implementation-handoff mode. | The 10-file package passes strict validation and its screenshot paths resolve. | Fix the package or send the checklist to the designer. |
| 3. Get Spark | Clone Spark and record its version and commit. | The design targets Spark and its web-components runtime. | Stop if the design targets another family. |
| 4. Create the theme | Run ntk init from the Spark checkout. | ntk list shows the new ID, and the new theme is not marked (Active). | Check the store, key, and theme ID. |
| 5. Implement | Load next-theme-dev and build from the package. | make verify-theme passes and compiled CSS is committed with its source. | Fix the failed check. Do not edit compiled CSS by hand. |
| 6. Upload | Approve the target and run targeted ntk push <files> commands. | The command exits zero and readback passes. | Stop on the first failure and check the target. |
| 7. Test | Run the readback, geometry, copy, behavior, and screenshot checks. | Each route and viewport passes or has an accepted gap with an owner. | Fix the named failure. |
| 8. Handoff | Record versions, target, files, settings, setup, and evidence paths. | Another person can resume without reconstructing the run. | Add the missing fact or artifact. |
What you need
Collect these inputs before the build starts.
| Input | Example | Why it matters |
|---|---|---|
| Store domain | https://<store-subdomain>.29next.store/ | The agent needs the network domain for API calls, preview URLs, and cache-safe review. |
| Theme API key | OAuth App API key with themes:read and themes:write | Required for ntk init, ntk list, and ntk push. Do not paste it into public tickets or committed files. |
| Design source | Figma link, screenshots, PDF, or approved design brief | next-theme-figma records the layout, copy, geometry, assets, responsive references, and approved platform differences. |
| Content and products | Product handles, hero copy, reviews, media logos, offer requirements | Use real catalogue data. Placeholders are not final content. |
| Launch intent | New theme, uploaded but not activated | Keep the new theme inactive until it passes review. |
Create the API key
Theme Kit uses a store OAuth App API key. Create a separate app for theme work.
Open API access
In the NEXT storefront admin, go to Settings → API Access and choose Create App.
Name and assign the app
Use a descriptive name such as Theme Development or the agency/developer name, then assign the app to the user who owns the work. Anyone with this key can modify every theme on the store, so revoke the app at handoff if access should end.
Grant theme permissions only
In the app's Permissions tab, enable themes:read and themes:write. Those are the permissions Theme Kit needs to list, create, pull, and push themes.
Save and store the key securely
Save the app to generate the API key. Put it in a local secret store or an uncommitted environment file, and revoke it when the developer or agent no longer needs access.
Reference: Theme Kit docs.
Tool stack
next-theme-figma prepares the design package. Spark is the starter theme. next-theme-dev covers implementation and testing. Theme Kit moves files between the checkout and the store.
Spark theme
Public starter theme with Tailwind v4, vanilla JavaScript, Web Components, GraphQL cart behavior, and committed compiled CSS.
https://github.com/NextCommerceCo/spark.git
Skills repo
Agent instructions. Run next-theme-figma first, then load next-theme-dev before editing or uploading theme files.
next-theme-figma · next-theme-dev
Theme Kit repo
Source and installation reference for ntk, the CLI used to create, pull, and push NEXT themes.
Install ntk per docs
Shopify → NEXT Theme Guide
Reference for DTL concepts, settings, templates, and differences from Shopify Liquid.
Companion storefront reference
Use current skills in order
Run next-theme-figma and validate its package before changing the theme. Then load next-theme-dev. Check for updates before each build and restart the agent after installing them.
git clone https://github.com/NextCommerceCo/skills.git
cd skills
git pull --ff-only
./skills.sh
./skills.sh status all next-theme-figma
./skills.sh status all next-theme-devFirst prompt
Replace the placeholders before sending this to the agent.
I want to apply this design to a new unpublished Spark theme.
Design source:
- Figma: <figma-url>
Target store:
- https://<store-subdomain>.29next.store/
Stage 1 (design handoff):
- Load and follow the next-theme-figma skill against the Figma file.
- Use implementation-handoff mode with theme_family=spark and
runtime_contract=web-components.
- Produce the complete 10-file package, including geometry.json and
copy.json, and validate it in strict mode.
- Confirm every reference screenshot path exists inside the package.
- If the design source is incomplete, stop and give me the designer
checklist instead of guessing.
Stage 2 (implementation):
- Clone a fresh copy of the public Spark theme (HTTPS).
- Record the Spark version and git commit before editing.
- Load and follow the next-theme-dev skill.
- Implement from the validated handoff package, not from memory of
the Figma file.
- Install or use ntk from the Theme Kit docs.
- Create a new theme on the store with ntk init and upload the work there.
- Do not activate the new theme.
- Treat configs/settings_data.json as saved Theme Editor state. If this
fresh preview build has a prepared copy as its source of truth, confirm
that with me and push it from the first upload. Otherwise exclude it.
- Work from a clean local project folder.
- The store API key is available as THEME_API_KEY in an uncommitted
.theme-build.env file. Never print or commit it.
- Run make verify-theme before uploading.
- Stop on any failed ntk push, then run the post-push readback gate.
Deliverables:
- The validated handoff package.
- A preview URL for the new theme.
- Spark, skill, and Theme Kit versions.
- The changed-file and pushed-file lists.
- The settings_data.json decision.
- Notes for any manual Dashboard setup still required, including
Offers for any free-shipping or gift promises in the design.
- Geometry, copy, readback, and behavior reports.
- Desktop PNGs at 1440px and mobile PNGs at 390px, or a recorded
accepted gap with an owner and reason.If the design source is private, make sure the agent can access it or provide screenshots and exported assets. If the store has existing products, include the product URLs or handles that must appear in the new theme.
Prepare the design handoff
Run next-theme-figma before changing theme files. The package is the source for implementation and review.
Run the skill against the design
The agent confirms the Figma file, routes, viewports, theme family, runtime contract, and work mode. It then classifies each section and records approved platform differences.
Generate and validate the package
The script produces a 10-file package. Strict validation requires route, section, asset, platform divergence, viewport, geometry, copy, and checklist data. It also checks that referenced screenshots exist inside the package. notes.md is optional.
Route the outcome
A ready package moves to implementation. A designer-input-needed result lists what the design owner must supply. Do not build around missing source material.
Do not rebuild the brief from memory
The package fixes the route map, copy, dimensions, source assets, and approved platform differences before implementation starts.
Start from Spark
Use a fresh Spark checkout for this workflow. Do not copy a merchant theme into the build.
Create a local workspace
mkdir -p storefront-builds
cd storefront-buildsClone Spark into a new theme folder
git clone https://github.com/NextCommerceCo/spark.git merchant-storefront
cd merchant-storefrontRead the starter contract
Record the version and commit. Read DESIGN.md, CLAUDE.md, CHANGELOG.md, docs/performance-load-order.md, and the relevant cart or PDP docs before editing those surfaces.
cat manifest.json
git rev-parse HEADPreserve Spark's runtime
Keep Tailwind v4, vanilla JavaScript, Web Components, GraphQL cart behavior, app hooks, the {% pixels %} block, and the documented script order. Do not add Bootstrap, jQuery, or cached CSRF form posts.
Create a new theme
Run ntk init from the complete Spark checkout. It creates an unpublished theme and writes the store and theme ID to config.yml.
# Store secrets outside committed files.
[ -f .theme-build.env ] && { echo ".theme-build.env already exists; edit it manually" >&2; exit 1; }
grep -qxF '.theme-build.env' .gitignore 2>/dev/null || printf '.theme-build.env\n' >> .gitignore
printf 'THEME_API_KEY=<paste-api-key-here>\n' > .theme-build.env
set -a
source .theme-build.env
set +a
[ "$THEME_API_KEY" = "<paste-api-key-here>" ] && { echo "Edit .theme-build.env with a real API key first" >&2; exit 1; }
export NTK_APIKEY="$THEME_API_KEY"
ntk init \
--name="Merchant Storefront - Agent Build" \
--store="https://<store-subdomain>.29next.store/"NTK_APIKEY keeps the key out of config.yml, which suits agent and CI runs. Spark's .gitignore already ignores config.yml. A developer who wants a long-lived local checkout can pass --apikey="$THEME_API_KEY" instead. Theme Kit then saves the key as plaintext in that file. Confirm it remains ignored, and use only one credential method.
If ntk is missing, install it from the Theme Kit docs. After config.yml exists, check the target:
ntk listThe new theme ID must appear in the list and must not be marked (Active). On an established storefront, record the separate active theme ID. If the store has no active theme, get merchant confirmation that there is no live storefront to protect. Record the store, environment, theme ID, and active status before uploading.
Apply the design
Build from the validated package and keep Spark's runtime contracts intact.
| Pass | Output | Check |
|---|---|---|
| Design package | Routes, sections, assets, copy, geometry, responsive references, and approved differences. | Use the manifests. Do not re-read Figma to fill gaps or invent missing assets. |
| Theme plan | The files and Spark modules each section will use. | Preserve app hooks, the {% pixels %} block, performance load order, and the PDP variant-state contract. |
| Settings | Controls for merchant-editable colors, copy, images, menus, products, and visibility. | Define controls in settings_schema.json. Treat settings_data.json as saved store state. |
| Styling | css/input.css, CSS custom properties, and compiled assets/main.css. | Spark uses source(none) and scans only templates/, partials/, layouts/, and assets/js. Add any new markup directory to the explicit @source list and its guard test. Do not build class names from template variables. |
| Cart and user state | Client-side GraphQL behavior for cart, account, variants, and checkout handoff. | Do not render private state in cached templates or replace GraphQL add-to-cart with a cached {% csrf_token %} form. Keep stale-cart recovery after checkout. |
Decide who owns settings data
Exclude configs/settings_data.json by default. Push it only when this fresh unpublished preview has a prepared settings file as its approved source of truth. Confirm that choice first, push the file before other theme files, and record the decision in the handoff.
Verify the theme and review the first upload list before changing the remote theme.
Confirm the target before every upload
Show the operator the store, environment, theme ID, active status, and file list. Obtain approval before each ntk push or ntk watch session.
# Install Tailwind once, then run the full pre-upload check.
make install-tailwind
make verify-theme
# Rebuild assets/main.css, then stage it with its source changes.
make release
git add <changed-source-files> assets/main.css
git diff --cached -- assets/main.css
git commit -m "Build storefront theme"
# If this fresh preview owns a prepared settings_data.json, push it first.
# ntk push configs/settings_data.json || exit 1
# Initial Spark upload, excluding saved Theme Editor state.
for dir in assets configs layouts locales partials templates; do
[ -d "$dir" ] && find "$dir" -type f
done \
| grep -v '^configs/settings_data.json$' \
| while IFS= read -r file; do ntk push "$file" || exit 1; doneAfter the first upload, use targeted pushes for iterative changes. Keep configs/settings_data.json out of the list unless changing saved Theme Editor values is the explicit task.
ntk push templates/index.html partials/header.html assets/main.css configs/settings_schema.jsonStop on upload failure
ntk push exits non-zero on a failed upload and stops at that file. Keep the reviewed file list, check the printed upload count, and run the readback gate. Do not assume later files were attempted.
Preview and test
Use the network-domain preview URL. It avoids the mapped-domain edge cache, but page and asset updates can still take time to reach every request.
https://<store-subdomain>.29next.store/?preview_theme=THEME_ID| Check | What it proves | Evidence |
|---|---|---|
| Post-push readback | Routes return 200, sections render, page height is plausible, and the served assets/main.css matches the committed file. | readback-assert.py report after every push |
| Geometry | Rendered element boxes, edges, and gaps match geometry.json within the recorded tolerances. | assert-geometry.mjs report for each route and viewport |
| Copy | Built text matches copy.json or an approved deviation. | copy-lint.py report with coverage required |
| Storefront behavior | Navigation, product data, variants, add-to-cart, quantity changes, removal, app hooks, pixels, and checkout handoff work. | Browser notes, console results, and any DOM smoke reports |
| Visual review | The build matches the saved Figma references at the same widths. | Desktop PNGs at 1440px and mobile PNGs at 390px |
Prepare the readback
Create qa-output/readback-expectations.json before the first upload. Use stable section markers from the implemented templates. The asset URL is the stylesheet URL served by the preview page.
{
"schema_version": "next-theme-dev/readback-expectations/v1",
"theme_id": "THEME_ID",
"routes": [{
"route_id": "home",
"url": "https://<store-subdomain>.29next.store/?preview_theme=THEME_ID",
"expect_status": 200,
"expect_section_count": 2,
"section_markers": [
{"section_id": "hero", "marker": "data-section=\"hero\""},
{"section_id": "featured", "marker": "data-section=\"featured\""}
]
}],
"assets": [{
"served_url": "https://<served-main-css-url>",
"committed_path": "assets/main.css"
}]
}Replace the example markers and URLs. After a browser review confirms the first push rendered the full page, add "expect_content_length": <rendered-text-length> as the baseline for later pushes.
python3 <next-theme-dev-skill-dir>/scripts/readback-assert.py \
--expect ./qa-output/readback-expectations.json \
--repo-root . \
--report ./qa-output/readback.jsonCheck geometry
Run this for every route and viewport in geometry.json. The probe prints browser JavaScript; evaluate it on the preview at the recorded frame width and save the returned JSON as geometry-boxes.json.
node <next-theme-dev-skill-dir>/scripts/assert-geometry.mjs selectors \
--manifest <handoff-package>/geometry.json \
--route home --viewport desktop
node <next-theme-dev-skill-dir>/scripts/assert-geometry.mjs probe \
--manifest <handoff-package>/geometry.json \
--route home --viewport desktop \
--out ./qa-output/geometry-probe.js
# Evaluate geometry-probe.js in the browser and save its JSON result.
node <next-theme-dev-skill-dir>/scripts/assert-geometry.mjs compare \
--manifest <handoff-package>/geometry.json \
--route home --viewport desktop \
--boxes ./qa-output/geometry-boxes.json \
--report ./qa-output/geometry-home-desktop.jsonCheck copy
python3 <next-theme-figma-skill-dir>/scripts/copy-lint.py \
--package <handoff-package> \
--templates partials \
--templates templates \
--require-coverage \
--report ./qa-output/copy-lint.jsonThe --templates flag is repeatable, so this command checks both directories. Run the geometry gate before judging pixel scores. A percentage score is useful for comparison, but it does not accept the build. If a required screenshot cannot be captured, record the route, viewport, owner, and reason as an accepted gap.
One request is not a cache check
Repeat the readback against exact section markers and changed asset content. Hard-refresh the browser before visual review. If the content is still stale, wait for propagation and repeat the readback. Do not use skip_cache=1 as proof that the uploaded revision is live.
Handoff
Record enough detail for another person or agent to resume the build.
New theme:
- Name: Merchant Storefront - Agent Build
- Theme ID: THEME_ID
- Store and environment: STORE / development
- Preview: https://<store-subdomain>.29next.store/?preview_theme=THEME_ID
- Status: Uploaded, not activated
- Active theme ID at handoff: ACTIVE_THEME_ID
Built from:
- Spark version and commit: VERSION / SHA
- Design source: <figma-url-or-brief>
- Handoff package: <path-or-link-to-validated-package>
- next-theme-figma version: VERSION
- next-theme-dev version: VERSION
- Theme Kit version: VERSION
Changed:
- <exact changed-file list>
Uploaded:
- <exact pushed-file list>
Settings:
- settings_data.json: excluded | pushed from first upload
- Reason: <reason>
Evidence:
- Strict handoff validation: PASS / report path
- Readback: PASS / report path
- Geometry: PASS / report paths
- Copy: PASS / report path
- Storefront behavior: PASS / report path
- Screenshots: desktop and mobile paths
- Accepted gaps: none | <route, viewport, owner, reason>
Dashboard work:
- <Offers, navigation, pages, products, or none>Existing themes
For an existing theme, check out the exact theme ID, preserve its current stack, and upload only changed files. Treat settings_data.json as merchant-owned state unless the task changes saved settings.
export NTK_APIKEY="$THEME_API_KEY"
ntk checkout \
--theme_id=THEME_ID \
--store="https://<store-subdomain>.29next.store/"If a change appears missing, repeat the readback against the preview URL and inspect the exact DOM marker or served asset content. Do not diagnose a rollback from one browser request.
Links
NextCommerceCo/spark
NextCommerceCo/skills
Available AI skills
Machine-readable skills index
NextCommerceCo/theme-kit
Shopify → NEXT Theme Guide
Theme developer docs
Theme Settings docs
Template tags docs
Storefront GraphQL docs
Guide for agent-assisted storefront builds. Keep API keys, private design links, and merchant-specific credentials out of committed files and public prompts.