Brand specification

Void Ember

RipFeed's confirmed visual identity. Near-black purple backgrounds, amber waveforms, gold usernames. Every decision in this document is locked — use it as the single source of truth for all product, marketing, and web surfaces.

Primary theme: Void Ember Trending signal: Purple Space Grotesk + DM Mono Icon: Inverted amber (primary) Backup: Night Bloom

All decisions at a glance

DecisionChoiceNotes
Theme nameVoid EmberNear-black purple bg, amber waveform accent. Confirmed primary.
Background#0F0E11 / #1A1820Base bg / card surface. Two stops only.
Waveform color#D4813A amberReplaces old #7F77DD in FFmpeg filter. See FFmpeg section.
Username / display#F0C27A goldPrimary identity color — usernames, wordmark, active nav.
Primary text#F5F2FFHeadings, labels, nav items.
Clip title / body#9a94aa ashClip titles, body copy, muted readable text.
Muted text#6a6474 duskCaptions, secondary muted copy.
Trending accent#7F77DD purpleSignal-only — trending badge, left border on trending cards. Used nowhere else.
Display fontSpace Grotesk 700Wordmark, large headings, usernames. ✓ Confirmed.
UI fontSpace Grotesk 400/500Body copy, captions, nav labels, card titles. ✓ Confirmed.
Utility fontDM Mono 400/500Tags, timestamps, play counts, badges, all numeric/data UI. ✓ Confirmed.
App icon (primary)Inverted — amber bg, dark bars✓ Confirmed. Pops on both light and dark home screens.
App icon (fallback)Waveform — dark bg, amber barsUse where amber bg clashes with wallpaper.
Corner radius (cards)14pxClip cards, ad cards, modals.
Corner radius (pills)20px (full pill)Tags, trending badge, nav pills.
Border weight0.5pxAll borders. Never 1px.
Backup themeNight BloomVoid bg + lime green waveform + gold trending. Held in reserve.
Color system

Palette

Ten named values. Every UI element maps to one of these. No off-palette hex values — ever.

Void
#0F0E11
Base background
Surface
#1A1820
Card background
Lift
#2e2b38
Borders, avatars, ghost buttons
Ember
#D4813A
Waveform, play btn, record, tags
Gold
#F0C27A
Usernames, wordmark, active nav
Signal
#7F77DD
Trending only — nowhere else
Frost
#F5F2FF
Primary text, headings
Ash
#9a94aa
Clip titles, body copy, muted readable
Dusk
#6a6474
Captions, secondary muted copy
Dim
#4a4555
Timestamps, counts, labels

Color usage rules

