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
- 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.
- 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).
- 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.
- 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.
- 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.
- 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
| Role | Family | Notes |
|---|---|---|
| Display (h1, h2) | Instrument Serif | 400 weight, fluid \clamp()\, italic + gradient-text for emphasis |
| Body | Inter | 400 / 500, 14–17px, line-height 1.55 |
| Labels, stats, mono | JetBrains Mono | 400 / 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)
- Floating nav — pill-shaped glass bar, logo + 5 links + 2 CTAs.
- 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).
- Marquee — quiet hairlined band of 8 customer marks, infinite scroll.
- 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.
- Spotlight cards — 3-column row, each with a serif headline, body,
and a hairline-divided stat in the bottom slot. Glow follows cursor.
- Pull-quote — large centered glass card on a soft secondary mesh wash.
- Stats strip — 4-column glass strip; numbers use gradient-text.
- Comparison table — full-width glass table with a highlighted "Prism"
column carrying a pill ribbon. Collapses to stacked cards under 720px.
- Pricing — 3 plans, middle one "feat" with a violet ribbon and a
raised inner gradient. Yearly toggle gives 20% off.
- CTA — large glass card with its own internal mesh + grain wash.
- Footer — single-line copyright + 5 quiet links.
6. Responsive strategy
| Breakpoint | What changes |
|---|---|
| ≤1080px | Bento collapses to a 4-column grid; hero mock stacks. |
| ≤900px | Nav links + ghost button hide; spotlight grid → 1 column; pricing → 1 column (max-width 460px, centered); stats → 2×2; section padding → 88px. |
| ≤720px | Bento → 1 column; comparison table swaps to a list of 3 stacked cards; hero-mock padding tightens. |
| ≤640px | Container padding 18px; hero h1 collapses; pricing price reduces; footer stacks. |
| ≤380px | Hard 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
- Drop in the design tokens (color + glass + mesh palette).
- Layer the page-mesh and grain as fixed siblings before any content.
These are the canvas — every other surface is glass over them.
- **Build \
.glass\, \Spotlight\, \Segmented\, \.marquee-wrap\** in
isolation. Once these four primitives look right, the page is just composition.
- Lock the headline treatment first. Get Instrument Serif loaded,
confirm the gradient-text italic clips correctly in WebKit + Firefox, then build outward.
- Wire the IntersectionObserver hook before adding sections — every
section author then just adds \data-reveal\ to the block they want to fade in.
- Compose top to bottom, hero first, CTA last. Don't tune sections in
isolation — the mesh moves the eye between them.
- 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.