Skip to main contentSkip to docs navigation

Floating labels

Float labels above textual form fields when a value is present or the field is focused.

Examples

Pass label to FloatingInput and wrap a TextInput, Select, or Textarea to enable floating labels with textual form fields. FloatingInput renders the floating <label> itself and ties it to the control inside, so pass neither label nor an id to the wrapped control. A placeholder is required on each <TextInput>, since this CSS-only technique relies on the :placeholder-shown pseudo-class.

JSX
import { FloatingInput, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <FloatingInput className="mb-md" label="Email address">
        <TextInput type="email" placeholder="name@example.com" />
      </FloatingInput>
      <FloatingInput label="Password">
        <TextInput autoComplete="current-password" type="password" placeholder="Password" />
      </FloatingInput>
    </>
  )
}

The label floats automatically when the wrapped control already has a value.

JSX
import { FloatingInput, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <FloatingInput label="Input with value">
      <TextInput type="email" placeholder="name@example.com" defaultValue="test@example.com" />
    </FloatingInput>
  )
}

Help and feedback

Passing help, invalid/invalidFeedback, or valid/validFeedback wraps the floating input in a .form-field and renders the corresponding text below it, same as the other form components. The control inside is described by the text that shows. Set invalid/valid on the control as well: its state is what shows the feedback and draws its border.

We'll never share your email.
JSX
import { FloatingInput, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <FloatingInput help="We'll never share your email." label="Email address">
      <TextInput type="email" placeholder="name@example.com" />
    </FloatingInput>
  )
}

Textareas

A <Textarea> renders at the same height as a <TextInput> by default.

JSX
import { FloatingInput, Textarea } from '@chassis-ui/react'

export const Example = () => {
  return (
    <FloatingInput label="Comments">
      <Textarea placeholder="Leave a comment here"></Textarea>
    </FloatingInput>
  )
}

Set an explicit height (inline or via custom CSS) for a custom <Textarea> height instead of the rows attribute, which floating labels don't account for.

JSX
import { FloatingInput, Textarea } from '@chassis-ui/react'

export const Example = () => {
  return (
    <FloatingInput label="Comments">
      <Textarea placeholder="Leave a comment here" style={{ height: '100px' }}></Textarea>
    </FloatingInput>
  )
}

Selects

Floating labels also work with <Select>, though a select's label always renders in its floated state rather than overlapping the value. Selects with size or multiple set aren't supported.

JSX
import { FloatingInput, Select } from '@chassis-ui/react'

export const Example = () => {
  return (
    <FloatingInput label="Works with selects">
      <Select>
        <option>Open this select menu</option>
        <option value="1">One</option>
        <option value="2">Two</option>
        <option value="3">Three</option>
      </Select>
    </FloatingInput>
  )
}

Layout

Place floating inputs in the columns of a <Grid> to lay them out side by side.

JSX
import { FloatingInput, Grid, Select, TextInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Grid columns={{ base: 1, md: 2 }} gap="sm">
      <div>
        <FloatingInput label="Email address">
          <TextInput type="email" placeholder="name@example.com" defaultValue="email@example.com" />
        </FloatingInput>
      </div>
      <div>
        <FloatingInput label="Works with selects">
          <Select>
            <option>Open this select menu</option>
            <option value="1">One</option>
            <option value="2">Two</option>
            <option value="3">Three</option>
          </Select>
        </FloatingInput>
      </div>
    </Grid>
  )
}

Other controls

FloatingInput generates the ids of its label, help and feedback, and a TextInput, Select or Textarea inside takes them by itself. Any other control, such as a native <input>, takes them by hand: choose the ids, pass them through the ids prop, and repeat them on the control, as for a <FormField>. The control needs the .form-input class and has to be a direct child, which is what the label floats over.

As it appears on your passport.
JSX
import { FloatingInput } from '@chassis-ui/react'

export const Example = () => {
  return (
    <FloatingInput
      help="As it appears on your passport."
      ids={{ help: 'floatingNativeHelp', input: 'floatingNative' }}
      label="Full name"
    >
      <input
        aria-describedby="floatingNativeHelp"
        className="form-input"
        id="floatingNative"
        placeholder="Jane Doe"
        type="text"
      />
    </FloatingInput>
  )
}

ids also replaces a generated id with one of your own, for a test or a link to the field: ids={{ input: 'email' }} gives the wrapped TextInput the id email. In development, FloatingInput warns when no element has the id its label points at.

API

FloatingInput

PropTypeDefault
childrenReactNode—
The form control (e.g. a TextInput, Select, or Textarea) the floating label attaches to. Chassis-css's floating-label CSS relies on a label:has(~ .form-input) selector, so this must render an element carrying the form-input class as a direct child.
className?string—
A string of all className you want applied to the .form-floating element.
help?ReactNode—
A description for the field, rendered below the control. Setting this (or invalidFeedback/ validFeedback) wraps the floating input in a .form-field.
ids?FormFieldIds—
The ids that tie the label, help and feedback to the wrapped control, in place of the ones FloatingInput generates. A field component of this library (TextInput, Select, Textarea) takes them by itself and needs none. Any other control, such as a native <input>, takes them by hand: input is the label's htmlFor and goes on the control as its id, label is the label's own id, for a control named through aria-labelledby, and help and feedback are the ids of the help and feedback text, for the control's aria-describedby. label without input leaves the label with no htmlFor.
invalid?boolean—
Set field validation state to invalid.
invalidFeedback?ReactNode—
An error message for the field, rendered below the control when invalid is set.
labelReactNode—
The field's caption, rendered as a floating FormLabel associated with the wrapped control.
valid?boolean—
Set field validation state to valid.
validFeedback?ReactNode—
A success message for the field, rendered below the control when valid is set.