# Bellows — Photography Portfolio Template Next.js

> A photography portfolio template nextjs developers can ship for a photographer: every crop drawn on the frame it came from, with usage and fees on the page.

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

## What's inside

- 8 pages
- 45 components
- 63 design tokens plus 63 dark overrides

## Pages

| Path | Sections |
| --- | --- |
| `/` | `masthead`, `hero-cut`, `deliverable`, `usage-forms`, `commission-index`, `enquiry-band`, `site-footer` |
| `/work` | `masthead`, `frame-sheet`, `commission-index`, `site-footer` |
| `/commission` | `masthead`, `commission-head`, `plate-run`, `shoot-record`, `grants-table`, `next-commission`, `site-footer` |
| `/frames` | `masthead`, `frame-register`, `site-footer` |
| `/licensing` | `masthead`, `three-cuts`, `tier-table`, `fee-bands`, `exclusions`, `enquiry-band`, `site-footer` |
| `/studio` | `masthead`, `studio-statement`, `method-rail`, `turnaround-record`, `credits`, `site-footer` |
| `/enquire` | `masthead`, `brief-form`, `terms-summary`, `site-footer` |
| `/components` | `masthead`, `site-footer` |

## Components

| Name | What it renders |
| --- | --- |
| `masthead` | The site's head: the wordmark, six routes, the mode control, and Enquire — which is the second action the hero deliberately does not carry. |
| `hero-cut` | The opener: TC-014's whole negative bleeding off the right edge with its 4:5 cover cut drawn interior on all four sides, the caption above the headline, and the cut record under the cut. |
| `deliverable` | What a client actually receives at the end of a job — the files, the reference, the licence document and the term — stated as four rows rather than promised. |
| `usage-forms` | The four things that can be licensed, each drawn as its own cut on its own frame, so the shapes are compared rather than described. |
| `commission-index` | Six commissions as ruled rows: job, client, days, exposures and the frames delivered, most recent first. |
| `enquiry-band` | One line and one action, at the foot of the pages where a reader has finished reading and has a job in mind. |
| `frame-sheet` | Fourteen delivered cuts at equal optical area, so four licence aspects sit side by side at equal weight; density and record placement both switch here. |
| `commission-head` | One job's standing: client, dates, days, exposures, delivered date and the turnaround it produced. |
| `plate-run` | The commission's frames in flush register — each negative overhanging both margins with its delivered cut registered to the text measure. |
| `shoot-record` | How the job was actually shot, in the photographer's own words, beside the exposure and keeper counts it produced. |
| `grants-table` | Every licence issued against this commission, itemised to a total that is a sum rather than a figure. |
| `next-commission` | The next job in the index, as a ruled row rather than a card, so the sequence keeps going. |
| `frame-register` | The fourteen frames as a filterable, sortable register: reference, subject, cut, coverage, and every grant with its state carried by texture and word. |
| `three-cuts` | The oversized moment: one negative at 1200px with a cover, a spread and a web lead drawn on it simultaneously, each with its own overhang. |
| `tier-table` | The four usage tiers with their aspect, term, territory, fee and what each one actually grants. |
| `fee-bands` | Shooting rates and what changes them, beside the usage fees rather than behind an enquiry. |
| `exclusions` | What a licence does not grant, said plainly, because a licensing page that only says yes is marketing. |
| `studio-statement` | Who the practice is and what it will not do, in the register of a delivery note. |
| `method-rail` | How a commission runs from brief to delivery, in four steps that each name what the client has to do. |
| `turnaround-record` | The delivery record derived from six commissions: fastest, median and slowest, with the arithmetic visible. |
| `credits` | Six publication credits as rows of title, issue and frame reference. Not a logo cloud. |
| `brief-form` | The brief: what, where, when, and which usage — and the usage field redraws the cut as it changes. |
| `terms-summary` | The standing terms a brief is accepted under, as disclosures, so the page states them without becoming a contract. |
| `site-footer` | The foot: the practice's standing details, the route list, and the licence line. |
| `ui/cut` | The signature: a licensed rectangle drawn at full strength on its own veiled negative, with corner ticks in the veil and every figure computed from the cut. |
| `ui/negative` | The whole frame a cut came out of, grayscaled and composited toward the board under the veil, sized so the overhang is sensor divided by cut. |
| `ui/record` | The mono caption block: reference, subject, place and date, or the four computed lines of a cut, on a hairline or on a board chip in the veil. |
| `ui/rule` | The hairline in three textures — solid, hatched and struck — which is how a licence state survives greyscale without a status hue. |
| `ui/button` | The one action shape: square, hairlined, with an explicit hover and active ground rather than an opacity. |
| `ui/input` | A single-line control on the field ground, square, with its own darker border so it clears the graphic tier on the board. |
| `ui/select` | A native select with the theme's own chevron, sized to the one control height every row on a form shares. |
| `ui/textarea` | A multi-line control at the same measure as the form's prose, so a brief is written at the width it is read at. |
| `ui/checkbox` | A square box with a drawn tick, its label a real label element, its focus ring the same one everything else wears. |
| `ui/table` | A ruled table with tabular figures, right-aligned money and a caption that says what the rows are. |
| `ui/tag` | The mono uppercase chip that carries a usage form, a territory or a term. |
| `ui/mode-toggle` | The light and dark control, an aria-pressed button whose glyph is chosen by CSS so it is right before the script runs. |
| `ui/field` | A labelled control on a ruled foot, where the message's rule weight is the verdict: 2px for a refusal, a hairline for a description. |
| `ui/form` | The brief's submit path — validation, a pending state that never dims the values a reader is watching, and a receipt that stays because an accepted brief is a fact. |
| `ui/form-error-summary` | A returned docket rather than a red box: a card lifted off the board under a 2px head rule, one hairline-divided row per unanswered field, focused on rejection. |
| `ui/accordion-disclosure` | The exclusions on the licensing page and the standing terms on the brief, set as a printed schedule: a rule under every clause and the reference held right. |
| `ui/dialog` | The full licence terms, over a scrim that is the theme's own frame veil — the page held back toward the board the way every negative is, not switched off. |
| `ui/tabs` | The four usage tiers as a card index: the selected tier lifts to the card ground, paints out its own bottom edge and joins its panel into one sheet. |
| `ui/search-input` | The frame register's filter, over reference, subject, client and place, with a drawn label because those are four different questions and a count sentence that is read as well as announced. |
| `ui/sort-head` | The register's comparators on reference, date made and expiry; the sorted column is marked by turning its own hairline to the ink, not by a coloured cell. |
| `ui/toggle-group` | The register's two filters and the sheet's density axis as one divided rail, because at radius zero a separated chip and a small button are the same rectangle. |
## Design tokens

