# Input

> The baseline single-line text control. Forwards every native input prop, adds an `invalid` flag that syncs `aria-invalid`.

- Category: form
- Status: stable (since 1.0.0)
- A11y pattern: https://www.w3.org/WAI/ARIA/apg/patterns/textbox/
- Tokens: --background-secondary, --foreground-primary, --foreground-secondary, --foreground-danger, --cta-background
- Playground: https://design.freecodecamp.org/playground#input
- npm dependencies: `react@>=18 <20`
- Registry dependencies: [theme](https://design.freecodecamp.org/registry/theme.md)
- Files:
  - `Input.tsx` → `src/ui/input/Input.tsx` (raw: https://design.freecodecamp.org/registry/input/Input.tsx)
  - `input.css` → `src/ui/input/input.css` (raw: https://design.freecodecamp.org/registry/input/input.css)

## Install (copy source)

1. Ensure the theme is installed once per project - tokens.css + base.css imported globally, fonts available. See https://design.freecodecamp.org/registry/theme.md and https://design.freecodecamp.org/registry/starter.md.
2. Copy the files below into `src/ui/input/` (adjust to your project layout) and import the CSS once from your global stylesheet, e.g. `@import './ui/input/input.css';`.
3. Colors, spacing and type come from tokens - tailor the component by editing the copied source; recolour by editing tokens.css, not the component CSS.

## Usage

`<Input>` is the baseline single-line text field. It forwards every native
input attribute, forces `className` composition, and exposes an `invalid`
boolean so callers do not have to hand-wire `aria-invalid`.

## Usage

```tsx
import { Input } from './ui/input/Input';
<Input type='email' placeholder='you@example.com' invalid={hasError} />;
```

Pair with `<FormGroup>` + `<HelpBlock>` for a full label/input/help/error
stack.

## Accessibility

Always associate an Input with a label via `<FormControl>` (or a wrapping
`<label>`). `invalid` drives `aria-invalid="true"`, so pair it with an
adjacent `<HelpBlock variant='error'>` that describes the problem.

## Example

```tsx
import { FormGroup } from './ui/form-group/FormGroup';
import { Input } from './ui/input/Input';
import { HelpBlock } from './ui/help-block/HelpBlock';

<FormGroup>
  <label htmlFor="email">Email address</label>
  <Input id="email" type="email" placeholder="camper@example.com" />
  <HelpBlock>We send one curriculum update per week.</HelpBlock>
</FormGroup>
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `invalid` | `boolean` | no | - |  |

## Source: Input.tsx

```tsx
import React, { forwardRef } from 'react';

export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  invalid?: boolean;
}

export const Input = forwardRef<HTMLInputElement, InputProps>(
  ({ className = '', invalid, ...rest }, ref) => {
    const classes = ['input', className].filter(Boolean).join(' ');
    return (
      <input
        ref={ref}
        className={classes}
        aria-invalid={invalid || undefined}
        {...rest}
      />
    );
  }
);
Input.displayName = 'Input';
```

## Source: input.css

```css
.input {
  display: block;
  width: 100%;
  height: 38px;
  padding: 6px 10px;
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  line-height: var(--lh-base);
  color: var(--foreground-primary);
  background: var(--background-primary);
  border: 1px solid var(--background-quaternary);
  border-radius: 0;
  transition:
    border-color 120ms,
    box-shadow 120ms;
}
.input:focus {
  outline: 0;
  border-color: var(--highlight-color);
  box-shadow: 0 0 0 2px var(--highlight-background);
}
.input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.input[aria-invalid='true'] {
  border-color: var(--danger-color);
}
.input--textarea {
  height: auto;
  min-height: 88px;
  resize: vertical;
  font-family: var(--font-mono);
}
```

## HTML / vanilla variant

```html
<div class="form-group">
  <label class="form-label" for="email">Email address</label>
  <input class="input" id="email" type="email" placeholder="camper@example.com" />
  <p class="form-help">We send one curriculum update per week.</p>
</div>
```

Interactive behaviours for plain HTML come from the vanilla runtime (data-uikit-* attributes): https://design.freecodecamp.org/registry/vanilla.md - or download https://design.freecodecamp.org/cdn/uikit.global.js once and self-host it (do not hotlink).

## For coding agents

This library is distributed as copyable source, not an npm package. Start at https://design.freecodecamp.org/registry/starter.md, discover components via https://design.freecodecamp.org/llms.txt, and copy files into the consuming project. Keep token names intact; recolour by editing the copied tokens.css.
