Skip to main content
RapidDev - Software Development Agency
lovable-issues

Resolving CSS Conflicts in Lovable Components

CSS conflicts in Lovable happen when Tailwind utility classes clash with shadcn/ui component defaults or when custom styles have lower specificity than the design system. Use the cn() utility function from shadcn/ui to merge class names correctly — it handles Tailwind class conflicts by letting the last class win. Avoid using !important. When customizing shadcn/ui components, pass className props rather than overriding global CSS, and use the Design view → Themes tab for consistent color and spacing changes.

Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate9 min read~10 minAll Lovable versions (Tailwind CSS + shadcn/ui + Radix UI)March 2026RapidDev Engineering Team
TL;DR

CSS conflicts in Lovable happen when Tailwind utility classes clash with shadcn/ui component defaults or when custom styles have lower specificity than the design system. Use the cn() utility function from shadcn/ui to merge class names correctly — it handles Tailwind class conflicts by letting the last class win. Avoid using !important. When customizing shadcn/ui components, pass className props rather than overriding global CSS, and use the Design view → Themes tab for consistent color and spacing changes.

Why CSS conflicts arise between components in Lovable projects

Lovable projects use Tailwind CSS for utility-based styling and shadcn/ui (built on Radix UI) for pre-built components. These two systems work well together, but conflicts occur when Tailwind utility classes compete with the default styles baked into shadcn/ui components. The root cause is CSS specificity. When you add className="bg-blue-500" to a shadcn/ui Button, the button may still show its default background color because the component's internal styles have equal or higher specificity. Tailwind classes and component defaults are both single-class selectors, so the one defined later in the stylesheet wins. Since shadcn/ui styles are imported after Tailwind's base layer, they often override your custom utilities. The cn() utility function (included in every Lovable project at src/lib/utils.ts) solves this by intelligently merging Tailwind classes. It uses the tailwind-merge library under the hood, which knows that bg-blue-500 and bg-primary are conflicting classes and keeps only the last one. Without cn(), both classes exist in the DOM and the browser picks the winner based on stylesheet order, which is unpredictable.

  • Tailwind utility class overridden by shadcn/ui component default styles due to stylesheet order
  • Not using cn() to merge custom className with component defaults — both conflicting classes stay in the DOM
  • Global CSS in index.css or globals.css overriding component-level Tailwind classes
  • Tailwind's dark mode classes conflicting with the shadcn/ui theme system
  • Using !important to force styles, which then becomes impossible to override later

Error messages you might see

Custom className not applying to shadcn/ui component

The shadcn/ui component has a default class that conflicts with your custom class. Use cn() to merge them: cn('default-class', customClassName). The cn() function handles conflicts by keeping the last class.

Tailwind class bg-red-500 not working on Button component

The Button component has a default background defined in its variants. Override it by passing the variant='ghost' or variant='outline' prop to remove the default background, then apply your custom color class.

Styles look different in preview vs published site

CSS class order can differ between development and production builds. Use cn() consistently for all dynamic class merging to ensure the same class wins in both environments.

Before you start

  • A Lovable project using the default Tailwind CSS + shadcn/ui stack
  • Understanding that Lovable components are in src/components/ui/ and can be customized
  • The cn() utility function available at src/lib/utils.ts (included by default in all Lovable projects)

How to fix it

1

Use cn() to merge custom classes with component defaults

cn() uses tailwind-merge to resolve conflicting Tailwind classes, keeping only the last one specified

When you need to customize a shadcn/ui component's styling, use the cn() function to merge your custom className with the component's defaults. The cn() function is already available in every Lovable project at src/lib/utils.ts. Pass the default classes first and your custom classes last — cn() ensures your custom classes win when there is a conflict. Import cn from @/lib/utils in any component file.

Before
typescript
// Custom bg class ignored because component default wins
import { Button } from "@/components/ui/button";
<Button className="bg-red-500 hover:bg-red-600">
Delete
</Button>
{/* Button still shows the default primary background */}
After
typescript
// Using cn() inside the Button component definition to support overrides
// In src/components/ui/button.tsx:
import { cn } from "@/lib/utils";
const Button = ({ className, variant, ...props }) => {
return (
<button
className={cn(
buttonVariants({ variant }), // Default styles first
className // Custom styles last — cn() resolves conflicts
)}
{...props}
/>
);
};
// Usage — now bg-red-500 correctly overrides the default:
<Button className="bg-red-500 hover:bg-red-600">Delete</Button>

