GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

css floating label 9가지 — 입력칸 라벨이 스프링·관성으로 움직이는 폼

사용자가 입력칸을 누르는 0.3초 안에 css floating label 이 어떻게 올라가느냐가 폼의 첫인상을 정한다 — 01번 스프링과 02번 노치가 그 0.3초를 계산으로 채운다.

아홉 가지 플로팅 라벨을 화면에서 바로 눌러 본다

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

css floating label 9가지 — 스프링·측정·보간으로 라벨이 살아난다

장면 하나는 폭 최대 440px 의 카드에 입력칸 두세 개를 세로로 쌓은 모양이다. 칸 높이는 52px, 칸 사이는 16px, 카드 안쪽 여백은 20px 이라 칸이 셋인 장면은 카드 높이가 228px, 둘인 장면은 160px 이 된다. 480×300 무대에서는 카드가 가운데에 놓이고, 높이 250px 이하의 폰 화면에서는 3번째 칸(06 은 2번째 칸)을 숨긴다.

아홉 장면에는 CSS transition 도 @keyframes 도 하나도 없다. 라벨 이동·윤곽선 틈·그림자는 모두 requestAnimationFrame 안에서 계산한 값이고, 시연은 2초 주기의 계획표가 칸에 글자를 넣으며 첫 변화는 200ms 에 일어난다. 사람이 칸을 누르거나 Tab 으로 들어가 포커스가 오면 시연이 멈추고 진짜 포커스와 input 이벤트가 이어받는다.

순서는 라벨을 움직이는 힘이 어디서 나오느냐로 잡았다. 01·04·07·09 는 속도를 쥔 스프링이고, 02·03·05·08 은 목표를 향해 프레임마다 일정 비율씩 다가가는 지수 접근(approach)이다. 06 은 라벨이 지수 접근이고 흔들림만 진폭이 줄어드는 사인을 곧바로 계산한다. 라벨이 15px 올라가며 배율 0.74 로 줄어드는 기본 동작은 같고(02 만 26px·0.78), 그 위에 얹은 한 겹이 장면마다 다르다.

회원가입 폼이면 01 부터, 윤곽선 스타일 폼이면 02 부터, 검증 오류 피드백이 목적이면 06 부터 보면 된다. 입력칸 하나하나의 모양이 궁금하면 입력창 디자인 9가지 — 복붙하면 되는 폼 위젯을, 오류 문구를 언제 띄울지는 폼 유효성 검사 UI 9종 — 입력하는 순간 판정하는 오류 안내를 같이 열어 두면 좋다.

css floating label input 을 기법별 코드로 본다

코드는 장면마다 index.html 의 <script> 안에서 이어진 줄만 가져왔고 조각은 25줄 이하다. css floating label input 이라 부르는 움직임은 대부분 여기 실린 계산 위에 라벨 한 겹을 얹은 것이라, 값이 어디서 나오는지부터 읽으면 고칠 자리가 보인다.

01넘쳤다가 자리 잡는 감쇠 스프링 라벨

포커스가 가는 순간 라벨 위치를 스프링 방정식으로 적분하면 트윈 곡선 없이도 목표를 살짝 넘쳤다 돌아오는 움직임이 나온다. 라벨 높이와 크기가 같은 x 에서 나오므로 넘칠 때는 라벨이 더 높이 올라가며 더 작아지고, 이메일·이름 칸이 쌓이는 회원가입 폼에 맞다. 렌더 캡처에서 누적면적은 48.9%, 평균 강도는 76.0 이었고 23/23 프레임이 움직였다.

라벨이 감쇠 스프링으로 올라가며 살짝 넘쳤다 자리 잡는 라벨
새 창에서 열기

01 · 라벨이 감쇠 스프링으로 올라가며 살짝 넘쳤다 자리 잡는 라벨 — 측정 누적면적 48.867% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

STIFFNESSDAMPINGRISE_PXLABEL_SCALE
    function springStep(s, target, k, c, dt) {
        let left = dt;
        while (left > 0) {
            const h = Math.min(left, 1 / 120);
            s.v += (-k * (s.x - target) - c * s.v) * h;
            s.x += s.v * h;
            left -= h;
        }
    }

