ted-craft
rules

React shadcn Components

shadcn/ui hierarchy: reuse shared primitives, extend via composition, and build feature components only when primitives cannot cover the UI.

ruledev-ted
ted-craft — zsh
$ npx ted-craft add react-shadcn-components -a cursor -g -y
---
description: UI component hierarchy — shadcn/Radix usage, extension, and custom components
globs:
  - "**/*.{tsx,jsx}"
alwaysApply: true
---

# UI Components — shadcn/ui & Custom Components

**Assumes:** shadcn/ui (Radix + Tailwind) with a configured components path (often `src/components/ui` or `src/components/common`). Works with `react-ui-decisions`, `react-accessibility`, and `react-styling`.

## Component Hierarchy

Follow this order. Do not skip steps.

```
1. Does shadcn/Radix (or an existing shared primitive) cover this?
   ├── Yes, fully     → Import from the project’s shared UI path. Add via CLI if missing.
   ├── Partially      → Extend inside the existing shared primitive, or compose primitives in a feature component.
   └── No             → Build a custom component under a feature/domain folder using primitives + tokens + Figma/spec.
```

Do **not** hand-roll a duplicate dialog/button/select if the shared UI folder already exposes one. Do **not** import **`@radix-ui/react-*`** directly from pages—keep Radix as an implementation detail inside the shared primitives folder (or add the primitive there first).

---

## Approved UI Stack

- **shadcn/ui** (Radix primitives, Tailwind, `components.json`) is the default UI layer. Styling stays **Tailwind + theme tokens**.
- Optional org/registry components are allowed **only** when the project already uses them or the user explicitly instructs it.
- **No new** general-purpose UI libraries (Chakra, Mantine, Material UI, additional kits) without explicit approval.

---

## Shared Primitives Folder

shadcn-generated files under the configured UI path are the shared wrappers: they centralise variants, `cn()`, focus rings, and Radix wiring.

**Pages and feature modules** should import from that shared path, not from raw Radix packages.

| Pattern | Import from (examples) |
|--------|-------------------------|
| Button | `@/components/ui/button` (or `@/components/common/button`) |
| Dialog / alert | `dialog`, `alert-dialog` |
| Dropdown / menu | `dropdown-menu` |
| Tabs | `tabs` |
| Inputs | `input`, `textarea`, `select`, `checkbox`, `switch` |
| Card / sheet / drawer | `card`, `sheet`, `drawer` |

If a wrapper file already exists, **reuse it** — do not add a second parallel primitive for the same Radix pattern.

**Why:** one place to update behaviour, tokens, and a11y; Radix stays an implementation detail.

---

## Extending shadcn / Radix Components

- Prefer **composition** in **feature components** using shared exports (`asChild`, slots, extra `className`, children).
- If the **primitive itself** must change (new variant, default size, shared behaviour): extend the **single** file under the shared UI folder (CVA variants, props). Avoid copying the whole component into a feature folder.
- Do **not** patch **`node_modules`** or fork Radix internals. If the official API cannot do it, build a **small custom** piece in the right feature folder using semantic HTML + tokens, or ask for design/product input.
- Non-obvious deviations from default shadcn behaviour: short comment at the top of the shared file or next to the variant.

---

## Building Custom Components

Build **custom** UI when no suitable shared primitive composition exists **and** you have **Figma/spec** (see `react-ui-decisions`).

Custom components must:

- Live under a **feature / domain** folder (e.g. `src/components/<domain>/` or `src/features/<domain>/components/`) — match neighbouring naming.
- Use **Tailwind + theme tokens** only; no ad hoc global CSS (see `react-styling`).
- Take **props** for labels, data, and callbacks — avoid hardcoded copy unless truly static.
- Meet `react-accessibility` (labels, focus, keyboard, contrast).

---

## Before Creating a New Component

- [ ] Searched the shared UI folder and **`npx shadcn@latest add`** list — nothing fits.
- [ ] Searched feature/components folders — no existing component to extend.
- [ ] **Figma/spec** exists for genuinely new UI (or user explicitly waived per `react-ui-decisions`).
- [ ] Not a **minor variant** of an existing component (then add variant/props instead).

