Skip to main content
90% of customization happens in three files: src/config/site.ts, src/app/globals.css, and messages/{en,zh}.json. The rest is swapping logo SVGs, editing email templates, and re-composing marketing blocks. This page is the map.

Quick swaps via siteConfig.ui

Two pre-built switches you flip without writing a single line of CSS — they exist because the underlying change is cross-cutting and easy to get wrong:
ui: {
  theme: 'cream' as 'cream' | 'white' | 'ash',
  // cream  — warm editorial paper (default; Loops / Resend / Clerk feel)
  // white  — clinical pure white (Stripe / Linear / Vercel feel)
  // ash    — cool gray-blue, airy (Apple / Bolt feel)

  navStyle: 'underline-fade' as
    | 'tone'              // Stripe / Resend — color-only shift
    | 'underline-fade'    // Notion / Loops — underline fades in (default)
    | 'pill'              // Linear / Vercel — rounded background
    | 'underline-appears' // NYT — instant underline on hover
    | 'split',            // v0 — pill on hover, line on active
}
Theme classes live in src/app/globals.css; nav recipes live in src/components/layout/nav-styles.ts (each preset documents the source site it’s modeled on). Add a new preset by extending the file — the TypeScript union narrows automatically.

Customize with AI

Vibestrap ships intentionally few pre-built presets. Most block-level changes — the hero, pricing card, footer, button variant, card visual — are faster for an AI agent to generate than for a preset system to maintain. For the common ones, STYLES.md → AI swap recipes ships copy-paste prompt templates that name the exact files and constraints. Open the recipe for the block you want to change, paste the prompt into Claude Code / Cursor / Codex, and the change usually lands in 2–5 minutes. Recipes available:
  • Swap the hero (terminal demo → centered headline / split-screen / video bg)
  • Restyle the pricing card (single → two-card monthly+yearly / 3-tier / slider)
  • Swap the feature-card visual (hard-border → soft-shadow / glass / gradient)
  • Compact the footer (4-col → minimal one-row / mega-with-newsletter)
  • Restyle the primary button (solid → outlined / gradient / ghost)
For full re-skins (cream-mono → dark tech, etc.) see the seven complete style recipes earlier in STYLES.md — same file, top of the document.

Brand identity

Edit src/config/site.ts — the central config:
export const siteConfig = {
  name: 'YourProduct',
  description: 'Your one-paragraph pitch.',
  shortDescription: 'Your one-line tagline.',
  url: 'https://your-domain.com',
  links: {
    github: 'https://github.com/yourorg/yourrepo',
    twitter: 'https://twitter.com/yourhandle',
    contact: 'mailto:hello@your-domain.com',
  },
  mail: {
    fromName: 'YourProduct',
    supportEmail: 'support@your-domain.com',
  },
};
Keep this file under 250 lines. Domain-heavy logic (pricing math, AI provider settings) belongs in its own module — see src/ai/pricing.ts for the pattern.

Colors (custom palette beyond the presets)

Tailwind v4 reads tokens from @theme in src/app/globals.css. Every color is OKLCH — perceptually uniform, predictable hue rotation, copy-paste-friendly with the OKLCH color picker. Vibestrap is light-only — there is no .dark class and no system-mode toggle. If you want a dark variant, see the dark-mode reference doc for a 5-step opt-in recipe. The default cream theme:
.theme-cream {
  --background: oklch(0.96 0.012 85);   /* warm off-white paper */
  --foreground: oklch(0.18 0 0);        /* near-black */
  --primary: oklch(0.18 0 0);
  --primary-foreground: oklch(0.97 0.008 85);
  --accent-tech: oklch(0.55 0.2 250);   /* electric blue — logo cursor + progress bar */
  --accent-tech-light: oklch(0.72 0.16 240);
  /* full set in globals.css */
}
Two ways to customize:
  1. Tweak a preset — edit the OKLCH values inside .theme-cream (or .theme-white / .theme-ash) in globals.css. shadcn/ui components consume these tokens by name, so a single edit re-skins everything.
  2. Add a new preset — duplicate one of the .theme-* blocks under a new name, then extend the union in siteConfig.ui.theme to include it. The TypeScript types pick it up automatically.
