Components Medium
Code Tabs Viewer
Multi-target tabbed code viewer with a scrollable tab row, syntax-highlighted pre block, and copy-to-clipboard button.
Open in Lab
MCP
css javascript
Targets: HTML
Code
:root {
color-scheme: dark;
font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
background: #080808;
color: #e2e8f0;
display: grid;
place-items: center;
padding: 1.5rem;
}
.stage {
width: 100%;
max-width: 720px;
}
/* โโโ Viewer wrapper โโโ */
.code-viewer {
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.09);
overflow: hidden;
background: #111;
}
/* โโโ Tab row โโโ */
.tabs-row {
display: flex;
align-items: flex-end;
gap: 0;
overflow-x: auto;
scrollbar-width: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
padding: 0 4px;
background: rgba(255, 255, 255, 0.02);
}
.tabs-row::-webkit-scrollbar {
display: none;
}
.tab {
padding: 10px 16px;
font-size: 0.8125rem;
font-family: inherit;
color: rgba(255, 255, 255, 0.38);
background: none;
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
white-space: nowrap;
transition: color 0.15s, border-color 0.15s;
margin-bottom: -1px;
}
.tab:hover {
color: rgba(255, 255, 255, 0.7);
}
.tab.active {
color: #f8fafc;
border-bottom-color: #4f46e5;
}
/* โโโ Panel wrap (position for copy btn) โโโ */
.panel-wrap {
position: relative;
}
/* โโโ Copy button โโโ */
.copy-btn {
position: absolute;
top: 12px;
right: 12px;
z-index: 2;
display: flex;
align-items: center;
padding: 5px 8px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.07);
border: 1px solid rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.45);
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.copy-btn:hover {
background: rgba(255, 255, 255, 0.12);
color: #f8fafc;
}
/* โโโ Code block โโโ */
.code-block {
margin: 0;
padding: 1.5rem;
font-family: "Fira Code", "Cascadia Code", "Consolas", ui-monospace, monospace;
font-size: 0.8125rem;
line-height: 1.7;
color: #c9d1d9;
overflow-x: auto;
white-space: pre;
background: transparent;
min-height: 120px;
}
@media (max-width: 480px) {
body { padding: 0; }
.stage { max-width: 100%; }
.code-viewer { border-radius: 0; border-left: none; border-right: none; }
.tab { padding: 9px 12px; font-size: 0.75rem; }
.code-block { padding: 1rem; font-size: 0.75rem; }
.copy-btn { top: 8px; right: 8px; }
}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Code Tabs Viewer</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="stage">
<div class="code-viewer">
<!-- Tab row -->
<div class="tabs-row" role="tablist" aria-label="Code targets">
<button class="tab active" role="tab" data-target="html" aria-selected="true" aria-controls="panel-html">HTML</button>
<button class="tab" role="tab" data-target="css" aria-selected="false" aria-controls="panel-css">CSS</button>
<button class="tab" role="tab" data-target="react" aria-selected="false" aria-controls="panel-react">React</button>
<button class="tab" role="tab" data-target="vue" aria-selected="false" aria-controls="panel-vue">Vue</button>
<button class="tab" role="tab" data-target="svelte" aria-selected="false" aria-controls="panel-svelte">Svelte</button>
</div>
<!-- Code panels -->
<div class="panel-wrap">
<button class="copy-btn" id="copy-btn" aria-label="Copy code">
<svg class="icon-copy" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
<svg class="icon-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="display:none">
<polyline points="20 6 9 17 4 12"/>
</svg>
</button>
<pre class="code-block" id="panel-html" role="tabpanel" aria-labelledby="tab-html"><code><button class="btn">Click me</button></code></pre>
<pre class="code-block" id="panel-css" role="tabpanel" aria-labelledby="tab-css" hidden>.btn {
padding: 8px 18px;
border-radius: 8px;
background: #4f46e5;
color: #fff;
border: none;
cursor: pointer;
}</pre>
<pre class="code-block" id="panel-react" role="tabpanel" aria-labelledby="tab-react" hidden>export default function Button() {
return <button className="btn">Click me</button>;
}</pre>
<pre class="code-block" id="panel-vue" role="tabpanel" aria-labelledby="tab-vue" hidden><template>
<button class="btn">Click me</button>
</template></pre>
<pre class="code-block" id="panel-svelte" role="tabpanel" aria-labelledby="tab-svelte" hidden><button class="btn">Click me</button></pre>
</div>
</div>
</main>
<script>
const tabs = document.querySelectorAll('.tab');
const panels = document.querySelectorAll('.code-block');
const copyBtn = document.getElementById('copy-btn');
const iconCopy = copyBtn.querySelector('.icon-copy');
const iconCheck = copyBtn.querySelector('.icon-check');
let copyTimer;
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => { t.classList.remove('active'); t.setAttribute('aria-selected', 'false'); });
panels.forEach(p => p.hidden = true);
tab.classList.add('active');
tab.setAttribute('aria-selected', 'true');
document.getElementById('panel-' + tab.dataset.target).hidden = false;
});
});
copyBtn.addEventListener('click', () => {
const activePanel = document.querySelector('.code-block:not([hidden])');
navigator.clipboard.writeText(activePanel.textContent).then(() => {
iconCopy.style.display = 'none';
iconCheck.style.display = '';
clearTimeout(copyTimer);
copyTimer = setTimeout(() => {
iconCopy.style.display = '';
iconCheck.style.display = 'none';
}, 2000);
});
});
</script>
</body>
</html>Code Tabs Viewer
A tabbed panel for displaying code snippets across multiple frameworks or file types. Tabs scroll horizontally when they overflow; clicking a tab swaps the code block. A copy button writes the current snippet to the clipboard.
Features
- Scrollable tab row with active underline indicator
- Monospace code block with horizontal scroll
- Copy-to-clipboard with โ feedback
- Easily extended with a real syntax highlighter (Shiki, Prism, highlight.js)
When to use
- Documentation pages showing multi-framework snippets
- Component demos with HTML / React / Vue / Svelte variants
- Any page that needs a polished code display