Compare commits

..

22 Commits

Author SHA1 Message Date
zguiyang
965ae613f8 refactor(all): update branding from "Joy Zhao" to "Joey Zhao" across site content and metadata 2026-03-14 14:59:59 +08:00
zguiyang
4fa8213e8f refactor(home): modernize layout, spacing, and component styles
- Reduced section spacing and adjusted minimum height for improved visual balance.
- Updated card designs, headlines, and typography for better readability and flow.
- Simplified styles and reorganized structure for enhanced maintainability.
- Refined call-to-action area with improved button styling and responsiveness.
2026-03-14 14:29:12 +08:00
zguiyang
e677c03e26 refactor(header): replace static image icon with SVG and update gradient text style
- Replaced hardcoded image icon in `GlassHeader` with inline SVG for better scalability and customization.
- Updated `.gradient-text` to use `--ui-title-gradient` variable for improved design consistency.
2026-03-14 12:45:32 +08:00
zguiyang
043cdc6734 refactor(styles): enhance glass effects, card designs, and transitions across components
- Replaced `client:only="react"` with `client:load transition:persist="header"` for `GlassHeader` to improve page transitions and header consistency.
- Updated card and surface designs with refined colors, borders, and shadows for a cleaner, modern look.
- Adjusted CSS variables for better contrast and distinct layers.
- Applied consistent blur and backdrop styles across components for visual cohesion.
2026-03-14 12:43:11 +08:00
zguiyang
56aac2f9f2 refactor(header): replace emoji icons with Lucide React components and enhance navigation structure
- Introduced `navItems` array with icons and links for streamlined navigation rendering.
- Added `isActive` function to handle active state for improved user feedback.
- Refined desktop and mobile navigation with updated styles and scalable icon usage.
- Removed hardcoded emoji-based navigation for consistency with other components.
2026-03-14 12:22:43 +08:00
zguiyang
621ac38369 refactor(about): enhance design and structure for socials, contact, and skills sections
- Reorganized contact and socials into dynamic configurations with expanded icons and link support.
- Improved layout for skills and interests sections with responsive spacing and modernized card styles.
- Added reusable `IconMap` for consistent icon handling across components.
- Updated translations for better localization and scalability.
2026-03-14 12:11:48 +08:00
zguiyang
a8ab7e6811 refactor(about): modernize and unify About pages with improved design and translations
- Replaced `.mdx` files for `about` pages with `.astro` format to streamline layout and enhance flexibility.
- Integrated `GlassHeader`, `HighlightBox`, and other reusable components for consistency.
- Enhanced design with dynamic backgrounds, gradients, and interactive hover effects.
- Updated i18n translations to support structured nested keys for improved localization and scalability.
- Refined contact and skills sections with responsive layouts and clearer organization.
2026-03-14 12:05:23 +08:00
zguiyang
8ff4074c1b refactor(hire): replace emoji icons with Lucide React components for improved scalability and consistency 2026-03-14 12:00:42 +08:00
zguiyang
a181dd7673 refactor(projects): add type annotation to applyFilter function for improved clarity 2026-03-14 11:42:29 +08:00
zguiyang
2474d51e1b feat(components): extract and reuse ProjectCard for cleaner project showcases
- Created `ProjectCard` component to unify and streamline project card presentation.
- Updated `index.astro`, `projects.astro`, and `zh/projects.astro` to use `ProjectCard` for improved maintainability and consistency.
- Simplified layout and removed duplicate styles across pages.
2026-03-14 11:41:45 +08:00
zguiyang
32954cf69a feat(home): enhance visual design and layout improvements
- Improved hero section with dynamic gradient and overlay enhancements.
- Updated background styles, component surfaces, and hover effects for a cohesive look.
- Enhanced section spacing, responsiveness, and typography for better flow.
- Refined service and project showcases with modernized card styling and interactivity.
2026-03-14 11:30:33 +08:00
zguiyang
9c6b7fd698 feat(home): redesign homepage with refined visuals and improved layout flow
- Enhanced hero section with updated typography, animations, and gradient backgrounds.
- Replaced single featured project with dynamic multi-project filtering.
- Improved section transitions and added cohesive hover effects for interactivity.
- Updated CSS variables for softer background colors and unified design language.
- Added responsive refinements for better experience across devices.
2026-03-14 11:17:02 +08:00
zguiyang
a85e9f57ba feat(home): redesign homepage into refined full-screen hero flow 2026-03-13 16:08:02 +08:00
zguiyang
98d9535e61 refactor(layout): unify inner-page typography rhythm with container-based widths 2026-03-13 15:37:35 +08:00
zguiyang
e2eb43e24c refactor(theme): introduce global UI design tokens for page surfaces 2026-03-13 15:27:02 +08:00
zguiyang
bf89acbd2c feat(ui): unify site visual language across key pages 2026-03-13 15:26:08 +08:00
zguiyang
33cc9a31b8 feat: add local font files and update global styles to use them 2026-03-13 15:08:25 +08:00
zguiyang
0d050b0c14 refactor: update styles and animations across pages for improved consistency and aesthetics
- Changed background gradients and color schemes in `now.astro` and `projects.astro` to use primary colors.
- Updated text styles and backgrounds to enhance readability and visual appeal.
- Added new font imports for better typography.
- Introduced custom animations and hover effects in `global.css` for enhanced user interaction.
- Adjusted CSS variables for a more cohesive design across light and dark modes.
2026-03-13 14:39:09 +08:00
zguiyang
30b097cc74 docs: refresh AGENTS and add Claude instructions 2026-03-13 14:11:57 +08:00
zguiyang
a208bab7ea feat(portfolio): redesign site as opportunity gateway
- add now/hire pages and update IA/navigation

- feature Elynd in projects and homepage messaging

- refresh services/blog/brand copy for AI product positioning

- reduce hydration overhead by using Astro Container

- remove Google Fonts external dependency and use local fallback stack
2026-03-13 14:01:04 +08:00
zguiyang
52a5d14a4e Add new OpenSpec skills for enhanced workflow management
- Introduced `openspec-explore` skill for exploratory discussions and idea clarification without implementation.
- Added `openspec-ff-change` skill to fast-forward artifact creation for quick implementation setups.
- Created `openspec-new-change` skill to guide users through structured change creation with a step-by-step approach.
- Implemented `openspec-onboard` skill for guided onboarding, walking users through a complete OpenSpec workflow cycle.
- Developed `openspec-sync-specs` skill to intelligently sync delta specs from changes to main specs without archiving.
- Added `openspec-verify-change` skill to validate that implementations match change artifacts before archiving.
2026-03-13 13:38:53 +08:00
zguiyang
bbb2f41591 feat: add new OpenSpec skills for change management and onboarding
- Created `openspec-ff-change` skill for fast-forward artifact creation.
- Introduced `openspec-new-change` skill for structured change creation.
- Developed `openspec-onboard` skill for guided onboarding through OpenSpec workflow.
- Added `openspec-sync-specs` skill for syncing delta specs to main specs.
- Implemented `openspec-verify-change` skill for verifying implementation against change artifacts.
- Updated `.gitignore` to exclude OpenSpec generated files.
- Added `skills-lock.json` to manage skill dependencies.
2026-03-13 13:18:03 +08:00
220 changed files with 23301 additions and 2055 deletions

View File

