{"slug":"bellows","name":"Bellows — Photography Portfolio & Licensing","version":"1.0.0","category":"portfolio","priceCents":3900,"description":"A photography portfolio template nextjs and Tailwind v4 developers can hand to a working photographer, built on the half of the category that every other template omits: what the client receives, and what they may do with it. Colorlib's own round-up of twenty-four photography templates has one that mentions session pricing, none that mention usage or licensing, and an FAQ that sends readers to a third-party service for proofing. This ships the commercial surface instead of the gallery. Eight routes: a home page that opens on the signature at full size, a frame sheet of fourteen delivered crops at equal optical area, one commission followed end to end to a fee that is a sum of its own lines, a filterable and sortable register of every frame and every licence issued against it, a licensing page that prices four usage forms and states plainly what a licence does not grant, a studio page whose delivery record shows all six of its inputs, an enquiry form that redraws the crop as the usage changes, and a living component styleguide. The signature is the cut. Every photograph is shown as the whole frame it came out of, veiled, with the delivered licensed rectangle drawn on it at full strength, and the amount the negative overhangs the page is not a design choice but sensor divided by crop. Every figure in the margin is that function's output: sixteen point six megapixels, four by five, thirty-six point six per cent of the frame, three hundred and eighty-six millimetres at 300 dpi. The build refuses a page opener whose crop is not interior on all four edges, so the drawing can never collapse into two rules on a grey field. One chromatic token, an aniline stamp plum, carries the crop mark, the corner ticks, every hairline, every figure, every link and the filled button. Everything else is achromatic. There is no status colour anywhere: a licence reads live, expiring or expired from a solid, dashed or cancelled rule and the word beside it, so the distinction survives greyscale and a colour-blind reader. The board sits at L 0.92 rather than white, because a print on white reads as a blown highlight, and the card sits above it. Radius zero, one language, no exceptions. Fourteen photographs ship uncropped at 2400 by 1600, one per delivered frame, because the crop is performed in code and a register where two frames shared a photograph would be a lie the drawing itself exposes. Sixty-three design tokens, every one re-authored for dark, all derived in OKLCH and every pair computed against its contrast tier. Self-hosted Zilla Slab, Commissioner and Inconsolata. Built for Tailwind v4, Next.js 15 and React 19, and shipped with an independently authored HTML and vanilla JavaScript edition; every colour lives in theme.css.","summary":"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.","tags":["photography portfolio template nextjs","photographer website template nextjs","commercial photography website template","tailwind photography portfolio template","photography licensing website template","react photographer portfolio template","tailwind-v4"],"palette":{"bg":"#e5e5e5","surface":"#f6f6f6","accent":"#520f40","text":"#141414","line":"#bfbfbf"},"accent_mode":"monochrome-plus-spot","darkMode":true,"pages":[{"path":"/","title":"Commissioned photography, licensed by the frame","sections":["masthead","hero-cut","deliverable","usage-forms","commission-index","enquiry-band","site-footer"]},{"path":"/work","title":"The frame sheet","sections":["masthead","frame-sheet","commission-index","site-footer"]},{"path":"/commission","title":"One commission, end to end","sections":["masthead","commission-head","plate-run","shoot-record","grants-table","next-commission","site-footer"]},{"path":"/frames","title":"The frame register","sections":["masthead","frame-register","site-footer"]},{"path":"/licensing","title":"Usage and fees","sections":["masthead","three-cuts","tier-table","fee-bands","exclusions","enquiry-band","site-footer"]},{"path":"/studio","title":"The practice","sections":["masthead","studio-statement","method-rail","turnaround-record","credits","site-footer"]},{"path":"/enquire","title":"Brief a shoot","sections":["masthead","brief-form","terms-summary","site-footer"]},{"path":"/components","title":"The parts","sections":["masthead","site-footer"]}],"components":[{"name":"masthead","file":"src/components/masthead.tsx","renders":"The site's head: the wordmark, six routes, the mode control, and Enquire — which is the second action the hero deliberately does not carry."},{"name":"hero-cut","file":"src/components/hero-cut.tsx","renders":"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."},{"name":"deliverable","file":"src/components/deliverable.tsx","renders":"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."},{"name":"usage-forms","file":"src/components/usage-forms.tsx","renders":"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."},{"name":"commission-index","file":"src/components/commission-index.tsx","renders":"Six commissions as ruled rows: job, client, days, exposures and the frames delivered, most recent first."},{"name":"enquiry-band","file":"src/components/enquiry-band.tsx","renders":"One line and one action, at the foot of the pages where a reader has finished reading and has a job in mind."},{"name":"frame-sheet","file":"src/components/frame-sheet.tsx","renders":"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."},{"name":"commission-head","file":"src/components/commission-head.tsx","renders":"One job's standing: client, dates, days, exposures, delivered date and the turnaround it produced."},{"name":"plate-run","file":"src/components/plate-run.tsx","renders":"The commission's frames in flush register — each negative overhanging both margins with its delivered cut registered to the text measure."},{"name":"shoot-record","file":"src/components/shoot-record.tsx","renders":"How the job was actually shot, in the photographer's own words, beside the exposure and keeper counts it produced."},{"name":"grants-table","file":"src/components/grants-table.tsx","renders":"Every licence issued against this commission, itemised to a total that is a sum rather than a figure."},{"name":"next-commission","file":"src/components/next-commission.tsx","renders":"The next job in the index, as a ruled row rather than a card, so the sequence keeps going."},{"name":"frame-register","file":"src/components/frame-register.tsx","renders":"The fourteen frames as a filterable, sortable register: reference, subject, cut, coverage, and every grant with its state carried by texture and word."},{"name":"three-cuts","file":"src/components/three-cuts.tsx","renders":"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."},{"name":"tier-table","file":"src/components/tier-table.tsx","renders":"The four usage tiers with their aspect, term, territory, fee and what each one actually grants."},{"name":"fee-bands","file":"src/components/fee-bands.tsx","renders":"Shooting rates and what changes them, beside the usage fees rather than behind an enquiry."},{"name":"exclusions","file":"src/components/exclusions.tsx","renders":"What a licence does not grant, said plainly, because a licensing page that only says yes is marketing."},{"name":"studio-statement","file":"src/components/studio-statement.tsx","renders":"Who the practice is and what it will not do, in the register of a delivery note."},{"name":"method-rail","file":"src/components/method-rail.tsx","renders":"How a commission runs from brief to delivery, in four steps that each name what the client has to do."},{"name":"turnaround-record","file":"src/components/turnaround-record.tsx","renders":"The delivery record derived from six commissions: fastest, median and slowest, with the arithmetic visible."},{"name":"credits","file":"src/components/credits.tsx","renders":"Six publication credits as rows of title, issue and frame reference. Not a logo cloud."},{"name":"brief-form","file":"src/components/brief-form.tsx","renders":"The brief: what, where, when, and which usage — and the usage field redraws the cut as it changes."},{"name":"terms-summary","file":"src/components/terms-summary.tsx","renders":"The standing terms a brief is accepted under, as disclosures, so the page states them without becoming a contract."},{"name":"site-footer","file":"src/components/site-footer.tsx","renders":"The foot: the practice's standing details, the route list, and the licence line."},{"name":"ui/cut","file":"src/components/ui/cut.tsx","renders":"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."},{"name":"ui/negative","file":"src/components/ui/negative.tsx","renders":"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."},{"name":"ui/record","file":"src/components/ui/record.tsx","renders":"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."},{"name":"ui/rule","file":"src/components/ui/rule.tsx","renders":"The hairline in three textures — solid, hatched and struck — which is how a licence state survives greyscale without a status hue."},{"name":"ui/button","file":"src/components/ui/button.tsx","renders":"The one action shape: square, hairlined, with an explicit hover and active ground rather than an opacity."},{"name":"ui/input","file":"src/components/ui/input.tsx","renders":"A single-line control on the field ground, square, with its own darker border so it clears the graphic tier on the board."},{"name":"ui/select","file":"src/components/ui/select.tsx","renders":"A native select with the theme's own chevron, sized to the one control height every row on a form shares."},{"name":"ui/textarea","file":"src/components/ui/textarea.tsx","renders":"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."},{"name":"ui/checkbox","file":"src/components/ui/checkbox.tsx","renders":"A square box with a drawn tick, its label a real label element, its focus ring the same one everything else wears."},{"name":"ui/table","file":"src/components/ui/table.tsx","renders":"A ruled table with tabular figures, right-aligned money and a caption that says what the rows are."},{"name":"ui/tag","file":"src/components/ui/tag.tsx","renders":"The mono uppercase chip that carries a usage form, a territory or a term."},{"name":"ui/mode-toggle","file":"src/components/ui/mode-toggle.tsx","renders":"The light and dark control, an aria-pressed button whose glyph is chosen by CSS so it is right before the script runs."},{"name":"ui/field","file":"src/components/ui/field.tsx","renders":"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."},{"name":"ui/form","file":"src/components/ui/form.tsx","renders":"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."},{"name":"ui/form-error-summary","file":"src/components/ui/form-error-summary.tsx","renders":"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."},{"name":"ui/accordion-disclosure","file":"src/components/ui/accordion-disclosure.tsx","renders":"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."},{"name":"ui/dialog","file":"src/components/ui/dialog.tsx","renders":"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."},{"name":"ui/tabs","file":"src/components/ui/tabs.tsx","renders":"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."},{"name":"ui/search-input","file":"src/components/ui/search-input.tsx","renders":"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."},{"name":"ui/sort-head","file":"src/components/ui/sort-head.tsx","renders":"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."},{"name":"ui/toggle-group","file":"src/components/ui/toggle-group.tsx","renders":"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."}],"tokens":{"--background":"#e5e5e5","--foreground":"#141414","--card":"#f6f6f6","--card-foreground":"#141414","--muted":"#dbdbdb","--muted-foreground":"#5a5a5a","--primary":"#520f40","--primary-foreground":"#ffffff","--primary-hover":"#3a0b2d","--primary-active":"#25051c","--accent":"#5a5a5a","--accent-foreground":"#f6f6f6","--secondary":"#d4d4d4","--secondary-foreground":"#141414","--secondary-hover":"#c9c9c9","--secondary-active":"#bebebe","--border":"#bfbfbf","--hairline":"#cfcfcf","--field":"#f6f6f6","--field-border":"#7d7d7d","--selection":"#cdcdcd","--selection-border":"#520f40","--ring":"#520f40","--frame-veil":"rgb(229 229 229 / 0.50)","--frame-desaturate":"1","--shadow-color":"#141414","--shadow-overlay":"0 12px 32px color-mix(in oklab, var(--shadow-color) 22%, transparent)","--radius":"0rem","--font-display":"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","--font-sans":"var(--font-text)","--font-mono":"var(--font-bellows-mono), ui-monospace, SFMono-Regular, Menlo, Consolas, monospace","--record-width":"88","--record-width-tight":"76","--text-caption":"0.75rem","--text-small":"0.875rem","--text-body":"1rem","--text-lead":"1.125rem","--leading-caption":"1.4","--leading-small":"1.45","--leading-body":"1.55","--leading-lead":"1.6","--text-d1":"1.5rem","--text-d2":"2rem","--text-d3":"clamp(2rem, 1.3rem + 2.8vw, 2.6656rem)","--text-d4":"clamp(2.4rem, 1.2rem + 4.8vw, 3.5533rem)","--leading-d1":"1.25","--leading-d2":"1.18","--leading-d3":"1.12","--leading-d4":"1.06","--tracking-d1":"0.002em","--tracking-d2":"-0.004em","--tracking-d3":"-0.010em","--tracking-d4":"-0.016em","--tracking-eyebrow":"0.18em","--tracking-label":"0.12em","--tracking-wordmark":"0.26em","--weight-display":"500","--weight-display-strong":"700","--control-height":"2.75rem","--ease-house":"cubic-bezier(0.22, 1, 0.36, 1)","--duration-micro":"120ms","--duration-state":"180ms"},"tokensDark":{"--background":"#1c1c1c","--foreground":"#ededed","--card":"#262626","--card-foreground":"#ededed","--muted":"#232323","--muted-foreground":"#a3a3a3","--primary":"#e2a3cc","--primary-foreground":"#141414","--primary-hover":"#eebbdd","--primary-active":"#f7d5ea","--accent":"#a3a3a3","--accent-foreground":"#141414","--secondary":"#303030","--secondary-foreground":"#ededed","--secondary-hover":"#3a3a3a","--secondary-active":"#444444","--border":"#434343","--hairline":"#353535","--field":"#141414","--field-border":"#787878","--selection":"#343434","--selection-border":"#e2a3cc","--ring":"#e2a3cc","--frame-veil":"rgb(28 28 28 / 0.70)","--frame-desaturate":"1","--shadow-color":"#000000","--shadow-overlay":"0 12px 32px color-mix(in oklab, var(--shadow-color) 58%, transparent)","--radius":"0rem","--font-display":"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","--font-sans":"var(--font-text)","--font-mono":"var(--font-bellows-mono), ui-monospace, SFMono-Regular, Menlo, Consolas, monospace","--record-width":"88","--record-width-tight":"76","--text-caption":"0.75rem","--text-small":"0.875rem","--text-body":"1rem","--text-lead":"1.125rem","--leading-caption":"1.4","--leading-small":"1.45","--leading-body":"1.55","--leading-lead":"1.6","--text-d1":"1.5rem","--text-d2":"2rem","--text-d3":"clamp(2rem, 1.3rem + 2.8vw, 2.6656rem)","--text-d4":"clamp(2.4rem, 1.2rem + 4.8vw, 3.5533rem)","--leading-d1":"1.25","--leading-d2":"1.18","--leading-d3":"1.12","--leading-d4":"1.06","--tracking-d1":"0.006em","--tracking-d2":"0.000em","--tracking-d3":"-0.006em","--tracking-d4":"-0.012em","--tracking-eyebrow":"0.18em","--tracking-label":"0.12em","--tracking-wordmark":"0.26em","--weight-display":"500","--weight-display-strong":"700","--control-height":"2.75rem","--ease-house":"cubic-bezier(0.22, 1, 0.36, 1)","--duration-micro":"120ms","--duration-state":"180ms"},"compatibility":{"next":">=15","react":">=19","tailwind":">=4"},"dependencies":["lucide-react"],"editions":{"react":{"source":"src","runtime":"react"},"html":{"source":"html/src","output":"html/dist","engine":"nunjucks","runtime":"vanilla","buildRequired":false}},"ui_library":{"version":"1.4.0","files":["html/src/js/_tt/accordion-disclosure.js","html/src/js/_tt/dialog.js","html/src/js/_tt/focusables.js","html/src/js/_tt/form-error-summary.js","html/src/js/_tt/layer.js","html/src/js/_tt/presence.js","html/src/js/_tt/reduced-motion.js","html/src/js/_tt/roving.js","html/src/js/_tt/search-input.js","html/src/js/_tt/sort-head.js","html/src/js/_tt/table-sort.js","html/src/js/_tt/tabs.js","html/src/js/_tt/toggle-group.js","src/components/ui/_tt/accordion-disclosure.axes.ts","src/components/ui/_tt/accordion-disclosure.fixtures.ts","src/components/ui/_tt/accordion-disclosure.tsx","src/components/ui/_tt/capture.ts","src/components/ui/_tt/dialog.axes.ts","src/components/ui/_tt/dialog.fixtures.ts","src/components/ui/_tt/dialog.tsx","src/components/ui/_tt/field-ids.ts","src/components/ui/_tt/field.axes.ts","src/components/ui/_tt/field.capture.ts","src/components/ui/_tt/field.fixtures.ts","src/components/ui/_tt/field.tsx","src/components/ui/_tt/focusables.ts","src/components/ui/_tt/form-error-summary.axes.ts","src/components/ui/_tt/form-error-summary.fixtures.ts","src/components/ui/_tt/form-error-summary.tsx","src/components/ui/_tt/form.axes.ts","src/components/ui/_tt/form.fixtures.ts","src/components/ui/_tt/form.part.tsx","src/components/ui/_tt/form.ts","src/components/ui/_tt/host.ts","src/components/ui/_tt/ids.ts","src/components/ui/_tt/layer.ts","src/components/ui/_tt/layout-effect.ts","src/components/ui/_tt/presence.ts","src/components/ui/_tt/recipe.ts","src/components/ui/_tt/reduced-motion.ts","src/components/ui/_tt/roving.ts","src/components/ui/_tt/search-input.axes.ts","src/components/ui/_tt/search-input.capture.ts","src/components/ui/_tt/search-input.fixtures.ts","src/components/ui/_tt/search-input.tsx","src/components/ui/_tt/sort-head.axes.ts","src/components/ui/_tt/sort-head.fixtures.ts","src/components/ui/_tt/sort-head.tsx","src/components/ui/_tt/table-sort.ts","src/components/ui/_tt/tabs.axes.ts","src/components/ui/_tt/tabs.fixtures.ts","src/components/ui/_tt/tabs.tsx","src/components/ui/_tt/toggle-group.axes.ts","src/components/ui/_tt/toggle-group.fixtures.ts","src/components/ui/_tt/toggle-group.tsx"]},"license":"standard","design":{"typefaces":{"display":"Zilla Slab","text":"Commissioner","mono":"Inconsolata"},"typeRatio":1.333,"signature":"The cut: every photograph shown as the whole frame it came out of, veiled, with the delivered licensed rectangle drawn on it at full strength and the overhang computed as sensor divided by cut."}}