code-architecture-tailwind-v4-best-practices

Guides Tailwind CSS v4 patterns for buttons and components. Use this skill when creating components with variants, choosing between CVA/tailwind-variants, or configuring Tailwind v4's CSS-first approach.

204 stars

Best use case

code-architecture-tailwind-v4-best-practices is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Guides Tailwind CSS v4 patterns for buttons and components. Use this skill when creating components with variants, choosing between CVA/tailwind-variants, or configuring Tailwind v4's CSS-first approach.

Teams using code-architecture-tailwind-v4-best-practices should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/code-architecture-tailwind-v4-best-practices/SKILL.md --create-dirs "https://raw.githubusercontent.com/flpbalada/my-opencode-config/main/skills/code-architecture-tailwind-v4-best-practices/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/code-architecture-tailwind-v4-best-practices/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How code-architecture-tailwind-v4-best-practices Compares

Feature / Agentcode-architecture-tailwind-v4-best-practicesStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Guides Tailwind CSS v4 patterns for buttons and components. Use this skill when creating components with variants, choosing between CVA/tailwind-variants, or configuring Tailwind v4's CSS-first approach.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Tailwind CSS v4: Best Practices

## Core Principle

**Use utilities directly in markup as the primary approach. Abstract with CVA/tailwind-variants only when you have 3+ variants.**

Tailwind v4's CSS-first configuration eliminates `tailwind.config.js` entirely. All configuration happens in CSS via `@theme` directive.

## The CSS-First Setup

```css
@import "tailwindcss";

@theme {
  --color-brand-primary: oklch(0.65 0.24 354.31);
  --color-brand-secondary: oklch(0.72 0.11 178);
  --font-sans: "Inter", sans-serif;
  --radius-button: 0.5rem;
}
```

Key v4 changes:
- Single `@import "tailwindcss"` replaces three `@tailwind` directives
- `--color-*` generates color utilities AND exposes as CSS variables
- Automatic template discovery (respects `.gitignore`)
- Oxide engine: 3.5x faster full builds, 8x faster incremental

## When to Abstract

### ✅ Use Pure Utilities When

- Component has 1-2 variants
- Prototyping or simple components
- Bundle size is critical (0KB overhead)

```tsx
// ✅ Simple button - no abstraction needed
<button className="
  inline-flex items-center justify-center gap-2
  px-4 py-2
  bg-blue-500 hover:bg-blue-600 active:bg-blue-700
  text-white text-sm font-medium
  rounded-md transition-colors
  focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500
  disabled:opacity-50 disabled:pointer-events-none
">
  Save Changes
</button>
```

### ✅ Use CVA When

- 3+ variants needed
- Type safety required
- Building component library
- ~1KB bundle cost acceptable

```typescript
import { cva, type VariantProps } from 'class-variance-authority';

const buttonVariants = cva(
  // Base classes
  "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        primary: "bg-blue-500 text-white hover:bg-blue-600",
        secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
        outline: "border-2 border-blue-500 text-blue-500 hover:bg-blue-50",
        ghost: "text-blue-500 hover:bg-blue-50"
      },
      size: {
        sm: "h-8 px-3 text-xs",
        md: "h-10 px-4 text-sm",
        lg: "h-12 px-6 text-base"
      }
    },
    defaultVariants: {
      variant: "primary",
      size: "md"
    }
  }
);

export type ButtonProps = VariantProps<typeof buttonVariants>;
```

### ✅ Use Tailwind-Variants When

- Responsive variants needed
- Multi-part/slot components (cards, accordions)
- Component composition via `extend`
- ~4KB bundle cost acceptable

```typescript
import { tv, type VariantProps } from 'tailwind-variants';

const card = tv({
  slots: {
    base: 'rounded-lg border bg-card shadow-sm',
    header: 'flex flex-col space-y-1.5 p-6',
    title: 'text-2xl font-semibold',
    content: 'p-6 pt-0',
    footer: 'flex items-center p-6 pt-0'
  },
  variants: {
    variant: {
      elevated: { base: 'shadow-xl' },
      flat: { base: 'shadow-none border' }
    }
  }
});

const { base, header, title, content, footer } = card({ variant: 'elevated' });
```

### ❌ Don't Use @apply

The Tailwind team discourages `@apply` except in narrow circumstances. Use component abstractions instead.

```css
/* ❌ Avoid - hides styling decisions, breaks variant support */
.btn-primary {
  @apply bg-blue-500 text-white px-4 py-2 rounded;
}

/* ✅ Use @utility for custom utilities if absolutely needed */
@utility btn-base {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
}
```

## Decision Matrix

| Approach | Bundle | Type Safe | Use Case |
|----------|--------|-----------|----------|
| Pure Tailwind | 0KB | ❌ | Simple, 1-2 variants, prototyping |
| CVA | ~1KB | ✅ | Component libraries, most projects |
| Tailwind-variants | ~4KB | ✅ | Complex design systems, slots |

## State Management with Data Attributes

V4 supports native data attributes for clean state management:

```tsx
export function Button({ isLoading, isDisabled, children }: ButtonProps) {
  return (
    <button
      data-loading={isLoading ?? ""}
      data-disabled={isDisabled ?? ""}
      className="
        bg-blue-500 text-white px-4 py-2 rounded
        hover:bg-blue-600
        data-loading:opacity-50 data-loading:cursor-wait
        data-disabled:opacity-50 data-disabled:pointer-events-none
      "
    >
      {isLoading && <Spinner className="mr-2" />}
      {children}
    </button>
  );
}
```

