ephemera

Input

Type on a ruled line. Includes a notebook Textarea and Label.

Install

$ npx shadcn@latest add https://ephemera.kerrshift.com/r/input.json

Or 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

PropTypeDefaultDescription
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}
    />
  );
}