Open demo

CSS code

HTML
<div class="btabs">
  <div role="tablist" aria-label="Files">
    <button type="button" role="tab" id="bt-tab-0" aria-selected="true" aria-controls="bt-panel-0">index.html</button>
    <button type="button" role="tab" id="bt-tab-1" aria-selected="false" aria-controls="bt-panel-1" tabindex="-1">style.css</button>
    <button type="button" role="tab" id="bt-tab-2" aria-selected="false" aria-controls="bt-panel-2" tabindex="-1">app.js</button>
  </div>
  <div class="panel">
    <div role="tabpanel" id="bt-panel-0" aria-labelledby="bt-tab-0" tabindex="0"><h2>Browser-style boxed tabs</h2><p>The active tab fuses into its panel — same background, shared border — exactly like browser or editor tabs. A one-pixel offset does the trick.</p></div>
    <div role="tabpanel" id="bt-panel-1" aria-labelledby="bt-tab-1" tabindex="0" hidden><h2>Styling</h2><p>Inactive tabs sit slightly darker and behind; hover lifts them a step. Everything is border-radius and two grays.</p></div>
    <div role="tabpanel" id="bt-panel-2" aria-labelledby="bt-tab-2" tabindex="0" hidden><h2>Behavior</h2><p>The same short tab script as the rest of the pack: click or arrow keys to switch, Home and End to jump.</p></div>
  </div>
</div>
CSS
/* Boxed tabs: the selected tab shares the panel's background and border, and a 1px offset fuses the two. */
.btabs{--bt-tab:#d6dde6;--bt-tab-hover:#e2e8f0;--bt-muted:#4a576d;--bt-text:#1c2430;--bt-border:#dde3ec;--bt-focus:#2563eb;
  box-sizing:border-box;width:100%;max-width:560px;color:var(--bt-text)}
.btabs [role=tablist]{display:flex;gap:6px;padding:0 10px}
.btabs [role=tab]{position:relative;top:1px;margin:0;border:1px solid transparent;border-bottom:0;background:var(--bt-tab);color:var(--bt-muted);
  font:inherit;font-size:.87rem;font-weight:700;padding:11px 20px 13px;border-radius:12px 12px 0 0;cursor:pointer;transition:background-color .15s}