Custom variants via `@custom-variant`:

```css
@custom-variant selected-not-disabled (&[data-selected]:not([data-disabled]));
```

## Modern React Pattern (shadcn/ui style)

```tsx
import { tv, type VariantProps } from 'tailwind-variants';

const buttonStyles = tv({
  base: "inline-flex items-center justify-center rounded-md font-medium transition-colors",
  variants: {
    variant: {
      primary: "bg-blue-500 text-white hover:bg-blue-600",
      secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300"
    },
    size: {
      sm: "h-8 px-3 text-xs",
      md: "h-10 px-4 text-sm"
    }
  }
});

type ButtonProps = React.ComponentProps<"button"> &
  VariantProps<typeof buttonStyles>;

export function Button({ variant, size, className, ...props }: ButtonProps) {
  return (
    <button
      data-slot="button"
      className={cn(buttonStyles({ variant, size }), className)}
      {...props}
    />
  );
}
```

## Accessibility Checklist

```tsx
<button
  type="button"
  disabled={disabled || loading}
  aria-disabled={disabled || loading}
  aria-busy={loading}
  aria-label={ariaLabel}
  className={buttonStyles({ variant, size })}
>
  {loading && <Spinner aria-hidden="true" />}
  {leftIcon && <span data-slot="icon">{leftIcon}</span>}
  <span data-slot="label">{children}</span>
</button>
```

## Breaking Changes from v3

| v3 | v4 |
|----|-----|
| `shadow-sm` | `shadow-xs` |
| `rounded-sm` | `rounded-xs` |
| `bg-opacity-50` | `bg-black/50` |
| `bg-gradient-to-r` | `bg-linear-to-r` |
| `border` (gray-200 default) | `border` (currentColor) |
| `ring` (3px blue-500) | `ring-3` (currentColor) |

Automated migration: `npx @tailwindcss/upgrade`

## Quick Reference

### DO

- Use utilities directly for simple components
- Wait for **3+ variants** before using CVA/tailwind-variants
- Use **data attributes** for state management
- Follow **shadcn/ui patterns** for React components
- Use **@theme** for design tokens (generates utilities + CSS vars)

### DON'T

- Use `@apply` for component styles
- Abstract prematurely (same rule as code abstractions)
- Mix approaches inconsistently within a project
- Forget accessibility attributes on interactive elements

## Recommended Stack (2025)

- **React**: Next.js 15 + shadcn/ui + CVA + Tailwind v4
- **Vue**: Vue 3 + shadcn/vue + Tailwind v4
- **Bundle**: CVA (~1KB) + clsx (~0.2KB) + tailwind-merge (~7KB) ≈ 8KB total

## References

- [Tailwind CSS v4 Docs](https://tailwindcss.com/docs)
- [CVA (class-variance-authority)](https://cva.style/docs)
- [Tailwind Variants](https://www.tailwind-variants.org/)
- [shadcn/ui](https://ui.shadcn.com/)

Related Skills

typescript-best-practices

210
from flpbalada/my-opencode-config

Guides TypeScript best practices for type safety, code organization, and maintainability. Use this skill when configuring TypeScript projects, deciding on typing strategies, writing async code, or reviewing TypeScript code quality.

tailwind-v4-configuration

210
from flpbalada/my-opencode-config

Configure Tailwind CSS v4 with CSS-first approach. Use when installing, migrating from v3, setting up build tools (Vite/PostCSS/CLI), customizing themes with @theme, or configuring plugins.

code-architecture-wrong-abstraction

210
from flpbalada/my-opencode-config

Guides when to abstract vs duplicate code. Use this skill when creating shared utilities, deciding between DRY/WET approaches, or refactoring existing abstractions.

what-not-to-do-as-product-manager

210
from flpbalada/my-opencode-config

Anti-patterns and mistakes to avoid as a product manager. Use when evaluating leadership behaviors, improving team dynamics, reflecting on management practices, or onboarding new product managers.

visual-cues-cta-psychology

210
from flpbalada/my-opencode-config

Design effective CTAs using visual attention and gaze psychology principles. Use when designing landing pages, button hierarchies, conversion elements, or optimizing user attention flow through interfaces.

vercel-sandbox

210
from flpbalada/my-opencode-config

Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include "Vercel Sandbox browser", "microVM Chrome", "agent-browser in sandbox", "browser automation on Vercel", or any task requiring Chrome in a Vercel Sandbox.

value-realization

210
from flpbalada/my-opencode-config

Analyze if end users discover clear value. Use when evaluating product concepts, analyzing adoption, or uncertain about direction.

user-story-fundamentals

210
from flpbalada/my-opencode-config

Capture requirements from user perspective with structured user stories. Use when writing backlog items, defining acceptance criteria, prioritizing features, or communicating requirements between product and development.

typescript-satisfies-operator

210
from flpbalada/my-opencode-config

Guides proper usage of TypeScript's satisfies operator vs type annotations. Use this skill when deciding between type annotations (colon) and satisfies, validating object shapes while preserving literal types, or troubleshooting type inference issues.

typescript-interface-vs-type

210
from flpbalada/my-opencode-config

Guides when to use interface vs type in TypeScript. Use this skill when defining object types, extending types, or choosing between interface and type aliases.

typescript-advanced-types

210
from flpbalada/my-opencode-config

Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.

trust-psychology

210
from flpbalada/my-opencode-config

Build trust signals that reduce perceived risk and enable user action. Use when designing landing pages, checkout flows, onboarding experiences, or any conversion point where user hesitation is a barrier.