# Select

> Native HTML select restyled to fit the terminal shell. A real <select> - no portal, no popover, no Combobox. Chevron is an inlined Lucide glyph.

- Category: form
- Status: stable (since 0.3.0)
- A11y pattern: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
- Tokens: --background-quaternary, --foreground-primary, --foreground-secondary, --danger-color, --border-width-thin
- Playground: https://design.freecodecamp.org/playground#select
- npm dependencies: `react@>=18 <20`
- Registry dependencies: [theme](https://design.freecodecamp.org/registry/theme.md)
- Files:
  - `Select.tsx` → `src/ui/select/Select.tsx` (raw: https://design.freecodecamp.org/registry/select/Select.tsx)
  - `select.css` → `src/ui/select/select.css` (raw: https://design.freecodecamp.org/registry/select/select.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/select/` (adjust to your project layout) and import the CSS once from your global stylesheet, e.g. `@import './ui/select/select.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

Select is the native dropdown. It renders a real HTML `<select>` - no
popover, no portal - styled with our border, spacing, and chevron so
it sits flush with the rest of the form shell. Reach for Combobox only
when you need filtering, async loads, or custom option rendering.

## Accessibility

Native `<select>` - every platform screen reader, keyboard navigation,
and mobile picker works out of the box. Pair with a `<label>` (or
FormGroup + HelpBlock) for an accessible name. Use `invalid` plus
HelpBlock's error tone to signal validation failures.

## Example

```tsx
import { Select } from './ui/select/Select';

<Select id="difficulty" defaultValue="intermediate">
  <option value="beginner">Beginner</option>
  <option value="intermediate">Intermediate</option>
  <option value="advanced">Advanced</option>
</Select>
```

## Props

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

## Source: Select.tsx

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

export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
  invalid?: boolean;
}

export const Select = forwardRef<HTMLSelectElement, SelectProps>(
  ({ className = '', invalid, children, ...rest }, ref) => {
    const classes = ['select', className].filter(Boolean).join(' ');
    return (
      <select
        ref={ref}
        className={classes}
        aria-invalid={invalid || undefined}
        {...rest}
      >
        {children}
      </select>
    );
  }
);
Select.displayName = 'Select';
```

## Source: select.css

```css
.select {
  display: block;
  width: 100%;
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  line-height: var(--lh-base);
  color: var(--foreground-primary);
  background-color: var(--background-quaternary);
  border: var(--border-width-thin) solid var(--foreground-secondary);
  padding: 6px 32px 6px 10px;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23808080' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 16px 16px;
  transition:
    border-color 120ms,
    background-color 120ms;
}
.select:focus-visible {
  outline: none;
  border-color: var(--foreground-primary);
}
.select[aria-invalid='true'] {
  border-color: var(--danger-color);
}
.select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
```

## HTML / vanilla variant

```html
<select class="select" id="difficulty">
  <option>Beginner</option>
  <option selected>Intermediate</option>
  <option>Advanced</option>
</select>
```

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.