.btabs [role=tab]:hover{background:var(--bt-tab-hover)}
.btabs [role=tab][aria-selected=true]{background:#fff;border-color:var(--bt-border);color:var(--bt-text)}
.btabs .panel{background:#fff;border:1px solid var(--bt-border);border-radius:14px;padding:26px 28px;box-shadow:0 16px 36px -22px rgba(20,30,50,.25)}
.btabs [role=tabpanel]{border-radius:6px}
.btabs h2{margin:0 0 8px;font-size:1rem}
.btabs p{margin:0;font-size:.88rem;line-height:1.7;color:var(--bt-muted)}
.btabs [role=tab]:focus-visible{outline:2px solid var(--bt-focus);outline-offset:-4px}
.btabs [role=tabpanel]:focus-visible{outline:2px solid var(--bt-focus);outline-offset:6px}
.btabs [hidden]{display:none}
@media (max-width:480px){.btabs [role=tab]{padding:11px 14px 13px}.btabs .panel{padding:22px 20px}}
@media (prefers-reduced-motion:reduce){.btabs [role=tab]{transition:none}}
JavaScript
/* WAI-ARIA tabs: click or use the arrow keys to select, Home/End jump to the first/last tab.
   Only the selected tab is in the Tab order (roving tabindex). */
document.querySelectorAll('.btabs').forEach(root => {
  const list = root.querySelector('[role=tablist]');
  const tabs = [...list.querySelectorAll('[role=tab]')];
  const select = (tab, focus) => {
    tabs.forEach(t => {
      const on = t === tab;
      t.setAttribute('aria-selected', on);
      t.tabIndex = on ? 0 : -1;
      document.getElementById(t.getAttribute('aria-controls')).hidden = !on;
    });
    if (focus) tab.focus();
  };
  list.addEventListener('click', e => { const t = e.target.closest('[role=tab]'); if (t) select(t); });
  list.addEventListener('keydown', e => {
    const i = tabs.indexOf(e.target), n = tabs.length;
    const j = { ArrowRight: i + 1, ArrowDown: i + 1, ArrowLeft: i - 1 + n, ArrowUp: i - 1 + n, Home: 0, End: n - 1 }[e.key];
    if (i < 0 || j === undefined) return;
    e.preventDefault();
    select(tabs[j % n], true);
  });
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS, Bootstrap 5.3.8 JS bundle

HTML
<div class="btabs">
  <ul class="nav nav-tabs" role="tablist" aria-label="Files">
    <li class="nav-item" role="presentation"><button class="nav-link active" type="button" id="bt-tab-0" data-bs-toggle="tab" data-bs-target="#bt-panel-0" role="tab" aria-controls="bt-panel-0" aria-selected="true">index.html</button></li>
    <li class="nav-item" role="presentation"><button class="nav-link" type="button" id="bt-tab-1" data-bs-toggle="tab" data-bs-target="#bt-panel-1" role="tab" aria-controls="bt-panel-1" aria-selected="false" tabindex="-1">style.css</button></li>
    <li class="nav-item" role="presentation"><button class="nav-link" type="button" id="bt-tab-2" data-bs-toggle="tab" data-bs-target="#bt-panel-2" role="tab" aria-controls="bt-panel-2" aria-selected="false" tabindex="-1">app.js</button></li>
  </ul>
  <div class="tab-content">
    <div class="tab-pane active" id="bt-panel-0" role="tabpanel" aria-labelledby="bt-tab-0" tabindex="0"><h2>Browser-style boxed tabs</h2><p>The active tab fuses into its panel — same background, shared border — exactly like browser or editor tabs. A one-pixel offset does the trick.</p></div>
    <div class="tab-pane" id="bt-panel-1" role="tabpanel" aria-labelledby="bt-tab-1" tabindex="0"><h2>Styling</h2><p>Inactive tabs sit slightly darker and behind; hover lifts them a step. Everything is border-radius and two grays.</p></div>
    <div class="tab-pane" id="bt-panel-2" role="tabpanel" aria-labelledby="bt-tab-2" tabindex="0"><h2>Behavior</h2><p>The same short tab script as the rest of the pack: click or arrow keys to switch, Home and End to jump.</p></div>
  </div>
</div>
CSS
/* Bootstrap .nav-tabs with the Tab plugin, restyled through the --bs-nav-tabs-* variables. The active tab's white bottom border fuses it into the panel. */
.btabs{--bt-tab:#d6dde6;--bt-tab-hover:#e2e8f0;--bt-muted:#4a576d;--bt-text:#1c2430;--bt-border:#dde3ec;--bt-focus:#2563eb;
  width:100%;max-width:560px;color:var(--bt-text)}
.btabs .nav-tabs{--bs-nav-link-padding-x:20px;--bs-nav-link-padding-y:11px;--bs-nav-link-font-size:.87rem;--bs-nav-link-font-weight:700;
  --bs-nav-link-color:var(--bt-muted);--bs-nav-link-hover-color:var(--bt-muted);
  --bs-nav-tabs-border-radius:12px;--bs-nav-tabs-link-hover-border-color:transparent;
  --bs-nav-tabs-link-active-color:var(--bt-text);--bs-nav-tabs-link-active-bg:#fff;
  --bs-nav-tabs-link-active-border-color:var(--bt-border) var(--bt-border) #fff;
  flex-wrap:nowrap;gap:6px;padding:0 10px;border-bottom:0}
.btabs .nav-link{padding-bottom:12px;background:var(--bt-tab);line-height:normal;white-space:nowrap;transition:background-color .15s}
.btabs .nav-link:hover{background:var(--bt-tab-hover)}
.btabs .tab-content{background:#fff;border:1px solid var(--bt-border);border-radius:14px;padding:26px 28px;box-shadow:0 16px 36px -22px rgba(20,30,50,.25)}
.btabs .tab-pane{border-radius:6px}
.btabs h2{margin:0 0 8px;font-size:1rem;font-weight:700;line-height:normal}
.btabs p{margin:0;font-size:.88rem;line-height:1.7;color:var(--bt-muted)}
.btabs .nav-link:focus-visible{box-shadow:none;outline:2px solid var(--bt-focus);outline-offset:-4px}
.btabs .tab-pane:focus-visible{outline:2px solid var(--bt-focus);outline-offset:6px}
@media (max-width:480px){.btabs .nav-tabs{--bs-nav-link-padding-x:14px}.btabs .tab-content{padding:22px 20px}}
@media (prefers-reduced-motion:reduce){.btabs .nav-link{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="btabs w-full max-w-[560px] text-bt-text">
  <div role="tablist" aria-label="Files" class="flex gap-1.5 px-2.5">
    <button type="button" role="tab" id="bt-tab-0" aria-selected="true" aria-controls="bt-panel-0" class="relative top-px cursor-pointer rounded-t-xl border border-b-0 border-transparent bg-bt-tab px-5 pt-[11px] pb-[13px] text-[.87rem] font-bold text-bt-muted transition-colors duration-150 hover:bg-bt-tab-hover focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-bt-focus aria-selected:border-bt-border aria-selected:bg-white aria-selected:text-bt-text motion-reduce:transition-none max-[480px]:px-3.5">index.html</button>
    <button type="button" role="tab" id="bt-tab-1" aria-selected="false" aria-controls="bt-panel-1" tabindex="-1" class="relative top-px cursor-pointer rounded-t-xl border border-b-0 border-transparent bg-bt-tab px-5 pt-[11px] pb-[13px] text-[.87rem] font-bold text-bt-muted transition-colors duration-150 hover:bg-bt-tab-hover focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-bt-focus aria-selected:border-bt-border aria-selected:bg-white aria-selected:text-bt-text motion-reduce:transition-none max-[480px]:px-3.5">style.css</button>
    <button type="button" role="tab" id="bt-tab-2" aria-selected="false" aria-controls="bt-panel-2" tabindex="-1" class="relative top-px cursor-pointer rounded-t-xl border border-b-0 border-transparent bg-bt-tab px-5 pt-[11px] pb-[13px] text-[.87rem] font-bold text-bt-muted transition-colors duration-150 hover:bg-bt-tab-hover focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-bt-focus aria-selected:border-bt-border aria-selected:bg-white aria-selected:text-bt-text motion-reduce:transition-none max-[480px]:px-3.5">app.js</button>
  </div>
  <div class="rounded-[14px] border border-bt-border bg-white px-7 py-[26px] shadow-[0_16px_36px_-22px_rgba(20,30,50,.25)] max-[480px]:px-5 max-[480px]:py-[22px]">
    <div role="tabpanel" id="bt-panel-0" aria-labelledby="bt-tab-0" tabindex="0" class="rounded-md focus-visible:outline-2 focus-visible:outline-offset-[6px] focus-visible:outline-bt-focus"><h2 class="mb-2 text-[1rem] font-bold">Browser-style boxed tabs</h2><p class="text-[.88rem] leading-[1.7] text-bt-muted">The active tab fuses into its panel — same background, shared border — exactly like browser or editor tabs. A one-pixel offset does the trick.</p></div>
    <div role="tabpanel" id="bt-panel-1" aria-labelledby="bt-tab-1" tabindex="0" hidden class="rounded-md focus-visible:outline-2 focus-visible:outline-offset-[6px] focus-visible:outline-bt-focus"><h2 class="mb-2 text-[1rem] font-bold">Styling</h2><p class="text-[.88rem] leading-[1.7] text-bt-muted">Inactive tabs sit slightly darker and behind; hover lifts them a step. Everything is border-radius and two grays.</p></div>
    <div role="tabpanel" id="bt-panel-2" aria-labelledby="bt-tab-2" tabindex="0" hidden class="rounded-md focus-visible:outline-2 focus-visible:outline-offset-[6px] focus-visible:outline-bt-focus"><h2 class="mb-2 text-[1rem] font-bold">Behavior</h2><p class="text-[.88rem] leading-[1.7] text-bt-muted">The same short tab script as the rest of the pack: click or arrow keys to switch, Home and End to jump.</p></div>
  </div>
</div>
CSS
@theme {
  --color-bt-tab: #d6dde6;
  --color-bt-tab-hover: #e2e8f0;
  --color-bt-muted: #4a576d;
  --color-bt-text: #1c2430;
  --color-bt-border: #dde3ec;
  --color-bt-focus: #2563eb;
}
JavaScript
/* WAI-ARIA tabs: click or use the arrow keys to select, Home/End jump to the first/last tab.
   Only the selected tab is in the Tab order (roving tabindex). */
document.querySelectorAll('.btabs').forEach(root => {
  const list = root.querySelector('[role=tablist]');
  const tabs = [...list.querySelectorAll('[role=tab]')];
  const select = (tab, focus) => {
    tabs.forEach(t => {
      const on = t === tab;
      t.setAttribute('aria-selected', on);
      t.tabIndex = on ? 0 : -1;
      document.getElementById(t.getAttribute('aria-controls')).hidden = !on;
    });
    if (focus) tab.focus();
  };
  list.addEventListener('click', e => { const t = e.target.closest('[role=tab]'); if (t) select(t); });
  list.addEventListener('keydown', e => {
    const i = tabs.indexOf(e.target), n = tabs.length;
    const j = { ArrowRight: i + 1, ArrowDown: i + 1, ArrowLeft: i - 1 + n, ArrowUp: i - 1 + n, Home: 0, End: n - 1 }[e.key];
    if (i < 0 || j === undefined) return;
    e.preventDefault();
    select(tabs[j % n], true);
  });
});

About this set

Folder-style tabs, labelled index.html, style.css and app.js, that sit on top of a white panel the way browser and code editor tabs do. Inactive tabs are a darker grey and lighten on hover, while the selected tab turns white, takes the panel’s border on three sides and overlaps the panel’s top edge by one pixel, so the two fuse into a single shape. Use it for code samples, file viewers, dashboards and any interface where the tabs should feel like physical dividers rather than links. The panel is a normal block and each tabpanel inside it can hold any content. The script sets aria-selected, the roving tabindex and hidden panels, and nothing about the look depends on it. The Bootstrap version is stock .nav-tabs recoloured through the –bs-nav-tabs-* variables, where the active link’s white bottom border does the fusing, and the Tailwind version uses a relative top-px offset with aria-selected utilities for the same effect.

What’s included

  • Selected tab fuses into the panel with a 1px overlap
  • Two greys for inactive and hovered tabs
  • Bootstrap version is stock .nav-tabs restyled by variables
  • Inactive tab text darkened to reach 4.5:1 on grey
  • Narrower tab padding on small screens

Accessibility

The file names are tabs in a tablist labelled Files, each controlling a panel labelled by its tab. Only the selected tab is in the Tab order; Left and Right (or Up and Down) move between tabs and select them, and Home and End jump to the first and last tab. Keyboard focus draws a 2px blue outline inside the tab and around the panel, and the background transition is removed under prefers-reduced-motion.

Customise it

Change --bt-tab, --bt-tab-hover, --bt-muted, --bt-text and --bt-border on .btabs in plain CSS and Bootstrap; in Bootstrap the corner radius and active colours come from --bs-nav-tabs-border-radius and --bs-nav-tabs-link-active-*. In Tailwind edit the --color-bt-* theme colours.