v0.dev's 9 Default Patterns (And How to Override Each One)
v0 generates working React fast. It also generates the same shadcn tokens, Inter font, and 3-card grid in every output. Here are the 9 patterns to override and the exact CSS to do it.
v0 is genuinely useful. It turns a text prompt into working React faster than any other tool. The catch is that v0 outputs the same design tokens every time: shadcn/ui defaults, Inter font, blue-indigo primary, rounded-lg border radius, 3-column feature grid.
These are not bad choices. They are Vercel's design system defaults, and they are technically solid. The problem is that every developer using v0 gets the same tokens. Two hundred v0 projects later, your landing page is recognizable at a glance as a v0 output.
Here are the 9 most common v0 defaults and the exact code to override each one.
Pattern 1: Inter as the only font
/* What v0 outputs in globals.css */
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
/* applied to both headings and body */The problem: Inter is the most common typeface on the web. Using it alone, with no display font for headings, produces text that looks like a settings page, not a product.
The fix:
/* Override in globals.css */
@import url('https://fonts.bunny.net/css?family=playfair-display:400,700,800&family=karla:400,500,600');
:root {
--font-display: 'Playfair Display', Georgia, serif;
--font-sans: 'Karla', system-ui, sans-serif;
}
h1, h2, h3 { font-family: var(--font-display); }
body, p, li, td { font-family: var(--font-sans); }Other non-AI display fonts: Fraunces, Bitter, Cormorant Garamond, Vollkorn, Bricolage Grotesque, Archivo Black.
Pattern 2: Primary color in the AI band (hue 200-290)
/* What v0 outputs */
--primary: 221.2 83.2% 53.3%; /* hsl — maps to #3B82F6 */
/* Alternative: */
--primary: 262.1 83.3% 57.8%; /* purple — maps to #7C3AED */The problem: v0 picks from a narrow slice of the color wheel. Every site built with v0 defaults looks like it shares a brand with every other v0 site.
The fix: Pick a hue outside 200-290. That band is entirely off-limits if you want to look distinctive.
:root {
--primary: 347 72% 48%; /* deep rose */
/* or: */
--primary: 22 85% 52%; /* burnt orange */
/* or: */
--primary: 156 60% 38%; /* forest green */
/* or: */
--primary: 320 65% 44%; /* magenta */
}Pattern 3: rounded-lg on everything
/* What v0 outputs — same radius everywhere */
<Button className="rounded-lg">...</Button>
<Card className="rounded-lg">...</Card>
<Input className="rounded-lg">...</Input>
<Badge className="rounded-lg">...</Badge>The problem: When every element shares the same corner radius, the page looks like it was assembled from a kit. Border radius is a visual tool — using it uniformly wastes it.
The fix: Two independent radius values, one for interactive elements and one for containers.
:root {
--radius: 0.25rem; /* default (overrides shadcn) */
--radius-card: 0; /* sharp cards — editorial feel */
}
/* Or the inverse: */
:root {
--radius: 0.5rem; /* buttons rounded */
--radius-card: 1rem; /* cards very rounded */
}Never set both to the same value.
Pattern 4: 3-column feature grid with equal columns
/* What v0 generates for features */
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<FeatureCard />
<FeatureCard />
<FeatureCard />
</div>The problem: Three identical columns signal template. Users recognize this as "the features section" before they read a word. That recognition stops them from reading.
The fix: Asymmetric layout with the main feature given more space.
/* Asymmetric grid — the primary feature is larger */
<div style={{ display: 'grid', gridTemplateColumns: '5fr 3fr', gap: '3rem 4rem' }}>
<div>
{/* Primary feature — full description */}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
{/* 2-3 secondary features stacked */}
</div>
</div>Alternatively, use a definition list (dl/dt/dd) instead of cards. More semantic, less template-looking.
Pattern 5: Centered hero with eyebrow badge
/* What v0 always generates */
<section className="text-center py-24">
<Badge>New feature</Badge>
<h1>The Future of Your Workflow</h1>
<p className="max-w-2xl mx-auto">AI-powered platform...</p>
<div className="flex justify-center gap-4">
<Button>Get Started</Button>
<Button variant="outline">Learn More</Button>
</div>
</section>The problem: This is the single most replicated hero pattern on the web in 2026. The eyebrow badge + centered h1 + centered subtitle + two-button group is recognizable at a glance as AI-generated.
The fix: Left-aligned text with offset layout. No eyebrow badge. No centering.
<section style={{ paddingBlock: '120px 80px', paddingLeft: '13%' }}>
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(3rem, 8vw, 6rem)', lineHeight: 1.05, textWrap: 'balance' }}>
Your site looks like<br />every other AI site.
</h1>
<p style={{ marginTop: '2rem', maxWidth: '480px', fontSize: '18px', lineHeight: 1.7 }}>
Sailop detects the patterns and rewrites them.
</p>
<a href="/install" style={{ marginTop: '3rem', display: 'inline-block' }}>
npx sailop install →
</a>
</section>Pattern 6: Fade-up animation on everything
/* What v0 generates (usually via a custom hook) */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-in { animation: fadeInUp 0.3s ease-in-out; }The problem: Fade-up is the #1 AI animation default. Applied to every section, it reads as a library effect rather than a design choice. Uniform duration (300ms) makes it worse.
The fix: Different animations per component type.
/* Headline: reveal from left */
@keyframes clipReveal {
from { clip-path: inset(0 100% 0 0); }
to { clip-path: inset(0 0 0 0); }
}
h1 { animation: clipReveal 0.7s cubic-bezier(0.16, 1, 0.3, 1); }
/* Features: slide from left with stagger */
@keyframes slideLeft {
from { opacity: 0; transform: translateX(-24px); }
to { opacity: 1; transform: translateX(0); }
}
.feature:nth-child(1) { animation: slideLeft 0.5s 0.1s both; }
.feature:nth-child(2) { animation: slideLeft 0.5s 0.25s both; }
.feature:nth-child(3) { animation: slideLeft 0.5s 0.4s both; }
/* CTA: scale up */
@keyframes scaleUp {
from { opacity: 0; transform: scale(0.94); }
to { opacity: 1; transform: scale(1); }
}
.cta { animation: scaleUp 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); }Pattern 7: Pure white background + pure black text
/* What v0 outputs */
--background: 0 0% 100%; /* #ffffff */
--foreground: 222.2 84% 4.9%; /* near-black */The problem: Pure white reads as default. Pure black text on pure white background has higher contrast than needed and looks unfinished.
The fix: Slightly warm white with two shades of text.
:root {
--background: 35 20% 96%; /* warm off-white */
--foreground: 220 15% 12%; /* dark blue-grey, not pure black */
--muted-foreground: 220 10% 40%; /* secondary text */
}Pattern 8: "Get Started" and "Learn More" CTAs
The problem: These are the two most common CTA strings on the web. They communicate nothing about what will happen when clicked.
The fix: Specific action language.
| Replace | With | |---------|------| | "Get Started" | "Scan your code" / "npx sailop install" | | "Learn More" | "See how it works" / "Read the docs" | | "Start Free Trial" | "Try it free — no card" | | "Book a Demo" | "See a 3-minute walkthrough" | | "Sign Up Now" | "Create account" |
The rule: say what happens when you click it.
Pattern 9: Uniform shadow values
/* What v0 applies to cards */
box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
/* shadcn shadow-sm — used on 90% of v0 cards */The problem: When every card has the same shadow, the visual hierarchy collapses. Shadows are depth signals — using one value for everything is like speaking in a monotone.
The fix: Vary shadow by elevation level or remove it and use borders instead.
/* Two-level shadow system */
.card-low { box-shadow: 0 1px 2px rgba(0,0,0,0.06); }
.card-raised { box-shadow: 0 4px 12px rgba(0,0,0,0.12); }
/* Or: no shadow, use border */
.card { border: 1px solid hsl(220, 15%, 88%); box-shadow: none; }
/* Or: directional shadow (looks designed) */
.card { box-shadow: 3px 3px 0 hsl(347, 72%, 48%); }Run it through Sailop
The fastest way to catch all nine patterns in a v0 project is to scan it:
sailop scan ./srcEach of the nine patterns maps to a specific Sailop rule. The scan will tell you exactly which files and which lines are affected, with a score for each dimension. Fix the F scores first. Most v0 projects score worst on color and typography.
The transform command rewrites all nine automatically and generates a unique design system for your project.
SHIP CODE THAT LOOKS INTENTIONAL
Scan your frontend for AI patterns. Generate a unique design system. Stop shipping the same blue gradient as everyone else.