Skip to content
Back to skills

Component Composition

ASecurity

Use on every UI change - Atomic Design levels, reuse-before-build, correct placement, and import direction through the shared component library.

  • 109 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 19, 2026
ai-agentsgoreacttestingapifrontend

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 2, 2026

npx -y skills add makifbaysal/tasktrooper --skill component-composition --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Component Composition?

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

Security grade badge for Component Composition
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/makifbaysal-component-composition/badge)](https://www.skillsdirectory.com/skills/makifbaysal-component-composition)

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: component-composition
category: frontend
description: Use on every UI change - Atomic Design levels, reuse-before-build, correct placement, and import direction through the shared component library.
---
# Component Composition (Atomic Design)

## Overview

All web UI follows Atomic Design on top of a shared component library. The quality bar is reuse and correct placement: a new screen is assembled from existing atoms and molecules, and anything genuinely new is added at the right level so the next screen reuses it too.

**Core principle:** Reuse before you build, and place new components at the level the import-direction rule expects — `ui → atoms → molecules → organisms → templates → pages`.

No design system yet in this repo (no tokens, no `components/` levels, no `INVENTORY.md`)? Lay the foundation with `web-design-system-foundation` first — this skill assumes it already exists.

## The Levels

| Level | What | Web examples |
|-------|------|------|
| `ui/` | Vendor layer: generated/wrapped Radix-shadcn primitives | Dialog, Popover, Tabs, Tooltip |
| **Atom** | Smallest reusable UI, no app logic, variants only | Button, Input, Label, Badge, Icon, Heading, Text, Container, Stack, Link |
| **Molecule** | A few atoms as one reusable unit | FormField (label+input+error), NavLink, PriceTag, Rating, SearchBar |
| **Organism** | A page section composed of molecules/atoms (may contain another organism) | SiteHeader, MobileNav, PricingTable, ContactForm, SiteFooter |
| **Template** | Page skeleton: owns the grid + breakpoints, no data | MarketingLayout, DashboardLayout |
| **Page** | Route component: data + state wiring, composes templates/organisms | `PricingPage`, `ProjectBoardPage` |

## The reuse procedure (every time, before writing a component)

1. Read `src/components/INVENTORY.md`.
2. Grep the level folders for the noun (`grep -ri button src/components`).
3. Found a close match → reuse it, or extend it with a new variant **backward-compatibly** (add a `cva` variant, don't change an existing one's output).
4. Nothing close → create it at the right level (see placement below) and add its `INVENTORY.md` line in the same commit.

## Placement decision list

- Does it fetch data or own app state? → **page** (or a container that wraps a page-level hook).
- Is it a page skeleton with no content, owning only the grid/breakpoints? → **template**.
- Several molecules/atoms forming a page section (pricing grid, contact form, header)? → **organism**.
- Label + input + error, or icon + text pairing — two or three atoms as one reusable unit? → **molecule**.
- A single element with variants and no composition (one button, one badge)? → **atom**.
- A generated/wrapped Radix or shadcn primitive with no app-specific variants of its own? → **`ui/`**.

## Import direction

`ui ← atoms ← molecules ← organisms ← templates ← pages`. A level imports only from levels to its left, plus `lib/`. Same-level imports are allowed ONLY for organisms (an organism may contain another organism). `hooks/` and `api/` are imported only by `pages/`. Where the repo runs `ui-guard` (from `web-design-system-foundation`), it enforces this on every build — a violation fails `npm run build`, it isn't a style note.

## Atom API rules

- No outer margin or absolute positioning — the parent lays out with `gap-*`; an atom that sets its own margin breaks every layout that reuses it.
- Variants through `cva`, never a pile of boolean props; `className` is merged **last** via `cn()` and callers use it for layout only (width, grid placement, alignment) — never to restyle the atom; a new look is a new variant.
- Every interactive state is styled: hover, `focus-visible`, active, disabled, `aria-invalid`, loading where relevant.
- Accepts `ref` as a plain prop (React 19) or `forwardRef` (React 18) — a shared atom that can't take a ref breaks the first caller that needs to measure or focus it.

## Composition over prop explosions

Three or more `isX` booleans on one component is a sign it's actually two components, or that it needs `children`/named slots instead. Prefer:
```tsx
// ❌ boolean-prop explosion
<Card isHighlighted isCompact hasFooter footerText="Save" />

// ✅ composition: the variant is a real prop, content is children/slots
<Card variant="highlighted" size="compact">
  <Card.Footer>Save</Card.Footer>
</Card>
```
An explicit variant component (`PrimaryCard` vs `<Card variant="primary">`) is fine when the two really don't share markup — don't force one component to branch internally just to avoid two small ones.

## Templates and organisms are container-aware

A template owns the grid and the breakpoints (`sm:`/`md:`/`lg:` live here) — organisms placed inside it should not assume a specific viewport width. Where an organism's internal layout depends on the space it's given rather than the screen, use a `@container` query or let the parent grid/flex sizing drive it, so the same organism works in a full-width template and a narrower sidebar slot.

## Existing repos

Map the repo's own folders onto these levels (e.g. `components/common/` ≈ atoms+molecules) and follow them as found. Never restructure an existing repo's component layout inside a feature task — if the mapping is genuinely broken, propose a follow-up task instead of doing it inline.

## Changing a shared component

Grep every caller first (`grep -r "from '@/components/atoms/Button'" src`). Keep the change backward-compatible (new optional prop, new variant) or update every caller in the same task — never leave some callers on an old, incompatible shape. Update `INVENTORY.md` if the component's variants/props changed. `npm run build` must stay green.

## Worked Example

Task: "add a pricing section to the marketing site."

1. Read `INVENTORY.md`, grep `components/atoms` → `Button`, `Badge`, `Heading` already exist. Reuse them.
2. Nothing in `molecules/` prices a plan → new **molecule** `PriceTag` (amount + interval) and **molecule** `PlanCard` (`Heading` + `PriceTag` + `Text` + `Button`, props in, `onSelect` callback out).
3. Nothing in `organisms/` lists several plans → new **organism** `PricingTable` (grid of `PlanCard`, `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3`, container-aware, no data fetching).
4. `PricingPage` (page) loads the plan list, wires `onSelectPlan`, and composes `MarketingLayout` (template, already exists) around `PricingTable`.
5. Add three `INVENTORY.md` lines (`PriceTag`, `PlanCard`, `PricingTable`) in the same commit, each with a co-located RTL test (see `component-testing`).

Only `PriceTag`, `PlanCard`, and `PricingTable` are new; `Button`, `Badge`, `Heading`, and `MarketingLayout` are pure reuse.

## Common Mistakes

- Rebuilding an existing atom/molecule inline instead of importing it (always grep first).
- A component that fetches data or reads a global store below the page level — split presentation from data.
- An atom with its own `margin-*` or `position: absolute`.
- Boolean-prop explosions (`isX`, `isY`, `isZ`) instead of a variant prop or composition.
- An organism that hardcodes `md:`/`lg:` assuming it's always full-width — breaks the day it's reused in a sidebar.
- Changing a shared component's props without grepping every caller.
- A component file long enough that you scroll to read it (~150 lines is the point to split).

## Red Flags

- Two screens render visually identical controls built two different ways.
- A hex color or raw Tailwind palette class (`bg-blue-500`) inside a component — `ui-guard`, where it runs, fails the build on this.
- A molecule or organism importing a hook from `hooks/` or a client from `api/`.
- A new component added with no `INVENTORY.md` line.
- A file over ~150 lines that's still growing.

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…