GODRICH
#css-animation#button#loader#transition#scroll#ui#xu-huong-thiet-ke

9 css glassmorphism — dán một file là chạy

Glassmorphism là đặt một tấm kính mờ lên màn hình: nền phía sau hiện lên mờ mềm còn chữ phía trên vẫn rõ nét.

Tự chạy · bấm ô để tới mục đó · cả 9 trong một zip

Chín mục không xếp theo độ hot mà theo thứ tự bạn gặp chất kính trên một màn hình. Mở đầu là nhóm đứng yên cũng trông ra kính (01 card, 02 navbar), tiếp đến nhóm phải chạm mới phản hồi (03 nút bấm, 04 nền động), rồi nhóm mở ra bằng cú nhấn (05 modal) và nhóm chỉ sống khi có chuột (06 vệt sáng bám con trỏ), khép lại bằng nhóm để chọn, bật, nhập (07 tab, 08 toggle, 09 ô nhập).

01Card kính mờ

Thiết kế kính mờ hợp với trang nào

Card giới thiệu của landing page hay portfolio chỉ cần thêm backdrop-filter: blur() cùng nền bán trong suốt là màu phía sau loang ra mờ mềm, tấm card nổi lên như một tấm kính.

Thẻ frosted kính mờ
Mở trong tab mới

01 · Thẻ frosted kính mờ — Diện tích đo được 4.222% · khung hình chuyển động 23/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

backdrop-filterscale300ms

Cốt lõi nằm ở đâu

Đưa chuột lên là card phồng nhẹ và đổ bóng sâu hơn.

.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;
}

02Navbar kính

Khi nào nên dùng

Trong thanh navbar bán trong suốt, viên thuốc kính mờ trượt đến sau mục đang chọn bằng translateX.

Thanh nav kính
Mở trong tab mới

02 · Thanh nav kính — Diện tích đo được 6.783% · khung hình chuyển động 14/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

backdrop-filtertranslateX220ms

Chi tiết đáng ghi nhớ

Tab giữ độ rộng cố định nên viên thuốc luôn dừng đúng chỗ.

.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}));
}

03Nút bấm khúc xạ ánh sáng

Màn hình như thế nào

Trên mặt nút nằm sẵn một dải sáng hẹp; đưa chuột lên là dải sáng quét ngang nút đúng một lần bằng translateX, tựa như ánh sáng khúc xạ trên kính lỏng.

Nút kính khúc xạ
Mở trong tab mới

03 · Nút kính khúc xạ — Diện tích đo được 3.422% · khung hình chuyển động 10/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

backdrop-filtertranslateX600ms

Cách dựng nên nó

.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);
}

04Nền blob chuyển động

Cái gì đang chuyển động

Sau tấm kính, hai khối blob mờ hai màu đổi chỗ chậm rãi bằng transform, backdrop-filter của panel chiếu chuyển động ấy thành vệt mờ trôi.

Nền blob kính di chuyển
Mở trong tab mới

04 · Nền blob kính di chuyển — Diện tích đo được 41.944% · khung hình chuyển động 23/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

transformblur(40px)2s loop

Đọc code theo hướng nào

.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); }
}

05Modal kính

Dùng chỗ nào

Bấm nút xác nhận, panel modal kính bung ra từ trạng thái thu nhỏ bằng opacity và scale, trang phía sau vẫn thấy được nhưng mờ đi.

Modal kính
Mở trong tab mới

05 · Modal kính — Diện tích đo được 14.35% · khung hình chuyển động 10/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

opacityscale600ms

Điểm chính của code

.gm-modal {
  opacity: 0;
  transform: scale(.85);
  transition: opacity $duration $easing, transform $duration $easing;
}
.gm-check:checked ~ .gm-modal {
  opacity: 1;
  transform: scale(1);
}

06Vệt sáng bám theo con trỏ

Mắt nhìn thấy gì

Rê chuột trên tấm kính lớn, hai custom property --mx và --my ghi lại tọa độ, vệt sáng hình elip bám theo thời gian thực như ngón tay lau trên mặt kính.

Vệt sáng bám theo chuột
Mở trong tab mới

06 · Vệt sáng bám theo chuột — Diện tích đo được 37.717% · khung hình chuyển động 23/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

custom propertyradial-gradientJS 8줄

Nguyên lý chạy thế nào

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%);
}

07Tab kính

Dùng trong tình huống nào

Chọn tab nào trong nhóm tab kính, thanh chỉ thị bán trong suốt giữ nguyên độ rộng và trượt đến dưới tab đó bằng translateX.

Tab kính
Mở trong tab mới

07 · Tab kính — Diện tích đo được 6.422% · khung hình chuyển động 14/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

backdrop-filtertranslateX220ms

Cách tạo ra nó

.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}));
}

08Toggle kính

Chuyện gì diễn ra

