Skip to content
kerfjsGitHub

Field

The frame every field shares: label, icon, hint, error. Wrap your own control in it.

SourcePackage @kerf-js/veneerTry it

Preview

tsx
import { Input, Stack } from '@kerf-js/veneer'export function Anatomy() {  return (  <Stack gap="group">    <Input      label="Domain"      icon="globe"      prefix="https://"      suffix=".kerf.dev"      hint="You can add your own later."      defaultValue="storefront"      clearable    />    <Input      label="Release name"      hint="Shown in the deploy list."      counter={40}      defaultValue="Autumn sale"    />  </Stack>  )}

Installation

npm install @kerf-js/veneer

Try it

Change the controls: the preview and its code update together. The address keeps what you set, so you can link to it.

Live preview
Controls
Variant
Codeupdates as you change the controls
import { Field } from '@kerf-js/veneer'<Input label="Domain" hint="You can add your own later." prefix="https://" suffix=".kerf.dev" />

Usage

tsx
import { Field } from '@kerf-js/veneer'

Variants

Use variant to put the label on the border, which is the default, or classic to put it above the field.

tsx
import { Grid, Input } from '@kerf-js/veneer'export function LabelStyles() {  return (  <Grid    cols={2}    gap="group"  >    <Input      label="Project name"      hint="Floats on the border."    />    <Input      label="Project name"      hint="Sits above the field."      variant="classic"    />  </Grid>  )}

Validation

An error takes the hint's place, so the layout doesn't jump.

tsx
import { Input, Stack } from '@kerf-js/veneer'export function Error() {  return (  <Stack gap="group">    <Input      label="Project name"      hint="Lowercase letters and dashes."      defaultValue="storefront"    />    <Input      label="Project name"      hint="Lowercase letters and dashes."      defaultValue="Store Front"      error="That name has a space in it."    />  </Stack>  )}

An action on the edge

Input takes every Field prop, so end adds to the right edge.

tsx
import { state } from '@kerf-js/core'import { Button, Input } from '@kerf-js/veneer'export function CustomControl() {  const token = state('tsl_4f9c2e71')  const rotate = () => {    const random = Math.random().toString(16)    token.value = `tsl_${random.slice(2, 10)}`  }  return (    <Input      label="Deploy token"      hint="Rotating it stops the old one straight away."      end={        <Button          size="sm"          emphasis="ghost"          tone="neutral"          onClick={rotate}        >          Rotate        </Button>      }      value={token.value}      readonly    />  )}

With form

Fields stack into a form, and autocomplete lets the browser fill in the name and email.

tsx
import { Input, Stack, Textarea } from '@kerf-js/veneer'export function Form() {  return (  <Stack gap="group">    <Input      label="Name"      autocomplete="name"      defaultValue="Mara Lindqvist"    />    <Input      label="Work email"      type="email"      autocomplete="email"      defaultValue="mara@kerf.dev"    />    <Textarea      label="About you"      hint="Shown to your team."    />  </Stack>  )}

When to use

Use it for

  • A control Veneer doesn't have that should sit with the rest of a form

Reach for something else

Accessibility

  • Use the id and describedby that control receives, so the label, hint and error stay tied to your control.

Theming

Restyle it app-wide from veneer() in your kerf config. Every key takes colour, spacing, size, radius, shadow, type and motion; its own keys are listed. A state nests inside: hover: { bg: 'ink' }. Text on a fill you set is contrast-checked at build. How theming works

KeyOwn keysStates
parts.fieldbg, radiushover, focus, press, active, open, disabled
parts.field.labeltexthover, focus
ts
veneer({  colors: { brand: '#e8590c', ink: '#1b1b1f' },  parts: {    field: { radius: 4, label: { focus: { text: 'brand' } } },  },})

API Reference

PropTypeDefaultDescription
labelstring—The name people read and screen readers announce. Always required, and it floats on the border by default.
hintunknown—A short line of help under the field.
errorunknown—Replaces the hint and marks the field invalid.
iconIconRef—An icon before the control.
prefixunknown—Fixed text before the value.
suffixunknown—Fixed text after the value.
variant"floating" | "classic"—floating puts the label on the border, classic above the field.
appearance"line"—line draws only the bottom edge.
size"sm" | "md" | "lg"—sm, md or lg. Each follows the density around it.
clearablebooleanfalseA clear button, shown once there is a value.
counternumber—The most it should hold. Draws a count under the field.
idstring—The element id. One is made for you if you leave it out.
valuestring—The current value, when you own the state.
disabledbooleanfalseCan't be focused or changed, and doesn't post with a form.
readonlybooleanfalseCan be focused, read and copied, but not changed. It still posts.
controlControl—Draws the control. Gets id, describedby, invalid and the remaining props.
endunknown—Sits at the right edge of the frame, after any suffix.
filledbooleanfalseKeeps the label up when your control holds a value the field can't see.
EventTypeDefaultDescription
onInput((e: InputEvent & { currentTarget: HTMLInputElement & HTMLTextAreaElemen…—Called on each keystroke.

RelatedInputSelectTextarea