If anything is unclear, **stop and ask**.

---

## Location Reference

| Kind | Location |
|------|----------|
| shadcn/Radix primitives & shared UI atoms | Configured UI path (`components/ui` or `components/common`) |
| Layout chrome (header, sidebar, footer) | Layout / shell feature folders |
| Feature / domain UI | Feature or domain component folders |
| Route-only glue | App Router `page.tsx` / `layout.tsx` — keep thin; lift reusable UI into components |

UI Components — shadcn/ui & Custom Components

Assumes: shadcn/ui (Radix + Tailwind) with a configured components path (often src/components/ui or src/components/common). Works with react-ui-decisions, react-accessibility, and react-styling.

Component Hierarchy

Follow this order. Do not skip steps.

1. Does shadcn/Radix (or an existing shared primitive) cover this?
   ├── Yes, fully     → Import from the project’s shared UI path. Add via CLI if missing.
   ├── Partially      → Extend inside the existing shared primitive, or compose primitives in a feature component.
   └── No             → Build a custom component under a feature/domain folder using primitives + tokens + Figma/spec.

Do not hand-roll a duplicate dialog/button/select if the shared UI folder already exposes one. Do not import @radix-ui/react-* directly from pages—keep Radix as an implementation detail inside the shared primitives folder (or add the primitive there first).


Approved UI Stack

  • shadcn/ui (Radix primitives, Tailwind, components.json) is the default UI layer. Styling stays Tailwind + theme tokens.
  • Optional org/registry components are allowed only when the project already uses them or the user explicitly instructs it.
  • No new general-purpose UI libraries (Chakra, Mantine, Material UI, additional kits) without explicit approval.

Shared Primitives Folder

shadcn-generated files under the configured UI path are the shared wrappers: they centralise variants, cn(), focus rings, and Radix wiring.

Pages and feature modules should import from that shared path, not from raw Radix packages.

PatternImport from (examples)
Button@/components/ui/button (or @/components/common/button)
Dialog / alertdialog, alert-dialog
Dropdown / menudropdown-menu
Tabstabs
Inputsinput, textarea, select, checkbox, switch
Card / sheet / drawercard, sheet, drawer

If a wrapper file already exists, reuse it — do not add a second parallel primitive for the same Radix pattern.

Why: one place to update behaviour, tokens, and a11y; Radix stays an implementation detail.


Extending shadcn / Radix Components

  • Prefer composition in feature components using shared exports (asChild, slots, extra className, children).
  • If the primitive itself must change (new variant, default size, shared behaviour): extend the single file under the shared UI folder (CVA variants, props). Avoid copying the whole component into a feature folder.
  • Do not patch node_modules or fork Radix internals. If the official API cannot do it, build a small custom piece in the right feature folder using semantic HTML + tokens, or ask for design/product input.
  • Non-obvious deviations from default shadcn behaviour: short comment at the top of the shared file or next to the variant.

Building Custom Components

Build custom UI when no suitable shared primitive composition exists and you have Figma/spec (see react-ui-decisions).

Custom components must:

  • Live under a feature / domain folder (e.g. src/components/&lt;domain&gt;/ or src/features/&lt;domain&gt;/components/) — match neighbouring naming.
  • Use Tailwind + theme tokens only; no ad hoc global CSS (see react-styling).
  • Take props for labels, data, and callbacks — avoid hardcoded copy unless truly static.
  • Meet react-accessibility (labels, focus, keyboard, contrast).

Before Creating a New Component

  • Searched the shared UI folder and npx shadcn@latest add list — nothing fits.
  • Searched feature/components folders — no existing component to extend.
  • Figma/spec exists for genuinely new UI (or user explicitly waived per react-ui-decisions).
  • Not a minor variant of an existing component (then add variant/props instead).

If anything is unclear, stop and ask.


Location Reference

KindLocation
shadcn/Radix primitives & shared UI atomsConfigured UI path (components/ui or components/common)
Layout chrome (header, sidebar, footer)Layout / shell feature folders
Feature / domain UIFeature or domain component folders
Route-only glueApp Router page.tsx / layout.tsx — keep thin; lift reusable UI into components

On this page