Skip to content
v1.4.2
Fit

Field ​

A labelled wrapper for a form control — label, an optional required/optional marker, and a hint or error line below. Pairs with Input, Select, Textarea, and the other form controls.

bash
jlds add field

Usage ​

Put any control in the default slot. Associate the label with it via htmlFor + the control's id.

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/field.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/input.css">

<div class="jl-field">
  <label class="jl-field__label" for="email">Email</label>
  <div class="jl-input-wrap jl-input-wrap--md">
    <input id="email" class="jl-input" type="email" placeholder="[email protected]" />
  </div>
  <div class="jl-field__hint">We'll only use this for account notices.</div>
</div>
vue
<script setup lang="ts">
import { Field } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
</script>

<template>
  <Field label="Email" hint="We'll only use this for account notices." html-for="email">
    <Input id="email" type="email" placeholder="[email protected]" />
  </Field>
</template>
tsx
import { Field } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

<Field label="Email" hint="We'll only use this for account notices." htmlFor="email">
  <Input id="email" type="email" placeholder="[email protected]" />
</Field>

Required & optional ​

Add required for a red asterisk, or optional for an "(optional)" marker.

html
<div class="jl-field">
  <label class="jl-field__label" for="name">
    Full name <span class="jl-field__req" aria-hidden="true">*</span>
  </label>
  <div class="jl-input-wrap jl-input-wrap--md"><input id="name" class="jl-input" /></div>
</div>
vue
<template>
  <Field label="Full name" required html-for="name">
    <Input id="name" />
  </Field>
  <Field label="Company" optional html-for="company">
    <Input id="company" />
  </Field>
</template>
tsx
<Field label="Full name" required htmlFor="name">
  <Input id="name" />
</Field>
<Field label="Company" optional htmlFor="company">
  <Input id="company" />
</Field>

Error state ​

Pass error to replace the hint with a red message (and pair with the control's invalid).

html
<div class="jl-field">
  <label class="jl-field__label" for="pw">Password</label>
  <div class="jl-input-wrap jl-input-wrap--md" data-invalid="true">
    <input id="pw" class="jl-input" type="password" value="123" aria-invalid="true" />
  </div>
  <div class="jl-field__error" role="alert">Must be at least 8 characters.</div>
</div>
vue
<template>
  <Field label="Password" error="Must be at least 8 characters." html-for="pw">
    <Input id="pw" type="password" invalid />
  </Field>
</template>
tsx
<Field label="Password" error="Must be at least 8 characters." htmlFor="pw">
  <Input id="pw" type="password" invalid />
</Field>

Props ​

Field takes the same props in React and Vue (in Vue, text props are strings and htmlFor is html-for).

PropTypeDefaultDescription
labelReact.ReactNode—Field label text
hintReact.ReactNode—Helper text below (hidden when error is set)
errorReact.ReactNode—Error message; replaces hint, styled red, role="alert"
requiredbooleanfalseShow a red required asterisk
optionalbooleanfalseShow an "(optional)" marker
htmlForstring—id of the control, to associate the label

Slots (Vue) / children (React): the form control itself.

CSS classes (HTML) ​

ClassPurpose
.jl-fieldWrapper — always required
.jl-field__labelThe <label>
.jl-field__reqRed required asterisk
.jl-field__optional"(optional)" marker
.jl-field__hintHelper text
.jl-field__errorError message (use with role="alert")