Skip to main content
vibld

Template

Ondriel

In an AI assistant's settings, a user connects an email account as a data source. Before redirecting to the provider's OAuth page, a warm, plain-language dialog explains what the assistant may do with the data, what control the user keeps, and where to learn more.

Third-party connector authorization consent dialog · App screen: confirmation · 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 Serif 4Headings: Source Serif 4 600 for the Settings page title (28px); dialog title in the body sans 600 20px
  • Source Sans 3Body: Source Sans 3 400 16px / 1.55

Patterns

  • consent dialog before OAuth redirect
  • plain-language data-use bullets
  • you-stay-in-control reassurance section
  • two-column settings (section list + content)
  • connector list with Configure / Connect buttons
  • icon rail navigation

States it is designed for

  • not connected
  • consent dialog open
  • redirecting (button busy, dialog locked)
  • connected with last sync time
  • OAuth denied by user (neutral message, no error styling)
  • token expired: row badge 'Reconnect needed'
  • admin-disabled connector for org accounts (button disabled with reason)

Who it is for

  • individual users of an AI assistant
  • privacy-conscious professionals connecting work tools

Layout

  1. Far-left icon rail (new chat, search, history, projects, code) with avatar at the bottom
  2. Settings header, then a two-column layout: section list (general, account, privacy, billing, usage, capabilities, connectors active) and content
  3. Connectors content: title and description, Browse connectors button, list rows with connector icon, name and Configure/Connect + overflow buttons, Add custom connector button
  4. Dialog (~470px) over a 45% scrim on a warm off-white surface: title with close, lead sentence, bullet list of permitted uses, bold subhead about retained control, second bullet list with an inline link, closing line with help link and redirect notice, footer with Cancel and Continue
  5. Mobile: rail becomes a bottom bar, settings sections become a select, dialog becomes a full-width sheet

Palette

Warm, quiet and reassuring - paper-toned surfaces and near-black type make a sensitive permission feel considered.

  • warm canvas#faf9f5
  • dialog surface#fcfbf7
  • text#141413
  • muted text#6b6a64
  • primary#141413
  • on primary#ffffff
  • secondary button border#8f8d86
  • link#141413
  • scrim#5d5d5d
  • focus ring#3b6fd4
  • danger#b3261e

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text on dialog17.80:14.5:1
Aamuted description on canvas5.15:14.5:1
AaContinue label on near-black18.43:14.5:1
secondary button border3.21:13:1
focus ring on canvas4.52:13:1
Aaerror text on dialog6.31:14.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 Serif 4 600 for the Settings page title (28px); dialog title in the body sans 600 20px
Body
Source Sans 3 400 16px / 1.55

Similar to the observed pairing of a bookish serif heading with a humanist sans; links underlined, not coloured.

Spacing and imagery

Relaxed; 4px base, dialog padding 24px, 12px between bullets, 20px between sections; content column max 640px; radius 8px buttons, 16px dialog; soft shadow 0 12px 32px rgba(20,20,19,.16).

Small monochrome connector icons in rows; no illustrations.

Components

  • IconRail
  • SettingsSectionNav
  • ConnectorRow (icon, name, status, Configure/Connect, overflow menu)
  • AuthorizeConnectorDialog
  • PermissionBulletList
  • ControlBulletList with inline link
  • RedirectNotice
  • AddCustomConnectorButton

Interactions

  • Connect opens the consent dialog; Continue starts the OAuth redirect in the same tab with a short 'Redirecting...' state on the button
  • Cancel/Escape/close return focus to the Connect button
  • After returning from the provider, the row shows Configure and a success toast; failure shows an inline error in the row with Try again
  • Overflow menu offers Disconnect with a confirmation
  • Hover on rows reveals a subtle background; links underline thicker on hover

Data

  • Connector{id, key, display_name, scopes[], category}
  • Connection{id, user_id, connector_id, status (connected|expired|revoked), connected_at, last_sync_at}
  • ConsentRecord{id, user_id, connector_id, scopes[], accepted_at, text_version}

