Skip to content
ssntl/ui

Input

A 48px text input: visible focus, a 3:1 border on every surface and errors from aria-invalid.

Installation

$ pnpm dlx shadcn@latest add @ssntl/input

Usage

tsx
import { Input } from "@/components/ui/input"

<Input type="email" autoComplete="email" placeholder="ana@studio.com" />
  • 48px tall, the same as buttons, so a field and its action line up.
  • The border uses the input token (3:1 on every surface); focus darkens it and shows the ring.
  • Use the right type and autoComplete. The placeholder shows an example value, never the label.
  • Errors come from aria-invalid plus a hint that says how to fix it (see Field).

Code

components/ui/input.tsx
import * as React from "react"

import { cn } from "@/lib/utils"

// Essential Design `es-input`: 48px (the height of buttons), `card` fill, a 1px
// `input` border that meets 3:1 on every surface, `radius-sm`. Focus darkens the
// border and shows the ring; errors come from aria-invalid, never color alone.
function Input({
  className,
  type = "text",
  ...props
}: React.ComponentProps<"input">) {
  return (
    <input
      data-slot="input"
      type={type}
      className={cn(
        "h-12 w-full min-w-0 rounded-sm border border-input bg-card px-4 text-body text-foreground",
        "transition-[border-color] duration-(--duration-fast) ease-out placeholder:text-muted-foreground",
        "hover:border-muted-foreground focus:border-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
        "disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-destructive",
        "file:mr-3 file:border-0 file:bg-transparent file:font-medium",
        className
      )}
      {...props}
    />
  )
}

export { Input }