Skip to content

Commit 285e566

Browse files
authored
Merge pull request #871 from ItsMeAnanyaSrivastava/feature/accessible-tabs
Add Accessible CSS-only Tabs demo (Tabs/Accessible-Tabs)
2 parents c6396f7 + e4d4522 commit 285e566

File tree

3 files changed

+193
-0
lines changed

3 files changed

+193
-0
lines changed

Tabs/Accessible-Tabs/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
```markdown
2+
# Accessible CSS-only Tabs
3+
4+
A small demo that implements tabbed content using only HTML and CSS (radio inputs + labels).
5+
This shows how to switch panels without JavaScript.
6+
7+
## Files
8+
- `index.html` — demo HTML
9+
- `style.css` — styles and layout
10+
- `README.md` — this file
11+
12+
## How to use
13+
1. Open `index.html` in your browser.
14+
2. Click the tab labels to switch panels.
15+
3. The demo uses `<input type="radio">` and `<label for="...">` to control which panel is displayed.
16+
17+
## Accessibility notes
18+
- The pattern uses semantic roles: `role="tablist"`, `role="tab"` and `role="tabpanel"`.
19+
- Keyboard navigation using Tab to focus labels works; however, arrow-key navigation between tabs and advanced focus management require JavaScript for full WAI-ARIA tab behavior. Document this limitation in the demo README so reviewers know it's intentional.
20+
- Ensure focus-visible styles are present (they are in `style.css`).
21+
22+
## Contribution
23+
- Add your name and GitHub handle to the "Contributed By" section if you submit this demo.
24+
- Follow the repository CONTRIBUTING.md: fork → branch → add folder (`Tabs/Accessible-Tabs/`) → update top-level README index if needed → PR.
25+
26+
## Contributed by
27+
- Your Name — https://github.com/itsmeananyasrivastava

Tabs/Accessible-Tabs/index.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width,initial-scale=1" />
6+
<title>Accessible CSS-only Tabs — You Don't Need JavaScript</title>
7+
<link rel="stylesheet" href="style.css" />
8+
</head>
9+
<body>
10+
<main class="demo">
11+
<h1 class="title">Accessible CSS-only Tabs</h1>
12+
13+
<div class="tabs" role="tablist" aria-label="Sample tabs">
14+
<!-- Radio controls (visually hidden but focusable) -->
15+
<input type="radio" name="tabs" id="tab1" checked>
16+
<label for="tab1" role="tab" aria-controls="panel1" class="tab">Overview</label>
17+
18+
<input type="radio" name="tabs" id="tab2">
19+
<label for="tab2" role="tab" aria-controls="panel2" class="tab">Features</label>
20+
21+
<input type="radio" name="tabs" id="tab3">
22+
<label for="tab3" role="tab" aria-controls="panel3" class="tab">Accessibility</label>
23+
24+
<!-- Panels -->
25+
<section id="panel1" class="panel" role="tabpanel" tabindex="0" aria-labelledby="tab1">
26+
<h2>Overview</h2>
27+
<p>This demo shows how to implement tabbed content with only HTML and CSS using radio buttons and labels. Switching tabs uses :checked state; no JavaScript required.</p>
28+
</section>
29+
30+
<section id="panel2" class="panel" role="tabpanel" tabindex="0" aria-labelledby="tab2" hidden>
31+
<h2>Features</h2>
32+
<ul>
33+
<li>Pure HTML + CSS (no JavaScript)</li>
34+
<li>Responsive layout</li>
35+
<li>Visible focus styles for keyboard users</li>
36+
</ul>
37+
</section>
38+
39+
<section id="panel3" class="panel" role="tabpanel" tabindex="0" aria-labelledby="tab3" hidden>
40+
<h2>Accessibility</h2>
41+
<p>This pattern uses semantic roles: <code>role="tablist"</code>, <code>role="tab"</code> and <code>role="tabpanel"</code>. Note: full WAI-ARIA tab keyboard support (arrow key navigation & roving focus) requires JS; this demo documents that limitation.</p>
42+
</section>
43+
</div>
44+
</main>
45+
</body>
46+
</html>

Tabs/Accessible-Tabs/style.css

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
:root{
2+
--accent:#4f46e5;
3+
--muted:#6b7280;
4+
--bg:#0f172a;
5+
--card:#0b1220;
6+
--radius:10px;
7+
--gap:12px;
8+
--text:#e6eef8;
9+
}
10+
11+
*{box-sizing:border-box}
12+
body{
13+
font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
14+
margin:0;
15+
min-height:100vh;
16+
color:var(--text);
17+
background:linear-gradient(180deg,#06102a 0%, #071024 50%, #071028 100%);
18+
display:flex;
19+
align-items:center;
20+
justify-content:center;
21+
padding:32px;
22+
}
23+
24+
.demo{
25+
width:100%;
26+
max-width:900px;
27+
}
28+
29+
.title{
30+
font-size:1.4rem;
31+
margin:0 0 16px 0;
32+
color:var(--text);
33+
text-align:center;
34+
}
35+
36+
.tabs{
37+
background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
38+
border-radius:var(--radius);
39+
padding:16px;
40+
box-shadow: 0 6px 22px rgba(2,6,23,0.6);
41+
display:grid;
42+
grid-template-columns: repeat(3, minmax(0,1fr));
43+
gap:var(--gap);
44+
align-items:start;
45+
}
46+
47+
/* Visually hide inputs but keep them accessible/focusable */
48+
input[type="radio"]{
49+
position: absolute;
50+
width: 1px;
51+
height: 1px;
52+
margin: -1px;
53+
padding: 0;
54+
border: 0;
55+
overflow: hidden;
56+
clip: rect(0 0 0 0);
57+
white-space: nowrap;
58+
}
59+
60+
/* Style labels as tabs */
61+
.tab{
62+
display:inline-flex;
63+
align-items:center;
64+
justify-content:center;
65+
padding:10px 14px;
66+
background:transparent;
67+
color:var(--muted);
68+
border-radius:8px;
69+
cursor:pointer;
70+
user-select:none;
71+
transition: all 200ms ease;
72+
border:1px solid transparent;
73+
font-weight:600;
74+
}
75+
76+
/* When the associated input is focused, style the label so keyboard users see it */
77+
input[type="radio"]:focus + .tab,
78+
.tab:focus{
79+
outline:3px solid rgba(79,70,229,0.18);
80+
outline-offset:2px;
81+
}
82+
83+
/* Layout: labels across top and panels below on small screens */
84+
@media (max-width:640px){
85+
.tabs{grid-template-columns: 1fr; grid-auto-rows: auto; }
86+
.tab{ justify-content:flex-start; }
87+
}
88+
89+
/* Panels style */
90+
.panel{
91+
grid-column: 1 / -1; /* span full width */
92+
background: linear-gradient(180deg, rgba(255,255,255,0.01), rgba(255,255,255,0.02));
93+
padding:18px;
94+
margin-top:8px;
95+
border-radius:8px;
96+
border:1px solid rgba(255,255,255,0.03);
97+
color:var(--text);
98+
box-shadow: inset 0 1px 0 rgba(255,255,255,0.02);
99+
}
100+
101+
/* Default hidden panels; show the one matching the :checked sibling */
102+
.panel{ display:none; }
103+
#tab1:checked ~ #panel1{ display:block; }
104+
#tab2:checked ~ #panel2{ display:block; }
105+
#tab3:checked ~ #panel3{ display:block; }
106+
107+
/* Active tab visual: style label for the checked input */
108+
#tab1:checked + label[for="tab1"],
109+
#tab2:checked + label[for="tab2"],
110+
#tab3:checked + label[for="tab3"]{
111+
color:var(--text);
112+
background: linear-gradient(90deg, rgba(79,70,229,0.12), rgba(79,70,229,0.06));
113+
border:1px solid rgba(79,70,229,0.2);
114+
box-shadow: 0 6px 18px rgba(15,23,42,0.6);
115+
transform: translateY(-4px);
116+
}
117+
118+
/* Small enhancements */
119+
.panel h2{ margin-top:0; color:var(--accent); }
120+
.panel p, .panel ul{ color: #cbd5e1; line-height:1.45; }

0 commit comments

Comments
 (0)