Pick a brand hue with the OKLCH picker. Always swap --accent-tech and --accent-tech-light together — the progress-bar gradient interpolates between them.

Fonts

The default stack is Geist Sans + Geist Mono, loaded in src/app/layout.tsx via next/font/google:
import { Geist, Geist_Mono } from 'next/font/google';
const geistSans = Geist({ variable: '--font-geist-sans', subsets: ['latin'] });
const geistMono = Geist_Mono({ variable: '--font-geist-mono', subsets: ['latin'] });
Swap in any next/font/google import. Update --font-sans / --font-mono in globals.css to match the CSS variable name and you’re done — no Tailwind config edit needed.

Marketing copy

All on-page copy lives in messages/en.json and messages/zh.json under the Home namespace. Both files must have identical key structure — next-intl throws at build time if keys diverge.
"Home": {
  "hero": {
    "title": "Your hero title",
    "subtitle": "Your supporting line."
  }
}
Hot-reload picks edits up instantly. When you add a new message key, add it to both files in the same commit.

Hero / features layout

The home page is a stack of independent blocks composed in src/app/[locale]/(marketing)/page.tsx:
<Hero />
<LogoCloud />
<Features />
<UseCases />
<WhyVibestrap />
<Quickstart />
<Pricing />
<FAQ />
<NewsletterCard />
<CTA />
Reorder or delete any of them. Each block is a self-contained component in src/components/blocks/ reading from messages/{locale}.json — no prop wiring across boundaries.

Pricing

Two distinct pricing surfaces in siteConfig: For the Vibestrap product itself (what you sell to your buyers):
product: {
  standardPriceCents: 9900,        // $99
  promo: { active: true, priceCents: 4900 }, // $49 limited offer
}
Toggle promo.active to flip between the two. The hero, pricing block, and checkout all read from activePriceCents() so a single edit propagates. For your buyer’s downstream app (sample subscription + credit pack pricing they show their own users):
demoPlans: [
  { id: 'lifetime_promo',    type: 'one_time',    priceCents:  4900, priceIdEnv: 'STRIPE_PRICE_VIBESTRAP_PROMO',    creditsGranted:   1000 },
  { id: 'lifetime_standard', type: 'one_time',    priceCents:  9900, priceIdEnv: 'STRIPE_PRICE_VIBESTRAP_STANDARD', creditsGranted:   1000 },
  { id: 'pro_monthly',       type: 'subscription', interval: 'monthly', priceCents:  999, priceIdEnv: 'STRIPE_PRICE_PRO_MONTHLY', creditsGranted:  1000 },
  { id: 'pro_yearly',        type: 'subscription', interval: 'yearly',  priceCents: 9900, priceIdEnv: 'STRIPE_PRICE_PRO_YEARLY',  creditsGranted: 12000 },
]
These render as a 2×2 grid in /settings/credits showing the four common pricing patterns. Replace prices, env names, and credit grants with whatever your buyer’s product charges. The default header and footer use the Sparkles icon from lucide-react. Replace it in two places:
// src/components/layout/header.tsx
import { Sparkles } from 'lucide-react';
// → swap for your own SVG component or <Image src="/logo.svg" .../>
<Sparkles className="size-5 text-primary" />
Same edit in src/components/layout/footer.tsx. For the OG image, replace public/og.png (1200x630, PNG or JPG).

Email templates

React Email templates live in src/mail/templates/:
  • verify-email.tsx — signup verification.
  • welcome.tsx — sent after the user verifies.
  • forgot-password.tsx — password reset link.
Edit them like any React component. Branding (siteConfig.name, siteConfig.mail.supportEmail) is already pulled in. To preview locally before the email-dev script ships, trigger the auth flow and watch the dev console for the rendered HTML.

Adding a locale

  1. Add the locale code to src/config/site.ts:
    i18n: { defaultLocale: 'en', locales: ['en', 'zh', 'ja'] }
    
  2. Create messages/ja.json mirroring the key structure of en.json.
  3. Translate every *.zh.mdx to *.ja.mdx for any docs you ship.
  4. Done. URLs become /ja/... automatically.

See also