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

9 hiệu ứng css parallax scrolling, không cần JS

CSS parallax scrolling là cách tạo cảm giác chiều sâu khi cuộn trang, bằng cách cho các lớp — nền, chữ, thẻ — di chuyển với tốc độ khác nhau, chỉ bằng

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

Chín hiệu ứng dưới đây xếp theo độ phức tạp của lớp: từ một lớp nền trôi chậm (01) đến ba lớp phối cảnh thật (02), rồi tới những biến thể dùng sticky, timeline ngang hoặc chuột thay cho cuộn dọc thuần túy. Trang demo không cuộn được, nên lưới 3×3 phía trên lặp lại chuyển động mỗi 2 giây trong một iframe cố định — bạn chỉ cần xem; các số đo dưới mỗi mục lấy từ chuyển động cuộn thật, không phải từ vòng lặp trình diễn này. animation-timeline hiện chạy trên Chrome 115+ và Safari 26+, còn Firefox bản ổn định vẫn cần bật cờ tính đến 9/2026.

01Nền trôi chậm

Bạn thấy gì

Chỉ một lớp nền di chuyển chậm hơn tốc độ cuộn, tạo khoảng cách với chữ phía trước và sinh ra cảm giác chiều sâu.

Nền trôi chậm
Mở trong tab mới

01 · Nền trôi chậm — Diện tích đo được 3.371% · 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

0.4배translateY단일 레이어

Dùng ở đâu

Dùng cho nền hero trang landing hoặc banner đầu section.

.bgslow__bg {
  position: absolute;
  inset: -20% -10%;
  background: radial-gradient($color 0 46px, transparent 47px) 24% 30%;

  @supports (animation-timeline: scroll()) {
    animation: drift linear both;
    animation-timeline: scroll(root);
  }
}

@keyframes drift {
  from { transform: translateY(0); }
  to   { transform: translateY(-24px); }
}

02Phối cảnh 3 lớp

Chuyện gì diễn ra trên màn hình

Ba lớp sau, giữa, trước di chuyển với tốc độ khác nhau, tạo phối cảnh thật cho sân khấu.

Phối cảnh 3 lớp
Mở trong tab mới

02 · Phối cảnh 3 lớp — Diện tích đo được 7.535% · 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

0.2·0.5·0.8배세 겹translateY

Chỗ dùng

Hợp với trang giới thiệu game hoặc section kể chuyện thương hiệu.

.layer3__back  { animation: drift-back linear both;  animation-timeline: scroll(root); }
.layer3__mid   { animation: drift-mid linear both;   animation-timeline: scroll(root); }
.layer3__front { animation: drift-front linear both; animation-timeline: scroll(root); }

@keyframes drift-back  { from { transform: translateY(0); } to { transform: translateY(-10px); } }
@keyframes drift-mid   { from { transform: translateY(0); } to { transform: translateY(-26px); } }
@keyframes drift-front { from { transform: translateY(0); } to { transform: translateY(-46px); } }

03Chữ trôi qua ảnh

Chuyển động thế nào

Ảnh gần như đứng yên, chỉ chữ di chuyển nhanh hơn tốc độ cuộn rồi trượt qua bên trên ảnh.

Chữ trôi qua ảnh
Mở trong tab mới

03 · Chữ trôi qua ảnh — Diện tích đo được 31.153% · 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

1.6배mix-blend-modetranslateY

Dùng khi nào

Hợp với hero kiểu tạp chí hoặc tiêu đề photo essay.

.textimg__image {
  animation: image-drift linear both;
  animation-timeline: scroll(root);
}
.textimg__text {
  mix-blend-mode: difference;
  animation: text-drift linear both;
  animation-timeline: scroll(root);
}

@keyframes image-drift { from { transform: translateY(0); }    to { transform: translateY(-14px); } }
@keyframes text-drift  { from { transform: translateY(60px); } to { transform: translateY(-70px); } }

04Hero co lại khi cuộn

Bạn thấy gì

Tiêu đề hero nhỏ dần và mờ dần trong lúc bạn cuộn, nhường chỗ cho section tiếp theo.

Hero co lại khi cuộn
Mở trong tab mới

04 · Hero co lại khi cuộn — Diện tích đo được 3.955% · 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

scale 1→.6opacity 동반축소+페이드