Trong rãnh kính, núm gạt trượt giữa bật và tắt bằng translateX, màu rãnh cũng đổi nhạt theo trạng thái.

Công tắc kính
Mở trong tab mới

08 · Công tắc kính — Diện tích đo được 1.328% · khung hình chuyển động 12/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

backdrop-filtertranslateX300ms

Vì sao dựng theo cách này

.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Ô nhập liệu kính

Trên màn hình việc gì xảy ra

Ô nhập kính nhận tiêu điểm thì viền sáng lên, label phía trên nhấc nhẹ bằng translateY và đứng vào vị trí mới.

Ô nhập liệu kính
Mở trong tab mới

09 · Ô nhập liệu kính — Diện tích đo được 2.953% · khung hình chuyển động 15/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

box-shadowtranslateY220ms

Cốt lõi của phần dựng

.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;
}

Glassmorphism dễ vỡ ở đâu

Vấn đề nằm ở đâu

Kể cả khi bạn bắt chước liquid glass của iPhone, chỗ vấp nhiều nhất trong chín mục vẫn là backdrop-filter. Thuộc tính này làm mờ nội dung thật nằm sau nó, nên sau tấm kính không có gì hoặc nền chỉ là một màu đặc thì tăng giá trị đến mấy bạn cũng chỉ nhận được một hộp bán trong suốt. Hãy trải sẵn blob mờ như mục 04, hoặc ở trang thật thì để hình ảnh và gradient đi ngang sau card. Blur quá đà cũng hỏng: lần đầu dựng mục 04 tôi đặt blur(40px), chênh màu giữa hai khung hình cạnh nhau nhỏ đến mức công cụ đo animation báo nhầm là khung đứng yên.

Thành phần Dải an toàn Khi nào tăng
blur() 12–20px nền sau nhiều chi tiết
Nền trắng rgba .10–.18 chữ cần nổi hơn
Viền trắng rgba .30–.40 khung dễ tan vào nền

Tránh thế nào

Tra css glassmorphism card hay lục glassmorphism css codepen cũng tiện, nhưng chín mục ở đây cho bạn copy nguyên mã mà không cần rời trang. Sân khấu và tấm kính cùng sáng, kiểu nền trắng kèm kính trắng ở mục 08, thì phải đậm viền và bóng hơn khung mới sống sót, không thì chỉ còn là hộp vô hình. Muốn mở tệp nén chứa chín hiệu ứng này bạn cần một chuỗi khóa, vn8q8tgw phần cuối đoạn này giấu nguyên văn chuỗi đó trong câu chữ. backdrop-filter chạy tốt trên bản mới của Safari, Chrome, Edge, Firefox, còn trình duyệt cũ chỉ còn nền bán trong suốt, nên đừng trông cậy thông tin quan trọng vào một hiệu ứng mờ.

Khả năng tiếp cận — chín hiệu ứng giữ lại gì khi bật reduced-motion

Bật reduced-motion còn lại gì

Khi hệ thống bật giảm chuyển động, cả chín hiệu ứng đều tắt animation và transition qua prefers-reduced-motion: reduce rồi đứng yên. Trang trí phản hồi hover hay cú nhấn như 01, 02, 03, 07, 08, 09 nhảy thẳng tới trạng thái cuối là đủ; blob trôi của 04 và vệt sáng bám chuột của 06 không mang nội dung nên đứng im cũng không mất thông tin. backdrop-filter tốn chi phí vẽ trên máy yếu, trang thật nhiều mục thì nên giới hạn số panel kính chạy cùng lúc.

Bài nên đọc tiếp

Muốn xem cùng chất kính ấy trong bố cục lưới thay vì card, bạn đọc tiếp 9 hiệu ứng hover bento grid CSS; danh mục đầy đủ nằm ở CSS animation.

FAQ — câu hỏi thường gặp

Glassmorphism có bắt buộc dùng backdrop-filter không?

Có, phần lõi làm mờ nền phía sau là backdrop-filter: blur(). Chỉ có background: rgba(255,255,255,.14) thì bạn mới được một tấm bán trong suốt, phía sau không mờ nên không ra chất kính. Hai thuộc tính này luôn đi cặp.

Trên trình duyệt cũ liquid glass hiện thế nào?

Trình duyệt thiếu backdrop-filter bỏ phần mờ, chỉ còn nền bán trong suốt kiểu rgba(255,255,255,.14). Đọc nội dung không vướng nhưng chất kính mất, nên tránh màu chữ tương phản quá thấp.

Code vanilla và react trong zip khác nhau ra sao?

Cách hoạt động như nhau. Bản vanilla diễn tả trạng thái mở, chọn bằng HTML thuần như checkbox và radio; bản React chuyển cùng trạng thái đó sang useState. Mục 05, 07, 08 là chỗ khác biệt rõ nhất.

Nhập mật khẩu file nén

Mật khẩu nằm ngay trong bài viết này. Đọc tới đó bạn sẽ thấy.