Overview
A dark, painterly HTML template for a fine art photographer of portraits and still lifes who sells her work as limited-edition prints. Tenebra ships three home pages, a dark and a light theme, right-to-left support, and logic that is worked out rather than typed in: every print in three sizes, each size its own numbered edition with the numbers left, the next number and a price that steps up as the edition sells; a configurator for paper, frame and mat; the edition register; portrait sittings booked in daylight from the season’s sunset; a proofing gallery; the exhibitions on now, next and past from the visitor’s date; a display check for print care; and a shop with a bag, coupon, shipping and tax.
What’s included
- 3 home pages, each with its own scroll moment: a still life composes itself (the linen, the jug, the pewter, two pears and a knife set down on the walnut table one by one), the edition ledger strikes its sold numbers, and a print’s frame and mat change option by option with the price of each
- A dark studio theme by default and a light theme on one switch in every header, remembered between visits and applied before the page paints
- Right-to-left stylesheets and a flag language picker in every header: a right-to-left language turns any page around
- WebGL: Craquelure, the fine cracks of old varnish spreading across the dark bands, and the footer’s cloth — a linen cloth simulated thread by thread, falling and settling over a still life you never see
- Limited editions: three sizes, each its own edition with 2 artist’s proofs, the numbers sold and on hold, the next number, a price ladder, and the edition register of every work
- The print page: size, paper, frame and mat with a live framed preview, the price line by line and the register for the chosen size
- Portrait sittings: four kinds, a four-step booking that only offers slots ending an hour before the day’s sunset, a deposit, and the sitter’s proofing gallery
- Portfolio masonry with series filters and search, a gallery wall, three single-work layouts, series and exhibitions now, next and past
- The print shop: series, subject, size and price filters, sort and search, bag, coupon, free-shipping meter, crate or tube shipping, tax, checkout with an order number, wishlist
- Print care with a display check, team, profile, journal, FAQ with search, account pages, coming soon, 404
- Window Light preloader, Side Light reveals, the mat bevel drawing in on hover, Swiper sliders and Fancybox lightboxes
- Fully responsive, zero console errors
Getting Started
Thank you for choosing Tenebra. Follow the steps below to get your site live.
1. Open locally
Unzip the downloaded package and open tenebra/index.html in any modern browser (Chrome, Firefox, Edge,
Safari). No build tools or server are required — the editions, the configurator, the sitting booking, the proofing
gallery, the display check and the shop all work straight from the files.
2. Deploy to a server
Upload the contents of the tenebra/ folder to your hosting provider using an FTP client (such as FileZilla)
or your hosting control panel. Once uploaded, navigate to your domain and the template will load immediately.
3. Customise
All custom styling lives in assets/css/style.css (with a numbered Table of CSS at the top) and
assets/css/responsive.css. Colours and fonts are controlled by CSS variables — see
Colors & Fonts. The works, their editions, the papers, frames and mats, the sittings, the
exhibitions and the shop rules live in one place — see Editions & Sittings.
assets/img/ with your own photography at the
same dimensions for the best result. The team portraits in img/team/ are 3:4; the still life’s objects in
img/stilllife/ are drawn, transparent PNGs and ship with the template.
File Structure
Below is an overview of the key files and folders included in this template.
index.html / homepage-2.html / homepage-3.html | Three home pages: The Studio (the still life composing itself), The Editions (the edition ledger), The Gallery beside the side menu (the frame and mat swap) |
portfolio.html / gallery.html | Every work in a masonry with series filters and search; the gallery wall at each work’s own proportion, with a lightbox |
work-details.html / work-details-2.html / work-details-3.html | Three single-work layouts: big images, a slider, small images with side notes; each with its three editions |
series.html / exhibition-details.html | The exhibitions on now, next and past from the date, the six series; one exhibition with its dates, works and a map |
sittings.html / book-sitting.html / proofing.html | The four sittings, the light by season; the four-step sitting booking; the sitter’s proofing gallery |
shop.html / product-single.html / editions.html | The print shop with filters; the print page with size, paper, frame and mat and its edition register; the register of every edition |
print-care.html | How to keep a print, and the display check that works out how long it will last on a wall |
cart.html / checkout.html / wishlist.html | The bag of numbered prints, checkout with delivery and an order number, the wishlist |
about.html / team.html / team-details.html | The photographer, the studio team, a team profile |
blog.html / blog-details.html | The journal with search; a post with reading time, share and comments |
faq.html | Questions by topic with search |
contact.html | Contact cards with the live viewing-room status, map, hours and the contact form |
account.html / login.html / register.html / forgot-password.html | The account (orders, numbered prints, sittings, details) and the login pages |
coming-soon.html / 404.html | Countdown to the new series, error page |
sendmail.php | AJAX mail handler for the contact and comment forms (see Contact Form) |
assets/css/style.css | All custom styles, numbered Table of CSS (19 sections) |
assets/css/responsive.css | Desktop-first breakpoints 1399 / 1199 / 991 / 767 / 575, plus wide screens from 1200, 1600 and 1960 |
assets/css/style.rtl.css / responsive.rtl.css | The right-to-left twins (see RTL) |
assets/css/plugins/ | Bootstrap (LTR and RTL), Swiper, Fancybox (vendor CSS) |
assets/js/main.js | All custom scripts, numbered JS INDEX (33 blocks) |
assets/js/plugins/ | jQuery, Bootstrap, Swiper, Fancybox, GSAP, ScrollTrigger |
assets/img/ | Images, one folder per section (see Images) |
assets/fonts/theme-icons/ | The sun and moon of the theme switch, loaded locally |
assets/webfonts/ + assets/css/fontawesome-all.css | Font Awesome Free, loaded locally |
Folder Structure
The package unzips to two folders: Documentation/ (this guide) and tenebra/, the template.
Every page sits in the template’s root; everything the pages load sits under assets/:
tenebra/
├── *.html the 33 pages, all in the root (see File Structure)
├── sendmail.php the AJAX mail handler for the forms
└── assets/
├── css/
│ ├── style.css all custom styles, numbered Table of CSS
│ ├── style.rtl.css the right-to-left twin of style.css
│ ├── responsive.css breakpoints 1399 / 1199 / 991 / 767 / 575
│ ├── responsive.rtl.css the right-to-left twin of responsive.css
│ ├── fontawesome-all.css Font Awesome Free, loaded locally
│ └── plugins/ vendor stylesheets
│ ├── bootstrap.min.css
│ ├── bootstrap.rtl.min.css Bootstrap for right-to-left pages
│ ├── jquery.fancybox.min.css
│ └── swiper-bundle.min.css
├── js/
│ ├── main.js all custom scripts, numbered JS INDEX (33 blocks)
│ └── plugins/ vendor scripts
│ ├── bootstrap.bundle.min.js
│ ├── gsap.min.js
│ ├── jquery-3.7.1.min.js
│ ├── jquery.fancybox.min.js
│ ├── ScrollTrigger.min.js
│ └── swiper-bundle.min.js
├── fonts/
│ └── theme-icons/ the sun and moon of the theme switch
├── webfonts/ Font Awesome Free font files
└── img/ images, one folder per section
├── blog/ 5 files
├── faces/ 5 files
├── flags/ 6 files
├── icons/ 34 files
├── logo/ 5 files
├── menu/ 3 files
├── stilllife/ 7 files
├── team/ 5 files
└── works/ 74 files
To publish the site, upload the contents of tenebra/ to your web root. Photos keep their folder,
file name and size when you replace them, so no page needs editing.
Colors & Fonts
The whole design is driven by CSS variables declared at the top of style.css. Change a token once and every
section, button and card updates. Colours used with transparency have an -rgb twin (for example
--tn-coral-rgb) — change both together.
--tn-charcoal--tn-raised--tn-slate--tn-umber--tn-coral--tn-coral-deep--tn-coral-soft--tn-mat--tn-heading--tn-textCharcoal is the studio: the ground of every page, with the raised panel for cards, the header bar and the frame round the
page. The footer slate is kept for the footer and the night bands; the olive umber is the still-life wall the scroll moments
and the craquelure bands are set on. Coral draws the hairline under every title, the prices, the active links and the
primary buttons (coral buttons carry charcoal text). Museum board is the mat round a print and the edition register’s
page. --tn-bg, --tn-surface, --tn-cream, --tn-heading and
--tn-text are the semantic names the components use — the light theme swaps those (see
Dark & Light Mode).
Fonts
Two Google Fonts are loaded in the head of each page: Old Standard TT (headings in widely tracked
capitals, prices and figures; its italic sets the soft word in a title and the leads) and Open Sans (body,
navigation, labels and buttons). Swap them by editing the Google Fonts link and the --tn-ff-display /
--tn-ff-body / --tn-ff-figure variables.
Logo
The logo is a T whose foot runs out into a looping hairline swash, in coral, beside or above TENEBRA in tracked Old
Standard TT capitals. It ships as ready-made PNG files in assets/img/logo/:
logo.png/logo-light.png— the row lockup for light and dark grounds (headers, the mobile menu)logo-stack-light.png— the stacked lockup on dark grounds (the footer, the preloader)mark.png— the T alone (the edition register, the seals on the login and error pages)favicon.png— the browser tab icon
The logo’s lettering appears only inside the images. To use your own logo, replace these files; the header sizes the logo by its height, so a wider or narrower file needs no CSS change.
Dark & Light Mode
Tenebra opens dark: the charcoal studio is the default and needs no attribute. Every header (and the mobile menu) has a
theme switch that opens the shutters. The choice is saved in the browser under the key tenebra-theme and
applied by a one-line script in the <head> before the page paints, so a light visitor never sees a dark
flash. The switch sets data-theme="light" on the <html> tag.
--tn-bg--tn-cream--tn-heading--tn-text--tn-accent-text- Tokens — the light values sit in one block,
[data-theme="light"] { … }, right after:rootinstyle.css. Change a light colour there. - Bands that stay dark — sections with the class
is-night(the footer, the mobile menu’s foot, the sidebar call to action, the account menu, the proofing bar) andis-umber(the still life, the edition ledger, the frame room, the light by season, the display check) keep their ground in both themes. - Museum board — the edition register’s page takes
tn-on-matand keeps its board and ink in both themes. - Icons — each illustrated icon ships in a dark and a light ink and the right one shows by theme and ground.
- Start light — to open the site light for first-time visitors, add
data-theme="light"to the<html>tag of each page; the switch still works and is still remembered.
Editions & Sittings
The figures on every page come from one data block, JS block 09 Tenebra Data near the
top of main.js (var TENEBRA). Edit it once and the editions, prices, dates and totals follow. Prices
are in US dollars; times are minutes after midnight (1080 = 6:00 pm); weekdays run 0 = Sunday to 6 = Saturday.
works | Every work: title, series, subject (still life or portrait), year, photograph and a price tier. An editioned work carries its sold numbers and the numbers on hold for each size (s, m, l); a work without them is shown in the portfolio only (private collection). |
sizes / proofs | 30 × 40 cm (edition of 25), 50 × 70 cm (edition of 15), 80 × 110 cm (edition of 7), each its own edition at its own base price, plus 2 artist’s proofs; the tube and crate shipping for each size. |
ladder | The price steps: First Release up to 40% of the edition sold, × 1.00; Second Release up to 80% of the edition sold, × 1.25; Last Numbers up to 100% of the edition sold, × 1.60. |
papers / frames / mats | Paper surcharges (Cotton Rag included, Baryta Fibre +10%, Japanese Kozo +18%), frames and mats priced by size; a mat needs a frame. |
series / exhibitions | The six series, and the exhibitions with their venue, city, start and end dates; the clock sorts them into on now, coming next and past from the visitor’s date. |
viewing / sunset | The viewing room’s hours by weekday (null = by appointment) for the open-now line, and the sunset in each month that the sitting slots follow. |
sittings | The sitting days, the slot starts, how far ahead, the deposit (30%), the four kinds (length, price, prints included, proofs), the backdrops and the extras. |
proofing | The sitter, the sitting, the proofs and the price of each extra print ($120). |
care | The paper’s rating (110 years at 450 lux, 12 hours a day), the rooms and the glazing for the display check. |
shop / team | Free shipping from $3,000, express $95 more, tax 8%, coupon COLLECTOR10 for 10% off; the studio team. |
How an edition is priced
A print’s base price is its size’s base times the work’s tier, to the nearest $10. The share of the edition already sold sets the step on the ladder, and the next number is sold at that step.
A configured print
A print in the bag holds its number: the register and every count on the site treat it as on hold for that visitor. The
bag, the wishlist and the coupon are kept in the browser (localStorage); the first visit starts with one demo
print and two saved works so every shop page has something to show — to start empty, remove the demo lines in
doShop() (block 27 Print Shop).
Sittings by daylight
A slot is offered only when the sitting, at its own length, ends an hour before that day’s sunset; longer sittings get fewer slots in winter.
Proofs and print care
Checkout, the sitting booking, the proofing gallery’s send and the account forms confirm on screen with a reference — connect them to your own systems to take real orders and bookings.
WebGL Layers
Two WebGL pieces move on the page: Craquelure — the fine cracks of an old varnish spreading slowly across the page titles and the dark bands, region by region, holding and fading — and the Cloth in the footer — a linen cloth with two coral threads, simulated point by point, falling and settling over a still life that is never shown, lifted away and dropped again. Each is one canvas:
<canvas class="tn-gl" data-gl="craquelure" aria-hidden="true"></canvas>
- Colours — the craquelure reads three colours and an opacity from CSS variables on its section:
--gl-1,--gl-2,--gl-3and--gl-alpha; the cloth reads--cloth-linen,--cloth-shadeand--cloth-threadon the footer. The theme switch repaints them. - Performance — a layer only runs while its section is on screen and the tab is visible. Visitors who prefer reduced motion get one still frame (the cloth at rest). Without WebGL, the section’s own background shows.
- Remove one — delete the canvas; nothing else depends on it.
RTL (Right To Left)
Tenebra is RTL-ready: the layout with its paddings and margins, the sliders and the animations all mirror. Right to
left comes from the stylesheets, not from separate pages: the language picker (a flag and a code) beside the dark / light
switch lists the languages that read left to right and right to left. Choosing Arabic stores the language and the
direction (keys tenebra-lang and tenebra-dir) and reloads; the one-line script in <head>
then sets dir="rtl" and swaps the three stylesheets for the files named in their data-rtl
attribute before the page paints. Nothing is translated — you bring your own right-to-left copy. To make a page
right to left for good:
1. Set The Direction
Add dir="rtl" to the page’s <html> tag (and set lang to your language):
<html lang="en" class="no-js" dir="rtl">
2. Swap Three Stylesheets
assets/css/plugins/bootstrap.min.css | → assets/css/plugins/bootstrap.rtl.min.css |
assets/css/style.css | → assets/css/style.rtl.css |
assets/css/responsive.css | → assets/css/responsive.rtl.css |
The scripts read the direction from the dir attribute: sliders and every move that must mirror follow it
on their own. Prices, edition numbers and the register keep reading left to right, and the still life keeps its window on
the left.
style.rtl.css and responsive.rtl.css are mirrored
twins of the LTR files, made with RTLCSS. A few hand-made rules at the end of style.rtl.css (after
RTL fixes) keep what must not mirror. If you change style.css, regenerate the twin with
npx rtlcss style.css style.rtl.css and copy the RTL fixes block back to its end.JavaScript Guide
All custom behaviour lives in assets/js/main.js, wrapped in a jQuery IIFE and organised into 33 numbered
blocks (see the JS INDEX comment at the top of the file):
- 01–08 — Preloader, Sticky Header, Offcanvas Menu, Scroll Reveals, Back To Top, Anchor Scroll, Light & Dark Mode, Direction, WebGL Layers. The core every page shares: the Window Light preloader, the header, the Side Light reveals, the theme and direction switch and the Craquelure layer.
- 09–10 — Tenebra Data, Editions Engine & Studio Clock. Tenebra Data holds every figure; the Editions Engine works out each edition’s numbers left, its next number and its price, and the Studio Clock the viewing room, today’s sunset and the exhibitions on now, next and past.
- 11–18 — Custom Select, Hero Sliders, Card Sliders, Accordion, Filter Tabs, Grid Search, Newsletter, Choice Cards. Shared components: selects, sliders, accordions, filters, search, forms.
- 19–21 — Still Life Composes Itself, Edition Ledger, Frame & Mat Swap. The three home pages’ scroll moments.
- 22–33 — Print Configurator, Sitting Booking, Proofing Gallery, Display Check, Contact Form, Print Shop, Account Forms, Article Tools, Demo Forms, Launch Countdown, Footer Cloth, FAQ Search. The feature pages: the print configurator, the sitting booking, the proofing gallery, the display check, contact, the print shop, accounts, the journal, the coming-soon countdown, the footer’s cloth and FAQ search.
Each block is a self-contained doSomething() function that safely skips pages where its section is not
present — you can delete any block you do not need. Every animation respects prefers-reduced-motion
automatically: content appears in its final state and the scrubbed scenes rest in place.
tn-lit to a photograph’s link or wrapper so it emerges from
black as light rises from its left (Side Light), tn-fade to text so it rises softly into place, and
tn-rule for the coral hairline under a title. data-delay="0.2" staggers any of them. Wrap a
photograph in <span class="tn-mat"> so a white museum mat with its bevel draws in round it on hover.doLaunchCountdown().Images
The download package ships with neutral placeholder images so you can see every layout at the correct dimensions.
Replace them with your own photos using the same file names, or update the src attributes. Images are
grouped inside assets/img/:
img/blog/ | Journal posts — 5 files: 1300×806, 2817×1747 |
img/faces/ | Round portraits for voices, bylines and avatar groups — 5 files: 305×305, 308×308 |
img/flags/ | The language picker’s flags (SVG, included) — 6 files: SVG |
img/icons/ | Illustrated line icons, each in a dark and a light ink (included) — 34 files: 128×128 |
img/logo/ | Logo, light logo, stacked logos, mark and favicon (included) — 5 files: 1339×205, 64×64, 1118×432, 360×425 |
img/menu/ | Home previews in the mega menu — 3 files: 560×350 |
img/stilllife/ | The still life’s table, linen, jug, pewter, pears and knife, transparent PNG (included) — 7 files: 1154×912, 513×770, 840×68, 282×587, 260×519, 804×183, … |
img/team/ | The studio team, portraits at 3:4 — 5 files: 767×1023, 836×1115, 837×1117, 583×777 |
img/works/ | The photographs: every work, the heroes, the page titles, the shop and the proofs — 74 files: 1100×1100, 900×900, 900×747, 1300×1300, 1218×888, 1508×969, … |
The logo files, the illustrated icons, the still life’s drawn objects and the language flags are included and ready to use. All demo photography shown on the live preview is for preview purposes only and is not included in the download.
Contact Form
The contact form and the comment form post to sendmail.php, a lightweight AJAX mail handler. The success
or error message appears under the button without reloading the page. The newsletter, sitting booking, proofing, checkout,
account, login, register and forgot-password forms are demo forms: they confirm on screen and are ready to connect to your
own systems. To activate the mail handler on your server:
1. Set your email address
Open sendmail.php and replace the placeholder recipient at the top of the file with your own address:
$email = 'example@example.com';
2. Upload to a PHP host
The form needs a server with PHP mail() support (virtually every shared host). It will not send from your
local computer or from a static-only host — there the form shows a friendly “upload to a PHP-enabled
host” notice instead.
3. Optional: enable Google reCAPTCHA v3
Invisible reCAPTCHA v3 spam protection is already built into the handler. It stays dormant until the form submits a token — that is why no badge or checkbox appears out of the box. To switch it on:
- Create v3 keys (a site key and a secret key) for your domain in the Google reCAPTCHA admin console
- In
sendmail.php, replaceYOUR_RECAPTCHA_V3_SECRET_KEYwith your secret key - In
contact.html, add a hidden token field inside the form and load the reCAPTCHA script with your site key just before</body>:
<!-- inside the form -->
<input type="hidden" name="g-recaptcha-response" id="g-recaptcha-response">
<!-- before the closing </body> tag -->
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
<script>
grecaptcha.ready(function () {
grecaptcha.execute('YOUR_SITE_KEY', { action: 'contact' }).then(function (token) {
document.getElementById('g-recaptcha-response').value = token;
});
});
</script>
Once the token field is present, sendmail.php verifies every submission with Google and rejects low-score
(bot) requests.
Credits
This template is built using the following open-source libraries and resources.
- Bootstrap 5.3.3
- jQuery 3.7.1
- Swiper 11
- GSAP 3 + ScrollTrigger
- Fancybox 3.5
- Font Awesome Free
- RTLCSS
- Google Fonts: Old Standard TT, Open Sans
All plugin files and icon fonts are loaded locally. The only external requests are the Google Fonts stylesheet, the Google Maps embeds and the video popups.
Support
If you have any questions or need help with the template, please reach out through the marketplace where you purchased the template: open the item there and send us your question from its support page. We aim to respond within 24–48 hours.