@@ -0,0 +1,88 @@
---
name: astro
description: Skill for using Astro projects. Includes CLI commands, project structure, core config options, and adapters. Use this skill when the user needs to work with Astro or when the user mentions Astro.
license: MIT
metadata:
authors: "Astro Team"
version: "0.0.1"
---
# Astro Usage Guide
**Always consult [docs.astro.build](https://docs.astro.build) for code examples and latest API.**
Astro is the web framework for content-driven websites.
---
## Quick Reference
### File Location
CLI looks for `astro.config.js`, `astro.config.mjs`, `astro.config.cjs`, and `astro.config.ts` in: `./`. Use `--config` for custom path.
### CLI Commands
- `npx astro dev` - Start the development server.
- `npx astro build` - Build your project and write it to disk.
- `npx astro check` - Check your project for errors.
- `npx astro add` - Add an integration.
- `npmx astro sync` - Generate TypeScript types for all Astro modules.
**Re-run after adding/changing plugins.**
### Project Structure
Astro leverages an opinionated folder layout for your project. Every Astro project root should include some directories and files. Reference [project structure docs](https://docs.astro.build/en/basics/project-structure).
- `src/*` - Your project source code (components, pages, styles, images, etc.)
- `src/pages` - Required sub-directory in your Astro project. Without it, your site will have no pages or routes!
- `src/components` - It is common to group and organize all of your project components together in this folder. This is a common convention in Astro projects, but it is not required. Feel free to organize your components however you like!
- `src/layouts` - Just like `src/components`, this directory is a common convention but not required.
- `src/styles` - It is a common convention to store your CSS or Sass files here, but this is not required. As long as your styles live somewhere in the src/ directory and are imported correctly, Astro will handle and optimize them.
- `public/*` - Your non-code, unprocessed assets (fonts, icons, etc.). The files in this folder will be copied into the build folder untouched, and then your site will be built.
- `package.json` - A project manifest.
- `astro.config.{js,mjs,cjs,ts}` - An Astro configuration file. (recommended)
- `tsconfig.json` - A TypeScript configuration file. (recommended)
---
## Core Config Options
| Option | Notes |
|--------|-------|
| `site` | Your final, deployed URL. Astro uses this full URL to generate your sitemap and canonical URLs in your final build. |
---
## Adapters
Deploy to your favorite server, serverless, or edge host with build adapters. Use an adapter to enable on-demand rendering in your Astro project.
**Add [Node.js](https://docs.astro.build/en/guides/integrations-guide/node) adapter using astro add:**
```
npx astro add node --yes
```
**Add [Cloudflare](https://docs.astro.build/en/guides/integrations-guide/cloudflare) adapter using astro add:**
```
npx astro add cloudflare --yes
```
**Add [Netlify](https://docs.astro.build/en/guides/integrations-guide/netlify) adapter using astro add:**
```
npx astro add netlify --yes
```
**Add [Vercel](https://docs.astro.build/en/guides/integrations-guide/vercel) adapter using astro add:**
```
npx astro add vercel --yes
```
[Other Community adapters](https://astro.build/integrations/2/?search=&categories%5B%5D=adapters)
## Resources
- [Docs](https://docs.astro.build)
- [Config Reference](https://docs.astro.build/en/reference/configuration-reference/)
- [llms.txt](https://docs.astro.build/llms.txt)
- [GitHub](https://github.com/withastro/astro)

View File

@@ -0,0 +1,205 @@
---
name: framer-motion
description: Expert guidelines for building performant animations with Framer Motion/Motion library in React applications
---
# Framer Motion / Motion Animation Guidelines
You are an expert in Framer Motion (now Motion), React, and TypeScript. Follow these guidelines when creating animations.
## Core Principles
### Import from the Correct Package
- Use `import { motion } from "motion/react"` for React projects (not "framer-motion" - this is outdated)
- The library was renamed from Framer Motion to Motion
- Always use the latest Motion API
### Performance-First Approach
- Animate transform properties (`x`, `y`, `scale`, `rotate`) and `opacity` for best performance
- These properties can be hardware-accelerated and don't trigger layout recalculations
- Avoid animating properties that cause layout shifts like `width`, `height`, `top`, `left`, `margin`, `padding`
## Hardware Acceleration
### Use will-change Properly
```tsx
// When animating transforms
<motion.div
style={{ willChange: "transform" }}
animate={{ x: 100, y: 50, scale: 1.2 }}
/>
// When animating other GPU-accelerated properties
<motion.div
style={{ willChange: "opacity, transform" }}
animate={{ opacity: 0.5, x: 100 }}
/>
```
### Properties to Add to willChange
- `transform` - for x, y, scale, rotate, skew
- `opacity` - for opacity animations
- `filter` - for blur, brightness, etc.
- `clipPath` - for clip-path animations
- `backgroundColor` - for background color transitions
## Animation Best Practices
### Use Variants for Complex Animations
```tsx
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
};
const itemVariants = {
hidden: { y: 20, opacity: 0 },
visible: { y: 0, opacity: 1 }
};
```
### Use layoutId for Shared Element Transitions
```tsx
<motion.div layoutId="shared-element" />
```
### Prefer spring Animations
```tsx
// Springs feel more natural than duration-based animations
<motion.div
animate={{ x: 100 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
/>
```
## React Integration
### Memoization for Performance
```tsx
// Memoize animation variants
const variants = useMemo(() => ({
hidden: { opacity: 0 },
visible: { opacity: 1 }
}), []);
// Memoize callbacks
const handleAnimationComplete = useCallback(() => {
// handler logic
}, []);
```
### Avoid Inline Style Objects
```tsx
// Bad - creates new object on every render
<motion.div style={{ willChange: "transform" }} />
// Good - define outside or memoize
const style = { willChange: "transform" };
<motion.div style={style} />
```
## Accessibility
### Respect Reduced Motion Preferences
```tsx
import { useReducedMotion } from "motion/react";
function Component() {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
animate={{ x: shouldReduceMotion ? 0 : 100 }}
transition={{ duration: shouldReduceMotion ? 0 : 0.3 }}
/>
);
}
```
## Gesture Animations
### Use Gesture Props Correctly
```tsx
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
/>
```
## Scroll Animations
### Use useScroll for Scroll-Linked Animations
```tsx
import { useScroll, useTransform, motion } from "motion/react";
function ParallaxComponent() {
const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], [0, -100]);
return <motion.div style={{ y }} />;
}
```
## Exit Animations
### Use AnimatePresence for Exit Animations
```tsx
import { AnimatePresence, motion } from "motion/react";
<AnimatePresence mode="wait">
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
```
## Common Patterns
### Staggered List Animation
```tsx
<motion.ul
initial="hidden"
animate="visible"
variants={{
visible: { transition: { staggerChildren: 0.07 } }
}}
>
{items.map((item) => (
<motion.li
key={item.id}
variants={{
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
}}
/>
))}
</motion.ul>
```
### Page Transitions
```tsx
const pageTransition = {
initial: { opacity: 0, x: -20 },
animate: { opacity: 1, x: 0 },
exit: { opacity: 0, x: 20 },
transition: { duration: 0.3 }
};
```
## Performance Debugging
- Use React DevTools to inspect re-renders
- Use Chrome DevTools Performance tab to identify animation jank
- Target 60fps minimum, 120fps on high refresh rate displays
- Test on actual devices, especially mid-range Android phones

View File

@@ -0,0 +1,240 @@
---
name: shadcn
description: Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", or "switch to --preset".
user-invocable: false
---
# shadcn/ui
A framework for building ui, components and design systems. Components are added as source code to the user's project via the CLI.
> **IMPORTANT:** Run all CLI commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest` — based on the project's `packageManager`. Examples below use `npx shadcn@latest` but substitute the correct runner for the project.
## Current Project Context
```json
!`npx shadcn@latest info --json 2>/dev/null || echo '{"error": "No shadcn project found. Run shadcn init first."}'`
```
The JSON above contains the project config and installed components. Use `npx shadcn@latest docs <component>` to get documentation and example URLs for any component.
## Principles
1. **Use existing components first.** Use `npx shadcn@latest search` to check registries before writing custom UI. Check community registries too.
2. **Compose, don't reinvent.** Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.
3. **Use built-in variants before custom styles.** `variant="outline"`, `size="sm"`, etc.
4. **Use semantic colors.** `bg-primary`, `text-muted-foreground` — never raw values like `bg-blue-500`.
## Critical Rules
These rules are **always enforced**. Each links to a file with Incorrect/Correct code pairs.
### Styling & Tailwind → [styling.md](./rules/styling.md)
- **`className` for layout, not styling.** Never override component colors or typography.
- **No `space-x-*` or `space-y-*`.** Use `flex` with `gap-*`. For vertical stacks, `flex flex-col gap-*`.
- **Use `size-*` when width and height are equal.** `size-10` not `w-10 h-10`.
- **Use `truncate` shorthand.** Not `overflow-hidden text-ellipsis whitespace-nowrap`.
- **No manual `dark:` color overrides.** Use semantic tokens (`bg-background`, `text-muted-foreground`).
- **Use `cn()` for conditional classes.** Don't write manual template literal ternaries.
- **No manual `z-index` on overlay components.** Dialog, Sheet, Popover, etc. handle their own stacking.
### Forms & Inputs → [forms.md](./rules/forms.md)
- **Forms use `FieldGroup` + `Field`.** Never use raw `div` with `space-y-*` or `grid gap-*` for form layout.
- **`InputGroup` uses `InputGroupInput`/`InputGroupTextarea`.** Never raw `Input`/`Textarea` inside `InputGroup`.
- **Buttons inside inputs use `InputGroup` + `InputGroupAddon`.**
- **Option sets (27 choices) use `ToggleGroup`.** Don't loop `Button` with manual active state.
- **`FieldSet` + `FieldLegend` for grouping related checkboxes/radios.** Don't use a `div` with a heading.
- **Field validation uses `data-invalid` + `aria-invalid`.** `data-invalid` on `Field`, `aria-invalid` on the control. For disabled: `data-disabled` on `Field`, `disabled` on the control.
### Component Structure → [composition.md](./rules/composition.md)
- **Items always inside their Group.** `SelectItem``SelectGroup`. `DropdownMenuItem``DropdownMenuGroup`. `CommandItem``CommandGroup`.
- **Use `asChild` (radix) or `render` (base) for custom triggers.** Check `base` field from `npx shadcn@latest info`. → [base-vs-radix.md](./rules/base-vs-radix.md)
- **Dialog, Sheet, and Drawer always need a Title.** `DialogTitle`, `SheetTitle`, `DrawerTitle` required for accessibility. Use `className="sr-only"` if visually hidden.
- **Use full Card composition.** `CardHeader`/`CardTitle`/`CardDescription`/`CardContent`/`CardFooter`. Don't dump everything in `CardContent`.
- **Button has no `isPending`/`isLoading`.** Compose with `Spinner` + `data-icon` + `disabled`.
- **`TabsTrigger` must be inside `TabsList`.** Never render triggers directly in `Tabs`.
- **`Avatar` always needs `AvatarFallback`.** For when the image fails to load.
### Use Components, Not Custom Markup → [composition.md](./rules/composition.md)
- **Use existing components before custom markup.** Check if a component exists before writing a styled `div`.
- **Callouts use `Alert`.** Don't build custom styled divs.
- **Empty states use `Empty`.** Don't build custom empty state markup.
- **Toast via `sonner`.** Use `toast()` from `sonner`.
- **Use `Separator`** instead of `<hr>` or `<div className="border-t">`.
- **Use `Skeleton`** for loading placeholders. No custom `animate-pulse` divs.
- **Use `Badge`** instead of custom styled spans.
### Icons → [icons.md](./rules/icons.md)
- **Icons in `Button` use `data-icon`.** `data-icon="inline-start"` or `data-icon="inline-end"` on the icon.
- **No sizing classes on icons inside components.** Components handle icon sizing via CSS. No `size-4` or `w-4 h-4`.
- **Pass icons as objects, not string keys.** `icon={CheckIcon}`, not a string lookup.
### CLI
- **Never decode or fetch preset codes manually.** Pass them directly to `npx shadcn@latest init --preset <code>`.
## Key Patterns
These are the most common patterns that differentiate correct shadcn/ui code. For edge cases, see the linked rule files above.
```tsx
// Form layout: FieldGroup + Field, not div + Label.
<FieldGroup>
<Field>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" />
</Field>
</FieldGroup>
// Validation: data-invalid on Field, aria-invalid on the control.
<Field data-invalid>
<FieldLabel>Email</FieldLabel>
<Input aria-invalid />
<FieldDescription>Invalid email.</FieldDescription>
</Field>
// Icons in buttons: data-icon, no sizing classes.
<Button>
<SearchIcon data-icon="inline-start" />
Search
</Button>
// Spacing: gap-*, not space-y-*.
<div className="flex flex-col gap-4"> // correct
<div className="space-y-4"> // wrong
// Equal dimensions: size-*, not w-* h-*.
<Avatar className="size-10"> // correct
<Avatar className="w-10 h-10"> // wrong
// Status colors: Badge variants or semantic tokens, not raw colors.
<Badge variant="secondary">+20.1%</Badge> // correct
<span className="text-emerald-600">+20.1%</span> // wrong
```
## Component Selection
| Need | Use |
| -------------------------- | --------------------------------------------------------------------------------------------------- |
| Button/action | `Button` with appropriate variant |
| Form inputs | `Input`, `Select`, `Combobox`, `Switch`, `Checkbox`, `RadioGroup`, `Textarea`, `InputOTP`, `Slider` |
| Toggle between 25 options | `ToggleGroup` + `ToggleGroupItem` |
| Data display | `Table`, `Card`, `Badge`, `Avatar` |
| Navigation | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination` |
| Overlays | `Dialog` (modal), `Sheet` (side panel), `Drawer` (bottom sheet), `AlertDialog` (confirmation) |
| Feedback | `sonner` (toast), `Alert`, `Progress`, `Skeleton`, `Spinner` |
| Command palette | `Command` inside `Dialog` |
| Charts | `Chart` (wraps Recharts) |
| Layout | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible` |
| Empty states | `Empty` |
| Menus | `DropdownMenu`, `ContextMenu`, `Menubar` |
| Tooltips/info | `Tooltip`, `HoverCard`, `Popover` |
## Key Fields
The injected project context contains these key fields:
- **`aliases`** → use the actual alias prefix for imports (e.g. `@/`, `~/`), never hardcode.
- **`isRSC`** → when `true`, components using `useState`, `useEffect`, event handlers, or browser APIs need `"use client"` at the top of the file. Always reference this field when advising on the directive.
- **`tailwindVersion`** → `"v4"` uses `@theme inline` blocks; `"v3"` uses `tailwind.config.js`.
- **`tailwindCssFile`** → the global CSS file where custom CSS variables are defined. Always edit this file, never create a new one.
- **`style`** → component visual treatment (e.g. `nova`, `vega`).
- **`base`** → primitive library (`radix` or `base`). Affects component APIs and available props.
- **`iconLibrary`** → determines icon imports. Use `lucide-react` for `lucide`, `@tabler/icons-react` for `tabler`, etc. Never assume `lucide-react`.
- **`resolvedPaths`** → exact file-system destinations for components, utils, hooks, etc.
- **`framework`** → routing and file conventions (e.g. Next.js App Router vs Vite SPA).
- **`packageManager`** → use this for any non-shadcn dependency installs (e.g. `pnpm add date-fns` vs `npm install date-fns`).
See [cli.md — `info` command](./cli.md) for the full field reference.
## Component Docs, Examples, and Usage
Run `npx shadcn@latest docs <component>` to get the URLs for a component's documentation, examples, and API reference. Fetch these URLs to get the actual content.
```bash
npx shadcn@latest docs button dialog select
```
**When creating, fixing, debugging, or using a component, always run `npx shadcn@latest docs` and fetch the URLs first.** This ensures you're working with the correct API and usage patterns rather than guessing.
## Workflow
1. **Get project context** — already injected above. Run `npx shadcn@latest info` again if you need to refresh.
2. **Check installed components first** — before running `add`, always check the `components` list from project context or list the `resolvedPaths.ui` directory. Don't import components that haven't been added, and don't re-add ones already installed.
3. **Find components**`npx shadcn@latest search`.
4. **Get docs and examples** — run `npx shadcn@latest docs <component>` to get URLs, then fetch them. Use `npx shadcn@latest view` to browse registry items you haven't installed. To preview changes to installed components, use `npx shadcn@latest add --diff`.
5. **Install or update**`npx shadcn@latest add`. When updating existing components, use `--dry-run` and `--diff` to preview changes first (see [Updating Components](#updating-components) below).
6. **Fix imports in third-party components** — After adding components from community registries (e.g. `@bundui`, `@magicui`), check the added non-UI files for hardcoded import paths like `@/components/ui/...`. These won't match the project's actual aliases. Use `npx shadcn@latest info` to get the correct `ui` alias (e.g. `@workspace/ui/components`) and rewrite the imports accordingly. The CLI rewrites imports for its own UI files, but third-party registry components may use default paths that don't match the project.
7. **Review added components** — After adding a component or block from any registry, **always read the added files and verify they are correct**. Check for missing sub-components (e.g. `SelectItem` without `SelectGroup`), missing imports, incorrect composition, or violations of the [Critical Rules](#critical-rules). Also replace any icon imports with the project's `iconLibrary` from the project context (e.g. if the registry item uses `lucide-react` but the project uses `hugeicons`, swap the imports and icon names accordingly). Fix all issues before moving on.
8. **Registry must be explicit** — When the user asks to add a block or component, **do not guess the registry**. If no registry is specified (e.g. user says "add a login block" without specifying `@shadcn`, `@tailark`, etc.), ask which registry to use. Never default to a registry on behalf of the user.
9. **Switching presets** — Ask the user first: **reinstall**, **merge**, or **skip**?
- **Reinstall**: `npx shadcn@latest init --preset <code> --force --reinstall`. Overwrites all components.
- **Merge**: `npx shadcn@latest init --preset <code> --force --no-reinstall`, then run `npx shadcn@latest info` to list installed components, then for each installed component use `--dry-run` and `--diff` to [smart merge](#updating-components) it individually.
- **Skip**: `npx shadcn@latest init --preset <code> --force --no-reinstall`. Only updates config and CSS, leaves components as-is.
## Updating Components
When the user asks to update a component from upstream while keeping their local changes, use `--dry-run` and `--diff` to intelligently merge. **NEVER fetch raw files from GitHub manually — always use the CLI.**
1. Run `npx shadcn@latest add <component> --dry-run` to see all files that would be affected.
2. For each file, run `npx shadcn@latest add <component> --diff <file>` to see what changed upstream vs local.
3. Decide per file based on the diff:
- No local changes → safe to overwrite.
- Has local changes → read the local file, analyze the diff, and apply upstream updates while preserving local modifications.
- User says "just update everything" → use `--overwrite`, but confirm first.
4. **Never use `--overwrite` without the user's explicit approval.**
## Quick Reference
```bash
# Create a new project.
npx shadcn@latest init --name my-app --preset base-nova
npx shadcn@latest init --name my-app --preset a2r6bw --template vite
# Create a monorepo project.
npx shadcn@latest init --name my-app --preset base-nova --monorepo
npx shadcn@latest init --name my-app --preset base-nova --template next --monorepo
# Initialize existing project.
npx shadcn@latest init --preset base-nova
npx shadcn@latest init --defaults # shortcut: --template=next --preset=base-nova
# Add components.
npx shadcn@latest add button card dialog
npx shadcn@latest add @magicui/shimmer-button
npx shadcn@latest add --all
# Preview changes before adding/updating.
npx shadcn@latest add button --dry-run
npx shadcn@latest add button --diff button.tsx
npx shadcn@latest add @acme/form --view button.tsx
# Search registries.
npx shadcn@latest search @shadcn -q "sidebar"
npx shadcn@latest search @tailark -q "stats"
# Get component docs and example URLs.
npx shadcn@latest docs button dialog select
# View registry item details (for items not yet installed).
npx shadcn@latest view @shadcn/button
```
**Named presets:** `base-nova`, `radix-nova`
**Templates:** `next`, `vite`, `start`, `react-router`, `astro` (all support `--monorepo`) and `laravel` (not supported for monorepo)
**Preset codes:** Base62 strings starting with `a` (e.g. `a2r6bw`), from [ui.shadcn.com](https://ui.shadcn.com).
## Detailed References
- [rules/forms.md](./rules/forms.md) — FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation states
- [rules/composition.md](./rules/composition.md) — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading
- [rules/icons.md](./rules/icons.md) — data-icon, icon sizing, passing icons as objects
- [rules/styling.md](./rules/styling.md) — Semantic colors, variants, className, spacing, size, truncate, dark mode, cn(), z-index
- [rules/base-vs-radix.md](./rules/base-vs-radix.md) — asChild vs render, Select, ToggleGroup, Slider, Accordion
- [cli.md](./cli.md) — Commands, flags, presets, templates
- [customization.md](./customization.md) — Theming, CSS variables, extending components

View File

@@ -0,0 +1,5 @@
interface:
display_name: "shadcn/ui"
short_description: "Manages shadcn/ui components — adding, searching, fixing, debugging, styling, and composing UI."
icon_small: "./assets/shadcn-small.png"
icon_large: "./assets/shadcn.png"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,255 @@
# shadcn CLI Reference
Configuration is read from `components.json`.
> **IMPORTANT:** Always run commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest`. Check `packageManager` from project context to choose the right one. Examples below use `npx shadcn@latest` but substitute the correct runner for the project.
> **IMPORTANT:** Only use the flags documented below. Do not invent or guess flags — if a flag isn't listed here, it doesn't exist. The CLI auto-detects the package manager from the project's lockfile; there is no `--package-manager` flag.
## Contents
- Commands: init, add (dry-run, smart merge), search, view, docs, info, build
- Templates: next, vite, start, react-router, astro
- Presets: named, code, URL formats and fields
- Switching presets
---
## Commands
### `init` — Initialize or create a project
```bash
npx shadcn@latest init [components...] [options]
```
Initializes shadcn/ui in an existing project or creates a new project (when `--name` is provided). Optionally installs components in the same step.
| Flag | Short | Description | Default |
| ----------------------- | ----- | --------------------------------------------------------- | ------- |
| `--template <template>` | `-t` | Template (next, start, vite, next-monorepo, react-router) | — |
| `--preset [name]` | `-p` | Preset configuration (named, code, or URL) | — |
| `--yes` | `-y` | Skip confirmation prompt | `true` |
| `--defaults` | `-d` | Use defaults (`--template=next --preset=base-nova`) | `false` |
| `--force` | `-f` | Force overwrite existing configuration | `false` |
| `--cwd <cwd>` | `-c` | Working directory | current |
| `--name <name>` | `-n` | Name for new project | — |
| `--silent` | `-s` | Mute output | `false` |
| `--rtl` | | Enable RTL support | — |
| `--reinstall` | | Re-install existing UI components | `false` |
| `--monorepo` | | Scaffold a monorepo project | — |
| `--no-monorepo` | | Skip the monorepo prompt | — |
`npx shadcn@latest create` is an alias for `npx shadcn@latest init`.
### `add` — Add components
> **IMPORTANT:** To compare local components against upstream or to preview changes, ALWAYS use `npx shadcn@latest add <component> --dry-run`, `--diff`, or `--view`. NEVER fetch raw files from GitHub or other sources manually. The CLI handles registry resolution, file paths, and CSS diffing automatically.
```bash
npx shadcn@latest add [components...] [options]
```
Accepts component names, registry-prefixed names (`@magicui/shimmer-button`), URLs, or local paths.
| Flag | Short | Description | Default |
| --------------- | ----- | -------------------------------------------------------------------------------------------------------------------- | ------- |
| `--yes` | `-y` | Skip confirmation prompt | `false` |
| `--overwrite` | `-o` | Overwrite existing files | `false` |
| `--cwd <cwd>` | `-c` | Working directory | current |
| `--all` | `-a` | Add all available components | `false` |
| `--path <path>` | `-p` | Target path for the component | — |
| `--silent` | `-s` | Mute output | `false` |
| `--dry-run` | | Preview all changes without writing files | `false` |
| `--diff [path]` | | Show diffs. Without a path, shows the first 5 files. With a path, shows that file only (implies `--dry-run`) | — |
| `--view [path]` | | Show file contents. Without a path, shows the first 5 files. With a path, shows that file only (implies `--dry-run`) | — |
#### Dry-Run Mode
Use `--dry-run` to preview what `add` would do without writing any files. `--diff` and `--view` both imply `--dry-run`.
```bash
# Preview all changes.
npx shadcn@latest add button --dry-run
# Show diffs for all files (top 5).
npx shadcn@latest add button --diff
# Show the diff for a specific file.
npx shadcn@latest add button --diff button.tsx
# Show contents for all files (top 5).
npx shadcn@latest add button --view
# Show the full content of a specific file.
npx shadcn@latest add button --view button.tsx
# Works with URLs too.
npx shadcn@latest add https://api.npoint.io/abc123 --dry-run
# CSS diffs.
npx shadcn@latest add button --diff globals.css
```
**When to use dry-run:**
- When the user asks "what files will this add?" or "what will this change?" — use `--dry-run`.
- Before overwriting existing components — use `--diff` to preview the changes first.
- When the user wants to inspect component source code without installing — use `--view`.
- When checking what CSS changes would be made to `globals.css` — use `--diff globals.css`.
- When the user asks to review or audit third-party registry code before installing — use `--view` to inspect the source.
> **`npx shadcn@latest add --dry-run` vs `npx shadcn@latest view`:** Prefer `npx shadcn@latest add --dry-run/--diff/--view` over `npx shadcn@latest view` when the user wants to preview changes to their project. `npx shadcn@latest view` only shows raw registry metadata. `npx shadcn@latest add --dry-run` shows exactly what would happen in the user's project: resolved file paths, diffs against existing files, and CSS updates. Use `npx shadcn@latest view` only when the user wants to browse registry info without a project context.
#### Smart Merge from Upstream
See [Updating Components in SKILL.md](./SKILL.md#updating-components) for the full workflow.
### `search` — Search registries
```bash
npx shadcn@latest search <registries...> [options]
```
Fuzzy search across registries. Also aliased as `npx shadcn@latest list`. Without `-q`, lists all items.
| Flag | Short | Description | Default |
| ------------------- | ----- | ---------------------- | ------- |
| `--query <query>` | `-q` | Search query | — |
| `--limit <number>` | `-l` | Max items per registry | `100` |
| `--offset <number>` | `-o` | Items to skip | `0` |
| `--cwd <cwd>` | `-c` | Working directory | current |
### `view` — View item details
```bash
npx shadcn@latest view <items...> [options]
```
Displays item info including file contents. Example: `npx shadcn@latest view @shadcn/button`.
### `docs` — Get component documentation URLs
```bash
npx shadcn@latest docs <components...> [options]
```
Outputs resolved URLs for component documentation, examples, and API references. Accepts one or more component names. Fetch the URLs to get the actual content.
Example output for `npx shadcn@latest docs input button`:
```
base radix
input
docs https://ui.shadcn.com/docs/components/radix/input
examples https://raw.githubusercontent.com/.../examples/input-example.tsx
button
docs https://ui.shadcn.com/docs/components/radix/button
examples https://raw.githubusercontent.com/.../examples/button-example.tsx
```
Some components include an `api` link to the underlying library (e.g. `cmdk` for the command component).
### `diff` — Check for updates
Do not use this command. Use `npx shadcn@latest add --diff` instead.
### `info` — Project information
```bash
npx shadcn@latest info [options]
```
Displays project info and `components.json` configuration. Run this first to discover the project's framework, aliases, Tailwind version, and resolved paths.
| Flag | Short | Description | Default |
| ------------- | ----- | ----------------- | ------- |
| `--cwd <cwd>` | `-c` | Working directory | current |
**Project Info fields:**
| Field | Type | Meaning |
| -------------------- | --------- | ------------------------------------------------------------------ |
| `framework` | `string` | Detected framework (`next`, `vite`, `react-router`, `start`, etc.) |
| `frameworkVersion` | `string` | Framework version (e.g. `15.2.4`) |
| `isSrcDir` | `boolean` | Whether the project uses a `src/` directory |
| `isRSC` | `boolean` | Whether React Server Components are enabled |
| `isTsx` | `boolean` | Whether the project uses TypeScript |
| `tailwindVersion` | `string` | `"v3"` or `"v4"` |
| `tailwindConfigFile` | `string` | Path to the Tailwind config file |
| `tailwindCssFile` | `string` | Path to the global CSS file |
| `aliasPrefix` | `string` | Import alias prefix (e.g. `@`, `~`, `@/`) |
| `packageManager` | `string` | Detected package manager (`npm`, `pnpm`, `yarn`, `bun`) |
**Components.json fields:**
| Field | Type | Meaning |
| -------------------- | --------- | ------------------------------------------------------------------------------------------ |
| `base` | `string` | Primitive library (`radix` or `base`) — determines component APIs and available props |
| `style` | `string` | Visual style (e.g. `nova`, `vega`) |
| `rsc` | `boolean` | RSC flag from config |
| `tsx` | `boolean` | TypeScript flag |
| `tailwind.config` | `string` | Tailwind config path |
| `tailwind.css` | `string` | Global CSS path — this is where custom CSS variables go |
| `iconLibrary` | `string` | Icon library — determines icon import package (e.g. `lucide-react`, `@tabler/icons-react`) |
| `aliases.components` | `string` | Component import alias (e.g. `@/components`) |
| `aliases.utils` | `string` | Utils import alias (e.g. `@/lib/utils`) |
| `aliases.ui` | `string` | UI component alias (e.g. `@/components/ui`) |
| `aliases.lib` | `string` | Lib alias (e.g. `@/lib`) |
| `aliases.hooks` | `string` | Hooks alias (e.g. `@/hooks`) |
| `resolvedPaths` | `object` | Absolute file-system paths for each alias |
| `registries` | `object` | Configured custom registries |
**Links fields:**
The `info` output includes a **Links** section with templated URLs for component docs, source, and examples. For resolved URLs, use `npx shadcn@latest docs <component>` instead.
### `build` — Build a custom registry
```bash
npx shadcn@latest build [registry] [options]
```
Builds `registry.json` into individual JSON files for distribution. Default input: `./registry.json`, default output: `./public/r`.
| Flag | Short | Description | Default |
| ----------------- | ----- | ----------------- | ------------ |
| `--output <path>` | `-o` | Output directory | `./public/r` |
| `--cwd <cwd>` | `-c` | Working directory | current |
---
## Templates
| Value | Framework | Monorepo support |
| -------------- | -------------- | ---------------- |
| `next` | Next.js | Yes |
| `vite` | Vite | Yes |
| `start` | TanStack Start | Yes |
| `react-router` | React Router | Yes |
| `astro` | Astro | Yes |
| `laravel` | Laravel | No |
All templates support monorepo scaffolding via the `--monorepo` flag. When passed, the CLI uses a monorepo-specific template directory (e.g. `next-monorepo`, `vite-monorepo`). When neither `--monorepo` nor `--no-monorepo` is passed, the CLI prompts interactively. Laravel does not support monorepo scaffolding.
---
## Presets
Three ways to specify a preset via `--preset`:
1. **Named:** `--preset base-nova` or `--preset radix-nova`
2. **Code:** `--preset a2r6bw` (base62 string, starts with lowercase `a`)
3. **URL:** `--preset "https://ui.shadcn.com/init?base=radix&style=nova&..."`
> **IMPORTANT:** Never try to decode, fetch, or resolve preset codes manually. Preset codes are opaque — pass them directly to `npx shadcn@latest init --preset <code>` and let the CLI handle resolution.
## Switching Presets
Ask the user first: **reinstall**, **merge**, or **skip** existing components?
- **Re-install** → `npx shadcn@latest init --preset <code> --force --reinstall`. Overwrites all component files with the new preset styles. Use when the user hasn't customized components.
- **Merge** → `npx shadcn@latest init --preset <code> --force --no-reinstall`, then run `npx shadcn@latest info` to get the list of installed components and use the [smart merge workflow](./SKILL.md#updating-components) to update them one by one, preserving local changes. Use when the user has customized components.
- **Skip** → `npx shadcn@latest init --preset <code> --force --no-reinstall`. Only updates config and CSS variables, leaves existing components as-is.

View File

@@ -0,0 +1,202 @@
# Customization & Theming
Components reference semantic CSS variable tokens. Change the variables to change every component.
## Contents
- How it works (CSS variables → Tailwind utilities → components)
- Color variables and OKLCH format
- Dark mode setup
- Changing the theme (presets, CSS variables)
- Adding custom colors (Tailwind v3 and v4)
- Border radius
- Customizing components (variants, className, wrappers)
- Checking for updates
---
## How It Works
1. CSS variables defined in `:root` (light) and `.dark` (dark mode).
2. Tailwind maps them to utilities: `bg-primary`, `text-muted-foreground`, etc.
3. Components use these utilities — changing a variable changes all components that reference it.
---
## Color Variables
Every color follows the `name` / `name-foreground` convention. The base variable is for backgrounds, `-foreground` is for text/icons on that background.
| Variable | Purpose |
| -------------------------------------------- | -------------------------------- |
| `--background` / `--foreground` | Page background and default text |
| `--card` / `--card-foreground` | Card surfaces |
| `--primary` / `--primary-foreground` | Primary buttons and actions |
| `--secondary` / `--secondary-foreground` | Secondary actions |
| `--muted` / `--muted-foreground` | Muted/disabled states |
| `--accent` / `--accent-foreground` | Hover and accent states |
| `--destructive` / `--destructive-foreground` | Error and destructive actions |
| `--border` | Default border color |
| `--input` | Form input borders |
| `--ring` | Focus ring color |
| `--chart-1` through `--chart-5` | Chart/data visualization |
| `--sidebar-*` | Sidebar-specific colors |
| `--surface` / `--surface-foreground` | Secondary surface |
Colors use OKLCH: `--primary: oklch(0.205 0 0)` where values are lightness (01), chroma (0 = gray), and hue (0360).
---
## Dark Mode
Class-based toggle via `.dark` on the root element. In Next.js, use `next-themes`:
```tsx
import { ThemeProvider } from "next-themes"
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
```
---
## Changing the Theme
```bash
# Apply a preset code from ui.shadcn.com.
npx shadcn@latest init --preset a2r6bw --force
# Switch to a named preset.
npx shadcn@latest init --preset radix-nova --force
npx shadcn@latest init --reinstall # update existing components to match
# Use a custom theme URL.
npx shadcn@latest init --preset "https://ui.shadcn.com/init?base=radix&style=nova&theme=blue&..." --force
```
Or edit CSS variables directly in `globals.css`.
---
## Adding Custom Colors
Add variables to the file at `tailwindCssFile` from `npx shadcn@latest info` (typically `globals.css`). Never create a new CSS file for this.
```css
/* 1. Define in the global CSS file. */
:root {
--warning: oklch(0.84 0.16 84);
--warning-foreground: oklch(0.28 0.07 46);
}
.dark {
--warning: oklch(0.41 0.11 46);
--warning-foreground: oklch(0.99 0.02 95);
}
```
```css
/* 2a. Register with Tailwind v4 (@theme inline). */
@theme inline {
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
}
```
When `tailwindVersion` is `"v3"` (check via `npx shadcn@latest info`), register in `tailwind.config.js` instead:
```js
// 2b. Register with Tailwind v3 (tailwind.config.js).
module.exports = {
theme: {
extend: {
colors: {
warning: "oklch(var(--warning) / <alpha-value>)",
"warning-foreground":
"oklch(var(--warning-foreground) / <alpha-value>)",
},
},
},
}
```
```tsx
// 3. Use in components.
<div className="bg-warning text-warning-foreground">Warning</div>
```
---
## Border Radius
`--radius` controls border radius globally. Components derive values from it (`rounded-lg` = `var(--radius)`, `rounded-md` = `calc(var(--radius) - 2px)`).
---
## Customizing Components
See also: [rules/styling.md](./rules/styling.md) for Incorrect/Correct examples.
Prefer these approaches in order:
### 1. Built-in variants
```tsx
<Button variant="outline" size="sm">Click</Button>
```
### 2. Tailwind classes via `className`
```tsx
<Card className="max-w-md mx-auto">...</Card>
```
### 3. Add a new variant
Edit the component source to add a variant via `cva`:
```tsx
// components/ui/button.tsx
warning: "bg-warning text-warning-foreground hover:bg-warning/90",
```
### 4. Wrapper components
Compose shadcn/ui primitives into higher-level components:
```tsx
export function ConfirmDialog({ title, description, onConfirm, children }) {
return (
<AlertDialog>
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{title}</AlertDialogTitle>
<AlertDialogDescription>{description}</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={onConfirm}>Confirm</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}
```
---
## Checking for Updates
```bash
npx shadcn@latest add button --diff
```
To preview exactly what would change before updating, use `--dry-run` and `--diff`:
```bash
npx shadcn@latest add button --dry-run # see all affected files
npx shadcn@latest add button --diff button.tsx # see the diff for a specific file
```
See [Updating Components in SKILL.md](./SKILL.md#updating-components) for the full smart merge workflow.

View File

@@ -0,0 +1,47 @@
{
"skill_name": "shadcn",
"evals": [
{
"id": 1,
"prompt": "I'm building a Next.js app with shadcn/ui (base-nova preset, lucide icons). Create a settings form component with fields for: full name, email address, and notification preferences (email, SMS, push notifications as toggle options). Add validation states for required fields.",
"expected_output": "A React component using FieldGroup, Field, ToggleGroup, data-invalid/aria-invalid validation, gap-* spacing, and semantic colors.",
"files": [],
"expectations": [
"Uses FieldGroup and Field components for form layout instead of raw div with space-y",
"Uses Switch for independent on/off notification toggles (not looping Button with manual active state)",
"Uses data-invalid on Field and aria-invalid on the input control for validation states",
"Uses gap-* (e.g. gap-4, gap-6) instead of space-y-* or space-x-* for spacing",
"Uses semantic color tokens (e.g. bg-background, text-muted-foreground, text-destructive) instead of raw colors like bg-red-500",
"No manual dark: color overrides"
]
},
{
"id": 2,
"prompt": "Create a dialog component for editing a user profile. It should have the user's avatar at the top, input fields for name and bio, and Save/Cancel buttons with appropriate icons. Using shadcn/ui with radix-nova preset and tabler icons.",
"expected_output": "A React component with DialogTitle, Avatar+AvatarFallback, data-icon on icon buttons, no icon sizing classes, tabler icon imports.",
"files": [],
"expectations": [
"Includes DialogTitle for accessibility (visible or with sr-only class)",
"Avatar component includes AvatarFallback",
"Icons on buttons use the data-icon attribute (data-icon=\"inline-start\" or data-icon=\"inline-end\")",
"No sizing classes on icons inside components (no size-4, w-4, h-4, etc.)",
"Uses tabler icons (@tabler/icons-react) instead of lucide-react",
"Uses asChild for custom triggers (radix preset)"
]
},
{
"id": 3,
"prompt": "Create a dashboard component that shows 4 stat cards in a grid. Each card has a title, large number, percentage change badge, and a loading skeleton state. Using shadcn/ui with base-nova preset and lucide icons.",
"expected_output": "A React component with full Card composition, Skeleton for loading, Badge for changes, semantic colors, gap-* spacing.",
"files": [],
"expectations": [
"Uses full Card composition with CardHeader, CardTitle, CardContent (not dumping everything into CardContent)",
"Uses Skeleton component for loading placeholders instead of custom animate-pulse divs",
"Uses Badge component for percentage change instead of custom styled spans",
"Uses semantic color tokens instead of raw color values like bg-green-500 or text-red-600",
"Uses gap-* instead of space-y-* or space-x-* for spacing",
"Uses size-* when width and height are equal instead of separate w-* h-*"
]
}
]
}

View File

@@ -0,0 +1,94 @@
# shadcn MCP Server
The CLI includes an MCP server that lets AI assistants search, browse, view, and install components from registries.
---
## Setup
```bash
shadcn mcp # start the MCP server (stdio)
shadcn mcp init # write config for your editor
```
Editor config files:
| Editor | Config file |
|--------|------------|
| Claude Code | `.mcp.json` |
| Cursor | `.cursor/mcp.json` |
| VS Code | `.vscode/mcp.json` |
| OpenCode | `opencode.json` |
| Codex | `~/.codex/config.toml` (manual) |
---
## Tools
> **Tip:** MCP tools handle registry operations (search, view, install). For project configuration (aliases, framework, Tailwind version), use `npx shadcn@latest info` — there is no MCP equivalent.
### `shadcn:get_project_registries`
Returns registry names from `components.json`. Errors if no `components.json` exists.
**Input:** none
### `shadcn:list_items_in_registries`
Lists all items from one or more registries.
**Input:** `registries` (string[]), `limit` (number, optional), `offset` (number, optional)
### `shadcn:search_items_in_registries`
Fuzzy search across registries.
**Input:** `registries` (string[]), `query` (string), `limit` (number, optional), `offset` (number, optional)
### `shadcn:view_items_in_registries`
View item details including full file contents.
**Input:** `items` (string[]) — e.g. `["@shadcn/button", "@shadcn/card"]`
### `shadcn:get_item_examples_from_registries`
Find usage examples and demos with source code.
**Input:** `registries` (string[]), `query` (string) — e.g. `"accordion-demo"`, `"button example"`
### `shadcn:get_add_command_for_items`
Returns the CLI install command.
**Input:** `items` (string[]) — e.g. `["@shadcn/button"]`
### `shadcn:get_audit_checklist`
Returns a checklist for verifying components (imports, deps, lint, TypeScript).
**Input:** none
---
## Configuring Registries
Registries are set in `components.json`. The `@shadcn` registry is always built-in.
```json
{
"registries": {
"@acme": "https://acme.com/r/{name}.json",
"@private": {
"url": "https://private.com/r/{name}.json",
"headers": { "Authorization": "Bearer ${MY_TOKEN}" }
}
}
}
```
- Names must start with `@`.
- URLs must contain `{name}`.
- `${VAR}` references are resolved from environment variables.
Community registry index: `https://ui.shadcn.com/r/registries.json`

View File

@@ -0,0 +1,306 @@
# Base vs Radix
API differences between `base` and `radix`. Check the `base` field from `npx shadcn@latest info`.
## Contents
- Composition: asChild vs render
- Button / trigger as non-button element
- Select (items prop, placeholder, positioning, multiple, object values)
- ToggleGroup (type vs multiple)
- Slider (scalar vs array)
- Accordion (type and defaultValue)
---
## Composition: asChild (radix) vs render (base)
Radix uses `asChild` to replace the default element. Base uses `render`. Don't wrap triggers in extra elements.
**Incorrect:**
```tsx
<DialogTrigger>
<div>
<Button>Open</Button>
</div>
</DialogTrigger>
```
**Correct (radix):**
```tsx
<DialogTrigger asChild>
<Button>Open</Button>
</DialogTrigger>
```
**Correct (base):**
```tsx
<DialogTrigger render={<Button />}>Open</DialogTrigger>
```
This applies to all trigger and close components: `DialogTrigger`, `SheetTrigger`, `AlertDialogTrigger`, `DropdownMenuTrigger`, `PopoverTrigger`, `TooltipTrigger`, `CollapsibleTrigger`, `DialogClose`, `SheetClose`, `NavigationMenuLink`, `BreadcrumbLink`, `SidebarMenuButton`, `Badge`, `Item`.
---
## Button / trigger as non-button element (base only)
When `render` changes an element to a non-button (`<a>`, `<span>`), add `nativeButton={false}`.
**Incorrect (base):** missing `nativeButton={false}`.
```tsx
<Button render={<a href="/docs" />}>Read the docs</Button>
```
**Correct (base):**
```tsx
<Button render={<a href="/docs" />} nativeButton={false}>
Read the docs
</Button>
```
**Correct (radix):**
```tsx
<Button asChild>
<a href="/docs">Read the docs</a>
</Button>
```
Same for triggers whose `render` is not a `Button`:
```tsx
// base.
<PopoverTrigger render={<InputGroupAddon />} nativeButton={false}>
Pick date
</PopoverTrigger>
```
---
## Select
**items prop (base only).** Base requires an `items` prop on the root. Radix uses inline JSX only.
**Incorrect (base):**
```tsx
<Select>
<SelectTrigger><SelectValue placeholder="Select a fruit" /></SelectTrigger>
</Select>
```
**Correct (base):**
```tsx
const items = [
{ label: "Select a fruit", value: null },
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
]
<Select items={items}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{items.map((item) => (
<SelectItem key={item.value} value={item.value}>{item.label}</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
```
**Correct (radix):**
```tsx
<Select>
<SelectTrigger>
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
```
**Placeholder.** Base uses a `{ value: null }` item in the items array. Radix uses `<SelectValue placeholder="...">`.
**Content positioning.** Base uses `alignItemWithTrigger`. Radix uses `position`.
```tsx
// base.
<SelectContent alignItemWithTrigger={false} side="bottom">
// radix.
<SelectContent position="popper">
```
---
## Select — multiple selection and object values (base only)
Base supports `multiple`, render-function children on `SelectValue`, and object values with `itemToStringValue`. Radix is single-select with string values only.
**Correct (base — multiple selection):**
```tsx
<Select items={items} multiple defaultValue={[]}>
<SelectTrigger>
<SelectValue>
{(value: string[]) => value.length === 0 ? "Select fruits" : `${value.length} selected`}
</SelectValue>
</SelectTrigger>
...
</Select>
```
**Correct (base — object values):**
```tsx
<Select defaultValue={plans[0]} itemToStringValue={(plan) => plan.name}>
<SelectTrigger>
<SelectValue>{(value) => value.name}</SelectValue>
</SelectTrigger>
...
</Select>
```
---
## ToggleGroup
Base uses a `multiple` boolean prop. Radix uses `type="single"` or `type="multiple"`.
**Incorrect (base):**
```tsx
<ToggleGroup type="single" defaultValue="daily">
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
</ToggleGroup>
```
**Correct (base):**
```tsx
// Single (no prop needed), defaultValue is always an array.
<ToggleGroup defaultValue={["daily"]} spacing={2}>
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
<ToggleGroupItem value="weekly">Weekly</ToggleGroupItem>
</ToggleGroup>
// Multi-selection.
<ToggleGroup multiple>
<ToggleGroupItem value="bold">Bold</ToggleGroupItem>
<ToggleGroupItem value="italic">Italic</ToggleGroupItem>
</ToggleGroup>
```
**Correct (radix):**
```tsx
// Single, defaultValue is a string.
<ToggleGroup type="single" defaultValue="daily" spacing={2}>
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
<ToggleGroupItem value="weekly">Weekly</ToggleGroupItem>
</ToggleGroup>
// Multi-selection.
<ToggleGroup type="multiple">
<ToggleGroupItem value="bold">Bold</ToggleGroupItem>
<ToggleGroupItem value="italic">Italic</ToggleGroupItem>
</ToggleGroup>
```
**Controlled single value:**
```tsx
// base — wrap/unwrap arrays.
const [value, setValue] = React.useState("normal")
<ToggleGroup value={[value]} onValueChange={(v) => setValue(v[0])}>
// radix — plain string.
const [value, setValue] = React.useState("normal")
<ToggleGroup type="single" value={value} onValueChange={setValue}>
```
---
## Slider
Base accepts a plain number for a single thumb. Radix always requires an array.
**Incorrect (base):**
```tsx
<Slider defaultValue={[50]} max={100} step={1} />
```
**Correct (base):**
```tsx
<Slider defaultValue={50} max={100} step={1} />
```
**Correct (radix):**
```tsx
<Slider defaultValue={[50]} max={100} step={1} />
```
Both use arrays for range sliders. Controlled `onValueChange` in base may need a cast:
```tsx
// base.
const [value, setValue] = React.useState([0.3, 0.7])
<Slider value={value} onValueChange={(v) => setValue(v as number[])} />
// radix.
const [value, setValue] = React.useState([0.3, 0.7])
<Slider value={value} onValueChange={setValue} />
```
---
## Accordion
Radix requires `type="single"` or `type="multiple"` and supports `collapsible`. `defaultValue` is a string. Base uses no `type` prop, uses `multiple` boolean, and `defaultValue` is always an array.
**Incorrect (base):**
```tsx
<Accordion type="single" collapsible defaultValue="item-1">
<AccordionItem value="item-1">...</AccordionItem>
</Accordion>
```
**Correct (base):**
```tsx
<Accordion defaultValue={["item-1"]}>
<AccordionItem value="item-1">...</AccordionItem>
</Accordion>
// Multi-select.
<Accordion multiple defaultValue={["item-1", "item-2"]}>
<AccordionItem value="item-1">...</AccordionItem>
<AccordionItem value="item-2">...</AccordionItem>
</Accordion>
```
**Correct (radix):**
```tsx
<Accordion type="single" collapsible defaultValue="item-1">
<AccordionItem value="item-1">...</AccordionItem>
</Accordion>
```

View File

@@ -0,0 +1,195 @@
# Component Composition
## Contents
- Items always inside their Group component
- Callouts use Alert
- Empty states use Empty component
- Toast notifications use sonner
- Choosing between overlay components
- Dialog, Sheet, and Drawer always need a Title
- Card structure
- Button has no isPending or isLoading prop
- TabsTrigger must be inside TabsList
- Avatar always needs AvatarFallback
- Use Separator instead of raw hr or border divs
- Use Skeleton for loading placeholders
- Use Badge instead of custom styled spans
---
## Items always inside their Group component
Never render items directly inside the content container.
**Incorrect:**
```tsx
<SelectContent>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
</SelectContent>
```
**Correct:**
```tsx
<SelectContent>
<SelectGroup>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
</SelectGroup>
</SelectContent>
```
This applies to all group-based components:
| Item | Group |
|------|-------|
| `SelectItem`, `SelectLabel` | `SelectGroup` |
| `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuSub` | `DropdownMenuGroup` |
| `MenubarItem` | `MenubarGroup` |
| `ContextMenuItem` | `ContextMenuGroup` |
| `CommandItem` | `CommandGroup` |
---
## Callouts use Alert
```tsx
<Alert>
<AlertTitle>Warning</AlertTitle>
<AlertDescription>Something needs attention.</AlertDescription>
</Alert>
```
---
## Empty states use Empty component
```tsx
<Empty>
<EmptyHeader>
<EmptyMedia variant="icon"><FolderIcon /></EmptyMedia>
<EmptyTitle>No projects yet</EmptyTitle>
<EmptyDescription>Get started by creating a new project.</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button>Create Project</Button>
</EmptyContent>
</Empty>
```
---
## Toast notifications use sonner
```tsx
import { toast } from "sonner"
toast.success("Changes saved.")
toast.error("Something went wrong.")
toast("File deleted.", {
action: { label: "Undo", onClick: () => undoDelete() },
})
```
---
## Choosing between overlay components
| Use case | Component |
|----------|-----------|
| Focused task that requires input | `Dialog` |
| Destructive action confirmation | `AlertDialog` |
| Side panel with details or filters | `Sheet` |
| Mobile-first bottom panel | `Drawer` |
| Quick info on hover | `HoverCard` |
| Small contextual content on click | `Popover` |
---
## Dialog, Sheet, and Drawer always need a Title
`DialogTitle`, `SheetTitle`, `DrawerTitle` are required for accessibility. Use `className="sr-only"` if visually hidden.
```tsx
<DialogContent>
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
<DialogDescription>Update your profile.</DialogDescription>
</DialogHeader>
...
</DialogContent>
```
---
## Card structure
Use full composition — don't dump everything into `CardContent`:
```tsx
<Card>
<CardHeader>
<CardTitle>Team Members</CardTitle>
<CardDescription>Manage your team.</CardDescription>
</CardHeader>
<CardContent>...</CardContent>
<CardFooter>
<Button>Invite</Button>
</CardFooter>
</Card>
```
---
## Button has no isPending or isLoading prop
Compose with `Spinner` + `data-icon` + `disabled`:
```tsx
<Button disabled>
<Spinner data-icon="inline-start" />
Saving...
</Button>
```
---
## TabsTrigger must be inside TabsList
Never render `TabsTrigger` directly inside `Tabs` — always wrap in `TabsList`:
```tsx
<Tabs defaultValue="account">
<TabsList>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">...</TabsContent>
</Tabs>
```
---
## Avatar always needs AvatarFallback
Always include `AvatarFallback` for when the image fails to load:
```tsx
<Avatar>
<AvatarImage src="/avatar.png" alt="User" />
<AvatarFallback>JD</AvatarFallback>
</Avatar>
```
---
## Use existing components instead of custom markup
| Instead of | Use |
|---|---|
| `<hr>` or `<div className="border-t">` | `<Separator />` |
| `<div className="animate-pulse">` with styled divs | `<Skeleton className="h-4 w-3/4" />` |
| `<span className="rounded-full bg-green-100 ...">` | `<Badge variant="secondary">` |

View File

@@ -0,0 +1,192 @@
# Forms & Inputs
## Contents
- Forms use FieldGroup + Field
- InputGroup requires InputGroupInput/InputGroupTextarea
- Buttons inside inputs use InputGroup + InputGroupAddon
- Option sets (27 choices) use ToggleGroup
- FieldSet + FieldLegend for grouping related fields
- Field validation and disabled states
---
## Forms use FieldGroup + Field
Always use `FieldGroup` + `Field` — never raw `div` with `space-y-*`:
```tsx
<FieldGroup>
<Field>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" type="email" />
</Field>
<Field>
<FieldLabel htmlFor="password">Password</FieldLabel>
<Input id="password" type="password" />
</Field>
</FieldGroup>
```
Use `Field orientation="horizontal"` for settings pages. Use `FieldLabel className="sr-only"` for visually hidden labels.
**Choosing form controls:**
- Simple text input → `Input`
- Dropdown with predefined options → `Select`
- Searchable dropdown → `Combobox`
- Native HTML select (no JS) → `native-select`
- Boolean toggle → `Switch` (for settings) or `Checkbox` (for forms)
- Single choice from few options → `RadioGroup`
- Toggle between 25 options → `ToggleGroup` + `ToggleGroupItem`
- OTP/verification code → `InputOTP`
- Multi-line text → `Textarea`
---
## InputGroup requires InputGroupInput/InputGroupTextarea
Never use raw `Input` or `Textarea` inside an `InputGroup`.
**Incorrect:**
```tsx
<InputGroup>
<Input placeholder="Search..." />
</InputGroup>
```
**Correct:**
```tsx
import { InputGroup, InputGroupInput } from "@/components/ui/input-group"
<InputGroup>
<InputGroupInput placeholder="Search..." />
</InputGroup>
```
---
## Buttons inside inputs use InputGroup + InputGroupAddon
Never place a `Button` directly inside or adjacent to an `Input` with custom positioning.
**Incorrect:**
```tsx
<div className="relative">
<Input placeholder="Search..." className="pr-10" />
<Button className="absolute right-0 top-0" size="icon">
<SearchIcon />
</Button>
</div>
```
**Correct:**
```tsx
import { InputGroup, InputGroupInput, InputGroupAddon } from "@/components/ui/input-group"
<InputGroup>
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<Button size="icon">
<SearchIcon data-icon="inline-start" />
</Button>
</InputGroupAddon>
</InputGroup>
```
---
## Option sets (27 choices) use ToggleGroup
Don't manually loop `Button` components with active state.
**Incorrect:**
```tsx
const [selected, setSelected] = useState("daily")
<div className="flex gap-2">
{["daily", "weekly", "monthly"].map((option) => (
<Button
key={option}
variant={selected === option ? "default" : "outline"}
onClick={() => setSelected(option)}
>
{option}
</Button>
))}
</div>
```
**Correct:**
```tsx
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
<ToggleGroup spacing={2}>
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
<ToggleGroupItem value="weekly">Weekly</ToggleGroupItem>
<ToggleGroupItem value="monthly">Monthly</ToggleGroupItem>
</ToggleGroup>
```
Combine with `Field` for labelled toggle groups:
```tsx
<Field orientation="horizontal">
<FieldTitle id="theme-label">Theme</FieldTitle>
<ToggleGroup aria-labelledby="theme-label" spacing={2}>
<ToggleGroupItem value="light">Light</ToggleGroupItem>
<ToggleGroupItem value="dark">Dark</ToggleGroupItem>
<ToggleGroupItem value="system">System</ToggleGroupItem>
</ToggleGroup>
</Field>
```
> **Note:** `defaultValue` and `type`/`multiple` props differ between base and radix. See [base-vs-radix.md](./base-vs-radix.md#togglegroup).
---
## FieldSet + FieldLegend for grouping related fields
Use `FieldSet` + `FieldLegend` for related checkboxes, radios, or switches — not `div` with a heading:
```tsx
<FieldSet>
<FieldLegend variant="label">Preferences</FieldLegend>
<FieldDescription>Select all that apply.</FieldDescription>
<FieldGroup className="gap-3">
<Field orientation="horizontal">
<Checkbox id="dark" />
<FieldLabel htmlFor="dark" className="font-normal">Dark mode</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
```
---
## Field validation and disabled states
Both attributes are needed — `data-invalid`/`data-disabled` styles the field (label, description), while `aria-invalid`/`disabled` styles the control.
```tsx
// Invalid.
<Field data-invalid>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" aria-invalid />
<FieldDescription>Invalid email address.</FieldDescription>
</Field>
// Disabled.
<Field data-disabled>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" disabled />
</Field>
```
Works for all controls: `Input`, `Textarea`, `Select`, `Checkbox`, `RadioGroupItem`, `Switch`, `Slider`, `NativeSelect`, `InputOTP`.

View File

@@ -0,0 +1,101 @@
# Icons
**Always use the project's configured `iconLibrary` for imports.** Check the `iconLibrary` field from project context: `lucide``lucide-react`, `tabler``@tabler/icons-react`, etc. Never assume `lucide-react`.
---
## Icons in Button use data-icon attribute
Add `data-icon="inline-start"` (prefix) or `data-icon="inline-end"` (suffix) to the icon. No sizing classes on the icon.
**Incorrect:**
```tsx
<Button>
<SearchIcon className="mr-2 size-4" />
Search
</Button>
```
**Correct:**
```tsx
<Button>
<SearchIcon data-icon="inline-start"/>
Search
</Button>
<Button>
Next
<ArrowRightIcon data-icon="inline-end"/>
</Button>
```
---
## No sizing classes on icons inside components
Components handle icon sizing via CSS. Don't add `size-4`, `w-4 h-4`, or other sizing classes to icons inside `Button`, `DropdownMenuItem`, `Alert`, `Sidebar*`, or other shadcn components. Unless the user explicitly asks for custom icon sizes.
**Incorrect:**
```tsx
<Button>
<SearchIcon className="size-4" data-icon="inline-start" />
Search
</Button>
<DropdownMenuItem>
<SettingsIcon className="mr-2 size-4" />
Settings
</DropdownMenuItem>
```
**Correct:**
```tsx
<Button>
<SearchIcon data-icon="inline-start" />
Search
</Button>
<DropdownMenuItem>
<SettingsIcon />
Settings
</DropdownMenuItem>
```
---
## Pass icons as component objects, not string keys
Use `icon={CheckIcon}`, not a string key to a lookup map.
**Incorrect:**
```tsx
const iconMap = {
check: CheckIcon,
alert: AlertIcon,
}
function StatusBadge({ icon }: { icon: string }) {
const Icon = iconMap[icon]
return <Icon />
}
<StatusBadge icon="check" />
```
**Correct:**
```tsx
// Import from the project's configured iconLibrary (e.g. lucide-react, @tabler/icons-react).
import { CheckIcon } from "lucide-react"
function StatusBadge({ icon: Icon }: { icon: React.ComponentType }) {
return <Icon />
}
<StatusBadge icon={CheckIcon} />
```

View File

@@ -0,0 +1,162 @@
# Styling & Customization
See [customization.md](../customization.md) for theming, CSS variables, and adding custom colors.
## Contents
- Semantic colors
- Built-in variants first
- className for layout only
- No space-x-* / space-y-*
- Prefer size-* over w-* h-* when equal
- Prefer truncate shorthand
- No manual dark: color overrides
- Use cn() for conditional classes
- No manual z-index on overlay components
---
## Semantic colors
**Incorrect:**
```tsx
<div className="bg-blue-500 text-white">
<p className="text-gray-600">Secondary text</p>
</div>
```
**Correct:**
```tsx
<div className="bg-primary text-primary-foreground">
<p className="text-muted-foreground">Secondary text</p>
</div>
```
---
## No raw color values for status/state indicators
For positive, negative, or status indicators, use Badge variants, semantic tokens like `text-destructive`, or define custom CSS variables — don't reach for raw Tailwind colors.
**Incorrect:**
```tsx
<span className="text-emerald-600">+20.1%</span>
<span className="text-green-500">Active</span>
<span className="text-red-600">-3.2%</span>
```
**Correct:**
```tsx
<Badge variant="secondary">+20.1%</Badge>
<Badge>Active</Badge>
<span className="text-destructive">-3.2%</span>
```
If you need a success/positive color that doesn't exist as a semantic token, use a Badge variant or ask the user about adding a custom CSS variable to the theme (see [customization.md](../customization.md)).
---
## Built-in variants first
**Incorrect:**
```tsx
<Button className="border border-input bg-transparent hover:bg-accent">
Click me
</Button>
```
**Correct:**
```tsx
<Button variant="outline">Click me</Button>
```
---
## className for layout only
Use `className` for layout (e.g. `max-w-md`, `mx-auto`, `mt-4`), **not** for overriding component colors or typography. To change colors, use semantic tokens, built-in variants, or CSS variables.
**Incorrect:**
```tsx
<Card className="bg-blue-100 text-blue-900 font-bold">
<CardContent>Dashboard</CardContent>
</Card>
```
**Correct:**
```tsx
<Card className="max-w-md mx-auto">
<CardContent>Dashboard</CardContent>
</Card>
```
To customize a component's appearance, prefer these approaches in order:
1. **Built-in variants**`variant="outline"`, `variant="destructive"`, etc.
2. **Semantic color tokens**`bg-primary`, `text-muted-foreground`.
3. **CSS variables** — define custom colors in the global CSS file (see [customization.md](../customization.md)).
---
## No space-x-* / space-y-*
Use `gap-*` instead. `space-y-4``flex flex-col gap-4`. `space-x-2``flex gap-2`.
```tsx
<div className="flex flex-col gap-4">
<Input />
<Input />
<Button>Submit</Button>
</div>
```
---
## Prefer size-* over w-* h-* when equal
`size-10` not `w-10 h-10`. Applies to icons, avatars, skeletons, etc.
---
## Prefer truncate shorthand
`truncate` not `overflow-hidden text-ellipsis whitespace-nowrap`.
---
## No manual dark: color overrides
Use semantic tokens — they handle light/dark via CSS variables. `bg-background text-foreground` not `bg-white dark:bg-gray-950`.
---
## Use cn() for conditional classes
Use the `cn()` utility from the project for conditional or merged class names. Don't write manual ternaries in className strings.
**Incorrect:**
```tsx
<div className={`flex items-center ${isActive ? "bg-primary text-primary-foreground" : "bg-muted"}`}>
```
**Correct:**
```tsx
import { cn } from "@/lib/utils"
<div className={cn("flex items-center", isActive ? "bg-primary text-primary-foreground" : "bg-muted")}>
```
---
## No manual z-index on overlay components
`Dialog`, `Sheet`, `Drawer`, `AlertDialog`, `DropdownMenu`, `Popover`, `Tooltip`, `HoverCard` handle their own stacking. Never add `z-50` or `z-[999]`.

View File

@@ -0,0 +1,594 @@
---
name: tailwindcss-advanced-layouts
description: Tailwind CSS advanced layout techniques including CSS Grid and Flexbox patterns
---
# Tailwind CSS Advanced Layout Techniques
## CSS Grid Mastery
### Complex Grid Layouts
```html
<!-- Holy Grail Layout -->
<div class="grid min-h-screen grid-rows-[auto_1fr_auto]">
<header class="bg-white shadow">Header</header>
<div class="grid grid-cols-[250px_1fr_300px]">
<aside class="bg-gray-50 p-4">Sidebar</aside>
<main class="p-6">Main Content</main>
<aside class="bg-gray-50 p-4">Right Sidebar</aside>
</div>
<footer class="bg-gray-800 text-white">Footer</footer>
</div>
<!-- Responsive Holy Grail -->
<div class="grid min-h-screen grid-rows-[auto_1fr_auto]">
<header>Header</header>
<div class="grid grid-cols-1 md:grid-cols-[250px_1fr] lg:grid-cols-[250px_1fr_300px]">
<aside class="order-2 md:order-1">Sidebar</aside>
<main class="order-1 md:order-2">Main</main>
<aside class="order-3 hidden lg:block">Right</aside>
</div>
<footer>Footer</footer>
</div>
```
### Grid Template Areas
```css
@utility grid-areas-dashboard {
grid-template-areas:
"header header header"
"nav main aside"
"nav footer footer";
}
@utility area-header { grid-area: header; }
@utility area-nav { grid-area: nav; }
@utility area-main { grid-area: main; }
@utility area-aside { grid-area: aside; }
@utility area-footer { grid-area: footer; }
```
```html
<div class="grid grid-areas-dashboard grid-cols-[200px_1fr_250px] grid-rows-[60px_1fr_40px] min-h-screen">
<header class="area-header bg-white shadow">Header</header>
<nav class="area-nav bg-gray-100">Navigation</nav>
<main class="area-main p-6">Main Content</main>
<aside class="area-aside bg-gray-50 p-4">Sidebar</aside>
<footer class="area-footer bg-gray-800 text-white">Footer</footer>
</div>
```
### Auto-Fill and Auto-Fit Grids
```html
<!-- Auto-fill: Creates as many tracks as fit, even empty ones -->
<div class="grid grid-cols-[repeat(auto-fill,minmax(250px,1fr))] gap-6">
<div class="bg-white rounded-lg shadow p-4">Card 1</div>
<div class="bg-white rounded-lg shadow p-4">Card 2</div>
<div class="bg-white rounded-lg shadow p-4">Card 3</div>
</div>
<!-- Auto-fit: Collapses empty tracks -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-6">
<!-- Cards stretch to fill available space -->
</div>
<!-- With arbitrary values -->
<div class="grid grid-cols-[repeat(auto-fill,minmax(min(100%,300px),1fr))] gap-4">
<!-- Handles edge case where container is smaller than minmax min -->
</div>
```
### Subgrid
```css
/* Enable subgrid in v4 */
@utility subgrid-cols {
grid-template-columns: subgrid;
}
@utility subgrid-rows {
grid-template-rows: subgrid;
}
```
```html
<div class="grid grid-cols-4 gap-4">
<!-- Span 2 columns but align children to parent grid -->
<div class="col-span-2 grid subgrid-cols gap-4">
<div>Aligned to parent column 1</div>
<div>Aligned to parent column 2</div>
</div>
</div>
```
## Advanced Flexbox Patterns
### Space Distribution
```html
<!-- Equal spacing with first/last at edges -->
<div class="flex justify-between">
<div>First</div>
<div>Second</div>
<div>Third</div>
</div>
<!-- Equal spacing everywhere including edges -->
<div class="flex justify-around">
<div>Item</div>
<div>Item</div>
<div>Item</div>
</div>
<!-- Double space between items vs edges -->
<div class="flex justify-evenly">
<div>Item</div>
<div>Item</div>
<div>Item</div>
</div>
```
### Flexible Item Sizing
```html
<!-- Items share space equally -->
<div class="flex">
<div class="flex-1">1/3</div>
<div class="flex-1">1/3</div>
<div class="flex-1">1/3</div>
</div>
<!-- First item takes 2x space -->
<div class="flex">
<div class="flex-[2]">2/4</div>
<div class="flex-1">1/4</div>
<div class="flex-1">1/4</div>
</div>
<!-- Fixed + flexible -->
<div class="flex">
<div class="w-64 shrink-0">Fixed 256px</div>
<div class="flex-1 min-w-0">Flexible (can shrink)</div>
</div>
<!-- Prevent shrinking with text overflow -->
<div class="flex min-w-0">
<div class="shrink-0">Icon</div>
<div class="min-w-0 truncate">Very long text that should truncate</div>
</div>
```
### Masonry-Like with Flexbox
```html
<!-- Column-based masonry -->
<div class="flex flex-col flex-wrap h-[800px] gap-4">
<div class="w-[calc(33.333%-1rem)] h-48">Item 1</div>
<div class="w-[calc(33.333%-1rem)] h-64">Item 2</div>
<div class="w-[calc(33.333%-1rem)] h-32">Item 3</div>
<!-- Items flow vertically then wrap to next column -->
</div>
```
## Container Queries
### Basic Container Queries
```css
@plugin "@tailwindcss/container-queries";
```
```html
<!-- Define container -->
<div class="@container">
<!-- Respond to container width -->
<div class="flex flex-col @md:flex-row @lg:grid @lg:grid-cols-3 gap-4">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
</div>
```
### Named Containers
```html
<!-- Multiple named containers -->
<div class="@container/sidebar">
<nav class="@[200px]/sidebar:flex-col @[300px]/sidebar:flex-row">
Navigation
</nav>
</div>
<div class="@container/main">
<article class="@[600px]/main:prose-lg @[900px]/main:prose-xl">
Content
</article>
</div>
```
### Container Query Units
```html
<!-- Size relative to container -->
<div class="@container">
<h1 class="text-[5cqw]">Scales with container width</h1>
<p class="text-[3cqi]">Scales with container inline size</p>
</div>
```
## Position and Layering
### Sticky Positioning
```html
<!-- Sticky header -->
<header class="sticky top-0 z-50 bg-white/80 backdrop-blur-sm border-b">
Navigation
</header>
<!-- Sticky sidebar -->
<aside class="sticky top-20 h-[calc(100vh-5rem)] overflow-auto">
Sidebar content
</aside>
<!-- Sticky table header -->
<div class="overflow-auto max-h-96">
<table>
<thead class="sticky top-0 bg-white shadow">
<tr>
<th class="sticky left-0 bg-white z-10">Corner cell</th>
<th>Column 2</th>
</tr>
</thead>
<tbody>...</tbody>
</table>
</div>
```
### Fixed Elements
```html
<!-- Fixed bottom navigation (mobile) -->
<nav class="fixed bottom-0 inset-x-0 z-50 bg-white border-t md:hidden">
<div class="flex justify-around py-2">
<a href="#">Home</a>
<a href="#">Search</a>
<a href="#">Profile</a>
</div>
</nav>
<!-- Fixed action button -->
<button class="fixed bottom-6 right-6 z-40 rounded-full bg-brand-500 p-4 shadow-lg">
<PlusIcon />
</button>
```
### Z-Index Management
```css
@theme {
--z-dropdown: 100;
--z-sticky: 200;
--z-fixed: 300;
--z-modal-backdrop: 400;
--z-modal: 500;
--z-popover: 600;
--z-tooltip: 700;
--z-toast: 800;
}
@utility z-dropdown { z-index: var(--z-dropdown); }
@utility z-sticky { z-index: var(--z-sticky); }
@utility z-fixed { z-index: var(--z-fixed); }
@utility z-modal-backdrop { z-index: var(--z-modal-backdrop); }
@utility z-modal { z-index: var(--z-modal); }
@utility z-popover { z-index: var(--z-popover); }
@utility z-tooltip { z-index: var(--z-tooltip); }
@utility z-toast { z-index: var(--z-toast); }
```
## Overflow and Scrolling
### Custom Scrollbars
```css
@utility scrollbar-thin {
scrollbar-width: thin;
}
@utility scrollbar-none {
scrollbar-width: none;
-ms-overflow-style: none;
}
@utility scrollbar-none::-webkit-scrollbar {
display: none;
}
/* Custom scrollbar styling */
@utility scrollbar-custom {
scrollbar-color: oklch(0.7 0 0) oklch(0.95 0 0);
}
@utility scrollbar-custom::-webkit-scrollbar {
width: 8px;
height: 8px;
}
@utility scrollbar-custom::-webkit-scrollbar-track {
background: oklch(0.95 0 0);
border-radius: 4px;
}
@utility scrollbar-custom::-webkit-scrollbar-thumb {
background: oklch(0.7 0 0);
border-radius: 4px;
}
@utility scrollbar-custom::-webkit-scrollbar-thumb:hover {
background: oklch(0.5 0 0);
}
```
### Scroll Snap
```html
<!-- Horizontal carousel -->
<div class="flex snap-x snap-mandatory overflow-x-auto gap-4 pb-4">
<div class="snap-start shrink-0 w-80">Card 1</div>
<div class="snap-start shrink-0 w-80">Card 2</div>
<div class="snap-start shrink-0 w-80">Card 3</div>
</div>
<!-- Full-page sections -->
<div class="h-screen snap-y snap-mandatory overflow-y-auto">
<section class="h-screen snap-start">Section 1</section>
<section class="h-screen snap-start">Section 2</section>
<section class="h-screen snap-start">Section 3</section>
</div>
<!-- Snap with padding -->
<div class="snap-x scroll-pl-6 overflow-x-auto">
<div class="snap-start">...</div>
</div>
```
### Scroll Margin for Anchors
```html
<!-- Offset for fixed header -->
<section id="about" class="scroll-mt-20">
<!-- Content appears below fixed header when linked -->
</section>
```
## Aspect Ratio and Object Fit
### Responsive Aspect Ratios
```html
<!-- Fixed aspect ratio container -->
<div class="aspect-video bg-gray-100">
<video class="h-full w-full object-cover">...</video>
</div>
<div class="aspect-square rounded-full overflow-hidden">
<img src="avatar.jpg" class="h-full w-full object-cover" />
</div>
<!-- Custom aspect ratio -->
<div class="aspect-[4/3]">4:3 content</div>
<div class="aspect-[21/9]">Ultra-wide content</div>
```
### Object Positioning
```html
<!-- Focus on specific part of image -->
<div class="h-64 overflow-hidden">
<img
src="portrait.jpg"
class="h-full w-full object-cover object-top"
/>
</div>
<!-- Arbitrary object position -->
<img class="object-cover object-[25%_75%]" src="..." />
```
## Advanced Spacing
### Logical Properties
```html
<!-- Works for LTR and RTL -->
<div class="ps-4 pe-6 ms-auto">
Padding and margin that respect text direction
</div>
<!-- Block direction (vertical in horizontal writing modes) -->
<div class="pbs-4 pbe-6 mbs-auto">
Block-direction spacing
</div>
```
### Space Between with Dividers
```html
<!-- Dividers between items -->
<ul class="divide-y divide-gray-200">
<li class="py-4">Item 1</li>
<li class="py-4">Item 2</li>
<li class="py-4">Item 3</li>
</ul>
<!-- Horizontal dividers -->
<div class="flex divide-x divide-gray-200">
<div class="px-4">Section 1</div>
<div class="px-4">Section 2</div>
<div class="px-4">Section 3</div>
</div>
```
### Negative Margins for Bleeds
```html
<!-- Full-bleed image in padded container -->
<article class="px-6">
<p>Padded content</p>
<img src="hero.jpg" class="-mx-6 w-[calc(100%+3rem)]" />
<p>More padded content</p>
</article>
<!-- Pull quote that breaks out -->
<div class="max-w-prose mx-auto px-4">
<p>Normal content...</p>
<blockquote class="-mx-8 md:-mx-16 px-8 md:px-16 py-8 bg-gray-100">
Featured quote that extends beyond content width
</blockquote>
</div>
```
## Multi-Column Layout
### Text Columns
```html
<!-- Responsive columns -->
<div class="columns-1 sm:columns-2 lg:columns-3 gap-8">
<p>Content flows across columns...</p>
</div>
<!-- Fixed-width columns -->
<div class="columns-[300px] gap-6">
<p>Creates as many 300px columns as fit</p>
</div>
<!-- Prevent breaks inside elements -->
<div class="columns-2">
<div class="break-inside-avoid mb-4">
Card that stays together
</div>
</div>
```
## Responsive Patterns
### Container Queries + Media Queries
```html
<div class="@container">
<div class="
/* Container query for component-level responsiveness */
@md:flex @md:gap-4
/* Media query for page-level responsiveness */
lg:grid lg:grid-cols-2
">
Content
</div>
</div>
```
### Breakpoint-Based Visibility
```html
<!-- Show different content per breakpoint -->
<nav>
<!-- Mobile menu button -->
<button class="md:hidden">Menu</button>
<!-- Desktop navigation -->
<ul class="hidden md:flex gap-4">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</nav>
```
### Fluid Sizing with Clamp
```html
<!-- Fluid padding -->
<section class="py-[clamp(2rem,5vw,6rem)] px-[clamp(1rem,3vw,4rem)]">
Content with responsive padding
</section>
<!-- Fluid max-width -->
<div class="mx-auto w-full max-w-[clamp(300px,90vw,1200px)]">
Responsive container
</div>
```
## Print Styles
```html
<!-- Hide elements when printing -->
<nav class="print:hidden">Navigation</nav>
<!-- Show only when printing -->
<div class="hidden print:block">Print-only content</div>
<!-- Print-specific styles -->
<article class="print:text-black print:bg-white">
<h1 class="text-2xl print:text-xl">Heading</h1>
<a href="..." class="text-blue-500 print:text-black print:underline">
Link (shows as text when printed)
</a>
</article>
<!-- Prevent page breaks -->
<div class="print:break-inside-avoid">
Keep this content together on one page
</div>
<!-- Force page break -->
<div class="print:break-before-page">
Start on new page
</div>
```
## Best Practices
### 1. Use Modern Layout Methods
```html
<!-- Prefer Grid for 2D layouts -->
<div class="grid grid-cols-3 gap-4">
<!-- Prefer Flexbox for 1D layouts -->
<div class="flex items-center gap-2">
```
### 2. Handle Edge Cases
```html
<!-- Prevent flex item from overflowing -->
<div class="flex min-w-0">
<div class="min-w-0 truncate">Long text</div>
</div>
<!-- Prevent grid blowout -->
<div class="grid grid-cols-1 min-w-0">
<div class="overflow-hidden">Content that might overflow</div>
</div>
```
### 3. Use Semantic Sizing
```html
<!-- Prefer max-w-prose for reading content -->
<article class="max-w-prose mx-auto">
<!-- Use container for page sections -->
<div class="container mx-auto px-4">
```
### 4. Test All Breakpoints
Create systematic tests for all responsive layouts to ensure they work at every breakpoint.

View File

@@ -0,0 +1,5 @@
# Generation Info
- **Source:** `sources/tailwindcss`
- **Git SHA:** `148fcb26ab87892177ec9a7eba4b31e2ee960039`
- **Generated:** 2026-01-28

View File

@@ -0,0 +1,150 @@
---
name: tailwindcss
description: Tailwind CSS utility-first CSS framework. Use when styling web applications with utility classes, building responsive designs, or customizing design systems with theme variables.
metadata:
author: Hairyf
version: "2026.2.2"
source: Generated from https://github.com/tailwindlabs/tailwindcss.com, scripts located at https://github.com/hairyf/skills
---
# Tailwind CSS
> The skill is based on Tailwind CSS v4.1.18, generated at 2026-01-28.
Tailwind CSS is a utility-first CSS framework for rapidly building custom user interfaces. Instead of writing custom CSS, you compose designs using utility classes directly in your markup. Tailwind v4 introduces CSS-first configuration with theme variables, making it easier to customize your design system.
## Core References
| Topic | Description | Reference |
|-------|-------------|-----------|
| Installation | Vite, PostCSS, CLI, and CDN setup | [core-installation](references/core-installation.md) |
| Utility Classes | Understanding Tailwind's utility-first approach and styling elements | [core-utility-classes](references/core-utility-classes.md) |
| Theme Variables | Design tokens, customizing theme, and theme variable namespaces | [core-theme](references/core-theme.md) |
| Responsive Design | Mobile-first breakpoints, responsive variants, and container queries | [core-responsive](references/core-responsive.md) |
| Variants | Applying utilities conditionally with state, pseudo-class, and media query variants | [core-variants](references/core-variants.md) |
| Preflight | Tailwind's base styles and how to extend or disable them | [core-preflight](references/core-preflight.md) |
## Layout
### Display & Flexbox & Grid
| Topic | Description | Reference |
|-------|-------------|-----------|
| Display | flex, grid, block, inline, hidden, sr-only, flow-root, contents | [layout-display](references/layout-display.md) |
| Flexbox | flex-direction, justify, items, gap, grow, shrink, wrap, order | [layout-flexbox](references/layout-flexbox.md) |
| Grid | grid-cols, grid-rows, gap, place-items, col-span, row-span, subgrid | [layout-grid](references/layout-grid.md) |
| Aspect Ratio | Controlling element aspect ratio for responsive media | [layout-aspect-ratio](references/layout-aspect-ratio.md) |
| Columns | Multi-column layout for magazine-style or masonry layouts | [layout-columns](references/layout-columns.md) |
### Positioning
| Topic | Description | Reference |
|-------|-------------|-----------|
| Position | Controlling element positioning with static, relative, absolute, fixed, and sticky | [layout-position](references/layout-position.md) |
| Inset | Controlling placement of positioned elements with top, right, bottom, left, and inset utilities | [layout-inset](references/layout-inset.md) |
### Sizing
| Topic | Description | Reference |
|-------|-------------|-----------|
| Width | Setting element width with spacing scale, fractions, container sizes, and viewport units | [layout-width](references/layout-width.md) |
| Height | Setting element height with spacing scale, fractions, viewport units, and content-based sizing | [layout-height](references/layout-height.md) |
| Min & Max Sizing | min-width, max-width, min-height, max-height constraints | [layout-min-max-sizing](references/layout-min-max-sizing.md) |
### Spacing
| Topic | Description | Reference |
|-------|-------------|-----------|
| Margin | Controlling element margins with spacing scale, negative values, logical properties, and space utilities | [layout-margin](references/layout-margin.md) |
| Padding | Controlling element padding with spacing scale, logical properties, and directional utilities | [layout-padding](references/layout-padding.md) |
### Overflow
| Topic | Description | Reference |
|-------|-------------|-----------|
| Overflow | Controlling how elements handle content that overflows their container | [layout-overflow](references/layout-overflow.md) |
### Images & Replaced Elements
| Topic | Description | Reference |
|-------|-------------|-----------|
| Object Fit & Position | Controlling how images and video are resized and positioned | [layout-object-fit-position](references/layout-object-fit-position.md) |
### Tables
| Topic | Description | Reference |
|-------|-------------|-----------|
| Table Layout | border-collapse, table-auto, table-fixed | [layout-tables](references/layout-tables.md) |
## Transforms
| Topic | Description | Reference |
|-------|-------------|-----------|
| Transform Base | Base transform utilities for enabling transforms, hardware acceleration, and custom transform values | [transform-base](references/transform-base.md) |
| Translate | Translating elements on x, y, and z axes with spacing scale, percentages, and custom values | [transform-translate](references/transform-translate.md) |
| Rotate | Rotating elements in 2D and 3D space with degree values and custom rotations | [transform-rotate](references/transform-rotate.md) |
| Scale | Scaling elements uniformly or on specific axes with percentage values | [transform-scale](references/transform-scale.md) |
| Skew | Skewing elements on x and y axes with degree values | [transform-skew](references/transform-skew.md) |
## Typography
| Topic | Description | Reference |
|-------|-------------|-----------|
| Font & Text | Font size, weight, color, line-height, letter-spacing, decoration, truncate | [typography-font-text](references/typography-font-text.md) |
| Text Align | Controlling text alignment with left, center, right, justify, and logical properties | [typography-text-align](references/typography-text-align.md) |
| List Style | list-style-type, list-style-position for bullets and markers | [typography-list-style](references/typography-list-style.md) |
## Visual
| Topic | Description | Reference |
|-------|-------------|-----------|
| Background | Background color, gradient, image, size, position | [visual-background](references/visual-background.md) |
| Border | Border width, color, radius, divide, ring | [visual-border](references/visual-border.md) |
| Effects | Box shadow, opacity, mix-blend, backdrop-blur, filter | [visual-effects](references/visual-effects.md) |
| SVG | fill, stroke, stroke-width for SVG and icon styling | [visual-svg](references/visual-svg.md) |
## Effects & Interactivity
| Topic | Description | Reference |
|-------|-------------|-----------|
| Transition & Animation | CSS transitions, animation keyframes, reduced motion | [effects-transition-animation](references/effects-transition-animation.md) |
| Visibility & Interactivity | Visibility, cursor, pointer-events, user-select, z-index | [effects-visibility-interactivity](references/effects-visibility-interactivity.md) |
| Form Controls | accent-color, appearance, caret-color, resize | [effects-form-controls](references/effects-form-controls.md) |
| Scroll Snap | scroll-snap-type, scroll-snap-align for carousels | [effects-scroll-snap](references/effects-scroll-snap.md) |
## Features
### Dark Mode
| Topic | Description | Reference |
|-------|-------------|-----------|
| Dark Mode | Implementing dark mode with the dark variant and custom strategies | [features-dark-mode](references/features-dark-mode.md) |
### Migration
| Topic | Description | Reference |
|-------|-------------|-----------|
| Upgrade Guide | Migrating from v3 to v4, breaking changes, rename mappings | [features-upgrade](references/features-upgrade.md) |
### Customization
| Topic | Description | Reference |
|-------|-------------|-----------|
| Custom Styles | Adding custom styles, utilities, variants, and working with arbitrary values | [features-custom-styles](references/features-custom-styles.md) |
| Functions & Directives | Tailwind's CSS directives and functions for working with your design system | [features-functions-directives](references/features-functions-directives.md) |
| Content Detection | How Tailwind detects classes and how to customize content scanning | [features-content-detection](references/features-content-detection.md) |
## Best Practices
| Topic | Description | Reference |
|-------|-------------|-----------|
| Utility Patterns | Managing duplication, conflicts, important modifier, when to use components | [best-practices-utility-patterns](references/best-practices-utility-patterns.md) |
## Key Recommendations
- **Use utility classes directly in markup** - Compose designs by combining utilities
- **Customize with theme variables** - Use `@theme` directive to define design tokens
- **Mobile-first responsive design** - Use unprefixed utilities for mobile, prefixed for breakpoints
- **Use complete class names** - Never construct classes dynamically with string interpolation
- **Leverage variants** - Stack variants for complex conditional styling
- **Prefer CSS-first configuration** - Use `@theme`, `@utility`, and `@custom-variant` over JavaScript configs

View File

@@ -0,0 +1,87 @@
---
name: best-practices-utility-patterns
description: Managing duplication, conflicts, important modifier, and when to use components
---
# Best Practices: Utility Patterns
Practical patterns when building with Tailwind utilities.
## Managing duplication
**Use components** for repeated UI: Extract into React/Vue/Svelte components or template partials.
```jsx
function Button({ children, variant = 'primary' }) {
const base = 'px-4 py-2 rounded-lg font-medium'
const variants = {
primary: 'bg-blue-500 hover:bg-blue-600 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900',
}
return <button className={`${base} ${variants[variant]}`}>{children}</button>
}
```
**Use loops** when markup is repeated from data—the class list is written once.
**Multi-cursor editing** for localized duplication in a single file.
## Managing conflicts
When two utilities target the same property, the one **later in the stylesheet** wins. Avoid adding conflicting classes:
```html
<!-- Bad -->
<div class="flex grid">Conflict</div>
<!-- Good: conditional class -->
<div class={gridLayout ? 'grid' : 'flex'}>
```
## Important modifier
Add `!` suffix to force `!important`:
```html
<div class="bg-teal-500 bg-red-500!">Red wins</div>
```
Use sparingly; prefer fixing specificity properly.
## Important flag (global)
```css
@import "tailwindcss" important;
```
Makes all utilities `!important`. Useful when integrating into existing high-specificity CSS.
## Prefix option
```css
@import "tailwindcss" prefix(tw);
```
Generates `tw:text-red-500` etc. Use when project class names conflict with Tailwind.
## When to use inline styles
- Values from API/database (e.g. brand colors)
- Dynamic values that change at runtime
- Complex arbitrary values hard to read as class names
- Pattern: set CSS variables via inline style, reference with `bg-(--my-var)`
## Key Points
- Extract repeated patterns into components
- Never add two conflicting utilities—use conditional classes
- `!` suffix = single utility `!important`
- `important` flag = all utilities `!important`
- `prefix(tw)` = prefix all utilities
- Use inline styles for dynamic values; utilities for static design
<!--
Source references:
- https://tailwindcss.com/docs/utility-first#managing-duplication
- https://tailwindcss.com/docs/utility-first#managing-style-conflicts
-->

View File

@@ -0,0 +1,89 @@
---
name: core-installation
description: Installing Tailwind CSS with Vite, PostCSS, CLI, or CDN
---
# Installation
How to add Tailwind CSS to a project.
## Vite (Recommended)
```bash
npm install tailwindcss @tailwindcss/vite
```
```ts
// vite.config.ts
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})
```
```css
/* style.css */
@import "tailwindcss";
```
## PostCSS
```bash
npm install tailwindcss @tailwindcss/postcss postcss
```
```js
// postcss.config.js
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}
```
```css
@import "tailwindcss";
```
## Tailwind CLI
```bash
npx @tailwindcss/cli -i ./src/input.css -o ./dist/output.css --watch
```
```css
/* input.css */
@import "tailwindcss";
```
## Play CDN (Development only)
```html
<script src="https://cdn.tailwindcss.com"></script>
```
Not for production: no purging, larger payload.
## Framework guides
- **Nuxt**: `@nuxtjs/tailwindcss` module or `@tailwindcss/vite`
- **Next.js**: Use Vite or PostCSS with `tailwind.config.js` if needed
- **React Router / SvelteKit / SolidJS**: Use `@tailwindcss/vite`
## Key Points
- Vite: `@tailwindcss/vite` plugin + `@import "tailwindcss"`
- PostCSS: `@tailwindcss/postcss`
- CLI: `npx @tailwindcss/cli`
- v4 uses CSS-first config; no `tailwind.config.js` required for basics
- Use `@theme` in CSS to customize design tokens
<!--
Source references:
- https://tailwindcss.com/docs/installation/using-vite
- https://tailwindcss.com/docs/installation/using-postcss
- https://tailwindcss.com/docs/installation/tailwind-cli
- https://tailwindcss.com/docs/installation/play-cdn
-->

View File

@@ -0,0 +1,200 @@
---
name: core-preflight
description: Understanding Tailwind's Preflight base styles and how to extend or disable them
---
# Preflight
Preflight is Tailwind's opinionated set of base styles that smooth over cross-browser inconsistencies.
## Overview
Built on top of modern-normalize, Preflight is automatically injected when you import `tailwindcss`:
```css
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css" layer(utilities);
```
## Key Resets
### Margins Removed
All default margins are removed from headings, paragraphs, blockquotes, etc:
```css
*,
::after,
::before {
margin: 0;
padding: 0;
}
```
This prevents accidentally relying on browser default margins that aren't part of your spacing scale.
### Border Styles Reset
Borders are reset to make adding borders easier:
```css
*,
::after,
::before {
box-sizing: border-box;
border: 0 solid;
}
```
Since the `border` utility only sets `border-width`, this ensures adding `border` always creates a solid `1px` border using `currentColor`.
### Headings Unstyled
All headings are unstyled by default:
```css
h1, h2, h3, h4, h5, h6 {
font-size: inherit;
font-weight: inherit;
}
```
**Reasons:**
- Avoids deviating from your type scale
- In UI development, headings should often be visually de-emphasized
### Lists Unstyled
Ordered and unordered lists have no bullets or numbers:
```css
ol, ul, menu {
list-style: none;
}
```
Style lists using utilities:
```html
<ul class="list-inside list-disc">
<li>One</li>
<li>Two</li>
</ul>
```
**Accessibility:** Add `role="list"` for screen readers when keeping lists unstyled:
```html
<ul role="list">
<li>One</li>
<li>Two</li>
</ul>
```
### Images Are Block-Level
Images and replaced elements are `display: block`:
```css
img, svg, video, canvas, audio, iframe, embed, object {
display: block;
vertical-align: middle;
}
```
Use `inline` utility if needed:
```html
<img class="inline" src="..." alt="..." />
```
### Images Are Constrained
Images and videos are constrained to parent width:
```css
img, video {
max-width: 100%;
height: auto;
}
```
Override with `max-w-none`:
```html
<img class="max-w-none" src="..." alt="..." />
```
### Hidden Attribute
Elements with `hidden` attribute stay hidden:
```css
[hidden]:where(:not([hidden="until-found"])) {
display: none !important;
}
```
## Extending Preflight
Add base styles to the `base` layer:
```css
@layer base {
h1 {
font-size: var(--text-2xl);
font-weight: 600;
}
h2 {
font-size: var(--text-xl);
font-weight: 600;
}
a {
color: var(--color-blue-600);
text-decoration-line: underline;
}
}
```
## Disabling Preflight
Import Tailwind components individually, omitting Preflight:
```css
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
/* @import "tailwindcss/preflight.css" layer(base); */ /* Omitted */
@import "tailwindcss/utilities.css" layer(utilities);
```
## Working Around Third-Party Libraries
Some libraries may conflict with Preflight. Override Preflight styles:
```css
@layer base {
.google-map * {
border-style: none;
}
}
```
## Key Points
- Preflight normalizes cross-browser inconsistencies
- Margins, borders, headings, and lists are reset
- Images are block-level and constrained by default
- Extend Preflight with `@layer base`
- Disable by omitting the preflight import
- Override Preflight styles when needed for third-party libraries
<!--
Source references:
- https://tailwindcss.com/docs/preflight
-->

View File

@@ -0,0 +1,163 @@
---
name: core-responsive
description: Building responsive designs with Tailwind's mobile-first breakpoint system and container queries
---
# Responsive Design
Every utility class in Tailwind can be applied conditionally at different breakpoints using responsive variants.
## Mobile-First Breakpoints
Tailwind uses a mobile-first approach. Unprefixed utilities apply to all screen sizes, while prefixed utilities apply at that breakpoint and above.
```html
<!-- Center text on mobile, left align on sm screens and up -->
<div class="text-center sm:text-left">Content</div>
```
## Default Breakpoints
| Breakpoint | Minimum Width | CSS |
|------------|---------------|-----|
| `sm` | 40rem (640px) | `@media (width >= 40rem)` |
| `md` | 48rem (768px) | `@media (width >= 48rem)` |
| `lg` | 64rem (1024px) | `@media (width >= 64rem)` |
| `xl` | 80rem (1280px) | `@media (width >= 80rem)` |
| `2xl` | 96rem (1536px) | `@media (width >= 96rem)` |
## Usage
Prefix any utility with a breakpoint name:
```html
<!-- Width of 16 by default, 32 on medium screens, 48 on large screens -->
<img class="w-16 md:w-32 lg:w-48" src="..." />
```
## Example: Responsive Card
```html
<div class="mx-auto max-w-md md:max-w-2xl">
<div class="md:flex">
<div class="md:shrink-0">
<img class="h-48 w-full object-cover md:h-full md:w-48" src="..." />
</div>
<div class="p-8">
<h2 class="text-lg font-semibold">Title</h2>
<p class="text-gray-500">Description</p>
</div>
</div>
</div>
```
## Targeting Mobile Screens
Use unprefixed utilities for mobile, not `sm:`. Think of `sm:` as "at the small breakpoint", not "on small screens".
```html
<!-- ❌ Only centers on screens 640px+, not mobile -->
<div class="sm:text-center"></div>
<!-- ✅ Centers on mobile, left aligns on 640px+ -->
<div class="text-center sm:text-left"></div>
```
## Breakpoint Ranges
Target a specific breakpoint range by stacking responsive variants with `max-*` variants:
```html
<!-- Only applies between md and xl -->
<div class="md:max-xl:flex">Content</div>
```
## Max-Width Variants
Tailwind generates `max-*` variants for each breakpoint:
| Variant | Media Query |
|---------|-------------|
| `max-sm` | `@media (width < 40rem)` |
| `max-md` | `@media (width < 48rem)` |
| `max-lg` | `@media (width < 64rem)` |
| `max-xl` | `@media (width < 80rem)` |
| `max-2xl` | `@media (width < 96rem)` |
## Custom Breakpoints
Add custom breakpoints using `--breakpoint-*` theme variables:
```css
@theme {
--breakpoint-xs: 30rem;
--breakpoint-3xl: 120rem;
}
```
```html
<div class="xs:grid-cols-2 3xl:grid-cols-6">Content</div>
```
## Arbitrary Breakpoints
Use arbitrary values for one-off breakpoints:
```html
<div class="min-[320px]:text-center max-[600px]:bg-sky-300">
Content
</div>
```
## Container Queries
Style elements based on parent container size instead of viewport:
```html
<!-- Mark container -->
<div class="@container">
<!-- Style based on container size -->
<div class="flex flex-col @md:flex-row">
Content
</div>
</div>
```
### Container Query Variants
| Variant | Minimum Width |
|---------|---------------|
| `@3xs` | 16rem (256px) |
| `@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 Containers
Name containers to target specific ones in nested structures:
```html
<div class="@container/main">
<div class="flex flex-row @sm/main:flex-col">
Content
</div>
</div>
```
## Key Points
- Mobile-first: unprefixed = mobile, prefixed = breakpoint and up
- Use unprefixed utilities for mobile, not `sm:`
- Stack variants for complex responsive behavior
- Container queries enable component-based responsive design
- Customize breakpoints with theme variables
<!--
Source references:
- https://tailwindcss.com/docs/responsive-design
-->

View File

@@ -0,0 +1,108 @@
---
name: core-theme
description: Understanding theme variables, design tokens, and customizing Tailwind's default theme
---
# Theme Variables
Theme variables are special CSS variables defined using the `@theme` directive that influence which utility classes exist in your project.
## Overview
Theme variables store design tokens like colors, fonts, spacing, shadows, and breakpoints. They're defined using the `@theme` directive:
```css
@import "tailwindcss";
@theme {
--color-mint-500: oklch(0.72 0.11 178);
--font-display: "Satoshi", "sans-serif";
--breakpoint-3xl: 120rem;
}
```
Now utilities like `bg-mint-500`, `font-display`, and `3xl:grid-cols-6` become available.
## Why @theme Instead of :root?
Theme variables aren't just CSS variables - they also instruct Tailwind to create new utility classes. Using `@theme` makes this explicit and ensures variables are defined top-level.
Use `@theme` for design tokens that map to utilities. Use `:root` for regular CSS variables that shouldn't have corresponding utilities.
## Theme Variable Namespaces
Theme variables are organized into namespaces that map to utility classes:
| Namespace | Utility Classes |
|-----------|----------------|
| `--color-*` | `bg-red-500`, `text-sky-300`, `border-indigo-600`, etc. |
| `--font-*` | `font-sans`, `font-serif`, `font-mono` |
| `--breakpoint-*` | Responsive variants like `md:`, `lg:`, `xl:` |
| `--spacing-*` | Spacing scale for padding, margin, gap utilities |
| `--shadow-*` | `shadow-sm`, `shadow-md`, `shadow-lg` |
| `--ease-*` | Transition timing functions |
## Extending the Default Theme
Add new theme variables to extend the default theme:
```css
@import "tailwindcss";
@theme {
/* Add new color */
--color-brand-500: oklch(0.65 0.2 250);
/* Add new breakpoint */
--breakpoint-3xl: 120rem;
/* Add new font */
--font-display: "Satoshi", "sans-serif";
}
```
## Using Theme Variables
Tailwind generates CSS variables for your theme variables, so you can reference them:
```html
<!-- Use utility classes -->
<div class="bg-brand-500 text-display">Content</div>
<!-- Reference CSS variables directly -->
<div style="background-color: var(--color-brand-500)">
Content
</div>
```
## Default Theme
When you import `tailwindcss`, it includes default theme variables:
```css
@layer theme, base, components, utilities;
@import "./theme.css" layer(theme);
@import "./preflight.css" layer(base);
@import "./utilities.css" layer(utilities);
```
The default theme includes:
- Color palette (red, blue, green, etc. with 50-950 shades)
- Font families (sans, serif, mono)
- Spacing scale
- Shadows
- Breakpoints (sm, md, lg, xl, 2xl)
## Key Points
- Theme variables define which utilities exist in your project
- Use `@theme` directive to define design tokens
- Variables must be defined top-level, not nested
- Tailwind generates both utilities and CSS variables
- Default theme provides a solid starting point
<!--
Source references:
- https://tailwindcss.com/docs/theme
-->

View File

@@ -0,0 +1,59 @@
---
name: core-utility-classes
description: Understanding Tailwind's utility-first approach and how to style elements with utility classes
---
# Utility Classes
Tailwind CSS uses a utility-first approach where you style elements by combining many single-purpose utility classes directly in your markup.
## Overview
Instead of writing custom CSS, you compose designs using utility classes:
```html
<div class="mx-auto flex max-w-sm items-center gap-x-4 rounded-xl bg-white p-6 shadow-lg">
<img class="size-12 shrink-0" src="/logo.svg" alt="Logo" />
<div>
<div class="text-xl font-medium text-black">ChitChat</div>
<p class="text-gray-500">You have a new message!</p>
</div>
</div>
```
## Benefits
- **Faster development** - No need to come up with class names or switch between HTML and CSS files
- **Safer changes** - Adding or removing utilities only affects that element
- **Easier maintenance** - Find the element and change classes, no need to remember custom CSS
- **More portable** - Copy entire UI chunks between projects easily
- **Smaller CSS** - Utilities are reusable, so CSS doesn't grow linearly
## Why Not Inline Styles?
Utility classes have important advantages over inline styles:
- **Design constraints** - Choose from a predefined design system instead of magic numbers
- **State variants** - Target hover, focus, and other states with variants like `hover:bg-blue-600`
- **Responsive design** - Use responsive variants like `md:flex` for media queries
## Utility Class Structure
Utility classes follow consistent naming patterns:
- **Property-value**: `bg-blue-500`, `text-lg`, `rounded-xl`
- **Responsive**: `md:flex`, `lg:text-center`
- **State variants**: `hover:bg-blue-600`, `focus:outline-2`
- **Arbitrary values**: `top-[117px]`, `bg-[#bada55]`
## Key Points
- Every utility class is single-purpose and composable
- Utilities can be combined with variants for conditional styling
- Use complete class names - Tailwind scans your files as plain text
- Avoid dynamically constructing class names with string interpolation
<!--
Source references:
- https://tailwindcss.com/docs/styling-with-utility-classes
-->

View File

@@ -0,0 +1,156 @@
---
name: core-variants
description: Using variants to apply utilities conditionally based on states, pseudo-classes, and media queries
---
# Variants
Variants let you apply utility classes conditionally based on states, pseudo-classes, pseudo-elements, media queries, and more.
## Overview
Add a variant prefix to any utility class to apply it conditionally:
```html
<!-- Apply bg-sky-700 only on hover -->
<button class="bg-sky-500 hover:bg-sky-700">Save</button>
```
Variants can be stacked to target specific situations:
```html
<!-- Dark mode, medium breakpoint, on hover -->
<button class="dark:md:hover:bg-fuchsia-600">Save</button>
```
## Pseudo-Class Variants
### Interactive States
```html
<button class="bg-violet-500 hover:bg-violet-600 focus:outline-2 focus:outline-violet-500 active:bg-violet-700">
Save changes
</button>
```
Common interactive variants:
- `hover:` - `:hover` pseudo-class
- `focus:` - `:focus` pseudo-class
- `active:` - `:active` pseudo-class
- `focus-visible:` - `:focus-visible`
- `focus-within:` - `:focus-within`
- `visited:` - `:visited`
### Structural Variants
```html
<ul>
<li class="py-4 first:pt-0 last:pb-0">Item 1</li>
<li class="py-4 first:pt-0 last:pb-0">Item 2</li>
<li class="py-4 first:pt-0 last:pb-0">Item 3</li>
</ul>
```
Common structural variants:
- `first:` - `:first-child`
- `last:` - `:last-child`
- `odd:` - `:nth-child(odd)`
- `even:` - `:nth-child(even)`
- `only:` - `:only-child`
### Form States
```html
<input class="border-gray-300 required:border-red-500 invalid:border-red-500" />
```
Common form variants:
- `required:` - `:required`
- `optional:` - `:optional`
- `invalid:` - `:invalid`
- `valid:` - `:valid`
- `disabled:` - `:disabled`
- `enabled:` - `:enabled`
- `checked:` - `:checked`
## Pseudo-Element Variants
```html
<input class="placeholder:text-gray-400 before:content-['*'] after:content-['required']" />
```
Common pseudo-element variants:
- `before:` - `::before`
- `after:` - `::after`
- `placeholder:` - `::placeholder`
- `selection:` - `::selection`
- `first-line:` - `::first-line`
- `first-letter:` - `::first-letter`
## Media Query Variants
### Responsive Variants
```html
<div class="text-sm md:text-base lg:text-lg">Responsive text</div>
```
### Dark Mode
```html
<div class="bg-white dark:bg-gray-800 text-gray-900 dark:text-white">
Content
</div>
```
By default uses `prefers-color-scheme`, but can be customized to use a class or data attribute.
### Reduced Motion
```html
<div class="transition-all motion-reduce:transition-none">
Animated content
</div>
```
## Attribute Selector Variants
```html
<!-- RTL support -->
<div class="text-left rtl:text-right">Content</div>
<!-- Open state -->
<details class="[&[open]]:bg-gray-100">Details</details>
```
## Arbitrary Variants
Use arbitrary variants for custom selectors:
```html
<ul>
<li class="lg:[&:nth-child(-n+3)]:hover:underline">Item</li>
</ul>
```
## Child Selector Variants
```html
<div class="[&>*]:mb-4 [&>*:last-child]:mb-0">
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</div>
```
## Key Points
- Variants prefix utilities to apply them conditionally
- Variants can be stacked: `dark:md:hover:bg-blue-600`
- Use variants for states, pseudo-classes, media queries, and more
- Arbitrary variants enable custom selector patterns
- Child selector variants target descendant elements
<!--
Source references:
- https://tailwindcss.com/docs/hover-focus-and-other-states
-->

View File

@@ -0,0 +1,76 @@
---
name: effects-form-controls
description: Form control styling with accent-color, appearance, caret-color, and resize
---
# Form Controls & Input Styling
Utilities for styling form controls: accent color, native appearance, caret color, and resize behavior.
## Usage
### Accent color
Control the accent color of checkboxes, radio buttons, range inputs, and progress:
```html
<input type="checkbox" class="accent-pink-500" checked />
<input type="radio" class="accent-rose-500" name="opt" />
<input type="range" class="accent-indigo-500" />
<!-- Opacity modifier -->
<input type="checkbox" class="accent-purple-500/75" />
```
### Appearance
Remove native form control styling for custom designs:
```html
<!-- Custom select with overlay icon -->
<div class="grid">
<select class="col-start-1 row-start-1 appearance-none bg-gray-50 ...">
<option>Yes</option>
</select>
<svg class="pointer-events-none col-start-1 row-start-1 ...">...</svg>
</div>
<!-- Restore default in forced-colors mode (accessibility) -->
<input type="checkbox" class="appearance-none forced-colors:appearance-auto" />
```
### Caret color
Set the text input cursor color:
```html
<textarea class="caret-pink-500">Focus to see caret</textarea>
<input class="caret-blue-500" />
```
### Resize
Control textarea resize behavior:
```html
<textarea class="resize-y">Vertical only</textarea>
<textarea class="resize-x">Horizontal only</textarea>
<textarea class="resize">Both directions</textarea>
<textarea class="resize-none">No resize handle</textarea>
```
## Key Points
- `accent-*` - theme colors for checkboxes, radio, range; use `accent-[#hex]` or `accent-(--var)` for custom
- `appearance-none` - remove native styling (custom selects, checkboxes)
- `appearance-auto` - restore default (e.g. for `forced-colors: active`)
- `caret-*` - theme colors for input cursor; matches text color patterns
- `resize` - both; `resize-x` - horizontal; `resize-y` - vertical; `resize-none` - no handle
<!--
Source references:
- https://tailwindcss.com/docs/accent-color
- https://tailwindcss.com/docs/appearance
- https://tailwindcss.com/docs/caret-color
- https://tailwindcss.com/docs/resize
-->

View File

@@ -0,0 +1,59 @@
---
name: effects-scroll-snap
description: CSS scroll snap for carousels and scroll containers
---
# Scroll Snap
Utilities for scroll snap behavior in overflow containers. Use for carousels, horizontal galleries, or paged scroll.
## Usage
### Container
```html
<!-- Horizontal snap (carousel) -->
<div class="snap-x snap-mandatory overflow-x-auto flex gap-4">
<div class="snap-center shrink-0 w-80">Slide 1</div>
<div class="snap-center shrink-0 w-80">Slide 2</div>
<div class="snap-center shrink-0 w-80">Slide 3</div>
</div>
<!-- Vertical snap -->
<div class="snap-y snap-mandatory overflow-y-auto h-screen">
<div class="snap-start h-screen">Section 1</div>
<div class="snap-start h-screen">Section 2</div>
</div>
<!-- Both axes -->
<div class="snap-both overflow-auto">...</div>
```
### Strictness
- `snap-mandatory` - always rest on a snap point
- `snap-proximity` - snap only when close to a point (default)
### Child alignment
```html
<div class="snap-x overflow-x-auto">
<div class="snap-center">Center snap</div>
<div class="snap-start">Start snap</div>
<div class="snap-end">End snap</div>
<div class="snap-align-none">No snap</div>
</div>
```
## Key Points
- `snap-x` - horizontal; `snap-y` - vertical; `snap-both` - both; `snap-none` - disable
- `snap-mandatory` / `snap-proximity` - strictness
- Child: `snap-center`, `snap-start`, `snap-end`, `snap-align-none`
- Requires overflow (e.g. `overflow-x-auto`) and scroll on container
<!--
Source references:
- https://tailwindcss.com/docs/scroll-snap-type
- https://tailwindcss.com/docs/scroll-snap-align
-->

View File

@@ -0,0 +1,80 @@
---
name: effects-transition-animation
description: CSS transitions, animation keyframes, and reduced motion support
---
# Transition & Animation
Utilities for CSS transitions and animations.
## Usage
### Transition property
```html
<button class="transition hover:bg-blue-600">Transitions common properties</button>
<button class="transition-all hover:scale-110">All properties</button>
<button class="transition-colors hover:bg-indigo-500">Colors only</button>
<button class="transition-opacity hover:opacity-75">Opacity only</button>
<button class="transition-transform hover:translate-y-1">Transform only</button>
<button class="transition-none">No transition</button>
```
### Transition duration and delay
```html
<button class="transition duration-150">150ms (default)</button>
<button class="transition duration-300">300ms</button>
<button class="transition duration-500">500ms</button>
<button class="transition delay-150">Delay 150ms</button>
<button class="transition duration-300 delay-100">Both</button>
```
### Transition timing
```html
<button class="transition ease-linear">Linear</button>
<button class="transition ease-in">Ease in</button>
<button class="transition ease-out">Ease out</button>
<button class="transition ease-in-out">Ease in-out (default)</button>
<button class="transition ease-[cubic-bezier(0.4,0,0.2,1)]">Arbitrary</button>
```
### Animation keyframes
```html
<div class="animate-spin">Spinning</div>
<div class="animate-ping">Ping effect</div>
<div class="animate-pulse">Pulse</div>
<div class="animate-bounce">Bounce</div>
```
Built-in: `animate-spin`, `animate-ping`, `animate-pulse`, `animate-bounce`. Use `@keyframes` in custom CSS for more.
### Reduced motion
```html
<button class="transition hover:-translate-y-1 motion-reduce:transition-none motion-reduce:hover:translate-y-0">
Respects prefers-reduced-motion
</button>
<div class="animate-spin motion-reduce:animate-none">Spinner hidden when reduced motion</div>
```
Use `motion-reduce:` to disable or simplify animations when user prefers reduced motion.
## Key Points
- Transition: `transition`, `transition-all`, `transition-colors`, `transition-opacity`, `transition-transform`
- Duration: `duration-{75,100,150,200,300,500,700,1000}`
- Delay: `delay-{75,100,150,200,300,500,700,1000}`
- Timing: `ease-{linear,in,in-out,out}`
- Animation: `animate-spin`, `animate-ping`, `animate-pulse`, `animate-bounce`
- Always consider `motion-reduce:` for accessibility
<!--
Source references:
- https://tailwindcss.com/docs/transition-property
- https://tailwindcss.com/docs/transition-duration
- https://tailwindcss.com/docs/transition-timing-function
- https://tailwindcss.com/docs/animation
-->

View File

@@ -0,0 +1,82 @@
---
name: effects-visibility-interactivity
description: Visibility, cursor, pointer-events, user-select, and z-index
---
# Visibility & Interactivity
Utilities for visibility, cursor, pointer-events, user-select, and stacking.
## Usage
### Visibility
```html
<div class="visible">Visible (default)</div>
<div class="invisible">Hidden but in layout</div>
<div class="collapse">Table collapse</div>
```
Use `invisible` when you need to keep layout space; use `hidden` (display:none) to remove from flow.
### Cursor
```html
<button class="cursor-pointer">Pointer</button>
<div class="cursor-not-allowed">Disabled</div>
<div class="cursor-wait">Loading</div>
<div class="cursor-move">Draggable</div>
<div class="cursor-grab">Grab</div>
<div class="cursor-grabbing">Grabbing</div>
<div class="cursor-text">Text select</div>
<div class="cursor-default">Default</div>
<div class="cursor-none">No cursor</div>
```
### Pointer events
```html
<div class="pointer-events-none">Ignore all pointer events</div>
<div class="pointer-events-auto">Default behavior</div>
```
Useful for overlays: make overlay `pointer-events-none` so clicks pass through, or `pointer-events-none` on disabled elements.
### User select
```html
<p class="select-none">Cannot select</p>
<p class="select-text">Select text (default)</p>
<p class="select-all">Select all on click</p>
<p class="select-auto">Browser default</p>
```
### Z-index
```html
<div class="z-0">0</div>
<div class="z-10">10</div>
<div class="z-20">20</div>
<div class="z-50">50</div>
<div class="z-auto">Auto</div>
<div class="z-[100]">Arbitrary</div>
```
Common: `z-0` (base), `z-10` (dropdowns), `z-20` (fixed nav), `z-50` (modal), `z-40` (overlay).
## Key Points
- Visibility: `visible`, `invisible`, `collapse`
- Cursor: `cursor-{pointer,not-allowed,wait,move,grab,text,default,none}`
- Pointer events: `pointer-events-none`, `pointer-events-auto`
- User select: `select-none`, `select-text`, `select-all`
- Z-index: `z-{0,10,20,30,40,50,auto}`, `z-[n]`
<!--
Source references:
- https://tailwindcss.com/docs/visibility
- https://tailwindcss.com/docs/cursor
- https://tailwindcss.com/docs/pointer-events
- https://tailwindcss.com/docs/user-select
- https://tailwindcss.com/docs/z-index
-->

View File

@@ -0,0 +1,175 @@
---
name: features-content-detection
description: How Tailwind detects classes in source files and how to customize content scanning
---
# Detecting Classes in Source Files
Tailwind scans your project for utility classes and generates CSS based on what you've actually used.
## How Classes Are Detected
Tailwind treats all source files as plain text and looks for tokens that could be class names:
```jsx
export function Button({ color, children }) {
const colors = {
black: "bg-black text-white",
blue: "bg-blue-500 text-white",
};
return (
<button className={`${colors[color]} rounded-full px-2 py-1.5`}>
{children}
</button>
);
}
```
Tailwind detects `bg-black`, `text-white`, `bg-blue-500`, `rounded-full`, `px-2`, and `py-1.5` from this file.
## Dynamic Class Names
Tailwind scans files as plain text, so it can't understand string concatenation or interpolation.
### ❌ Don't Construct Classes Dynamically
```html
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
```
The strings `text-red-600` and `text-green-600` don't exist in the file, so Tailwind won't generate them.
### ✅ Use Complete Class Names
```html
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}">
Content
</div>
```
### ❌ Don't Build Classes from Props
```jsx
function Button({ color, children }) {
return <button className={`bg-${color}-600 hover:bg-${color}-500`}>{children}</button>;
}
```
### ✅ Map Props to Static Classes
```jsx
function Button({ color, children }) {
const colorVariants = {
blue: "bg-blue-600 hover:bg-blue-500",
red: "bg-red-600 hover:bg-red-500",
};
return <button className={`${colorVariants[color]} ...`}>{children}</button>;
}
```
## Which Files Are Scanned
Tailwind scans every file in your project except:
- Files in `.gitignore`
- Files in `node_modules`
- Binary files (images, videos, zip files)
- CSS files
- Common package manager lock files
## Explicitly Registering Sources
Use `@source` to explicitly register source paths:
```css
@import "tailwindcss";
@source "../node_modules/@acmecorp/ui-lib";
```
This is useful for external libraries built with Tailwind that are in `.gitignore`.
## Setting Base Path
Set the base path for source detection:
```css
@import "tailwindcss" source("../src");
```
Useful in monorepos where build commands run from the root.
## Ignoring Specific Paths
Use `@source not` to ignore paths:
```css
@import "tailwindcss";
@source not "../src/components/legacy";
```
## Disabling Automatic Detection
Use `source(none)` to disable automatic detection:
```css
@import "tailwindcss" source(none);
@source "../admin";
@source "../shared";
```
Useful for projects with multiple Tailwind stylesheets.
## Safelisting Utilities
Force Tailwind to generate specific classes with `@source inline()`:
```css
@import "tailwindcss";
@source inline("underline");
```
### Safelisting with Variants
Generate classes with variants:
```css
@import "tailwindcss";
@source inline("{hover:,focus:,}underline");
```
### Safelisting with Ranges
Use brace expansion to generate multiple classes:
```css
@import "tailwindcss";
@source inline("{hover:,}bg-red-{50,{100..900..100},950}");
```
This generates `bg-red-50` through `bg-red-950` with hover variants.
## Explicitly Excluding Classes
Use `@source not inline()` to prevent specific classes from being generated:
```css
@import "tailwindcss";
@source not inline("{hover:,focus:,}bg-red-{50,{100..900..100},950}");
```
## Key Points
- Tailwind scans files as plain text
- Always use complete class names, never construct them dynamically
- Map props/state to static class names
- Use `@source` to explicitly register or ignore paths
- Use `@source inline()` to safelist utilities
- Brace expansion works in inline sources for ranges
<!--
Source references:
- https://tailwindcss.com/docs/detecting-classes-in-source-files
-->

View File

@@ -0,0 +1,203 @@
---
name: features-custom-styles
description: Adding custom styles, utilities, variants, and working with arbitrary values
---
# Adding Custom Styles
Tailwind is designed to be extensible. This guide covers customizing your theme, using arbitrary values, adding custom CSS, and extending the framework.
## Customizing Your Theme
Add custom design tokens using `@theme`:
```css
@import "tailwindcss";
@theme {
--font-display: "Satoshi", "sans-serif";
--breakpoint-3xl: 120rem;
--color-brand-500: oklch(0.65 0.2 250);
--ease-fluid: cubic-bezier(0.3, 0, 0, 1);
}
```
## Arbitrary Values
Use square bracket notation for one-off values that don't belong in your theme:
```html
<!-- Arbitrary values -->
<div class="top-[117px] bg-[#bada55] text-[22px]">
Content
</div>
<!-- With variants -->
<div class="top-[117px] lg:top-[344px]">
Content
</div>
```
### CSS Variables as Arbitrary Values
Reference CSS variables:
```html
<div class="fill-(--my-brand-color)">
Content
</div>
```
This is shorthand for `fill-[var(--my-brand-color)]`.
## Arbitrary Properties
Use square brackets for CSS properties Tailwind doesn't have utilities for:
```html
<div class="[mask-type:luminance]">
Content
</div>
<!-- With variants -->
<div class="[mask-type:luminance] hover:[mask-type:alpha]">
Content
</div>
```
### CSS Variables
Set CSS variables with arbitrary properties:
```html
<div class="[--scroll-offset:56px] lg:[--scroll-offset:44px]">
Content
</div>
```
## Arbitrary Variants
Create custom selectors on the fly:
```html
<ul>
<li class="lg:[&:nth-child(-n+3)]:hover:underline">Item</li>
</ul>
```
## Handling Whitespace
Use underscores for spaces in arbitrary values:
```html
<div class="grid-cols-[1fr_500px_2fr]">
Content
</div>
```
Tailwind converts underscores to spaces, except in contexts where underscores are valid (like URLs).
## Custom Utilities
Add custom utilities with `@utility`:
```css
@import "tailwindcss";
@utility tab-4 {
tab-size: 4;
}
```
Now you can use `tab-4` utility class, and it works with variants:
```html
<div class="tab-4 hover:tab-8">
Content
</div>
```
## Custom Variants
Add custom variants with `@custom-variant`:
```css
@import "tailwindcss";
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));
```
Now you can use `theme-midnight:` variant:
```html
<div class="theme-midnight:bg-black theme-midnight:text-white">
Content
</div>
```
## Using Variants in CSS
Apply Tailwind variants to custom CSS with `@variant`:
```css
.my-element {
background: white;
@variant dark {
background: black;
}
@variant hover {
background: gray;
}
}
```
## Base Styles
Add base styles to the `base` layer:
```css
@layer base {
h1 {
font-size: var(--text-2xl);
font-weight: 600;
}
h2 {
font-size: var(--text-xl);
font-weight: 600;
}
a {
color: var(--color-blue-600);
text-decoration-line: underline;
}
}
```
## Component Styles
Add component styles to the `components` layer:
```css
@layer components {
.btn-primary {
@apply bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded;
}
}
```
## Key Points
- Use `@theme` to customize design tokens
- Arbitrary values with `[]` for one-off values
- Arbitrary properties for CSS properties without utilities
- `@utility` for custom utilities
- `@custom-variant` for custom variants
- `@layer` for organizing base and component styles
<!--
Source references:
- https://tailwindcss.com/docs/adding-custom-styles
-->

View File

@@ -0,0 +1,137 @@
---
name: features-dark-mode
description: Implementing dark mode with Tailwind's dark variant and custom dark mode strategies
---
# Dark Mode
Tailwind includes a `dark` variant that lets you style your site differently when dark mode is enabled.
## Overview
Use the `dark:` variant to apply styles in dark mode:
```html
<div class="bg-white dark:bg-gray-800 text-gray-900 dark:text-white">
Content
</div>
```
## Default Behavior
By default, the `dark` variant uses the `prefers-color-scheme` CSS media feature:
```css
@media (prefers-color-scheme: dark) {
.dark\:bg-gray-800 {
background-color: rgb(31 41 55);
}
}
```
## Manual Toggle with Class
Override the `dark` variant to use a class selector:
```css
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
```
Now dark mode utilities apply when the `dark` class is present:
```html
<html class="dark">
<body>
<div class="bg-white dark:bg-black">Content</div>
</body>
</html>
```
## Manual Toggle with Data Attribute
Use a data attribute instead:
```css
@import "tailwindcss";
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
```
```html
<html data-theme="dark">
<body>
<div class="bg-white dark:bg-black">Content</div>
</body>
</html>
```
## Three-Way Theme Toggle
Support light mode, dark mode, and system preference:
```js
// On page load
document.documentElement.classList.toggle(
"dark",
localStorage.theme === "dark" ||
(!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
);
// Set light mode
localStorage.theme = "light";
document.documentElement.classList.remove("dark");
// Set dark mode
localStorage.theme = "dark";
document.documentElement.classList.add("dark");
// Respect system preference
localStorage.removeItem("theme");
document.documentElement.classList.toggle(
"dark",
window.matchMedia("(prefers-color-scheme: dark)").matches
);
```
## Common Patterns
### Cards
```html
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg dark:shadow-none">
<h2 class="text-gray-900 dark:text-white">Title</h2>
<p class="text-gray-500 dark:text-gray-400">Description</p>
</div>
```
### Borders
```html
<div class="border border-gray-200 dark:border-gray-700">
Content
</div>
```
### Buttons
```html
<button class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white">
Button
</button>
```
## Key Points
- Use `dark:` variant for dark mode styles
- Default uses `prefers-color-scheme` media query
- Override with `@custom-variant` for manual toggles
- Can use class or data attribute selectors
- Combine with responsive variants: `dark:md:bg-gray-800`
<!--
Source references:
- https://tailwindcss.com/docs/dark-mode
-->

View File

@@ -0,0 +1,241 @@
---
name: features-functions-directives
description: Tailwind's CSS directives (@import, @theme, @utility, @variant) and functions (--alpha, --spacing)
---
# Functions and Directives
Tailwind provides custom CSS directives and functions for working with your design system.
## Directives
Directives are custom Tailwind-specific at-rules that offer special functionality.
### @import
Import CSS files, including Tailwind:
```css
@import "tailwindcss";
```
### @theme
Define your project's design tokens:
```css
@theme {
--font-display: "Satoshi", "sans-serif";
--breakpoint-3xl: 120rem;
--color-brand-500: oklch(0.65 0.2 250);
--ease-fluid: cubic-bezier(0.3, 0, 0, 1);
}
```
### @source
Explicitly specify source files for class detection:
```css
@import "tailwindcss";
@source "../node_modules/@my-company/ui-lib";
```
### @utility
Add custom utilities:
```css
@utility tab-4 {
tab-size: 4;
}
```
Custom utilities work with variants:
```html
<div class="tab-4 hover:tab-8">Content</div>
```
### @variant
Apply Tailwind variants to styles in your CSS:
```css
.my-element {
background: white;
@variant dark {
background: black;
}
}
```
### @custom-variant
Add custom variants:
```css
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));
```
```html
<div class="theme-midnight:bg-black">Content</div>
```
### @apply
Inline existing utility classes into custom CSS:
```css
.select2-dropdown {
@apply rounded-b-lg shadow-md;
}
.select2-search {
@apply rounded border border-gray-300;
}
```
### @reference
Import stylesheet for reference without including styles (useful for Vue/Svelte components):
```html
<style>
@reference "../../app.css";
h1 {
@apply text-2xl font-bold text-red-500;
}
</style>
```
Or reference Tailwind directly:
```html
<style>
@reference "tailwindcss";
h1 {
@apply text-2xl font-bold;
}
</style>
```
### Subpath Imports
Directives support subpath imports (like TypeScript path aliases):
```json
{
"imports": {
"#app.css": "./src/css/app.css"
}
}
```
```html
<style>
@reference "#app.css";
h1 {
@apply text-2xl font-bold;
}
</style>
```
## Functions
Tailwind provides build-time functions for working with colors and spacing.
### --alpha()
Adjust the opacity of a color:
```css
.my-element {
color: --alpha(var(--color-lime-300) / 50%);
}
```
Compiles to:
```css
.my-element {
color: color-mix(in oklab, var(--color-lime-300) 50%, transparent);
}
```
### --spacing()
Generate spacing values based on your theme:
```css
.my-element {
margin: --spacing(4);
}
```
Compiles to:
```css
.my-element {
margin: calc(var(--spacing) * 4);
}
```
Useful in arbitrary values with `calc()`:
```html
<div class="py-[calc(--spacing(4)-1px)]">
Content
</div>
```
## Compatibility Directives
For compatibility with Tailwind CSS v3.x:
### @config
Load a legacy JavaScript-based configuration:
```css
@config "../../tailwind.config.js";
```
### @plugin
Load a legacy JavaScript-based plugin:
```css
@plugin "@tailwindcss/typography";
```
### theme()
Access theme values using dot notation (deprecated):
```css
.my-element {
margin: theme(spacing.12);
}
```
**Note:** Prefer using CSS theme variables instead.
## Key Points
- Directives are Tailwind-specific at-rules
- `@theme` defines design tokens
- `@utility` creates custom utilities
- `@custom-variant` creates custom variants
- `@apply` inlines utilities into CSS
- `--alpha()` and `--spacing()` are build-time functions
- Compatibility directives support v3.x configs
<!--
Source references:
- https://tailwindcss.com/docs/functions-and-directives
-->

View File

@@ -0,0 +1,91 @@
---
name: features-upgrade
description: Migrating from Tailwind CSS v3 to v4
---
# Upgrade Guide (v3 → v4)
Key changes when upgrading from Tailwind CSS v3 to v4. Use the automated upgrade tool when possible.
## Upgrade Tool
```bash
npx @tailwindcss/upgrade
```
Requires Node.js 20+. Run in a new branch, review diff, test. Handles most migration automatically.
## Installation Changes
- **PostCSS**: Use `@tailwindcss/postcss`; remove `postcss-import` and `autoprefixer` (handled by v4)
- **Vite**: Prefer `@tailwindcss/vite` over PostCSS
- **CLI**: Use `npx @tailwindcss/cli` instead of `npx tailwindcss`
## Import Change
```css
/* v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* v4 */
@import "tailwindcss";
```
## Renamed Utilities
| v3 | v4 |
|----|-----|
| `shadow-sm` | `shadow-xs` |
| `shadow` | `shadow-sm` |
| `rounded-sm` | `rounded-xs` |
| `rounded` | `rounded-sm` |
| `blur-sm` | `blur-xs` |
| `blur` | `blur-sm` |
| `outline-none` | `outline-hidden` |
| `ring` | `ring-3` |
## Removed / Replaced
- `bg-opacity-*`, `text-opacity-*`, etc. → use `bg-black/50`, `text-black/50`
- `flex-shrink-*``shrink-*`
- `flex-grow-*``grow-*`
- `overflow-ellipsis``text-ellipsis`
## Important Modifier
```html
<!-- v3: ! at start -->
<div class="!bg-red-500">
<!-- v4: ! at end -->
<div class="bg-red-500!">
```
## Ring & Border Defaults
- `ring` width: 3px → 1px; use `ring-3` for v3 behavior
- `ring` / `border` default color: `currentColor` (was gray-200 / blue-500)
- Always specify color: `ring-3 ring-blue-500`, `border border-gray-200`
## Other Breaking Changes
- **Space/divide selectors**: Changed from `:not([hidden]) ~ :not([hidden])` to `:not(:last-child)`; may affect layout
- **Variant stacking**: Left-to-right in v4 (was right-to-left)
- **Transform reset**: `transform-none` no longer resets rotate/scale/translate; use `scale-none`, `rotate-none`, etc.
- **Hover on mobile**: `hover` only applies when device supports hover; override with `@custom-variant hover (&:hover)` if needed
- **Arbitrary values**: Use `bg-(--var)` not `bg-[--var]` for CSS variables
- **theme()**: Use `theme(--breakpoint-xl)` not `theme(screens.xl)`
- **@layer utilities/components**: Use `@utility` directive instead
- **corePlugins, safelist, separator**: Not supported; use `@source inline()` for safelisting
- **Sass/Less/Stylus**: v4 not designed for use with CSS preprocessors
## Browser Support
v4 targets Safari 16.4+, Chrome 111+, Firefox 128+. For older browsers, stay on v3.4.
<!--
Source references:
- https://tailwindcss.com/docs/upgrade-guide
-->

View File

@@ -0,0 +1,39 @@
---
name: layout-aspect-ratio
description: Controlling element aspect ratio for responsive media
---
# Aspect Ratio
Utilities for controlling the aspect ratio of an element (e.g. video, images).
## Usage
```html
<!-- 16:9 video container -->
<div class="aspect-video">
<iframe src="..."></iframe>
</div>
<!-- 4:3 -->
<div class="aspect-[4/3]">Content</div>
<!-- Square -->
<div class="aspect-square">1:1</div>
<!-- Auto (intrinsic) -->
<div class="aspect-auto">Natural ratio</div>
```
## Key Points
- `aspect-video` - 16:9
- `aspect-square` - 1:1
- `aspect-auto` - browser default (intrinsic)
- `aspect-[4/3]` - arbitrary ratio
- Useful for responsive video embeds and image containers
<!--
Source references:
- https://tailwindcss.com/docs/aspect-ratio
-->

View File

@@ -0,0 +1,80 @@
---
name: layout-columns
description: Multi-column layout with columns utility for masonry-like or newspaper layouts
---
# Columns
Utilities for CSS multi-column layout. Content flows into multiple columns within a single element.
## Usage
### By number of columns
```html
<div class="columns-3 gap-8">
<img class="aspect-3/2" src="1.jpg" />
<img class="aspect-square" src="2.jpg" />
<!-- Content flows into 3 columns -->
</div>
```
### By column width
Use container scale for ideal column width; column count adjusts automatically:
```html
<div class="columns-3xs gap-4">...</div>
<div class="columns-sm gap-6">...</div>
<div class="columns-md">...</div>
```
### Column gap
Use `gap-*` utilities for space between columns:
```html
<div class="columns-3 gap-8">...</div>
```
### Responsive
```html
<div class="columns-2 gap-4 sm:columns-3 sm:gap-8">...</div>
```
### Custom value
```html
<div class="columns-[30vw]">...</div>
<div class="columns-(--my-columns)">...</div>
```
### Column / page breaks
Use with multi-column or print layouts:
```html
<div class="columns-2">
<p>Content...</p>
<p class="break-after-column">Force break after this</p>
<p>Next column...</p>
</div>
```
- `break-after-column` / `break-before-column` - column break
- `break-after-page` / `break-before-page` - page break (print)
- `break-after-avoid` / `break-inside-avoid` - avoid breaking
## Key Points
- `columns-<n>` - fixed number of columns (e.g. `columns-3`)
- `columns-3xs` through `columns-7xl` - column width from container scale
- `columns-auto` - auto columns
- `gap-*` controls column gap (same as flex/grid gap)
- Use for magazine-style layouts, image galleries, long text
<!--
Source references:
- https://tailwindcss.com/docs/columns
-->

View File

@@ -0,0 +1,110 @@
---
name: layout-display
description: Controlling element display type including flex, grid, block, inline, hidden, and sr-only
---
# Display
Utilities for controlling the display box type of an element.
## Usage
### Basic display types
```html
<span class="inline">display: inline</span>
<span class="inline-block">display: inline-block</span>
<div class="block">display: block</div>
```
### Flex and Grid containers
```html
<!-- Flex container -->
<div class="flex items-center gap-4">Flex layout</div>
<span class="inline-flex items-baseline">Inline flex with text</span>
<!-- Grid container -->
<div class="grid grid-cols-3 gap-4">Grid layout</div>
<span class="inline-grid grid-cols-3 gap-4">Inline grid</span>
```
### Flow root
Use `flow-root` to create a block-level element with its own block formatting context (fixes margin collapse):
```html
<div class="flow-root">
<div class="my-4">Content with isolated BFC</div>
</div>
```
### Contents
Use `contents` for a "phantom" container whose children act like direct children of the parent:
```html
<div class="flex">
<div class="flex-1">01</div>
<div class="contents">
<div class="flex-1">02</div>
<div class="flex-1">03</div>
</div>
<div class="flex-1">04</div>
</div>
```
### Table display
```html
<div class="table w-full">
<div class="table-header-group bg-gray-100">
<div class="table-row">
<div class="table-cell p-2">Header 1</div>
<div class="table-cell p-2">Header 2</div>
</div>
</div>
<div class="table-row-group">
<div class="table-row">
<div class="table-cell p-2">Cell 1</div>
<div class="table-cell p-2">Cell 2</div>
</div>
</div>
</div>
```
### Hidden
```html
<div class="hidden">Removed from document flow</div>
<div class="md:block hidden">Visible only on md+</div>
```
For visual-only hiding while keeping in DOM, use `invisible` or `opacity-0` instead.
### Screen reader only
```html
<a href="#">
<svg><!-- icon --></svg>
<span class="sr-only">Settings</span>
</a>
```
Use `not-sr-only` to undo: `<span class="sr-only sm:not-sr-only">Settings</span>`
## Key Points
- `flex` / `inline-flex` - Flexbox layout
- `grid` / `inline-grid` - CSS Grid layout
- `block` / `inline` / `inline-block` - Basic flow
- `hidden` - `display: none` (removes from flow)
- `sr-only` - Visually hidden but accessible to screen readers
- `contents` - Children participate in parent's layout
- `flow-root` - Establishes new BFC
- Table utilities: `table`, `table-row`, `table-cell`, `table-header-group`, etc.
<!--
Source references:
- https://tailwindcss.com/docs/display
-->

View File

@@ -0,0 +1,110 @@
---
name: layout-flexbox
description: Flexbox layout utilities including flex-direction, justify, items, gap, grow, shrink, and wrap
---
# Flexbox
Utilities for building flex layouts: direction, alignment, gap, and item sizing.
## Usage
### Flex direction and wrap
```html
<div class="flex flex-row">Horizontal (default)</div>
<div class="flex flex-row-reverse">Reversed</div>
<div class="flex flex-col">Vertical</div>
<div class="flex flex-col-reverse">Vertical reversed</div>
<div class="flex flex-wrap">Wrap when needed</div>
<div class="flex flex-nowrap">No wrap (default)</div>
<div class="flex flex-wrap-reverse">Wrap reversed</div>
```
### Justify content
```html
<div class="flex justify-start">Start</div>
<div class="flex justify-end">End</div>
<div class="flex justify-center">Center</div>
<div class="flex justify-between">Space between</div>
<div class="flex justify-around">Space around</div>
<div class="flex justify-evenly">Space evenly</div>
```
### Align items
```html
<div class="flex items-start">Align start</div>
<div class="flex items-end">Align end</div>
<div class="flex items-center">Center (common)</div>
<div class="flex items-baseline">Baseline</div>
<div class="flex items-stretch">Stretch (default)</div>
```
### Align self (on flex children)
```html
<div class="flex">
<div class="self-auto">Auto</div>
<div class="self-start">Start</div>
<div class="self-center">Center</div>
<div class="self-stretch">Stretch</div>
</div>
```
### Gap
```html
<div class="flex gap-4">gap-4 (1rem)</div>
<div class="flex gap-x-2 gap-y-4">Different x/y gap</div>
<div class="flex gap-0">No gap</div>
```
### Flex grow, shrink, basis
```html
<div class="flex">
<div class="w-14 flex-none">Fixed</div>
<div class="flex-1">Grows and shrinks</div>
<div class="flex-initial">Shrink only</div>
<div class="flex-auto">Grow/shrink with initial size</div>
</div>
```
- `flex-1` = `flex: 1 1 0%` - equal distribution
- `flex-initial` = `flex: 0 1 auto` - shrink, don't grow
- `flex-auto` = `flex: 1 1 auto` - grow/shrink from content size
- `flex-none` = `flex: none` - no grow or shrink
### Order
```html
<div class="flex">
<div class="order-2">Second</div>
<div class="order-1">First</div>
<div class="order-last">Last</div>
</div>
```
## Key Points
- Use `flex` or `inline-flex` as container (see layout-display)
- Direction: `flex-row`, `flex-col`, `flex-row-reverse`, `flex-col-reverse`
- Justify: `justify-start`, `justify-center`, `justify-between`, `justify-evenly`
- Align: `items-center`, `items-start`, `items-stretch`
- Gap: `gap-{n}`, `gap-x-{n}`, `gap-y-{n}` (spacing scale)
- Item sizing: `flex-1`, `flex-none`, `flex-auto`, `flex-initial`
- Self alignment: `self-center`, `self-start`, etc.
- Order: `order-{n}`, `order-first`, `order-last`
<!--
Source references:
- https://tailwindcss.com/docs/flex-direction
- https://tailwindcss.com/docs/justify-content
- https://tailwindcss.com/docs/align-items
- https://tailwindcss.com/docs/gap
- https://tailwindcss.com/docs/flex
- https://tailwindcss.com/docs/order
-->

View File

@@ -0,0 +1,87 @@
---
name: layout-grid
description: CSS Grid utilities including grid-cols, grid-rows, gap, place-items, and grid placement
---
# Grid
Utilities for CSS Grid layouts: columns, rows, gap, and item placement.
## Usage
### Grid columns and rows
```html
<div class="grid grid-cols-3 gap-4">3 equal columns</div>
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">Responsive</div>
<div class="grid grid-cols-none">No explicit tracks</div>
<div class="grid grid-rows-3 gap-2">3 rows</div>
<div class="grid grid-rows-[auto_1fr_auto]">Custom rows</div>
```
### Custom grid template
```html
<div class="grid grid-cols-[200px_minmax(900px,1fr)_100px]">Custom template</div>
<div class="grid grid-cols-(--my-cols)">CSS variable</div>
```
### Subgrid
```html
<div class="grid grid-cols-4 gap-4">
<div>01</div><div>02</div><div>03</div><div>04</div>
<div>05</div>
<div class="col-span-3 grid grid-cols-subgrid gap-4">
<div class="col-start-2">06</div>
</div>
</div>
```
### Gap
```html
<div class="grid gap-4">Uniform gap</div>
<div class="grid gap-x-2 gap-y-4">Different x/y</div>
```
### Place items (align + justify)
```html
<div class="grid place-items-center">Center both</div>
<div class="grid place-items-start">Start both</div>
<div class="grid items-center justify-center">Equivalent</div>
```
### Grid placement (col/row span and start)
```html
<div class="grid grid-cols-4 gap-4">
<div class="col-span-2">Spans 2 columns</div>
<div class="col-start-3">Starts at col 3</div>
<div class="row-span-2">Spans 2 rows</div>
<div class="col-span-full">Full width</div>
</div>
```
## Key Points
- `grid` / `inline-grid` - Grid container (see layout-display)
- Columns: `grid-cols-{n}`, `grid-cols-none`, `grid-cols-subgrid`, `grid-cols-[...]`
- Rows: `grid-rows-{n}`, `grid-rows-[...]`
- Gap: `gap-{n}`, `gap-x-{n}`, `gap-y-{n}`
- Placement: `col-span-{n}`, `col-start-{n}`, `row-span-{n}`, `row-start-{n}`
- `col-span-full` / `row-span-full` - span all
- `place-items-*` - shorthand for align + justify
- Use `grid-cols-[...]` for custom templates like `minmax()` or `repeat()`
<!--
Source references:
- https://tailwindcss.com/docs/grid-template-columns
- https://tailwindcss.com/docs/grid-template-rows
- https://tailwindcss.com/docs/gap
- https://tailwindcss.com/docs/place-items
- https://tailwindcss.com/docs/grid-column
- https://tailwindcss.com/docs/grid-row
-->

View File

@@ -0,0 +1,97 @@
---
name: layout-height
description: Setting element height with spacing scale, fractions, viewport units, and content-based sizing
---
# Height
Utilities for setting the height of an element.
## Usage
### Spacing scale
Use `h-<number>` utilities based on the spacing scale:
```html
<div class="h-24">h-24</div>
<div class="h-48">h-48</div>
<div class="h-64">h-64</div>
```
### Percentage heights
Use `h-full` or `h-<fraction>` for percentage-based heights:
```html
<div class="h-full">100%</div>
<div class="h-1/2">50%</div>
<div class="h-3/4">75%</div>
<div class="h-9/10">90%</div>
```
### Viewport units
Use `h-screen` for full viewport height, or dynamic viewport units:
```html
<div class="h-screen">Full viewport height (100vh)</div>
<div class="h-dvh">Dynamic viewport height</div>
<div class="h-svh">Small viewport height</div>
<div class="h-lvh">Large viewport height</div>
```
### Content-based heights
Use `h-auto`, `h-fit`, `h-min`, `h-max` for content-based sizing:
```html
<div class="h-auto">Auto height</div>
<div class="h-fit">Fit content</div>
<div class="h-min">Min content</div>
<div class="h-max">Max content</div>
```
### Line height
Use `h-lh` to match line height:
```html
<div class="h-lh">Matches line height</div>
```
### Size utility
Use `size-<number>` to set both width and height:
```html
<div class="size-16">16x16</div>
<div class="size-24">24x24</div>
<div class="size-full">100% x 100%</div>
```
### Custom values
Use arbitrary values for custom heights:
```html
<div class="h-[117px]">Custom pixel height</div>
<div class="h-[50vh]">Custom viewport height</div>
<div class="h-[calc(100vh-2rem)]">Custom calculation</div>
```
## Key Points
- Spacing scale: `h-0` through `h-96` (and beyond)
- Fractions: `h-1/2`, `h-1/3`, `h-2/3`, `h-1/4`, `h-3/4`, `h-9/10`
- Viewport units: `h-screen` (100vh), `h-dvh`, `h-svh`, `h-lvh`
- `h-dvh` adapts to browser UI (address bar, etc.)
- `h-svh` uses smallest viewport height
- `h-lvh` uses largest viewport height
- `size-*` utilities set both width and height simultaneously
- Use `h-auto` to reset height at specific breakpoints
<!--
Source references:
- https://tailwindcss.com/docs/height
-->

View File

@@ -0,0 +1,91 @@
---
name: layout-inset
description: Controlling placement of positioned elements with top, right, bottom, left, and inset utilities
---
# Top / Right / Bottom / Left
Utilities for controlling the placement of positioned elements.
## Usage
### Basic positioning
Use `top-<number>`, `right-<number>`, `bottom-<number>`, `left-<number>` to position elements:
```html
<!-- Pin to top left corner -->
<div class="relative size-32">
<div class="absolute top-0 left-0 size-16">01</div>
</div>
<!-- Span top edge -->
<div class="relative size-32">
<div class="absolute inset-x-0 top-0 h-16">02</div>
</div>
<!-- Fill entire parent -->
<div class="relative size-32">
<div class="absolute inset-0">05</div>
</div>
```
### Inset utilities
Use `inset-<number>` for all sides, `inset-x-<number>` for horizontal, `inset-y-<number>` for vertical:
```html
<div class="absolute inset-0">Fill parent</div>
<div class="absolute inset-x-0 top-0">Span top</div>
<div class="absolute inset-y-0 left-0">Span left</div>
```
### Negative values
Prefix with a dash for negative values:
```html
<div class="relative size-32">
<div class="absolute -top-4 -left-4 size-14"></div>
</div>
```
### Logical properties
Use `start-<number>` and `end-<number>` for RTL-aware positioning:
```html
<div dir="ltr">
<div class="absolute start-0 top-0">Left in LTR</div>
</div>
<div dir="rtl">
<div class="absolute start-0 top-0">Right in RTL</div>
</div>
```
### Percentage and custom values
Use fractions for percentages or arbitrary values:
```html
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
Centered
</div>
<div class="absolute top-[117px] left-[20%]">
Custom position
</div>
```
## Key Points
- `inset-0` sets all sides to 0 (equivalent to `top-0 right-0 bottom-0 left-0`)
- `inset-x-0` sets left and right to 0
- `inset-y-0` sets top and bottom to 0
- Use `start`/`end` for logical properties that adapt to text direction
- Negative values use dash prefix: `-top-4`, `-left-8`
- Combine with `position` utilities (`absolute`, `fixed`, `relative`, `sticky`)
<!--
Source references:
- https://tailwindcss.com/docs/top-right-bottom-left
-->

View File

@@ -0,0 +1,126 @@
---
name: layout-margin
description: Controlling element margins with spacing scale, negative values, logical properties, and space utilities
---
# Margin
Utilities for controlling an element's margin.
## Usage
### All sides
Use `m-<number>` to set margin on all sides:
```html
<div class="m-4">Margin on all sides</div>
<div class="m-8">Larger margin</div>
```
### Individual sides
Use `mt-<number>`, `mr-<number>`, `mb-<number>`, `ml-<number>`:
```html
<div class="mt-6">Top margin</div>
<div class="mr-4">Right margin</div>
<div class="mb-8">Bottom margin</div>
<div class="ml-2">Left margin</div>
```
### Horizontal and vertical
Use `mx-<number>` for horizontal, `my-<number>` for vertical:
```html
<div class="mx-8">Horizontal margin</div>
<div class="my-8">Vertical margin</div>
```
### Negative margins
Prefix with dash for negative values:
```html
<div class="-mt-8">Negative top margin</div>
<div class="-mx-4">Negative horizontal margin</div>
```
### Auto margins
Use `m-auto` or directional auto margins for centering:
```html
<div class="mx-auto">Centered horizontally</div>
<div class="ml-auto">Pushed to right</div>
```
### Logical properties
Use `ms-<number>` (margin-inline-start) and `me-<number>` (margin-inline-end) for RTL support:
```html
<div dir="ltr">
<div class="ms-8">Left margin in LTR</div>
<div class="me-8">Right margin in LTR</div>
</div>
<div dir="rtl">
<div class="ms-8">Right margin in RTL</div>
<div class="me-8">Left margin in RTL</div>
</div>
```
### Space between children
Use `space-x-<number>` or `space-y-<number>` to add margin between children:
```html
<div class="flex space-x-4">
<div>01</div>
<div>02</div>
<div>03</div>
</div>
<div class="flex flex-col space-y-8">
<div>01</div>
<div>02</div>
<div>03</div>
</div>
```
### Reversing space
Use `space-x-reverse` or `space-y-reverse` with reversed flex directions:
```html
<div class="flex flex-row-reverse space-x-4 space-x-reverse">
<div>01</div>
<div>02</div>
<div>03</div>
</div>
```
### Custom values
Use arbitrary values for custom margins:
```html
<div class="m-[5px]">Custom margin</div>
<div class="mx-[calc(50%-1rem)]">Custom calculation</div>
```
## Key Points
- Spacing scale: `m-0` through `m-96` (and beyond)
- Negative: prefix with dash (`-m-4`, `-mt-8`, `-mx-4`)
- Auto: `m-auto`, `mx-auto`, `my-auto`, `mt-auto`, etc.
- Logical: `ms-*` (start), `me-*` (end) adapt to text direction
- Space utilities: `space-x-*`, `space-y-*` add margin to all children except last
- Space reverse: `space-x-reverse`, `space-y-reverse` for reversed flex layouts
- Limitations: Space utilities don't work well with grids or complex layouts - use `gap` utilities instead
<!--
Source references:
- https://tailwindcss.com/docs/margin
-->

View File

@@ -0,0 +1,63 @@
---
name: layout-min-max-sizing
description: Setting minimum and maximum width and height with min-w, max-w, min-h, max-h
---
# Min & Max Sizing
Utilities for constraining element dimensions with minimum and maximum width/height.
## Usage
### Min width
```html
<!-- Spacing scale -->
<div class="min-w-24">min-w-24</div>
<div class="min-w-64">min-w-64</div>
<!-- Percentage -->
<div class="min-w-full">min-w-full</div>
<div class="min-w-3/4">min-w-3/4</div>
<!-- Container scale -->
<div class="min-w-sm">min-w-sm</div>
<div class="min-w-xl">min-w-xl</div>
<!-- Content-based -->
<div class="min-w-min">min-content</div>
<div class="min-w-max">max-content</div>
<div class="min-w-fit">fit-content</div>
<div class="min-w-auto">auto</div>
<!-- Custom -->
<div class="min-w-[220px]">Custom</div>
```
### Max width
Use `max-w-<value>` with similar scales: spacing numbers, fractions (`max-w-1/2`), container sizes (`max-w-md`), `max-w-full`, `max-w-screen`, `max-w-min`, `max-w-max`, `max-w-fit`, `max-w-none`.
### Min / Max height
```html
<div class="min-h-screen">At least full viewport height</div>
<div class="min-h-0">Allow shrinking in flex</div>
<div class="max-h-96 overflow-y-auto">Scrollable with max height</div>
```
## Key Points
- min-w: spacing scale, fractions, container scale (3xs7xl), `full`, `screen`, `min`, `max`, `fit`, `auto`
- max-w: same options plus `none`
- min-h / max-h: similar scales; `min-h-0` important for flex children to shrink
- Viewport units: `min-w-screen`, `min-w-dvw`, `min-w-svw`, `min-w-lvw`
- Container scale: `min-w-3xs` through `min-w-7xl` map to `--container-*` variables
<!--
Source references:
- https://tailwindcss.com/docs/min-width
- https://tailwindcss.com/docs/max-width
- https://tailwindcss.com/docs/min-height
- https://tailwindcss.com/docs/max-height
-->

View File

@@ -0,0 +1,64 @@
---
name: layout-object-fit-position
description: Controlling how replaced elements (images, video) are resized and positioned within their container
---
# Object Fit & Object Position
Utilities for controlling how replaced elements like `<img>` and `<video>` are resized and positioned within their container.
## Usage
### Object fit
```html
<!-- Cover container, may crop -->
<img class="h-48 w-96 object-cover" src="photo.jpg" />
<!-- Contain within, may letterbox -->
<img class="h-48 w-96 object-contain" src="photo.jpg" />
<!-- Stretch to fill -->
<img class="h-48 w-96 object-fill" src="photo.jpg" />
<!-- Scale down only if needed, else original size -->
<img class="h-48 w-96 object-scale-down" src="photo.jpg" />
<!-- Original size, ignore container -->
<img class="h-48 w-96 object-none" src="photo.jpg" />
```
### Object position
```html
<!-- Named positions -->
<img class="size-24 object-cover object-top-left" src="photo.jpg" />
<img class="size-24 object-cover object-center" src="photo.jpg" />
<img class="size-24 object-cover object-bottom-right" src="photo.jpg" />
<!-- Custom value -->
<img class="object-cover object-[25%_75%]" src="photo.jpg" />
<img class="object-(--my-object)" src="photo.jpg" />
```
### Responsive
```html
<img class="object-contain md:object-cover object-center md:object-top" src="photo.jpg" />
```
## Key Points
- `object-contain` - maintain aspect ratio, fit inside container
- `object-cover` - maintain aspect ratio, cover container (may crop)
- `object-fill` - stretch to fill container
- `object-scale-down` - like `contain` but never upscale
- `object-none` - original size, ignore container
- Position: `object-top-left`, `object-top`, `object-top-right`, `object-left`, `object-center`, `object-right`, `object-bottom-left`, `object-bottom`, `object-bottom-right`
- Custom: `object-[25%_75%]`, `object-(--custom-property)`
<!--
Source references:
- https://tailwindcss.com/docs/object-fit
- https://tailwindcss.com/docs/object-position
-->

View File

@@ -0,0 +1,57 @@
---
name: layout-overflow
description: Controlling how elements handle content that overflows their container
---
# Overflow
Utilities for controlling how an element handles content that is too large for the container.
## Usage
### Basic overflow
Use `overflow-auto`, `overflow-hidden`, `overflow-visible`, `overflow-scroll`:
```html
<div class="overflow-auto">Scrolls if needed</div>
<div class="overflow-hidden">Clips overflow</div>
<div class="overflow-visible">Shows overflow</div>
<div class="overflow-scroll">Always shows scrollbars</div>
```
### Axis-specific overflow
Use `overflow-x-*` or `overflow-y-*` for horizontal/vertical control:
```html
<div class="overflow-x-auto overflow-y-hidden">
Horizontal scroll, vertical clip
</div>
<div class="overflow-x-scroll overflow-y-auto">
Horizontal always scrolls, vertical scrolls if needed
</div>
```
### Overflow clip
Use `overflow-clip` for clip behavior (similar to hidden but different scroll behavior):
```html
<div class="overflow-clip">Clips without creating scroll container</div>
```
## Key Points
- `overflow-auto` - shows scrollbars only when needed
- `overflow-hidden` - clips content that overflows
- `overflow-visible` - allows content to overflow (default for most elements)
- `overflow-scroll` - always shows scrollbars
- `overflow-clip` - clips without creating scroll container
- Use `overflow-x-*` and `overflow-y-*` for axis-specific control
- Common pattern: `overflow-hidden` for images, `overflow-auto` for scrollable content
<!--
Source references:
- https://tailwindcss.com/docs/overflow
-->

View File

@@ -0,0 +1,77 @@
---
name: layout-padding
description: Controlling element padding with spacing scale, logical properties, and directional utilities
---
# Padding
Utilities for controlling an element's padding.
## Usage
### All sides
Use `p-<number>` to set padding on all sides:
```html
<div class="p-4">Padding on all sides</div>
<div class="p-8">Larger padding</div>
```
### Individual sides
Use `pt-<number>`, `pr-<number>`, `pb-<number>`, `pl-<number>`:
```html
<div class="pt-6">Top padding</div>
<div class="pr-4">Right padding</div>
<div class="pb-8">Bottom padding</div>
<div class="pl-2">Left padding</div>
```
### Horizontal and vertical
Use `px-<number>` for horizontal, `py-<number>` for vertical:
```html
<div class="px-8">Horizontal padding</div>
<div class="py-8">Vertical padding</div>
```
### Logical properties
Use `ps-<number>` (padding-inline-start) and `pe-<number>` (padding-inline-end) for RTL support:
```html
<div dir="ltr">
<div class="ps-8">Left padding in LTR</div>
<div class="pe-8">Right padding in LTR</div>
</div>
<div dir="rtl">
<div class="ps-8">Right padding in RTL</div>
<div class="pe-8">Left padding in RTL</div>
</div>
```
### Custom values
Use arbitrary values for custom padding:
```html
<div class="p-[5px]">Custom padding</div>
<div class="px-[calc(50%-1rem)]">Custom calculation</div>
```
## Key Points
- Spacing scale: `p-0` through `p-96` (and beyond)
- Individual: `pt-*`, `pr-*`, `pb-*`, `pl-*` for specific sides
- Axes: `px-*` (horizontal), `py-*` (vertical)
- Logical: `ps-*` (start), `pe-*` (end) adapt to text direction
- No negative padding - padding cannot be negative in CSS
- Common patterns: `p-4`, `px-6`, `py-8`, `pt-2`, `pb-4`
<!--
Source references:
- https://tailwindcss.com/docs/padding
-->

View File

@@ -0,0 +1,85 @@
---
name: layout-position
description: Controlling element positioning with static, relative, absolute, fixed, and sticky utilities
---
# Position
Utilities for controlling how an element is positioned in the document.
## Usage
### Static positioning
Use `static` to position an element according to the normal flow. Offsets are ignored and it won't act as a position reference for absolutely positioned children:
```html
<div class="static">
<p>Static parent</p>
<div class="absolute bottom-0 left-0">
<p>Absolute child</p>
</div>
</div>
```
### Relative positioning
Use `relative` to position an element in normal flow. Offsets are calculated relative to the element's normal position, and it acts as a position reference for absolutely positioned children:
```html
<div class="relative">
<p>Relative parent</p>
<div class="absolute bottom-0 left-0">
<p>Absolute child</p>
</div>
</div>
```
### Absolute positioning
Use `absolute` to position an element outside the normal flow. Neighboring elements act as if it doesn't exist. Offsets are calculated relative to the nearest positioned parent:
```html
<div class="relative">
<div class="absolute top-0 right-0">
<p>Absolute child</p>
</div>
</div>
```
### Fixed positioning
Use `fixed` to position an element relative to the browser window. Offsets are calculated relative to the viewport:
```html
<div class="fixed top-0 right-0 left-0">
<p>Fixed header</p>
</div>
```
### Sticky positioning
Use `sticky` to position an element as `relative` until it crosses a threshold, then treat it as `fixed` until its parent is off screen:
```html
<div>
<div class="sticky top-0">
<p>Sticky header</p>
</div>
<div>Content...</div>
</div>
```
## Key Points
- `static` is the default - elements flow normally
- `relative` maintains normal flow but allows offsets and becomes a positioning context
- `absolute` removes from flow and positions relative to nearest positioned ancestor
- `fixed` positions relative to viewport, stays in place when scrolling
- `sticky` combines relative and fixed behavior based on scroll position
- Always use with offset utilities like `top-0`, `right-0`, `inset-0`, etc.
<!--
Source references:
- https://tailwindcss.com/docs/position
-->

View File

@@ -0,0 +1,67 @@
---
name: layout-tables
description: Table layout and border behavior with border-collapse, table-layout
---
# Table Layout
Utilities for controlling table display, border behavior, and layout algorithm.
## Usage
### Border collapse
```html
<!-- Collapsed: adjacent borders merge into single border -->
<table class="border-collapse border border-gray-400">
<thead>
<tr>
<th class="border border-gray-300 p-2">State</th>
<th class="border border-gray-300 p-2">City</th>
</tr>
</thead>
<tbody>
<tr>
<td class="border border-gray-300 p-2">Indiana</td>
<td class="border border-gray-300 p-2">Indianapolis</td>
</tr>
</tbody>
</table>
<!-- Separate: each cell has its own borders -->
<table class="border-separate border border-gray-400">...</table>
```
### Table layout
```html
<!-- Auto: columns sized to content -->
<table class="table-auto">...</table>
<!-- Fixed: columns use first row widths -->
<table class="table-fixed">...</table>
```
### Table display (from layout-display)
Combine with `table`, `table-row`, `table-cell`, `table-header-group`, etc. for semantic table structure.
### Responsive
```html
<table class="border-collapse md:border-separate">...</table>
```
## Key Points
- `border-collapse` - adjacent borders merge (single border between cells)
- `border-separate` - each cell displays its own borders
- `table-auto` - column widths from content
- `table-fixed` - fixed layout; first row sets column widths
- In v4, `border-*` and `divide-*` default to `currentColor`; specify a color (e.g. `border-gray-200`) explicitly
<!--
Source references:
- https://tailwindcss.com/docs/border-collapse
- https://tailwindcss.com/docs/table-layout
-->

View File

@@ -0,0 +1,102 @@
---
name: layout-width
description: Setting element width with spacing scale, fractions, container sizes, and viewport units
---
# Width
Utilities for setting the width of an element.
## Usage
### Spacing scale
Use `w-<number>` utilities based on the spacing scale:
```html
<div class="w-24">w-24</div>
<div class="w-48">w-48</div>
<div class="w-64">w-64</div>
```
### Percentage widths
Use `w-full` or `w-<fraction>` for percentage-based widths:
```html
<div class="flex">
<div class="w-1/2">50%</div>
<div class="w-1/2">50%</div>
</div>
<div class="flex">
<div class="w-2/5">40%</div>
<div class="w-3/5">60%</div>
</div>
<div class="w-full">100%</div>
```
### Container scale
Use container size utilities like `w-sm`, `w-md`, `w-lg`:
```html
<div class="w-sm">Small container</div>
<div class="w-md">Medium container</div>
<div class="w-xl">Extra large container</div>
```
### Viewport units
Use `w-screen` for full viewport width, or dynamic viewport units:
```html
<div class="w-screen">Full viewport width</div>
<div class="w-dvw">Dynamic viewport width</div>
<div class="w-svw">Small viewport width</div>
<div class="w-lvw">Large viewport width</div>
```
### Content-based widths
Use `w-auto`, `w-fit`, `w-min`, `w-max` for content-based sizing:
```html
<div class="w-auto">Auto width</div>
<div class="w-fit">Fit content</div>
<div class="w-min">Min content</div>
<div class="w-max">Max content</div>
```
### Size utility
Use `size-<number>` to set both width and height:
```html
<div class="size-16">16x16</div>
<div class="size-24">24x24</div>
<div class="size-full">100% x 100%</div>
```
### Custom values
Use arbitrary values for custom widths:
```html
<div class="w-[117px]">Custom pixel width</div>
<div class="w-[50%]">Custom percentage</div>
<div class="w-[calc(100%-2rem)]">Custom calculation</div>
```
## Key Points
- Spacing scale: `w-0` through `w-96` (and beyond)
- Fractions: `w-1/2`, `w-1/3`, `w-2/3`, `w-1/4`, `w-3/4`, `w-1/5`, `w-4/5`, `w-1/6`, `w-5/6`
- Container sizes: `w-3xs`, `w-2xs`, `w-xs`, `w-sm`, `w-md`, `w-lg`, `w-xl`, `w-2xl`, `w-3xl`, `w-4xl`, `w-5xl`, `w-6xl`, `w-7xl`
- Viewport units: `w-screen` (100vw), `w-dvw`, `w-svw`, `w-lvw`
- `size-*` utilities set both width and height simultaneously
- Use `w-auto` to reset width at specific breakpoints
<!--
Source references:
- https://tailwindcss.com/docs/width
-->

View File

@@ -0,0 +1,68 @@
---
name: transform-base
description: Base transform utilities for enabling transforms, hardware acceleration, and custom transform values
---
# Transform
Base utilities for transforming elements.
## Usage
### Hardware acceleration
Use `transform-gpu` to force GPU acceleration for better performance:
```html
<div class="scale-150 transform-gpu">
GPU-accelerated transform
</div>
```
### CPU rendering
Use `transform-cpu` to force CPU rendering if needed:
```html
<div class="scale-150 transform-cpu">
CPU-rendered transform
</div>
```
### Removing transforms
Use `transform-none` to remove all transforms:
```html
<div class="skew-y-3 md:transform-none">
Skewed on mobile, normal on desktop
</div>
```
### Custom transforms
Use arbitrary values for custom transform functions:
```html
<div class="transform-[matrix(1,2,3,4,5,6)]">
Custom matrix transform
</div>
<div class="transform-[perspective(1000px)_rotateX(45deg)]">
Custom 3D transform
</div>
```
## Key Points
- `transform-gpu` enables hardware acceleration with `translateZ(0)`
- `transform-cpu` forces CPU rendering (removes GPU acceleration)
- `transform-none` removes all transforms at once
- Use `transform-gpu` for better animation performance
- Custom transforms use arbitrary values: `transform-[...]`
- Transform utilities (translate, rotate, scale, skew) automatically enable transform
- Hardware acceleration is usually beneficial for animations
<!--
Source references:
- https://tailwindcss.com/docs/transform
-->

View File

@@ -0,0 +1,70 @@
---
name: transform-rotate
description: Rotating elements in 2D and 3D space with degree values and custom rotations
---
# Rotate
Utilities for rotating elements.
## Usage
### 2D rotation
Use `rotate-<number>` to rotate by degrees:
```html
<div class="rotate-45">45 degrees</div>
<div class="rotate-90">90 degrees</div>
<div class="-rotate-45">-45 degrees (counterclockwise)</div>
```
### 3D rotation
Use `rotate-x-<number>`, `rotate-y-<number>`, `rotate-z-<number>` for 3D rotation:
```html
<div class="rotate-x-50 rotate-z-45">3D rotation</div>
<div class="rotate-y-25 rotate-z-30">Combined 3D rotation</div>
```
### Common rotations
```html
<div class="rotate-90">Quarter turn</div>
<div class="rotate-180">Half turn</div>
<div class="rotate-270">Three-quarter turn</div>
```
### Custom values
Use arbitrary values for custom rotations:
```html
<div class="rotate-[3.142rad]">Custom radian rotation</div>
<div class="rotate-[45deg]">Explicit degree rotation</div>
```
### Removing rotation
Use `rotate-none` to remove rotation:
```html
<div class="rotate-45 md:rotate-none">Rotated on mobile only</div>
```
## Key Points
- `rotate-*` rotates in 2D plane (around z-axis)
- `rotate-x-*` rotates around x-axis (3D)
- `rotate-y-*` rotates around y-axis (3D)
- `rotate-z-*` rotates around z-axis (3D, same as `rotate-*`)
- Negative values rotate counterclockwise: `-rotate-45`
- Common values: `45`, `90`, `180`, `270` degrees
- Can combine multiple axes: `rotate-x-50 rotate-y-30 rotate-z-45`
- Use `rotate-none` to remove all rotations
<!--
Source references:
- https://tailwindcss.com/docs/rotate
-->

View File

@@ -0,0 +1,83 @@
---
name: transform-scale
description: Scaling elements uniformly or on specific axes with percentage values
---
# Scale
Utilities for scaling elements.
## Usage
### Uniform scaling
Use `scale-<number>` to scale on both axes (number represents percentage):
```html
<div class="scale-75">75% size</div>
<div class="scale-100">100% size (default)</div>
<div class="scale-125">125% size</div>
<div class="scale-150">150% size</div>
```
### Axis-specific scaling
Use `scale-x-<number>` or `scale-y-<number>` to scale on one axis:
```html
<div class="scale-x-75">75% width</div>
<div class="scale-y-125">125% height</div>
```
### Negative scaling
Use negative values to mirror and scale:
```html
<div class="-scale-x-100">Mirrored horizontally</div>
<div class="-scale-y-100">Mirrored vertically</div>
<div class="-scale-100">Mirrored both axes</div>
```
### Hover effects
Common pattern for interactive scaling:
```html
<div class="scale-95 hover:scale-100 transition-transform">
Grows on hover
</div>
```
### Removing scale
Use `scale-none` to remove scaling:
```html
<div class="scale-125 md:scale-none">Scaled on mobile only</div>
```
### Custom values
Use arbitrary values for custom scaling:
```html
<div class="scale-[1.7]">Custom scale value</div>
<div class="scale-x-[0.8]">Custom x-axis scale</div>
```
## Key Points
- `scale-*` scales uniformly on both axes
- `scale-x-*` scales horizontally only
- `scale-y-*` scales vertically only
- Values represent percentages: `scale-75` = 75%, `scale-125` = 125%
- `scale-100` is the default (no scaling)
- Negative values mirror the element: `-scale-x-100` flips horizontally
- Common for hover effects: `hover:scale-110`, `active:scale-95`
- Use `scale-none` to remove all scaling
<!--
Source references:
- https://tailwindcss.com/docs/scale
-->

View File

@@ -0,0 +1,62 @@
---
name: transform-skew
description: Skewing elements on x and y axes with degree values
---
# Skew
Utilities for skewing (distorting) elements.
## Usage
### Skewing both axes
Use `skew-<number>` to skew on both axes:
```html
<div class="skew-3">Slight skew</div>
<div class="skew-6">Moderate skew</div>
<div class="skew-12">Strong skew</div>
```
### Axis-specific skewing
Use `skew-x-<number>` or `skew-y-<number>` to skew on one axis:
```html
<div class="skew-x-12">Skewed horizontally</div>
<div class="skew-y-6">Skewed vertically</div>
```
### Negative skewing
Use negative values for opposite direction:
```html
<div class="-skew-3">Negative skew</div>
<div class="-skew-x-12">Negative x-axis skew</div>
```
### Custom values
Use arbitrary values for custom skew:
```html
<div class="skew-[3.142rad]">Custom radian skew</div>
<div class="skew-x-[15deg]">Custom degree skew</div>
```
## Key Points
- `skew-*` skews on both x and y axes
- `skew-x-*` skews horizontally only
- `skew-y-*` skews vertically only
- Values are in degrees: `skew-3` = 3 degrees
- Negative values skew in opposite direction
- Common values: `3`, `6`, `12` degrees for subtle effects
- Use sparingly - excessive skewing can make text hard to read
<!--
Source references:
- https://tailwindcss.com/docs/skew
-->

View File

@@ -0,0 +1,77 @@
---
name: transform-translate
description: Translating elements on x, y, and z axes with spacing scale, percentages, and custom values
---
# Translate
Utilities for translating (moving) elements.
## Usage
### Spacing scale
Use `translate-<number>` to translate on both axes, or `translate-x-<number>` / `translate-y-<number>` for single axis:
```html
<div class="translate-2">Moved 2 units</div>
<div class="-translate-4">Moved -4 units</div>
<div class="translate-x-4">Moved right 4 units</div>
<div class="translate-y-6">Moved down 6 units</div>
```
### Percentage translation
Use `translate-<fraction>` to translate by percentage of element size:
```html
<div class="translate-1/2">Moved 50% on both axes</div>
<div class="translate-x-1/4">Moved 25% right</div>
<div class="-translate-y-full">Moved 100% up</div>
```
### Centering elements
Common pattern for centering absolutely positioned elements:
```html
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
Centered
</div>
```
### Z-axis translation
Use `translate-z-<number>` for 3D translation (requires `transform-3d` on parent):
```html
<div class="transform-3d">
<div class="translate-z-4">Forward in 3D space</div>
<div class="-translate-z-8">Backward in 3D space</div>
</div>
```
### Custom values
Use arbitrary values for custom translations:
```html
<div class="translate-[3.142rad]">Custom translation</div>
<div class="translate-x-[117px]">Custom pixel value</div>
```
## Key Points
- `translate-*` moves on both x and y axes
- `translate-x-*` moves horizontally (right = positive)
- `translate-y-*` moves vertically (down = positive)
- `translate-z-*` moves in 3D space (forward = positive)
- Negative values use dash prefix: `-translate-4`, `-translate-x-8`
- Percentages are relative to element's own size
- Common centering: `top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2`
- Z-axis requires `transform-3d` utility on parent element
<!--
Source references:
- https://tailwindcss.com/docs/translate
-->

View File

@@ -0,0 +1,118 @@
---
name: typography-font-text
description: Font size, weight, style, text color, line height, letter spacing, and text decoration
---
# Typography: Font & Text
Utilities for font styling, text color, line height, letter spacing, and decoration.
## Usage
### Font size
```html
<p class="text-xs">Extra small</p>
<p class="text-sm">Small</p>
<p class="text-base">Base (default)</p>
<p class="text-lg">Large</p>
<p class="text-xl">Extra large</p>
<p class="text-2xl">2xl</p>
<p class="text-[17px]">Arbitrary</p>
```
### Font weight
```html
<p class="font-thin">100</p>
<p class="font-light">300</p>
<p class="font-normal">400</p>
<p class="font-medium">500</p>
<p class="font-semibold">600</p>
<p class="font-bold">700</p>
<p class="font-extrabold">800</p>
```
### Font style and family
```html
<p class="italic">Italic</p>
<p class="not-italic">Not italic</p>
<p class="font-sans">Sans (default)</p>
<p class="font-serif">Serif</p>
<p class="font-mono">Monospace</p>
```
### Text color
```html
<p class="text-gray-900">Dark text</p>
<p class="text-blue-500">Blue</p>
<p class="text-red-500/50">50% opacity</p>
<p class="text-[#1da1f2]">Arbitrary color</p>
<p class="text-(--my-color)">CSS variable</p>
```
### Line height
```html
<p class="leading-none">1</p>
<p class="leading-tight">1.25</p>
<p class="leading-normal">1.5</p>
<p class="leading-relaxed">1.625</p>
<p class="leading-loose">2</p>
<p class="leading-[3rem]">Arbitrary</p>
```
### Letter spacing
```html
<p class="tracking-tighter">-0.05em</p>
<p class="tracking-tight">-0.025em</p>
<p class="tracking-normal">0</p>
<p class="tracking-wide">0.025em</p>
<p class="tracking-widest">0.1em</p>
```
### Text decoration
```html
<p class="underline">Underline</p>
<p class="line-through">Strikethrough</p>
<p class="no-underline">Remove</p>
<p class="overline">Overline</p>
<p class="underline decoration-2 underline-offset-4">Custom</p>
```
### Text transform and overflow
```html
<p class="uppercase">UPPERCASE</p>
<p class="lowercase">lowercase</p>
<p class="capitalize">Capitalize Each</p>
<p class="normal-case">Normal</p>
<p class="truncate">Single line ellipsis</p>
<p class="text-ellipsis overflow-hidden">Ellipsis</p>
<p class="line-clamp-3">Clamp to 3 lines</p>
```
## Key Points
- Font size: `text-xs` through `text-9xl`, theme scale
- Weight: `font-thin` to `font-black`
- Color: `text-{color}-{shade}`, opacity modifier `/50`, arbitrary `text-[#hex]`
- Line height: `leading-none`, `leading-tight`, `leading-normal`, `leading-loose`
- Letter spacing: `tracking-tighter` to `tracking-widest`
- Decoration: `underline`, `line-through`, `no-underline`, `decoration-*`, `underline-offset-*`
- Overflow: `truncate` (ellipsis), `line-clamp-{n}`
<!--
Source references:
- https://tailwindcss.com/docs/font-size
- https://tailwindcss.com/docs/font-weight
- https://tailwindcss.com/docs/text-color
- https://tailwindcss.com/docs/line-height
- https://tailwindcss.com/docs/letter-spacing
- https://tailwindcss.com/docs/text-decoration
-->

View File

@@ -0,0 +1,65 @@
---
name: typography-list-style
description: Controlling list marker style and position with list-style-type and list-style-position
---
# List Style
Utilities for controlling the marker style and position of list items.
## Usage
### List style type
```html
<ul class="list-disc">
<li>Disc bullets (default for ul)</li>
</ul>
<ol class="list-decimal">
<li>Decimal numbers (default for ol)</li>
</ol>
<ul class="list-none">
<li>No markers (often with custom bullets via before/after)</li>
</ul>
<!-- Custom value -->
<ol class="list-[upper-roman]">Roman numerals</ol>
<ul class="list-(--my-marker)">Custom property</ul>
```
### List style position
```html
<!-- Bullets inside content flow -->
<ul class="list-inside list-disc">
<li>5 cups chopped Porcini mushrooms</li>
</ul>
<!-- Bullets outside content flow (default) -->
<ul class="list-outside list-disc">
<li>5 cups chopped Porcini mushrooms</li>
</ul>
```
### Responsive
```html
<ul class="list-none md:list-disc md:list-outside">...</ul>
```
## Key Points
- `list-disc` - disc bullets (ul default)
- `list-decimal` - decimal numbers (ol default)
- `list-none` - no markers
- Custom: `list-[upper-roman]`, `list-[lower-alpha]`, `list-(--var)`
- `list-inside` - markers inside content box
- `list-outside` - markers outside content box (default)
<!--
Source references:
- https://tailwindcss.com/docs/list-style-type
- https://tailwindcss.com/docs/list-style-position
-->

View File

@@ -0,0 +1,60 @@
---
name: typography-text-align
description: Controlling text alignment with left, center, right, justify, and logical properties
---
# Text Align
Utilities for controlling the alignment of text.
## Usage
### Basic alignment
Use `text-left`, `text-center`, `text-right`, `text-justify`:
```html
<p class="text-left">Left aligned</p>
<p class="text-center">Center aligned</p>
<p class="text-right">Right aligned</p>
<p class="text-justify">Justified text</p>
```
### Logical properties
Use `text-start` and `text-end` for RTL-aware alignment:
```html
<div dir="ltr">
<p class="text-start">Left in LTR</p>
<p class="text-end">Right in LTR</p>
</div>
<div dir="rtl">
<p class="text-start">Right in RTL</p>
<p class="text-end">Left in RTL</p>
</div>
```
### Responsive alignment
```html
<p class="text-left md:text-center lg:text-right">
Responsive alignment
</p>
```
## Key Points
- `text-left` - aligns to left edge
- `text-center` - centers text
- `text-right` - aligns to right edge
- `text-justify` - justifies text (spaces words evenly)
- `text-start` - aligns to start (left in LTR, right in RTL)
- `text-end` - aligns to end (right in LTR, left in RTL)
- Use logical properties (`text-start`, `text-end`) for internationalization
- Common pattern: `text-center` for headings, `text-left` for body text
<!--
Source references:
- https://tailwindcss.com/docs/text-align
-->

View File

@@ -0,0 +1,76 @@
---
name: visual-background
description: Background color, gradient, image, and attachment utilities
---
# Background
Utilities for background color, gradients, images, and attachment.
## Usage
### Background color
```html
<div class="bg-white">White</div>
<div class="bg-gray-100">Light gray</div>
<div class="bg-blue-500">Blue</div>
<div class="bg-sky-500/50">50% opacity</div>
<div class="bg-[#1da1f2]">Arbitrary</div>
<div class="bg-(--my-bg)">CSS variable</div>
```
Color palette follows theme (red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose, slate, gray, zinc, neutral, stone).
### Background image and gradient
```html
<div class="bg-gradient-to-r from-blue-500 to-purple-600">Linear gradient</div>
<div class="bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500">Multi-stop</div>
<div class="bg-[url('/img/hero.jpg')]">Image</div>
<div class="bg-[url('/img/hero.jpg')] bg-cover bg-center">Image with size/position</div>
```
Gradient directions: `to-t`, `to-tr`, `to-r`, `to-br`, `to-b`, `to-bl`, `to-l`, `to-tl`.
### Background size and position
```html
<div class="bg-auto">auto</div>
<div class="bg-cover">cover</div>
<div class="bg-contain">contain</div>
<div class="bg-center">center</div>
<div class="bg-top">top</div>
<div class="bg-bottom">bottom</div>
<div class="bg-left">left</div>
<div class="bg-right">right</div>
<div class="bg-[position:2rem_2rem]">Arbitrary</div>
```
### Background repeat and attachment
```html
<div class="bg-repeat">repeat (default)</div>
<div class="bg-no-repeat">no-repeat</div>
<div class="bg-repeat-x">repeat-x</div>
<div class="bg-fixed">fixed (parallax)</div>
<div class="bg-local">local</div>
<div class="bg-scroll">scroll</div>
```
## Key Points
- Colors: `bg-{color}-{shade}`, opacity `/50`, arbitrary `bg-[#hex]`
- Gradients: `bg-gradient-to-{dir}`, `from-*`, `via-*`, `to-*`
- Image: `bg-[url('...')]`, `bg-cover`, `bg-center`, etc.
- Size: `bg-auto`, `bg-cover`, `bg-contain`
- Position: `bg-center`, `bg-top`, `bg-[position:...]`
<!--
Source references:
- https://tailwindcss.com/docs/background-color
- https://tailwindcss.com/docs/background-image
-->

View File

@@ -0,0 +1,108 @@
---
name: visual-border
description: Border width, color, style, and border radius
---
# Border
Utilities for border width, color, style, and radius.
## Usage
### Border width
```html
<div class="border">1px all sides</div>
<div class="border-2">2px</div>
<div class="border-0">No border</div>
<div class="border-t border-r border-b border-l">Per side</div>
<div class="border-x-2">Horizontal</div>
<div class="border-y">Vertical</div>
```
### Border color
```html
<div class="border border-gray-300">Gray border</div>
<div class="border-2 border-blue-500">Blue</div>
<div class="border border-red-500/50">With opacity</div>
```
### Border style
```html
<div class="border border-solid">Solid (default)</div>
<div class="border border-dashed">Dashed</div>
<div class="border border-dotted">Dotted</div>
<div class="border border-double">Double</div>
<div class="border border-none">None</div>
```
### Border radius
```html
<div class="rounded">Small (default)</div>
<div class="rounded-sm">2px</div>
<div class="rounded-md">6px</div>
<div class="rounded-lg">8px</div>
<div class="rounded-xl">12px</div>
<div class="rounded-2xl">16px</div>
<div class="rounded-full">Pill/circle</div>
<div class="rounded-none">0</div>
<div class="rounded-t-lg rounded-b-none">Per corner</div>
<div class="rounded-s-md rounded-e-xl">Logical (start/end)</div>
```
### Divide (between children)
```html
<div class="divide-y divide-gray-200">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
<div class="divide-x divide-gray-300 flex">
<div>Col 1</div>
<div>Col 2</div>
</div>
```
### Ring (focus outline)
```html
<button class="ring-2 ring-blue-500 ring-offset-2">Ring</button>
<button class="focus:ring-3 focus:ring-blue-500">Focus ring</button>
```
### Outline
Separate from border; used for focus states. In v4: `outline` = 1px; `outline-2`, `outline-4` for width. Use `outline-offset-2` for offset.
```html
<button class="outline outline-offset-2 outline-sky-500">Outline</button>
<button class="focus:outline-2 focus:outline-cyan-500">Focus outline</button>
<!-- Accessibility: invisible but visible in forced-colors -->
<button class="focus:outline-hidden">Focus outline-hidden</button>
```
v4: `outline-none` = `outline-style: none`; `outline-hidden` = invisible but shows in forced-colors mode.
## Key Points
- Width: `border`, `border-{0,2,4,8}`, `border-{t,r,b,l,x,y}`
- Color: `border-{color}`, opacity modifier
- Radius: `rounded-{size}`, `rounded-full`, `rounded-{t,r,b,l,s,e}-*`, logical `rounded-s-*`, `rounded-e-*`
- Divide: `divide-{x,y}`, `divide-{color}` for borders between flex/grid children
- Ring: `ring`, `ring-{n}`, `ring-{color}`, `ring-offset-{n}` (v4 default ring = 1px; use `ring-3` for 3px)
- Outline: `outline`, `outline-{n}`, `outline-{color}`, `outline-offset-{n}`, `outline-hidden`, `outline-none`
<!--
Source references:
- https://tailwindcss.com/docs/border-width
- https://tailwindcss.com/docs/border-color
- https://tailwindcss.com/docs/border-radius
- https://tailwindcss.com/docs/divide-width
- https://tailwindcss.com/docs/ring-width
-->

View File

@@ -0,0 +1,90 @@
---
name: visual-effects
description: Box shadow, opacity, mix-blend, and filter effects
---
# Effects
Utilities for box shadow, opacity, mix-blend, and filters.
## Usage
### Box shadow
```html
<div class="shadow-sm">Small</div>
<div class="shadow">Default</div>
<div class="shadow-md">Medium</div>
<div class="shadow-lg">Large</div>
<div class="shadow-xl">Extra large</div>
<div class="shadow-2xl">2xl</div>
<div class="shadow-none">None</div>
<div class="shadow-lg shadow-blue-500/50">Colored shadow</div>
<div class="shadow-[0_0_15px_rgba(0,0,0,0.2)]">Arbitrary</div>
```
### Opacity
```html
<div class="opacity-0">Invisible</div>
<div class="opacity-50">50%</div>
<div class="opacity-100">Full</div>
```
### Mix blend mode
```html
<div class="mix-blend-normal">Normal</div>
<div class="mix-blend-multiply">Multiply</div>
<div class="mix-blend-screen">Screen</div>
<div class="mix-blend-overlay">Overlay</div>
```
### Backdrop blur and filter
```html
<div class="backdrop-blur-sm">Blur backdrop</div>
<div class="backdrop-blur-md">Medium blur</div>
<div class="backdrop-blur-none">No blur</div>
<div class="backdrop-opacity-50">Backdrop opacity</div>
```
### Filter (blur, brightness, contrast, etc.)
```html
<div class="blur-sm">Blur</div>
<div class="brightness-90">Brightness</div>
<div class="contrast-125">Contrast</div>
<div class="grayscale">Grayscale</div>
<div class="invert">Invert</div>
<div class="sepia">Sepia</div>
<div class="blur-none">No filter</div>
```
### Object fit (images/video)
```html
<img class="object-contain" /> <!-- Fit within bounds -->
<img class="object-cover" /> <!-- Cover area, may crop -->
<img class="object-fill" /> <!-- Stretch -->
<img class="object-none" /> <!-- No resize -->
<img class="object-scale-down" /> <!-- contain or none, whichever is smaller -->
<img class="object-top object-cover" /> <!-- Position -->
```
## Key Points
- Shadow: `shadow-{size}`, `shadow-{color}/opacity`, `shadow-none`
- Opacity: `opacity-{0-100}`
- Mix blend: `mix-blend-{mode}`
- Backdrop: `backdrop-blur-*`, `backdrop-opacity-*`
- Filter: `blur-*`, `brightness-*`, `contrast-*`, `grayscale`, `invert`, `sepia`
<!--
Source references:
- https://tailwindcss.com/docs/box-shadow
- https://tailwindcss.com/docs/opacity
- https://tailwindcss.com/docs/backdrop-blur
-->

View File

@@ -0,0 +1,82 @@
---
name: visual-svg
description: Styling SVG elements with fill, stroke, and stroke-width utilities
---
# SVG Styling
Utilities for styling SVG fill and stroke. Essential when working with icon sets like Heroicons.
## Usage
### Fill
```html
<!-- Theme colors -->
<svg class="fill-blue-500">...</svg>
<svg class="fill-indigo-500 hover:fill-indigo-600">...</svg>
<!-- Inherit from text color -->
<button class="text-indigo-600 hover:text-white">
<svg class="size-5 fill-current">...</svg>
Check for updates
</button>
<!-- Special values -->
<svg class="fill-none">...</svg>
<svg class="fill-inherit">...</svg>
<svg class="fill-transparent">...</svg>
<!-- Custom -->
<svg class="fill-[#243c5a]">...</svg>
<svg class="fill-(--my-fill)">...</svg>
```
### Stroke
```html
<!-- Theme colors -->
<svg class="stroke-cyan-500" fill="none">...</svg>
<!-- Inherit from text -->
<button class="text-pink-600 hover:text-white">
<svg class="size-5 stroke-current" fill="none">...</svg>
Download
</button>
<!-- Special values -->
<svg class="stroke-none stroke-inherit stroke-transparent">...</svg>
```
### Stroke width
```html
<svg class="stroke-1">Thin stroke</svg>
<svg class="stroke-2">Medium stroke</svg>
<svg class="stroke-[1.5]">Custom width</svg>
<svg class="stroke-(length:--my-stroke)">Custom property</svg>
```
### Combined
```html
<svg class="size-6 fill-blue-500 stroke-blue-700 stroke-2" fill="none">
<!-- Outlined icon with colored stroke -->
</svg>
```
## Key Points
- `fill-*` / `stroke-*` - all theme colors (e.g. `fill-red-500`)
- `fill-current` / `stroke-current` - use current text color (common for icons in buttons)
- `fill-none` / `stroke-none` - no fill/stroke
- `stroke-1`, `stroke-2`, etc. - stroke width (number = px)
- Custom: `fill-[#hex]`, `stroke-(--var)`, `stroke-[1.5]`
- Use with variants: `hover:fill-blue-600`, `md:stroke-2`
<!--
Source references:
- https://tailwindcss.com/docs/fill
- https://tailwindcss.com/docs/stroke
- https://tailwindcss.com/docs/stroke-width
-->

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
# React Best Practices
A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.
## Structure
- `rules/` - Individual rule files (one per rule)
- `_sections.md` - Section metadata (titles, impacts, descriptions)
- `_template.md` - Template for creating new rules
- `area-description.md` - Individual rule files
- `src/` - Build scripts and utilities
- `metadata.json` - Document metadata (version, organization, abstract)
- __`AGENTS.md`__ - Compiled output (generated)
- __`test-cases.json`__ - Test cases for LLM evaluation (generated)
## Getting Started
1. Install dependencies:
```bash
pnpm install
```
2. Build AGENTS.md from rules:
```bash
pnpm build
```
3. Validate rule files:
```bash
pnpm validate
```
4. Extract test cases:
```bash
pnpm extract-tests
```
## Creating a New Rule
1. Copy `rules/_template.md` to `rules/area-description.md`
2. Choose the appropriate area prefix:
- `async-` for Eliminating Waterfalls (Section 1)
- `bundle-` for Bundle Size Optimization (Section 2)
- `server-` for Server-Side Performance (Section 3)
- `client-` for Client-Side Data Fetching (Section 4)
- `rerender-` for Re-render Optimization (Section 5)
- `rendering-` for Rendering Performance (Section 6)
- `js-` for JavaScript Performance (Section 7)
- `advanced-` for Advanced Patterns (Section 8)
3. Fill in the frontmatter and content
4. Ensure you have clear examples with explanations
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json
## Rule File Structure
Each rule file should follow this structure:
```markdown
---
title: Rule Title Here
impact: MEDIUM
impactDescription: Optional description
tags: tag1, tag2, tag3
---
## Rule Title Here
Brief explanation of the rule and why it matters.
**Incorrect (description of what's wrong):**
```typescript
// Bad code example
```
**Correct (description of what's right):**
```typescript
// Good code example
```
Optional explanatory text after examples.
Reference: [Link](https://example.com)
## File Naming Convention
- Files starting with `_` are special (excluded from build)
- Rule files: `area-description.md` (e.g., `async-parallel.md`)
- Section is automatically inferred from filename prefix
- Rules are sorted alphabetically by title within each section
- IDs (e.g., 1.1, 1.2) are auto-generated during build
## Impact Levels
- `CRITICAL` - Highest priority, major performance gains
- `HIGH` - Significant performance improvements
- `MEDIUM-HIGH` - Moderate-high gains
- `MEDIUM` - Moderate performance improvements
- `LOW-MEDIUM` - Low-medium gains
- `LOW` - Incremental improvements
## Scripts
- `pnpm build` - Compile rules into AGENTS.md
- `pnpm validate` - Validate all rule files
- `pnpm extract-tests` - Extract test cases for LLM evaluation
- `pnpm dev` - Build and validate
## Contributing
When adding or modifying rules:
1. Use the correct filename prefix for your section
2. Follow the `_template.md` structure
3. Include clear bad/good examples with explanations
4. Add appropriate tags
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json
6. Rules are automatically sorted by title - no need to manage numbers!
## Acknowledgments
Originally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).

View File

@@ -0,0 +1,141 @@
---
name: vercel-react-best-practices
description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
license: MIT
metadata:
author: vercel
version: "1.0.0"
---
# Vercel React Best Practices
Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 62 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation.
## When to Apply
Reference these guidelines when:
- Writing new React components or Next.js pages
- Implementing data fetching (client or server-side)
- Reviewing code for performance issues
- Refactoring existing React/Next.js code
- Optimizing bundle size or load times
## Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|----------|----------|--------|--------|
| 1 | Eliminating Waterfalls | CRITICAL | `async-` |
| 2 | Bundle Size Optimization | CRITICAL | `bundle-` |
| 3 | Server-Side Performance | HIGH | `server-` |
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `client-` |
| 5 | Re-render Optimization | MEDIUM | `rerender-` |
| 6 | Rendering Performance | MEDIUM | `rendering-` |
| 7 | JavaScript Performance | LOW-MEDIUM | `js-` |
| 8 | Advanced Patterns | LOW | `advanced-` |
## Quick Reference
### 1. Eliminating Waterfalls (CRITICAL)
- `async-defer-await` - Move await into branches where actually used
- `async-parallel` - Use Promise.all() for independent operations
- `async-dependencies` - Use better-all for partial dependencies
- `async-api-routes` - Start promises early, await late in API routes
- `async-suspense-boundaries` - Use Suspense to stream content
### 2. Bundle Size Optimization (CRITICAL)
- `bundle-barrel-imports` - Import directly, avoid barrel files
- `bundle-dynamic-imports` - Use next/dynamic for heavy components
- `bundle-defer-third-party` - Load analytics/logging after hydration
- `bundle-conditional` - Load modules only when feature is activated
- `bundle-preload` - Preload on hover/focus for perceived speed
### 3. Server-Side Performance (HIGH)
- `server-auth-actions` - Authenticate server actions like API routes
- `server-cache-react` - Use React.cache() for per-request deduplication
- `server-cache-lru` - Use LRU cache for cross-request caching
- `server-dedup-props` - Avoid duplicate serialization in RSC props
- `server-hoist-static-io` - Hoist static I/O (fonts, logos) to module level
- `server-serialization` - Minimize data passed to client components
- `server-parallel-fetching` - Restructure components to parallelize fetches
- `server-after-nonblocking` - Use after() for non-blocking operations
### 4. Client-Side Data Fetching (MEDIUM-HIGH)
- `client-swr-dedup` - Use SWR for automatic request deduplication
- `client-event-listeners` - Deduplicate global event listeners
- `client-passive-event-listeners` - Use passive listeners for scroll
- `client-localstorage-schema` - Version and minimize localStorage data
### 5. Re-render Optimization (MEDIUM)
- `rerender-defer-reads` - Don't subscribe to state only used in callbacks
- `rerender-memo` - Extract expensive work into memoized components
- `rerender-memo-with-default-value` - Hoist default non-primitive props
- `rerender-dependencies` - Use primitive dependencies in effects
- `rerender-derived-state` - Subscribe to derived booleans, not raw values
- `rerender-derived-state-no-effect` - Derive state during render, not effects
- `rerender-functional-setstate` - Use functional setState for stable callbacks
- `rerender-lazy-state-init` - Pass function to useState for expensive values
- `rerender-simple-expression-in-memo` - Avoid memo for simple primitives
- `rerender-move-effect-to-event` - Put interaction logic in event handlers
- `rerender-transitions` - Use startTransition for non-urgent updates
- `rerender-use-ref-transient-values` - Use refs for transient frequent values
- `rerender-no-inline-components` - Don't define components inside components
### 6. Rendering Performance (MEDIUM)
- `rendering-animate-svg-wrapper` - Animate div wrapper, not SVG element
- `rendering-content-visibility` - Use content-visibility for long lists
- `rendering-hoist-jsx` - Extract static JSX outside components
- `rendering-svg-precision` - Reduce SVG coordinate precision
- `rendering-hydration-no-flicker` - Use inline script for client-only data
- `rendering-hydration-suppress-warning` - Suppress expected mismatches
- `rendering-activity` - Use Activity component for show/hide
- `rendering-conditional-render` - Use ternary, not && for conditionals
- `rendering-usetransition-loading` - Prefer useTransition for loading state
- `rendering-resource-hints` - Use React DOM resource hints for preloading
- `rendering-script-defer-async` - Use defer or async on script tags
### 7. JavaScript Performance (LOW-MEDIUM)
- `js-batch-dom-css` - Group CSS changes via classes or cssText
- `js-index-maps` - Build Map for repeated lookups
- `js-cache-property-access` - Cache object properties in loops
- `js-cache-function-results` - Cache function results in module-level Map
- `js-cache-storage` - Cache localStorage/sessionStorage reads
- `js-combine-iterations` - Combine multiple filter/map into one loop
- `js-length-check-first` - Check array length before expensive comparison
- `js-early-exit` - Return early from functions
- `js-hoist-regexp` - Hoist RegExp creation outside loops
- `js-min-max-loop` - Use loop for min/max instead of sort
- `js-set-map-lookups` - Use Set/Map for O(1) lookups
- `js-tosorted-immutable` - Use toSorted() for immutability
- `js-flatmap-filter` - Use flatMap to map and filter in one pass
### 8. Advanced Patterns (LOW)
- `advanced-event-handler-refs` - Store event handlers in refs
- `advanced-init-once` - Initialize app once per app load
- `advanced-use-latest` - useLatest for stable callback refs
## How to Use
Read individual rule files for detailed explanations and code examples:
```
rules/async-parallel.md
rules/bundle-barrel-imports.md
```
Each rule file contains:
- Brief explanation of why it matters
- Incorrect code example with explanation
- Correct code example with explanation
- Additional context and references
## Full Compiled Document
For the complete guide with all rules expanded: `AGENTS.md`

View File

@@ -0,0 +1,55 @@
---
title: Store Event Handlers in Refs
impact: LOW
impactDescription: stable subscriptions
tags: advanced, hooks, refs, event-handlers, optimization
---
## Store Event Handlers in Refs
Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.
**Incorrect (re-subscribes on every render):**
```tsx
function useWindowEvent(event: string, handler: (e) => void) {
useEffect(() => {
window.addEventListener(event, handler)
return () => window.removeEventListener(event, handler)
}, [event, handler])
}
```
**Correct (stable subscription):**
```tsx
function useWindowEvent(event: string, handler: (e) => void) {
const handlerRef = useRef(handler)
useEffect(() => {
handlerRef.current = handler
}, [handler])
useEffect(() => {
const listener = (e) => handlerRef.current(e)
window.addEventListener(event, listener)
return () => window.removeEventListener(event, listener)
}, [event])
}
```
**Alternative: use `useEffectEvent` if you're on latest React:**
```tsx
import { useEffectEvent } from 'react'
function useWindowEvent(event: string, handler: (e) => void) {
const onEvent = useEffectEvent(handler)
useEffect(() => {
window.addEventListener(event, onEvent)
return () => window.removeEventListener(event, onEvent)
}, [event])
}
```
`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.

View File

@@ -0,0 +1,42 @@
---
title: Initialize App Once, Not Per Mount
impact: LOW-MEDIUM
impactDescription: avoids duplicate init in development
tags: initialization, useEffect, app-startup, side-effects
---
## Initialize App Once, Not Per Mount
Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.
**Incorrect (runs twice in dev, re-runs on remount):**
```tsx
function Comp() {
useEffect(() => {
loadFromStorage()
checkAuthToken()
}, [])
// ...
}
```
**Correct (once per app load):**
```tsx
let didInit = false
function Comp() {
useEffect(() => {
if (didInit) return
didInit = true
loadFromStorage()
checkAuthToken()
}, [])
// ...
}
```
Reference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)

View File

@@ -0,0 +1,39 @@
---
title: useEffectEvent for Stable Callback Refs
impact: LOW
impactDescription: prevents effect re-runs
tags: advanced, hooks, useEffectEvent, refs, optimization
---
## useEffectEvent for Stable Callback Refs
Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.
**Incorrect (effect re-runs on every callback change):**
```tsx
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
const [query, setQuery] = useState('')
useEffect(() => {
const timeout = setTimeout(() => onSearch(query), 300)
return () => clearTimeout(timeout)
}, [query, onSearch])
}
```
**Correct (using React's useEffectEvent):**
```tsx
import { useEffectEvent } from 'react';
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
const [query, setQuery] = useState('')
const onSearchEvent = useEffectEvent(onSearch)
useEffect(() => {
const timeout = setTimeout(() => onSearchEvent(query), 300)
return () => clearTimeout(timeout)
}, [query])
}
```

View File

@@ -0,0 +1,38 @@
---
title: Prevent Waterfall Chains in API Routes
impact: CRITICAL
impactDescription: 2-10× improvement
tags: api-routes, server-actions, waterfalls, parallelization
---
## Prevent Waterfall Chains in API Routes
In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.
**Incorrect (config waits for auth, data waits for both):**
```typescript
export async function GET(request: Request) {
const session = await auth()
const config = await fetchConfig()
const data = await fetchData(session.user.id)
return Response.json({ data, config })
}
```
**Correct (auth and config start immediately):**
```typescript
export async function GET(request: Request) {
const sessionPromise = auth()
const configPromise = fetchConfig()
const session = await sessionPromise
const [config, data] = await Promise.all([
configPromise,
fetchData(session.user.id)
])
return Response.json({ data, config })
}
```
For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).

View File

@@ -0,0 +1,80 @@
---
title: Defer Await Until Needed
impact: HIGH
impactDescription: avoids blocking unused code paths
tags: async, await, conditional, optimization
---
## Defer Await Until Needed
Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.
**Incorrect (blocks both branches):**
```typescript
async function handleRequest(userId: string, skipProcessing: boolean) {
const userData = await fetchUserData(userId)
if (skipProcessing) {
// Returns immediately but still waited for userData
return { skipped: true }
}
// Only this branch uses userData
return processUserData(userData)
}
```
**Correct (only blocks when needed):**
```typescript
async function handleRequest(userId: string, skipProcessing: boolean) {
if (skipProcessing) {
// Returns immediately without waiting
return { skipped: true }
}
// Fetch only when needed
const userData = await fetchUserData(userId)
return processUserData(userData)
}
```
**Another example (early return optimization):**
```typescript
// Incorrect: always fetches permissions
async function updateResource(resourceId: string, userId: string) {
const permissions = await fetchPermissions(userId)
const resource = await getResource(resourceId)
if (!resource) {
return { error: 'Not found' }
}
if (!permissions.canEdit) {
return { error: 'Forbidden' }
}
return await updateResourceData(resource, permissions)
}
// Correct: fetches only when needed
async function updateResource(resourceId: string, userId: string) {
const resource = await getResource(resourceId)
if (!resource) {
return { error: 'Not found' }
}
const permissions = await fetchPermissions(userId)
if (!permissions.canEdit) {
return { error: 'Forbidden' }
}
return await updateResourceData(resource, permissions)
}
```
This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.

View File

@@ -0,0 +1,51 @@
---
title: Dependency-Based Parallelization
impact: CRITICAL
impactDescription: 2-10× improvement
tags: async, parallelization, dependencies, better-all
---
## Dependency-Based Parallelization
For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.
**Incorrect (profile waits for config unnecessarily):**
```typescript
const [user, config] = await Promise.all([
fetchUser(),
fetchConfig()
])
const profile = await fetchProfile(user.id)
```
**Correct (config and profile run in parallel):**
```typescript
import { all } from 'better-all'
const { user, config, profile } = await all({
async user() { return fetchUser() },
async config() { return fetchConfig() },
async profile() {
return fetchProfile((await this.$.user).id)
}
})
```
**Alternative without extra dependencies:**
We can also create all the promises first, and do `Promise.all()` at the end.
```typescript
const userPromise = fetchUser()
const profilePromise = userPromise.then(user => fetchProfile(user.id))
const [user, config, profile] = await Promise.all([
userPromise,
fetchConfig(),
profilePromise
])
```
Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)

View File

@@ -0,0 +1,28 @@
---
title: Promise.all() for Independent Operations
impact: CRITICAL
impactDescription: 2-10× improvement
tags: async, parallelization, promises, waterfalls
---
## Promise.all() for Independent Operations
When async operations have no interdependencies, execute them concurrently using `Promise.all()`.
**Incorrect (sequential execution, 3 round trips):**
```typescript
const user = await fetchUser()
const posts = await fetchPosts()
const comments = await fetchComments()
```
**Correct (parallel execution, 1 round trip):**
```typescript
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
])
```

View File

@@ -0,0 +1,99 @@
---
title: Strategic Suspense Boundaries
impact: HIGH
impactDescription: faster initial paint
tags: async, suspense, streaming, layout-shift
---
## Strategic Suspense Boundaries
Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.
**Incorrect (wrapper blocked by data fetching):**
```tsx
async function Page() {
const data = await fetchData() // Blocks entire page
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<div>
<DataDisplay data={data} />
</div>
<div>Footer</div>
</div>
)
}
```
The entire layout waits for data even though only the middle section needs it.
**Correct (wrapper shows immediately, data streams in):**
```tsx
function Page() {
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<div>
<Suspense fallback={<Skeleton />}>
<DataDisplay />
</Suspense>
</div>
<div>Footer</div>
</div>
)
}
async function DataDisplay() {
const data = await fetchData() // Only blocks this component
return <div>{data.content}</div>
}
```
Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data.
**Alternative (share promise across components):**
```tsx
function Page() {
// Start fetch immediately, but don't await
const dataPromise = fetchData()
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<Suspense fallback={<Skeleton />}>
<DataDisplay dataPromise={dataPromise} />
<DataSummary dataPromise={dataPromise} />
</Suspense>
<div>Footer</div>
</div>
)
}
function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise) // Unwraps the promise
return <div>{data.content}</div>
}
function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise) // Reuses the same promise
return <div>{data.summary}</div>
}
```
Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.
**When NOT to use this pattern:**
- Critical data needed for layout decisions (affects positioning)
- SEO-critical content above the fold
- Small, fast queries where suspense overhead isn't worth it
- When you want to avoid layout shift (loading → content jump)
**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.

View File

@@ -0,0 +1,59 @@
---
title: Avoid Barrel File Imports
impact: CRITICAL
impactDescription: 200-800ms import cost, slow builds
tags: bundle, imports, tree-shaking, barrel-files, performance
---
## Avoid Barrel File Imports
Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).
Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.
**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.
**Incorrect (imports entire library):**
```tsx
import { Check, X, Menu } from 'lucide-react'
// Loads 1,583 modules, takes ~2.8s extra in dev
// Runtime cost: 200-800ms on every cold start
import { Button, TextField } from '@mui/material'
// Loads 2,225 modules, takes ~4.2s extra in dev
```
**Correct (imports only what you need):**
```tsx
import Check from 'lucide-react/dist/esm/icons/check'
import X from 'lucide-react/dist/esm/icons/x'
import Menu from 'lucide-react/dist/esm/icons/menu'
// Loads only 3 modules (~2KB vs ~1MB)
import Button from '@mui/material/Button'
import TextField from '@mui/material/TextField'
// Loads only what you use
```
**Alternative (Next.js 13.5+):**
```js
// next.config.js - use optimizePackageImports
module.exports = {
experimental: {
optimizePackageImports: ['lucide-react', '@mui/material']
}
}
// Then you can keep the ergonomic barrel imports:
import { Check, X, Menu } from 'lucide-react'
// Automatically transformed to direct imports at build time
```
Direct imports provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.
Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.
Reference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)

View File

@@ -0,0 +1,31 @@
---
title: Conditional Module Loading
impact: HIGH
impactDescription: loads large data only when needed
tags: bundle, conditional-loading, lazy-loading
---
## Conditional Module Loading
Load large data or modules only when a feature is activated.
**Example (lazy-load animation frames):**
```tsx
function AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {
const [frames, setFrames] = useState<Frame[] | null>(null)
useEffect(() => {
if (enabled && !frames && typeof window !== 'undefined') {
import('./animation-frames.js')
.then(mod => setFrames(mod.frames))
.catch(() => setEnabled(false))
}
}, [enabled, frames, setEnabled])
if (!frames) return <Skeleton />
return <Canvas frames={frames} />
}
```
The `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.

View File

@@ -0,0 +1,49 @@
---
title: Defer Non-Critical Third-Party Libraries
impact: MEDIUM
impactDescription: loads after hydration
tags: bundle, third-party, analytics, defer
---
## Defer Non-Critical Third-Party Libraries
Analytics, logging, and error tracking don't block user interaction. Load them after hydration.
**Incorrect (blocks initial bundle):**
```tsx
import { Analytics } from '@vercel/analytics/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}
```
**Correct (loads after hydration):**
```tsx
import dynamic from 'next/dynamic'
const Analytics = dynamic(
() => import('@vercel/analytics/react').then(m => m.Analytics),
{ ssr: false }
)
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}
```

View File

@@ -0,0 +1,35 @@
---
title: Dynamic Imports for Heavy Components
impact: CRITICAL
impactDescription: directly affects TTI and LCP
tags: bundle, dynamic-import, code-splitting, next-dynamic
---
## Dynamic Imports for Heavy Components
Use `next/dynamic` to lazy-load large components not needed on initial render.
**Incorrect (Monaco bundles with main chunk ~300KB):**
```tsx
import { MonacoEditor } from './monaco-editor'
function CodePanel({ code }: { code: string }) {
return <MonacoEditor value={code} />
}
```
**Correct (Monaco loads on demand):**
```tsx
import dynamic from 'next/dynamic'
const MonacoEditor = dynamic(
() => import('./monaco-editor').then(m => m.MonacoEditor),
{ ssr: false }
)
function CodePanel({ code }: { code: string }) {
return <MonacoEditor value={code} />
}
```

View File

@@ -0,0 +1,50 @@
---
title: Preload Based on User Intent
impact: MEDIUM
impactDescription: reduces perceived latency
tags: bundle, preload, user-intent, hover
---
## Preload Based on User Intent
Preload heavy bundles before they're needed to reduce perceived latency.
**Example (preload on hover/focus):**
```tsx
function EditorButton({ onClick }: { onClick: () => void }) {
const preload = () => {
if (typeof window !== 'undefined') {
void import('./monaco-editor')
}
}
return (
<button
onMouseEnter={preload}
onFocus={preload}
onClick={onClick}
>
Open Editor
</button>
)
}
```
**Example (preload when feature flag is enabled):**
```tsx
function FlagsProvider({ children, flags }: Props) {
useEffect(() => {
if (flags.editorEnabled && typeof window !== 'undefined') {
void import('./monaco-editor').then(mod => mod.init())
}
}, [flags.editorEnabled])
return <FlagsContext.Provider value={flags}>
{children}
</FlagsContext.Provider>
}
```
The `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.

View File

@@ -0,0 +1,74 @@
---
title: Deduplicate Global Event Listeners
impact: LOW
impactDescription: single listener for N components
tags: client, swr, event-listeners, subscription
---
## Deduplicate Global Event Listeners
Use `useSWRSubscription()` to share global event listeners across component instances.
**Incorrect (N instances = N listeners):**
```tsx
function useKeyboardShortcut(key: string, callback: () => void) {
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.metaKey && e.key === key) {
callback()
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
}, [key, callback])
}
```
When using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.
**Correct (N instances = 1 listener):**
```tsx
import useSWRSubscription from 'swr/subscription'
// Module-level Map to track callbacks per key
const keyCallbacks = new Map<string, Set<() => void>>()
function useKeyboardShortcut(key: string, callback: () => void) {
// Register this callback in the Map
useEffect(() => {
if (!keyCallbacks.has(key)) {
keyCallbacks.set(key, new Set())
}
keyCallbacks.get(key)!.add(callback)
return () => {
const set = keyCallbacks.get(key)
if (set) {
set.delete(callback)
if (set.size === 0) {
keyCallbacks.delete(key)
}
}
}
}, [key, callback])
useSWRSubscription('global-keydown', () => {
const handler = (e: KeyboardEvent) => {
if (e.metaKey && keyCallbacks.has(e.key)) {
keyCallbacks.get(e.key)!.forEach(cb => cb())
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
})
}
function Profile() {
// Multiple shortcuts will share the same listener
useKeyboardShortcut('p', () => { /* ... */ })
useKeyboardShortcut('k', () => { /* ... */ })
// ...
}
```

View File

@@ -0,0 +1,71 @@
---
title: Version and Minimize localStorage Data
impact: MEDIUM
impactDescription: prevents schema conflicts, reduces storage size
tags: client, localStorage, storage, versioning, data-minimization
---
## Version and Minimize localStorage Data
Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.
**Incorrect:**
```typescript
// No version, stores everything, no error handling
localStorage.setItem('userConfig', JSON.stringify(fullUserObject))
const data = localStorage.getItem('userConfig')
```
**Correct:**
```typescript
const VERSION = 'v2'
function saveConfig(config: { theme: string; language: string }) {
try {
localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))
} catch {
// Throws in incognito/private browsing, quota exceeded, or disabled
}
}
function loadConfig() {
try {
const data = localStorage.getItem(`userConfig:${VERSION}`)
return data ? JSON.parse(data) : null
} catch {
return null
}
}
// Migration from v1 to v2
function migrate() {
try {
const v1 = localStorage.getItem('userConfig:v1')
if (v1) {
const old = JSON.parse(v1)
saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })
localStorage.removeItem('userConfig:v1')
}
} catch {}
}
```
**Store minimal fields from server responses:**
```typescript
// User object has 20+ fields, only store what UI needs
function cachePrefs(user: FullUser) {
try {
localStorage.setItem('prefs:v1', JSON.stringify({
theme: user.preferences.theme,
notifications: user.preferences.notifications
}))
} catch {}
}
```
**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.
**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.

View File

@@ -0,0 +1,48 @@
---
title: Use Passive Event Listeners for Scrolling Performance
impact: MEDIUM
impactDescription: eliminates scroll delay caused by event listeners
tags: client, event-listeners, scrolling, performance, touch, wheel
---
## Use Passive Event Listeners for Scrolling Performance
Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.
**Incorrect:**
```typescript
useEffect(() => {
const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
document.addEventListener('touchstart', handleTouch)
document.addEventListener('wheel', handleWheel)
return () => {
document.removeEventListener('touchstart', handleTouch)
document.removeEventListener('wheel', handleWheel)
}
}, [])
```
**Correct:**
```typescript
useEffect(() => {
const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
document.addEventListener('touchstart', handleTouch, { passive: true })
document.addEventListener('wheel', handleWheel, { passive: true })
return () => {
document.removeEventListener('touchstart', handleTouch)
document.removeEventListener('wheel', handleWheel)
}
}, [])
```
**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.
**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.

View File

@@ -0,0 +1,56 @@
---
title: Use SWR for Automatic Deduplication
impact: MEDIUM-HIGH
impactDescription: automatic deduplication
tags: client, swr, deduplication, data-fetching
---
## Use SWR for Automatic Deduplication
SWR enables request deduplication, caching, and revalidation across component instances.
**Incorrect (no deduplication, each instance fetches):**
```tsx
function UserList() {
const [users, setUsers] = useState([])
useEffect(() => {
fetch('/api/users')
.then(r => r.json())
.then(setUsers)
}, [])
}
```
**Correct (multiple instances share one request):**
```tsx
import useSWR from 'swr'
function UserList() {
const { data: users } = useSWR('/api/users', fetcher)
}
```
**For immutable data:**
```tsx
import { useImmutableSWR } from '@/lib/swr'
function StaticContent() {
const { data } = useImmutableSWR('/api/config', fetcher)
}
```
**For mutations:**
```tsx
import { useSWRMutation } from 'swr/mutation'
function UpdateButton() {
const { trigger } = useSWRMutation('/api/user', updateUser)
return <button onClick={() => trigger()}>Update</button>
}
```
Reference: [https://swr.vercel.app](https://swr.vercel.app)

View File

@@ -0,0 +1,107 @@
---
title: Avoid Layout Thrashing
impact: MEDIUM
impactDescription: prevents forced synchronous layouts and reduces performance bottlenecks
tags: javascript, dom, css, performance, reflow, layout-thrashing
---
## Avoid Layout Thrashing
Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.
**This is OK (browser batches style changes):**
```typescript
function updateElementStyles(element: HTMLElement) {
// Each line invalidates style, but browser batches the recalculation
element.style.width = '100px'
element.style.height = '200px'
element.style.backgroundColor = 'blue'
element.style.border = '1px solid black'
}
```
**Incorrect (interleaved reads and writes force reflows):**
```typescript
function layoutThrashing(element: HTMLElement) {
element.style.width = '100px'
const width = element.offsetWidth // Forces reflow
element.style.height = '200px'
const height = element.offsetHeight // Forces another reflow
}
```
**Correct (batch writes, then read once):**
```typescript
function updateElementStyles(element: HTMLElement) {
// Batch all writes together
element.style.width = '100px'
element.style.height = '200px'
element.style.backgroundColor = 'blue'
element.style.border = '1px solid black'
// Read after all writes are done (single reflow)
const { width, height } = element.getBoundingClientRect()
}
```
**Correct (batch reads, then writes):**
```typescript
function avoidThrashing(element: HTMLElement) {
// Read phase - all layout queries first
const rect1 = element.getBoundingClientRect()
const offsetWidth = element.offsetWidth
const offsetHeight = element.offsetHeight
// Write phase - all style changes after
element.style.width = '100px'
element.style.height = '200px'
}
```
**Better: use CSS classes**
```css
.highlighted-box {
width: 100px;
height: 200px;
background-color: blue;
border: 1px solid black;
}
```
```typescript
function updateElementStyles(element: HTMLElement) {
element.classList.add('highlighted-box')
const { width, height } = element.getBoundingClientRect()
}
```
**React example:**
```tsx
// Incorrect: interleaving style changes with layout queries
function Box({ isHighlighted }: { isHighlighted: boolean }) {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if (ref.current && isHighlighted) {
ref.current.style.width = '100px'
const width = ref.current.offsetWidth // Forces layout
ref.current.style.height = '200px'
}
}, [isHighlighted])
return <div ref={ref}>Content</div>
}
// Correct: toggle class
function Box({ isHighlighted }: { isHighlighted: boolean }) {
return (
<div className={isHighlighted ? 'highlighted-box' : ''}>
Content
</div>
)
}
```
Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.
See [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.

View File

@@ -0,0 +1,80 @@
---
title: Cache Repeated Function Calls
impact: MEDIUM
impactDescription: avoid redundant computation
tags: javascript, cache, memoization, performance
---
## Cache Repeated Function Calls
Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.
**Incorrect (redundant computation):**
```typescript
function ProjectList({ projects }: { projects: Project[] }) {
return (
<div>
{projects.map(project => {
// slugify() called 100+ times for same project names
const slug = slugify(project.name)
return <ProjectCard key={project.id} slug={slug} />
})}
</div>
)
}
```
**Correct (cached results):**
```typescript
// Module-level cache
const slugifyCache = new Map<string, string>()
function cachedSlugify(text: string): string {
if (slugifyCache.has(text)) {
return slugifyCache.get(text)!
}
const result = slugify(text)
slugifyCache.set(text, result)
return result
}
function ProjectList({ projects }: { projects: Project[] }) {
return (
<div>
{projects.map(project => {
// Computed only once per unique project name
const slug = cachedSlugify(project.name)
return <ProjectCard key={project.id} slug={slug} />
})}
</div>
)
}
```
**Simpler pattern for single-value functions:**
```typescript
let isLoggedInCache: boolean | null = null
function isLoggedIn(): boolean {
if (isLoggedInCache !== null) {
return isLoggedInCache
}
isLoggedInCache = document.cookie.includes('auth=')
return isLoggedInCache
}
// Clear cache when auth changes
function onAuthChange() {
isLoggedInCache = null
}
```
Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
Reference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)

View File

@@ -0,0 +1,28 @@
---
title: Cache Property Access in Loops
impact: LOW-MEDIUM
impactDescription: reduces lookups
tags: javascript, loops, optimization, caching
---
## Cache Property Access in Loops
Cache object property lookups in hot paths.
**Incorrect (3 lookups × N iterations):**
```typescript
for (let i = 0; i < arr.length; i++) {
process(obj.config.settings.value)
}
```
**Correct (1 lookup total):**
```typescript
const value = obj.config.settings.value
const len = arr.length
for (let i = 0; i < len; i++) {
process(value)
}
```

View File

@@ -0,0 +1,70 @@
---
title: Cache Storage API Calls
impact: LOW-MEDIUM
impactDescription: reduces expensive I/O
tags: javascript, localStorage, storage, caching, performance
---
## Cache Storage API Calls
`localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.
**Incorrect (reads storage on every call):**
```typescript
function getTheme() {
return localStorage.getItem('theme') ?? 'light'
}
// Called 10 times = 10 storage reads
```
**Correct (Map cache):**
```typescript
const storageCache = new Map<string, string | null>()
function getLocalStorage(key: string) {
if (!storageCache.has(key)) {
storageCache.set(key, localStorage.getItem(key))
}
return storageCache.get(key)
}
function setLocalStorage(key: string, value: string) {
localStorage.setItem(key, value)
storageCache.set(key, value) // keep cache in sync
}
```
Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
**Cookie caching:**
```typescript
let cookieCache: Record<string, string> | null = null
function getCookie(name: string) {
if (!cookieCache) {
cookieCache = Object.fromEntries(
document.cookie.split('; ').map(c => c.split('='))
)
}
return cookieCache[name]
}
```
**Important (invalidate on external changes):**
If storage can change externally (another tab, server-set cookies), invalidate cache:
```typescript
window.addEventListener('storage', (e) => {
if (e.key) storageCache.delete(e.key)
})
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
storageCache.clear()
}
})
```

View File

@@ -0,0 +1,32 @@
---
title: Combine Multiple Array Iterations
impact: LOW-MEDIUM
impactDescription: reduces iterations
tags: javascript, arrays, loops, performance
---
## Combine Multiple Array Iterations
Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.
**Incorrect (3 iterations):**
```typescript
const admins = users.filter(u => u.isAdmin)
const testers = users.filter(u => u.isTester)
const inactive = users.filter(u => !u.isActive)
```
**Correct (1 iteration):**
```typescript
const admins: User[] = []
const testers: User[] = []
const inactive: User[] = []
for (const user of users) {
if (user.isAdmin) admins.push(user)
if (user.isTester) testers.push(user)
if (!user.isActive) inactive.push(user)
}
```

View File

@@ -0,0 +1,50 @@
---
title: Early Return from Functions
impact: LOW-MEDIUM
impactDescription: avoids unnecessary computation
tags: javascript, functions, optimization, early-return
---
## Early Return from Functions
Return early when result is determined to skip unnecessary processing.
**Incorrect (processes all items even after finding answer):**
```typescript
function validateUsers(users: User[]) {
let hasError = false
let errorMessage = ''
for (const user of users) {
if (!user.email) {
hasError = true
errorMessage = 'Email required'
}
if (!user.name) {
hasError = true
errorMessage = 'Name required'
}
// Continues checking all users even after error found
}
return hasError ? { valid: false, error: errorMessage } : { valid: true }
}
```
**Correct (returns immediately on first error):**
```typescript
function validateUsers(users: User[]) {
for (const user of users) {
if (!user.email) {
return { valid: false, error: 'Email required' }
}
if (!user.name) {
return { valid: false, error: 'Name required' }
}
}
return { valid: true }
}
```

View File

@@ -0,0 +1,60 @@
---
title: Use flatMap to Map and Filter in One Pass
impact: LOW-MEDIUM
impactDescription: eliminates intermediate array
tags: javascript, arrays, flatMap, filter, performance
---
## Use flatMap to Map and Filter in One Pass
**Impact: LOW-MEDIUM (eliminates intermediate array)**
Chaining `.map().filter(Boolean)` creates an intermediate array and iterates twice. Use `.flatMap()` to transform and filter in a single pass.
**Incorrect (2 iterations, intermediate array):**
```typescript
const userNames = users
.map(user => user.isActive ? user.name : null)
.filter(Boolean)
```
**Correct (1 iteration, no intermediate array):**
```typescript
const userNames = users.flatMap(user =>
user.isActive ? [user.name] : []
)
```
**More examples:**
```typescript
// Extract valid emails from responses
// Before
const emails = responses
.map(r => r.success ? r.data.email : null)
.filter(Boolean)
// After
const emails = responses.flatMap(r =>
r.success ? [r.data.email] : []
)
// Parse and filter valid numbers
// Before
const numbers = strings
.map(s => parseInt(s, 10))
.filter(n => !isNaN(n))
// After
const numbers = strings.flatMap(s => {
const n = parseInt(s, 10)
return isNaN(n) ? [] : [n]
})
```
**When to use:**
- Transforming items while filtering some out
- Conditional mapping where some inputs produce no output
- Parsing/validating where invalid inputs should be skipped

View File

@@ -0,0 +1,45 @@
---
title: Hoist RegExp Creation
impact: LOW-MEDIUM
impactDescription: avoids recreation
tags: javascript, regexp, optimization, memoization
---
## Hoist RegExp Creation
Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.
**Incorrect (new RegExp every render):**
```tsx
function Highlighter({ text, query }: Props) {
const regex = new RegExp(`(${query})`, 'gi')
const parts = text.split(regex)
return <>{parts.map((part, i) => ...)}</>
}
```
**Correct (memoize or hoist):**
```tsx
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
function Highlighter({ text, query }: Props) {
const regex = useMemo(
() => new RegExp(`(${escapeRegex(query)})`, 'gi'),
[query]
)
const parts = text.split(regex)
return <>{parts.map((part, i) => ...)}</>
}
```
**Warning (global regex has mutable state):**
Global regex (`/g`) has mutable `lastIndex` state:
```typescript
const regex = /foo/g
regex.test('foo') // true, lastIndex = 3
regex.test('foo') // false, lastIndex = 0
```

View File

@@ -0,0 +1,37 @@
---
title: Build Index Maps for Repeated Lookups
impact: LOW-MEDIUM
impactDescription: 1M ops to 2K ops
tags: javascript, map, indexing, optimization, performance
---
## Build Index Maps for Repeated Lookups
Multiple `.find()` calls by the same key should use a Map.
**Incorrect (O(n) per lookup):**
```typescript
function processOrders(orders: Order[], users: User[]) {
return orders.map(order => ({
...order,
user: users.find(u => u.id === order.userId)
}))
}
```
**Correct (O(1) per lookup):**
```typescript
function processOrders(orders: Order[], users: User[]) {
const userById = new Map(users.map(u => [u.id, u]))
return orders.map(order => ({
...order,
user: userById.get(order.userId)
}))
}
```
Build map once (O(n)), then all lookups are O(1).
For 1000 orders × 1000 users: 1M ops → 2K ops.

View File

@@ -0,0 +1,49 @@
---
title: Early Length Check for Array Comparisons
impact: MEDIUM-HIGH
impactDescription: avoids expensive operations when lengths differ
tags: javascript, arrays, performance, optimization, comparison
---
## Early Length Check for Array Comparisons
When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.
In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).
**Incorrect (always runs expensive comparison):**
```typescript
function hasChanges(current: string[], original: string[]) {
// Always sorts and joins, even when lengths differ
return current.sort().join() !== original.sort().join()
}
```
Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings.
**Correct (O(1) length check first):**
```typescript
function hasChanges(current: string[], original: string[]) {
// Early return if lengths differ
if (current.length !== original.length) {
return true
}
// Only sort when lengths match
const currentSorted = current.toSorted()
const originalSorted = original.toSorted()
for (let i = 0; i < currentSorted.length; i++) {
if (currentSorted[i] !== originalSorted[i]) {
return true
}
}
return false
}
```
This new approach is more efficient because:
- It avoids the overhead of sorting and joining the arrays when lengths differ
- It avoids consuming memory for the joined strings (especially important for large arrays)
- It avoids mutating the original arrays
- It returns early when a difference is found

View File

@@ -0,0 +1,82 @@
---
title: Use Loop for Min/Max Instead of Sort
impact: LOW
impactDescription: O(n) instead of O(n log n)
tags: javascript, arrays, performance, sorting, algorithms
---
## Use Loop for Min/Max Instead of Sort
Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.
**Incorrect (O(n log n) - sort to find latest):**
```typescript
interface Project {
id: string
name: string
updatedAt: number
}
function getLatestProject(projects: Project[]) {
const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt)
return sorted[0]
}
```
Sorts the entire array just to find the maximum value.
**Incorrect (O(n log n) - sort for oldest and newest):**
```typescript
function getOldestAndNewest(projects: Project[]) {
const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt)
return { oldest: sorted[0], newest: sorted[sorted.length - 1] }
}
```
Still sorts unnecessarily when only min/max are needed.
**Correct (O(n) - single loop):**
```typescript
function getLatestProject(projects: Project[]) {
if (projects.length === 0) return null
let latest = projects[0]
for (let i = 1; i < projects.length; i++) {
if (projects[i].updatedAt > latest.updatedAt) {
latest = projects[i]
}
}
return latest
}
function getOldestAndNewest(projects: Project[]) {
if (projects.length === 0) return { oldest: null, newest: null }
let oldest = projects[0]
let newest = projects[0]
for (let i = 1; i < projects.length; i++) {
if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i]
if (projects[i].updatedAt > newest.updatedAt) newest = projects[i]
}
return { oldest, newest }
}
```
Single pass through the array, no copying, no sorting.
**Alternative (Math.min/Math.max for small arrays):**
```typescript
const numbers = [5, 2, 8, 1, 9]
const min = Math.min(...numbers)
const max = Math.max(...numbers)
```
This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability.

View File

@@ -0,0 +1,24 @@
---
title: Use Set/Map for O(1) Lookups
impact: LOW-MEDIUM
impactDescription: O(n) to O(1)
tags: javascript, set, map, data-structures, performance
---
## Use Set/Map for O(1) Lookups
Convert arrays to Set/Map for repeated membership checks.
**Incorrect (O(n) per check):**
```typescript
const allowedIds = ['a', 'b', 'c', ...]
items.filter(item => allowedIds.includes(item.id))
```
**Correct (O(1) per check):**
```typescript
const allowedIds = new Set(['a', 'b', 'c', ...])
items.filter(item => allowedIds.has(item.id))
```

View File

@@ -0,0 +1,57 @@
---
title: Use toSorted() Instead of sort() for Immutability
impact: MEDIUM-HIGH
impactDescription: prevents mutation bugs in React state
tags: javascript, arrays, immutability, react, state, mutation
---
## Use toSorted() Instead of sort() for Immutability
`.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.
**Incorrect (mutates original array):**
```typescript
function UserList({ users }: { users: User[] }) {
// Mutates the users prop array!
const sorted = useMemo(
() => users.sort((a, b) => a.name.localeCompare(b.name)),
[users]
)
return <div>{sorted.map(renderUser)}</div>
}
```
**Correct (creates new array):**
```typescript
function UserList({ users }: { users: User[] }) {
// Creates new sorted array, original unchanged
const sorted = useMemo(
() => users.toSorted((a, b) => a.name.localeCompare(b.name)),
[users]
)
return <div>{sorted.map(renderUser)}</div>
}
```
**Why this matters in React:**
1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only
2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior
**Browser support (fallback for older browsers):**
`.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator:
```typescript
// Fallback for older browsers
const sorted = [...items].sort((a, b) => a.value - b.value)
```
**Other immutable array methods:**
- `.toSorted()` - immutable sort
- `.toReversed()` - immutable reverse
- `.toSpliced()` - immutable splice
- `.with()` - immutable element replacement

View File

@@ -0,0 +1,26 @@
---
title: Use Activity Component for Show/Hide
impact: MEDIUM
impactDescription: preserves state/DOM
tags: rendering, activity, visibility, state-preservation
---
## Use Activity Component for Show/Hide
Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.
**Usage:**
```tsx
import { Activity } from 'react'
function Dropdown({ isOpen }: Props) {
return (
<Activity mode={isOpen ? 'visible' : 'hidden'}>
<ExpensiveMenu />
</Activity>
)
}
```
Avoids expensive re-renders and state loss.

View File

@@ -0,0 +1,47 @@
---
title: Animate SVG Wrapper Instead of SVG Element
impact: LOW
impactDescription: enables hardware acceleration
tags: rendering, svg, css, animation, performance
---
## Animate SVG Wrapper Instead of SVG Element
Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.
**Incorrect (animating SVG directly - no hardware acceleration):**
```tsx
function LoadingSpinner() {
return (
<svg
className="animate-spin"
width="24"
height="24"
viewBox="0 0 24 24"
>
<circle cx="12" cy="12" r="10" stroke="currentColor" />
</svg>
)
}
```
**Correct (animating wrapper div - hardware accelerated):**
```tsx
function LoadingSpinner() {
return (
<div className="animate-spin">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
>
<circle cx="12" cy="12" r="10" stroke="currentColor" />
</svg>
</div>
)
}
```
This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations.

View File

@@ -0,0 +1,40 @@
---
title: Use Explicit Conditional Rendering
impact: LOW
impactDescription: prevents rendering 0 or NaN
tags: rendering, conditional, jsx, falsy-values
---
## Use Explicit Conditional Rendering
Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.
**Incorrect (renders "0" when count is 0):**
```tsx
function Badge({ count }: { count: number }) {
return (
<div>
{count && <span className="badge">{count}</span>}
</div>
)
}
// When count = 0, renders: <div>0</div>
// When count = 5, renders: <div><span class="badge">5</span></div>
```
**Correct (renders nothing when count is 0):**
```tsx
function Badge({ count }: { count: number }) {
return (
<div>
{count > 0 ? <span className="badge">{count}</span> : null}
</div>
)
}
// When count = 0, renders: <div></div>
// When count = 5, renders: <div><span class="badge">5</span></div>
```

View File

@@ -0,0 +1,38 @@
---
title: CSS content-visibility for Long Lists
impact: HIGH
impactDescription: faster initial render
tags: rendering, css, content-visibility, long-lists
---
## CSS content-visibility for Long Lists
Apply `content-visibility: auto` to defer off-screen rendering.
**CSS:**
```css
.message-item {
content-visibility: auto;
contain-intrinsic-size: 0 80px;
}
```
**Example:**
```tsx
function MessageList({ messages }: { messages: Message[] }) {
return (
<div className="overflow-y-auto h-screen">
{messages.map(msg => (
<div key={msg.id} className="message-item">
<Avatar user={msg.author} />
<div>{msg.content}</div>
</div>
))}
</div>
)
}
```
For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render).

View File

@@ -0,0 +1,46 @@
---
title: Hoist Static JSX Elements
impact: LOW
impactDescription: avoids re-creation
tags: rendering, jsx, static, optimization
---
## Hoist Static JSX Elements
Extract static JSX outside components to avoid re-creation.
**Incorrect (recreates element every render):**
```tsx
function LoadingSkeleton() {
return <div className="animate-pulse h-20 bg-gray-200" />
}
function Container() {
return (
<div>
{loading && <LoadingSkeleton />}
</div>
)
}
```
**Correct (reuses same element):**
```tsx
const loadingSkeleton = (
<div className="animate-pulse h-20 bg-gray-200" />
)
function Container() {
return (
<div>
{loading && loadingSkeleton}
</div>
)
}
```
This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render.
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary.

View File

@@ -0,0 +1,82 @@
---
title: Prevent Hydration Mismatch Without Flickering
impact: MEDIUM
impactDescription: avoids visual flicker and hydration errors
tags: rendering, ssr, hydration, localStorage, flicker
---
## Prevent Hydration Mismatch Without Flickering
When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.
**Incorrect (breaks SSR):**
```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
// localStorage is not available on server - throws error
const theme = localStorage.getItem('theme') || 'light'
return (
<div className={theme}>
{children}
</div>
)
}
```
Server-side rendering will fail because `localStorage` is undefined.
**Incorrect (visual flickering):**
```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState('light')
useEffect(() => {
// Runs after hydration - causes visible flash
const stored = localStorage.getItem('theme')
if (stored) {
setTheme(stored)
}
}, [])
return (
<div className={theme}>
{children}
</div>
)
}
```
Component first renders with default value (`light`), then updates after hydration, causing a visible flash of incorrect content.
**Correct (no flicker, no hydration mismatch):**
```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
return (
<>
<div id="theme-wrapper">
{children}
</div>
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
try {
var theme = localStorage.getItem('theme') || 'light';
var el = document.getElementById('theme-wrapper');
if (el) el.className = theme;
} catch (e) {}
})();
`,
}}
/>
</>
)
}
```
The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch.
This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values.

Some files were not shown because too many files have changed in this diff Show More