The photo is the page
The active environment photograph sits in a rounded card inset 20 px from every edge. Almost the entire viewport is the picture. When the visitor presses play, the inset collapses; when they go idle, even the wash on the photo drops to zero. Three states, one model.
Default state. `.background` inset 20 px, wash at 50 % black.
Playing. Inset collapses to 0 px, menus fade.
Playing + idle. Wash drops to 0, chrome fades. Pure imagery.
Dark imagery — wash + framing still works.
Dark page bezel (`#181818`) with bright imagery.
When the visitor stops moving
Press play and the frame collapses to full bleed. Then go idle for 2.5 s and even the wash drops to zero — the chrome fades, the photo plays alone. This is the brand's defining moment.
Default — entrance / mixer: 20 px inset, 50 % wash, chrome visible. · Playing — `.zen--playing`: photo full bleed, wash still 50 %. · Idle — `.zen--playing.idle--true`: wash 0 %, chrome opacity 0.
The 50 % black overlay
The wash lives on the photo, not on the page. It's the brand's sacrificial layer — without it, white text fights a sunlit frame. Default is 50 % black. The card-level wash on individual sound / environment cards is 30 %, relaxing to 10 % on hover.
The same wash level reads differently across photos — try cycling through bright/dark/saturated frames to feel why the 50 % default exists.
Two functional colours
Red for action. Green for on-state. White text on imagery. Solid white / #181818 as the page bezel. There are no other accent colours.
Brand red — in context
Red anchors the primary action against the photo — never a second red on the same surface.
Bergen Text · one family
Three weights — Regular 400, SemiBold 600, Bold 700. Fallback: Arial, Helvetica. No Inter, no Playfair, no monospace. Sentence case headings.
On imagery
The wordmark
Two-line lockup. "Create your" in regular, "zen" lowercase in bold, pushed -9 px left to align optically. line-height: 0.6 on purpose — the lines sit visually overlapping. The brand never renders "Zen" capitalised, anywhere.
On imagery — any mood
The wordmark reads white over every photo in the library, thanks to the 50 % wash baked into the framed-photo model.
App icon — logo on red
The PWA installed icon is the standalone "zen" wordmark on the brand red #f74847. The OS handles the corner mask via its superellipse — the source is a square red tile.
200 × 200 — marketing / OG
120 × 120 — iOS @2x
60 × 60 — favicon / spotlight
On the home screen
How the app actually lands when a visitor adds it to their home screen. The red square sits among any wallpaper; the OS rounds the corners.
Sound & environment cards
Each card is itself a photograph with a 30 % black wash on top. On hover, the wash relaxes to 10 % and the image scales to 1.05. Radius 20 px (10 px on small screens).
Sound rail
Environment rail (one selected)
The quote card
.background__quote — 20 px radius, 40 % black, 6 px blur. Bergen Text 700 quote, 400 attribution.
Small blurs, never heavy
Three glass patterns live in the product. They sit over imagery without breaking it — the blur is deliberately small (5 – 6 px). Heavy frosted treatments would lose the photograph.
Badge. rgba(0,0,0,0.10) + 6 px blur — the per-card volume readout.
Popover. rgba(0,0,0,0.50) + 5 px blur — settings, share, nav drawers.
Quote card. rgba(0,0,0,0.40) + 6 px blur, 20 px radius — the in-session quote.
Brand red, glass rail
The product owns its scrollbars. Two patterns: a horizontal rail on the mixer's sound + environment menus (powered by vue3-perfect-scrollbar, re-skinned to brand colours), and a 3 px reading-progress bar fixed to the top of every info page. The native OS scrollbar is a brand violation on the player surface.
Horizontal rail (mixer menus)
Brand-red thumb on a dark glass rail (rgba(0,0,0,0.50) + 5 px blur). Vertical rails are hidden entirely — the menus only scroll horizontally. Rail itself is invisible by default; it appears only on hover / focus / drag, so the photograph isn't interrupted by chrome the visitor isn't using.
Scroll horizontally or drag the rail directly. The thumb width reflects the visible-vs-total ratio; its position tracks scroll progress. The rail is hidden until hover. Mouse-drag-to-scroll on the inner row is added on top via JS on desktop (cursor flips to grab / grabbing); touch input on mobile uses the native scroller.
Reading-progress bar (info pages)
A different "scrollbar" — not a control, an indicator. 3 px tall, brand red, fixed to the top edge of the viewport, at z-index: 20000 (above everything). Width = scrollY / (docHeight − viewportHeight) × 100 %.
Drag the slider to see the 3 px bar grow as the visitor reads down the article. Class: .article__scrollbar.
The card responds to input
Sound and environment cards aren't just buttons — they absorb wheel, keyboard, and touch input to adjust volume, cycle imagery, and trigger play. The card itself is the control surface; there are no slider widgets next to it.
Try it
Focus this sound card (click or tab) then use the wheel or arrow keys. The volume % updates and the dark fill at the bottom rises with the level. Wheel bumps by ±1 per event — slow on purpose, matching the live product. On a touch device, tap to enter active mode then drag vertically.
Wave
40%Scroll up / down · ↑ ↓ · tap then drag on touch
Component: <ZenItem type="sound"> — every interaction lives on the card.
The gesture vocabulary
| Surface | Gesture | Result |
|---|---|---|
| Sound card | Wheel up / down | Volume +/− 1 per event (slow on purpose) |
| Sound card | ↑ ↓ (focused) | Volume +/− 5 |
| Sound card | Tap (mobile) | Card goes fullscreen — enters "active" mode |
| Sound card | Drag vertically (mobile, active) | Volume tracks finger Y directly — top = 100 %, bottom = 0 % |
| Sound card | Lift finger (mobile) | Exit active mode, return to grid |
| Sound card | Space / Enter | Toggle play |
| Environment card | Click / Space / Enter | Toggle selection (green tick) |
| Environment card | ← → (when slideshow off) | Cycle imagery within env |
| Environment card | On-screen arrow icons | Same — slideshow off only |
| Menu rail | Mouse drag (desktop) | Horizontal scroll, 3× delta, cursor → grabbing |
| Menu rail | Native touch (mobile) | Standard horizontal scroller |
| Master volume | Vertical range slider | Cascades into every active sound |
| Player | 2.5 s of no input (while playing) | Chrome fades, wash drops to 0 |
| Player | Any mouse / key / touch input | Idle dissolve cancels, chrome returns |
Why on the card, not next to it
The brand's chrome rule is "remove a UI element when in doubt" — see the do's & don'ts. A separate volume slider would be a permanent fixture next to every sound card, multiplying the chrome on the mixer surface. Putting the gesture on the card keeps the photograph dominant and the controls invisible until needed. The card body is the control.
Composed from the primitives
The entrance and the mixer, rebuilt here from the same components (<Button>, <Logo>, <ZenItemCard>, the framed photo) — no design-site shortcuts. This is what every section above adds up to.
Entrance
Relaxing background sounds, music and picturesque environments to help you be calm and focus.
Real environment photo + wordmark + two CTAs.
Mixer
Choose your sound(s)
Wave
40%Stream
0%Birds
0%Fireworks
20%Choose your environment(s)
Beach
Mountain
Space
Desert
The same wordmark + the photo-fill cards stacked into the brand grid.
The 60 / 20 rhythm
Two inset numbers carry the whole product. 60 px for outer chrome (nav, controls, footer). 20 px for the photo frame inset and the card radius. On small screens, 60 → 30 and 20 → 10.
Why these specific numbers?
These aren't multiples of 4 / 8 — they're what the product actually ships with. The design site preserves them verbatim from ../app/assets/scss/. If you find yourself reaching for 8 / 16 / 24 radii, you're reaching outside the brand.
Slow on purpose
Two brand-defining motions: backgroundGrow (a 1000-second scale-1-to-5-to-1 on the active photo — sixteen minutes per cycle) and the idle dissolve (after 2.5 s of no input while playing, chrome fades, wash drops, photo plays alone).
Short. Quiet. Specific.
Sentence case. No exclamation marks. The imagery and the audio do the emoting — the copy doesn't have to.
Write this
- Create your Zen
- Make a zen for me
- Choose your sound(s)
- Share your Zen
- Saved.
Not this
- Get Started!
- Build a random environment for me
- Available audio tracks
- Share this page
- Your changes have been saved successfully.