Guardrails

Experience

  • State what the assistant may do, then what the user controls, in plain language
  • Tell the user they are about to leave for the provider before they click
  • Keep Cancel and Continue the same size; Continue is the only filled button
  • After connecting, show where to manage or disconnect
  • Treat a user denial as neutral, not an error

Accessibility

  • Dialog content is readable top-to-bottom with real lists (ul/li), not line breaks
  • Inline links have descriptive text ('delete your conversations') and visible underline
  • Continue's accessible name mentions the redirect ('Continue to sign in with the email provider')
  • Row buttons include the connector name in their accessible names
  • Focus ring is a 2px blue outline so it stays visible on warm off-white

Security

  • Use OAuth with PKCE and a state parameter; reject callbacks whose state doesn't match
  • Request the minimum scopes listed in the dialog and show exactly those
  • Encrypt refresh tokens at rest and never return them to the client
  • Record consent (user, scopes, text version, time) and allow revocation that deletes tokens
  • Admin policy can block connectors per organisation

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 **Ondriel**, the connectors settings page of an AI assistant and its authorization consent dialog for linking a fictional email service. Include connector rows in every status, the consent dialog, the redirect round-trip (mocked) and disconnect.

### Stack
Use React 18, TypeScript, Vite, Tailwind CSS, shadcn/ui, Radix, lucide-react, TanStack Query, react-hook-form, zod, Supabase. OAuth exchange runs in an Edge Function; tokens are encrypted at rest. Use Supabase for Auth, Postgres (row-level security on every table) and Storage where noted; keep only the anon key in the browser and run privileged work in Edge Functions.

### Pages & layout
1. **/settings/connectors** with rows and dialog.
2. **/oauth/callback** route that finalises the connection and redirects back with a status query param.
3. Other settings sections as simple placeholders.

Regions, in order:
- Far-left icon rail (new chat, search, history, projects, code) with avatar at the bottom
- Settings header, then a two-column layout: section list (general, account, privacy, billing, usage, capabilities, connectors active) and content
- Connectors content: title and description, Browse connectors button, list rows with connector icon, name and Configure/Connect + overflow buttons, Add custom connector button
- Dialog (~470px) over a 45% scrim on a warm off-white surface: title with close, lead sentence, bullet list of permitted uses, bold subhead about retained control, second bullet list with an inline link, closing line with help link and redirect notice, footer with Cancel and Continue
- Mobile: rail becomes a bottom bar, settings sections become a select, dialog becomes a full-width sheet

### Design system
- Colors: `--warm-canvas: #faf9f5` (warm canvas), `--dialog-surface: #fcfbf7` (dialog surface), `--text: #141413` (text), `--muted-text: #6b6a64` (muted text), `--primary: #141413` (primary), `--on-primary: #ffffff` (on primary), `--secondary-button-border: #8f8d86` (secondary button border), `--link: #141413` (link), `--scrim: #5d5d5d` (scrim), `--focus-ring: #3b6fd4` (focus ring), `--danger: #b3261e` (danger).
- Fonts: Source Serif 4 600 for the Settings page title (28px); dialog title in the body sans 600 20px for headings; Source Sans 3 400 16px / 1.55 for body. Similar to the observed pairing of a bookish serif heading with a humanist sans; links underlined, not coloured.
- Spacing, radius and shadows: Relaxed; 4px base, dialog padding 24px, 12px between bullets, 20px between sections; content column max 640px; radius 8px buttons, 16px dialog; soft shadow 0 12px 32px rgba(20,20,19,.16).
- Motion: 150-200 ms ease-out for hover, focus and overlay transitions; overlays fade and scale from 98% to 100%; everything collapses to an instant change under prefers-reduced-motion.
- Mood: Warm, quiet and reassuring - paper-toned surfaces and near-black type make a sensitive permission feel considered. Imagery: Small monochrome connector icons in rows; no illustrations.

