9 CSS Text Shine Animation — dán 1 dòng là chạy
CSS text shine animation là các hiệu ứng CSS thuần khiến chữ phát sáng hoặc đổi màu liên tục, không cần ảnh hay JavaScript.
Tự chạy · bấm ô để tới mục đó · cả 9 trong một zip
- 01 Chữ gradient chảy
- 02 Gradient xoay góc
- 03 Chữ neon glow
- 04 Chữ quét sáng
- 05 Chữ glitch
- 06 Chữ kim loại chrome
- 07 Chữ nhấp nháy
- 08 Viền chữ glow
- 09 Chữ cầu vồng
Thứ tự chín kiểu dưới đây theo cách gradient tác động lên chữ, không phải theo độ nổi bật. 01 và 02 là màu tự chảy hoặc tự xoay bên trong con chữ. 03 và 04 dùng ánh sáng để nhấn — một quầng thở nhẹ, một dải sáng quét qua một lần. 05 và 06 làm chữ trông như màn hình bị lỗi hoặc như bề mặt kim loại thật. 07 và 08 tách riêng từng chữ, một bên nhấp nháy, một bên chỉ còn viền rỗng. 09 khép lại bằng cả vòng màu xoay liên tục. Chữ trong demo chỉ là ví dụ, đổi nội dung bên trong thẻ <h1> thì mọi rule vẫn chạy y nguyên.
01Chữ gradient chảy
Gradient xanh dương và kem được nạp vào chữ qua background-clip: text trên một background-size phóng to gấp ba, rồi background-position trượt qua lại khiến màu trông như đang chảy liên tục bên trong con chữ.
.flow {
background: linear-gradient(90deg, $color, $subject-cream, $color, $subject-cream, $color);
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
animation: flow $duration $easing infinite;
}
@keyframes flow {
0% { background-position: 0% 50%; }
100% { background-position: 100% 50%; }
}
02Gradient xoay góc
Một custom property kiểu <angle> đăng ký qua @property được đặt vào đúng chỗ góc của linear-gradient(), rồi chỉ mỗi giá trị góc đó quay từ 0deg đến 360deg bằng keyframes, nên chính dải gradient xoay bên trong chữ.
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.spin {
--angle: 0deg;
background: linear-gradient(var(--angle), $color, $stage-orange, $color);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: rotate-angle $duration $easing infinite;
}
@keyframes rotate-angle { from { --angle: 0deg; } to { --angle: 360deg; } }
03Chữ neon glow
Một lớp chữ giống hệt nằm phía sau qua ::before, chỉ áp filter: blur() để tạo quầng sáng, rồi cho bán kính mờ và độ mờ lớn nhỏ lặp lại theo vài nhịp ngắn để quầng sáng thở nhẹ, thay vì cả bảng neon bật tắt dứt khoát.
.neon { position: relative; isolation: isolate; }
.neon::before {
content: attr(data-text);
position: absolute; inset: 0; z-index: -1;
background: inherit;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
filter: blur(4px);
animation: breathe $duration $easing infinite;
}
@keyframes breathe {
0%, 10% { filter: blur(4px); opacity: .28; }
15%, 25% { filter: blur(20px); opacity: 1; }
}
04Chữ quét sáng
Chồng thêm một lớp chữ giống hệt lên trên nền chữ màu đơn, riêng lớp trên chỉ để lộ một dải sáng hẹp qua background-clip: text rồi quét một lượt theo một hướng bằng background-position, như ánh sáng lướt qua bề mặt kim loại.
.light {
background: linear-gradient(100deg, transparent 42%, rgba($stage-paper, .95) 50%, transparent 58%);
background-size: 260% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: sweep $duration $easing infinite;
}
@keyframes sweep {
0% { background-position: 140% 50%; }
60% { background-position: -40% 50%; }
}
05Chữ glitch
Hai lớp màu khác nhau xếp chồng lên nhau, vị trí của mask-image dải ngang nhảy cóc theo timing steps() làm scanline vỡ vụn, cùng lúc filter: hue-rotate giật ngắn khiến màu lệch tức thời. Kỹ thuật cắt mảnh rồi đẩy bằng clip-path không xuất hiện ở đây.
.r, .b {
position: absolute; inset: 0;
mix-blend-mode: screen;
-webkit-mask-image: repeating-linear-gradient(to bottom, #000 0 3px, transparent 3px 7px);
mask-image: repeating-linear-gradient(to bottom, #000 0 3px, transparent 3px 7px);
animation: mask-jump-r $duration $easing infinite;
}
@keyframes mask-jump-r {
0%, 40%, 100% { -webkit-mask-position: 0 0; mask-position: 0 0; opacity: 0; }
45%, 55% { -webkit-mask-position: 0 3px; mask-position: 0 3px; opacity: .9; }
}
06Chữ kim loại chrome
Gradient xen kẽ dày đặc giữa màu tối, trắng và kem được nạp vào chữ qua background-clip: text, rồi background-position trượt chậm rãi để mô phỏng ánh phản chiếu lướt qua bề mặt chrome.
.chrome {
background: linear-gradient(
100deg,
$color 0%, $stage-paper 12%, $color 24%,
$subject-cream 40%, $stage-paper 52%, $color 64%,
$stage-paper 80%, $color 100%
);
background-size: 220% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: reflect $duration $easing infinite;
}
07Chữ nhấp nháy
Từng chữ được tách vào một span riêng, biến --i gán độ trễ khác nhau cho mỗi chữ để opacity dao động ngắn. Không phải cả cụm tắt rồi bật cùng lúc — mỗi chữ nhấp nháy độc lập, giống biển hiệu bóng đèn marquee kiểu cũ.
.marquee span {
display: inline-block;
color: $color;
animation: bulb $duration $easing infinite;
animation-delay: calc(var(--i, 0) * 120ms);
}
@keyframes bulb {
0%, 40%, 100% { opacity: 1; }
47% { opacity: .22; }
54% { opacity: 1; }
}
08Viền chữ glow
Phần trong chữ bị rút rỗng bằng color: transparent, -webkit-text-stroke chỉ giữ lại đường viền, rồi cho bán kính của filter: drop-shadow() lớn nhỏ lặp lại để ánh sáng lan quanh viền như đang thở.
.outline {
color: transparent;
-webkit-text-stroke: 2px $color;
animation: glow $duration $easing infinite;
}
@keyframes glow {
0%, 100% { filter: drop-shadow(0 0 3px rgba($color, .5)); }
50% {
filter: drop-shadow(0 0 14px rgba($color, .9))
drop-shadow(0 0 26px rgba($color, .5));
}
}
09Chữ cầu vồng
Một gradient cầu vồng nhiều màu cố định được nạp vào chữ qua background-clip: text. Thay vì đổi vị trí hay góc, filter: hue-rotate quay liên tục từ 0deg đến 360deg để cả vòng màu tuần hoàn ngay trong khi con chữ đứng yên.
.rainbow {
background: linear-gradient(90deg, $color, $stage-yellow, $stage-orange, $error, $subject-cream, $color);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: hue-cycle $duration $easing infinite;
}
@keyframes hue-cycle {
from { filter: hue-rotate(0deg); }
to { filter: hue-rotate(360deg); }
}
Bẫy nằm ở đâu — tiền tố background-clip và @property
background-clip: text — bảy trong chín mục (01, 02, 03, 04, 05, 06, 09) đều dựa vào — từng cần tiền tố suốt một thời gian dài. Bảng hỗ trợ background-clip-text trên caniuse ghi rõ Chrome và Edge bỏ tiền tố từ bản 120, Safari từ 15.5, còn bản cũ hơn bắt buộc phải có -webkit-background-clip: text thì chữ mới được lấp đầy bằng gradient thay vì hiện màu đặc. Vì vậy cả bảy file này trong zip đều giữ song song cặp -webkit-background-clip: text và -webkit-text-fill-color: transparent bên cạnh khai báo không tiền tố (08 rút chữ bằng -webkit-text-stroke chứ không dùng background-clip, 07 không rút chữ nên không cần cặp này). Đây cũng là bẫy thật đã gặp khi dựng mục 03: quầng sáng của ::before đặt z-index: -1 để nằm dưới lớp chữ chính, nhưng nếu quên khai isolation: isolate trên .neon, quầng sáng đó rơi thẳng ra ngoài ngữ cảnh xếp chồng của cha và bị nền sân khấu che kín — không còn thấy đâu cả dù CSS không báo lỗi gì.
@property ở mục 02 có vùng hỗ trợ hẹp hơn nhiều. Theo trang @property của MDN, Chrome và Edge hỗ trợ từ bản 85, Safari từ 16.4, còn Firefox chỉ chính thức nhận từ bản 128 trở đi. Trên trình duyệt chưa có @property, giá trị góc không nội suy được nên gradient đứng im, không quay — bố cục không vỡ, chỉ mất chuyển động, nên khi đưa vào sản phẩm thật hãy coi trạng thái đứng yên là mặc định an toàn, còn vòng quay là phần cộng thêm. Muốn mở khoá xem đủ chín file đã dựng và đo khung hình thật, mật khẩu 5hzhmkcb nằm ngay trên dòng này, gõ đúng như vậy.
Khả năng tiếp cận
Cả chín hiệu ứng đều xử lý prefers-reduced-motion: reduce, nhưng phần còn lại sau khi tắt chuyển động không giống nhau. Nhóm chảy hoặc xoay — 01, 02, 06 — dừng lại giữa chừng vòng lặp và giữ nguyên một trạng thái tĩnh ở đó, chứ không phải điểm đầu hay điểm cuối. Mục 04 cũng dừng giữa chừng nhưng theo cách riêng: dải sáng đứng khựng ngay giữa chữ (background-position: 50% 50%) thay vì trôi tiếp sang một bên. Mục 09 thì khác hẳn — nó reset thẳng về điểm xuất phát của vòng màu (hue-rotate(0deg)), nên đứng yên đúng thứ tự gradient ban đầu chứ không kẹt ở một pha bất kỳ. Nhóm phát sáng — 03, 08 — khoá ở một mức sáng dịu duy nhất thay vì thở lên xuống. Mục 05 tắt cả ba lớp cùng lúc: lớp nền hết giật màu, còn hai lớp mask-image chuyển thẳng sang opacity: 0 nên chỉ còn lại chữ gradient trơn, không còn scanline nào. Mục 07 tắt hẳn animation nên mọi chữ hiện cùng một độ sáng, không còn chữ nào nhấp nháy riêng lẻ. Những hiệu ứng rút rỗng chữ bằng background-clip: text có thể giảm độ tương phản với người mù màu hoặc màn hình bão hoà thấp, nên khi dùng cho tiêu đề thật, đừng để riêng hiệu ứng này gánh hết việc truyền thông tin — hãy đặt thêm chữ thường hoặc icon đi kèm. Cách trình duyệt bật media query này được ghi ở trang prefers-reduced-motion của MDN.
Ngoài hiệu ứng chữ, site còn gom cả những trò về nền, ảnh và nút bấm trong danh mục CSS, còn cách chọn demo nào để đăng thì trang giới thiệu giải thích riêng.
FAQ
Tiền tố -webkit- có còn cần cho chữ gradient không?
Phần lớn khách ghé site đã dùng Chrome 120 hoặc Safari 15.5 trở lên nên chỉ riêng background-clip: text không tiền tố là đủ chạy. Nhưng thêm tiền tố gần như không tốn gì, còn trên trình duyệt cũ, thiếu tiền tố nghĩa là chữ không hiện gradient chút nào chứ không phải hiện xấu hơn, nên cả bảy file dùng background-clip: text đều giữ nguyên cặp tiền tố.
Mục 09 quay cả vòng màu có gây khó cho người mù màu không?
Việc màu liên tục đổi vẫn nhận ra được dù một người khó phân biệt hai sắc cụ thể nào đó, vì tín hiệu ở đây là chuyển động chứ không phải một cặp màu cố định. Chỉ nên dùng hiệu ứng này cho tiêu đề trang trí, không nên dùng màu để phân biệt ý nghĩa như thành công hay lỗi.
Ghép nhiều hiệu ứng cùng lúc lên một dòng tiêu đề có ổn không?
Về mặt kỹ thuật thì chạy được, nhưng không nên. background-clip: text cộng thêm filter: blur() hay mask-image xếp chồng nhiều lớp buộc trình duyệt vẽ lại nhiều lớp hơn mỗi khung hình, và máy di động yếu sẽ giật trước tiên. Một tiêu đề chỉ nên mang đúng một trong chín kiểu này.