seedflip
Archive
Mixtapes
Pricing
Sign in

shadcn/ui + Tailwind v4: What Broke and How to Fix It

You upgraded to Tailwind v4, and now your shadcn/ui components look wrong. Borders are invisible or too dark. Focus rings are thin. Some utilities don't do what they used to. This isn't a bug in your code. Tailwind v4 changed several defaults that shadcn/ui components rely on. Here is every breakage and the exact fix.

Generate a v4-compatible theme →

Border color default changed

What broke: Elements with the border utility suddenly have dark borders matching the text color instead of subtle gray borders.

Why: Tailwind v3 defaulted border color to gray-200. Tailwind v4 defaults to currentColor. Every element with border that didn't specify a color was getting a free light gray. Now it inherits the text color.

The fix: Add explicit border colors to your base styles or component classes:

/* Option 1: Global base style */ @layer base { *, ::after, ::before { border-color: hsl(var(--border)); } } /* Option 2: Add border-border to each component */ <Card className="border border-border">...</Card>

Option 1 restores the v3 behavior globally. It's the quickest fix for existing projects. New components should use explicit border colors anyway.

Ring width default changed

What broke: Focus rings look thinner than before. Buttons and inputs that used focus:ring now have barely visible 1px rings.

Why: In v3, the ring utility applied a 3px ring. In v4, it applies a 1px ring to align with the border utility convention.

The fix: Replace ring with ring-3 wherever you want the old width:

/* Before (v3 behavior) */ className="focus:ring focus:ring-ring" /* After (v4 equivalent) */ className="focus:ring-3 focus:ring-ring"

Check every shadcn component that uses focus styles. Button, Input, Select, and Textarea are the most common.

Ring offset removed

What broke: Components using ring-offset-background or ring-offset-2 might not work as expected.

Why: Tailwind v4 changed how rings are implemented under the hood. The ring-offset utilities still exist but the rendering mechanism uses outline instead of box-shadow in some contexts.

The fix: Test your focus styles visually. If the offset ring looks wrong, use outline utilities instead:

/* If ring-offset doesn't look right */ className="focus:outline-2 focus:outline-offset-2 focus:outline-ring"

Shadow utility syntax

What broke: Custom shadow values in your Tailwind config don't map to utilities the same way.

Why: In v4, shadow tokens use the --shadow- prefix in @theme. If you were using theme.extend.boxShadow in your JS config, those need to move.

The fix:

/* v3: tailwind.config.js */ boxShadow: { card: '0 1px 3px rgba(0,0,0,0.06)', elevated: '0 4px 12px rgba(0,0,0,0.1)', } /* v4: @theme block */ @theme { --shadow-card: 0 1px 3px rgba(0,0,0,0.06); --shadow-elevated: 0 4px 12px rgba(0,0,0,0.1); }

Color opacity modifiers behave differently

What broke: bg-primary/50 produces unexpected results or doesn't work at all.

Why: Tailwind v4 uses modern CSS color functions for opacity. If your color variable contains raw HSL channels (like 239 84% 67%), the v4 opacity modifier needs the full hsl() wrapper to decompose it correctly.

The fix: In your @theme block, wrap variables with the color function:

@theme { /* This works with opacity modifiers in v4 */ --color-primary: hsl(var(--primary)); /* Or use OKLCH for v4-native approach */ --color-primary: oklch(0.59 0.21 277); }

Space utilities use gap now

What broke: Components using space-x-* or space-y-* look different. Children that were previously spaced now might not be, or the spacing applies differently when elements wrap.

Why: v3 used the > * + * selector (sometimes called the "lobotomized owl"). v4 uses gap on a flex container. The visual result is similar for simple layouts but differs when children are conditionally rendered or when the parent isn't flex.

The fix: Make sure the parent element has flex or grid display. If you relied on the old selector behavior, switch to explicit gap-* utilities:

/* If space-y-4 doesn't work on a non-flex parent */ <div className="flex flex-col gap-4"> {children} </div>

Gradient syntax changes

What broke: Gradients using from-primary, via-accent, or to-background look different or don't render.

Why: Tailwind v4 uses the oklch color space for gradient interpolation by default, which produces smoother gradients than the old sRGB interpolation. If your colors are defined in HSL, the interpolation path is different.

The fix: The gradients still work. They just look slightly different (usually better). If you need to match the old appearance exactly, define gradient-specific colors in sRGB:

@theme { /* Force sRGB for specific gradient colors */ --color-gradient-start: rgb(99, 102, 241); --color-gradient-end: rgb(139, 92, 246); }

The @apply directive with custom utilities

What broke: @apply with custom color utilities from your config doesn't resolve.

Why: If you moved colors to @theme but the @apply rule appears before the @theme block in your CSS file, the token isn't registered yet.

The fix: Make sure @theme appears before any @apply rules that reference custom tokens. The order in your CSS file matters.

/* Correct order */ @import "tailwindcss"; @theme { --color-brand: #6366F1; } @layer base { .btn-brand { @apply bg-brand text-white; } }

Container query prefix

What broke: Container queries using the @container syntax work differently with Tailwind v4.

Why: Tailwind v4 has first-class container query support built in, replacing the old plugin. If you were using @tailwindcss/container-queries, remove it and use the native syntax.

/* v4: native container queries */ <div className="@container"> <div className="@sm:flex @md:grid @md:grid-cols-2"> {children} </div> </div>

Most of these breakages have a pattern: Tailwind v4 moved from implicit defaults to explicit declarations. The fixes are all two-line changes once you know what shifted. For the full color system migration story, see Tailwind v4 Color System. For a deep dive into the globals.css structure, read How to Customize shadcn/ui globals.css. Or skip the debugging. SeedFlip generates themes that work with both Tailwind v3 and v4 out of the box. Every export accounts for the default changes so you don't have to.

Ready to stop guessing?

One flip. Complete design system. Free CSS export.

Generate a v4-compatible theme →