Created
February 28, 2025 17:19
-
-
Save rmbrntt/3263c5873cd554246cd684d713910547 to your computer and use it in GitHub Desktop.
cursor/rules/tailwind.mdc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Tailwind CSS v4 Rules | |
<system_context> | |
You are an advanced Tailwind CSS v4 developer. You create modern, responsive, and accessible UIs following Tailwind CSS v4 best practices and leveraging its CSS-first approach. | |
</system_context> | |
<core_principles> | |
- **CSS-First**: Tailwind v4 uses a CSS-first approach with native CSS features | |
- **Utility-Based**: Build interfaces by composing utility classes directly in HTML | |
- **Constraints-Driven**: Work within a design system through theme variables | |
- **Mobile-First**: Start with mobile layouts and add complexity at larger breakpoints | |
- **Variant-Oriented**: Use variants to handle states, media queries, and selectors | |
</core_principles> | |
<css_essentials> | |
- Import Tailwind with standard CSS: `@import "tailwindcss";` | |
- Define theme variables with `@theme` directive, not JavaScript config | |
- Use native CSS cascade layers instead of custom `@layer` directives | |
- Create custom utilities with `@utility` instead of `@layer utilities` | |
- Create custom variants with `@custom-variant` | |
- Apply Tailwind variants to CSS with the `@variant` directive | |
- Import styles for reference (without duplication) using `@reference` | |
- All CSS is automatically vendor-prefixed, no autoprefixer needed | |
</css_essentials> | |
<theme_variables> | |
- Use `@theme` to define design tokens that generate corresponding utility classes: | |
```css | |
@import "tailwindcss"; | |
@theme { | |
/* These create color utilities like bg-brand, text-accent, etc. */ | |
--color-brand: #0284c7; | |
--color-accent: #7c3aed; | |
/* Creates font-display utility */ | |
--font-display: "Montserrat", sans-serif; | |
/* Creates text-xl utility */ | |
--text-xl: 1.25rem; | |
--text-xl--line-height: 1.75; | |
/* Creates breakpoint for responsive utilities */ | |
--breakpoint-3xl: 88rem; | |
/* Creates shadow-custom utility */ | |
--shadow-custom: 0 10px 15px -3px rgb(0 0 0 / 0.1); | |
} | |
``` | |
- Key namespace mappings that generate utilities: | |
- `--color-*`: All color utilities (bg, text, border, etc.) | |
- `--font-*`: Font family utilities (font-sans, font-display, etc.) | |
- `--text-*`: Font size utilities (text-sm, text-xl, etc.) | |
- `--font-weight-*`: Font weight utilities (font-bold, etc.) | |
- `--tracking-*`: Letter spacing utilities (tracking-wide, etc.) | |
- `--leading-*`: Line height utilities (leading-tight, etc.) | |
- `--breakpoint-*`: Responsive variants (sm:, md:, lg:, etc.) | |
- `--container-*`: Container query sizes (@sm, @lg, etc.) | |
- `--spacing-*`: Spacing and sizing utilities (p-4, m-6, etc.) | |
- `--radius-*`: Border radius utilities (rounded-lg, etc.) | |
- `--shadow-*`: Box shadow utilities (shadow-md, etc.) | |
- `--inset-shadow-*`: Inset shadow utilities (inset-shadow-xl, etc.) | |
- `--drop-shadow-*`: Drop shadow utilities (drop-shadow-lg, etc.) | |
- `--blur-*`: Blur utilities (blur-md, etc.) | |
- `--aspect-*`: Aspect ratio utilities (aspect-video, etc.) | |
- `--ease-*`: Transition timing utilities (ease-in-out, etc.) | |
- `--animate-*`: Animation utilities (animate-spin, etc.) | |
- Reference existing variables with `inline` option: | |
```css | |
@theme inline { | |
--color-primary: var(--color-blue-500); | |
} | |
``` | |
- Override existing theme variables by redefining them: | |
```css | |
@theme { | |
--color-blue-500: oklch(0.6 0.2 250); | |
} | |
``` | |
- Disable entire color scales with the asterisk syntax: | |
```css | |
@theme { | |
--color-yellow-*: initial; | |
--color-lime-*: initial; | |
} | |
``` | |
- Replace the entire default theme: | |
```css | |
@theme { | |
--*: initial; | |
/* Your complete custom theme here */ | |
} | |
``` | |
</theme_variables> | |
<css_functions> | |
- Use built-in CSS functions to work with theme values: | |
```css | |
/* Adjust opacity of colors */ | |
.custom-element { | |
background-color: --alpha(var(--color-blue-500) / 75%); | |
} | |
/* Use spacing scale values */ | |
.custom-element { | |
margin: --spacing(4); | |
padding: calc(--spacing(2) * 1.5); | |
} | |
``` | |
- Use these in arbitrary values too: | |
```html | |
<div class="m-[--spacing(4)] bg-[--alpha(var(--color-blue-500)/50%)]"> | |
<!-- Content --> | |
</div> | |
``` | |
</css_functions> | |
<utilities_and_variants> | |
- Utility classes are the primary way to style elements: | |
```html | |
<button class="rounded-lg bg-blue-500 px-4 py-2 font-medium text-white hover:bg-blue-600"> | |
Click me | |
</button> | |
``` | |
- Variants modify utilities based on conditions: | |
- State variants: `hover:`, `focus:`, `active:`, `disabled:`, etc. | |
- Responsive variants: `sm:`, `md:`, `lg:`, etc. | |
- Dark mode: `dark:` | |
- Print: `print:` | |
- Parent state: `group-hover:`, `peer-checked:`, etc. | |
- Media queries: `motion-safe:`, `contrast-more:`, etc. | |
- Variants stack left-to-right (not right-to-left as in v3): | |
```html | |
<!-- In v4 (correct) --> | |
<div class="dark:hover:bg-gray-800"> | |
<!-- Content --> | |
</div> | |
<!-- In v3 (outdated) --> | |
<div class="hover:dark:bg-gray-800"> | |
<!-- Content --> | |
</div> | |
``` | |
- Custom variants are created with `@custom-variant`: | |
```css | |
@custom-variant supports-grid (@supports (display: grid)); | |
@custom-variant theme-red (&:where([data-theme="red"], [data-theme="red"] *)); | |
``` | |
```html | |
<div class="flex supports-grid:grid"> | |
<!-- Will be a grid when grid is supported --> | |
</div> | |
``` | |
- Using variants with your own CSS: | |
```css | |
.custom-card { | |
background-color: white; | |
@variant dark { | |
background-color: #1f2937; | |
} | |
@variant hover { | |
transform: translateY(-2px); | |
} | |
} | |
``` | |
</utilities_and_variants> | |
<arbitrary_values> | |
- Square brackets for one-off values: | |
```html | |
<!-- Colors --> | |
<div class="bg-[#ff5500] text-[rgb(22,33,44)]"> | |
Custom colors | |
</div> | |
<!-- Sizes --> | |
<div class="w-[clamp(200px,50vw,600px)] h-[300px]"> | |
Custom dimensions | |
</div> | |
<!-- Complex values --> | |
<div class="grid-cols-[repeat(auto-fill,minmax(200px,1fr))]"> | |
Auto-filling grid | |
</div> | |
``` | |
- Use parentheses for CSS variables: | |
```html | |
<!-- Variables defined elsewhere --> | |
<div class="bg-(--brand-color) text-(--text-color)"> | |
Using CSS variables | |
</div> | |
<!-- With inline styles --> | |
<div style="--brand: blue; --accent: gold" | |
class="bg-(--brand) border-(--accent)"> | |
Inline variables | |
</div> | |
``` | |
- Arbitrary variants for complex selectors: | |
```html | |
<!-- Target specific child --> | |
<ul class="space-y-2 [&>li:nth-child(odd)]:bg-gray-100"> | |
<li>Item 1</li> | |
<li>Item 2</li> | |
</ul> | |
<!-- Media query in variant --> | |
<div class="[@supports(backdrop-filter:blur(8px))]:backdrop-blur"> | |
Conditional backdrop blur | |
</div> | |
``` | |
- Type hints for ambiguous values: | |
```html | |
<!-- Explicitly setting value type --> | |
<div class="text-(length:--custom-size)"> | |
With type hint | |
</div> | |
``` | |
</arbitrary_values> | |
<responsive_design> | |
- Mobile-first approach - smallest screen is the default: | |
```html | |
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> | |
<!-- Grid that starts with 1 column and expands --> | |
</div> | |
``` | |
- Default breakpoints: | |
- `sm`: 40rem (640px) | |
- `md`: 48rem (768px) | |
- `lg`: 64rem (1024px) | |
- `xl`: 80rem (1280px) | |
- `2xl`: 96rem (1536px) | |
- Custom breakpoints with `@theme`: | |
```css | |
@theme { | |
--breakpoint-xs: 20rem; /* 320px */ | |
--breakpoint-3xl: 120rem; /* 1920px */ | |
} | |
``` | |
- Targeting specific ranges with max-width variants: | |
```html | |
<div class="md:max-lg:grid-cols-2"> | |
<!-- 2 columns only between md and lg breakpoints --> | |
</div> | |
``` | |
- Arbitrary breakpoints: | |
```html | |
<div class="min-[320px]:text-sm min-[860px]:text-base"> | |
<!-- Text size changes at specific breakpoints --> | |
</div> | |
``` | |
- Container queries: | |
- Mark container with `@container` class | |
- Use `@sm`, `@md`, etc. for container-based sizing | |
```html | |
<div class="@container"> | |
<div class="grid grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-4"> | |
<!-- Grid responds to container size, not viewport --> | |
</div> | |
</div> | |
``` | |
- Container query sizes: | |
- `@3xs`: 16rem (256px) | |
- `@2xs`: 18rem (288px) | |
- `@xs`: 20rem (320px) | |
- `@sm`: 24rem (384px) | |
- `@md`: 28rem (448px) | |
- `@lg`: 32rem (512px) | |
- `@xl`: 36rem (576px) | |
- `@2xl`: 42rem (672px) | |
- `@3xl`: 48rem (768px) | |
- ... up to `@7xl`: 80rem (1280px) | |
- Named container contexts: | |
```html | |
<div class="@container/sidebar"> | |
<div class="@md/sidebar:hidden"> | |
<!-- Hidden when sidebar container is md or larger --> | |
</div> | |
</div> | |
``` | |
</responsive_design> | |
<dark_mode> | |
- Default dark mode uses `prefers-color-scheme`: | |
```html | |
<div class="bg-white text-gray-900 dark:bg-gray-900 dark:text-white"> | |
<!-- Content that adjusts to dark mode automatically --> | |
</div> | |
``` | |
- Custom dark mode implementation: | |
```css | |
@custom-variant dark (&:where(.dark, .dark *)); | |
``` | |
```html | |
<html class="dark"> | |
<body> | |
<!-- All dark: variants now activate based on .dark class --> | |
</body> | |
</html> | |
``` | |
- Using data attributes instead: | |
```css | |
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *)); | |
``` | |
```html | |
<html data-theme="dark"> | |
<!-- ... --> | |
</html> | |
``` | |
- Common dark mode patterns: | |
```html | |
<!-- Background/text shift --> | |
<div class="bg-white text-gray-900 dark:bg-gray-900 dark:text-white"> | |
<!-- Content --> | |
</div> | |
<!-- Inversions --> | |
<div class="shadow-lg dark:shadow-none dark:border dark:border-gray-700"> | |
<!-- Drop shadows often look wrong in dark mode, borders work better --> | |
</div> | |
<!-- Color shifts --> | |
<button class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-500"> | |
<!-- Brighter base color in dark mode --> | |
</button> | |
``` | |
</dark_mode> | |
<custom_utilities> | |
- Basic custom utility: | |
```css | |
@utility content-visibility-auto { | |
content-visibility: auto; | |
} | |
``` | |
- Utilities with pseudoelements: | |
```css | |
@utility scrollbar-hidden { | |
&::-webkit-scrollbar { | |
display: none; | |
} | |
scrollbar-width: none; | |
} | |
``` | |
- Functional utilities that accept values: | |
```css | |
@utility tab-* { | |
/* Accept matching theme values */ | |
tab-size: --value(--tab-size-*); | |
/* Accept integers */ | |
tab-size: --value(integer); | |
/* Accept arbitrary values */ | |
tab-size: --value([integer]); | |
} | |
``` | |
```html | |
<!-- Now you can use --> | |
<pre class="tab-4">...</pre> | |
<pre class="tab-[8]">...</pre> | |
``` | |
- Supporting modifiers: | |
```css | |
@utility text-* { | |
font-size: --value(--text-*, [length]); | |
line-height: --modifier(--leading-*, [length], [*]); | |
} | |
``` | |
```html | |
<p class="text-lg/relaxed"> | |
<!-- text-lg with line-height: var(--leading-relaxed) --> | |
</p> | |
``` | |
</custom_utilities> | |
<component_patterns> | |
- Abstract repetitive utility combinations into components: | |
```jsx | |
// React component | |
function Button({ variant = "primary", size = "md", children }) { | |
const variants = { | |
primary: "bg-blue-500 hover:bg-blue-600 text-white", | |
secondary: "bg-gray-200 hover:bg-gray-300 text-gray-900", | |
danger: "bg-red-500 hover:bg-red-600 text-white", | |
}; | |
const sizes = { | |
sm: "px-2 py-1 text-sm", | |
md: "px-4 py-2", | |
lg: "px-6 py-3 text-lg", | |
}; | |
return ( | |
<button className={`rounded-lg font-medium ${variants[variant]} ${sizes[size]}`}> | |
{children} | |
</button> | |
); | |
} | |
``` | |
- When using template languages, extract partial templates: | |
```html | |
<!-- _button.html --> | |
<button class="rounded-lg bg-blue-500 px-4 py-2 font-medium text-white hover:bg-blue-600"> | |
{{ label }} | |
</button> | |
<!-- Usage --> | |
{% include "_button.html" with label="Click me" %} | |
``` | |
- For simpler elements, use component CSS: | |
```css | |
@utility btn-primary { | |
border-radius: var(--radius-lg); | |
background-color: var(--color-blue-500); | |
padding-inline: --spacing(4); | |
padding-block: --spacing(2); | |
font-weight: var(--font-weight-medium); | |
color: var(--color-white); | |
@media (hover: hover) { | |
&:hover { | |
background-color: var(--color-blue-600); | |
} | |
} | |
} | |
``` | |
```html | |
<button class="btn-primary">Click me</button> | |
``` | |
</component_patterns> | |
<examples> | |
<example id="card_component"> | |
<description> | |
A responsive card component with dark mode support | |
</description> | |
<code language="html"> | |
<div class="overflow-hidden rounded-xl bg-white shadow-md outline outline-1 outline-black/5 dark:bg-gray-800 dark:outline-white/10"> | |
<div class="relative h-48 overflow-hidden"> | |
<img | |
src="/image.jpg" | |
alt="Card image" | |
class="absolute inset-0 h-full w-full object-cover" | |
/> | |
<div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent"></div> | |
<div class="absolute bottom-0 p-4"> | |
<h3 class="text-lg font-bold text-white">Card Title</h3> | |
<p class="text-sm text-white/80">Subtitle text</p> | |
</div> | |
</div> | |
<div class="p-4 sm:p-6"> | |
<p class="text-gray-700 dark:text-gray-300"> | |
This is a responsive card with dark mode support. It showcases modern Tailwind v4 | |
practices like outlines instead of borders, proper dark mode handling, and responsive padding. | |
</p> | |
<div class="mt-4 flex flex-wrap gap-2"> | |
<span class="inline-flex items-center rounded-full bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"> | |
Tag 1 | |
</span> | |
<span class="inline-flex items-center rounded-full bg-purple-100 px-2.5 py-0.5 text-xs font-medium text-purple-800 dark:bg-purple-900/30 dark:text-purple-300"> | |
Tag 2 | |
</span> | |
</div> | |
<div class="mt-6"> | |
<button class="inline-flex items-center rounded-lg bg-blue-500 px-4 py-2 text-sm font-medium text-white transition hover:bg-blue-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 dark:bg-blue-600 dark:hover:bg-blue-700"> | |
<svg class="mr-2 size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
<path stroke-linecap="round" stroke-linejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" /> | |
</svg> | |
View Details | |
</button> | |
</div> | |
</div> | |
</div> | |
</code> | |
<key_points> | |
- Uses modern Tailwind v4 syntax for sizing: `size-4` instead of `h-4 w-4` | |
- Proper outline usage with `outline outline-1 outline-black/5` | |
- Responsive padding with `p-4 sm:p-6` | |
- Proper dark mode with `dark:bg-gray-800 dark:outline-white/10` | |
- Gradient overlay with semi-transparent colors | |
- Focus visible treatment for buttons | |
- Uses `gap-2` for spacing between elements | |
</key_points> | |
</example> | |
<example id="form_inputs"> | |
<description> | |
Modern form inputs with validation states | |
</description> | |
<code language="html"> | |
<form class="space-y-6"> | |
<!-- Standard input --> | |
<div class="space-y-1"> | |
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300"> | |
</label> | |
<input | |
id="email" | |
type="email" | |
required | |
class="block w-full rounded-md border-0 px-3 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-500 invalid:ring-red-500 invalid:focus:ring-red-500 dark:bg-gray-800 dark:text-white dark:ring-gray-700 dark:placeholder:text-gray-500 dark:focus:ring-blue-400" | |
placeholder="[email protected]" | |
/> | |
<p class="hidden text-sm text-red-600 peer-invalid:block dark:text-red-400"> | |
Please enter a valid email address | |
</p> | |
</div> | |
<!-- Select input --> | |
<div class="space-y-1"> | |
<label for="country" class="block text-sm font-medium text-gray-700 dark:text-gray-300"> | |
Country | |
</label> | |
<select | |
id="country" | |
class="block w-full rounded-md border-0 px-3 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:ring-gray-700 dark:focus:ring-blue-400" | |
> | |
<option value="">Select a country</option> | |
<option value="us">United States</option> | |
<option value="ca">Canada</option> | |
<option value="mx">Mexico</option> | |
</select> | |
</div> | |
<!-- Checkbox with accessibility --> | |
<div class="flex items-center gap-x-3"> | |
<input | |
id="terms" | |
type="checkbox" | |
required | |
class="size-4 rounded border-0 text-blue-500 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:ring-gray-700" | |
/> | |
<label for="terms" class="text-sm text-gray-700 dark:text-gray-300"> | |
I agree to the | |
<a href="#" class="font-medium text-blue-600 hover:underline dark:text-blue-400"> | |
Terms and Conditions | |
</a> | |
</label> | |
</div> | |
<!-- Submit button with states --> | |
<div> | |
<button | |
type="submit" | |
class="flex w-full justify-center rounded-md bg-blue-500 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 active:bg-blue-700 disabled:opacity-50 dark:bg-blue-600 dark:hover:bg-blue-500" | |
> | |
Sign Up | |
</button> | |
</div> | |
</form> | |
</code> | |
<key_points> | |
- Modern form styling with ring utilities instead of borders | |
- Uses `peer-invalid:block` to show validation messages | |
- Handles dark mode for all input types | |
- Proper focus states with `focus:ring-2 focus:ring-inset` | |
- Consistent styling between different input types | |
- Accessible labels and proper contrast | |
- Proper disabled and active states for the button | |
</key_points> | |
</example> | |
<example id="navigation_menu"> | |
<description> | |
Responsive navigation with mobile dropdown | |
</description> | |
<code language="html"> | |
<!-- Main navigation --> | |
<nav class="border-b border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900"> | |
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> | |
<div class="flex h-16 justify-between"> | |
<!-- Logo and desktop navigation --> | |
<div class="flex"> | |
<div class="flex shrink-0 items-center"> | |
<img class="h-8 w-auto" src="/logo.svg" alt="Company Logo"> | |
</div> | |
<div class="hidden sm:ml-6 sm:flex sm:items-center sm:space-x-4"> | |
<a href="#" class="border-b-2 border-blue-500 px-3 py-5 text-sm font-medium text-gray-900 dark:text-white">Dashboard</a> | |
<a href="#" class="border-b-2 border-transparent px-3 py-5 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:text-gray-400 dark:hover:border-gray-600 dark:hover:text-gray-300">Team</a> | |
<a href="#" class="border-b-2 border-transparent px-3 py-5 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:text-gray-400 dark:hover:border-gray-600 dark:hover:text-gray-300">Projects</a> | |
<a href="#" class="border-b-2 border-transparent px-3 py-5 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:text-gray-400 dark:hover:border-gray-600 dark:hover:text-gray-300">Calendar</a> | |
</div> | |
</div> | |
<!-- Right side nav items and mobile menu button --> | |
<div class="flex items-center"> | |
<div class="hidden sm:ml-6 sm:flex sm:items-center"> | |
<!-- Profile dropdown button --> | |
<button type="button" class="flex rounded-full bg-white focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-gray-900"> | |
<img class="size-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="User profile"> | |
</button> | |
</div> | |
<!-- Mobile menu button --> | |
<div class="flex items-center sm:hidden"> | |
<button type="button" class="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-hidden focus:ring-2 focus:ring-inset focus:ring-blue-500 dark:hover:bg-gray-800 dark:hover:text-gray-300" aria-controls="mobile-menu" aria-expanded="false"> | |
<span class="sr-only">Open main menu</span> | |
<!-- Icon when menu is closed --> | |
<svg class="block size-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> | |
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /> | |
</svg> | |
<!-- Icon when menu is open --> | |
<svg class="hidden size-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> | |
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> | |
</svg> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Mobile menu --> | |
<div class="hidden sm:hidden" id="mobile-menu"> | |
<div class="space-y-1 px-2 pb-3 pt-2"> | |
<a href="#" class="block rounded-md bg-blue-50 px-3 py-2 text-base font-medium text-blue-700 dark:bg-blue-900/50 dark:text-blue-300">Dashboard</a> | |
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-white">Team</a> | |
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-white">Projects</a> | |
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-white">Calendar</a> | |
</div> | |
<div class="border-t border-gray-200 pb-3 pt-4 dark:border-gray-700"> | |
<div class="flex items-center px-5"> | |
<div class="shrink-0"> | |
<img class="size-10 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="User profile"> | |
</div> | |
<div class="ml-3"> | |
<div class="text-base font-medium text-gray-800 dark:text-white">Tom Cook</div> | |
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">[email protected]</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</nav> | |
</code> | |
<key_points> | |
- Complete responsive navigation with mobile dropdown | |
- Uses `hidden sm:flex` pattern for responsive visibility | |
- Proper active state with border indicator | |
- Accessible markup with aria attributes | |
- Dark mode support throughout | |
- Modern focus treatment with `focus:outline-hidden focus:ring-2` | |
- Uses size-* utility for width and height (new in v4) | |
</key_points> | |
</example> | |
<example id="container_queries"> | |
<description> | |
Layout with advanced container queries | |
</description> | |
<code language="html"> | |
<div class="mx-auto max-w-7xl p-4 lg:p-8"> | |
<h2 class="mb-6 text-2xl font-bold text-gray-900 dark:text-white">Dashboard Components</h2> | |
<!-- Grid layout with container query responsive components --> | |
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4"> | |
<!-- Revenue card with container queries --> | |
<div class="@container rounded-xl bg-white p-6 shadow-md dark:bg-gray-800"> | |
<div class="flex items-center justify-between"> | |
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Revenue</h3> | |
<span class="rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900/30 dark:text-green-300">+12%</span> | |
</div> | |
<!-- Stats that change layout based on container width --> | |
<div class="mt-4 flex @xs:flex-row @max-xs:flex-col gap-4"> | |
<div class="@max-xs:border-b @max-xs:border-gray-200 @max-xs:pb-4 @xs:border-r @xs:border-gray-200 @xs:pr-4 dark:@max-xs:border-gray-700 dark:@xs:border-gray-700"> | |
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Today</p> | |
<p class="mt-1 text-2xl font-semibold text-gray-900 dark:text-white">$12,426</p> | |
</div> | |
<div> | |
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">This Month</p> | |
<p class="mt-1 text-2xl font-semibold text-gray-900 dark:text-white">$192,556</p> | |
</div> | |
</div> | |
<!-- Chart changes height based on container size --> | |
<div class="mt-6 @xs:h-40 @sm:h-48 @md:h-56 rounded bg-gray-100 dark:bg-gray-700"> | |
<!-- Chart would go here --> | |
</div> | |
</div> | |
<!-- Users card with container queries --> | |
<div class="@container rounded-xl bg-white p-6 shadow-md dark:bg-gray-800"> | |
<div class="flex items-center justify-between"> | |
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Active Users</h3> | |
<div> | |
<!-- Dropdown is shown at larger container widths --> | |
<button class="@max-xs:hidden rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-gray-300"> | |
<span class="sr-only">View options</span> | |
<svg class="size-5" viewBox="0 0 20 20" fill="currentColor"> | |
<path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" /> | |
</svg> | |
</button> | |
</div> | |
</div> | |
<div class="mt-6"> | |
<div class="flex items-center"> | |
<span class="text-3xl font-bold text-gray-900 dark:text-white">2,340</span> | |
<span class="ml-2 text-sm font-medium text-green-600 dark:text-green-400">+11.3%</span> | |
</div> | |
<!-- User list with container query responsive design --> | |
<div class="mt-6 space-y-3 @md:space-y-0 @md:grid @md:grid-cols-2 @md:gap-4"> | |
<div class="flex items-center"> | |
<img class="size-8 rounded-full" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="User"> | |
<div class="ml-3"> | |
<p class="text-sm font-medium text-gray-900 dark:text-white">Leslie Alexander</p> | |
<p class="text-xs text-gray-500 dark:text-gray-400">Last active 12m ago</p> | |
</div> | |
</div> | |
<div class="flex items-center"> | |
<img class="size-8 rounded-full" src="https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="User"> | |
<div class="ml-3"> | |
<p class="text-sm font-medium text-gray-900 dark:text-white">Michael Foster</p> | |
<p class="text-xs text-gray-500 dark:text-gray-400">Online now</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Two more cards would go here --> | |
</div> | |
</div> | |
</code> | |
<key_points> | |
- Uses @container to create container query context | |
- Container query variants like @xs, @sm, @md for responsive changes | |
- Bi-directional variants like @max-xs for specific size ranges | |
- Adaptive layouts within cards based on available space | |
- Container query-based grid layouts within components | |
- Effective handling of borders in different container sizes | |
- Advanced containment isolation with component-based responsive design | |
</key_points> | |
</example> | |
<example id="theme_customization"> | |
<description> | |
Custom theme with overrides and new values | |
</description> | |
<code language="css"> | |
@import "tailwindcss"; | |
/* Define design tokens */ | |
@theme { | |
/* Override existing variables */ | |
--color-slate-50: oklch(0.985 0.003 250); | |
--color-slate-900: oklch(0.2 0.04 260); | |
/* Define new brand colors */ | |
--color-primary: oklch(0.65 0.29 230); | |
--color-primary-light: oklch(0.75 0.25 230); | |
--color-primary-dark: oklch(0.55 0.31 230); | |
--color-secondary: oklch(0.75 0.18 130); | |
/* Custom typography */ | |
--font-brand: "Montserrat", sans-serif; | |
--text-display: 2.5rem; | |
--text-display--line-height: 1.2; | |
/* Custom spacing */ | |
--spacing-custom: 1.75rem; | |
/* Custom shadows */ | |
--shadow-elevated: 0 10px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); | |
/* Custom animations */ | |
--animate-slide-in: slide-in 0.3s ease-out; | |
@keyframes slide-in { | |
0% { | |
opacity: 0; | |
transform: translateY(10px); | |
} | |
100% { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
} | |
/* Custom utility for a promo section */ | |
@utility promo-section { | |
border-radius: var(--radius-xl); | |
background-color: var(--color-primary-light); | |
padding: --spacing(8); | |
color: white; | |
box-shadow: var(--shadow-elevated); | |
} | |
/* Custom variant for overlapping elements */ | |
@custom-variant overlapped (&:not(:first-child)); | |
/* Component styles */ | |
.brand-card { | |
border-radius: var(--radius-lg); | |
background-color: white; | |
padding: --spacing(6); | |
box-shadow: var(--shadow-md); | |
@variant dark { | |
background-color: var(--color-slate-900); | |
box-shadow: none; | |
border: 1px solid var(--color-slate-800); | |
} | |
@variant hover { | |
transform: translateY(-2px); | |
box-shadow: var(--shadow-elevated); | |
@variant dark { | |
border-color: var(--color-primary); | |
} | |
} | |
} | |
</code> | |
<usage> | |
<!-- Using the custom theme --> | |
<section class="promo-section"> | |
<h2 class="font-brand text-display">Welcome to our platform</h2> | |
<p class="mt-4">Using our custom theme for consistent branding</p> | |
</section> | |
<div class="mt-custom grid grid-cols-1 md:grid-cols-3 gap-4"> | |
<div class="animate-slide-in brand-card">Card 1</div> | |
<div class="animate-slide-in brand-card overlapped:mt-8 md:overlapped:mt-0">Card 2</div> | |
<div class="animate-slide-in brand-card overlapped:mt-8 md:overlapped:mt-0">Card 3</div> | |
</div> | |
</usage> | |
<key_points> | |
- Comprehensive theme customization with @theme | |
- Custom variables in each namespace (color, font, spacing, etc.) | |
- Custom animation with @keyframes defined in @theme | |
- Custom utility created with @utility | |
- Custom variant created with @custom-variant | |
- Combined variants with @variant in CSS components | |
- Reference to theme variables in component CSS | |
</key_points> | |
</example> | |
</examples> | |
<best_practices> | |
- Embrace utility classes for all styling when possible | |
- Work within the constraints of your theme for consistent design | |
- Extract components for reusable patterns, not for DRY CSS | |
- Always consider dark mode when building interfaces | |
- Start with mobile layouts first, then add complexity at larger screens | |
- Use container queries for component-based responsive design | |
- Only use arbitrary values for true one-offs, not for repetitive values | |
- Prefer @theme variables over hardcoded values | |
- Use modern alternative CSS like OKLCH for colors when possible | |
- Use size-* utilities instead of w-* h-* pairs when elements are square | |
- Keep arbitrary selectors ([&:nth-child(odd)]) as a last resort | |
- Prefer proper focus handling with focus-visible and outline/ring | |
- Consider print: styles for documents that may be printed | |
- Use the CSS functions like --alpha() and --spacing() for calculations | |
</best_practices> | |
<performance_tips> | |
- Use standard HTML elements when possible instead of custom components | |
- Reuse utility patterns with components or templates to avoid duplication | |
- Use purging with `source()` to ensure only used utilities are included | |
- Enable just-in-time (JIT) mode to optimize CSS output | |
- Keep arbitrary values to a minimum as they increase CSS size | |
- Use container queries instead of media queries for reusable components | |
- Organize complex selectors with variants instead of custom CSS | |
- Combine small icons and common SVGs into an icon system | |
- Structure your CSS imports efficiently with the @reference directive | |
- Prefer utility classes over @apply when possible for better performance | |
</performance_tips> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment