Input
Type on a ruled line. Includes a notebook Textarea and Label.
Install
$ npx shadcn@latest add https://ephemera.kerrshift.com/r/input.jsonOr copy the source below into components/ui/input.tsx. Core components need the Paper Theme and Radix where noted.
Usage
example.tsx
import { Input, Textarea, Label } from "@/components/ui/input";
<Label htmlFor="name">Your name</Label>
<Input id="name" placeholder="Ada Lovelace" />
<Label htmlFor="note">Note</Label>
<Textarea id="note" placeholder="Dear diary…" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| Input | <input> props | — | A single ruled line. The line turns to ink on focus and red when aria-invalid. |
| Textarea | <textarea> props | — | Notebook paper with a margin. Text sits on the 28px lines. |
| Label | <label> props | — | Small typed capitals. |
Any other props are passed to the root element.
Fonts
Components read optional CSS variables and fall back to system fonts: --font-display, --font-receipt, --font-type, --font-hand and --font-stamp. This site uses Fraunces, IBM Plex Mono, Courier Prime, Caveat and Anton.
Source
components/ui/input.tsx
import * as React from "react";
const type = { fontFamily: "var(--font-type, 'Courier New', ui-monospace, monospace)" };
/** Typed on a single ruled line. */
export function Input({ className = "", style, ...props }: React.InputHTMLAttributes<HTMLInputElement>) {
return (
<input
className={`h-10 w-full min-w-0 border-0 border-b-[1.5px] border-(--rule) bg-transparent px-0.5 pb-1 pt-2 text-[15px] text-(--ink) caret-(--pen) outline-none transition-colors placeholder:text-(--ink-faint) focus:border-(--pen) aria-invalid:border-(--danger) disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
style={{ ...type, ...style }}
{...props}
/>
);
}
/** Ruled notebook paper with a margin line. Text sits on the lines. */
export function Textarea({ className = "", style, rows = 5, ...props }: React.TextareaHTMLAttributes<HTMLTextAreaElement>) {
return (
<textarea
rows={rows}
className={`paper-ruled block w-full resize-y rounded-[3px] border border-(--edge) bg-(--paper-raised) py-0 pl-12 pr-4 text-[15px] text-(--ink) caret-(--pen) outline-none transition-colors placeholder:text-(--ink-faint) focus:border-(--pen) aria-invalid:border-(--danger) disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
style={{ ...type, lineHeight: "28px", paddingTop: 0, ...style }}
{...props}
/>
);
}
/** Small typed caps above a field. */
export function Label({ className = "", style, ...props }: React.LabelHTMLAttributes<HTMLLabelElement>) {
return (
<label
className={`block text-[10.5px] font-bold uppercase tracking-[0.2em] text-(--ink-muted) ${className}`}
style={{ ...type, ...style }}
{...props}
/>
);
}