Name tag

No dependencies
Code format
React + Tailwind
import { useId, useState } from 'react';

const css = `
  @keyframes field-nametag-write { from { clip-path: inset(-20% 100% -20% -20%); } to { clip-path: inset(-20% -20% -20% -20%); } }
  @keyframes field-nametag-slap-a { 0% { transform: translateY(-3px) rotate(-1deg) scale(1.02); } 45% { transform: rotate(-2deg) scale(0.97); } 100% { transform: rotate(-2deg); } }
  @keyframes field-nametag-slap-b { 0% { transform: translateY(-3px) rotate(-1deg) scale(1.02); } 45% { transform: rotate(-2deg) scale(0.97); } 100% { transform: rotate(-2deg); } }
  .field-nametag { width: 270px; padding: 6px; }
  /* A white sticker in both themes; the band and marker ink stay dark on it. */
  /* The shadow is a drop-shadow on this wrapper so it follows the curled corner. */
  .field-nametag__lift {
    border-radius: 14px;
    filter: drop-shadow(0 1px 1px light-dark(rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.25))) drop-shadow(0 3px 5px light-dark(rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.12)));
    transform: rotate(-2deg);
    transition: transform 0.35s cubic-bezier(0.3, 1.3, 0.5, 1), filter 0.35s;
  }
  .field-nametag__sticker {
    position: relative;
    overflow: hidden;
    border-radius: 14px;
    background: light-dark(#fbfcfd, #e5e6e9);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 100% 100%, 0 100%);
    transition: clip-path 0.3s;
  }
  .field-nametag__band { display: block; padding: 10px 0 7px; background: light-dark(#42454c, #393c41); color: light-dark(#fbfcfd, #f1f2f3); text-align: center; cursor: text; }
  .field-nametag__hello { display: block; font: 800 24px/1 system-ui, sans-serif; letter-spacing: 0.14em; }
  .field-nametag__sub { display: block; margin-top: 3px; font-size: 12px; letter-spacing: 0.04em; }
  .field-nametag__write { position: relative; height: 70px; }
  .field-nametag__input, .field-nametag__mirror {
    box-sizing: border-box;
    width: 100%;
    height: 100%;
    padding: 0 16px;
    font: 400 32px/70px 'Marker Felt', 'Chalkboard SE', 'Comic Sans MS', 'Segoe Print', cursive;
    font-kerning: none;
    font-variant-ligatures: none;
    text-align: center;
  }
  .field-nametag__input { position: relative; display: block; margin: 0; border: 0; outline: none; background: transparent; color: transparent; caret-color: light-dark(#373a40, #232529); }
  .field-nametag__input::placeholder { color: light-dark(#cbced3, #7d8087); }
  .field-nametag__input::selection { background: light-dark(#dbdde1, #a7abb2); }
  .field-nametag__mirror { position: absolute; inset: 0; overflow: hidden; color: light-dark(#373a40, #232529); white-space: pre; pointer-events: none; }
  .field-nametag__ch { display: inline-block; }
  .field-nametag__ch--new { animation: field-nametag-write 0.24s ease-out; }
  .field-nametag__foot { height: 10px; background: light-dark(#42454c, #393c41); }
  /* While you write, the sticker lifts and its corner curls; it presses flat once you leave it. */
  .field-nametag__curl { position: absolute; right: 0; bottom: 0; width: 0; height: 0; background: linear-gradient(135deg, light-dark(#cbced3, #a7abb2), light-dark(#ebedf0, #393c41)); clip-path: polygon(0 0, 100% 0, 0 100%); transition: width 0.3s, height 0.3s; }
  .field-nametag__lift:focus-within { transform: translateY(-3px) rotate(-1deg); filter: drop-shadow(0 2px 2px light-dark(rgba(0, 0, 0, 0.06), rgba(0, 0, 0, 0.2))) drop-shadow(0 10px 10px light-dark(rgba(0, 0, 0, 0.06), rgba(0, 0, 0, 0.2))); outline: 2px solid light-dark(#5f626a, #a7abb2); outline-offset: 4px; }
  .field-nametag__lift:focus-within .field-nametag__sticker { clip-path: polygon(0 0, 100% 0, 100% calc(100% - 22px), calc(100% - 22px) 100%, 0 100%); }
  .field-nametag__lift:focus-within .field-nametag__curl { width: 22px; height: 22px; }
  .field-nametag__lift[data-slap='a'] { animation: field-nametag-slap-a 0.4s ease-out; }
  .field-nametag__lift[data-slap='b'] { animation: field-nametag-slap-b 0.4s ease-out; }
  @media (prefers-reduced-motion: reduce) {
    .field-nametag__lift, .field-nametag__sticker, .field-nametag__curl { transition: none; }
    .field-nametag__ch--new, .field-nametag__lift[data-slap] { animation: none; }
  }
`;

export function NametagInput() {
  const id = useId();
  const [v, setV] = useState('');
  const [grew, setGrew] = useState(false);
  const [slap, setSlap] = useState<'a' | 'b' | undefined>();
  return (
    <>
      <style>
        {css}
      </style>
      <div className="field-nametag">
        <div className="field-nametag__lift" data-slap={slap}>
          <div className="field-nametag__sticker">
            <label className="field-nametag__band" htmlFor={id}>
              <span className="field-nametag__hello">HELLO</span>
              <span className="field-nametag__sub">my name is</span>
            </label>
            <div className="field-nametag__write">
              <span className="field-nametag__mirror" aria-hidden="true">
                {[...v].map((ch, i) => (
                  <span key={`${i}${ch}`} className={`field-nametag__ch${grew && i === v.length - 1 ? ' field-nametag__ch--new' : ''}`}>
                    {ch}
                  </span>
                ))}
              </span>
              <input
                id={id}
                className="field-nametag__input"
                value={v}
                maxLength={14}
                placeholder="Your name"
                autoComplete="given-name"
                spellCheck={false}
                onChange={(e) => {
                  setGrew(e.target.value.length > v.length);
                  setV(e.target.value);
                }}
                onBlur={() => v.trim() && setSlap((s) => (s === 'a' ? 'b' : 'a'))}
              />
            </div>
            <div className="field-nametag__foot" />
            <span className="field-nametag__curl" aria-hidden="true" />
          </div>
        </div>
      </div>
    </>
  );
}

Free to use in any project, commercial included. MIT-0, no attribution required. Licence

Install with shadcn

npx shadcn@latest add @movin/input-nametag
Source on GitHub

About this component

A "Hello, my name is" sticker: your name is written on in marker one letter at a time, and the sticker lifts at the corner while you type and presses flat when you are done.

Dependencies
None
Difficulty
Intermediate
Triggers
state change, focus, keyframe
Techniques
keyframes, clip path, filter
Reduced motion
Respected
Theme
Light and dark (follows your page's color-scheme)

The "Hello, my name is" band is the input's <label>, and the mirror is aria-hidden. Focus outlines the sticker. Under prefers-reduced-motion it lifts and writes without motion.

More inputs

See all
Piggy bank
Price tag

Up to 5 kg

Kitchen scale
Skywriter