The Create Your Zen design system

Every brand primitive,
mirrored from the product.

This site documents the visual language of createyourzen.app — the framed-photo surfaces, Bergen Text, brand red and green, the 60 / 20 rhythm. Bespoke SCSS — no Nuxt UI, no Tailwind, no Google Fonts. Static-only.

#f74847 · brand red#19b483 · on-state#ffffff · paper#181818 · dark paperBergen Text · sans20 px frame · 60 px chrome
v0.3 · 2026 · mirrors ../DESIGN.mdStatic build · no server · nuxt generate
01 — Surfaces

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 — 20 px frame, 50 % wash, chrome over the photo.

Default state. `.background` inset 20 px, wash at 50 % black.

.zen--playing

Photo full bleed; cards faded.

Playing. Inset collapses to 0 px, menus fade.

Pure photo, no chrome.

Playing + idle. Wash drops to 0, chrome fades. Pure imagery.

Dark imagery — wash + framing still works.

Dark page bezel (`#181818`) with bright imagery.

01b — Idle dissolve

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.

02 — Wash

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.

Choose your sound(s)

The wash carries the contrast

Even on this photograph, the white text holds because of the wash above it. Toggle it off and watch the words disappear into the highlights.

The same wash level reads differently across photos — try cycling through bright/dark/saturated frames to feel why the 50 % default exists.

03 — Colour

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 · action#f74847
Brand green · on-state#19b483
Paper · light bezel#ffffff
Paper · dark bezel#181818

Brand red — in context

Red anchors the primary action against the photo — never a second red on the same surface.

04 — Typography

Bergen Text · one family

Three weights — Regular 400, SemiBold 600, Bold 700. Fallback: Arial, Helvetica. No Inter, no Playfair, no monospace. Sentence case headings.

The quick brown fox
Choose your sound(s)
Sub-heading at 600 weight
Body copy at 16 px, weight 400. Bergen Text reads warm and quiet — the brand voice on the page. Layer your sound. Pick your environment. Sit with it.
12 px caption / volume %

On imagery

Choose your sound(s)

Bergen Text reads
warm on photo.

The 50 % wash carries the contrast. No text-shadow needed — the wash is the brand's sacrificial layer.

06 — Buttons

Rounded rectangles — not pills

border-radius: 10 px, padding 15 px 25 px, min-height 40 px, weight 700. Variants: red, white, green, blurred. The brand button is almost always seen on imagery.

On imagery — the canonical case

Entrance pattern — red solid + blurred-glass secondary.

Settings-drawer pattern — green for on-state, blurred for cancel.

The play button

Special variant — the master play / pause on the mixer. A circular bordered icon button, brand red fill, 3 px white border. Not a `.zen__button`.

.control__playpause — 100 % radius, 3 px white border, brand red fill, white play / pause glyph.

Connected button group

Slideshow interval

.zen__buttons.buttons--connected — outer corners 10 px, inner corners 0.

On paper (rare)

The portal CMS and the info pages use the same button on a white surface — same red, same radius, same weight.

07 — Cards

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

Choose your sound(s)

Wave

40%

Birds

0%

Fireworks

20%

Stream

0%

Environment rail (one selected)

Choose your environment(s)

Underwater

Space

Mountain

Desert

The quote card

I believe that we are fundamentally the same and have the same basic potential.
Dalai Lama

.background__quote — 20 px radius, 40 % black, 6 px blur. Bergen Text 700 quote, 400 attribution.

07b — Glass

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.

40 %

Badge. rgba(0,0,0,0.10) + 6 px blur — the per-card volume readout.

SettingsSlideshow interval — 1 minQuote interval — 30 s

Popover. rgba(0,0,0,0.50) + 5 px blur — settings, share, nav drawers.

Sit with it.— the brand

Quote card. rgba(0,0,0,0.40) + 6 px blur, 20 px radius — the in-session quote.

07c — Scrollbars

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.

Choose your sound(s) — scroll the rail →

Wave

40%

Birds

0%

Fireworks

20%

Stream

0%

Ship

0%

Office

0%

Cafe

0%

Underwater

0%

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 %.

Scroll progress34%

Drag the slider to see the 3 px bar grow as the visitor reads down the article. Class: .article__scrollbar.

07d — Gestures

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

SurfaceGestureResult
Sound cardWheel up / downVolume +/− 1 per event (slow on purpose)
Sound card↑ ↓ (focused)Volume +/− 5
Sound cardTap (mobile)Card goes fullscreen — enters "active" mode
Sound cardDrag vertically (mobile, active)Volume tracks finger Y directly — top = 100 %, bottom = 0 %
Sound cardLift finger (mobile)Exit active mode, return to grid
Sound cardSpace / EnterToggle play
Environment cardClick / Space / EnterToggle selection (green tick)
Environment card← → (when slideshow off)Cycle imagery within env
Environment cardOn-screen arrow iconsSame — slideshow off only
Menu railMouse drag (desktop)Horizontal scroll, 3× delta, cursor → grabbing
Menu railNative touch (mobile)Standard horizontal scroller
Master volumeVertical range sliderCascades into every active sound
Player2.5 s of no input (while playing)Chrome fades, wash drops to 0
PlayerAny mouse / key / touch inputIdle 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.

/* Inside <ZenItem> */ @wheel="volumeChange($event)" @keyup.up="volumeKey('up')" @keyup.down="volumeKey('down')" @keyup.space="itemClicked" @keyup.enter="itemClicked" @keyup.right="imageChange('right')" @keyup.left="imageChange('left')" @mousedown="itemClicked" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"
08 — Mockups

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.

09 — Layout

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.

--zen-inset-chrome: 60px; /* 30 px on mobile */ --zen-inset-frame: 20px; /* 10 px on mobile */ --zen-radius-background: 20px; --zen-radius-card: 20px; --zen-radius-button: 10px; --zen-radius-glass: 12px; --zen-radius-popover: 7px; --zen-radius-tick: 100%; --zen-radius-page-card: 25px;

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.

10 — Motion

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).

@keyframes backgroundGrow { 0% { transform: scale(1); } 50% { transform: scale(5); } 100% { transform: scale(1); } } .background.run--animation .background__image { animation: backgroundGrow 1000s ease; }
11 — Voice

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.