meowerse_
docs menu

Field

A labelled text input with an optional hint or error, and a reveal button for passwords.

import { Field } from "@meowerse/ui"; · source packages/ui/src/components/Field.tsx

variants and states

letters, digits and _ only
with a hint
password — with a reveal button
that name is taken — try another
error
disabled
floating label — labelPlacement="float": rests inside while empty, moves onto the border once focused or filled
12–128 characters
floating label, filled — stays visible on the border; never runs under the reveal button

props

proptypedefaultrequired
type (html)HTMLInputTypeAttribute"text"no
labelstring the visible label, always shown and tied to the input.—yes
labelPlacement"above" | "float" where the label sits: above the input, or float — inside the empty input like a placeholder, moving up onto its top border once it is focused or filled, so it stays visible (CSS only, works without JS)."above"no
hintstring helper text shown below the input, linked with aria-describedby.—no
errorstring an error message shown instead of the hint; also sets aria-invalid.—no

…plus all InputHTMLAttributes<HTMLInputElement>.

css custom properties

variabledarklight
--c-bg-elev#111113#f1f0ec
--c-danger#ff6b63#b42318
--c-fg#ededeb#1a1a1c
--c-fg-muted#b3b3af#46464a
--c-fg-subtle#8c8c88#5e5e5b
--c-focus#00ff82#0a6e3c
--c-line-input#6a6a70#7a7973
--control-height44pxsame
--d-fast120mssame
--disabled-opacity0.5same
--easecubic-bezier(.16,1,.3,1)same
--fs-20.8125remsame
--fs-41remsame
--r-m4pxsame
--sp-14pxsame
--sp-28pxsame
--sp-312pxsame

Classes: .mw-field. Set any of these variables on an ancestor (or wrap a subtree in .mw-theme--dark / .mw-theme--light) to restyle just that part of a page.

usage

with a hint

tsx
import { Field } from "@meowerse/ui";

<Field label="username" hint="letters, digits and _ only" autoCapitalize="none" autoCorrect="off" spellCheck={false} />

html (needs @meowerse/ui/tokens.css)
<div class="mw-field"><label for="_R_0_">username</label><div class="mw-field__control"><input id="_R_0_" type="text" aria-describedby="_R_0_-hint" autoCapitalize="none" autoCorrect="off" spellCheck="false"/></div><span id="_R_0_-hint" class="mw-field__hint">letters, digits and _ only</span></div>

password

tsx
import { Field } from "@meowerse/ui";

<Field label="password" type="password" defaultValue="correct horse" />

error

tsx
import { Field } from "@meowerse/ui";

<Field label="username" defaultValue="alxnko" error="that name is taken — try another" />

html (needs @meowerse/ui/tokens.css)
<div class="mw-field mw-field--error"><label for="_R_0_">username</label><div class="mw-field__control"><input id="_R_0_" type="text" aria-invalid="true" aria-describedby="_R_0_-err" value="alxnko"/></div><span id="_R_0_-err" role="alert" class="mw-field__error">that name is taken — try another</span></div>

disabled

tsx
import { Field } from "@meowerse/ui";

<Field label="email" defaultValue="not collected" disabled />

html (needs @meowerse/ui/tokens.css)
<div class="mw-field"><label for="_R_0_">email</label><div class="mw-field__control"><input id="_R_0_" type="text" disabled="" value="not collected"/></div></div>

floating label

tsx
import { Field } from "@meowerse/ui";

<Field label="username" labelPlacement="float" autoCapitalize="none" autoCorrect="off" spellCheck={false} />

html (needs @meowerse/ui/tokens.css)
<div class="mw-field mw-field--float"><div class="mw-field__control"><input id="_R_0_" type="text" autoCapitalize="none" autoCorrect="off" spellCheck="false" placeholder=" "/><label for="_R_0_" class="mw-field__float-label">username</label></div></div>

floating label, filled

tsx
import { Field } from "@meowerse/ui";

<Field label="password" labelPlacement="float" type="password" defaultValue="correct horse" hint="12–128 characters" />

accessibility

  • The label is always visible and tied to the input.
  • With labelPlacement="float" the label is still the input's real <label>, so its accessible name doesn't change; it only moves (CSS alone, no JS), keeping a long label on one line with an ellipsis.
  • The hint and the error are linked with aria-describedby; an error sets aria-invalid and is announced.
  • The reveal button is 44×44 px and named "show password" or "hide password".
  • Inputs are 16 px, so phones don't zoom in.
keydoes
Tabmoves to the input, then to the reveal button

do and don't

do

  • Say what is wrong and how to fix it in the error.
  • Use autoCapitalize="none" for usernames.

don't

  • Don't use the placeholder as the label.
  • Don't validate on every keystroke before the person has finished.