Every landing page I built started the same way: nav, hero, feature cards, pricing, FAQ, footer, then a day of making it responsive. So I wrote an engine that does that part for me.
devtemDesign takes one config object and renders the whole page. No framework, no build step, no dependencies. Two tags and one function call.
This post is both an introduction and a reference for v0.1.0.
Try it in 30 seconds
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/devtemdesign@0.1.0/style.min.css">
<script src="https://cdn.jsdelivr.net/npm/devtemdesign@0.1.0/script.min.js"></script>
<div id="app-root"></div>
<script>
devtemDesign("app-root", {
template: "cyber",
logoName: "MYAPP",
heroTitleLine1: "Declarative UI,",
heroTitleLine2: "Zero-Config Motion",
heroSubtitle: "One config object. Complete animated landing.",
primaryBtnText: "Get started",
primaryBtnHref: "/docs"
});
</script>
That gives you a sticky glass navbar, an animated hero, ambient background orbs, reveal-on-scroll, a mobile drawer and a footer. Every section except the hero is optional, and a section only renders when its config key is present.
If you want to skip the code entirely, the builder generates a config from a description and lets you edit it live.
What's in 0.1.0
-
7 templates:
figsh,cyber,aurora,monochrome,sunset,ocean, andpaper(light). - Live theming: switch at runtime, add a built-in theme button, or override any design token.
- New sections: stats with count-up numbers, FAQ accordion, closing CTA, and pricing with a Monthly/Yearly toggle.
- Interactions: scroll progress bar, active nav link, card spotlight, seamless brand marquee, back-to-top button.
-
A real lifecycle:
devtemDesign()returns{ setTemplate, destroy }. -
Accessibility: skip link, focus rings,
aria-expandedon the menu, native<details>for FAQ, fullprefers-reduced-motionsupport.
A fuller example
const page = devtemDesign("app-root", {
template: "ocean",
themeSwitcher: true,
logoName: "ORBIT",
navLinks: [
{ label: "Features", href: "#features" },
{ label: "Pricing", href: "#pricing" },
{ label: "FAQ", href: "#faq" }
],
ctaText: "Start free",
ctaHref: "#pricing",
heroBadge: "New: team workspaces",
heroTitleLine1: "Ship analytics,",
heroTitleLine2: "not dashboards",
heroSubtitle: "Turn raw events into answers your whole team can read.",
primaryBtnText: "Start free",
primaryBtnHref: "#pricing",
stats: [
{ value: "12k+", label: "Teams" },
{ value: "99.9%", label: "Uptime" }
],
features: [
{ title: "Live metrics", desc: "Streams update in under a second.", icon: "⚡" },
{ title: "Shared boards", desc: "One link for the whole team.", icon: "📊" }
],
yearlyBadge: "Save 20%",
pricing: [
{ tier: "Starter", amount: "$0", features: ["3 boards"], cta: "Start free", href: "#" },
{ tier: "Team", amount: "$19", period: "/mo",
yearly: { amount: "$182", period: "/yr" },
featured: true, badge: "Most popular",
features: ["Unlimited boards", "Priority support"], cta: "Go Team", href: "#" }
],
faq: [{ q: "Is there a free plan?", a: "Yes. Starter is free forever." }],
cta: { title: "See your data differently", text: "Create account", href: "#" },
footer: { tagline: "Built with devtemDesign.", copy: "© 2026 Orbit" }
});
You can see this running, with a template picker and a mobile preview, on the examples page.
Templates
| Template | Vibe | Accents |
|---|---|---|
figsh |
Futuristic glass | Cyan, purple, pink |
cyber |
Neon cyberpunk | Cyan, magenta, red |
aurora |
Organic nature | Emerald, violet, blue |
monochrome |
Minimal grayscale | White, silver |
sunset |
Warm dusk | Orange, rose, gold |
ocean |
Cool depth | Sky, teal, indigo |
paper |
Light mode | Indigo, pink, cyan |
Every color in the stylesheet comes from a token, which is why the light template works without a separate stylesheet.
Runtime control
const page = devtemDesign("app-root", config);
page.setTemplate("sunset"); // switch theme, no re-render
page.destroy(); // remove listeners, observers and markup
devtemDesign.version; // "0.1.0"
devtemDesign.templates; // ["figsh", "cyber", "aurora", ...]
Calling devtemDesign() again on the same container destroys the previous instance first, so there are no leaked listeners.
Theme tokens
Override any token with the theme option:
theme: { accent: "#ff4d6d", accent2: "#ffb703", "bg-deep": "#0b0710" }
| Token | Purpose |
|---|---|
--accent, --accent2, --accent3
|
Brand colors and gradients |
--text, --muted
|
Primary and secondary text |
--bg-deep |
Page background, and text on filled buttons |
--glass-bg, --glass-border, --glass-blur
|
Card surfaces |
--surface, --line
|
Subtle fills and dividers |
--orb |
Ambient orb opacity |
Config reference
All text fields accept HTML, so icons and inline markup work. Only pass content you trust.
Core and navigation
| Option | Type | Default | Description |
|---|---|---|---|
template |
string | "figsh" |
One of the 7 templates |
theme |
object | {} |
Token overrides |
themeSwitcher |
boolean or string[] | false |
Navbar theme button. An array limits the cycle. Choice is saved in localStorage
|
cursor |
boolean | true |
Custom cursor on or off |
logo, logoName, logoHref
|
string | fscss logo, "CORE", "/"
|
Brand image, name and link |
navLinks |
(string or { label, href })[] |
Features, How it works, Pricing | Navbar and drawer links |
ctaText, ctaHref
|
string |
"Get started", "#"
|
Navbar button |
Hero
| Option | Description |
|---|---|
heroBadge |
Pill above the title |
heroTitleLine1, heroTitleLine2
|
H1 lines. The second has an animated gradient |
heroSubtitle |
Paragraph under the H1 |
primaryBtnText, primaryBtnHref
|
Main button |
secondaryBtnText, secondaryBtnHref
|
Ghost button, rendered only when text is set |
brands, brandsSubtitle
|
Hero chips and a seamless marquee that pauses on hover |
Sections
| Section | Keys | Anchor |
|---|---|---|
| Stats | stats: [{ value, label }] |
#stats |
| Features |
featuresLabel, featuresTitle, featuresSub, features: [{ title, desc, icon }]
|
#features |
| Steps |
stepsLabel, stepsTitle, stepsSub, steps: [{ title, desc }]
|
#how |
| Panels |
panelsLabel, panelsTitle, panelsSub, panels: [{ tag, title, desc }]
|
none |
| Pricing |
pricingLabel, pricingTitle, pricingSub, yearlyBadge, pricing: [...]
|
#pricing |
| FAQ |
faqLabel, faqTitle, faqSub, faq: [{ q, a }]
|
#faq |
| CTA | cta: { title, sub, text, href } |
none |
Stat values keep their prefix, suffix, commas and decimals, so "$1,200", "99.9%" and "12k+" all count up correctly.
A pricing plan looks like this. A Monthly/Yearly toggle appears automatically when any plan has yearly:
{
tier: "Pro", amount: "$12", period: "/mo", desc: "For product landings.",
features: ["Everything in Free", "Priority support"],
cta: "Go Pro", href: "/signup",
featured: true, badge: "Most popular",
yearly: { amount: "$115", period: "/yr" }
}
Footer
footer: {
tagline: "Declarative UI engine for the modern web.",
copy: "© 2026 devtem-dev",
columns: [{ heading: "Product", links: [{ label: "Docs", href: "/docs" }] }],
social: [{ icon: "fab fa-github", href: "https://github.com/devtem-dev", label: "GitHub" }],
legal: [{ label: "Privacy", href: "/privacy" }]
}
The full schema, with types and defaults, lives at design.devtem.org/api.
Upgrading from 0.0.2
Three option names changed to match what the code actually reads:
| 0.0.2 docs | Use now |
|---|---|
heroTag |
heroBadge |
featuresSectionLabel |
featuresLabel |
featuresSectionTitle |
featuresTitle |
Good to know
- Font Awesome is optional. It is only needed for
iconfields and footer social icons. The hamburger, checkmarks and accordions are pure CSS. - Theming uses
color-mix(), so you need Chrome 111+, Safari 16.2+ or Firefox 113+. Older browsers still render the page but lose some tinted surfaces. - Rendering replaces the container's contents. Give the engine its own wrapper element.
Links
- Docs: https://design.devtem.org/docs
- Schema reference: https://design.devtem.org/api
- Examples: https://design.devtem.org/examples
- Builder: https://design.devtem.org/builder
- npm: https://www.npmjs.com/package/devtemdesign
- GitHub: https://github.com/devtem-dev/devtemDesign
It is MIT licensed and open to contributions. If you build something with it, I'd like to see it. Drop a link in the comments, and tell me which section you want next
Top comments (0)