# Kiln — Tailwind Ecommerce Next.js Template

> For makers selling small batches: a Tailwind ecommerce Nextjs template with a shop built around photography, collection filters and a working cart. 7 pages.

- Price: $34, one-time
- License: Standard
- Version: 2.7.3
- Category: E-commerce
- Editions: React, HTML
- Dark mode: Yes
- Stack: Next.js >=15, React >=19, Tailwind CSS >=4
- Dependencies: `lucide-react`

## What's inside

- 7 pages
- 39 components
- 56 design tokens plus 56 dark overrides

## Pages

| Path | Sections |
| --- | --- |
| `/` | `site-header`, `hero`, `product-grid`, `collection-split`, `promises`, `reviews`, `newsletter`, `site-footer` |
| `/collection` | `site-header`, `page-header`, `collection-browser`, `site-footer` |
| `/product` | `site-header`, `product-detail`, `reviews`, `product-grid`, `site-footer` |
| `/making` | `site-header`, `page-header`, `collection-split`, `firing-calendar`, `product-grid`, `newsletter`, `site-footer` |
| `/delivery` | `site-header`, `page-header`, `delivery-rates`, `promises`, `returns-faq`, `small-print`, `site-footer` |
| `/cart` | `site-header`, `page-header`, `cart-contents`, `site-footer` |
| `/components` | `site-header`, `hero`, `product-grid`, `collection-browser`, `product-detail`, `cart-contents`, `collection-split`, `firing-calendar`, `promises`, `delivery-rates`, `returns-faq`, `small-print`, `reviews`, `newsletter`, `page-header`, `site-footer` |

## Components