02윤곽선이 라벨 폭만큼만 열리는 노치 라벨

라벨을 getBoundingClientRect 로 재서 윤곽선 윗변의 틈을 그 폭에 맞추고 clip-path 로 잘라 내므로 글자가 길든 짧든 틈이 딱 맞는다. 틈 목표는 라벨 실측 폭에 0.78 을 곱하고 양쪽 여백 5px 씩을 더한 값이며, 머티리얼 느낌의 설정·프로필 폼에 어울린다. 누적면적 49.0%, 평균 강도 83.6 이고 프레임은 23/23 이 움직였다.

윤곽선이 라벨 실측 폭만큼만 열리는 노치형 라벨
새 창에서 열기

02 · 윤곽선이 라벨 실측 폭만큼만 열리는 노치형 라벨 — 측정 누적면적 48.998% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

NOTCH_PADGAP_LERPRADIUSBORDER_W
    function render() {
        fields.forEach(function (f) {
            const s = 1 - (1 - LABEL_SCALE) * f.p;
            f.label.style.transform = "translate3d(0," + (-RISE_PX * f.p).toFixed(2) + "px,0) scale(" + s.toFixed(3) + ")";
            f.box.style.setProperty("--p", f.p.toFixed(3));
            const x0 = LABEL_X - NOTCH_PAD;
            const x1 = x0 + f.gap;
            const cut = f.gap > 0.5 ? "polygon(0 0," + x0.toFixed(1) + "px 0," + x0.toFixed(1) + "px " + (BORDER_W + 1) + "px," + x1.toFixed(1) + "px " + (BORDER_W + 1) + "px," + x1.toFixed(1) + "px 0,100% 0,100% 100%,0 100%)" : "none";
            f.outline.style.clipPath = cut;
        });
    }

03밑줄의 빛 점이 캐럿을 관성으로 따라간다

캐럿 앞 글자 폭을 숨은 거울 요소에 넣어 재고, 밑줄 위 빛 점이 그 x 좌표를 지수 감쇠로 따라가므로 타이핑이 빠를수록 점이 뒤처지는 것이 눈에 보인다. 점이 목표 위치에서 멀수록 밑줄 꼬리의 불투명도가 올라가도록 계산해서 검색창이나 한 줄 메모 입력에 얹기 좋다. 누적면적 27.3% 로 아홉 중 가장 작고 평균 강도는 43.0 이며 23/23 프레임에서 움직임이 잡혔다.

밑줄이 캐럿 위치를 관성으로 따라다니는 입력칸
새 창에서 열기

03 · 밑줄이 캐럿 위치를 관성으로 따라다니는 입력칸 — 측정 누적면적 27.345% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

LERPUNDERLINE_HCARET_OFFSETTRAIL_FADE
    function caretX(f) {
        const at = f.input.selectionStart;
        const n = at === null ? f.input.value.length : at;
        f.mirror.textContent = f.input.value.slice(0, n);
        return INPUT_PAD + f.mirror.getBoundingClientRect().width + CARET_OFFSET;
    }

04라벨 글자가 해시 지연으로 한 글자씩 떠오르는 물결

라벨을 글자별 <span> 으로 쪼개고 글자마다 hash(i × 17.3 + fi × 91.7 + 7) 로 만든 지연을 준 뒤 각자 스프링으로 올라가므로 라벨이 한 덩어리가 아니라 물결처럼 일어선다. 「이름」 두 글자는 239·186ms, 「이메일 주소」 여섯 글자는 255·127·180·86·54·179ms 에 출발하고, 라벨 요소 자체는 그대로 <label for> 라서 랜딩 페이지의 뉴스레터 구독 칸에 써도 이름은 한 문장으로 남는다. 누적면적 32.6%, 평균 강도 66.7, 움직인 프레임 23/23 이 잡혔다.

라벨 글자가 해시 지연으로 한 글자씩 떠오르는 물결 라벨
새 창에서 열기

