Skip to content
v1.0.3

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.ReactNodeField label text
hintReact.ReactNodeHelper text below (hidden when error is set)
errorReact.ReactNodeError message; replaces hint, styled red, role="alert"
requiredbooleanfalseShow a red required asterisk
optionalbooleanfalseShow an "(optional)" marker
htmlForstringid 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")