add-card

Create a new card part within a section with factory/class pattern, Card wrapper, and proper cleanup

181 stars

Best use case

add-card is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Create a new card part within a section with factory/class pattern, Card wrapper, and proper cleanup

Teams using add-card should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/add-card/SKILL.md --create-dirs "https://raw.githubusercontent.com/majiayu000/claude-skill-registry/main/skills/data/add-card/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/add-card/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How add-card Compares

Feature / Agentadd-cardStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Create a new card part within a section with factory/class pattern, Card wrapper, and proper cleanup

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Add Card Skill

## Usage
```
/add-card <SectionName>/<CardName>
```

Example: `/add-card Pets/InventoryCard` or `/add-card Alerts/WeatherCard`

---

## What is a Card?

A **Card** is a self-contained UI part within a Section. It:
- Wraps content in the `Card` component (expandable/collapsible container)
- Has a specific purpose (logs viewer, team manager, item list, etc.)
- Lives in `src/ui/sections/<SectionName>/parts/<cardName>/`
- Can be either **class-based** (complex state) or **factory-based** (simpler)

### Cards vs Components
| Aspect | Card (Part) | Component |
|--------|-------------|-----------|
| Location | `sections/<Section>/parts/` | `components/` |
| Scope | Section-specific | Reusable across sections |
| State | Often section-specific state | Props-driven, minimal internal state |
| Complexity | Higher (orchestrates components) | Lower (single purpose) |

---

## Step 1: Ask Questions

### 1. Parent Section
```
Which section will contain this card?
→ Must exist in src/ui/sections/
```

### 2. Card Purpose
```
Brief description (1 sentence):
What does this card display/manage?
```

### 3. Pattern Type
```
A) Factory pattern (recommended for simpler cards)
   - createXxxCard(options): XxxCardHandle
   - Better for cards with minimal internal state
   - Example: ShopsCard, PublicCard

B) Class pattern (for complex cards)
   - class XxxCardPart { build(), destroy(), render() }
   - Better for cards with complex state, multiple modes, drag handlers
   - Example: TeamCard, TrackerCard, AbilityLogsCard
```

### 4. Card Features
```
[ ] Expandable (collapsible header with chevron)
[ ] Filterable (search bar, selects)
[ ] Scrollable list (virtual scrolling if >50 items)
[ ] Modes toggle (SegmentedControl for overview/manage/etc.)
[ ] Refresh capability (manual refresh button)
```

### 5. Data Sources
```
A) Globals (reactive) → subscribe + cleanup
B) MGData (static) → MGData.get('plants')
C) Feature state → feature.getState()
D) API fetch → async loading
E) Section state → state.ts
F) Combination of above
```

### 6. Child Components
```
Check existing components before creating new:

Layout:
[ ] Card (always used - the wrapper)

Inputs/Controls:
[ ] SearchBar - text filtering
[ ] Select - dropdown filter
[ ] SegmentedControl - mode toggle
[ ] Checkbox - multi-select
[ ] Button - actions

Display:
[ ] Table - tabular data with sorting
[ ] Badge - status indicators
[ ] ProgressBar - progress display
[ ] TeamListItem - team rows

Utility:
[ ] Modal - popups
[ ] SoundPicker - audio selection
```

### 7. Sprites
```
Does it display game sprites? → MGSprite.toCanvas()
```

---

## Step 2: Create Structure

```
src/ui/sections/<SectionName>/parts/<cardName>/
├── <CardName>.ts           # Main card logic (class or factory)
├── <cardName>.css.ts       # Scoped styles (optional)
├── index.ts                # Barrel exports
└── <cardName>Data.ts       # Data processing helpers (optional, for complex data)
└── <cardName>Table.ts      # Table configuration (optional, if using Table)
```

**Read existing cards for templates:**
- Factory pattern: `src/ui/sections/Alerts/parts/shop/shopsCard.ts`
- Factory pattern: `src/ui/sections/Room/parts/public.ts`
- Class pattern: `src/ui/sections/Pets/parts/ability/AbilityLogsCard.ts`
- Class pattern: `src/ui/sections/Pets/parts/team/TeamCard.ts`

---

## Step 3: File Templates

### Factory Pattern (Recommended for simpler cards)