04 · 라벨 글자가 해시 지연으로 한 글자씩 떠오르는 물결 라벨 — 측정 누적면적 32.578% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

STAGGER_MSWAVE_AMPNOISE_SEEDSETTLE
    function step(dt, ts) {
        fields.forEach(function (f) {
            f.chars.forEach(function (c) {
                c.t += dt;
                if (!c.fired && c.t >= c.wait) {
                    c.fired = true;
                    c.goal = f.up ? -RISE_PX : 0;
                    c.sp.v += (f.up ? -1 : 1) * WAVE_AMP;
                }
                springStep(c.sp, c.goal, SETTLE, DAMP, dt);
            });
        });
    }

05입력 길이만큼 윤곽선이 한 바퀴 채워지는 진행 링

<svg> 안 rect 의 둘레를 getTotalLength 로 재서 stroke-dashoffset 을 글자 수 비율로 보간하므로 최대 글자 수까지 얼마가 남았는지가 윤곽선 자체로 보인다. 칸 폭 400px 에서 둘레는 약 880.8px 이고 카드가 254px 로 줄면 약 589.6px 이 되며, 24자를 채우면 테두리가 민트로 바뀌어 닉네임·한 줄 소개처럼 글자 수 제한이 있는 칸에 맞다. 누적면적 48.7%, 평균 강도 102.0 으로 아홉 중 가장 세며 23/23 프레임이 움직였다.

입력 길이에 따라 윤곽선이 한 바퀴 채워지는 진행 링 칸
새 창에서 열기

05 · 입력 길이에 따라 윤곽선이 한 바퀴 채워지는 진행 링 칸 — 측정 누적면적 48.748% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

MAX_LENRING_LERPSTROKE_WSTART_FILL
    function render() {
        fields.forEach(function (f) {
            const s = 1 - (1 - LABEL_SCALE) * f.p;
            f.label.style.transform = "translate3d(0," + (-RISE_PX * f.p).toFixed(2) + "px,0) scale(" + s.toFixed(3) + ")";
            f.box.style.setProperty("--p", f.p.toFixed(3));
            f.rect.style.strokeDashoffset = (f.perimeter * (1 - f.fill)).toFixed(2);
            if (f.fill > 0.995) f.box.setAttribute("data-full", "");
            else f.box.removeAttribute("data-full");
        });
    }

06오류가 나면 감쇠 사인으로 흔들리고 안내 색이 켜진다

칸을 떠날 때 값이 비어 있지 않고 이메일 모양이 아니면 aria-invalid 와 aria-describedby 를 걸고, 진폭 9px 사인 흔들림을 프레임마다 0.9 의 거듭제곱으로 줄여 한 번 거절된 느낌을 준다. 라벨 색과 오류 문구가 같은 순간에 바뀌고 올바르게 고치면 둘 다 빠지므로 로그인·결제 폼의 검증 피드백에 쓸 만하다. 누적면적 34.0%, 평균 강도 57.2 이고 움직인 프레임은 22/23 에 그쳤는데 이유는 확인하지 않았다.

오류가 나면 감쇠 사인으로 흔들리고 라벨이 안내 색으로 바뀌는 칸
새 창에서 열기

06 · 오류가 나면 감쇠 사인으로 흔들리고 라벨이 안내 색으로 바뀌는 칸 — 측정 누적면적 34.009% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

SHAKE_AMPSHAKE_DECAYSHAKE_FREQERR_HOLD
    function shakeStep(s, dt) {
        s.amp *= Math.pow(SHAKE_DECAY, dt * 60);
        s.phase += SHAKE_FREQ * dt;
        return s.amp * Math.sin(s.phase);
    }
    function setErr(f, bad) {
        if (f.err === bad) return;
        f.err = bad;
        if (bad) {
            f.amp = SHAKE_AMP;
            f.phase = 0;
            f.flash = 1;
            f.box.setAttribute("data-err", "");
            f.input.setAttribute("aria-invalid", "true");
            if (f.msg) f.input.setAttribute("aria-describedby", f.msg.id);
        } else {
            f.box.removeAttribute("data-err");
            f.input.removeAttribute("aria-invalid");
            f.input.removeAttribute("aria-describedby");
            f.flash = 0;
        }
        if (reduce) render();
    }