| Name | What it renders |
| --- | --- |
| `site-header` | Sticky bar with the wordmark, three nav links, the light/dark toggle, the live cart drawer and a native mobile menu - a server component composing two client islands |
| `hero` | A8 asymmetric offset: the studio statement on a plaster ground - a generated grayscale wall texture blended into the background token - offset in the left three fifths, with the studio photograph taking the right two fifths and bleeding off the page edge |
| `product-grid` | The shelf: a full-bleed hairline plate of white bays holding square product photographs with name, price and stock state, filterable by id and reusable as a related strip |
| `collection-browser` | A local collection browser with availability and price filters, shelf and price sorting, a polite result count, removable active filters, full plates for larger sets, normal-width sparse results and an explicit resettable empty state |
| `product-detail` | Three-shot gallery of one object at wide width with the lead shot breaking the container, price, rating, glaze radios, quantity, an add to cart that really adds, and three spec disclosures at prose width |
| `cart-contents` | The live cart: line items with tight-crop thumbnails, a working quantity stepper and remove, a gift note, an order summary whose total is set at display size and whose delivery charge is arithmetic, plus the designed empty state you reach by emptying it |
| `collection-split` | Process photograph beside four numbered making stages with real timings, split seven columns to five, with an optional link to the long version |
| `firing-calendar` | The kiln year: a reading block at prose width whose next firing date is set at display size, above the four firings in the full-bleed hairline plate |
| `promises` | Dispatch, packing and returns in the full-bleed hairline plate with lucide marks - the quiet density register on every route it appears on, with an optional link to the full terms |
| `delivery-rates` | The free-delivery threshold at display size beside a real table of costs and transit windows by destination, split five columns to seven |
| `returns-faq` | Four native disclosures at prose width covering returns, breakages and glaze swaps, carrying the #returns anchor |
| `small-print` | The shop's small print in three clauses under a rule - who you buy from, when an order is an order, what happens to your email address - unboxed at caption size rather than in a fourth plate |
| `reviews` | Three named buyer quotes to a page in a full-bleed plate, the first at display size, each tied to the piece that buyer owns, with one average-rating line; on /product the list is the reviews of the piece being sold |
| `newsletter` | Restock-list signup on the sage accent surface, with a labelled email input and the theme s one validation style |
| `page-header` | Editorial opener with eyebrow, title, optional lede and an optional mono spec column in the right third, for pages that are not the shop front |
| `site-footer` | Four-column footer with studio address, three nav groups and a firing-date legal bar |
| `ui/button` | Button, ButtonLink and buttonClasses - three variants, three sizes, hover and press from the state ramp, a disabled state, a pending state that holds the pressed colour and carries aria-busy so a submit cannot fire twice, and 2px of optical padding trimmed on the icon side |
| `ui/badge` | Badge in three appearance variants, including the opaque surface variant used over photographs, and Eyebrow on the label step |
| `ui/container` | The single gutter rhythm at prose, default and wide widths |
| `ui/field` | The form family every Kiln control is built from - TextField, SelectField, TextareaField and the Field shell. One label, one optional read-only sentence and one message line whose precedence is error then success then description, so a form that is wrong in one place no longer reveals every sentence it holds. Both validation modes survive: the browser-driven :user-invalid style by default, and a declared invalid where JavaScript owns the verdict |
| `ui/product-card` | One bay of the shelf: a white lit surface holding a square photograph, name, price and stock chip, with an optional href so a sold-out piece is not a click target |
| `ui/catalogue` | No markup, the six canonical photographed products shared by product-grid and collection-browser, with integer unit prices, glaze labels, stock and per-surface detail-link eligibility |
| `ui/promises` | No markup, the three dispatch, packing and returns promises written once: the hero's fact row reads their titles, the promises plate reads titles and bodies, and the buy panel and cart summary set the one returns phrase inside their own sentence |
| `ui/reviews` | No markup, the eighteen published reviews written once, each keyed to a catalogue piece: the reviews block walks them, the buy panel reads its count and mean from the same rows, and no review count is typed anywhere |
| `ui/icons` | KilnMark, the theme's only hand-drawn SVG |
| `ui/cart-store` | No markup, all of the state: the priced fixture table, the lines, the totals arithmetic, localStorage persistence and the useCart hook every cart surface reads |
| `ui/cart-drawer` | The header's cart as a real modal sheet: a live count in the trigger's own name, the lines and the computed subtotal from the store, one action that names where it goes, and its own empty state |
| `ui/mobile-nav` | The narrow-width nav: a sheet from the left, opened by the one control in the bar that is not the cart, with aria-current on the route a reader is already on |
| `ui/mode-toggle` | The navbar light/dark switch: Sun and Moon swapped in CSS, aria-pressed derived from the observed theme root, paired light/dark classes, and a remembered standalone choice |
| `ui/accordion-disclosure` | The disclosure row three files used to retype: a summary on a hairline, a chevron that turns on the theme's own 250ms clock, and the answer tweened open through ::details-content. Escape inside an open panel collapses it and hands focus back to the summary, which is the row native details never gave |
| `ui/sheet-drawer` | The edge-anchored modal panel the cart and the narrow-width nav both are: a scrim that dismisses, a trapped Tab, a locked page, Escape, and focus back on the control that opened it. Four edges, three depths, and the panel arrives with the 16px and the 400/350 the theme's one considered transition has always used |
| `ui/quantity-stepper` | One bounded quantity control for the cart line and the product page, replacing a select of six options on one and a minus-plus cluster on the other. A real number input between two controls: it posts, the browser clamps it, both bounds disable, and the press that reaches one moves focus to the figure before the button under it goes dead |
| `ui/variant-selector` | The glaze axis as native radios in a fieldset, so a choice posts with no JavaScript. A chosen chip takes the sage ground and a tick; an option that is out takes the muted ground and carries the firing it comes back in inside its own label, where a screen reader meets it |
| `ui/rating-input` | The rating a buyer sets in the review form: five stars, whole marks only, and a No rating option first so ArrowLeft takes it back off. Filled is a fill and not a colour, so the marks read in a greyscale capture |
| `ui/filter-chip` | One active-filter receipt in the collection browser: the value drawn as a pill and a separately named remove control, so the words on screen are the words in the accessible name. Removing one lands focus on the select that set it |
| `ui/search-input` | The shelf search over name, piece family and glaze: a real type=search with a drawn label, a clear that returns focus to the field before it empties, and a polite count that is the one sentence the whole filter bar announces through |
| `ui/pagination` | The reviews strip: mono page targets, ends that disable rather than wrap, one elision either side of a long run, and a live range sentence that says what the last move did |
| `ui/form` | The submit flow behind the restock list and the review form: validation on submit only, noValidate set from script so the JavaScript-free path still posts, values preserved through a refusal, a disabled and aria-busy fieldset while the request is out, and a focus destination at both ends |
| `ui/form-error-summary` | The counted panel a refused review lands on, with one link per field. It is drawn as the theme's card with the clay edge rather than as a second red band, because Kiln declares no critical token |
## Design tokens

