Lumina documentation
A 24-page agency and portfolio template built with HTML5, CSS3, vanilla JavaScript and Bootstrap 5. No other framework, no build step required, and no network connection needed to run it.
1. Getting started
The HTML files in the root of the template are the template. Open any of them in a browser and it works — there is nothing to install and no server to start.
- Unzip the download.
-
Open
index.htmlin a browser to check everything looks right. - Edit the HTML directly, or use the source layout described in section 10 if you would rather change the header once instead of twenty-four times.
- Upload the whole folder to any static host.
Optional local server. Some browsers restrict
file:// pages. If a font or script does not load when
you open a file directly, serve the folder instead:
npx serve . or python3 -m http.server.
2. File structure
lumina-html-template/
├── *.html 24 ready-to-edit pages
├── assets/
│ ├── css/
│ │ ├── style.css design system + every component
│ │ ├── style.min.css minified copy
│ │ ├── fonts.css @font-face rules (generated)
│ │ └── fonts.min.css
│ ├── js/
│ │ ├── main.js all interactions, commented
│ │ └── main.min.js minified copy
│ ├── img/ 25 generated SVG placeholders
│ ├── fonts/ 8 self-hosted woff2 files
│ └── vendor/ Bootstrap, Swiper, Lenis + licences
├── src/ sources for the optional build
│ ├── partials/ head, header, footer, scripts
│ ├── pages/ body content, one file per page
│ └── icons.json the 45 icons this template uses
├── tools/ asset, vendor, icon and minify scripts
├── docs/ this documentation
├── build.js assembles src/ into the root HTML
└── README.md
3. The 24 pages
| Page | File | Notable |
|---|---|---|
| Home — Agency | index.html | Rotating headline, process rows, slider |
| Home — Portfolio | home-portfolio.html | Split hero, experience list |
| Home — Studio | home-studio.html | Tabs, skill bars |
| About | about.html | Values, stats, team preview |
| Services | services.html | Numbered rows, accordion |
| Service Details | service-details.html | Sticky sidebar |
| Portfolio Grid | portfolio.html | Category filter |
| Portfolio Masonry | portfolio-masonry.html | CSS columns, lightbox |
| Case Study | portfolio-details.html | Result stats, next project |
| Team | team.html | Hover social bar |
| Team Member | team-details.html | Focus areas, counters |
| Pricing | pricing.html | Monthly / yearly switch |
| Testimonials | testimonials.html | Filterable reviews |
| Careers | careers.html | Job cards, benefits |
| Job Opening | career-details.html | Application form |
| Journal Grid | blog.html | Featured post, pagination |
| Journal List | blog-list.html | Filterable list rows |
| Article | blog-details.html | Contents sidebar, author |
| FAQ | faq.html | Three accordion groups |
| Contact | contact.html | Validated form |
| Privacy Policy | privacy.html | Sticky contents |
| Terms of Service | terms.html | Sticky contents |
| 404 | 404.html | No header or footer |
| Coming Soon | coming-soon.html | Countdown, subscribe |
4. Colours & themes
Every colour is a CSS custom property declared at the top of
assets/css/style.css. Change the accent and the
buttons, links, icons, badges, focus rings and gradients all follow.
:root {
--lu-accent: #ff4d2e; /* drives the whole template */
--lu-accent-ink: #ff4d2e; /* the accent used as text */
--lu-accent-fill: #e11d00; /* the accent behind white text */
--lu-bg: #0a0a0c;
--lu-bg-elev: #121216;
--lu-text: #f4f4f1;
--lu-text-muted: #8f8f99;
--lu-line: rgba(244, 244, 241, 0.1);
}
The accent is three tokens rather than one, because a single colour
cannot do all three jobs and still meet WCAG AA.
--lu-accent is the brand colour and paints lines, dots,
glows, the progress bar and the rotating word in the hero headline
— display-size text needs only 3:1, and the brand colour gives
3.1:1 even on the light background. --lu-accent-ink is the
accent used as text; it stays the brand colour on dark and
darkens to #c81500 in the light theme, where the bright
one reads at only 3.1:1 on paper. --lu-accent-fill is
the accent behind white text — the primary button, the
skip link, the pricing badge — because white on
#ff4d2e is 3.3:1 and needs 4.5:1. If you change the
accent, pick all three: a light brand colour, a darker one for text
on light surfaces, and one dark enough to carry white.
Light theme
The same names are redefined under
[data-theme="light"]. The toggle in the header writes
the choice to localStorage, and a small script in
<head> applies it before the first paint so the
page never flashes.
To ship light as the default, change
<html lang="en" data-theme="dark"> to
data-theme="light" on every page — or once in
build.js if you use the build.
5. Typography
| Role | Family | Token |
|---|---|---|
| Display | Syne 600/700/800 | --lu-font-display |
| Body | Inter 400/500/600 | --lu-font-body |
| Labels | JetBrains Mono 400/500 | --lu-font-mono |
All three are self-hosted in assets/fonts/ under the
SIL Open Font License, with @font-face rules generated
into assets/css/fonts.css. Nothing is fetched from
Google Fonts at runtime.
Swapping a family
- Install the replacement:
npm i @fontsource/your-font - Edit the
FAMILIESarray intools/vendor.js - Run
npm run vendor - Update the token in
style.css
Sizes use clamp(), so headings scale smoothly between
phone and desktop without per-breakpoint overrides.
Watch the display size. The masked line reveal
clips anything wider than its column. A hero that shares its row
with another column should carry
lu-hero--split, which uses a smaller scale.
6. Icons
Icons are inlined as plain <svg> when the page is
built, so there is no icon library and no runtime pass over the DOM.
The 45 icons used here come from Lucide and live in
src/icons.json.
Adding an icon
-
Put a placeholder in your markup:
<i data-lucide="rocket"></i> - Run
npm run iconsto extract it - Run
npm run build
Editing the root HTML by hand instead? Copy an existing
<svg> and swap its paths for any 24×24 icon on a
2px stroke grid.
Names change between Lucide versions. If
npm run icons reports one as not found, look up its
current name — home became house and
unlock became lock-open, for example.
7. Images
The 25 files in assets/img/ are abstract SVGs generated
by tools/gen-assets.js. Alongside them sits og.jpg, the 1200×630 social-share card referenced by og:image on every page — replace it with your own and update the URL in src/partials/head.html. No stock photography is
bundled, so there is nothing to license before you redistribute or
sell a site built on this template.
Replacing them with real photography: keep the aspect ratios.
| Use | Ratio | Files |
|---|---|---|
| Portfolio tiles | 4 : 3 | work-1…8.svg |
| Article thumbnails | 16 : 10 | post-1…6.svg |
| Team portraits | 3 : 4 | member-1…8.svg |
| Wide features | 16 : 9 | feature-1…2.svg |
Every <img> already carries
width, height and
loading="lazy". Keep those attributes — they are what
stops the layout shifting while images load.
8. Components
| Component | Markup hook | Notes |
|---|---|---|
| Scroll reveal | data-reveal | up (default), left, right, zoom, fade; delay with --d |
| Line reveal | .lu-lines | Splits the heading on <br> |
| Counter | data-count="140" | Decimals respected |
| Word rotator | .lu-rotator | One <span> per word |
| Filter | data-filter-bar + data-cat | Works for work, posts, reviews |
| Accordion | .lu-acc | data-single="false" allows several open |
| Tabs | data-tabs | ARIA tablist, arrow keys |
| Skill bar | data-pct="96" | Fills when scrolled into view |
| Slider | data-swiper | data-per-view sets desktop columns |
| Lightbox | data-lightbox="path" | Arrow keys, Escape, focus restored |
| Search | data-search-open | Index is SEARCH_INDEX in main.js |
| Cookie bar | .lu-cookie | Choice stored in localStorage |
| Countdown | data-countdown="ISO date" | Used on Coming Soon |
| Pricing switch | data-billing | data-monthly / data-yearly |
Example
<div data-reveal>Fades up when it enters the viewport</div>
<div data-reveal="left" style="--d: 120ms">Slides in, slightly later</div>
9. Forms
Four forms ship with the template: contact, job application, newsletter and the Coming Soon subscribe box. All are validated in the browser and none of them post anywhere — that part is yours to wire up.
Open assets/js/main.js, find
initForms(), and replace the success branch:
if (ok) {
const res = await fetch("/api/contact", {
method: "POST",
body: new FormData(form),
});
// show your own confirmation
}
Any endpoint works — a serverless function, Formspree, Basin, or
your own backend. Add data-validate to any new form to
get the same validation behaviour.
10. Right-to-left
The template ships an RTL stylesheet,
assets/css/rtl.css. Most of the layout already uses
logical properties — padding-inline,
margin-inline, inset-inline — so the file
only restates the genuinely directional rules and stays under
4 KB.
Mirroring a page takes two changes, and both are already marked in
every page’s <head>:
<html lang="ar" dir="rtl" data-theme="dark">
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="assets/css/rtl.css"> <!-- uncomment this -->
index-rtl.html is a working example: it is
index.html with exactly those two changes and nothing
else. Open the two side by side to see what moves.
The example keeps its English copy on purpose, so you can compare it with the original. One thing to expect while it does: a full stop after Latin text lands on the left in an RTL context. That is the browser’s bidirectional algorithm behaving correctly, and it goes away as soon as the text is Arabic, Hebrew, Farsi or Urdu.
Checked in RTL across all 24 pages: no horizontal scroll, and nothing escaping the viewport.
11. Build tools
Optional. The root HTML files are complete on their own; the build exists so shared markup lives in one place while you work.
| Command | Does |
|---|---|
npm run build | Assembles src/ into the 24 root pages |
npm run build:min | Same, pointing at the minified assets |
npm run minify | Regenerates the .min files |
npm run icons | Re-extracts used icons into src/icons.json |
npm run assets | Regenerates the SVG placeholders |
npm run vendor | Re-copies libraries and fonts from node_modules |
npm run setup | All of the above, in order |
npm run serve | Serves the folder locally |
Page titles and meta descriptions live in the
MANIFEST array in build.js, alongside the
filename. Add an entry there and a matching file in
src/pages/ to create a new page.
12. Going live
-
Replace
https://example.comin the canonical, Open Graph and Twitter tags. They are insrc/partials/head.html, or in each page's<head>. -
Add a real
assets/img/og.jpgat 1200×630 for social sharing. -
Replace the placeholder contact details, addresses and the
example.comemail addresses. -
Have a lawyer review
privacy.htmlandterms.html. They are drafting aids, not legal advice. - Run
npm run build:minfor the minified build. - Upload everything except
node_modules/.
Any static host works: Netlify, Vercel, Cloudflare Pages, GitHub Pages, S3, or ordinary shared hosting. There is no server-side code.
13. Accessibility
- Semantic landmarks throughout, one
<h1>per page, headings in order - Skip-to-content link as the first focusable element
- Visible focus rings on every interactive element
aria-expanded,aria-controls,aria-selectedandaria-pressedon togglesaria-livestatus messages on forms- Dialogs trap nothing away from you: Escape closes, focus returns
- Full
prefers-reduced-motionsupport — reveals, marquees, smooth scroll and autoplay stand down - A
<noscript>block renders the whole site without JavaScript - No interactive element nests inside another
- On phones every control — buttons, the menu, filters, pagination, breadcrumbs, social and footer links — is at least 44 × 44 px. Links inside a sentence keep their natural size, as WCAG allows.
- The team cards’ social row is revealed on hover with a mouse and shown outright on a touch screen, so it is never out of reach
If you add content, keep contrast in mind in both themes, and give
every image a meaningful alt — or an empty one when it
is purely decorative.
14. FAQ
Do I need Node to use this?
No. The root HTML files work as they are. Node is only for the optional build and asset scripts.
Can I use it for a client project?
Yes, under your purchased licence. One regular licence covers one end product.
Why is there no jQuery?
Nothing here needs it. Everything is plain modern JavaScript, which keeps the payload small and removes a dependency to maintain.
Can I use Bootstrap’s JavaScript components?
Yes. Lumina’s own script runs the menus, tabs, accordions and
sliders, so Bootstrap’s JavaScript isn’t loaded by default
— that keeps every page lighter. The full bundle still ships in
assets/vendor/bootstrap.bundle.min.js. To use a modal,
dropdown, tooltip or any other Bootstrap component, load it before
the template script:
<script src="assets/vendor/bootstrap.bundle.min.js" defer></script>
Add that line to each page that needs it, or once to
src/partials/scripts.html if you rebuild with
node build.js — the line is already there,
commented out.
How do I remove an animation?
Delete the data-reveal attribute, or remove the
.lu-lines class from a heading. Page scrolling is
native; Lenis only animates in-page anchor links. To turn that off,
remove the Lenis <script> tag — the code checks
for it and falls back cleanly.
Can I use it with WordPress or another CMS?
The markup is plain HTML and easy to port into templates, but this package is a static template only. No theme files are included.
My contact form does nothing.
That is by design — see section 9. It validates and stops; you connect it to a backend.
15. Credits & licences
The design, layout, components and written content of this template are original. Placeholder artwork is generated from code, so no stock photography is included or required.
| Item | Licence | Used for | Source |
|---|---|---|---|
| Bootstrap 5.3.3 | MIT | Grid and layout utilities | getbootstrap.com |
| Swiper 11.2.10 | MIT | Testimonial slider | swiperjs.com |
| Lenis 1.1.18 | MIT | Smooth anchor-link scrolling | github.com/darkroomengineering/lenis |
| Lucide icons | ISC | The 45 inlined icons | lucide.dev |
| Syne | SIL OFL 1.1 | Display typeface | fonts.google.com/specimen/Syne |
| Inter | SIL OFL 1.1 | Body typeface | rsms.me/inter |
| JetBrains Mono | SIL OFL 1.1 | Labels and data | jetbrains.com/lp/mono |
Licence texts travel with the files: the libraries' in
assets/vendor/ (LICENSE and
LICENSE-lucide), the fonts' in
assets/fonts/LICENSE.txt, and all of them together in
licensing.txt at the top of the download. Each licence
permits redistribution inside a commercial product as long as the
notices travel with it, which they do in this package.
16. Verification
Every figure below was measured on this package, not estimated. You can reproduce all of it yourself.
W3C validation
All 24 pages pass the Nu Html Checker — the same engine behind validator.w3.org — with no errors and no warnings.
npx vnu-jar --format json *.html
# or upload any page to validator.w3.org
Lighthouse
Home page, mobile profile with throttling, measured against the
minified build (npm run build:min) — the median
of five runs:
| Category | Score |
|---|---|
| Performance | 96 |
| Accessibility | 100 |
| Best Practices | 100 |
| SEO | 100 |
| Metric | Value |
|---|---|
| First Contentful Paint | 1.7 s |
| Largest Contentful Paint | 2.6 s |
| Total Blocking Time | 7 ms |
| Cumulative Layout Shift | 0.004 |
npm run build:min
npm run serve
npx lighthouse http://localhost:3000/ --view
Where the remaining points go. The two libraries
ship as their full distributions, so upgrading either stays a file
swap: bootstrap.min.css is 227 KB (30 KB
over gzip) and swiper-bundle.min.js is 151 KB
(42 KB over gzip), and the template uses a fraction of each.
If you want the last few points, run PurgeCSS over the Bootstrap
file and build Swiper with only the modules you need.
Other checks
- No horizontal scroll at 320, 360, 390, 768, 1024 or 1440 px on any page, in either theme
- No JavaScript or console errors on any page — checked in Chrome, Firefox and Safari (WebKit), including with browser storage blocked, as inside the ThemeForest preview frame
- No broken links and no missing images
- Exactly one
<h1>per page, headings in order - No interactive element nested inside another
- Contrast meets WCAG AA in both themes: every run of text on every page was measured against the surface behind it, and none falls below 4.5:1 (3:1 for large text)
- Every control is at least 44 × 44 px at phone widths
17. Changelog
v1.0.0 — 27 September 2026
- First release.
- 24 pages sharing one design system, dark and light themes, and a
right-to-left stylesheet with
index-rtl.htmlas a working example. - Mega menu, search overlay, lightbox, filter bars, counters, tabs, accordions, sliders, cookie bar and four validated forms.
- 45 icons inlined as SVG at build time; fonts and libraries self-hosted, so the template makes no external request.
- Optional source layout with a static builder, an icon inliner and a minifier; minified CSS and JS included.
18. Support
Before you contact support, please check this documentation and the FAQ above — most questions are answered here.
What to include in a support request
- The page or component you are working on.
- What you changed, and what you expected to happen.
- A screenshot or a short description of the result.
- Your browser and operating system, if the problem looks like a rendering issue.
Contact
Use the Item Support tab on your ThemeForest purchase. We aim to reply within two working days.
Item support covers help with the template, bugs and small adjustments. It does not cover custom design work or third-party plugin configuration — those can be quoted separately.