07포커스하면 그림자가 스프링으로 솟는 카드 칸

높이 값 e 하나로 두 겹 box-shadow 의 오프셋·번짐·불투명도를 함께 계산하고, 라벨 상승과 칸이 3px 위로 뜨는 양도 같은 e 에서 나온다. e 가 0 이면 1.0px/2.0px/0.10 한 겹(세로 오프셋·번짐·불투명도 순)이고 1 이면 8.2px/20.4px/0.26 에 18.0px/61.2px/0.224 를 더한 두 겹이라, 대시보드의 설정·필터 입력에 얹기 좋다. 측정값은 누적면적 45.4%, 평균 강도 54.7, 23/23 프레임이다.

포커스하면 그림자 높이가 스프링으로 솟는 카드형 입력칸
새 창에서 열기

07 · 포커스하면 그림자 높이가 스프링으로 솟는 카드형 입력칸 — 측정 누적면적 45.378% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

ELEVATIONSHADOW_BLUR_KSPRING_KTINT_ALPHA
    function shadowOf(e) {
        const h = clamp(e, 0, 1.4);
        const y1 = 1 + h * ELEVATION * 0.4;
        const b1 = 2 + h * ELEVATION * 0.6 * SHADOW_BLUR_K;
        const a1 = 0.1 + h * TINT_ALPHA;
        const y2 = h * ELEVATION;
        const b2 = h * ELEVATION * 2 * SHADOW_BLUR_K;
        const a2 = h * TINT_ALPHA * 1.4;
        return "0 " + y1.toFixed(1) + "px " + b1.toFixed(1) + "px rgba(23,20,26," + a1.toFixed(3) + "), 0 " + y2.toFixed(1) + "px " + b2.toFixed(1) + "px rgba(95,70,200," + a2.toFixed(3) + ")";
    }

08줄이 늘면 부드럽게 자라고 라벨은 위에 떠 있는 텍스트 영역

scrollHeight 로 필요한 높이를 재서 목표를 정하고 현재 높이가 거기로 관성 보간되므로 줄이 늘어도 화면이 덜컥 뛰지 않는다. 박스는 52px 에서 4줄 한도인 124px 까지 자라고 그 위는 스크롤이 생기며, 문의하기·후기 작성 폼에 맞다. 누적면적은 59.7%, 평균 강도는 71.6 이고 프레임은 23/23 이 움직였다.

줄 수에 따라 부드럽게 자라고 라벨은 칸 위쪽에 떠 있는 텍스트 영역
새 창에서 열기

08 · 줄 수에 따라 부드럽게 자라고 라벨은 칸 위쪽에 떠 있는 텍스트 영역 — 측정 누적면적 59.731% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

MAX_ROWSGROW_LERPLINE_HPAD_TOP
    function wanted(f) {
        if (!f.grow) return MIN_H;
        const ta = f.input;
        ta.style.height = "0px";
        const need = ta.scrollHeight;
        ta.style.height = "";
        const cap = PAD_TOP + PAD_BOTTOM + MAX_ROWS * LINE_H;
        ta.style.overflowY = need > cap ? "auto" : "hidden";
        return clamp(need, MIN_H, cap);
    }

09쉐브론이 스프링으로 돌고 값을 고르면 라벨이 올라가는 셀렉트

열림 정도 x 하나에서 쉐브론 각도·목록 높이·라벨 상승이 모두 파생되므로 열림·닫힘과 라벨이 한 상태로 맞물린다. 진짜 <select> 가 아니라 <button role="combobox"> 와 role="listbox" 목록이라 국가·직군 고르기 같은 선택 칸에 쓰고, 키보드는 방향키와 Escape 를 keydown 으로, Enter·Space 는 버튼 click 으로 받는다. 누적면적 62.6% 로 아홉 중 가장 넓고 평균 강도 44.0, 움직인 프레임은 21/23 이다.

