Back to templates
Template · SaaS · Glass · Mesh Gradient

Prism.

A deep-midnight SaaS landing page layered with a slow-drifting six-stop mesh gradient and glass surfaces throughout. Editorial Instrument Serif headlines with gradient-text italics, a floating glass nav, an asymmetric bento, three cursor-following spotlight cards, an auto-scroll marquee, a comparison table that gracefully collapses to cards on mobile, a per-seat pricing trio with a billing toggle, and quiet IntersectionObserver-driven reveals. Built mobile-first with explicit breakpoints at 1080, 900, 720, 640, and 380px.

View code
01 · Preview

The live page.

Prism 4 · Studio is live

The workspace where ambitious teams render reality faster than they can describe it.

Prism is a creative operations platform that turns scattered briefs, assets, and approvals into one calm, glassy canvas. Ship campaigns in days, not quarters.

14-day trialNo card requiredSSO & SOC 2
prism.studio / autumn-launchv4.2.1
Pipeline velocity
+218%
14d rolling · vs Q3
Active streams
autumn-launch · live12s
brand-refresh · review2m
enterprise-deck · draft14m
keynote-reel · queued1h
NorthwindHelio LabsMercer & ValeTesseraQuill & CoLoomis StudioAtrium AIHalcyonNorthwindHelio LabsMercer & ValeTesseraQuill & CoLoomis StudioAtrium AIHalcyon

The platform

One canvas for the entire creative pipeline.

Studio
on tap.

An infinite canvas for moodboards, storyboards and live edits — synced to every brief in real time.

RealtimeVersionedComment-aware

Render farm, demystified.

Distributed renders that finish before your coffee. Per-frame caching, smart prioritisation, no queues.

SOC 2 + SSO baked in.

SAMLSCIMAudit logEU resident

26 regions, one URL.

Edge-rendered previews from Sydney to São Paulo. Reviewers see frames, not loaders.

Stack-aware.

Drops into Figma, After Effects, Notion and Linear without a single zap.

Approvals in one click.

Stakeholders sign off from email, Slack or DM. No new accounts.

Why teams switch

Numbers your CFO will actually read.

Hover any card — the spotlight follows. The same calm restraint shows up everywhere in Prism.

From quarter to quarter-hour.

Compress the brief → first cut loop from days to minutes. Prism stitches references, voice notes, and feedback into a single timeline.

14×faster first
cut delivery

Reviews that arrive on time.

Stakeholders annotate frames where they live — Slack, email, a phone in a cab. Approval times collapse without a single nudge.

62%drop in approval
latency

Headcount you don’t backfill.

One Prism workspace replaces the project manager spreadsheet, the file-handoff Slack channel, and the “final-final-v9” folder.

$48kaverage annual
tooling savings
We replaced six tools with Prism in a fortnight. Our creative team hasn’t opened the project tracker since — and somehow the campaigns ship twice as fast.
Maren Holloway · VP Brand · Loomis Studio
4,200+
creative teams
on Prism today
99.99%
render uptime
over 18 months
12 min
median brief
→ first cut
26
edge regions,
one workspace

The honest comparison

What you stop paying for the day you switch.

Side-by-side with the stack most teams arrive with. We left out every feature where we’re still catching up.

CapabilityPrismLegacy DAMSpreadsheets + Slack
Live multiplayer canvas Built-in Add-on No
Approvals from email & Slack One click Email only Manual
Distributed render farm Included Separate vendor Local only
SSO, SCIM, audit log Every plan Enterprise tier No
EU + US data residency Both US only Unmanaged
Per-seat price (50 seats)$24 / seat$58 / seat + add-onsHidden in headcount
Prism Recommended
Live canvasBuilt-in
ApprovalsOne click
Render farmIncluded
SSO + SCIMEvery plan
Per seat$24 / seat
Legacy DAM
Live canvasAdd-on
ApprovalsEmail only
Render farmSeparate vendor
SSO + SCIMEnterprise tier
Per seat$58 / seat
Sheets + Slack
Live canvas
ApprovalsManual
Render farmLocal only
SSO + SCIM
Per seatHidden