Expected result: The Button renders with a red background because cn() resolves the conflict between bg-primary (default) and bg-red-500 (custom) in favor of the last class.

2

Use component variants instead of overriding styles directly

shadcn/ui components have built-in variants designed to be customized without CSS conflicts

Before adding custom Tailwind classes, check if the shadcn/ui component has a variant that matches what you need. Button has variants like default, destructive, outline, secondary, ghost, and link. Using a variant gives you consistent styling that is already tested with the theme system. Only add custom classes when no variant matches your design.

Before
typescript
// Fighting the default variant with custom classes
<Button className="bg-transparent border border-gray-300 text-gray-700 hover:bg-gray-50">
Cancel
</Button>
{/* Many conflicting classes needed to override the default variant */}
After
typescript
// Using the outline variant — achieves the same look with one prop
<Button variant="outline">
Cancel
</Button>
{/* Clean, themeable, no class conflicts */}

Expected result: The button renders with an outline style that matches the app's theme. No custom CSS overrides needed.

3

Use Design view → Themes tab for global style changes

Changing theme values in one place updates all components consistently, avoiding per-component CSS overrides

For global style changes (primary color, border radius, font family, spacing), use Lovable's Design view instead of overriding individual components. Click the + button next to Preview, open Design view, and go to the Themes tab. Change the primary color, and every shadcn/ui component that uses the primary color updates automatically. This avoids the CSS conflicts that come from manually overriding individual components with different colors.

Before
typescript
/* Manually overriding every component's color */
.custom-button { @apply bg-blue-600; }
.custom-card { @apply border-blue-600; }
.custom-link { @apply text-blue-600; }
/* Inconsistent if you miss one component */
After
typescript
/* Using CSS variables set by the Themes tab */
/* In tailwind.config.ts, primary maps to a CSS variable: */
/* --primary: 220 90% 56%; (set via Design view → Themes) */
/* All components use the same primary automatically: */
<Button>Uses primary</Button>
<Card className="border-primary">Uses primary</Card>
<Link className="text-primary">Uses primary</Link>

Expected result: All components share the same primary color set in the theme. Changing the theme value in Design view updates everything at once.

4

Avoid !important and diagnose specificity issues properly

Using !important creates a specificity arms race that makes future style changes nearly impossible

If you find yourself reaching for !important, stop and investigate why the style is not applying. Open the browser DevTools (right-click → Inspect), find the element, and check the Computed tab to see which CSS rule is winning. Usually the fix is using cn() correctly, choosing the right component variant, or adjusting the stylesheet import order. If the style conflict spans multiple generated components and you cannot find the root cause, RapidDev's engineers have resolved CSS specificity battles across 600+ Lovable projects.

Before
typescript
/* !important arms race — each override needs more !important */
<div className="bg-white !important">
<Button className="!bg-red-500 !hover:bg-red-600 !text-white">
Delete
</Button>
</div>
{/* Fragile — breaks as soon as another style uses !important */}
After
typescript
/* Proper fix — use cn() and component variants */
<div className="bg-background">
<Button variant="destructive">
Delete
</Button>
</div>
{/* Uses the theme system — no !important needed */}

Expected result: The destructive button renders with the correct red styling from the theme. No !important declarations clutter the codebase.

Complete code example

src/components/StyledCard.tsx
1import { cn } from "@/lib/utils";
2import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
3import { Button } from "@/components/ui/button";
4import { Badge } from "@/components/ui/badge";
5
6interface StyledCardProps {
7 title: string;
8 description: string;
9 status: "active" | "inactive" | "pending";
10 className?: string;
11 onAction?: () => void;
12}
13
14// Example of conflict-free styling with cn() and variants
15const StyledCard = ({ title, description, status, className, onAction }: StyledCardProps) => {
16 // Map status to badge variants — no custom CSS needed
17 const badgeVariant = {
18 active: "default" as const,
19 inactive: "secondary" as const,
20 pending: "outline" as const,
21 }[status];
22
23 return (
24 <Card className={cn(
25 "transition-shadow hover:shadow-md", // Default styles
26 status === "inactive" && "opacity-60", // Conditional styles
27 className // Allow parent to override — cn() resolves conflicts
28 )}>
29 <CardHeader className="flex flex-row items-center justify-between">
30 <CardTitle className="text-lg">{title}</CardTitle>
31 <Badge variant={badgeVariant}>{status}</Badge>
32 </CardHeader>
33 <CardContent className="space-y-4">
34 <p className="text-muted-foreground">{description}</p>
35 {onAction && (
36 <Button
37 variant={status === "active" ? "default" : "outline"}
38 size="sm"
39 onClick={onAction}
40 >
41 {status === "active" ? "Deactivate" : "Activate"}
42 </Button>
43 )}
44 </CardContent>
45 </Card>
46 );
47};
48
49export default StyledCard;