셰브런이 스프링으로 돌고 선택하면 라벨이 올라가는 셀렉트 칸
새 창에서 열기

09 · 셰브런이 스프링으로 돌고 선택하면 라벨이 올라가는 셀렉트 칸 — 측정 누적면적 62.578% · 움직인 프레임 21/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

CHEV_STIFFOPEN_DEGPICK_DELAYOPT_H
    function render() {
        fields.forEach(function (f) {
            const x = f.sp.x;
            const o = clamp(x, 0, 1);
            const r = f.sel >= 0 ? Math.max(1, clamp(x, 0, 1.25)) : clamp(x, 0, 1.25);
            const s = 1 - (1 - LABEL_SCALE) * r;
            f.label.style.transform = "translate3d(0," + (-RISE_PX * r).toFixed(2) + "px,0) scale(" + s.toFixed(3) + ")";
            f.chev.style.transform = "rotate(" + (OPEN_DEG * x).toFixed(1) + "deg)";
            f.box.style.setProperty("--p", clamp(r, 0, 1).toFixed(3));
            const h = (OPT_H * f.opts.length + 2 * MENU_PAD) * o;
            f.menu.style.height = h.toFixed(1) + "px";
            f.menu.style.opacity = clamp(o * 1.6, 0, 1).toFixed(3);
            f.menu.style.visibility = h < 1 ? "hidden" : "visible";
        });
    }

값이 라벨의 인상을 바꾸는 곳 — 강성·감쇠·보간 비율

값 하나로 인상이 크게 달라지는 곳은 셋이다. 아래 숫자는 소스의 springStep·approach·shakeStep 를 노드에서 60fps 고정 스텝으로 직접 불러 센 값이라, 브라우저 렌더로 잰 표와는 구분한다.

강성을 올리면 빨라지는 대신 넘침이 두 배가 된다

01 의 STIFFNESS 를 170 에서 320 으로 올리면(감쇠 14 고정, 감쇠비 0.54 에서 0.39) 라벨이 15px 위에 처음 닿는 시각은 200ms 에서 117ms 로 앞당겨진다. 대신 넘침이 12.4%(최고점 16.9px)에서 25.1%(18.8px)로 커지고, 목표 0.3px 안에 머무는 시각은 약 433ms 에서 약 467ms 로 오히려 늦는다.

보간 비율을 낮추면 점의 꼬리가 길어진다

03 의 LERP 를 0.18 에서 0.06 으로 낮추면 120px 점프의 절반 지점은 67ms 에서 200ms, 1px 안에 들어오는 시각은 417ms 에서 1300ms 로 늘어난다. 빠르게 치면 점이 글자보다 한참 뒤처진다.

감쇠를 올리면 흔들림이 입력을 방해할 만큼 길어진다

06 의 SHAKE_DECAY 를 0.9 에서 0.97 로 올리면 진폭이 1px 아래로 내려오는 시각이 350ms 에서 1217ms, 1px 을 넘는 좌우 흔들림이 5번에서 19번으로 늘어난다. 거절된 느낌은 세지지만 그만큼 오래 남는다.

렌더 캡처로 잰 아홉 장면의 값은 이 표에 있다. 가장 작은 장면은 03 의 27.3% 이고 가장 큰 장면은 09 의 62.6% 다. 움직인 프레임이 23 에 못 미친 장면은 06 과 09 둘뿐이다.

항목 누적면적 평균 강도 움직인 프레임
01 스프링 라벨 48.9% 76.0 23/23
02 노치 틈 49.0% 83.6 23/23
03 캐럿 밑줄 27.3% 43.0 23/23
04 글자 물결 32.6% 66.7 23/23
05 진행 링 48.7% 102.0 23/23
06 오류 흔들림 34.0% 57.2 22/23
07 그림자 높이 45.4% 54.7 23/23
08 자라는 영역 59.7% 71.6 23/23
09 셀렉트 62.6% 44.0 21/23