### Components & interactions
Build these components: IconRail; SettingsSectionNav; ConnectorRow (icon, name, status, Configure/Connect, overflow menu); AuthorizeConnectorDialog; PermissionBulletList; ControlBulletList with inline link; RedirectNotice; AddCustomConnectorButton.

- Connect opens the consent dialog; Continue starts the OAuth redirect in the same tab with a short 'Redirecting...' state on the button
- Cancel/Escape/close return focus to the Connect button
- After returning from the provider, the row shows Configure and a success toast; failure shows an inline error in the row with Try again
- Overflow menu offers Disconnect with a confirmation
- Hover on rows reveals a subtle background; links underline thicker on hover

### Data & state
Model: `Connector{id, key, display_name, scopes[], category}`; `Connection{id, user_id, connector_id, status (connected|expired|revoked), connected_at, last_sync_at}`; `ConsentRecord{id, user_id, connector_id, scopes[], accepted_at, text_version}`.

Store consent with the exact text version shown so it can be audited. Connection status is fetched with TanStack Query and refreshed after the callback.

States to implement and demo:
- not connected
- consent dialog open
- redirecting (button busy, dialog locked)
- connected with last sync time
- OAuth denied by user (neutral message, no error styling)
- token expired: row badge 'Reconnect needed'
- admin-disabled connector for org accounts (button disabled with reason)

### Accessibility
- Dialog content is readable top-to-bottom with real lists (ul/li), not line breaks
- Inline links have descriptive text ('delete your conversations') and visible underline
- Continue's accessible name mentions the redirect ('Continue to sign in with the email provider')
- Row buttons include the connector name in their accessible names
- Focus ring is a 2px blue outline so it stays visible on warm off-white
- Body text is 16px with line-height 1.5 (15px only inside dense tables), nothing renders below 12px, weights of 300 or lighter appear only at 24px and above, and uppercase is limited to short labels with at least 0.05em tracking.
Verified contrast: body text on dialog: #141413 on #fcfbf7 = 17.80:1; muted description on canvas: #6b6a64 on #faf9f5 = 5.15:1; Continue label on near-black: #ffffff on #141413 = 18.43:1; secondary button border: #8f8d86 on #fcfbf7 = 3.21:1; focus ring on canvas: #3b6fd4 on #faf9f5 = 4.52:1; error text on dialog: #b3261e on #fcfbf7 = 6.31:1.

### Security
- Use OAuth with PKCE and a state parameter; reject callbacks whose state doesn't match
- Request the minimum scopes listed in the dialog and show exactly those
- Encrypt refresh tokens at rest and never return them to the client
- Record consent (user, scopes, text version, time) and allow revocation that deletes tokens
- Admin policy can block connectors per organisation

RLS: `connections` and `consent_records` - select/delete only where `user_id = auth.uid()`, inserts via the callback Edge Function (service role); `connectors` - public select; tokens live in a separate `connection_secrets` table with no client policies at all.

### Performance & SEO
Settings pages are client routes; prefetch connector metadata. Noindex. Keep the dialog in the settings chunk.

### Guardrails
- State what the assistant may do, then what the user controls, in plain language
- Tell the user they are about to leave for the provider before they click
- Keep Cancel and Continue the same size; Continue is the only filled button
- After connecting, show where to manage or disconnect
- Treat a user denial as neutral, not an error
- Use the product name Ondriel and fresh, generic copy throughout; all people, companies, amounts and IDs are invented, and no third-party brand, logo or wordmark appears.
- Keep components small and typed (no `any`), and surface every failure visibly instead of swallowing it.

Acceptance criteria:
- [ ] Consent dialog lists scopes matching the requested scopes
- [ ] Mock OAuth round-trip updates the row
- [ ] Disconnect removes the connection and tokens
- [ ] Keyboard and screen-reader flow works
- [ ] 390px layout works

Open the builderAll templatesThis palette on its own