Next Commerce

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.

StepActionCheckIf it fails
1. Install the skillsInstall 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 designRun 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 SparkClone 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 themeRun 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. ImplementLoad 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. UploadApprove 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. TestRun 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. HandoffRecord 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.

InputExampleWhy it matters
Store domainhttps://<store-subdomain>.29next.store/The agent needs the network domain for API calls, preview URLs, and cache-safe review.
Theme API keyOAuth App API key with themes:read and themes:writeRequired for ntk init, ntk list, and ntk push. Do not paste it into public tickets or committed files.
Design sourceFigma link, screenshots, PDF, or approved design briefnext-theme-figma records the layout, copy, geometry, assets, responsive references, and approved platform differences.
Content and productsProduct handles, hero copy, reviews, media logos, offer requirementsUse real catalogue data. Placeholders are not final content.
Launch intentNew theme, uploaded but not activatedKeep 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.

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-dev

First 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-builds

Clone Spark into a new theme folder

git clone https://github.com/NextCommerceCo/spark.git merchant-storefront
cd merchant-storefront

Read 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 HEAD

Preserve 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 list

The 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.

PassOutputCheck
Design packageRoutes, 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 planThe files and Spark modules each section will use.Preserve app hooks, the {% pixels %} block, performance load order, and the PDP variant-state contract.
SettingsControls for merchant-editable colors, copy, images, menus, products, and visibility.Define controls in settings_schema.json. Treat settings_data.json as saved store state.
Stylingcss/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 stateClient-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; done

After 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.json

Stop 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
CheckWhat it provesEvidence
Post-push readbackRoutes 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
GeometryRendered element boxes, edges, and gaps match geometry.json within the recorded tolerances.assert-geometry.mjs report for each route and viewport
CopyBuilt text matches copy.json or an approved deviation.copy-lint.py report with coverage required
Storefront behaviorNavigation, 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 reviewThe 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.json

Check 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.json

Check 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.json

The --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.


Guide for agent-assisted storefront builds. Keep API keys, private design links, and merchant-specific credentials out of committed files and public prompts.

On this page