어디서 깨지는가 — 자동완성·긴 라벨·움직임 줄이기 설정

자동완성이 값을 채워도 라벨이 안 올라갈 수 있다

01~08 의 isFilled() 는 value 가 비었는지에 더해 :-webkit-autofill 선택자도 try/catch 로 확인한다. 소스 주석은 자동완성이 값을 채울 때 input 이벤트가 없을 수 있어서 함께 본다고 적는다. 09 에도 같은 함수가 정의돼 있지만 호출은 없고, 01~08 의 확인 시점은 포커스·입력·크기 변화로 retarget() 가 불릴 때이며 주기적 확인 코드는 없다. 값이 이벤트 없이 바뀌는 경로는 직접 열어 확인해 보는 것이 안전하다.

라벨이 길면 02 의 틈이 칸을 넘어간다

틈은 라벨 실측 폭 × 0.78 + 10px 이라 번역된 라벨이 길어지면 틈이 칸 폭을 넘어 윤곽선이 거의 사라진다. 글꼴이 늦게 들어오면 measure() 시점의 폭이 틀려지므로 document.fonts.ready 와 ResizeObserver 가 폭을 다시 잰다.

이메일 칸의 type 이 03 을 멈춘다

type="email" 칸은 selectionStart 가 null 이고 setSelectionRange 는 예외를 던진다. 처음 이메일 칸을 그렇게 두었더니 시연 루프가 1초 뒤 이 예외로 멈춰 움직인 프레임이 8/23 이었고, 지금은 type="text" 에 inputmode="email" 을 둔다. 캐럿 위치를 읽어야 하는 칸이면 같은 처방이 필요하다.

글자 수는 UTF-16 길이로 센다

05 의 채움은 value.length 로 센다. 소스로 확인한 것은 여기까지이고, 이 값이 UTF-16 코드 유닛 수라 이모지나 일부 한자가 둘로 세어진다는 것은 집필 때 정리한 조사 메모에서 가져온 설명이다. 조합 중인 글자도 value 에 들어가 있어 폭과 글자 수에 잡히고, 상한은 진짜 maxLength 속성이 맡는다.

움직임 줄이기에서는 색과 문구가 오류를 맡는다

06 은 prefers-reduced-motion 일 때 흔들림 오프셋만 0 으로 두고 data-err 와 aria-* 는 그대로 바꾼다. 색만으로 오류를 알리지 않도록 문구가 같이 바뀐다.

height 를 직접 보간하면 아래 칸이 밀린다

height 를 직접 보간하는 곳은 08 의 박스와 09 의 목록 둘이다. 08 은 카드가 160px 에서 232px 로 자라 아래가 밀리므로 scrollHeight 를 입력·폭 변화 때만 재고 그 사이 프레임은 숫자 계산만 하며, 09 의 목록은 position: absolute 라 아래를 밀지 않는다.

윗변에 걸친 라벨에는 오버슛이 새어 나간다

01 은 라벨이 최고 16.9px 까지 올라가도 위쪽 여백 24px 안에 머문다. 02 는 라벨이 윤곽선 윗변에 걸치는 구성이라 스프링의 오버슛이 선 밖으로 나가고, 그래서 지수 접근을 쓴다.

목록의 mousedown 을 막지 않으면 클릭이 닿지 않는다

09 는 목록 위 mousedown 의 preventDefault 를 빼면 버튼이 포커스를 잃어 blur 로 목록이 닫히고 클릭이 항목에 닿지 않는다. 아홉 파일 가운데 preventDefault 를 쓰는 곳은 09 뿐이다.

직접 고쳐 보며 경계를 확인하는 법

01 의 STIFFNESS, 03 의 LERP, 06 의 SHAKE_DECAY 를 하나씩 바꿔 보면 위 숫자가 어디서 바뀌는지 손으로 확인된다. 내려받는 압축 파일의 비밀번호는 f8c3a4n6 이고, 안에 아홉 장면의 HTML·SCSS 와 리액트 판이 들어 있다.

변형 세 가지와 접근성

