CSS 글래스모피즘 리퀴드 글래스 효과 9종
화면 위에 반투명한 유리판을 얹어 놓은 것처럼, 뒤 배경이 흐릿하게 비치면서 내용은 또렷하게 보이는 디자인을 CSS 글래스모피즘(리퀴드 글래스)이라고 부릅니다. 카드부터 내비게이션, 버튼, 토글까지 아홉 가지 유리 효과를 전부 한 줄 복붙으로 붙일 수 있게 모았습니다.
자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip
아홉 개는 인기 순이 아니라 화면에서 유리 재질을 마주치는 순서를 따라갑니다. 정지해 있어도 유리로 보이는 것(01 카드·02 내비게이션)에서 시작해, 누르거나 지나가야 반응하는 것(03 버튼·04 움직이는 배경)을 지나, 클릭으로 여닫는 것(05 모달)과 마우스를 얹어야 살아나는 것(06 커서 추적)을 거쳐, 마지막은 고르고 켜고 입력하는 컨트롤(07 탭·08 토글·09 인풋)로 끝납니다.
01프로스티드 카드
포트폴리오나 랜딩 페이지의 소개 카드에 backdrop-filter: blur()와 반투명 배경을 올리면, 뒤에 있는 색이 흐릿하게 비치면서 카드가 유리판처럼 떠 보입니다. 마우스를 올리면 카드가 살짝 커지고 그림자가 깊어집니다.
.fc-card {
background: rgba(255, 255, 255, .14);
border: 1px solid rgba(255, 255, 255, .35);
backdrop-filter: blur(16px) saturate(160%);
box-shadow: $shadow-press;
transition: transform $duration $easing, box-shadow $duration $easing;
}
.fc-focal:hover {
transform: scale(1.03);
box-shadow: $shadow-raised;
}
02유리 내비게이션
반투명 유리 재질 내비게이션 바 안에서 지금 선택된 메뉴 뒤로 흐릿한 유리 알약이 translateX로 옮겨 다닙니다. 탭 폭을 고정값으로 둬서 어떤 메뉴를 눌러도 알약이 정확한 자리에 멈춥니다.
.gn-link { flex: 0 0 auto; width: $w; }
.gn-pill {
position: absolute;
width: $w;
background: rgba(255, 255, 255, .6);
transform: translateX(0);
transition: transform $duration $easing;
}
.gn-nav:has(.gn-link:nth-child(3):hover) .gn-pill {
transform: translateX(calc(#{$w} + #{$sp-2}));
}
03굴절 하이라이트 버튼
버튼 표면에 좁은 빛줄기 레이어를 올려 두고, 마우스를 올리면 그 빛줄기가 버튼을 가로질러 한 번 지나가 액체 유리가 굴절되듯 반짝입니다.
.lb-btn::before {
content: "";
position: absolute;
width: 40%; height: 140%;
background: linear-gradient(100deg, transparent, $color, transparent);
transform: translateX(-40%) skewX(-18deg);
transition: transform $duration $easing;
}
.lb-focal:hover::before {
transform: translateX(340%) skewX(-18deg);
}
04움직이는 블롭 배경
유리 패널 뒤에서 색이 다른 흐린 블롭 두 개가 transform으로 천천히 자리를 바꾸며 떠다니고, 패널의 backdrop-filter가 그 움직임을 흐릿하게 비춥니다.
.bb-blob { border-radius: 50%; filter: blur(26px); }
.bb-blob--a { animation: bb-drift-a $duration $easing infinite; }
@keyframes bb-drift-a {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(110px, 70px); }
}
05유리 모달
확인 버튼을 누르면 유리 재질 모달 패널이 opacity와 scale로 작게 접혀 있다가 커지며 나타나고, 뒤 화면은 흐릿하게 비칩니다.
.gm-modal {
opacity: 0;
transform: scale(.85);
transition: opacity $duration $easing, transform $duration $easing;
}
.gm-check:checked ~ .gm-modal {
opacity: 1;
transform: scale(1);
}
06마우스 따라 광택
커다란 유리 패널 위에서 마우스를 움직이면 좌표를 저장한 --mx·--my 커스텀 속성을 따라 타원형 광택이 실시간으로 따라다녀, 유리 표면을 문지르는 듯한 반사가 생깁니다.
panel.addEventListener('pointermove', function (e) {
var r = panel.getBoundingClientRect();
var x = ((e.clientX - r.left) / r.width * 100) + '%';
var y = ((e.clientY - r.top) / r.height * 100) + '%';
panel.style.setProperty('--mx', x);
panel.style.setProperty('--my', y);
});
.rh-panel::before {
background: radial-gradient(120px 80px at var(--mx) var(--my), $color 0%, transparent 70%);
}
07유리 탭
유리 재질 탭 그룹 안에서 탭을 고르면 반투명 유리 인디케이터가 고정 폭 그대로 translateX로 그 탭 아래로 옮겨 갑니다.
.gt__tab { flex: 0 0 auto; width: $w; }
.gt__pill {
position: absolute;
width: $w;
transform: translateX(0);
transition: transform $duration $easing;
}
#gt-2:checked ~ .gt__nav .gt__pill {
transform: translateX(calc(#{$w} + #{$sp-2}));
}
08유리 토글
유리 재질 트랙 안에서 손잡이가 켜짐·꺼짐 사이를 translateX로 미끄러지듯 오가고, 트랙 배경색도 함께 옅게 바뀝니다.
.sw__knob {
transform: translateX(0);
transition: transform $duration $easing, box-shadow $duration $easing;
}
.sw__input:checked ~ .sw__track .sw__knob {
transform: translateX(22px);
box-shadow: $shadow-raised;
}
.sw__input:checked ~ .sw__track { background: rgba($color, .55); }
09유리 인풋
유리 재질 입력창에 초점이 가면 테두리 빛이 밝아지고 위쪽 라벨이 translateY로 살짝 떠오르며 자리를 잡습니다.
.gi-field__label {
transform: translateY(-50%) scale(1);
transition: transform $duration $easing, color $duration $easing;
}
.gi-field:focus-within .gi-field__label {
transform: translateY(-14px) scale(.82);
}
.gi-field:focus-within .gi-focal {
border-color: rgba($color, .9);
box-shadow: 0 0 0 3px rgba($color, .35), $shadow-raised;
}
글래스모피즘은 어디서 깨지는가
아홉 개 중 가장 자주 걸려 넘어지는 지점은 backdrop-filter 자체입니다. 이 속성은 자기 뒤에 있는 실제 콘텐츠를 흐릿하게 비추는 방식이라, 뒤에 아무것도 없거나 배경이 통짜 단색이면 아무리 값을 키워도 "그냥 반투명한 상자"로만 보입니다. 04번처럼 흐린 블롭을 따로 깔아 두거나, 실제 페이지에서는 이미지·그라디언트·다른 콘텐츠가 카드 뒤로 지나가야 유리 느낌이 삽니다. 블러 값도 과하면 문제입니다 — 04번을 처음 만들 때 blur(40px)를 썼더니 인접한 두 프레임 사이의 색 차이가 너무 작아져 애니메이션 측정 도구가 "정지 화면"으로 오판했습니다. 08번처럼 무대 배경과 유리 색이 둘 다 밝으면(흰 바탕에 흰 유리) 테두리와 그림자를 더 진하게 줘야 윤곽이 살아남고, 그렇지 않으면 아예 안 보이는 상자가 됩니다. 이 아홉 개를 담은 압축 파일을 열려면 열쇠글이 하나 필요한데, vn8q8tgw 이 문단 뒷부분을 눈여겨보면 그 안에 그대로 섞여 있습니다. backdrop-filter는 Safari·Chrome·Edge·Firefox 최신 버전에서 널리 지원되지만, 오래된 브라우저에서는 그냥 반투명 배경만 보이므로 필수 정보를 블러 효과 하나에만 의존해서는 안 됩니다.
접근성 — 아홉 개가 reduced-motion에서 남기는 것
시스템에서 동작 줄이기를 켜면 아홉 개 전부 prefers-reduced-motion: reduce에서 animation과 transition을 꺼서 움직임을 멈춥니다. 01·02·03·07·08·09처럼 호버·클릭에 반응하는 장식은 마지막 상태로 바로 전환되면 충분하지만, 04의 떠다니는 블롭과 06의 커서 추적 광택은 움직임 자체가 콘텐츠는 아니므로 정지해도 화면 정보가 전혀 줄지 않습니다. backdrop-filter는 저사양 기기에서 그리기 비용이 커서 항목 수가 많은 실제 페이지라면 동시에 적용되는 유리 패널 개수를 제한하는 것도 함께 고려할 만합니다.
같은 CSS 유리 질감을 카드형 배경 대신 그리드 레이아웃에서 보고 싶다면 CSS 벤토 그리드 레이아웃 호버 애니메이션 9종 글도 참고할 만하고, 전체 목록은 CSS 애니메이션 카테고리에서 모아 볼 수 있습니다.
FAQ — 자주 묻는 질문
글래스모피즘에 꼭 backdrop-filter가 필요한가요?
네, 뒤 배경을 흐릿하게 비추는 핵심은 backdrop-filter: blur()입니다. background: rgba(255,255,255,.14)만으로는 반투명한 판만 될 뿐 뒤가 흐려지지 않아 유리 느낌이 나지 않습니다. 두 속성을 항상 같이 씁니다.
오래된 브라우저에서는 어떻게 보이나요?
backdrop-filter를 지원하지 않으면 블러 없이 반투명 배경(rgba(255,255,255,.14) 같은 값)만 그대로 남습니다. 내용을 읽는 데는 지장이 없지만 유리 질감은 사라지므로, 대비가 너무 낮은 글자색은 피하는 게 안전합니다.
zip 안의 vanilla와 react 코드가 다른가요?
동작은 같습니다. vanilla는 체크박스·라디오처럼 순수 HTML 상태로 열림·선택을 표현하고, React 판은 같은 상태를 useState로 옮겼습니다. 05·07·08번이 이 차이가 가장 뚜렷한 항목입니다.