#### `<CardName>.ts`
```typescript
/**
 * <CardName> Card Part
 * <Brief description>
 */

import { Card } from "../../../../components/Card/Card";
import { element } from "../../../../styles/helpers";
// Import needed components
// import { SearchBar } from "../../../../components/SearchBar/SearchBar";
// import { Select } from "../../../../components/Select/Select";
// Import data sources
// import { getMyInventory } from "../../../../../globals/variables/myInventory";
// import { MGData } from "../../../../../modules";

/* ─────────────────────────── Types ─────────────────────────── */

export interface <CardName>Options {
    defaultExpanded?: boolean;
    onExpandChange?: (expanded: boolean) => void;
    // Add card-specific options
}

export interface <CardName>Handle {
    root: HTMLElement;
    refresh?(): void;
    destroy(): void;
}

/* ─────────────────────────── Factory ─────────────────────────── */

export function create<CardName>(options: <CardName>Options = {}): <CardName>Handle {
    const { defaultExpanded = true, onExpandChange } = options;

    // Internal state
    let root: HTMLElement | null = null;
    const cleanups: (() => void)[] = [];

    // Component references (for cleanup)
    // let searchHandle: SearchBarHandle | null = null;

    /**
     * Build the card UI
     */
    function buildCard(): HTMLElement {
        const content = element("div", {
            style: "display: flex; flex-direction: column; gap: 12px;",
        }) as HTMLDivElement;

        // Add filters, lists, content...
        // const searchBar = SearchBar({ ... });
        // content.appendChild(searchBar.root);
        // cleanups.push(() => searchBar.destroy?.());

        root = Card(
            {
                title: "<Card Title>",
                subtitle: "<Card subtitle description>",
                expandable: true,
                defaultExpanded,
                padding: "md",
                onExpandChange,
            },
            content
        );

        return root;
    }

    /**
     * Refresh data (optional)
     */
    function refresh(): void {
        // Reload data, update UI
    }

    /**
     * Cleanup all resources
     */
    function destroy(): void {
        cleanups.forEach(fn => fn());
        cleanups.length = 0;
        root = null;
    }

    return {
        root: buildCard(),
        refresh,
        destroy,
    };
}
```

### Class Pattern (For complex cards with state)

#### `<CardName>.ts`
```typescript
/**
 * <CardName> Card Part
 * <Brief description>
 *
 * Per .claude/rules/ui/sections.md
 */

import { Card } from "../../../../components/Card/Card";
import { element } from "../../../../styles/helpers";
// Import needed components
// import { SegmentedControl, SegmentedControlHandle } from "../../../../components/SegmentedControl/SegmentedControl";
// Import data sources
// import { Globals } from "../../../../../globals";
// import { MGData } from "../../../../../modules";

/* ─────────────────────────── Types ─────────────────────────── */

export interface <CardName>PartOptions {
    // Card-specific options
    onSomeEvent?: () => void;
}

/* ─────────────────────────── Class ─────────────────────────── */

export class <CardName>Part {
    private card: HTMLDivElement | null = null;
    private content: HTMLDivElement | null = null;
    private options: <CardName>PartOptions;
    private cleanups: (() => void)[] = [];

    // Component references
    // private modeControl: SegmentedControlHandle | null = null;

    // Internal state
    // private mode: "overview" | "manage" = "overview";

    constructor(options: <CardName>PartOptions = {}) {
        this.options = options;
    }

    /* ───────────────────── Public API ───────────────────── */

    build(): HTMLDivElement {
        if (this.card) return this.card;
        return this.createCard();
    }

    destroy(): void {
        this.cleanups.forEach(fn => fn());
        this.cleanups.length = 0;

        // Destroy child components
        // this.modeControl?.destroy();
        // this.modeControl = null;

        this.card = null;
        this.content = null;
    }

    render(): void {
        if (!this.card) return;
        this.renderContent();
    }

    /* ───────────────────── Card Setup ───────────────────── */

    private createCard(): HTMLDivElement {
        const wrapper = element("div", {
            className: "<card-name>-wrapper",
        });

        this.content = element("div", {
            className: "<card-name>__content",
        });
        wrapper.appendChild(this.content);

        this.card = Card(
            {
                title: "<Card Title>",
                subtitle: "<Card subtitle description>",
                expandable: true,
                defaultExpanded: true,
            },
            wrapper
        );

        return this.card;
    }

    /* ───────────────────── Rendering ───────────────────── */

    private renderContent(): void {
        if (!this.content) return;

        this.content.replaceChildren();

        // Build UI based on state...
    }

    /* ───────────────────── Event Handlers ───────────────────── */

    // private handleSomeAction(): void { ... }
}
```