이름 값 느낌
스프링 강성 바꾸기 01 STIFFNESS 170 → 320 처음 닿는 시각이 200ms 에서 117ms 로 빨라지지만 넘침이 12.4% 에서 25.1% 로 두 배가 된다.
밑줄 보간 비율 바꾸기 03 LERP 0.18 → 0.06 1px 안에 들어오는 시각이 417ms 에서 1300ms 로 늘어 점이 글자를 한참 뒤따른다.
흔들림 감쇠 바꾸기 06 SHAKE_DECAY 0.9 → 0.97 진폭 1px 아래까지 350ms 에서 1217ms 로, 1px 넘는 흔들림은 5번에서 19번으로 늘어난다.

변형 표의 수치도 위 세 절과 같은 노드 호출에서 나왔다.

입력이 있는 01~08 은 진짜 <input>(08 은 <textarea>)이고 keydown 핸들러도 preventDefault 도 없어서 타이핑을 막는 키가 없다. 09 만 keydown 한 개가 ArrowDown·ArrowUp 에서만 preventDefault 를 부르고, aria-expanded·aria-activedescendant·aria-selected 도 09 에만 있다. 아홉 장면의 라벨은 모두 <label for> 로 칸에 이어지고 aria-live 는 어느 파일에도 없으며, 포커스 표시는 :focus-visible 로 2px 외곽선이 뜬다.

prefers-reduced-motion 이 켜져 있으면 아홉 장면 모두 requestAnimationFrame 루프를 시작하지 않고, 입력이 들어오면 보간 없이 목표 값을 곧바로 그린다. 09 는 x 를 0 과 1 로 바로 놓고 닫기도 즉시 한다.

pure css floating label 만으로 충분한가?

라벨이 비었을 때와 값이 있을 때 두 상태를 오가는 데 그치는 칸이면 이 편의 계산은 과하다. 아홉 장면이 CSS transition 과 @keyframes 를 0 개로 두고 JS 로 계산한 것은 길이를 재야 하거나 값 하나에서 여러 속성을 파생해야 해서다. 02 의 틈은 라벨 실측 폭이, 03 은 캐럿 앞 글자 폭이, 05 는 둘레 길이가, 08 은 scrollHeight 가 필요하다.

이 편 코드를 floating label css codepen 에서 찾은 조각과 견줄 때는 칸이 비었는지 판단하는 isFilled() 와 라벨을 옮기는 render() 가 갈라져 있는지를 보면 된다. 갈라져 있으면 오른쪽 계산만 바꿔 끼울 수 있다. 입력 글자 수를 세는 쪽이 목적이면 textarea 글자수 카운터 9가지 — 자소 단위로 세고 한도를 링으로 보여 주는 입력창이, 값 모양을 알리는 쪽이면 비밀번호 강도 UI 9종 — 강도 값이 계산되는 아홉 가지 방식이 이어진다.

css form floating label 을 가져다 쓰기 전에 자주 묻는 질문

css floating label input 의 라벨이 올라가는 높이와 크기는 어디서 바꾸나?

값은 장면마다 <script> 의 const 줄에 모여 있어 그 숫자만 바꾸면 된다. 올라가는 높이는 RISE_PX(02 만 26), 줄어드는 배율은 LABEL_SCALE 이고, 칩에 적힌 이름이 그 장면에서 바꿔 볼 값이다.

키보드나 움직임 줄이기 설정에서도 쓸 수 있나?

입력칸은 키 입력을 막지 않고 09 의 목록만 방향키·Escape·Enter·Space 로 움직인다. 움직임 줄이기가 켜지면 루프를 시작하지 않고 결과만 그리며, 06 의 오류는 색과 문구로 남는다.

라벨이 훨씬 길어지면 어떻게 되나?

02 는 틈이 라벨 폭을 따라 넓어지다가 칸 폭을 넘으면 윤곽선이 거의 사라지고, 04 는 글자 수만큼 지연이 생겨 물결이 길어진다. 번역 라벨을 쓸 계획이면 가장 긴 문구로 한 번 열어 보고 넣는다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.