Files
heroui-inc--heroui/.claude/guides/tailwindcss-v4-css-guide.md
T
Junior Garcia 3c3f82432a Beta 1 [v3] (#5872)
* refactor: compound and named pattern (#5850)

* refactor(component): compound and named pattern

* refactor(component): tabs component naming consistency

* docs(changelog): v3.0.0-alpha-36

* fix(docs): correct broken storybook links (#5854)

* feat(components): checkbox (#5829)

* fix(component): support compound pattern in server component

* fix(component): keep accordion and chip in sync with compound pattern

* fix(component): type of forward refs

* fix(calendar): temporary workaround for ref error

* fix(storybook): imports

* feat(checkbox): initial draft

* refactor(styles): remove orientation

* refactor: migration from dot notation to separted components to support RSC

* refactor: migrate to React 19 ref pattern - remove forwardRef wrappers and explicit ref declarations from all components

* chore(checkbox): revise checkbox story

* feat(checkbox): checkmark & indeterminate

* chore(styles): checkmark & indeterminate styles

* refactor: provider context removed as it is not longer needed on react 19

* feat: made the migration smoother by still supporting the "dot" exports but adjusting the main compound component

* fix: compound patter, radio group api, ref on react 19

* refactor(switch): split switch and switch-group into separate components following radio/radio-group pattern

* chore(changelog): update v3.0.0-alpha.35 release notes and date

* fix(page): update version label to reflect RSC support

* chore(changelog): update examples to use new component names CardRoot and TabsRoot

* refactor(checkbox): adopt latest component structure

* chore: remove radio stories

* refactor(checkbox): adopt new api and revised stories

* refactor(checkbox-group): adopt new api and add stories

* chore(checkbox-group): revise examples

* chore(checkbox): separate checkbox group css

* chore(styles): revise checkbox styels

* fix(styles): add border for invalid

* feat(checkbox): add invalid story

* chore: sync changes from v3 redesign

* refactor(checkbox): remove slots

* refactor(checkbox): apply compound and named pattern

* feat(docs): checkbox page

* refactor(checkbox): remove use client

* chore(docs): revise based on the latest revamp

---------

Co-authored-by: Tianen Pang <32772271+tianenpang@users.noreply.github.com>
Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>

* V3 Redesign (#5851)

* fix(docs): update Link component to use Link.Root for consistency with new component structure

* feat: light theme in progress, button, spearator & accordion adjusted to new default theme

* feat: button and chip done

* feat: enhance Skeleton component with new Grid and Single Shimmer previews; update card and disclosure styles for consistency

* feat: update Kbd component styles and add Abbr support in stories

* feat: avatar and chip updated

* fix(accordion): adjust separator styling for consistency in accordion items

* refactor: close button adjsuted

* feat(link): add underline variants and offset options to Link component documentation and implementation

* feat(link): enhance link component with hover state and transition effects for icon opacity

* refactor: surfaces variables are now calculated by default, card, button, disclosures updated

* refactor: update styles for various components to improve consistency and reduce motion effects

* feat(storybook): add welcome story and update story order; feat(styles): include variants in package config and copy script

* refactor: update accordion, card, and kbd components to enhance styling and introduce new variants

* refactor(link): update hover underline effect to use opacity transition instead of scale transformation

* refactor(styles): update background color variables and remove gradient backgrounds from layout components

* refactor(storybook): enhance color stories by adding calculated color variants and removing deprecated soft colors

* refactor(styles): introduce background quaternary color and update shadow variables for popover and tooltip components

* feat: alert component created

* refactor(alert-demo, x-profile-demo): simplify button usage and enhance layout alignment

* feat: input otp

* feat: inpuit otp docs

* refactor(switch): enhance styling with improved transition timings and add box-shadow for active states

* feat: home page design in progress

* feat: add Surface component with context-based on-surface styling

- Add Surface component with four variants (default, secondary, tertiary, quaternary)
- Implement SurfaceContext to provide variant to child components
- Update Input, TextArea, InputOTP, and RadioGroup to automatically detect Surface context
- Components now apply on-surface styling when wrapped in Surface component
- Add OnSurface stories for all affected components
- Create comprehensive Surface documentation with examples
- Add OnSurface demo examples to all component documentation pages
- Update CSS classes to support on-surface modifier variants

* feat: add Surface component documentation and update meta.json

* feat(card): implement form handling in WithForm story using Form, Input, and Label components

* feat(card): refactor WithForm to use Form component for improved form handling and submission

* refactor(accordion): remove background class from stories and enhance AccordionRoot with SurfaceContext for elevated variant

* refactor(accordion): update variant from elevated to surface across components and documentation

* feat: add checkbox-group component and checkbox enhancements

* feat(demo): add UIComponentsDemo to the DemoComponents

* refactor: migrate Component.Root to Component in docs and demos

- Update all component documentation files to use Component instead of Component.Root
- Update all demo files to use simplified component syntax
- Update API reference headers from Component.Root Props to Component Props
- Exclude changelog files from migration as requested

This change promotes the simpler and more elegant Component syntax while
maintaining backward compatibility (both patterns are still supported).

* style: update global styles and card component

* feat(storybook): enhance color stories with new "On Surface" color variants and remove obsolete field color states

* feat(demos): update chip component props from 'type' to 'color' and add "use client" directive in multiple demo files

* feat(demos): add "use client" directive to basic button demo

* refactor: theme variables update background color in dark mode

* feat(demos): add "use client" directive to multiple demo files

* feat(demos): add FieldError component to TextfieldDemo for improved validation feedback

* fix(demo): update avatar images and names in avatar-group-demo for consistency

* feat: listbox component preview created and documented

* feat: selecte component created

* feat: export React Aria components from rac module

* feat: slider component created, documentation styles and content updated. Home page completed

* fix(demo): correct spelling of "credits" in AlertDemo component

* refactor(docs): simplify component usage by using compound pattern (#5870)

* fix(component): checkbox build errors

* refactor(docs): simplify component usage by using compound pattern

* fix(component): checkbox build errors

* chore(docs): correct pattern description

* feat(docs): enhance component preview with intersection observer for lazy loading

* feat(docs): enhance code block styling and functionality with isolated rendering option

* chore(root): upgrade package manager

* fix(docs): update image sources and enhance styling across various components

---------

Co-authored-by: Tianen Pang <32772271+tianenpang@users.noreply.github.com>

* fix(docs): update Twitter card image and adjust styles in route component for improved readability

* refactor(docs): updated color documentation

* fix(docs): update icon import in home page and remove unused client directive from Iconify component

* chore(docs): update documentation to reflect beta status and adjust storybook links for components

* feat: fix alert responsive

* feat(docs): replace img tags with next/image component for improved performance and consistency in home page and demo components

* fix: improve Lighthouse performance scores

- Replace picture/source elements with Next.js Image component
- Add fetchPriority='high' and loading='eager' to LCP images
- Fix viewport: remove userScalable: false and increase maximumScale to 5
- Add robots metadata with index: true for proper SEO
- Update sitemap URL in robots.txt

* fix: explicitly set X-Robots-Tag header to ensure proper indexing

- Add headers function in next.config.ts to set X-Robots-Tag: index, follow
- This ensures the header is explicitly set and prevents noindex issues

* fix(docs): update Link component styling in home page for improved visibility

* feat: card demos adjusted

* fix(docs): update introduction images for consistency and improved presentation

* fix(docs): update image sources in introduction and README for consistency

* fix(docs): update card component to include icon property for enhanced documentation

* fix(docs): update icon property for ListBox, Select, Slider, and MCP Server components to 'new' for improved documentation clarity

* fix(docs): update card demo layout to use flex-wrap for improved responsiveness

* feat(docs): implement changelog filtering in search API response

* feat: beta-1 changelog (draft)

* fix(docs): update version label in home page to reflect major redesign

* feat(docs): update changelog for v3-0-0-beta-1 with new components and improved API details

* fix(docs): correct formatting in changelog for v3-0-0-beta-1

* chore(docs): minor changes on v3 beta doc (#5879)

* chore(docs): update release date

* chore(docs): point to v3 component doc instead of v2

* chore(docs): add updated icon

* fix(input): typo

* refactor(styles): reduce motion (#5873)

* refactor(styles): reduce motion

* refactor(styles): reduce motion

* docs(handbook): disabling animations in animation section

* style(component): extend motion-reduce and adjust motion priority

* docs(handbook): disabling animations in animation section

* chore(docs): update design principles and changelog for v3-0-0-beta-1

* feat(docs): enhance HomePage and DocsImage components with static rendering and improved image handling

* fix(docs): update background colors for input, textarea, skeleton components and tabs in showcase

---------

Co-authored-by: Tianen Pang <32772271+tianenpang@users.noreply.github.com>
Co-authored-by: WK <wingkwong.code@gmail.com>
2025-11-06 11:08:05 -03:00

9.3 KiB

Tailwind CSS v4 Component CSS Writing Guide

This guide explains how to write CSS files for components using Tailwind CSS v4 syntax, based on the patterns used in HeroUI v3 components.

Table of Contents

  1. Basic CSS Syntax
  2. @apply Directive
  3. CSS Nesting
  4. Custom Properties & CSS Variables
  5. Pseudo-selectors and States
  6. Group and Peer Modifiers
  7. Media Queries and Responsive Design
  8. Component Patterns

Basic CSS Syntax

In Tailwind CSS v4, you can write standard CSS files that utilize Tailwind's utility classes through the @apply directive (though its usage has changed in v4).

Simple Component Example

.button {
  @apply inline-flex items-center justify-center rounded px-4 py-2;
  /* Custom CSS properties can be mixed with @apply */
  transition-duration: 0.2s;
  transition-property: color, background-color, border-color;
}

@apply Directive

The @apply directive allows you to compose Tailwind utility classes within your CSS. In v4, there are some changes to be aware of:

Basic Usage

.avatar {
  @apply relative flex size-10 shrink-0 overflow-hidden rounded-full;
}

.avatar-group {
  @apply flex overflow-hidden;
}

When to Use @apply

Use @apply for properties that have direct Tailwind utility equivalents:

.button {
  /* Use @apply for Tailwind utilities */
  @apply inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2 font-medium transition-colors duration-150;

  /* Keep custom CSS for properties without utilities */
  cursor: var(--cursor-interactive);

  /* Complex CSS functions stay as regular CSS */
  text-decoration-color: color-mix(in oklch, var(--link) 50%, transparent);
}

Animation with tw-animate-css

For animations, use tw-animate-css utilities with @apply:

/* Entering animations */
.tooltip[data-entering] {
  @apply animate-in zoom-in-90 fade-in-0 duration-200 ease-in-out;
}

/* Placement-specific animations */
.tooltip[data-entering][data-placement="top"] {
  @apply slide-in-from-bottom-1;
}

/* Exiting animations */
.tooltip[data-exiting] {
  @apply animate-out zoom-out-95 fade-out duration-150 ease-out;
}

Complex @apply Examples

/* Combine multiple utilities in one @apply */
.accordion__trigger {
  @apply hover:bg-default flex flex-1 items-center justify-between px-4 py-4 text-left font-medium;
}

/* Size modifiers with @apply */
.button--sm {
  @apply h-9 px-3 text-sm md:h-8;
}

/* Color variants */
.button--primary {
  @apply bg-accent text-accent-foreground hover:bg-accent-hover active:bg-accent-hover data-[pressed=true]:bg-accent-hover;
}

CSS Nesting

Tailwind CSS v4 has built-in nesting support (no postcss-nested plugin needed). You can nest selectors using the & symbol:

Basic Nesting

.avatar {
  @apply relative inline-flex;

  & > div {
    @apply block aspect-square overflow-hidden;
  }

  img {
    @apply h-full w-full object-cover;
  }
}

Nested Pseudo-classes

.avatar-online {
  &:before {
    content: "";
    @apply bg-success z-1 absolute block rounded-full;
    outline: 2px solid var(--color-base-100);
    width: 15%;
    height: 15%;
  }
}

Complex Nesting with :where()

.menu {
  :where(li ul) {
    @apply relative ms-4 whitespace-nowrap ps-2;

    &:before {
      @apply bg-base-content absolute bottom-3 start-0 top-3 opacity-10;
      width: var(--border);
      content: "";
    }
  }
}

Custom Properties & CSS Variables

Tailwind CSS v4 embraces CSS custom properties for theming and dynamic values:

Using CSS Variables

.button {
  --button-p: 1rem;
  --button-bg: var(--button-color, var(--color-base-200));
  --button-fg: var(--color-base-content);

  padding-inline: var(--button-p);
  color: var(--button-fg);
  background-color: var(--button-bg);
}

Dynamic Calculations

.checkbox {
  --size: calc(var(--size-selector, 0.25rem) * 6);
  width: var(--size);
  height: var(--size);
}

Pseudo-selectors and States

Hover States

.button {
  @media (hover: hover) {
    &:hover {
      --button-bg: color-mix(in oklab, var(--button-color, var(--color-base-200)), #000 7%);
    }
  }
}

Focus States

.button {
  &:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
  }
}

Complex State Combinations

.dropdown {
  &:not(details, .dropdown-open, .dropdown-hover:hover, :focus-within) {
    .dropdown-content {
      @apply hidden origin-top opacity-0;
      scale: 95%;
    }
  }
}

Group and Peer Modifiers

Group-based Styling

.avatar-group {
  @apply flex overflow-hidden;

  :where(.avatar) {
    @apply overflow-hidden rounded-full;
    border: 4px solid var(--color-base-100);
  }
}

Complex Group Selectors

.swap {
  input:is(:checked, :indeterminate) {
    & ~ .swap-off {
      @apply opacity-0;
    }
  }

  input:checked ~ .swap-on {
    @apply opacity-100;
  }
}

Media Queries and Responsive Design

Forced Colors Mode

.checkbox {
  &:checked {
    @media (forced-colors: active) {
      &:before {
        @apply rotate-0 bg-transparent [--tw-content:"✔︎"] [clip-path:none];
      }
    }
  }
}

Print Styles

.checkbox {
  @media print {
    &:before {
      @apply rotate-0 bg-transparent;
      --tw-content: "✔︎";
      clip-path: none;
    }
  }
}

Component Patterns

Size Variants

.button-xs {
  --fontsize: 0.6875rem;
  --button-p: 0.5rem;
  --size: calc(var(--size-field, 0.25rem) * 6);
}

.button-sm {
  --fontsize: 0.75rem;
  --button-p: 0.75rem;
  --size: calc(var(--size-field, 0.25rem) * 8);
}

Color Variants

.button-primary {
  --button-color: var(--color-primary);
  --button-fg: var(--color-primary-content);
}

.checkbox-success {
  @apply text-success-content;
  --input-color: var(--color-success);
}

State Modifiers

.button {
  &:is(:disabled, [disabled], .button-disabled) {
    &:not(.button-link, .button-ghost) {
      @apply bg-base-content/10;
      box-shadow: none;
    }
    @apply pointer-events-none;
  }
}

Advanced Selectors

.tab {
  &:is(.tab-active, [aria-selected="true"]):not(.tab-disabled, [disabled]),
  &:is(input:checked),
  &:is(label:has(:checked)) {
    background-color: var(--tab-bg);
  }
}

Best Practices

  1. Prioritize @apply for Tailwind Utilities: Use @apply for properties that have Tailwind equivalents

    .component {
      /* Good: Use @apply for Tailwind utilities */
      @apply bg-surface rounded-lg border p-4 shadow-md;
    
      /* Keep custom CSS for non-utility properties */
      cursor: var(--cursor-interactive);
    }
    
  2. Keep Focus States Consistent: Use custom outline styles for focus states

    .component {
      &:focus-visible {
        outline: 2px solid var(--focus);
        outline-offset: 2px;
      }
    }
    
  3. Use tw-animate-css for Animations: Leverage the tw-animate-css package for enter/exit animations

    .popover[data-entering] {
      @apply animate-in zoom-in-90 fade-in-0 duration-200 ease-out;
    }
    
    .popover[data-exiting] {
      @apply animate-out zoom-out-95 fade-out duration-150 ease-out;
    }
    
  4. Group Related Utilities: Combine multiple utilities in a single @apply statement

    /* Good: Single @apply with all utilities */
    .button {
      @apply relative inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2 font-medium transition-colors duration-150;
    }
    
    /* Avoid: Multiple @apply statements */
    .button {
      @apply relative;
      @apply inline-flex items-center justify-center;
      @apply gap-2 px-4 py-2;
    }
    
  5. Preserve Complex CSS Functions: Keep color-mix(), calc(), and other modern CSS functions as regular CSS

    .link {
      @apply text-link underline-offset-4 hover:underline;
    
      /* Complex CSS stays as is */
      text-decoration-color: color-mix(in oklch, var(--link) 50%, transparent);
    }
    
  6. Use Arbitrary Values When Needed: For specific values not in Tailwind's scale

    .component {
      @apply duration-[400ms] ease-[cubic-bezier(0.34,1.56,0.64,1)];
    }
    

Important Notes for Tailwind CSS v4

  1. Native CSS Nesting: v4 includes built-in CSS nesting support - no plugins needed
  2. Lightning CSS: v4 uses Lightning CSS under the hood for vendor prefixing and modern syntax transforms
  3. @apply Changes: The @apply directive behavior may vary in v4, especially in non-Vue projects
  4. CSS-First Configuration: v4 emphasizes configuring design tokens directly in CSS using @theme
  5. Modern CSS Features: v4 is built on cascade layers, @property, and color-mix()

Migration Tips

When creating components for Tailwind CSS v4:

  1. Start with the component's base styles using @apply
  2. Add CSS custom properties for dynamic values
  3. Use nesting for child elements and states
  4. Implement size and color variants through CSS variables
  5. Test thoroughly as @apply behavior may differ from v3

Remember that Tailwind CSS v4 is optimized for performance and modern CSS features, so embrace CSS custom properties and native CSS capabilities alongside Tailwind's utility classes.