Best practices to prevent this

  • Always use cn() from @/lib/utils to merge Tailwind classes — it resolves conflicts using tailwind-merge
  • Use shadcn/ui component variants (variant='destructive', variant='outline') before writing custom CSS overrides
  • Use Design view → Themes tab for global color, font, and spacing changes instead of per-component overrides
  • Never use !important in Tailwind classes — it creates an escalating specificity war that is hard to undo
  • Pass className as the last argument to cn() so custom classes override defaults, not the other way around
  • When customizing shadcn/ui components in src/components/ui/, always spread the className prop through cn()
  • Check browser DevTools → Computed tab to see which CSS rule is actually winning when a style conflict occurs
  • Keep global CSS in index.css or globals.css minimal — prefer Tailwind utilities and theme variables for all styling

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I'm having CSS conflicts in my Lovable (lovable.dev) project between my custom Tailwind classes and shadcn/ui component defaults. Here is the component where the conflict occurs: [paste your component code here] The issue: [describe which style is not applying — e.g., 'my bg-red-500 class is being overridden by the Button default background'] Please: 1. Explain why the CSS conflict is happening (specificity, class order, etc.) 2. Show the fix using cn() from @/lib/utils 3. Check if there's a shadcn/ui variant I should use instead of custom classes 4. Suggest whether I should modify the component in src/components/ui/ or use className overrides

Lovable Prompt

I have CSS conflicts where my custom Tailwind classes are not applying to shadcn/ui components. Please review @src/components/ui/button.tsx and ensure the className prop is being merged using cn() as the last argument so custom classes can override defaults. Then check @src/components/[MyComponent].tsx to make sure all class merging uses cn() instead of template literals or string concatenation.

Frequently asked questions

What is the cn() function in Lovable projects?

cn() is a utility function at src/lib/utils.ts that merges Tailwind CSS class names using clsx and tailwind-merge. It resolves conflicts by keeping the last conflicting class. For example, cn('bg-red-500', 'bg-blue-500') returns 'bg-blue-500'.

Why is my custom Tailwind class not applying to a shadcn/ui component?

The component's default styles may have equal specificity and appear later in the stylesheet. Use cn() to merge your custom className with the defaults, or check if there is a component variant (like variant='destructive') that matches your desired style.

Should I use !important to fix CSS conflicts in Lovable?

No. Using !important creates a specificity arms race that makes future style changes extremely difficult. Instead, use cn() for class merging, component variants for common style changes, and Design view → Themes tab for global theme modifications.

How do I change the primary color across my entire Lovable app?

Open Design view (click + next to Preview) → Themes tab and change the primary color. This updates the CSS variable that all shadcn/ui components use. Every button, link, and badge using the primary color updates automatically.

Can I edit shadcn/ui component files directly?

Yes. The shadcn/ui components are fully yours — they live in src/components/ui/ and can be customized. Edit them in Dev Mode or ask Lovable to modify them. Just make sure you keep the cn() className merging pattern so custom overrides still work.

How do I handle dark mode CSS conflicts?

Lovable uses CSS variables for theme colors, which automatically switch between light and dark mode. Use theme-aware classes like bg-background, text-foreground, and border-border instead of hardcoded colors like bg-white or text-black.

What if I can't resolve CSS conflicts myself?

CSS specificity issues that span multiple generated components can be difficult to untangle. RapidDev's engineers have resolved styling conflicts across 600+ Lovable projects and can restructure your styles for consistency.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your issue.

Book a free consultation

Need help with your Lovable project?

Our experts have built 600+ apps and can solve your issue fast. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.