| Token | Light | Dark override |
| --- | --- | --- |
| `--background` | `#f6f1ea` | `#171310` |
| `--foreground` | `#231c16` | `#f3ece2` |
| `--card` | `#ffffff` | `#201a16` |
| `--card-foreground` | `#231c16` | `#f3ece2` |
| `--primary` | `#b34a2c` | `#bc5234` |
| `--primary-hover` | `#9f3718` | `#ab4325` |
| `--primary-active` | `#8a2500` | `#9b3313` |
| `--primary-foreground` | `#ffffff` | `#ffffff` |
| `--secondary` | `#eee5d8` | `#2c211a` |
| `--secondary-foreground` | `#44372e` | `#e7ded1` |
| `--muted` | `#f1eae0` | `#241e19` |
| `--muted-foreground` | `#6a5c50` | `#ab9d8f` |
| `--accent` | `#e8ecdc` | `#24271b` |
| `--accent-foreground` | `#8f3a1f` | `#f0a184` |
| `--border` | `#e3d8c8` | `#3a2f27` |
| `--ring` | `#b34a2c` | `#bc5234` |
| `--input-border` | `#887d72` | `#81766c` |
| `--surface-tint` | `#f0e6dd` | `#2a2118` |
| `--shadow-color` | `#1f1307` | `#070200` |
| `--radius` | `0.375rem` | `0.375rem` |
| `--font-display` | `var(--font-newsreader), "Iowan Old Style", Georgia, "Times New Roman", serif` | `var(--font-newsreader), "Iowan Old Style", Georgia, "Times New Roman", serif` |
| `--font-text` | `var(--font-karla), ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif` | `var(--font-karla), ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif` |
| `--font-sans` | `var(--font-text)` | `var(--font-text)` |
| `--font-mono` | `var(--font-spline-mono), ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace` | `var(--font-spline-mono), ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace` |
| `--text-d1` | `1.5rem` | `1.5rem` |
| `--leading-d1` | `1.25` | `1.25` |
| `--tracking-d1` | `-0.01em` | `-0.01em` |
| `--text-d2` | `2rem` | `2rem` |
| `--leading-d2` | `1.2` | `1.2` |
| `--tracking-d2` | `-0.01em` | `-0.01em` |
| `--text-d3` | `clamp(2rem, 1.5rem + 2.5vw, 2.665rem)` | `clamp(2rem, 1.5rem + 2.5vw, 2.665rem)` |
| `--leading-d3` | `1.15` | `1.15` |
| `--tracking-d3` | `-0.02em` | `-0.02em` |
| `--text-d4` | `clamp(2.5rem, 1.75rem + 3.75vw, 3.553rem)` | `clamp(2.5rem, 1.75rem + 3.75vw, 3.553rem)` |
| `--leading-d4` | `1.08` | `1.08` |
| `--tracking-d4` | `-0.025em` | `-0.025em` |
| `--text-lead` | `1.125rem` | `1.125rem` |
| `--leading-lead` | `1.6` | `1.6` |
| `--text-body` | `1rem` | `1rem` |
| `--leading-body` | `1.55` | `1.55` |
| `--text-small` | `0.875rem` | `0.875rem` |
| `--leading-small` | `1.45` | `1.45` |
| `--text-caption` | `0.75rem` | `0.75rem` |
| `--leading-caption` | `1.4` | `1.4` |
| `--tracking-label` | `0.1em` | `0.1em` |
| `--display-weight` | `500` | `400` |
| `--duration-feedback` | `150ms` | `150ms` |
| `--ease-feedback` | `cubic-bezier(0, 0, 0.2, 1)` | `cubic-bezier(0, 0, 0.2, 1)` |
| `--duration-state` | `300ms` | `300ms` |
| `--ease-house` | `cubic-bezier(0.22, 1, 0.36, 1)` | `cubic-bezier(0.22, 1, 0.36, 1)` |
| `--duration-disclosure` | `250ms` | `250ms` |
| `--duration-panel-open` | `400ms` | `400ms` |
| `--duration-panel-close` | `350ms` | `350ms` |
| `--ease-swap` | `ease-in-out` | `ease-in-out` |
| `--scrim` | `color-mix(in oklab, var(--shadow-color) 38%, transparent)` | `color-mix(in oklab, var(--shadow-color) 62%, transparent)` |
| `--sheet-shift` | `1rem 0` | `1rem 0` |
## Use it

- Registry: `bunx shadcn add https://tailthemes.com/r/kiln.json`
- MCP: [https://tailthemes.com/api/mcp](https://tailthemes.com/api/mcp)
- Download: [https://tailthemes.com/api/download/kiln](https://tailthemes.com/api/download/kiln)

## Links

- Product page: [https://tailthemes.com/themes/kiln](https://tailthemes.com/themes/kiln)
- Live preview: [https://tailthemes.com/preview/kiln](https://tailthemes.com/preview/kiln)
- Docs: [https://tailthemes.com/docs/kiln](https://tailthemes.com/docs/kiln)
- Manifest JSON: [https://tailthemes.com/api/themes/kiln](https://tailthemes.com/api/themes/kiln)
- Registry item: [https://tailthemes.com/r/kiln.json](https://tailthemes.com/r/kiln.json)