| Token | Light | Dark override |
| --- | --- | --- |
| `--background` | `#e5e5e5` | `#1c1c1c` |
| `--foreground` | `#141414` | `#ededed` |
| `--card` | `#f6f6f6` | `#262626` |
| `--card-foreground` | `#141414` | `#ededed` |
| `--muted` | `#dbdbdb` | `#232323` |
| `--muted-foreground` | `#5a5a5a` | `#a3a3a3` |
| `--primary` | `#520f40` | `#e2a3cc` |
| `--primary-foreground` | `#ffffff` | `#141414` |
| `--primary-hover` | `#3a0b2d` | `#eebbdd` |
| `--primary-active` | `#25051c` | `#f7d5ea` |
| `--accent` | `#5a5a5a` | `#a3a3a3` |
| `--accent-foreground` | `#f6f6f6` | `#141414` |
| `--secondary` | `#d4d4d4` | `#303030` |
| `--secondary-foreground` | `#141414` | `#ededed` |
| `--secondary-hover` | `#c9c9c9` | `#3a3a3a` |
| `--secondary-active` | `#bebebe` | `#444444` |
| `--border` | `#bfbfbf` | `#434343` |
| `--hairline` | `#cfcfcf` | `#353535` |
| `--field` | `#f6f6f6` | `#141414` |
| `--field-border` | `#7d7d7d` | `#787878` |
| `--selection` | `#cdcdcd` | `#343434` |
| `--selection-border` | `#520f40` | `#e2a3cc` |
| `--ring` | `#520f40` | `#e2a3cc` |
| `--frame-veil` | `rgb(229 229 229 / 0.50)` | `rgb(28 28 28 / 0.70)` |
| `--frame-desaturate` | `1` | `1` |
| `--shadow-color` | `#141414` | `#000000` |
| `--shadow-overlay` | `0 12px 32px color-mix(in oklab, var(--shadow-color) 22%, transparent)` | `0 12px 32px color-mix(in oklab, var(--shadow-color) 58%, transparent)` |
| `--radius` | `0rem` | `0rem` |
| `--font-display` | `var(--font-bellows-display), ui-serif, Rockwell, Georgia, "Times New Roman", serif` | `var(--font-bellows-display), ui-serif, Rockwell, Georgia, "Times New Roman", serif` |
| `--font-text` | `var(--font-bellows-text), ui-sans-serif, system-ui, -apple-system, "Segoe UI", Arial, sans-serif` | `var(--font-bellows-text), ui-sans-serif, system-ui, -apple-system, "Segoe UI", Arial, sans-serif` |
| `--font-sans` | `var(--font-text)` | `var(--font-text)` |
| `--font-mono` | `var(--font-bellows-mono), ui-monospace, SFMono-Regular, Menlo, Consolas, monospace` | `var(--font-bellows-mono), ui-monospace, SFMono-Regular, Menlo, Consolas, monospace` |
| `--record-width` | `88` | `88` |
| `--record-width-tight` | `76` | `76` |
| `--text-caption` | `0.75rem` | `0.75rem` |
| `--text-small` | `0.875rem` | `0.875rem` |
| `--text-body` | `1rem` | `1rem` |
| `--text-lead` | `1.125rem` | `1.125rem` |
| `--leading-caption` | `1.4` | `1.4` |
| `--leading-small` | `1.45` | `1.45` |
| `--leading-body` | `1.55` | `1.55` |
| `--leading-lead` | `1.6` | `1.6` |
| `--text-d1` | `1.5rem` | `1.5rem` |
| `--text-d2` | `2rem` | `2rem` |
| `--text-d3` | `clamp(2rem, 1.3rem + 2.8vw, 2.6656rem)` | `clamp(2rem, 1.3rem + 2.8vw, 2.6656rem)` |
| `--text-d4` | `clamp(2.4rem, 1.2rem + 4.8vw, 3.5533rem)` | `clamp(2.4rem, 1.2rem + 4.8vw, 3.5533rem)` |
| `--leading-d1` | `1.25` | `1.25` |
| `--leading-d2` | `1.18` | `1.18` |
| `--leading-d3` | `1.12` | `1.12` |
| `--leading-d4` | `1.06` | `1.06` |
| `--tracking-d1` | `0.002em` | `0.006em` |
| `--tracking-d2` | `-0.004em` | `0.000em` |
| `--tracking-d3` | `-0.010em` | `-0.006em` |
| `--tracking-d4` | `-0.016em` | `-0.012em` |
| `--tracking-eyebrow` | `0.18em` | `0.18em` |
| `--tracking-label` | `0.12em` | `0.12em` |
| `--tracking-wordmark` | `0.26em` | `0.26em` |
| `--weight-display` | `500` | `500` |
| `--weight-display-strong` | `700` | `700` |
| `--control-height` | `2.75rem` | `2.75rem` |
| `--ease-house` | `cubic-bezier(0.22, 1, 0.36, 1)` | `cubic-bezier(0.22, 1, 0.36, 1)` |
| `--duration-micro` | `120ms` | `120ms` |
| `--duration-state` | `180ms` | `180ms` |
## Use it

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

## Links

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