07Menu người dùng thu gọn
Phần tên người dùng dính bên cạnh avatar bên phải gập lại theo chiều rộng khi cuộn, chỉ còn icon avatar.
Dùng choHeader của app dashboard, thanh trên cùng của console quản trị
max-width 축소텍스트 clip아바타만 유지

<main class="stage">
<header class="head is-demo">
<span class="head__logo">GODRICH</span>
<div class="head__user">
<span class="head__name">Alex Kim</span>
<span class="head__avatar" aria-hidden="true"></span>
</div>
</header>
<div class="page">
<p class="page__line"></p>
<p class="page__line"></p>
<p class="page__line short"></p>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개. 변형은 이것만 바꾼다 ──────────────────────────
$duration: $dur-base; // 이름이 접히는 시간
$easing: $ease-out;
$color: $subject-cream; // 아바타 색
// ── 데모 무대 ──────────────────────────────────────────
html, body { margin: 0; overflow: hidden; }
.stage {
min-height: 100vh;
margin: 0;
background: $stage-orange;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.head {
position: sticky;
top: 0;
height: 56px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 clamp(16px, 5vw, 24px);
background: $stage-orange;
&__logo { color: $subject-ink; font-weight: 800; font-size: 18px; line-height: 1; font-family: inherit; letter-spacing: .04em; } // 주황 헤더 위 글자 — AA 4.5 ($color 크림은 3.11:1)
&__user { display: flex; align-items: center; }
&__avatar { width: 32px; height: 32px; border-radius: 50%; background: $color; flex: 0 0 auto; }
// 폭까지 접어야 아바타가 실제로 왼쪽으로 붙는다 — 텍스트 하나에만 한정
&__name {
display: inline-block;
max-width: 104px;
margin-right: $sp-2;
overflow: hidden;
white-space: nowrap;
color: $subject-ink; // 주황 헤더 위 글자 — AA 4.5
font-weight: 600;
font-size: 13px;
line-height: 32px;
font-family: inherit;
opacity: 1;
transform: scaleX(1);
transform-origin: left; // 규칙8: 이름 텍스트 좌측 기준 접힘
transition: max-width $duration $easing, margin-right $duration $easing,
opacity $duration $easing, transform $duration $easing;
}
// 실제 사용: 스크롤하면 이름이 접히고 아바타만 남는다
&.is-scrolled &__name { max-width: 0; margin-right: 0; opacity: 0; transform: scaleX(0); }
}
// ── 데모(.is-demo): 이름이 접혔다 펼쳐졌다 2초 루프 ──────
.head.is-demo .head__name {
animation: name-collapse $dur-loop $easing infinite;
}
@keyframes name-collapse {
0%, 15% { max-width: 104px; margin-right: $sp-2; opacity: 1; transform: scaleX(1); }
45%, 70% { max-width: 0; margin-right: 0; opacity: 0; transform: scaleX(0); }
95%, 100% { max-width: 104px; margin-right: $sp-2; opacity: 1; transform: scaleX(1); }
}
// ── 배경: 문서처럼 보이는 자리 ─────────────────────────
.page { padding: $sp-6 $sp-6 0; }
.page__line {
height: 12px;
margin: 0 0 $sp-3;
border-radius: $r-xs;
background: rgba($stage-paper, .28);
&.short { width: 60%; }
}
// ── 접근성: 움직임 줄이기 설정이면 즉시 상태만 바뀐다 ────────
@media (prefers-reduced-motion: reduce) {
.head__name { transition: none; }
.head.is-demo .head__name { animation: none !important; max-width: 104px; margin-right: $sp-2; opacity: 1; transform: scaleX(1); }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
min-height: 100vh;
margin: 0;
background: #ff4d1f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.head {
position: sticky;
top: 0;
height: 56px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 clamp(16px, 5vw, 24px);
background: #ff4d1f;
}
.head__logo {
color: #17141a;
font-weight: 800;
font-size: 18px;
line-height: 1;
font-family: inherit;
letter-spacing: 0.04em;
}
.head__user {
display: flex;
align-items: center;
}
.head__avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: #fff7e6;
flex: 0 0 auto;
}
.head__name {
display: inline-block;
max-width: 104px;
margin-right: 8px;
overflow: hidden;
white-space: nowrap;
color: #17141a;
font-weight: 600;
font-size: 13px;
line-height: 32px;
font-family: inherit;
opacity: 1;
transform: scaleX(1);
transform-origin: left;
transition: max-width 300ms ease-out, margin-right 300ms ease-out, opacity 300ms ease-out, transform 300ms ease-out;
}
.head.is-scrolled .head__name {
max-width: 0;
margin-right: 0;
opacity: 0;
transform: scaleX(0);
}
.head.is-demo .head__name {
animation: name-collapse 2s ease-out infinite;
}
@keyframes name-collapse {
0%, 15% {
max-width: 104px;
margin-right: 8px;
opacity: 1;
transform: scaleX(1);
}
45%, 70% {
max-width: 0;
margin-right: 0;
opacity: 0;
transform: scaleX(0);
}
95%, 100% {
max-width: 104px;
margin-right: 8px;
opacity: 1;
transform: scaleX(1);
}
}
.page {
padding: 24px 24px 0;
}
.page__line {
height: 12px;
margin: 0 0 12px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.28);
}
.page__line.short {
width: 60%;
}
@media (prefers-reduced-motion: reduce) {
.head__name {
transition: none;
}
.head.is-demo .head__name {
animation: none !important;
max-width: 104px;
margin-right: 8px;
opacity: 1;
transform: scaleX(1);
}
}// 실제 페이지: 스크롤을 40px 넘게 내리면 사용자 이름이 접히고 아바타만 남는다 (7줄)
var head = document.querySelector('.head');
window.addEventListener('scroll', function () {
head.classList.toggle('is-scrolled', window.scrollY > 40);
}, { passive: true });import { useEffect, useState } from "react";
import type { CSSProperties } from "react";
import styles from "./UserMenuHeader.module.scss";
export type UserMenuHeaderProps = {
logo: string;
name: string;
/** 이름이 접히는 시간 (기본 300ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function UserMenuHeader({
logo,
name,
duration = 300,
easing = "ease-out",
color = "#fff7e6",
}: UserMenuHeaderProps) {
const [scrolled, setScrolled] = useState(false);
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
useEffect(() => {
function onScroll() {
setScrolled(window.scrollY > 40);
}
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
return (
<header className={styles.head} style={vars}>
<span className={styles.logo}>{logo}</span>
<div className={styles.user}>
<span className={scrolled ? `${styles.name} ${styles.collapsed}` : styles.name}>
{name}
</span>
<span className={styles.avatar} aria-hidden="true" />
</div>
</header>
);
}07 Menu người dùng thu gọn — Diện tích đo được 0.298% · khung hình chuyển động 12/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 mẫu css sticky header — copy-paste là chạy
Bài viết chứa demo này
9 mẫu css sticky header — copy-paste là chạyCss sticky header là cách giữ hàng menu trên cùng bám mãi trên màn hình dù cuộn xuống đâu, chỉ một dòng position: sticky Xem tất cả demo trong bài này


