Skip to main content
vibld

Template

Iris Hale

An editorial, warm-ivory portfolio and blog for a copywriter and brand strategist. A giant serif quote-style intro weaves small rounded photos between words, with deep-gold italic highlights, and a left rail of ✦ markers navigates six resume sections: intro, about, skills, experience, education and languages. A blog with comments and a contact form sit alongside.

Literary writer showcase with essays · Website · Portfolio · full-stack app (auth + DB)

A mock-up of the homepage, drawn from this design’s layout, palette and typefaces. A build follows the full prompt below.

Start from this templateRead the build prompt

Typefaces

Playfair's variable optical sizes give an expressive, warmer literary serif than Playfair Display, and Rubik stays as the friendly rounded body.

  • PlayfairHeadings: intro 96px, section heads 60px, italic for emphasis
  • RubikBody: body 17px, 12px uppercase labels

Who it is for

  • Freelance writers and copywriters
  • Brand strategists and content leads
  • Writers who want a blog alongside a portfolio
  • Agency creatives serving multiple clients

Layout

  1. Centered top nav: Resume (active), Blog, Contact
  2. Fixed left scroll-spy rail with ✦ markers: Intro, About, Skills, Experience, Education, Languages
  3. Intro: huge serif statement in quotes with inline rounded photos between words and deep-gold italic key words; underlined location, email and phone links
  4. About me paragraph
  5. What I do: ✦ labelled skill groups (Writing, Strategy, Collaboration) as inline lists separated by gold dots, alternating italic
  6. Where I've been: experience entries
  7. What I studied: education
  8. What I speak: languages
  9. Let's talk: contact form
  10. Blog index and post pages with comments

Palette

Literary, warm, witty, elegant, personal. It reads like a beautifully typeset author's page.

  • background (ivory)#f6f2ee
  • text#2a2622
  • accent (gold)#d4a02a
  • body text#181818
  • muted#6f6962
  • divider#e4ded7
  • gold text#8c691c
  • input border#9e896f

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text on ivory15.94:14.5:1
Aaheadings/foreground on ivory13.48:14.5:1
Aadeep-gold italic key words on ivory4.54:14.5:1
Aamuted meta text on ivory4.87:14.5:1
input border on ivory3.01:13:1
focus ring (dark) on ivory13.48:13:1
Aaprimary button text on dark fill13.48: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. Marked tokens are solved from the palette, because no swatch held that role at 4.5:1.

  • background
  • card
  • muted
  • primary
  • secondary
  • accent
  • destructive *

Type scale

Display
Serif (Playfair / Georgia-like) 96px intro, 60px section headings, italic for emphasis (Georgia is not on Google Fonts; a vibld project loads Playfair)
Body
Rubik 17px at 1.6 line-height; 12px uppercase labels (one word each) with 0.1em tracking and ✦

An expressive serif carries the voice; a rounded sans keeps UI friendly. Italic vs roman alternation adds rhythm.

Spacing and imagery

Centered ~900px text column, fixed left rail at 32px, large vertical gaps between sections, rounded-square inline images (~12px radius).

Small rounded workspace photos embedded inline within the headline; otherwise type-only.

Components

  • Centered top nav
  • Scroll-spy ✦ side rail
  • Quote-style hero with inline images
  • Gold italic highlights
  • Underlined contact links
  • Skill groups with dot separators
  • Experience entries
  • Education entries
  • Languages list
  • Contact form
  • Blog index
  • Blog post with comments
  • Floating avatar animation

Interactions

  • Side rail highlights the section in view and scrolls smoothly on click
  • Staggered entrance animations for hero words and images
  • Inline images gently float
  • Links underline in gold on hover
  • Blog comments submit with validation and moderation state
  • Contact form with toast confirmation

Data

  • Experience{role, org, start, end, summary}
  • Education{degree, school, years}
  • SkillGroup{label, items[]}
  • Language{name, level}
  • Post{slug, title, excerpt, body_md, published_at, cover}
  • Comment{id, post_slug, author_name, body, created_at, status}
  • Message{name, email, message}

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 a template'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 templates)
- 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.
### Goal
Build a literary portfolio and blog for a fictional copywriter and brand strategist, "Iris Hale". It serves marketing leads and editors hiring writers. The home page is a scrolling résumé in six sections guided by a ✦ side rail. It opens with an oversized, quote-like sentence that weaves tiny photos between the words. A blog with moderated comments shows writing samples.

### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix Form, Textarea, Toast via Sonner) and lucide-react. Use Framer Motion for staggered hero words and floating images, react-hook-form + zod, React Router for `/`, `/blog`, `/blog/:slug` and `/contact`, and react-markdown with DOMPurify for posts. Supabase provides Postgres for posts and comments, Auth (email magic link, owner only) for moderation and publishing, and an Edge Function for comment submission with spam checks.

### Pages & layout
1. Top nav: three centered links, active dark, hairline border.
2. Left rail (desktop, fixed and vertically centered): ✦ plus a label for each of the six sections. The active ✦ turns bright gold and the label turns dark and italic.
3. Intro: a three-line serif statement at clamp(48px, 8vw, 96px) wrapped in large quote marks. Two or three 64-96px rounded photos sit inline between words, and key words are deep-gold italic (#8c691c). Below it, underlined italic links for city, email and phone (placeholder).
4. About: 1 paragraph at 22px serif.
5. What I do: groups labelled "✦ WRITING / STRATEGY / COLLABORATION" in small caps, each followed by a 28px serif inline list that alternates roman and italic with gold "·" separators.
6. Experience, Education and Languages: serif titles with sans meta.
7. Let's talk: the contact form.
8. Blog index (cards with title, date and excerpt), then the post page with a comments list and form.

### Design system
- `--background: #f6f2ee`, `--foreground: #2a2622`, `--body: #181818`, `--accent: #d4a02a` (decorative ✦, dots and underlines only), `--accent-text: #8c691c` (gold words), `--muted-foreground: #6f6962`, `--border: #e4ded7` (decorative), `--input-border: #9e896f`.
- Fonts: Playfair 400/400i for display and Rubik 400/500 for body, from Google Fonts. Scale: 12 / 14 / 17 / 22 / 28 / 60 / 96px.
- Spacing: 8px base, 900px column, 160px section rhythm. Radius 12px on inline images and 6px on inputs.
- Motion: 600ms word stagger (40ms each), a 4s ease-in-out float (±4px) and 250ms link underline.

### Components & interactions
- ScrollSpyRail: `aria-current` on the active item, with click-to-scroll.
- InlinePhoto: decorative (`alt=""`), with the float paused under reduced motion.
- CommentForm: name, comment and honeypot. States for invalid, submitting, "Awaiting moderation" success and error. Comments render only once approved.
- Admin (owner only): approve/delete comments, publish posts.

### Data & state
Tables: `posts(slug pk, title, excerpt, body_md, cover, published_at, author_id)`, `comments(id, post_slug fk, author_name, body, status check in ('pending','approved','rejected'), created_at, ip_hash)`. Résumé data is static TypeScript. Seed three sample essays.

### Accessibility
- Bright gold #d4a02a on ivory is only 2.13:1, so it is reserved for decorative ✦, dots and underlines. Gold words use #8c691c (4.54:1). Links use the dark text color with a gold underline.
- Make inline photos `aria-hidden` so the sentence reads cleanly.
- The rail is a `<nav aria-label="Résumé sections">`. Comments and forms have labels, and errors are announced.
- Verified contrast: body #181818 on #f6f2ee = 15.94:1; gold words #8c691c on #f6f2ee = 4.54:1; muted #6f6962 on #f6f2ee = 4.87:1; input border #9e896f on #f6f2ee = 3.01:1.

### Security
- RLS on `posts`: public `select` where `published_at <= now()`, and writes only for the owner (`auth.uid() = author_id`).
- RLS on `comments`: public `select` only where `status = 'approved'`. Inserts go only through the Edge Function (service role in function env), which validates with zod, checks the honeypot, applies a per-IP-hash rate limit and length caps, and stores `status = 'pending'`. Update and delete are owner only.
- Sanitize markdown with DOMPurify. No raw HTML in comments.

### Performance & SEO
Lazy-load post covers. Add Article JSON-LD, OG images per post and an RSS feed.

### Guardrails
- Do write all copy yourself and use placeholder imagery (Unsplash-style stock or generated); no trademarked names, client logos or real people.
- Don't present invented testimonials or awards as real; label sample content as sample.
- Keep components small and fully typed; no `any`. Surface errors visibly instead of failing silently.
- Don't collect or store more personal data than the contact form needs.
- Done when:
  - [ ] Scroll-spy rail tracks all six résumé sections
  - [ ] Hero sentence reads correctly to screen readers despite inline photos
  - [ ] Comments appear only after owner approval; anon cannot read pending ones
  - [ ] Markdown posts render sanitized
  - [ ] Gold accent never used for small body text

Open the builderAll templatesThis palette on its own