Dùng ở đâu

Hợp với màn hình đầu trang landing hoặc phần mở đầu giới thiệu app.

.heroshrink__title {
  animation: shrink linear both;
  animation-timeline: scroll(root);
}

@keyframes shrink {
  from { transform: scale(1);   opacity: 1; }
  to   { transform: scale(.55); opacity: .25; }
}

05Timeline cuộn ngang

Chuyện gì diễn ra trên màn hình

Cuộn dọc như bình thường nhưng dãy thẻ lại trôi ngang, đọc như một timeline.

Timeline cuộn ngang
Mở trong tab mới

05 · Timeline cuộn ngang — Diện tích đo được 13.686% · 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

translateXscroll(root)가로 흐름

Chỗ dùng

Hợp với trang lịch sử công ty hoặc roadmap sản phẩm.

.hline__track {
  display: flex;
  gap: 14px;
  animation: travel linear both;
  animation-timeline: scroll(root);
}

@keyframes travel {
  from { transform: translateX(0); }
  to   { transform: translateX(-120px); }
}

06Thẻ xếp lớp

Chuyển động thế nào

Ba thẻ xếp chồng lên nhau bong ra với tốc độ khác nhau mỗi khi bạn cuộn, để lộ chiều sâu bên dưới.

Thẻ xếp lớp
Mở trong tab mới

06 · Thẻ xếp lớp — Diện tích đo được 16.794% · 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

scale 단계별animation-timeline: scroll(root)translateY

Dùng khi nào

Hợp với thẻ giới thiệu tính năng hoặc so sánh gói giá.

.depthcards__card--1 { animation: peel-1 linear both; animation-timeline: scroll(root); }
.depthcards__card--2 { animation: peel-2 linear both; animation-timeline: scroll(root); }
.depthcards__card--3 { animation: peel-3 linear both; animation-timeline: scroll(root); }

@keyframes peel-1 { from { transform: translateY(0) scale(1); }      to { transform: translateY(-34px) scale(1); } }
@keyframes peel-2 { from { transform: translateY(9px) scale(.94); }  to { transform: translateY(-25px) scale(1); } }
@keyframes peel-3 { from { transform: translateY(18px) scale(.88); } to { transform: translateY(-30px) scale(1); } }

07Sticky kết hợp parallax

Bạn thấy gì

Chữ phía trước dính cố định trên màn hình bằng position: sticky, chỉ nền phía sau tiếp tục trôi qua.

Sticky kết hợp parallax
Mở trong tab mới

07 · Sticky kết hợp parallax — Diện tích đo được 77.093% · 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

position sticky배경만 이동고정+흐름

Dùng ở đâu

Hợp với section kể chuyện hoặc giải thích thông số sản phẩm.

.stickypar__caption {
  position: sticky;
  top: 40%;
}
.stickypar__bg {
  animation: bg-drift linear both;
  animation-timeline: scroll(root);
}

@keyframes bg-drift {
  from { transform: translateY(0); }
  to   { transform: translateY(-40px); }
}

08Parallax theo chuột

Chuyện gì diễn ra trên màn hình

Các lớp bám theo hướng con trỏ di chuyển với biên độ khác nhau, tạo cảm giác nổi khối mà không cần cuộn trang.

Parallax theo chuột
Mở trong tab mới

08 · Parallax theo chuột — Diện tích đo được 6.447% · 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

pointermove레이어별 배율translate

Chỗ dùng

Hợp với showcase sản phẩm hoặc hero portfolio.

.mousepar__layer {
  transition: transform 120ms ease-out;
  // JS đặt --mx, --my từ pointermove; --depth khác nhau theo từng lớp
  transform: translate(
    calc(var(--mx, 0) * var(--depth) * 22px),
    calc(var(--my, 0) * var(--depth) * 22px)
  );
}

09Zoom out khi cuộn

Chuyển động thế nào

Ảnh phóng to sẵn thu nhỏ dần khi bạn cuộn, để lộ layout xung quanh nó.

Zoom out khi cuộn
Mở trong tab mới

09 · Zoom out khi cuộn — Diện tích đo được 22.201% · 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

scale 1.4→1scroll(root)줌아웃

Dùng khi nào

Hợp với màn hình đầu trang chi tiết sản phẩm hoặc phần mở đầu gallery.

