agentleFS
thoughtfulllcthoughtfulllc/gridland/.claude/skills/create-component/SKILL.md

create-component

Skill363 starsChanged 7 months agoMITView on GitHub

Guide for creating a new UI component in @gridland/ui. Covers file structure, focus integration, keyboard handling, theme usage, JSDoc, export registration, and documentation.

12 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

10 rules. 0 are common, 2 are distinctive, and 8 are very specific to this project.

  • 0

    Common

    Most of the 11 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use kebab-case for directory/file names

    line 15
  2. Distinctive:

    Use PascalCase for component function and props interface

    line 16
  3. Very specific:

    Import theme from "@/registry/gridland/lib/theme", use textStyle() for styling

    line 39
  4. Very specific:

    Use the @/registry/gridland/{ui,lib,hooks}/* alias form for every intra-package import.

    line 40
  5. Very specific:

    Never hardcode hex colors — use theme.* tokens or accept color prop

    line 41
  6. Very specific:

    Create the core demo in packages/demo/demos/<component-name>.tsx — export a named app component (e.g., export function MyComponentApp())

    line 96
  7. Very specific:

    Create a doc page at packages/docs/content/docs/components/<component-name>.mdx

    line 98
  8. Very specific:

    If the MDX page needs multiple demo variants, create a thin wrapper at packages/docs/components/demos/<component-name>-demo.tsx that …

    line 99
  9. Very specific:

    Run /review to validate focus coverage and export conventions

    line 105
  10. Very specific:

    Run /sync-context to update packages/ui/CLAUDE.md with the new component

    line 106

“Common” is measured: kept by at least 6 in 10 of the 11 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Guide for creating a new UI component in @gridland/ui.

1. File structure

packages/ui/components/<component-name>/
  <component-name>.tsx       # Main component file
  • Use kebab-case for directory/file names
  • Use PascalCase for component function and props interface

2. Component template

// @ts-nocheck — only if using OpenTUI intrinsic elements (<box>, <text>, <span>)
import type { ReactNode } from "react"
import { textStyle } from "@/registry/gridland/lib/text-style"
import { useTheme } from "@/registry/gridland/lib/theme"

export interface MyComponentProps {
  /** JSDoc every prop — agents and docs generators read these. */
  children: ReactNode
}

/** One-line description of what the component does. */
export function MyComponent({ children }: MyComponentProps) {
  const theme = useTheme()
  // ...
}
  • // @ts-nocheck at top ONLY when using <box>, <text>, <span>
  • Import theme from "@/registry/gridland/lib/theme", use textStyle() for styling
  • Use the @/registry/gridland/{ui,lib,hooks}/* alias form for every intra-package import; shadcn's CLI rewrites these to the user's components.json aliases at install time
  • Never hardcode hex colors — use theme.* tokens or accept color prop
  • JSDoc on props interface, every prop, and component function

3. Focus integration (interactive components only)

import { useInteractive, FocusScope } from "@gridland/utils"

const { isFocused, isSelected, focusId, focusRef, onKey } = useInteractive({
  id,
  disabled,
  shortcuts: [{ key: "enter", label: "select" }],
})

// Attach focusRef to root element
<box ref={focusRef}>...</box>

// Wrap nested interactive content
<FocusScope trap selectable autoFocus>{children}</FocusScope>

// Handle keys while selected (fires only when isSelected)
onKey((e) => {
  if (e.name === "return") submit()
})
  • disabled alone excludes from navigation — do NOT also set tabIndex: -1
  • useInteractive inside FocusScope auto-binds to that scope (pass scopeId={null} for global scope)
  • Display-only wrappers that share a focusId with an inner interactive child should call useInteractive({ id }) without shortcuts and without onKey — the internal dispatch is a no-op so the inner component's shortcuts survive

4. Keyboard handling

import { useKeyboardContext } from "../provider/provider"

export interface MyComponentProps {
  useKeyboard?: (handler: (event: any) => void) => void
}

const useKeyboard = useKeyboardContext(useKeyboardProp)
useKeyboard?.((event) => { /* handle keys */ })

5. Export registration

In packages/ui/components/index.ts:

export { MyComponent } from "./<component-name>/<component-name>"
export type { MyComponentProps } from "./<component-name>/<component-name>"

Both runtime export AND type export required.

6. Documentation

  1. Create the core demo in packages/demo/demos/<component-name>.tsx — export a named app component (e.g., export function MyComponentApp())
  2. Register it in packages/demo/demos/index.tsx (import, re-export, and add to demos array)
  3. Create a doc page at packages/docs/content/docs/components/<component-name>.mdx
  4. If the MDX page needs multiple demo variants, create a thin wrapper at packages/docs/components/demos/<component-name>-demo.tsx that imports from @demos/<component-name>

Interactive components need interactive demos with useState + useKeyboard.

7. After creation

  • Run /review to validate focus coverage and export conventions
  • Run /sync-context to update packages/ui/CLAUDE.md with the new component

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.