Pricing

Simple, per-seat, no traps.

Starter

Studio

For small teams getting their first calm pipeline up.

$10/seat / mo
  • Up to 10 seats
  • 200 GB asset storage
  • Realtime canvas
  • Slack & email approvals
  • Community support
Most popularGrowth

Studio Pro

For brand & creative teams shipping campaigns every week.

$19/seat / mo
  • Unlimited seats
  • 2 TB storage · render farm included
  • SSO + SCIM
  • Audit log & permissions
  • Priority support · 4h SLA
Enterprise

Atelier

For agencies and global brands with bespoke compliance needs.

Custom
  • Dedicated render region
  • EU / US data residency
  • Custom DPA + redlines
  • Named CSM · 1h SLA
  • White-glove onboarding

Render reality, this afternoon.

14-day trial. Every feature unlocked. No credit card. Bring your team — invitations are free.

02 · Source

The code.

tsx
1import { useEffect, useRef, useState } from "react";
2import {
3 ArrowRight, ArrowUpRight, Check, Minus, Sparkles, Zap, Shield,
4 Globe, Layers, Wand2, Cpu, Compass, Quote,
5} from "lucide-react";
6import "./prism.css"; // see styles tab
7
8// IntersectionObserver hook — toggles .in on every [data-reveal] element.
9const useReveal = () => {
10 useEffect(() => {
11 const els = document.querySelectorAll(".prism [data-reveal]");
12 const io = new IntersectionObserver((entries) => {
13 entries.forEach((e) => {
14 if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); }
15 });
16 }, { threshold: 0.12, rootMargin: "0px 0px -10% 0px" });
17 els.forEach((el) => io.observe(el));
18 return () => io.disconnect();
19 }, []);
20};
21
22// Spotlight card — radial glow follows the cursor via CSS variables.
23const Spotlight = ({ variant, children }) => {
24 const ref = useRef(null);
25 const onMove = (e) => {
26 const r = ref.current.getBoundingClientRect();
27 ref.current.style.setProperty("--mx", \`\${e.clientX - r.left}px\`);
28 ref.current.style.setProperty("--my", \`\${e.clientY - r.top}px\`);
29 };
30 return <div ref={ref} onMouseMove={onMove} className={\`spot \${variant ?? ""}\`}>{children}</div>;
31};
32
33const Segmented = ({ options, active, onChange }) => (
34 <div className="segctl" role="tablist">
35 {options.map((o, i) => (
36 <button key={o} className={\`seg \${i === active ? "on" : ""}\`} onClick={() => onChange?.(i)}>{o}</button>
37 ))}
38 </div>
39);
40
41export default function PrismPage() {
42 useReveal();
43 const [billing, setBilling] = useState(1);
44 const yearly = billing === 1;
45 const planPrice = (m) => yearly ? Math.round(m * 0.8) : m;
46
47 return (
48 <div className="prism">
49 {/* Drifting mesh gradient + grain layered behind everything */}
50 <div className="page-mesh" aria-hidden />
51 <div className="grain" aria-hidden />
52
53 <header className="nav">
54 <div className="nav-inner">
55 <a className="brand" href="#"><span className="brand-mark" />Prism</a>
56 <nav className="nav-links">
57 <a href="#">Platform</a><a href="#">Studio</a><a href="#">Pricing</a>
58 <a href="#">Customers</a><a href="#">Docs</a>
59 </nav>
60 <div className="nav-cta">
61 <button className="btn btn-ghost">Sign in</button>
62 <button className="btn btn-glass">Get started</button>
63 </div>
64 </div>
65 </header>
66
67 {/* HERO ─ glass mock pinned below the headline */}
68 <section className="hero">
69 <div className="wrap">
70 <span className="hero-eyebrow"><span className="pip" />Prism 4 · Studio is live</span>
71 <h1 className="h1">
72 The workspace where ambitious teams <em>render reality</em> faster
73 than they can describe it.
74 </h1>
75 <p className="lede">Prism is a creative operations platform that turns scattered briefs, assets, and approvals into one calm, glassy canvas.</p>
76 <div className="hero-cta">
77 <button className="btn btn-solid btn-lg">Start free <ArrowRight size={16} /></button>
78 <button className="btn btn-glass btn-lg">Watch the tour <ArrowUpRight size={15} /></button>
79 </div>
80
81 <div className="hero-mock glass">
82 {/* … see live preview for full markup … */}
83 </div>
84 </div>
85 </section>
86
87 {/* MARQUEE — auto-scroll customer logos with edge fade */}
88 <div className="marquee-wrap">
89 <div className="marquee">
90 {/* repeat the array twice for seamless loop */}
91 </div>
92 </div>
93
94 {/* BENTO — 6-cell asymmetric grid with a segmented control header */}
95 <section className="section">
96 <div className="wrap">
97 <div className="section-head">
98 <h2 className="h2">One canvas for the <em>entire</em> creative pipeline.</h2>
99 <Segmented options={["Plan", "Build", "Ship"]} active={0} onChange={() => {}} />
100 </div>
101 <div className="bento">
102 {/* tall · wide · med · med · small · small */}
103 </div>
104 </div>
105 </section>
106
107 {/* SPOTLIGHT CARDS — three variants with cursor-following glow */}
108 <section className="section">
109 <div className="wrap">
110 <div className="spotlight-grid">
111 <Spotlight variant="warm">{/* … */}</Spotlight>
112 <Spotlight variant="cool">{/* … */}</Spotlight>
113 <Spotlight variant="amber">{/* … */}</Spotlight>
114 </div>
115 </div>
116 </section>
117
118 {/* QUOTE — glass card on a soft mesh wash */}
119 <section className="section">
120 <div className="wrap">
121 <div className="quote">
122 <blockquote>We replaced six tools with Prism in a fortnight…</blockquote>
123 </div>
124 </div>
125 </section>
126
127 {/* STATS — 4-column glass strip */}
128 <section className="section">
129 <div className="wrap">
130 <div className="stats">{/* 4 stat-cells */}</div>
131 </div>
132 </section>
133
134 {/* COMPARISON TABLE — desktop table + mobile card fallback */}
135 <section className="section">
136 <div className="wrap">
137 <div className="table-wrap"><table className="ptable">{/* … */}</table></div>
138 <div className="table-cards">{/* mobile fallback */}</div>
139 </div>
140 </section>
141
142 {/* PRICING — 3 plans, middle one feat with a ribbon, segmented billing toggle */}
143 <section className="section">
144 <div className="wrap">
145 <div className="pricing-head">
146 <h2 className="h2">Simple, <em>per-seat</em>, no traps.</h2>
147 <Segmented options={["Monthly", "Yearly · −20%"]} active={billing} onChange={setBilling} />
148 </div>
149 <div className="pricing-grid">
150 <div className="plan">{/* Studio · $\${planPrice(12)} */}</div>
151 <div className="plan feat"><span className="ribbon">Most popular</span>{/* Studio Pro · $\${planPrice(24)} */}</div>
152 <div className="plan">{/* Atelier · Custom */}</div>
153 </div>
154 </div>
155 </section>
156
157 {/* CTA — glass card with its own mesh wash */}
158 <section className="section">
159 <div className="wrap">
160 <div className="cta-card">
161 <h2 className="h2">Render reality, <em>this afternoon</em>.</h2>
162 </div>
163 </div>
164 </section>
165
166 <footer className="footer">© 2026 Prism Labs</footer>
167 </div>
168 );
169}
170
03 · Design brief

How to rebuild it.

More than the code: the design tokens, primitives, layout rhythm and step-by-step playbook so you can apply this system to your own product — not just paste the file.

Prism — Design Brief

A multi-stop mesh-gradient SaaS landing page on a deep midnight canvas, layered with glassmorphism surfaces. Editorial display serif, restrained mono labels, scroll-triggered reveals, and a quiet film grain to keep the gradients from reading as cheap.


1. Design principles

  1. One drifting mesh, behind everything. A single \fixed\ mesh layer

(\.page-mesh\) sits at \z-index: 0\ and slowly oscillates over 24s. Every section inherits its glow — no per-section gradients, no random hue introductions.

  1. Glass is the surface, not the decoration. Cards, the nav pill, the

pricing tiers, the comparison table — all use the same \.glass\ primitive (14% white fill, 14% white hairline, 24px backdrop-blur, inset highlight on the top edge).

  1. Editorial serif × clinical mono. Instrument Serif (italic for

emphasis) carries every headline. JetBrains Mono carries every label, stat caption and chip. Inter handles all body copy.

  1. Multi-stop gradients only. The mesh palette has six stops

(\rose · peach · violet · indigo · cyan · amber\). Two-stop ramps are forbidden — they read as "vibecode". The italic accent in headlines reuses 4 of those stops so the typography belongs to the system.

  1. Grain is non-negotiable. A 0.18-opacity SVG fractal noise layer in

\overlay\ blend mode quiets the gradient. Without it the page looks muddy on OLED screens.

  1. Scroll-reveal, not scroll-jacking. A native IntersectionObserver

adds \.in\ to each \[data-reveal]\ block for a soft 18px rise + fade. No animation libraries, no parallax, nothing the user has to wait for.


2. Color tokens

\\\`css --bg: #07070d; / page / --bg-2: #0c0c16; --ink: #f5f3ff; / primary text / --ink-soft: rgba(245,243,255,0.72); --ink-mute: rgba(245,243,255,0.46); --line: rgba(245,243,255,0.08); --line-2: rgba(245,243,255,0.16);

/ mesh palette — six stops, never used in pairs of two / --m-rose: #ff8fb1; --m-peach: #ffd1a6; --m-violet: #b89cff; --m-indigo: #6e7dff; --m-cyan: #8ee0ff; --m-amber: #ffb37a;

/ glass tokens / --glass-fill: rgba(255,255,255,0.06); --glass-stroke: rgba(255,255,255,0.14); --glass-blur: 24px; \\\`

Rule: never introduce a 7th hue. Tonal variation comes from glass-fill opacity (\0.06 → 0.10 → 0.14\), not new colors.


3. Type system

RoleFamilyNotes
Display (h1, h2)Instrument Serif400 weight, fluid \clamp()\, italic + gradient-text for emphasis
BodyInter400 / 500, 14–17px, line-height 1.55
Labels, stats, monoJetBrains Mono400 / 500, 11–13px, +0.10–0.18em letter-spacing on uppercase

Italic emphasis in headlines uses gradient-text: \\\css .h1 em { font-style: italic; background: linear-gradient(95deg, var(--m-peach), var(--m-rose) 35%, var(--m-violet) 70%, var(--m-cyan)); -webkit-background-clip: text; color: transparent; } \\\


4. Reusable primitives

4.1 \.page-mesh\ + \.grain\

Two fixed full-viewport layers that sit at \z-index: 0\ and \1\. Every section uses \.wrap\ at \z-index: 2\ to stack above them.

4.2 \.glass\ / \.glass-2\

The single surface treatment used across cards, nav, pricing, table. Two opacities only.

4.3 \Spotlight\

A card whose radial glow follows the cursor. The glow color comes from a \variant\ prop (\warm | cool | amber | default\) that swaps the \background\ of the \::before\ pseudo-element. Implementation: a React ref + \onMouseMove\ writes \--mx\ and \--my\ CSS variables on the host.

4.4 \Segmented\

A pill with rounded glass background and a white "on" thumb. Used three times in the page (feature filter, pricing cadence, mock toolbar).

4.5 \.marquee-wrap\

A horizontal auto-scroller with 120px linear-gradient masks on both edges so logos fade in/out. The track contains the items twice and translates \-50%\ over 38s for a seamless loop.


5. Section anatomy (top to bottom)

  1. Floating nav — pill-shaped glass bar, logo + 5 links + 2 CTAs.
  2. Hero — eyebrow + serif H1 (with gradient-text italic) + lede + 2

CTAs + meta strip + a glass "studio" mock with two tiles (a serif stat with a sparkline; a pipeline list).

  1. Marquee — quiet hairlined band of 8 customer marks, infinite scroll.
  2. Bento — segmented control header + 6-cell asymmetric grid

(\tall · wide · med · med · small · small\). Tall cell carries an orb visual; wide cell carries a bar chart.

  1. Spotlight cards — 3-column row, each with a serif headline, body,

and a hairline-divided stat in the bottom slot. Glow follows cursor.

  1. Pull-quote — large centered glass card on a soft secondary mesh wash.
  2. Stats strip — 4-column glass strip; numbers use gradient-text.
  3. Comparison table — full-width glass table with a highlighted "Prism"

column carrying a pill ribbon. Collapses to stacked cards under 720px.

  1. Pricing — 3 plans, middle one "feat" with a violet ribbon and a

raised inner gradient. Yearly toggle gives 20% off.

  1. CTA — large glass card with its own internal mesh + grain wash.
  2. Footer — single-line copyright + 5 quiet links.

6. Responsive strategy

BreakpointWhat changes
≤1080pxBento collapses to a 4-column grid; hero mock stacks.
≤900pxNav links + ghost button hide; spotlight grid → 1 column; pricing → 1 column (max-width 460px, centered); stats → 2×2; section padding → 88px.
≤720pxBento → 1 column; comparison table swaps to a list of 3 stacked cards; hero-mock padding tightens.
≤640pxContainer padding 18px; hero h1 collapses; pricing price reduces; footer stacks.
≤380pxHard cap on h1 at 2.2rem so it never overflows on 320px viewports.

Non-negotiables:

  • Every glass surface must keep a visible 1px hairline at 14% white or

brighter — anything below disappears on OLED.

  • The marquee-wrap's edge fades must always reference \--bg\ exactly so

there is no visible seam between the fade and the page.

  • \text-wrap: balance\ on every \.h1\ and \.h2\ so headlines never

break a single word vertically.


7. How to rebuild this from scratch

  1. Drop in the design tokens (color + glass + mesh palette).
  2. Layer the page-mesh and grain as fixed siblings before any content.

These are the canvas — every other surface is glass over them.

  1. **Build \.glass\, \Spotlight\, \Segmented\, \.marquee-wrap\** in

isolation. Once these four primitives look right, the page is just composition.

  1. Lock the headline treatment first. Get Instrument Serif loaded,

confirm the gradient-text italic clips correctly in WebKit + Firefox, then build outward.

  1. Wire the IntersectionObserver hook before adding sections — every

section author then just adds \data-reveal\ to the block they want to fade in.

  1. Compose top to bottom, hero first, CTA last. Don't tune sections in

isolation — the mesh moves the eye between them.

  1. Test at 375px and 320px before shipping. The bento → 1-column,

table → cards swap, and pricing stack are non-negotiable.


8. Common pitfalls

  • ❌ Adding a 2-color gradient (peach → pink, purple → blue). Always pull

at least 4 stops from the mesh palette.

  • ❌ Removing the grain "to clean up" the gradient. The grain is what

rescues it from looking AI-generated.

  • ❌ Increasing \--glass-blur\ past 28px. It starts smearing the mesh

into a flat haze.

  • ❌ Using shadows instead of inset highlights for depth. Glass surfaces

get \inset 0 1px 0 rgba(255,255,255,0.08)\ on the top edge — that's the refraction trick. Outer shadows stay extremely soft (40–80px blur, 60% negative spread).

  • ❌ Placing content directly on \.page-mesh\. Always wrap in \.wrap\

so the z-index stack is preserved and text contrast stays readable.

04 · Keep browsing

Try another template.