Design Lounge
Install

Nº 521 of 528

Label that rises in the field

The label sits in the field at 15px, then rises and scales to 11px when the field is focused or already filled.

Preview

Open full size
1280 × 800 · 5.3 KB · 114 lines

Use it

Free to use · credit appreciated

1 Once, in your project

npx skills add SusanAcharya/Design-Lounge

2 Then ask

Using the Design Lounge skill, build "Label that rises in the field" (piece field-label-morph) in my stack. Match its brief; restyle it onto my product's theme.

No skill? Paste the brief instead. It carries the numbers, tokens and behaviour.

Brief

166 lines

Build brief for a coding agent. Rebuild this piece in the reader’s stack. If they haven’t said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don’t “improve” them. When a kit is locked, map the colours onto that kit. Keep the rise.

What it is

A short note form for Fold, a fictional clay studio. The card is 440px on warm paper. Three fields: name, email, and a note. The email starts filled with mira@fold.studio, so its label is already raised. The other two labels sit inside the field. Focusing an empty field, or typing in it, moves that label up. This is the field pattern. The map-and-address block in contact-split-map-form is a different piece. Use this one when the page is a form and the motion dial is 4 to 7.

Reference behaviour

  1. First frame: name and note are empty, labels inside. Email is filled, label raised. No error is showing. The status line is empty.
  2. Focus an empty field. Over 200ms the label moves up 11px and scales to 0.73. The border becomes #241c16.
  3. Type, then blur. If the value is non-empty after trim, the label stays up. If the value is empty, the label returns inside.
  4. Submit with a gap: that field gets a clay-red border and a #f8ebe8 fill. The alert under it reads “Add a name.”, “Add an email.”, or “Add a short note.”
  5. Email without @ reads “That email needs an @.”
  6. A valid submit writes “Sent to the Thursday firing.” in #2f6b45 and clears the errors.
  7. Reduced motion: the label still ends in the right place. The transition is 1ms.

Structure

440 card, padding 28
heading 32 serif
lede 14
field 58, radius 12
  label absolute
  input
error line 18
field 58 (filled)
error line
field 118 (note)
error line
row: status · button 44
  • section.card holds h1, p.lede, and form.
  • Each .field is a div containing label[for] and input or textarea.
  • The error is a sibling p.err, not inside the field, so the label’s absolute box stays 58px.
  • The button is type="submit".

Tokens

:root {
  --bg: #f3efe6;
  --card: #fffcf7;
  --line: #e4dcd0;
  --ink: #241c16;
  --muted: #7a6e62;
  --accent: #b24a28;
  --danger: #9c2f24;
  --danger-bg: #f8ebe8;
  --ok: #2f6b45;
  --serif: "Newsreader", Georgia, serif;
  --sans: "Outfit", system-ui, sans-serif;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --rise: 200ms;
  --field-h: 58px;
  --note-h: 118px;
  --radius: 12px;
}

Typography

RoleFamilySizeWeightLineTracking
HeadingNewsreader32px6001.05-0.02em
Lede, statusOutfit14px / 13px4001.450
Label at restOutfit15px40010
Label raisedOutfit11px (15 × 0.73)40010
ValueOutfit15px4001.40
ButtonOutfit14px60010
ErrorOutfit13px4001.30

Motion

ThingTriggerPropertyFrom → toDurationEasingReduced motion
Labelfocus, or value becomes non-emptytransformnone → translateY(-11px) scale(0.73)200mscubic-bezier(.2,.7,.2,1)1ms, same end state
Label coloursamecolor#7a6e62 → #241c16200mssameinstant
Borderfocus-withinborder-color#e4dcd0 → #241c16none—instant

The input text does not animate. Do not animate the field’s height.

States

  • Rest, empty: label at top 18px, left 14px, 15px, #7a6e62. Border #e4dcd0.
  • Rest, filled: label transformed. Value padded so it sits under the label (padding-top: 22px).
  • Focus-within: border #241c16. No extra glow.
  • Error: border #9c2f24, background #f8ebe8, label #9c2f24. The alert text is the same red.
  • Button hover: background #3a2e24.
  • Button focus-visible: 2px #b24a28 outline, offset 3px.
  • Success status: #2f6b45. The fields stay as they are.

Accessibility

  • Every field has a real label for matching the input id. The label is not a placeholder.
  • Errors use role="alert". The status line uses role="status".
  • Submit is a button in the form, so Enter submits.
  • Focus order: name, email, note, button.
  • Do not remove the outline on the button. The inputs hide the browser outline because the field border is the focus indicator.
  • Contrast: #241c16 on #fffcf7 passes. #7a6e62 on white is for the resting label only, and it becomes #241c16 when that field is the one in use.
  • Hit target: the field is 58px tall. The button is 44px tall.

Responsive rules

  • ≥1280: the card stays 440px, centred.
  • 768: same card, page padding 24px.
  • <640: the card is width: calc(100% - 32px), heading 28px, button stays 44px. The fields do not sit side by side.
  • A phone product uses phone-form-fields instead of this card.

Acceptance checklist

Always

  • Empty label sits inside the field. Focus or a non-empty value raises it 11px and scales it to 0.73 in 200ms.
  • The label is a <label for>, not a placeholder attribute.
  • Blur on an empty field returns the label inside.
  • Error border is #9c2f24 with a sentence in the alert under that field.
  • Button is 44px tall. Fields are 58px, the note 118px.
  • Reduced motion keeps the end state and drops the 200ms ease.
  • One accent. No gradient, no floating shadow on the label.

This demo

  • Heading is “A note for Fold”.
  • Email starts as mira@fold.studio with the label already raised.
  • Empty alerts are “Add a name.”, “Add an email.”, “Add a short note.”
  • A missing @ reads “That email needs an @.”
  • Success reads “Sent to the Thursday firing.”

Implementation notes

The raised look is one transform, so it stays on the compositor:

.field label {
  position: absolute;
  left: 14px;
  top: 18px;
  font-size: 15px;
  transform-origin: left center;
  transition: transform 200ms cubic-bezier(.2,.7,.2,1);
}
.field.filled label,
.field:focus-within label {
  transform: translateY(-11px) scale(.73);
}

Toggle .filled from the trimmed value on input, including once at load so a prefilled field does not wait for a keystroke.

Common mistakes:

  • Using placeholder as the label. A placeholder disappears on type and is not a name for the field.
  • Animating font-size and top separately. Scale plus translate is enough, and the raised size is 15 × 0.73 ≈ 11px.
  • Putting the error inside the relative field. It changes the box the label is positioned against.
  • Copying the whole map from contact-split-map-form when the screen is only a form.

Details

Palette
Type
Newsreader · Outfit
Motion
Subtle motion
Build
Quick build
Designed bySusan AcharyaKathmandu

Studied

Tailwind CSS docs

A control. It studies this site for quiet, not for a show.

All sources