Introduction

StoreKit is an open-source library of ecommerce storefront sections for shadcn. 136 items: announcement bars, navigation, heroes, product cards, collections, product pages, carts, checkout, upsells, reviews, marketing, compliance and footers.

You do not install a package you import from. Each item copies its source into your repo under components/storekit/, with dependencies resolved. Edit it, theme it, ship it. There is no runtime dependency on StoreKit.

The one idea that makes it work: every section reads the same CSS variable contract, the sk-* tokens. Swap one token file and the whole store reskins. The example store is 18 registry items and nothing else, and the 7 live stores run the same engine under 7 token files.

Installation

StoreKit is a shadcn registry. In any project with shadcn initialized, register the namespace once in components.json:

components.json
5 lines
{
  "registries": {
    "@storekit": "https://storekit-eight.vercel.app/r/{name}.json"
  }
}

Then add the token contract to your global stylesheet and the sk color block to your Tailwind config. Both are below under theming. That is the whole setup.

No shadcn yet? npx shadcn@latest init first. StoreKit works on Next.js, Vite, Remix and anything else shadcn supports.

Adding components

Add any item by name. Its files land in components/storekit/<section>/, its npm dependencies install, and nested registry dependencies (primitives like button and price) resolve automatically and only once.

$npx shadcn@latest add @storekit/pdp-buybox
$npx shadcn@latest add @storekit/cart-drawer @storekit/cart-shipping-bar

Or skip the namespace and install straight from a URL:

$npx shadcn@latest add https://storekit-eight.vercel.app/r/hero-split.json

Every section page shows each variant live; click a card for the install commands, and open the component page for the source, the files it writes, and the tokens it reads.

Theming

Theming is one file. Components reference variables; you set the values. Colors are RGB triplets so Tailwind opacity modifiers like bg-sk-accent/10 work.

app/globals.css
22 lines
/* app/globals.css: the sk-* contract. RGB triplets so opacity modifiers work. */
:root {
  --sk-accent: 47 111 208;      /* the action color */
  --sk-accent-ink: 255 255 255; /* text on accent */
  --sk-ink: 11 18 32;           /* primary text */
  --sk-muted: 96 106 124;       /* secondary text */
  --sk-paper: 255 255 255;      /* page */
  --sk-card: 255 255 255;       /* raised */
  --sk-surface: 244 246 250;    /* recessed */
  --sk-line: 226 230 237;       /* hairlines */
  --sk-price: 11 18 32;
  --sk-savings: 22 128 84;
  --sk-sale: 47 111 208;
  --sk-bar: 11 18 32;           /* announcement bar */
  --sk-bar-ink: 255 255 255;
  --sk-radius-card: 14px;
  --sk-radius-btn: 10px;
  --sk-radius-chip: 999px;
  --sk-font-display: "Archivo", sans-serif;
  --sk-font-body: "Inter", sans-serif;
  --sk-font-mono: "JetBrains Mono", monospace;
}

Map the variables to class names in Tailwind so component source reads as bg-sk-accent, text-sk-ink, rounded-sk-card:

tailwind.config.ts
20 lines
// tailwind.config.ts
const sk = (n: string) => `rgb(var(--sk-${n}) / <alpha-value>)`
export default {
  theme: {
    extend: {
      colors: {
        sk: {
          accent: sk("accent"), "accent-ink": sk("accent-ink"),
          ink: sk("ink"), muted: sk("muted"),
          paper: sk("paper"), card: sk("card"), surface: sk("surface"), line: sk("line"),
          price: sk("price"), savings: sk("savings"), sale: sk("sale"),
          bar: sk("bar"), "bar-ink": sk("bar-ink"),
        },
      },
      borderRadius: { "sk-card": "var(--sk-radius-card)", "sk-btn": "var(--sk-radius-btn)", "sk-chip": "var(--sk-radius-chip)" },
      fontFamily: { "sk-display": "var(--sk-font-display)", "sk-body": "var(--sk-font-body)", "sk-mono": "var(--sk-font-mono)" },
      transitionTimingFunction: { "sk-out": "cubic-bezier(0.23, 1, 0.32, 1)" },
    },
  },
}

