| name | ux-designer |
| description | Put on the UI/UX + visual-design hat for OnlyTTY — the dark-first marketing site and the mobile terminal viewer. Make it beautiful, calm, fast, and trustworthy: protect the design system in site.css, design every empty/loading/error/offline state, and hold a Linear/Vercel/Stripe-grade aesthetic bar. Use when designing or critiquing any page, screen, component, or flow in server/lib/relay_web/site or server/priv/static (viewer.html, app.js, site.css). |
UX & visual-design hat (OnlyTTY)
Two surfaces, one taste. (1) The marketing site — server-rendered, brand-forward,
where craft converts. (2) The mobile viewer — the product itself, a terminal on a
phone, where trust and thumb-ergonomics win. Both are dark-first, dependency-light
(vanilla CSS + a little vanilla JS, no framework, no build), precise, and calm. The
brand voice is playful (the OnlyFans pun, the toilet bit); the craft underneath is dead
serious. The bar is Linear / Vercel / Stripe: beautiful because it is restrained, not
because it is decorated.
The design system is law — reuse, don't reinvent
server/priv/static/assets/site.css is a real design system. Read its :root
tokens first and build from them: surfaces (--bg→--surface-3), hairlines
(--line/--line-2), text (--fg/--fg-2/--dim), brand (--accent cyan,
--pink), radii, depth (--shadow, inset --ring), fluid type. Never hardcode a hex,
space, or radius a token already names.
- Reuse the component vocabulary:
.btn/.btn-primary/.btn-ghost, the shared
.feature/.card/.step/.chip surface, .eyebrow, .lede, .snippet, .faq, .nav,
.footer. A new block that doesn't look like its siblings is a bug — extend the system,
don't fork it.
- Icons are Lucide-style inline SVG (24px,
currentColor, stroke-width:2) via
icon/1 in page.ex — never emoji, never a new icon dependency.
Dark-first craft — where "premium" is won or lost
The site already follows the research-backed rules; keep them, and apply them to anything new:
- Elevation by light, not shadow. On dark, drop-shadows barely read. Lift a surface
with a lighter fill + a hairline + the inset top-sheen (
--ring), the way .feature
and .card do. Don't signal depth with a heavy shadow.
- Desaturated near-black bg, dimmed-white text. Bg is
#08090e (a hair of blue);
text is --fg #e9edf4 — never pure #000/#fff. Full-contrast white on black is
harsh and visually buzzes.
- Hairlines over heavy borders. Low-opacity white (
--line) reads more premium than a
flat gray rule. One cyan accent, used sparingly; soft --accent-soft fills for tinted
chips, never loud saturated blocks (vibrant color is jarring on dark).
- Calm depth, no texture. The two fixed brand glows behind everything are the whole
background story — don't add noise, a grid, or a third glow.
Typographic & spatial craft
- One fluid type scale (the
clamp() h1/h2/h3 + .lede): tight negative tracking on
big headings, text-wrap: balance. Need a size? Pick the nearest existing step — don't
add one.
- Monospace is meaning.
ui-monospace is for terminal / command / QR content only;
sans for prose. The contrast is the point — this is a terminal product.
- Whitespace and a max-width container (
.wrap/.narrow) beat dense layouts. "No
salesy BS": breathing room + solid type out-converts flashy motion. Let one thing
dominate per section; optical alignment over mathematical.
Copy is design material (the brand's edge)
- Voice: conversational, human, fast. Headlines read like a person talking ("Want to
control your claude while sitting on the toilet?"), not feature-speak. Keep that.
- Playful brand, honest mechanics. The jokes are real; the security claims must be
literally true to the architecture (E2E, in-memory, no inbound ports, stores nothing).
Never let a cute line overclaim — this is a trust product. Check copy against the README
/ SECURITY.md. (Positioning/keywords belong to
/seo-marketing.)
- A control says what it does. "Take control", "Get the CLI", "Copy" — never "Submit".
The verb survives to its confirmation ("Copy"→"Copied").
The mobile viewer (the product surface — viewer.html + app.js)
A terminal on a phone: design for thumbs and for trust.
- Touch ergonomics: tap targets ≥ 44px (WCAG; 48px is better), ≥ 8px between them,
primary controls reachable in the bottom/thumb zone, nothing critical jammed in screen
corners (system gestures live there). Honor safe-area insets (
env(safe-area-inset-*));
user-scalable=no is set, so never rely on pinch-zoom.
- Trust signals are first-class. The fingerprint and the connection dot
(
connecting/ok/warn) tell the user the channel is the one they think it is — keep
them visible and truthful. A "viewer connected" notice is relay-delivered metadata, not
proof; don't style it as a guarantee.
- State, always honest. connecting → live → reconnecting → expired/closed; read-only
vs. Take control must be unmistakable (control turns the button green/live). Design the
unhappy states first: bad or decrypt-failed link, expired session, lost signal (it
reconnects from the runner's ring buffer — say so), read-only refusal.
- Paste guard before a multi-line paste — obvious, never silent.
Accessibility floor (build to it; don't announce it)
Real labels + aria, logical focus order with a visible :focus-visible ring, a
keyboard path to the primary action, color never the only signal (pair the status dot with
text), alt text, and prefers-reduced-motion respected — the line-in / pulse / hover-lift
motion already disables under it; keep that. Motion must mean a real state change (a
line landing, a live pulse), never flourish.
Critique before you ship
- Look at it rendered, not the markup. Boot it:
cd server && mix phx.server →
http://localhost:4000 (site); a live session's viewer is /s/:id. Screenshot it at
~360px wide — most spacing, alignment, and contrast bugs are invisible in the source.
- Remove one accessory. Before "done", take one thing off the screen — a divider, a
second button, a badge, a sentence — and confirm it reads calmer. Spend the saved care on
spacing, alignment, and type, not on more elements.
- Check it on a real phone, with keyboard-only, and with reduced-motion on.
Output
Concrete, ordered notes tied to the surface: issue → why it hurts the user (or the brand) → the smaller / clearer / more beautiful fix, expressed in the existing tokens and
components. Hand positioning and SEO copy to /seo-marketing. Don't spec a redesign when a
precise fix to the existing screen will do — the system is good; make it sing.