# CSS for Components & UI
Component-driven development requires careful consideration of interaction states, accessibility, and responsive behavior. This chapter provides patterns for building reusable UI components using modern CSS.
# Table of Contents
- Buttons & Form Controls
- Cards & Panels
- Navigation & Menus
- Modals & Overlays
- Tables & Data Grids
- Tooltips, Tabs & Accordions
- Gotchas & Best Practices
- Quick Cheatsheet
- Practice Tasks
- Review Questions
- Related Links
# Buttons & Form Controls
Buttons need accessible focus styles, disabled states, and support for prefers-reduced-motion. Use appearance: none and accent-color for customizing form controls while preserving semantics.
.btn {
appearance: none;
padding: .5rem 1rem;
border: none;
border-radius: .25rem;
background: var(--color-accent, #0070f3);
color: #fff;
font-weight: 600;
cursor: pointer;
transition: background .2s ease;
}
.btn:hover {
background: color-mix(in oklab, var(--color-accent) 80%, black);
}
.btn:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.btn:disabled {
opacity: .5;
cursor: not-allowed;
}
input[type="checkbox"] {
accent-color: var(--color-accent);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<button class="btn">Primary Button</button>
<label><input type="checkbox" checked> Checkbox with accent color</label>
2
# Cards & Panels
Cards group related content. Use box-shadow and border radii for depth. Provide clear structure with headings and footers.
.card {
background: white;
border-radius: .5rem;
padding: 1rem;
box-shadow: 0 2px 4px rgba(0,0,0,.1);
display: flex;
flex-direction: column;
gap: .5rem;
}
.card .title {
font-size: 1.25rem;
font-weight: 600;
}
.card .footer {
margin-top: auto;
font-size: .875rem;
color: #666;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="card">
<div class="title">Card Title</div>
<p>Body text goes here.</p>
<div class="footer">Footer info</div>
</div>
2
3
4
5
# Navigation & Menus
Navigation bars must adapt to screen sizes. Use flexbox for horizontal lists and convert to vertical lists on smaller viewports. Provide skip links for accessibility.
.nav {
display: flex;
flex-wrap: wrap;
list-style: none;
gap: 1rem;
padding: 0;
background: var(--nav-bg, #222);
color: white;
}
.nav a {
color: inherit;
text-decoration: none;
padding: .5rem 1rem;
}
.nav a:focus-visible {
outline: 2px dashed currentColor;
}
/* Mobile toggle */
.nav-toggle {
display: none;
}
@media (max-width: 40rem) {
.nav {
flex-direction: column;
align-items: flex-start;
}
.nav-toggle {
display: block;
margin-left: auto;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<a href="#main" class="skip-link">Skip to content</a>
<button class="nav-toggle" aria-expanded="false">Menu</button>
<nav class="nav">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</nav>
2
3
4
5
6
7
# Modals & Overlays
Modals should trap focus and respect motion preferences. Use dialog element where supported, fallback to a custom overlay. The ::backdrop pseudo-element can dim the background.
dialog {
border: none;
border-radius: .5rem;
padding: 2rem;
box-shadow: 0 8px 16px rgba(0,0,0,.25);
width: min(90%, 30rem);
}
dialog::backdrop {
background: rgba(0,0,0,.5);
}
.modal-enter {
opacity: 0;
transform: translateY(2rem);
transition: opacity .3s ease, transform .3s ease;
}
.modal-enter[data-open] {
opacity: 1;
transform: none;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dialog id="modal">
<h2>Modal Title</h2>
<p>Modal content goes here.</p>
<button onclick="modal.close()">Close</button>
</dialog>
2
3
4
5
Add script to open/close the dialog and manage focus trap; CSS alone cannot trap focus.
# Tables & Data Grids
Tables need responsive handling on narrow screens. Use overflow-x: auto or convert to stacked rows on mobile. Employ :nth-child() for zebra striping and caption-side: bottom to place captions.
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ccc;
padding: .5rem;
text-align: left;
}
tr:nth-child(even) {
background: #fafafa;
}
.responsive-table {
overflow-x: auto;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="responsive-table">
<table>
<caption>Sales Data</caption>
<thead><tr><th>Product</th><th>Price</th><th>Units</th></tr></thead>
<tbody>
<tr><td>Widget A</td><td>$10</td><td>50</td></tr>
<tr><td>Widget B</td><td>$15</td><td>30</td></tr>
</tbody>
</table>
</div>
2
3
4
5
6
7
8
9
10
# Tooltips, Tabs & Accordions
Tooltips should be accessible; use aria-describedby linking to a hidden element or title. Tabs require proper roles and keyboard interactions. Accordions need semantic heading structure and ARIA attributes.
/* Tooltip using data-* attribute */
.has-tooltip {
position: relative;
cursor: help;
}
.has-tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #333;
color: #fff;
padding: .25rem .5rem;
border-radius: .25rem;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity .2s ease;
}
.has-tooltip:hover::after {
opacity: 1;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<span class="has-tooltip" data-tooltip="Helpful information here">Hover me</span>
# Tabs & Accordions
You can implement tabs and accordions purely with CSS using the :checked pseudo-class. Ensure proper labeling for screen readers.
<div class="tabs">
<input type="radio" id="tab1" name="tabs" checked>
<label for="tab1">Tab 1</label>
<input type="radio" id="tab2" name="tabs">
<label for="tab2">Tab 2</label>
<div class="tab-content">
<section id="panel1">Content 1</section>
<section id="panel2">Content 2</section>
</div>
</div>
2
3
4
5
6
7
8
9
10
.tabs {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: auto 1fr;
}
.tabs input { display: none; }
.tabs label {
padding: .5rem; border: 1px solid #ccc; cursor: pointer;
}
.tab-content section { display: none; padding: 1rem; border: 1px solid #ccc; }
#tab1:checked ~ .tab-content #panel1,
#tab2:checked ~ .tab-content #panel2 {
display: block;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Gotchas & Best Practices
- Provide clear focus indicators on interactive elements.
- Avoid removing built-in form semantics; use
appearance: nonecautiously. - For modals, trap focus and provide
aria-modal="true"for accessibility. - Responsive tables may need additional design patterns like cards on small screens.
- Use ARIA roles (
role="tablist",role="tab",role="tabpanel") when building custom tabs.
# Quick Cheatsheet
- Buttons:
appearance,accent-color,focus-visible. - Cards:
box-shadow,border-radius, flex for vertical/horizontal alignment. - Navigation:
flex-wrap,gap,skip-link. - Modals:
dialog,::backdrop, transitions. - Tables:
border-collapse,overflow-xwrapper, zebra striping. - Tooltips/Tabs:
::aftercontent,:checkedhack.
# Practice Tasks
- Build an accessible toggle button that visually indicates its on/off state and updates ARIA attributes.
- Create a responsive navigation bar that collapses into a hamburger menu on small screens.
- Implement a modal using
<dialog>and manage its open/close with JavaScript; ensure focus trap. - Develop a responsive table that converts to card-like rows on mobile using CSS.
# Review Questions
- How does
appearance: noneaffect form controls? - What is the
dialogelement and how does it differ from custom modal implementations? - Describe how to implement zebra striping in tables with pure CSS.
- What are the requirements for accessible tabs and accordions?
- How can
accent-colorsimplify styling form controls?