Dark brands are not a mode toggle, they are a token file where paper is dark and ink is light. Scope a second set of values under [data-theme="dark"] if you want both.

Brand packs

A brand pack is a complete set of values. Here is Caldera, the oxblood and cream brand from the live demos. Paste it over the defaults and every installed section follows.

tokens.caldera.css
29 lines
:root {
  --sk-accent: 156 42 56;
  --sk-accent-ink: 255 247 240;
  --sk-ink: 42 26 22;
  --sk-muted: 120 104 98;
  --sk-paper: 247 242 234;
  --sk-card: 252 249 244;
  --sk-surface: 240 233 222;
  --sk-line: 222 212 198;
  --sk-price: 42 26 22;
  --sk-savings: 92 112 72;
  --sk-sale: 156 42 56;
  --sk-bar: 42 26 22;
  --sk-bar-ink: 247 242 234;
  --sk-radius-card: 4px;
  --sk-radius-btn: 4px;
  --sk-radius-chip: 999px;
  --sk-font-display: var(--font-fraunces);
  --sk-font-body: var(--font-inter);
  --sk-font-mono: var(--font-mono);
  --sk-display-weight: 600;
  --sk-display-case: none;
  --sk-display-tracking: -0.02em;
  --sk-btn-case: none;
  --sk-btn-tracking: 0em;
  --sk-btn-weight: 500;
  --sk-border: 1px;
  --sk-shadow-card: none;
}

Every style is a pack like this one. Open any of the 41 to preview it on real blocks and copy its token file, or use the switcher on any component page.

Registry

Every item is published as a shadcn registry item at /r/<name>.json, generated from one source by scripts/build-registry.mjs. The index is at /r/registry.json.

The item schema matches shadcn's registry-item.json exactly. Ecommerce meaning lives in categories (the section and niche tags) and meta (variant, commerce facets, preview height). The build fails on any hard-coded color, so the token contract is enforced, not suggested.

Agents

Because StoreKit is a plain shadcn registry, Claude Code, Cursor, Codex and v0 can already install from it through the shadcn MCP server or by running the add command. The whole catalog, with one line per item and the token contract, is at /llms.txt.

A useful prompt: "Read https://storekit-eight.vercel.app/llms.txt, then build a product page for a supplement brand using @storekit items. Use the Caldera brand pack."

Analytics

Every StoreKit block carries data-sk-section and data-sk-variant on its root, and blocks with an action use the useSkBlock hook (installed as @storekit/sk-analytics). It fires a view event once at half visibility and lets the block report interact and convert with a label and a value in cents.

Nothing is sent to StoreKit. Events go to whatever is already on the page: the GA4 dataLayer, PostHog, Microsoft Clarity, Shopify analytics and rrweb custom events, plus a window.__sk listener array for your own code. Because the section and variant are on the element, session replays and heatmaps can attribute a drop-off to a specific block and a specific variant, which is the data the later recommendation engine runs on.

House style

Six rules keep the library from looking generated. Product first: the hero is a product, not a gradient. Trust near the button: proof sits beside add to cart, never a badge row at the bottom. One accent, rationed: accent is for the action, everything else is ink, paper and hairlines. Motion under 300ms: press feedback, drawers and price flips are quick and interruptible, marketing reveals are rare and once. Every state designed: sold out, low stock, loading, empty and error are first class. Honest numbers: tabular prices, real savings math, shipping thresholds that compute, no fake urgency.

Contributing

The repo is a pnpm monorepo. The site and registry live in apps/showcase; items are one .tsx plus one .demo.tsx per section folder. The rules every item follows (token contract, motion budget, states, copy) are in registry/CONTRIBUTING.md. Run node scripts/build-registry.mjs before opening a pull request; it rejects forbidden colors and dashes.