.zoomout__media {
  transform: scale(1.45);
  animation: zoom-out linear both;
  animation-timeline: scroll(root);
}

@keyframes zoom-out {
  from { transform: scale(1.45); }
  to   { transform: scale(1); }
}

Chỗ dễ hỏng — cái bẫy nằm ở đâu?

Nhánh dự phòng của @supports

Cả 9 hiệu ứng đều đặt code thật trong @supports (animation-timeline: scroll()), và bẫy dễ mắc nhất là quên nhánh dự phòng: trình duyệt chưa hỗ trợ không báo lỗi gì cả, lớp đó chỉ đứng yên, còn phần còn lại của trang vẫn chạy bình thường nên rất dễ bỏ sót khi test nhanh trên một trình duyệt mới. Bẫy thứ hai nằm ở mục 03 — mix-blend-mode: difference đọc màu nền ngay bên dưới nó, nên nếu bạn đổi ảnh nền mà quên kiểm tra lại độ tương phản, chữ có thể biến mất hoàn toàn trên một vài đoạn ảnh. Bẫy thứ ba là hiệu năng: cả 9 hiệu ứng chỉ animate transform và opacity, không đụng vào width hay top, vì hai thuộc tính đó buộc trình duyệt tính lại layout ở từng frame còn transform thì không.

Toàn bộ 9 file, kèm nhánh dự phòng và biến thể đã chỉnh sẵn, nằm trong file nén mà mật khẩu là x6462unj viết liền không dấu cách.

Khả năng tiếp cận

Phần giữ lại khi giảm chuyển động

Cả 9 hiệu ứng đều tắt được qua prefers-reduced-motion: reduce, nhưng mỗi hiệu ứng giữ lại một phần khác nhau thay vì đứng hình hoàn toàn. Mục 01, 02, 05, 06, 07 và 09 dừng animation và giữ nguyên vị trí cuối; mục 03 giữ ảnh và chữ tĩnh nhưng vẫn giữ mix-blend-mode để không mất độ tương phản; mục 04 giữ tiêu đề ở kích thước gốc thay vì đứng ở giữa quá trình co lại; mục 08 tắt transition theo chuột hoàn toàn. MDN có tài liệu đầy đủ về prefers-reduced-motion và cách hệ điều hành bật nó.

@media (prefers-reduced-motion: reduce) {
  .bgslow__bg, .layer3__back, .layer3__mid, .layer3__front,
  .textimg__image, .textimg__text, .heroshrink__title,
  .hline__track, .depthcards__card--1, .depthcards__card--2,
  .depthcards__card--3, .stickypar__bg, .zoomout__media {
    animation: none !important;
    transform: none;
  }
}

Các hiệu ứng cuộn khác của trang này gom trong chuyên mục CSS, còn cách trang chọn hiệu ứng để viết nằm ở trang giới thiệu.

FAQ

animation-timeline: scroll() có thay được thư viện parallax dùng JS không?

Được với phần lớn trường hợp. Chín hiệu ứng ở trên đều chạy hoàn toàn bằng CSS, không cần thư viện nào tính toán vị trí cuộn mỗi frame, nên nhẹ hơn và không có độ trễ giữa lúc cuộn và lúc lớp di chuyển. Chỉ mục 08 (theo chuột) cần một đoạn JS ngắn, vì đó là chuyển động theo con trỏ chứ không phải theo cuộn.

Vì sao lưới demo phía trên không cuộn được như trang thật?

Vì mỗi ô demo nằm trong một iframe cố định 480×300, không có nội dung để cuộn thật sự, nên mỗi ô lặp lại đúng chuyển động đó bằng một animation 2 giây thay vì đọc animation-timeline: scroll(). Bản trong file zip vẫn dùng scroll() thật; bạn cần xem trên trang có nội dung dài mới thấy đúng cảm giác.

Dùng cả 9 hiệu ứng trên cùng một trang có ổn không?

Không nên. Mỗi hiệu ứng gắn với một vị trí cụ thể trên trang — nền hero, một section kể chuyện, một dãy thẻ — nên dùng hai đến ba hiệu ứng khác khu vực trong cùng một trang là hợp lý, còn dùng cả 9 liên tiếp sẽ khiến người đọc chóng mặt vì tốc độ cuộn cảm nhận được liên tục thay đổi.

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.