### `index.ts` (Barrel exports)
```typescript
/**
 * <CardName> Card Parts - Barrel exports
 */

// Factory pattern:
export { create<CardName> } from "./<CardName>";
export type { <CardName>Options, <CardName>Handle } from "./<CardName>";

// OR Class pattern:
export { <CardName>Part } from "./<CardName>";
export type { <CardName>PartOptions } from "./<CardName>";

// Optional CSS export
export { <cardName>CardCss } from "./<cardName>.css";
```

### `<cardName>.css.ts` (Optional)
```typescript
/**
 * <CardName> Card styles
 */

export const <cardName>CardCss = /* css */`
/* Scoped to card */
.<card-name>-wrapper {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.<card-name>__content {
    /* Content styles */
}

.<card-name>__list {
    max-height: 400px;
    overflow-y: auto;
}

.<card-name>__empty {
    padding: 24px;
    text-align: center;
    color: color-mix(in oklab, var(--fg) 60%, #9ca3af);
    font-size: 14px;
}
`;
```

---

## Step 4: Register in Section

### Update `parts/index.ts`
```typescript
// <CardName> parts
export { create<CardName> } from "./<cardName>/<CardName>";
export type { <CardName>Options, <CardName>Handle } from "./<cardName>/<CardName>";

// OR for class pattern:
export { <CardName>Part } from "./<cardName>/<CardName>";
export type { <CardName>PartOptions } from "./<cardName>/<CardName>";
```

### Use in `section.ts`
```typescript
import { create<CardName> } from "./parts";
// OR
import { <CardName>Part } from "./parts";

// In build():
// Factory pattern:
const card = create<CardName>({
    defaultExpanded: true,
    onExpandChange: (expanded) => { ... },
});
container.appendChild(card.root);
cleanups.push(() => card.destroy());

// OR Class pattern:
const cardPart = new <CardName>Part({ ... });
container.appendChild(cardPart.build());
cardPart.render();
cleanups.push(() => cardPart.destroy());
```

---

## Step 5: Validate

### Structure
- [ ] Card file named `<CardName>.ts` (PascalCase)
- [ ] CSS file named `<cardName>.css.ts` (camelCase)
- [ ] `index.ts` exports card and types
- [ ] Card lives in `parts/<cardName>/` folder

### API
- [ ] Factory returns `{ root, destroy, refresh? }` OR class has `build()`, `destroy()`, `render()`
- [ ] Options interface defined for configuration
- [ ] Handle/Options types exported

### Card Wrapper
- [ ] Uses `Card` component from `components/Card/Card`
- [ ] Has title and subtitle
- [ ] Has `expandable: true` if collapsible
- [ ] Has `defaultExpanded` option

### Cleanup
- [ ] All subscriptions unsubscribed in `destroy()`
- [ ] All child components' `destroy()` called
- [ ] All event listeners removed
- [ ] `cleanups[]` array used for tracking

### Styling
- [ ] Uses CSS variables (no hardcoded colors)
- [ ] Classes scoped with card name prefix
- [ ] Touch-friendly (min 44px targets)
- [ ] Responsive (flexible widths)

### Data (if applicable)
- [ ] Globals subscribed with cleanup
- [ ] MGData for static game data
- [ ] Section state for persisted preferences
- [ ] Loading states handled

### Components (if applicable)
- [ ] Reuses existing components from `src/ui/components/`
- [ ] Child components tracked in `cleanups[]`

---

## Existing Cards Reference

### Factory Pattern (simpler)
- `Alerts/parts/shop/shopsCard.ts` - Table with filters
- `Alerts/parts/weather/weatherCard.ts` - Weather alerts
- `Room/parts/public.ts` - Rooms list with API fetch

### Class Pattern (complex)
- `Pets/parts/ability/AbilityLogsCard.ts` - Virtual scrolling list
- `Pets/parts/team/TeamCard.ts` - CRUD with drag-drop
- `Pets/parts/teamDetails/TeamDetailsCard.ts` - Expansion panels
- `Trackers/parts/TrackerCard.ts` - Team list with expansion

---

## Common Patterns

### Filters Row
```typescript
const filters = element("div", {
    className: "<card>-filters",
    style: "display: flex; gap: 8px; margin-bottom: 12px;",
});

const select = Select({
    options: [...],
    onChange: (value) => applyFilters(),
});

const search = SearchBar({
    placeholder: "Search...",
    onSearch: (value) => applyFilters(),
});

filters.append(select.root, search.root);
```

### Scrollable List
```typescript
const list = element("div", {
    className: "<card>__list",
    style: "max-height: 400px; overflow-y: auto;",
});
```

### Empty State
```typescript
if (items.length === 0) {
    const empty = element("div", {
        className: "<card>__empty",
        style: "padding: 24px; text-align: center; color: color-mix(in oklab, var(--fg) 60%, #9ca3af);",
    }, "No items yet");
    content.appendChild(empty);
    return;
}
```

### Mode Toggle
```typescript
const modeControl = SegmentedControl({
    segments: [
        { id: "simple", label: "Simple" },
        { id: "detailed", label: "Detailed" },
    ],
    selected: "simple",
    onChange: (id) => {
        mode = id;
        renderContent();
    },
});
```

### Subscribe to Globals
```typescript
const unsub = getMyInventory().subscribe((inventory) => {
    items = inventory.items;
    renderList();
});
cleanups.push(unsub);
```

---

## References

- Rules: `.claude/rules/ui/sections.md`
- Card component: `src/ui/components/Card/Card.ts`
- Existing cards: `src/ui/sections/*/parts/*/`
- UI Components: `src/ui/components/`
- Globals: `src/globals/variables/`
- Section workflow: `.claude/workflows/ui/section/add-section.md`

Related Skills

ux

159
from majiayu000/claude-skill-registry

This AI agent skill provides comprehensive guidance for creating professional and insightful User Experience (UX) designs, covering user research, information architecture, interaction design, visual guidance, and usability evaluation. It aims to produce actionable, user-centered solutions that avoid generic AI aesthetics.

UX Design & StrategyClaude

thor-skills

159
from majiayu000/claude-skill-registry

An entry point and router for AI agents to manage various THOR-related cybersecurity tasks, including running scans, analyzing logs, troubleshooting, and maintenance.

SecurityClaude

chrome-debug

159
from majiayu000/claude-skill-registry

This skill empowers AI agents to debug web applications and inspect browser behavior using the Chrome DevTools Protocol (CDP), offering both collaborative (headful) and automated (headless) modes.

Coding & DevelopmentClaude

grail-miner

159
from majiayu000/claude-skill-registry

This skill assists in setting up, managing, and optimizing Grail miners on Bittensor Subnet 81, handling tasks like environment configuration, R2 storage, model checkpoint management, and performance tuning.

DevOps & Infrastructure

astro

159
from majiayu000/claude-skill-registry

This skill provides essential Astro framework patterns, focusing on server-side rendering (SSR), static site generation (SSG), middleware, and TypeScript best practices. It helps AI agents implement secure authentication, manage API routes, and debug rendering behaviors within Astro projects.

Coding & Development

lets-go-rss

159
from majiayu000/claude-skill-registry

A lightweight, full-platform RSS subscription manager that aggregates content from YouTube, Vimeo, Behance, Twitter/X, and Chinese platforms like Bilibili, Weibo, and Douyin, featuring deduplication and AI smart classification.

Content & Documentation

whisper-transcribe

159
from majiayu000/claude-skill-registry

Transcribes audio and video files to text using OpenAI's Whisper CLI, enhanced with contextual grounding from local markdown files for improved accuracy.

Media Processing

ontopo

159
from majiayu000/claude-skill-registry

An AI agent skill to search for Israeli restaurants, check table availability, view menus, and retrieve booking links via the Ontopo platform, acting as an unofficial interface to its data.

General Utilities

modal-deployment

159
from majiayu000/claude-skill-registry

Run Python code in the cloud with serverless containers, GPUs, and autoscaling using Modal. This skill enables agents to generate code for deploying ML models, running batch jobs, serving APIs, and scaling compute-intensive workloads.

DevOps & Infrastructure

tech-blog

159
from majiayu000/claude-skill-registry

Generates comprehensive technical blog posts, offering detailed explanations of system internals, architecture, and implementation, either through source code analysis or document-driven research.

Content & DocumentationClaude

vly-money

159
from majiayu000/claude-skill-registry

Generate crypto payment links for supported tokens and networks, manage access to X402 payment-protected content, and provide direct access to the vly.money wallet interface.

Fintech & CryptoClaude

advanced-skill-creator

181
from majiayu000/claude-skill-registry

Meta-skill that generates domain-specific skills using advanced reasoning techniques. PROACTIVELY activate for: (1) Create/build/make skills, (2) Generate expert panels for any domain, (3) Design evaluation frameworks, (4) Create research workflows, (5) Structure complex multi-step processes, (6) Instantiate templates with parameters. Triggers: "create a skill for", "build evaluation for", "design workflow for", "generate expert panel for", "how should I approach [complex task]", "create skill", "new skill for", "skill template", "generate skill"