# Key switch

One mechanical keyswitch, sectioned: a stem on a coil spring whose contact closes partway down the travel and opens again higher than it closed.

> For the complete index, see [llms.txt](https://robocn.dev/llms.txt). A Markdown version of any page is available by appending `.md` to its URL or by sending an `Accept: text/markdown` header.

## Install

```bash
bunx --bun shadcn@latest add https://robocn.dev/r/key-switch.json
```

Registry item: `key-switch` · [`https://robocn.dev/r/key-switch.json`](https://robocn.dev/r/key-switch.json)

## Notes

- The only machine here whose output is discrete. Everything else in the set reports a number; this reports a contact that is either closed or not, and the point it closes at is partway down a continuous travel with overtravel left after it.
- It has real hysteresis: the leaf resets a tenth of the travel higher than it actuated, so easing off a key that has registered does not open it again straight away. The `flutter` behaviour is there to show exactly that.
- Bottoming out is geometry rather than a limit: the stem's skirt lands on the cavity floor at the end of the travel, and the cap lands on the housing at the same moment.
- The cutaway is a drawing in the machine's own fore-aft plane, projected. From `plan` and `front` that plane is edge on — a line, which is what a section seen from the side is — so from those two the switch is drawn as the solids it is made of, and the cap's travel is still what moves.
- No dynamics: no force curve, no tactile bump force, no click leaf physics, no debounce. The spring is drawn compressing at its real proportion; it is not resolving any load.

## Usage

```tsx
import { KeySwitch } from "@/components/ui/key-switch"

<KeySwitch action="tactile" behavior="tap" />

// Controlled, or a switch you can press with a finger.
<KeySwitch press={0.55} travel={4} actuation={2} />
<KeySwitch interactive onActuatedChange={setClosed} />
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `view` | `"plan" | "front" | "profile" | "iso"` | `"profile"` | Where the camera stands. One switch, four projections: straight down, straight on, side elevation, or three-quarter from above. |
| `press` | `number` | — | Controlled press, 0 at rest to 1 bottomed out. Omit it and the stem runs behavior. |
| `behavior` | `"tap" | "flutter" | "hold" | "static"` | `"tap"` | One whole keystroke a cycle, the band around the actuation point where the hysteresis stops it chattering, or pressed and held. |
| `action` | `"linear" | "tactile" | "clicky"` | `"tactile"` | What the stem's leg does on the way down: a plain bar, a bar with a lobe on it, or one that drives a separate jacket. |
| `travel` | `number` | `4` | Full travel of the stem in world units, clamped to 2–6. It is geometry: more travel stands the cap higher at rest. |
| `actuation` | `number` | `travel / 2` | How far down the contact closes, world units. It reopens a tenth of the travel higher than that. |
| `speed` | `number` | `0.5` | Strokes per second. |
| `animate` | `boolean` | `true` | Off parks the machine at phase and stops rendering. A reduced-motion preference does the same. |
| `paused` | `boolean` | `false` | Freeze where it stands. |
| `phase` | `number` | `0` | Seconds of offset, so a row of machines breaks step. |
| `interactive` | `boolean` | `false` | Drag down the frame to press it, or arrow-key it 5% at a time; Home and End park it up and bottomed out. |
| `onPressChange` | `(press: number) => void` | — | Press throughout a drag or a key press. |
| `onActuatedChange` | `(closed: boolean) => void` | — | Fires when the contact closes or opens — once per event, never once per frame. |
| `showBoard` | `boolean` | `true` | The board the switch is mounted through. |
| `label` | `string` | — | Caption below the state readout. |
| `variant` | `"solid" | "outline" | "blueprint" | "wire"` | `"solid"` | How the machine is painted. Geometry never changes between variants. |
| `size` | `"xs" | "sm" | "md" | "lg" | "xl" | number` | `"md"` | Rendered width in pixels, or a step on the scale. |
| `color` | `string` | `var(--robot-shell)` | Body panels — the colour the machine reads as. |
| `accent` | `string` | `var(--robot-accent)` | Status colour: tip light, live tool, readouts. |
| `metal` | `string` | `var(--robot-metal)` | Bare machined parts: collars, bolts, tool bodies. |
| `dark` | `string` | `var(--robot-dark)` | Cast joints, base, shadow side. |
| `palette` | `Partial<RobotPalette>` | — | Override any subset of roles at once, including glow and grid. |

## Source

- `src/components/ui/key-switch.tsx`
