Template
Mossgate
A last-step profile form shown right after sign-up on a developer community hub. It collects a unique handle, a display name and a handful of optional public links so the new member's profile page is not empty on day one. It keeps the site's top navigation visible so the step feels like part of the product, not a detour.
Community profile-completion form card · App screen: account setup · Small tools and apps · full-stack app (auth + DB)
A mock-up of the screen, drawn from its layout, palette and typefaces. A build follows the full prompt below.
Start from this screenRead the build prompt
Typefaces
The catalog's own faces. A screen composed into a template is drawn in that template's typefaces.
- Source Sans 3Headings: Source Sans 3 700, 28px, tracking -0.01em for the card title
- Source Sans 3Body: Source Sans 3 400, 16px / 1.5; labels 15px 600; helper and (optional) suffix 14px 400 muted
Patterns
- centered form card on soft vertical gradient
- avatar disc overlapping card top edge
- two-column form grid collapsing to one
- (optional) suffix labels
- brand-icon prefixed social handle fields
- terms checkbox gating submit
- global top nav kept visible during setup
States it is designed for
- idle with empty required fields
- handle checking spinner
- handle taken with suggestions
- invalid URL inline error
- avatar uploading progress
- avatar too large / wrong type error
- submitting (button spinner, fields read-only)
- server error banner with retry
- success redirect to the new profile
- session expired prompt to sign in again
Who it is for
- new members of a developer or research community
- open-source contributors
- hobbyist model builders
Layout
- Top bar: wordmark left, search field, text nav links with small icons, a 'new' pill badge on one link, sign-in text link and a dark pill sign-up button right
- Canvas: full-bleed vertical gradient from pale lavender to warm cream
- Card: ~560px wide, 16px radius, 1px cool-grey border, avatar placeholder disc (64px) straddling the top edge
- Card header: bold centred title, one-line muted subtitle
- Form grid: two columns (handle | display name; avatar upload | social handle A; social handle B | social handle C), then full-width homepage URL and interests textarea
- Consent row: checkbox with inline underlined policy links
- Full-width dark submit button at the card foot
- Below 640px: nav collapses to a menu button, grid becomes one column, card goes edge-to-edge with 16px gutters
Palette
Friendly, airy, welcoming. A calm gradient and a single white card make an optional-heavy form feel short.
- canvas-top
#e8ebfa - canvas-bottom
#fff9f1 - surface
#ffffff - text
#0b0f19 - muted
#5f6675 - border
#ccd1df - input-border
#8a92a6 - primary
#0b0f19 - on-primary
#ffffff - focus
#4b58d1 - new-badge
#3a47c4 - error
#c0262d
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on card | 19.15:1 | 4.5:1 |
| Aa | muted (optional) label on card | 5.76:1 | 4.5:1 |
| Aa | primary button label | 19.15:1 | 4.5:1 |
| input border on card | 3.11:1 | 3:1 | |
| focus ring on card | 5.81:1 | 3:1 | |
| Aa | new badge text on lavender canvas | 6.15:1 | 4.5:1 |
| Aa | error text on card | 5.90:1 | 4.5:1 |
| Aa | nav text on canvas bottom | 18.30:1 | 4.5:1 |
As vibld’s tokens
The palette on the fifteen colour tokens vibld styles a project with, each text colour on the fill it is read on.
- background
- card
- muted
- primary
- secondary
- accent
- destructive
Type scale
- Display
- Source Sans 3 700, 28px, tracking -0.01em for the card title
- Body
- Source Sans 3 400, 16px / 1.5; labels 15px 600; helper and (optional) suffix 14px 400 muted
Similar to the observed humanist sans; nav links 15px 500. No uppercase except the tiny 'new' badge (12px, 600, +0.06em).
Spacing and imagery
Comfortable density; 8px base; card padding 24px; 16px column gap and 20px row gap; container max 560px; radius 16px card, 8px inputs and buttons, full pill for the nav CTA; one soft shadow on the card (0 8px 30px rgba(20,24,60,.06)).
No photography. A gradient avatar placeholder disc, monochrome brand glyphs for social fields, and a small palette icon button beside upload for generating a default avatar.
Components
- top navigation bar with search
- gradient avatar placeholder
- text inputs with placeholders
- file upload button with generate-avatar icon button
- icon-prefixed field labels
- (optional) label suffix
- multiline textarea
- consent checkbox with inline links
- full-width submit button
- inline field error text
- handle availability indicator
Interactions
- Handle field debounces 400ms and shows available / taken with an icon and text
- Upload opens a file picker; the preview replaces the placeholder disc with a crop dialog
- Generate-avatar icon button shuffles a gradient default
- Submit stays disabled until handle, name and consent are valid
- Focus moves to the first invalid field on submit
- Card fades up 12px on mount (disabled under reduced motion)
Data
Profile{user_id, handle (unique, 3-30 chars), display_name, avatar_path, homepage_url, interests, social_a, social_b, social_c, accepted_terms_at, created_at}HandleReservation{handle, user_id, reserved_at}
Guardrails
Experience
- Only handle, display name and consent are required; every other label reads (optional)
- Explain handle rules (letters, numbers, hyphens) under the field before the user types
- Offer two or three alternative handles when one is taken
- Keep the site nav visible but make leaving warn if fields are dirty
- Accept social handles with or without a leading @ and strip it
- Keep the submit button label action-specific, e.g. 'Finish profile'
Accessibility
- Every input has a visible <label>; the (optional) suffix is inside the label text
- Handle availability is announced in an aria-live=polite region
- Error text is linked with aria-describedby and uses an icon plus words, not colour alone
- Placeholders never replace labels and meet 4.5:1
- Checkbox hit area is at least 24x24px and the policy links are separately focusable
- Visible 2px indigo focus ring with 2px offset on every control
Security
- Supabase RLS: profiles readable publicly but insert/update only where user_id = auth.uid()
- Validate handle, URLs and lengths with zod on client and in a database check constraint; reject javascript: URLs
- Avatar uploads: image MIME whitelist, 2 MB cap, re-encode server-side, random object names in a user-scoped bucket path
- Rate-limit handle availability checks per IP and per user
- Store only public handles; never ask for passwords or tokens of linked services
Build prompt
The baseline every prompt in the catalog assumes, then this design’s own ten sections, from goal to guardrails.
The baseline
### How to use these prompts Paste an entry's build prompt into your coding agent as the first message. Each prompt names its own stack, tokens and acceptance criteria; the rules below apply to all of them and can be prepended once per project. ### Engineering baseline - TypeScript strict mode, no `any`, small typed components, feature folders, and one source of truth for design tokens (CSS variables consumed by Tailwind). - Validate every input with a shared zod schema on the client and again on the server or edge function. Never trust client-side checks alone. - Show loading, empty and error states for every async view. Surface errors in plain language with a retry, and log details to the console in development only. - Keep secrets out of the bundle. Only publishable keys (for example a Supabase anon key) belong in client code; service-role keys, API keys and webhooks live in server or edge-function environment variables. ### Data and auth baseline (full-stack entries) - Enable Row Level Security on every table before inserting data. Default-deny, then add owner-scoped policies (`auth.uid() = user_id`) and explicit role checks for admin views. - Store roles in a separate table checked by a security-definer function, never in a user-editable profile field. - Upload files to private storage buckets with size and MIME limits, and serve them through signed URLs. - Rate-limit public endpoints (forms, auth, AI calls) and add a honeypot field or captcha to anonymous forms. - Take payments through a hosted checkout and verify webhooks by signature. Never handle raw card data. ### Accessibility and UX baseline - Target WCAG 2.2 AA: 4.5:1 contrast for normal text and 3:1 for large text, input borders, focus rings and meaningful icons or chart lines. Every palette in this catalog lists its verified pairs; re-check with a contrast tool after any colour change. - Keep body text at 16px or larger with 1.5 line height, nothing below 12px, no light weights under 24px, and uppercase only for short labels. - Give every interactive element a visible focus ring, full keyboard support, semantic landmarks, labelled form fields, and alt text on meaningful images. - Respect `prefers-reduced-motion` for every animation. Give drag-and-drop and carousels keyboard and button alternatives. - Build mobile-first and test at 375px, 768px and 1280px. ### Content guardrails - Use original copy, fictional sample data and placeholder or licensed imagery. Do not reuse another product's name, logo, screenshots or marketing text. - Label demo testimonials and metrics as samples. Collect the minimum personal data the feature needs. ### SaaS screen baseline - Design every screen for its full set of states: first-run empty, loading skeleton, partial data, error with retry, permission-denied, and success feedback. Each entry lists the states its screen needs. - Keep destructive actions (delete, revoke, downgrade, remove member) behind a confirmation that names the object, and prefer undo over a second dialog where the action is reversible. - Enforce authorisation on the server for every action a screen exposes. Hiding a button is not access control; check the role again in the API or RLS policy. - Never show secrets (API keys, tokens) in full after creation. Show them once, then mask them, and offer rotate and revoke. - Keep the app shell (navigation, workspace switcher, account menu) consistent across screens, and preserve filters, sort and scroll position when the user navigates back.
### Goal
Build **Mossgate**, the 'complete your profile' step of a developer community hub. A newly registered member lands on one card that asks for a handle, a display name and a few optional public links, agrees to the community rules and is dropped onto their new public profile. The screen must feel short even though it has nine fields, because only three are required.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix) for Input, Textarea, Checkbox, Dialog and Button, lucide-react icons. react-hook-form + zod for validation, TanStack Query for the handle availability check and submit mutation. Supabase Auth for the session, Postgres for `profiles`, Storage for avatars.
### Pages & layout
1. **/setup/profile** (this screen): global top bar (wordmark, search, five nav links, sign-in link, dark pill CTA) above a full-height lavender-to-cream gradient. A 560px white card sits centred 64px below the bar with a 64px gradient avatar disc half outside its top edge.
2. Card contents top to bottom: title 'Finish setting up your profile', subtitle 'One quick step before you join the conversation'; two-column grid of handle and display name; avatar upload + generate button beside social handle A; social handles B and C; full-width homepage URL; interests textarea (3 rows, resizable vertically); consent checkbox; full-width submit.
3. **/u/:handle**: minimal public profile used as the success destination.
4. Mobile (<640px): single column, card full-width with 16px gutters, nav collapsed behind a menu button, avatar disc stays centred.
### Design system
- Colors: `--canvas-top: #e8ebfa`, `--canvas-bottom: #fff9f1`, `--surface: #ffffff`, `--text: #0b0f19`, `--muted: #5f6675`, `--border: #ccd1df`, `--input-border: #8a92a6`, `--primary: #0b0f19`, `--on-primary: #ffffff`, `--focus: #4b58d1`, `--new-badge: #3a47c4`, `--error: #c0262d`.
- Fonts: Source Sans 3 (similar to the observed humanist sans). Title 28px/700, labels 15px/600, body and inputs 16px/400 at line-height 1.5, helper text 14px. No text below 12px.
- Spacing: 8px base; card padding 24px; grid gap 16px x 20px.
- Radius: card 16px, inputs 8px, nav CTA full pill.
- Shadows: card `0 8px 30px rgba(20,24,60,.06)`; inputs flat with 1px border.
- Motion: 200ms ease-out for focus and hover; card mount fade-up 240ms; none under `prefers-reduced-motion`.
### Components & interactions
`ProfileCard`, `AvatarPicker` (placeholder disc, upload button, generate icon button, crop Dialog with zoom slider), `HandleField` (debounced availability, check or cross icon plus text, suggestion chips when taken), `LabeledInput` with optional suffix and leading brand glyph, `InterestsTextarea` with 300-char counter, `ConsentCheckbox` with two inline links opening in a new tab, `SubmitButton` (disabled until valid, spinner while saving). Hover darkens inputs' border one step; focus shows a 2px indigo ring. Enter in any single-line field submits once valid.
### Data & state
`profiles(user_id pk references auth.users, handle citext unique, display_name, avatar_path, homepage_url, interests, social_a, social_b, social_c, accepted_terms_at, created_at)`. The form state lives in react-hook-form; the availability query is keyed by handle and cached 30s. Mock data for design review: handle 'river-oak', display name 'Priya Anand', homepage 'https://example.dev'. On success invalidate the session profile query and route to `/u/:handle`.
### Accessibility
Labels are real `<label>` elements; the optional marker is part of the label text so screen readers hear it. Availability results go to an `aria-live="polite"` region. Invalid fields get `aria-invalid` and a linked error message with an icon. The crop dialog traps focus and returns it to the upload button. Brand glyphs are `aria-hidden`; the field label names the network.
Verified contrast: body text on card: #0b0f19 on #ffffff = 19.15:1; muted (optional) label on card: #5f6675 on #ffffff = 5.76:1; primary button label: #ffffff on #0b0f19 = 19.15:1; input border on card: #8a92a6 on #ffffff = 3.11:1; focus ring on card: #4b58d1 on #ffffff = 5.81:1; new badge text on lavender canvas: #3a47c4 on #e8ebfa = 6.15:1; error text on card: #c0262d on #ffffff = 5.9:1; nav text on canvas bottom: #0b0f19 on #fff9f1 = 18.3:1.
### Security
RLS on `profiles`: `select` for everyone (public profiles), `insert` and `update` only where `user_id = auth.uid()`, no `delete` from the client. Storage bucket `avatars` with policy restricting writes to `avatars/{auth.uid()}/*`, image MIME whitelist and 2 MB limit; re-encode to WebP in an Edge Function. zod plus database check constraints for handle pattern and URL scheme (https only). Rate-limit availability checks (e.g. 20/min per user).
### Performance & SEO
Ship the setup route as its own chunk; lazy-load the crop dialog. Debounce availability calls. Mark `/setup/*` noindex; public profiles get title and description from the display name.
### Guardrails
- Use invented example names, handles and URLs only; no real brands or people
- Do not add fields beyond the ones listed; everything except handle, name and consent stays optional
- Never block the submit on optional fields' network checks
- Keep components typed; no `any`; surface every server error in the UI
- Acceptance criteria: (1) the form submits with only the three required inputs; (2) a taken handle shows suggestions and is announced; (3) avatar upload rejects a 5 MB PDF with a clear message; (4) RLS prevents editing another user's profile; (5) the layout works at 390px without horizontal scroll.