Skip to content
Back to skills

Design System Component

ASecurity

Use this skill to create or refactor React + TypeScript + Tailwind CSS + CVA components following design system conventions. It provides the canonical file structure, per-component styles pattern, types, context, store, barrel exports, and Storybook story templates that are specific to this codebase. Invoke whenever the user wants to build a new UI component, add CVA variants or sub-components, set up component context or store, create barrel exports, write component stories, or restructure a...

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 13, 2026
developmenttypescriptreact

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 13, 2026

npx -y skills add JanSzewczyk/claude-plugins --skill design-system-component --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Design System Component?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Design System Component
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/janszewczyk-design-system-component/badge)](https://www.skillsdirectory.com/skills/janszewczyk-design-system-component)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: design-system-component
description: >
  Use this skill to create or refactor React + TypeScript + Tailwind CSS + CVA components
  following design system conventions. It provides the canonical file structure, per-component
  styles pattern, types, context, store, barrel exports, and Storybook story templates that
  are specific to this codebase. Invoke whenever the user wants to build a new UI component,
  add CVA variants or sub-components, set up component context or store, create barrel exports,
  write component stories, or restructure an existing component directory — even for tasks
  that seem straightforward like "create a Button" or "add a variant", because the skill
  ensures correct file placement and naming conventions.
allowed-tools: Read, Write, Edit, Glob, Grep
argument-hint: "[optional path to a component entry file to refactor]"
---

# Design System Component Guidelines

Stack: **React + TypeScript + Tailwind CSS + CVA**. Radix UI optional — use when available.
Check existing components first to confirm path aliases and `cn()` location.

This file holds the structure and rules. Copy-paste code for each file type is in the reference:

> - [references/file-templates.md](./references/file-templates.md) — templates for index/types/styles/
>   component/constants/utils/context/store/stories.

## Usage Modes

- **Create from scratch** — describe the component; follow the structure and rules below.
- **Refactor existing** (pass a path, e.g. `/design-system-component src/components/ui/data-table/index.tsx`):
  1. Read the entry file and its same-directory imports.
  2. Compare against the rules below; report what's missing/misaligned (styles not split per
     sub-component, missing `data-slot`, no barrel export, props using `HTMLAttributes` instead of
     `ComponentProps`).
  3. Propose the target structure, then split/create files to match.

## File Structure

File names: **kebab-case**. Exported symbols: **PascalCase**.

**Simple** (no sub-parts, no shared state):

```
button/
├── index.tsx
├── button.tsx
├── button.styles.ts           # only when variants exist
├── button.types.ts            # only when CVA-derived types or const enums exist
└── button.stories.tsx
```

**Composite** (sub-parts — each sub-component with variants gets its own styles file):

```
tabs/
├── index.tsx
├── tabs.tsx                    # root component + provider
├── tabs.styles.ts              # root variants (if any)
├── tabs.types.ts               # shared types, const enums, CVA-derived types
├── tabs.constants.ts
├── tabs.utils.ts
├── tabs.context.tsx
├── tabs-trigger.tsx            # sub-component
├── tabs-trigger.styles.ts      # sub-component's own variants
├── tabs-content.tsx
├── tabs-content.styles.ts
├── tabs.store.tsx              # useSyncExternalStore if needed
└── tabs.stories.tsx
```

**Key rule:** each component/sub-component that has CVA variants gets its **own** `.styles.ts` —
styles are never shared across sub-components (`tabs-trigger.styles.ts` defines only
`tabsTriggerVariants`). Only create files that are genuinely needed: a sub-component with no variants
(just fixed `cn()` classes) needs no styles file.

## When to Use Which Pattern

| Situation                                                                  | Pattern                              |
| -------------------------------------------------------------------------- | ------------------------------------ |
| Single element, no sub-parts                                               | Simple component                     |
| Multiple related parts (trigger + content, header + body)                  | Composite component                  |
| Parent needs to share state with children                                  | Context (`React.createContext`)      |
| Many siblings need independent subscriptions without cascading re-renders  | Store (`useSyncExternalStore`)       |
| Component wraps a Radix primitive                                          | Use Radix, layer CVA + `data-slot`   |
| No Radix primitive exists                                                  | Build from scratch with proper ARIA  |

## Rules by File Type

Code templates for each are in [references/file-templates.md](./references/file-templates.md).

**`index.tsx` (barrel)** — `export *` for component and type files; **named** re-exports for context
hooks (never `export *` from a context file); never export internals (store factory, private helpers).

**`*.types.ts`** — holds const enums and CVA-derived variant types **only**. Props types always live in
the component's own `.tsx` file (root and sub-components alike).

**`*.styles.ts`** — one file per component/sub-component that has variants; inline `cn()` classes
directly if the set is single and unconditional. Always use CSS custom-property tokens
(`bg-primary`, `text-muted-foreground`) — never raw hex/rgb/oklch. Use data-attribute variants for
state (`data-[state=active]:...`). Name the export `<componentName>Variants` (camelCase).

**`*.tsx` (component)** — `import * as React from "react"`; type HTML props with
`React.ComponentProps<"el">`, never `HTMLAttributes<...>`; always add `data-slot="component-name"`;
support `asChild` via `Slot` when the root could be swapped; spread `...props` last and merge
`className` through `cn()`; use `React.useId()` for ARIA-relationship IDs; expose observable state as
data attributes (`data-disabled`, `data-state`, `data-orientation`).

**`*.constants.ts`** — SCREAMING_SNAKE_CASE names (component display names, key maps).

**`*.utils.ts`** — pure functions, no React imports, no side effects; `.ts` not `.tsx`.

**`*.context.tsx`** — pass `consumerName` into the hook for actionable errors; stabilise the value with
`React.useMemo` in the provider; export the hook by name from `index.tsx`, never via `export *`.

**`*.store.tsx`** — use `useSyncExternalStore` when state must live outside render (e.g. a child
registration map) or many siblings need independent subscriptions. Create the store in the root
component so its identity never changes (`useMemo` + `useLazyRef`).

**`*.stories.tsx`** — check the project's CSF version (CSF 3 vs CSF Next) and match it. Title
`"Components/MyComponent"`; `tags: ["autodocs"]`; define `argTypes` for every variant/size prop; use
`satisfies Meta<typeof Component>` (never `as`); tag `"test"` for integration-test inclusion,
`"experimental"` for exclusion.

## Path Aliases

Use the `~/` alias (check `tsconfig.json`) for cross-directory imports (`import { cn } from "~/utils"`);
use relative `./` for files within the same component directory.

## Accessibility Checklist

- Use semantic HTML (`button`, `nav`, `dialog`, `ul/li`) as the default root.
- Add `role` only when a non-semantic element is used.
- Wire ARIA: `aria-controls`, `aria-labelledby`, `aria-describedby`, `aria-selected`, `aria-expanded`,
  `aria-current`, `aria-posinset`, `aria-setsize`.
- Disabled state: set both `disabled` on the element AND the `data-disabled` attribute.
- Keyboard navigation: arrow keys, Home/End, Tab/Shift+Tab for all interactive components.

Files in this skill

  • SKILL.md7.1 KB
  • references/file-templates.md5.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.