01
Ember (#D4813A) is the primary action color. Play buttons, record button, active waveform, progress overlay, tags, upload CTA pill.
02
Gold (#F0C27A) identifies people. Usernames, wordmark, profile highlights, active nav. Never used for actions.
03
Signal (#7F77DD) means trending — and only trending. Left border on trending cards, TRENDING badge. If used anywhere else, it dilutes the signal.
04
Ad cards use a cooler dark tint (#15151e bg, #252333 border) — differentiates without a hard break.
05
All borders are 0.5px. No exceptions.
Typography

Type system — ✓ confirmed

Two families. Space Grotesk for everything human-readable; DM Mono for everything that is data — timestamps, counts, tags, badges. Never mix within a single semantic element.

Wordmark / display
Space Grotesk 700 · −0.03em · #F0C27A
RipFeed
Screen heading
Space Grotesk 700 · 22px · −0.03em · #F5F2FF
Following feed
Username
Space Grotesk 500 · 13–14px · #F0C27A
@ripmaster99
Clip title / caption
Space Grotesk 400 · 13px · #9a94aa ash
elevator rip, full crowd witness 💀
Tags / timestamps
DM Mono 400 · 10–11px · #D4813A or #4a4555
#flatsound 2 min ago · 0:11 ▶ 4.2k
Badges
DM Mono 500 · 8–10px · uppercase · 0.05em spacing
TRENDING PRO SPONSORED

Google Fonts import

<!-- Add to index.html or _document.tsx -->
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
App icon

Icon system — ✓ confirmed

Primary: inverted palette — amber background, dark waveform bars. Pops on both light and dark home screens. Fallback: dark bg with amber bars for contexts where amber clashes.

Primary — Inverted (amber bg) ✓

96px
60px
40px
20px

Fallback — Waveform (dark bg)

96px
60px
40px
20px

Icon SVG — primary (1024×1024 for App Store)

<!-- Primary icon — amber bg, dark bars -->
<svg width="1024" height="1024" viewBox="0 0 96 96">
  <rect width="96" height="96" rx="22" fill="#D4813A"/>
  <g fill="#0F0E11">
    <rect x="18" y="34" width="6" height="28" rx="3"/>
    <rect x="28" y="26" width="6" height="44" rx="3"/>
    <rect x="38" y="38" width="6" height="20" rx="3"/>
    <rect x="48" y="18" width="6" height="60" rx="3"/>
    <rect x="58" y="30" width="6" height="36" rx="3"/>
    <rect x="68" y="38" width="6" height="20" rx="3"/>
    <rect x="78" y="26" width="6" height="44" rx="3"/>
  </g>
</svg>
Feed UI

Clip card anatomy

Every card follows the same structure. Playing state, trending state, and ad cards are variants — not separate components. Implemented as a FlatList in React Native with ad injection every 8–10 organic cards (free users only).

Card anatomy

Top
Avatar · username (Space Grotesk 500, gold) · timestamp + duration (DM Mono, dim) · optional TRENDING or PRO badge right-aligned.
Title
Space Grotesk 400, 13px, ash (#9a94aa). 1–2 lines max.
Waveform
32px tall in app, 64px on web player. Played = full opacity. Unplayed = 22% opacity. Progress: amber semi-transparent overlay + 1.5px right border line at playhead.
Bottom
Play/pause button (left). Stats row (DM Mono, dim). Tags row below when present.

Card states

Default: #1A1820 bg, #252333 border. Ghost play button (dark bg, amber arrow icon).
Playing: Amber fill play button. Waveform split: played bars at full opacity, unplayed at 22%. Amber progress line marks playhead.
Trending: border-left: 2px solid #7F77DD. TRENDING badge in DM Mono, purple bg. Purple is used nowhere else in the feed.
Ad: #15151e bg, cooler #252333 border. SPONSORED label in near-invisible DM Mono 8px. Every 8–10 organic cards, free users only.
Pro user: No ad cards rendered. usePro() hook returns true → skip ad entirely. No placeholder, no gap.

Bottom navigation

5 items: Feed · Profile · Upload (center) · Notifs · Explore.
Upload button is the amber pill — largest tap target on screen. Amber bg (#D4813A), dark cross (#0F0E11), border-radius 50%. No label needed.
Active tab icon strokes in amber. Label in amber (DM Mono 7–8px). Inactive in #38334a.
Onboarding flow

7-screen onboarding

Splash auto-advances after ~2 seconds. Sign-in users jump directly to screen 7. Progress bar shown on screens 4–6 only — those are the form steps. Hidden elsewhere so the flow doesn't feel like a checklist.

ScreenNameKey details
1SplashAnimated waveform bars + wordmark + tagline. Auto-advance ~2s. No user action required.
2Value prop"15 seconds. Maximum damage." Large waveform visual. Single CTA: Get started. Existing users: Sign in link.
3Sign upEmail + password fields. Google SSO. Apple SSO. "Create account" amber CTA. RipFeed wordmark top-left.
4Username@ handle picker. Amber active border on input field. DM Mono availability check (green ✓ available). Progress bar 50%. Keyboard "next" key in amber.
5NotificationsBell icon + explanation. Native iOS/Android permission sheet. "Allow" in amber. Progress bar 75%.
6Content policy✓/✕ do/don't list. Satisfies Apple guideline 1.2. "Original recordings only" stated clearly. Terms + Content Policy linked. Progress bar 100%.
7WelcomePrimary icon + "You're in." + @handle greeting. Two CTAs: "Browse the feed" (amber primary) + "Record your first clip" (outlined secondary).

Copy decisions

01
Value prop headline: "15 seconds. Maximum damage." — leads with the content truth, not a feature list.
02
Sign-up subhead: "Original recordings only. 15 seconds max." — sets expectation before account creation.
03
Content policy title: "One rule. Keep it yours." — frames the restriction as a community value, not a legal warning.
04
Welcome footer: "15 seconds. original recordings. let it rip." — DM Mono, lowercase, dim. Echoes the whole brand in one line.
Web player

ripfeed.io/clip/[id]

Minimal Cloudflare Pages site. Reads clip metadata from Supabase public API (anon key, is_public=true only). Streams audio via HTML5 <audio> from R2 CDN. No auth, no additional backend infrastructure required.

Page sections

Nav
RipFeed wordmark (gold) + "Get the app →" amber CTA pill. No other nav items — keeps focus on the clip.
Creator
Avatar, username (gold), follower count, + Follow button (amber outline). Mirrors app card anatomy.
Player
64px waveform (double in-app height — more room on desktop). Played/unplayed split + amber progress line + scrubber track. Play/pause, volume. HTML5 <audio> pointing at R2 CDN URL.
Share
Share strip: Twitter/X, iMessage, Copy link. All client-side — no backend needed.
Banner
App download banner. Primary icon (amber inverted) + "Hear more on RipFeed" + App Store / Google Play CTAs. This is the acquisition moment for non-app users — keep it above the fold on mobile.
More
2 more clips from the same creator. Playable — users can hear before they install. Drives discovery and download intent.
Footer
Content Policy, DMCA, Contact links. Required for App Store submission and DMCA compliance. Must be present before launch.

Open Graph tags (server-rendered per clip)

<!-- Powers link previews in iMessage, Twitter, WhatsApp, Slack -->
<meta property="og:title"       content="@username on RipFeed"/>
<meta property="og:description"  content="[clip title] [tags]"/>
<meta property="og:image"       content="https://cdn.ripfeed.io/waveforms/{clipId}.png"/>
<meta property="og:audio"       content="https://cdn.ripfeed.io/clips/{clipId}.aac"/>
<meta property="twitter:card"   content="player"/>

The waveform PNG from FFmpeg doubles as the OG image. Same file, zero extra work. Visually distinctive — immediately reads as "audio content" before anyone taps.

Audio processing

FFmpeg waveform command

Waveform color is updated to amber #D4813A from the original purple #7F77DD. One character change in the filter string. The transparent background flag is non-negotiable.

# Void Ember — single pass, two outputs
# ⚠️  Waveform color updated: #7F77DD → #D4813A (amber)
# Update in: lib/ffmpeg-worker/index.ts

ffmpeg -i input \
  -c:a aac -b:a 128k -t 15 output.aac \
  -filter_complex "aformat=channel_layouts=mono,compand,showwavespic=s=300x64:colors=#D4813A|0x00000000" \
  -frames:v 1 waveform.png

# Filter chain:
#   aformat=channel_layouts=mono  — collapse stereo to unified waveform shape
#   compand                       — dynamic range compression, every clip looks alive
#   showwavespic=s=300x64         — 300×64px static waveform image
#   colors=#D4813A|0x00000000     — AMBER bars, TRANSPARENT background (mandatory)
#
# Night Bloom backup theme: swap #D4813A → #5CDD6E (one-char change)
01
Update #7F77DD to #D4813A in the FFmpeg worker (lib/ffmpeg-worker/index.ts) before first clip upload goes live.
02
Never remove the transparent background. 0x00000000 is mandatory — the PNG must composite correctly over #1A1820 card and #0F0E11 base backgrounds.
03
The -t 15 flag is non-negotiable. Server-side hard cap. Never remove regardless of subscription tier. The 30s Pro path does not exist yet.
04
Night Bloom: swap #D4813A#5CDD6E in the filter string. One-character change enables the full backup theme waveform color.
Backup theme

Night Bloom

Held in reserve. Same Void background as the primary theme — only the waveform color and trending accent change. Candidate for Pro custom themes or a seasonal variant in Phase 3. Do not display both themes simultaneously.

Night Bloom
Void bg × lime green waveform × gold trending
held in reserve
#0F0E11
#1A1820
#5CDD6E
#A8F0B0
#F0C27A
Waveform → #5CDD6E (was #D4813A)
Trending → #F0C27A gold (was #7F77DD purple)
FFmpeg filter → colors=#5CDD6E|0x00000000
All other values (bg, fonts, card structure) → unchanged
Implementation

Non-negotiables

These rules protect the identity and the product. Breaking any degrades the system.

01
Purple is trending-only. #7F77DD on trending card borders, TRENDING badges only. Never a primary action color or decoration.
02
Gold identifies people, amber drives actions. #F0C27A = usernames, wordmark, identity. #D4813A = play button, waveform, record, progress, tags, CTAs. Never swap them.
03
No off-palette colors. Every hex value maps to one of the ten named palette values. No exceptions.
04
DM Mono is for data, Space Grotesk is for language. Timestamps, counts, tags, durations, badges → DM Mono. Names, titles, descriptions, nav → Space Grotesk. Never mix within one semantic element.
05
All borders are 0.5px. No exceptions. Heavier borders break the refined dark aesthetic.
06
Waveform PNG background is always transparent. 0x00000000 in FFmpeg filter is mandatory. Opaque backgrounds mismatch on card surfaces.
07
Pro users never see ads. Check usePro() before rendering any ad component. If isPro === true, skip entirely — no placeholder.
08
The -t 15 flag is server-side and non-negotiable. Never trust client duration. 30s Pro path does not exist yet.
09
Night Bloom is backup, not concurrent. Full theme swap only — never both themes displayed simultaneously.
10
APP_NAME is a constant. Never hardcode "RipFeed" as a string literal. Use APP_NAME from config throughout.
11
Web player reads public data only. Supabase anon key, is_public=true clips only. No authenticated routes. No sensitive data on the web player.
12
Content policy screen is required at onboarding. Screen 6 satisfies Apple guideline 1.2. Must be present at first App Store submission — not optional.