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.

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',
  ogImage: '/og.png',
  links: {
    github: 'https://github.com/yourorg/yourrepo',
    twitter: 'https://twitter.com/yourhandle',
    discord: 'https://discord.gg/yourchannel',
    contact: 'mailto:[email protected]',
  },
  mail: {
    fromName: 'YourProduct',
    supportEmail: '[email protected]',
  },
};
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

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.
:root {
  --primary: oklch(0.205 0 0);          /* near-black, neutral */
  --primary-foreground: oklch(0.985 0 0);
  --background: oklch(1 0 0);
  /* ...complete tokens in globals.css */
}

.dark {
  --primary: oklch(0.985 0 0);          /* invert for dark mode */
  /* ... */
}
Pick a brand hue with the OKLCH picker, then sweep through --primary, --primary-foreground, --accent, --ring, and --chart-{1..5}. shadcn/ui components consume these tokens by name, so a single edit re-skins everything.

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):
plans: { free, pro, lifetime },
creditPacks: [ basic, standard, premium, enterprise ],
These render in /settings/billing and /settings/credits. Replace the sample values 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