Pseudo-classes và Pseudo-elements
Pseudo-class (lớp giả) chọn phần tử theo trạng thái hoặc vị trí trong cây DOM.
Pseudo-element (phần tử giả) chọn phần của phần tử hoặc tạo nội dung ảo.
Pseudo-classes phổ biến
Tương tác người dùng
a:hover { color: #ed7d31; } /* Khi chuột di qua */
a:focus { outline: 2px solid blue; } /* Khi được chọn (tab/click) */
a:active { opacity: 0.7; } /* Khi đang nhấn */
a:visited { color: purple; } /* Đã từng truy cập */
Cấu trúc DOM
li:first-child { font-weight: bold; } /* Con đầu tiên */
li:last-child { border-bottom: none; } /* Con cuối */
li:nth-child(2) { color: red; } /* Con thứ 2 */
li:nth-child(odd) { background: #f5f5f5; } /* Con lẻ */
li:nth-child(even) { background: white; } /* Con chẵn */
li:nth-child(3n+1) { color: blue; } /* 1, 4, 7, ... */
p:nth-of-type(2) { margin-top: 2rem; } /* Phần tử <p> thứ 2 */
div:not(.special) { opacity: .5; } /* Mọi div KHÔNG có .special */
Trạng thái form
input:checked { /* checkbox/radio được chọn */ }
input:disabled { opacity: .4; cursor: not-allowed; }
input:focus { border-color: #4472c4; outline: none; }
p:empty { display: none; }
Pseudo-elements phổ biến
/* Thêm nội dung trước/sau phần tử */
.btn::before { content: "→ "; }
.req::after { content: " *"; color: red; }
/* Trang trí dòng đầu và chữ đầu */
p::first-line { font-weight: bold; }
p::first-letter { font-size: 2em; float: left; }
/* Placeholder trong input */
input::placeholder { color: #aaa; font-style: italic; }
/* Vùng chọn (selection) */
::selection { background: #ffd700; color: #333; }
Pseudo-elements dùng
::(hai dấu hai chấm) theo chuẩn CSS3. Một dấu:vẫn hoạt động nhưng không được khuyến khích.
Thuộc tính content
content chỉ dùng được với ::before và ::after:
.icon::before { content: "🔗"; } /* Text/emoji */
.separator::after { content: " / "; } /* Separator */
li::before { content: counter(item) ". "; } /* Counter */
.img::after { content: url('/icon.svg'); } /* Ảnh */
blockquote::before { content: "\201C"; } /* Unicode */
Demo: :hover, :focus, :active
<div class="demo-wrap">
<h3>Trạng thái button</h3>
<div class="btn-group">
<button class="btn-state">Hover / Active</button>
<button class="btn-state btn-focus-demo">Focus (Tab vào đây)</button>
<button class="btn-state" disabled>Disabled</button>
</div>
<h3>Input states</h3>
<form>
<div class="form-row">
<label>Email:</label>
<input type="email" placeholder="Nhập email..." class="inp">
</div>
<div class="form-row">
<label>Disabled:</label>
<input type="text" value="Không thể nhập" disabled class="inp">
</div>
<div class="form-row">
<label>
<input type="checkbox" class="chk"> Đồng ý điều khoản
</label>
</div>
</form>
<h3>Link states</h3>
<p>
<a href="#visited-demo" class="demo-link">Link thường</a>
<a href="https://example.com" class="demo-link" target="_blank">Link đã thăm (visited)</a>
</p>
</div>
body { font-family: sans-serif; padding: 1rem; }
h3 { font-size: .9rem; color: #555; margin: 1rem 0 .4rem; }
.btn-group { display: flex; gap: 10px; flex-wrap: wrap; }
.btn-state {
padding: 8px 18px; border: 2px solid #4472c4;
background: #4472c4; color: white; border-radius: 6px;
cursor: pointer; font-size: .9rem;
transition: background .2s, transform .1s, box-shadow .2s;
}
.btn-state:hover { background: #2a50a0; }
.btn-state:active { transform: scale(.97); background: #1a3070; }
.btn-state:focus { outline: none; box-shadow: 0 0 0 3px rgba(68,114,196,.4); }
.btn-state:disabled { background: #aaa; border-color: #aaa; cursor: not-allowed; opacity: .6; }
.form-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.form-row label { min-width: 80px; font-size: .9rem; }
.inp {
padding: 6px 10px; border: 2px solid #ccc; border-radius: 4px;
font-size: .9rem; transition: border-color .2s, box-shadow .2s;
}
.inp:focus { border-color: #4472c4; outline: none; box-shadow: 0 0 0 3px rgba(68,114,196,.2); }
.inp:disabled { background: #f5f5f5; color: #999; cursor: not-allowed; }
.inp:valid { border-color: #28a745; }
.chk { width: 16px; height: 16px; cursor: pointer; accent-color: #4472c4; }
.demo-link { color: #4472c4; transition: color .2s; }
.demo-link:hover { color: #ed7d31; text-decoration: underline; }
.demo-link:active { color: #c00; }
.demo-link:visited { color: #7b2d8b; }
Demo: ::before và ::after
<div class="demo-wrap">
<h3>Icon với ::before</h3>
<ul class="icon-list">
<li class="icon-check">Tính năng đã hoàn thành</li>
<li class="icon-check">Hỗ trợ đa thiết bị</li>
<li class="icon-star">Tính năng nổi bật</li>
<li class="icon-error">Lỗi cần sửa</li>
</ul>
<h3>Breadcrumb separator với ::after</h3>
<nav class="breadcrumb" aria-label="breadcrumb">
<a href="#" class="bc-item">Trang chủ</a>
<a href="#" class="bc-item">Web</a>
<a href="#" class="bc-item">CSS</a>
<span class="bc-item bc-current">Pseudo-elements</span>
</nav>
<h3>Trang trí với ::before và ::after</h3>
<h2 class="decorated-title">Tiêu đề có trang trí</h2>
<h3>Tooltip với ::after</h3>
<p>Di chuột vào đây: <span class="tooltip" data-tip="Đây là tooltip bằng CSS thuần!">Văn bản có tooltip</span></p>
</div>
body { font-family: sans-serif; padding: 1rem; }
h3 { font-size: .9rem; color: #555; margin: 1rem 0 .4rem; }
/* Icon list */
.icon-list { list-style: none; padding: 0; margin: 0 0 .5rem; }
.icon-list li { padding: 4px 0 4px 24px; position: relative; }
.icon-check::before { content: "✓"; position: absolute; left: 0; color: #28a745; font-weight: bold; }
.icon-star::before { content: "★"; position: absolute; left: 0; color: #e09a00; }
.icon-error::before { content: "✗"; position: absolute; left: 0; color: #dc3545; font-weight: bold; }
/* Breadcrumb */
.breadcrumb { display: flex; flex-wrap: wrap; gap: 0; font-size: .9rem; }
.bc-item { color: #4472c4; text-decoration: none; }
.bc-item::after { content: " / "; color: #999; padding: 0 4px; }
.bc-item:last-child::after { content: ""; }
.bc-current { color: #333; }
/* Decorated title */
.decorated-title {
text-align: center; position: relative; font-size: 1.3rem;
margin: .5rem 0; padding: 0 2rem;
}
.decorated-title::before,
.decorated-title::after {
content: "—";
color: #4472c4;
position: absolute;
top: 50%; transform: translateY(-50%);
}
.decorated-title::before { left: 0; }
.decorated-title::after { right: 0; }
/* Tooltip */
.tooltip { position: relative; cursor: help; border-bottom: 1px dashed #4472c4; color: #4472c4; }
.tooltip::after {
content: attr(data-tip);
position: absolute; bottom: 110%; left: 50%; transform: translateX(-50%);
background: #333; color: white; padding: 4px 10px; border-radius: 4px;
white-space: nowrap; font-size: .8rem; pointer-events: none;
opacity: 0; transition: opacity .2s;
z-index: 10;
}
.tooltip:hover::after { opacity: 1; }
Demo: :nth-child và :checked tùy chỉnh
<div class="demo-wrap">
<h3>Zebra rows với :nth-child</h3>
<table class="zebra-table">
<thead>
<tr><th>Tên</th><th>Môn học</th><th>Điểm</th></tr>
</thead>
<tbody>
<tr><td>An</td><td>Toán</td><td>9.5</td></tr>
<tr><td>Bình</td><td>Lý</td><td>8.0</td></tr>
<tr><td>Chi</td><td>Hóa</td><td>9.0</td></tr>
<tr><td>Dũng</td><td>Anh</td><td>7.5</td></tr>
<tr><td>Em</td><td>Văn</td><td>8.5</td></tr>
</tbody>
</table>
<h3>Custom checkbox với :checked</h3>
<div class="checkbox-group">
<label class="custom-check">
<input type="checkbox">
<span class="checkmark"></span>
Lựa chọn đầu tiên
</label>
<label class="custom-check">
<input type="checkbox" checked>
<span class="checkmark"></span>
Lựa chọn thứ hai (mặc định chọn)
</label>
<label class="custom-check">
<input type="checkbox">
<span class="checkmark"></span>
Lựa chọn thứ ba
</label>
</div>
</div>
body { font-family: sans-serif; padding: 1rem; }
h3 { font-size: .9rem; color: #555; margin: 1rem 0 .4rem; }
/* Zebra table */
.zebra-table { border-collapse: collapse; width: 100%; margin-bottom: 1rem; }
.zebra-table th { background: #4472c4; color: white; padding: 8px 12px; text-align: left; }
.zebra-table td { padding: 7px 12px; border-bottom: 1px solid #e0e0e0; }
.zebra-table tbody tr:nth-child(even) { background: #f0f4ff; }
.zebra-table tbody tr:nth-child(odd) { background: white; }
.zebra-table tbody tr:hover { background: #ddeeff; }
.zebra-table tbody tr:last-child td { border-bottom: none; }
.zebra-table tbody tr:first-child td { font-weight: bold; }
/* Custom checkbox */
.checkbox-group { display: flex; flex-direction: column; gap: 10px; }
.custom-check {
display: flex; align-items: center; gap: 10px;
cursor: pointer; user-select: none; font-size: .95rem;
}
.custom-check input[type="checkbox"] { display: none; }
.checkmark {
width: 20px; height: 20px; border: 2px solid #ccc;
border-radius: 4px; background: white; flex-shrink: 0;
transition: background .2s, border-color .2s;
position: relative;
}
.checkmark::after {
content: "";
position: absolute;
left: 5px; top: 2px;
width: 6px; height: 10px;
border: 2px solid white;
border-left: none; border-top: none;
transform: rotate(45deg);
opacity: 0; transition: opacity .15s;
}
.custom-check input:checked + .checkmark { background: #4472c4; border-color: #4472c4; }
.custom-check input:checked + .checkmark::after { opacity: 1; }
.custom-check:hover .checkmark { border-color: #4472c4; }
Bình luận