Chapter 7: CSS Links, Buttons, Icons & User Interface Controls
Interactive UI elements engage users and drive actions. Mastering link pseudo-class states, custom button designs, icon library integration (FontAwesome & SVG), custom cursors, user text selection, pointer events, and element resizing gives you complete control over web user interfaces.
7.1 Link Pseudo-Classes & State Order (:link, :visited, :hover, :active)
Anchor links (<a>) transition through distinct interactive states as users navigate a page. To ensure rules trigger correctly, pseudo-class declarations must follow the LoVe HAte (LVHA) rule order: :link → :visited → :hover → :active.
| Pseudo-Class | Order (LVHA) | Trigger Condition | Example CSS Rule |
|---|---|---|---|
:link |
1 (L) | Unvisited link that has not been clicked yet by the user. | a:link { color: #3b82f6; text-decoration: none; } |
:visited |
2 (V) | Link already visited in browser history (restricted CSS properties for privacy). | a:visited { color: #8b5cf6; } |
:hover |
3 (H) | User hovers mouse cursor over the link element. | a:hover { color: #60a5fa; text-decoration: underline; } |
:active |
4 (A) | Exact moment mouse button is clicked down on the link. | a:active { color: #ef4444; } |
:focus-visible |
Accessibility | Element receives keyboard focus (tabbed into by user). | a:focus-visible { outline: 3px solid #38bdf8; } |
The LVHA Ordering Rule
If you place a:hover before a:visited, the :visited color will override the hover state! Always define pseudo-classes in LVHA order: :link → :visited → :hover → :active.
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
h3 { margin-top: 0; color: #fbbf24; }
.link-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.link-list li { background: #1e293b; padding: 10px 16px; border-radius: 8px; }
.note { font-size: 11px; color: #64748b; margin-top: 4px; }
/* LVHA order — correct! */
a:link { color: #3b82f6; text-decoration: none; font-weight: 600; }
a:visited { color: #8b5cf6; }
a:hover { color: #60a5fa; text-decoration: underline; }
a:active { color: #ef4444; }
a:focus-visible { outline: 3px solid #38bdf8; outline-offset: 3px; border-radius: 3px; }
</style>
</head>
<body>
<h3>Link Pseudo-Class States (LVHA Order)</h3>
<p style="font-size:13px;color:#94a3b8;">Hover over each link. Press Tab to see the focus ring. Click to see :active.</p>
<ul class="link-list">
<li>
<a href="#">Unvisited Link</a> — blue (:link state)
<div class="note">a:link { color: #3b82f6 }</div>
</li>
<li>
<a href="#section2">Hover over me!</a> — turns light blue + underline (:hover)
<div class="note">a:hover { color: #60a5fa; text-decoration: underline }</div>
</li>
<li>
<a href="#section3">Click and hold me!</a> — turns red while pressed (:active)
<div class="note">a:active { color: #ef4444 }</div>
</li>
<li>
<a href="#section4">Tab to me for focus ring</a> — cyan outline (:focus-visible)
<div class="note">a:focus-visible { outline: 3px solid #38bdf8 }</div>
</li>
</ul>
</body>
</html>
7.2 Designing Custom Buttons & Smooth Micro-Interactions
Standard browser <button> tags look dated. With CSS3 gradients, borders, shadows, and transitions, you can craft modern, accessible button components.
<!DOCTYPE html>
<html>
<head>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
h3 { margin-top: 0; color: #818cf8; }
.btn-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 20px; }
/* Base button reset */
button { font-family: inherit; font-size: 14px; font-weight: 700; border: none; cursor: pointer; user-select: none; }
/* 1. Gradient Primary Button */
.btn-primary {
background: linear-gradient(135deg, #4f46e5, #06b6d4);
color: #fff;
padding: 12px 24px;
border-radius: 8px;
box-shadow: 0 4px 14px rgba(79,70,229,0.35);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(79,70,229,0.5); }
.btn-primary:active { transform: translateY(0); }
.btn-primary:focus-visible { outline: 3px solid #38bdf8; outline-offset: 3px; }
/* 2. Outline Ghost Button */
.btn-outline {
background: transparent;
color: #6366f1;
padding: 11px 23px;
border: 2px solid #6366f1;
border-radius: 8px;
transition: background 0.2s, color 0.2s;
}
.btn-outline:hover { background: #6366f1; color: #fff; }
.btn-outline:focus-visible { outline: 3px solid #818cf8; outline-offset: 3px; }
/* 3. Pill Button */
.btn-pill {
background: linear-gradient(135deg, #10b981, #059669);
color: #fff;
padding: 10px 28px;
border-radius: 9999px;
box-shadow: 0 4px 12px rgba(16,185,129,0.4);
transition: transform 0.2s, box-shadow 0.2s;
}
.btn-pill:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(16,185,129,0.6); }
/* 4. Icon Button */
.btn-icon {
background: #1e293b;
color: #f59e0b;
padding: 10px 20px;
border-radius: 8px;
border: 1px solid #334155;
display: flex; align-items: center; gap: 8px;
transition: background 0.2s, color 0.2s;
}
.btn-icon:hover { background: #f59e0b; color: #0f172a; }
/* 5. Disabled Button */
.btn-disabled {
background: #334155; color: #64748b;
padding: 12px 24px; border-radius: 8px;
opacity: 0.6; cursor: not-allowed; pointer-events: none;
}
</style>
</head>
<body>
<h3>Custom Button Designs — Hover to see micro-interactions!</h3>
<div class="btn-row">
<button class="btn-primary">Gradient Primary</button>
<button class="btn-outline">Ghost Outline</button>
<button class="btn-pill">Pill Shape</button>
<button class="btn-icon">⭐ Icon Button</button>
<button class="btn-disabled" disabled>Disabled</button>
</div>
<p style="font-size:12px;color:#64748b;">Hover lifts buttons (translateY). Click shows :active press-down. Tab shows :focus-visible ring.</p>
</body>
</html>
7.3 Icon Libraries (FontAwesome & Inline SVG Styling)
Icons provide visual affordance for web navigation and buttons. Icons can be added via web icon fonts (FontAwesome) or styled directly using inline SVG vectors.
FontAwesome Icon Fonts
Font icons inherit CSS font properties (color, font-size, text-shadow):
<!DOCTYPE html>
<html>
<head>
<!-- FontAwesome CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<style>
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
h3 { margin-top: 0; color: #818cf8; }
.icon-row { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; margin-bottom: 16px; }
.icon-btn {
display: flex; align-items: center; gap: 8px;
background: #1e293b; border: 1px solid #334155; border-radius: 8px;
padding: 10px 16px; font-size: 14px; font-weight: 600; cursor: pointer;
color: #f8fafc; transition: background 0.2s, color 0.2s;
}
.icon-btn i { transition: color 0.2s; }
.icon-btn:hover { background: #6366f1; }
.icon-btn:hover i { color: #fff; }
.icon-showcase { display: flex; flex-wrap: wrap; gap: 18px; }
.icon-item { text-align: center; }
.icon-item i { font-size: 1.8rem; display: block; margin-bottom: 6px; }
.icon-item span { font-size: 11px; color: #64748b; }
</style>
</head>
<body>
<h3>FontAwesome Icons — Styling with CSS</h3>
<div class="icon-row">
<button class="icon-btn"><i class="fas fa-paper-plane" style="color:#38bdf8;"></i> Send Message</button>
<button class="icon-btn"><i class="fas fa-heart" style="color:#ef4444;"></i> Like Post</button>
<button class="icon-btn"><i class="fas fa-download" style="color:#10b981;"></i> Download</button>
<button class="icon-btn"><i class="fas fa-share-nodes" style="color:#f59e0b;"></i> Share</button>
</div>
<div class="icon-showcase">
<div class="icon-item"><i class="fas fa-rocket" style="color:#6366f1;"></i><span>fa-rocket</span></div>
<div class="icon-item"><i class="fas fa-star" style="color:#f59e0b;"></i><span>fa-star</span></div>
<div class="icon-item"><i class="fas fa-shield-halved" style="color:#10b981;"></i><span>fa-shield</span></div>
<div class="icon-item"><i class="fas fa-code" style="color:#38bdf8;"></i><span>fa-code</span></div>
<div class="icon-item"><i class="fas fa-fire" style="color:#ef4444;"></i><span>fa-fire</span></div>
</div>
</body>
</html>
Inline SVG Styling (fill & stroke)
SVG vector paths are styled in CSS using vector properties (fill, stroke, stroke-width):
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
h3 { margin-top: 0; color: #34d399; }
.svg-row { display: flex; flex-wrap: wrap; gap: 20px; align-items: center; }
.svg-item { text-align: center; }
.svg-item span { font-size: 11px; color: #64748b; display: block; margin-top: 6px; font-family: monospace; }
/* SVG fill styling */
svg.icon-star { width: 40px; height: 40px; fill: #f59e0b; stroke: #d97706; stroke-width: 1; transition: fill 0.3s, transform 0.2s; cursor: pointer; }
svg.icon-star:hover { fill: #ef4444; transform: scale(1.2); }
svg.icon-heart { width: 40px; height: 40px; fill: #ef4444; stroke: none; transition: fill 0.3s, transform 0.2s; cursor: pointer; }
svg.icon-heart:hover { fill: #f43f5e; transform: scale(1.2); }
/* Stroke-only SVG (outline style) */
svg.icon-circle { width: 40px; height: 40px; fill: none; stroke: #38bdf8; stroke-width: 3; transition: stroke 0.3s; cursor: pointer; }
svg.icon-circle:hover { stroke: #6366f1; }
</style>
</head>
<body>
<h3>Inline SVG — fill, stroke, :hover</h3>
<div class="svg-row">
<div class="svg-item">
<svg class="icon-star" viewBox="0 0 24 24">
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26"/>
</svg>
<span>fill: #f59e0b<br/>hover → red + scale</span>
</div>
<div class="svg-item">
<svg class="icon-heart" viewBox="0 0 24 24">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
</svg>
<span>fill: #ef4444<br/>hover → scale up</span>
</div>
<div class="svg-item">
<svg class="icon-circle" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>
<span>fill: none<br/>stroke: #38bdf8</span>
</div>
</div>
</body>
</html>
7.4 User Interface Controls: cursor, user-select, pointer-events & resize
CSS provides precise control over user input interactions, cursor feedback, text highlighting, and click passthrough behavior.
| UI Property | Popular Values | Behavior & Practical Use Case |
|---|---|---|
cursor |
pointer, grab, not-allowed, wait, crosshair |
Changes the mouse pointer cursor when hovering over the target element. |
user-select |
none, text, all |
Controls whether text can be highlighted/selected by the user. Use user-select: none on buttons and UI tabs! |
pointer-events |
none, auto |
When set to pointer-events: none, the element becomes completely invisible to mouse clicks and touches (clicks pass through to elements beneath). |
resize |
both, horizontal, vertical, none |
Allows users to manually drag-resize elements (requires overflow: auto or scroll). Commonly used on <textarea>. |
<!DOCTYPE html>
<html>
<head>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
h3 { margin-top: 0; color: #60a5fa; }
.cursor-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 20px; }
.cursor-box {
background: #1e293b; border: 1px solid #334155; border-radius: 8px;
padding: 12px 16px; font-size: 13px; font-family: monospace; color: #94a3b8;
}
/* Cursor varieties */
.c-pointer { cursor: pointer; }
.c-grab { cursor: grab; }
.c-not-allowed { cursor: not-allowed; }
.c-wait { cursor: wait; }
.c-crosshair { cursor: crosshair; }
.c-text { cursor: text; }
/* user-select demos */
.no-select { user-select: none; background: rgba(239,68,68,0.12); border: 1px solid #ef4444; border-radius:8px; padding:12px; margin-bottom:10px; }
.all-select { user-select: all; background: rgba(16,185,129,0.12); border: 1px solid #10b981; border-radius:8px; padding:12px; margin-bottom:10px; }
/* Resizable box */
.resizable-card {
background: #1e293b; border: 2px solid #3b82f6; border-radius: 10px;
padding: 16px; resize: both; overflow: auto;
min-width: 200px; min-height: 80px; max-width: 100%;
}
</style>
</head>
<body>
<h3>cursor, user-select, pointer-events & resize</h3>
<p style="font-size:13px;color:#94a3b8;">Hover each box to see the cursor change:</p>
<div class="cursor-row">
<div class="cursor-box c-pointer">cursor: pointer 👆</div>
<div class="cursor-box c-grab">cursor: grab ✊</div>
<div class="cursor-box c-not-allowed">cursor: not-allowed 🚫</div>
<div class="cursor-box c-wait">cursor: wait ⏳</div>
<div class="cursor-box c-crosshair">cursor: crosshair ⊕</div>
<div class="cursor-box c-text">cursor: text 𝐼</div>
</div>
<div class="no-select">
<strong>user-select: none</strong> — Try selecting this text! You can't. Used for buttons, tabs, and UI labels.
</div>
<div class="all-select">
<strong>user-select: all</strong> — Click once to select all this text instantly! Great for code snippets and copy-paste zones.
</div>
<p style="font-size:13px;color:#94a3b8;">Drag the bottom-right corner to resize:</p>
<div class="resizable-card">
<strong>resize: both</strong> — drag the corner handle to resize this box in both directions! Requires overflow: auto.
</div>
</body>
</html>
7.5 Interactive Sandbox — Buttons, Icons & UI Controls
Test custom buttons, cursor states, SVG hover fills, and user-select: none protections live in the interactive code playground below: