Claude-skill-registry form-components

StepLeague reusable form components with accessibility features. Use when creating any form, input field, select dropdown, checkbox, or file upload in the application. Keywords: form, input, select, checkbox, textarea, file upload, accessibility, FormInput, FormSelect.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/form-components" ~/.claude/skills/majiayu000-claude-skill-registry-form-components && rm -rf "$T"
manifest: skills/data/form-components/SKILL.md
source content

Form Components Skill

Core Rule

ALWAYS use form components from

@/components/ui/form-fields
.

These components:

  • Auto-generate
    id
    /
    name
    attributes
  • Include accessibility features (aria-describedby, aria-invalid)
  • Consistent styling across the app
  • Proper label association

Available Components

ComponentPurpose
FormInput
Text, email, password, number inputs
FormSelect
Dropdown select
FormCheckbox
Checkbox with label
FormTextarea
Multi-line text input
FormFileInput
File upload

Usage Examples

FormInput

import { FormInput } from "@/components/ui/form-fields";

<FormInput
  fieldName="user-email"
  label="Email Address"
  type="email"
  placeholder="you@example.com"
  required
  error={errors.email}
  description="We'll never share your email"
/>

<FormInput
  fieldName="step-count"
  label="Steps"
  type="number"
  min={0}
  max={100000}
  value={steps}
  onChange={(e) => setSteps(e.target.value)}
/>

FormSelect

import { FormSelect } from "@/components/ui/form-fields";

<FormSelect
  fieldName="league-select"
  label="Select League"
  value={selectedLeague}
  onChange={(e) => setSelectedLeague(e.target.value)}
  required
>
  <option value="">Choose a league...</option>
  {leagues.map((league) => (
    <option key={league.id} value={league.id}>
      {league.name}
    </option>
  ))}
</FormSelect>

FormCheckbox

import { FormCheckbox } from "@/components/ui/form-fields";

<FormCheckbox
  fieldName="accept-terms"
  label="I accept the terms and conditions"
  checked={acceptedTerms}
  onChange={(e) => setAcceptedTerms(e.target.checked)}
  required
/>

<FormCheckbox
  fieldName="remember-me"
  label="Remember me on this device"
  description="We'll keep you logged in for 30 days"
/>

FormTextarea

import { FormTextarea } from "@/components/ui/form-fields";

<FormTextarea
  fieldName="feedback"
  label="Your Feedback"
  placeholder="Tell us what you think..."
  rows={4}
  maxLength={500}
  value={feedback}
  onChange={(e) => setFeedback(e.target.value)}
/>

FormFileInput

import { FormFileInput } from "@/components/ui/form-fields";

<FormFileInput
  fieldName="screenshot"
  label="Upload Screenshot"
  accept="image/*"
  onChange={handleFileChange}
  description="PNG, JPG, or GIF up to 5MB"
  error={fileError}
/>

Props Reference

Common Props (All Components)

PropTypeRequiredDescription
fieldName
string
Unique identifier (becomes id/name)
label
string
Label text
error
string
Error message to display
description
string
Help text below field
required
boolean
Adds required indicator
disabled
boolean
Disables the field
className
string
Additional CSS classes

FormInput Additional Props

PropTypeDescription
type
string
Input type (text, email, password, number, etc.)
placeholder
string
Placeholder text
min
,
max
number
For number inputs
pattern
string
Validation pattern

FormSelect Additional Props

PropTypeDescription
children
ReactNode
Option elements
placeholder
string
First disabled option text

Accessibility Features

The form components automatically provide:

1. Label Association

<!-- Generated HTML -->
<label for="user-email">Email Address</label>
<input id="user-email" name="user-email" />

2. Error Announcements

<!-- When error prop is provided -->
<input
  id="user-email"
  aria-invalid="true"
  aria-describedby="user-email-error"
/>
<p id="user-email-error" role="alert">Invalid email address</p>

3. Description Association

<input aria-describedby="user-email-description" />
<p id="user-email-description">We'll never share your email</p>

Form Patterns

Basic Form

import { FormInput, FormSelect, FormCheckbox } from "@/components/ui/form-fields";

function MyForm() {
  const [formData, setFormData] = useState({
    email: '',
    role: '',
    newsletter: false,
  });
  const [errors, setErrors] = useState({});

  return (
    <form onSubmit={handleSubmit}>
      <FormInput
        fieldName="email"
        label="Email"
        type="email"
        value={formData.email}
        onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
        error={errors.email}
        required
      />
      
      <FormSelect
        fieldName="role"
        label="Role"
        value={formData.role}
        onChange={(e) => setFormData(prev => ({ ...prev, role: e.target.value }))}
        required
      >
        <option value="">Select role...</option>
        <option value="member">Member</option>
        <option value="admin">Admin</option>
      </FormSelect>
      
      <FormCheckbox
        fieldName="newsletter"
        label="Subscribe to newsletter"
        checked={formData.newsletter}
        onChange={(e) => setFormData(prev => ({ ...prev, newsletter: e.target.checked }))}
      />
      
      <button type="submit">Submit</button>
    </form>
  );
}

With Loading State

<FormInput
  fieldName="email"
  label="Email"
  disabled={isSubmitting}
/>

<button type="submit" disabled={isSubmitting}>
  {isSubmitting ? "Saving..." : "Save"}
</button>

Anti-Patterns

❌ Don't Use Raw HTML Elements

// ❌ WRONG
<label>Email</label>
<input type="email" name="email" />

// ✅ CORRECT
<FormInput fieldName="email" label="Email" type="email" />

❌ Don't Forget fieldName

// ❌ WRONG - no fieldName
<FormInput label="Email" type="email" />

// ✅ CORRECT
<FormInput fieldName="email" label="Email" type="email" />

❌ Don't Duplicate IDs

// ❌ WRONG - duplicate fieldNames
<FormInput fieldName="email" label="Email" />
<FormInput fieldName="email" label="Confirm Email" />

// ✅ CORRECT - unique fieldNames
<FormInput fieldName="email" label="Email" />
<FormInput fieldName="confirm-email" label="Confirm Email" />

Reference Files

FilePurpose
src/components/ui/form-fields.tsx
Component implementations
docs/FORM_SYSTEM.md
Full documentation

Related Skills

  • design-system
    - Form styling and theming
  • architecture-philosophy
    - Use existing components