Scrolling tabs HTML and CSS only

19,514

Solution 1

So this would probably be better done using JavaScript, but a solution does exist in CSS, if you're willing to allow certain constraints on widths and heights and where the scrollbar ends up.

The basic idea is to make the tabs themselves be inline-block, and have their container have overflow-x: auto. That will put a horizontal scrollbar on the container when the tabs get too big.

That said, that requires the container to have a fixed size, and you have to force the content to have a fixed size too if you're going to keep the content elements inside each of the tabs' divs. I wouldn't want to build it this way if I could avoid it, but this is a pure HTML-and-CSS solution.

A runnable snippet, derived from your original, is below. I set the width on the container specifically to 300px so you can see what the horizontal overflow looks like.

.tabs {
    position: relative;   
    min-height: 200px;
    clear: both;
    margin: 0;
    padding-top: 15px;
    width: 300px;
    white-space: nowrap;
    overflow-x: auto;
}

.tab {
    display: inline-block;
    vertical-align: top;
}

.tab label {
    background: #eee; 
    padding: 10px; 
    border: 1px solid #ccc; 
    margin-left: -1px;
    cursor:pointer;
    position: relative;
    left: 1px;
}

.tab [type=radio] {
    display: none;
}

.tab .content {
    position: fixed;
    white-space: normal;
    top: 50px;
    left: 0;
    background: white;
    height: 100px;
    right: 0;
    bottom: 0;
    padding: 20px;
    width: 266px;
    border: 1px solid #ccc;
}

.tab [type=radio]:checked ~ label {
    background: white;
    border-bottom: 1px solid white;
    z-index: 2;
}

.tab [type=radio]:checked ~ label ~ .content {
    z-index: 1;
}
<div class="tabs">
   <div class="tab">
      <input name="tab-group-1" id="tab-1" type="radio"/>
      <label for="tab-1">Tab One</label>
      <div class="content">
         Content 1
      </div> 
   </div>
   <div class="tab">
      <input name="tab-group-1" id="tab-2" type="radio"/>
      <label for="tab-2">Tab Two</label>
      <div class="content"> Content 2 </div> 
   </div>
   <div class="tab">
      <input name="tab-group-1" id="tab-3" type="radio"/>
      <label for="tab-3">Tab Three</label>
      <div class="content"> Content 3 </div> 
   </div>
   <div class="tab">
      <input name="tab-group-1" id="tab-4" type="radio"/>
      <label for="tab-4">Tab Four</label>
      <div class="content"> Content 4 </div> 
   </div>
</div>

Update: Per request, here's a way to have the horizontal scrollbar sandwiched between the tabs and the content. Bear in mind that this is even hackier than the above solution, really abusing overflow and height control, but it's doable. Just don't try to put anything below the content divs, because that definitely won't work well.

.tabs {
    position: relative;   
    clear: both;
    margin: 0;
    padding-top: 15px;
    width: 300px;
    height: 45px;
    white-space: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
}

.tab {
    display: inline-block;
    vertical-align: top;
}

.tab label {
    background: #eee; 
    padding: 10px; 
    border: 1px solid #ccc; 
    margin-left: -1px;
    cursor:pointer;
    position: relative;
    left: 1px;
}

.tab [type=radio] {
    display: none;
}

.tab .content {
    position: fixed;
    white-space: normal;
    top: 70px;
    left: 0;
    background: white;
    height:100px;
    right: 0;
    bottom: 0;
    padding: 20px;
    width: 266px;
    border: 1px solid #ccc;
 }

.tab [type=radio]:checked ~ label {
    background: white;
    border-bottom: 1px solid white;
    z-index: 2;
}

.tab [type=radio]:checked ~ label ~ .content {
    z-index: 1;
}
<div class="tabs">
   <div class="tab">
      <input name="tab-group-1" id="tab-1" type="radio"/>
      <label for="tab-1">Tab One</label>
      <div class="content">
         Content 1
      </div> 
   </div>
   <div class="tab">
      <input name="tab-group-1" id="tab-2" type="radio"/>
      <label for="tab-2">Tab Two</label>
      <div class="content"> Content 2 </div> 
   </div>
   <div class="tab">
      <input name="tab-group-1" id="tab-3" type="radio"/>
      <label for="tab-3">Tab Three</label>
      <div class="content"> Content 3 </div> 
   </div>
   <div class="tab">
      <input name="tab-group-1" id="tab-4" type="radio"/>
      <label for="tab-4">Tab Four</label>
      <div class="content"> Content 4 </div> 
   </div>
</div>

Solution 2

This can be done by moving the labels into a separate div and using the focus hack to highlight the tabs. Here is a working JSFiddle.

<head>
    <title>Test Page</title>
    <style>
        .wrapper
        {   
            width:800px;
            margin-left:auto;
            margin-right:auto;
        }

        .tab-radio
        {
            display:none;
        }

        .tabs
        {
            font-size: 0;
            margin: 0;
        }

        .labels
        {
            overflow-y: auto;
            white-space: nowrap;
        }

        .labels .tab-label
        {
            background: #eee; 
            border: 1px solid #ccc; 
            display: inline-block;
            font-size: 1rem;
            left: 1px;
            margin-left: -1px;
            padding: 5px; 
            position: relative;
            vertical-align: bottom;
        }

        .tabs .tab-panel
        {
            position: fixed;
            display: inline-block;
            overflow: hidden;
            position: relative;
            height: 0;
            width: 0;
        }

        .tabs .tab-content
        {
            display: block;
            float: left;
            font-size: 1rem;
            width: 100%;
            white-space: normal;
        }

        span:focus
        {
            outline: none;
        }

        span:focus > .tab-label
        {
            background: white;
        }

        .tabs .tab [type="radio"]:checked ~ .tab-panel
        {
            display: inline;
        }
    </style>
</head>
<body>
    <div class="wrapper">
        <div class="labels">
            <span tabindex="0">
                <label class="tab-label" for="tab-tab1">Tab Caption 1</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-0">Tab Caption 2</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-1">Tab Caption 3</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-2">Tab Caption 4</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-3">Tab Caption 5</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-4">Tab Caption 6</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-5">Tab Caption 7</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-6">Tab Caption 8</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-7">Tab Caption 9</label>
            </span>
            <span tabindex="0">
                <label class="tab-label" for="tab-tab2-8">Tab Caption 10</label>
            </span>
        </div>
        <div class="tabs">
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab1" name="tabs-main" checked>
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 1</h2>
                        <p>A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text. A long piece of text.</p>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-0" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 2</h2>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-1" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 3</h2>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-2" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 4</h2>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-3" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 5</h2>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-4" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 6</h2>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-5" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 7</h2>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-6" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 8</h2>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-7" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 9</h2>
                    </div>
                </div>
            </div>
            <div class="tab">
                <input class="tab-radio" type="radio" id="tab-tab2-8" name="tabs-main">
                <div class="tab-panel">
                    <div class="tab-content">
                        <h2>Tab 10</h2>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
Share:
19,514

Related videos on Youtube

David Kris
Author by

David Kris

Updated on June 04, 2022

Comments

  • David Kris
    David Kris almost 2 years

    I was wondering if there is anyway to create scrolling tabs without using JavaScript. I've been assigned a task to strictly use HTML and CSS, that's it. The code below is the best solution I could find to create tabs without using anything else besides HTML and CSS. I've found some code for scrolling tabs online, but again it uses JavaScript. So, is it possible to do scrolling tabs with just HTML and CSS? Anything helps, thanks!

    .tabs
    {position: relative;   
    min-height: 200px;
    clear: both;
    margin: 25px 0;}
    
    .tab
    {float: left;}
    
    .tab label
    {background: #eee; 
    padding: 10px; 
    border: 1px solid #ccc; 
    margin-left: -1px;
    cursor:pointer;
    position: relative;
    left: 1px; }
    
    .tab [type=radio]
    {display: none;}
    
    .content {position: absolute;
    top: 28px;
    left: 0;
    background: white;
    height:100px;
    right: 0;
    bottom: 0;
    padding: 20px;
    border: 1px solid #ccc;}
    
    [type=radio]:checked ~ label
    {background: white;
    border-bottom: 1px solid white;
    z-index: 2;}
    
    [type=radio]:checked ~ label ~ .content
    {z-index: 1;}
    <div class="tabs">
       <div class="tab">
          <input name="tab-group-1" id="tab-1" type="radio"/>
          <label for="tab-1">Tab One</label>
          <div class="content">
             Content 1
          </div> 
       </div>
       <div class="tab">
          <input name="tab-group-1" id="tab-2" type="radio"/>
          <label for="tab-2">Tab Two</label>
          <div class="content"> Content 2 </div> 
       </div>
    </div>

  • David Kris
    David Kris about 7 years
    I found this for scrolling HTML and CSS only. Anyway to incorporate this to what I have? w3schools.com/howto/howto_css_menu_horizontal_scroll.asp
  • b2ok
    b2ok about 7 years
    If it is what you look for, let use that.
  • David Kris
    David Kris about 7 years
    Not quite, I need tabs so different tab headings display different content in the box, but if I have too many tab headings the scroll bar pops up. That's a nav bar linked to different pages. It has the scrolling function I'm looking for but I couldn't find a way to use it in my code.
  • b2ok
    b2ok about 7 years
    What about drop-down, select option, option, etc? Maby is posible all tabs incorporate like options in select element?
  • David Kris
    David Kris about 7 years
    Is there anyway to have the scrollbar underneath the tab headings, but above the display box?
  • Sean Werkema
    Sean Werkema about 7 years
    Well, yes, and I've updated the answer with a solution that does that (with no shortage of caveats to it), but we're getting into really hacky CSS territory here. I would not reasonably expect other markup on the page to play nicely around those tabs. But what you're asking is at least possible.
  • Dan Bray
    Dan Bray over 6 years
    It should be white-space: normal for the content class, instead of white-space: wrap.
  • Sean Werkema
    Sean Werkema over 6 years
    Oh, good point, and I don't know how I missed that when I wrote my answer. I've updated it with the fix.
  • Wayne Barron
    Wayne Barron almost 2 years
    I was using the code that Dan Bray provided in one post above but ran into some strange issues under Google Chrome in Windows 10. So, I looked back at this code, your second option here, and it works without the weird behavior.
  • Wayne Barron
    Wayne Barron almost 2 years
    It seems there is an issue with your code Dan, in Google Chrome on Windows 10, to where if you have a textarea embedded in the tab at a width larger than 30% would cause a scrollbar to appear at the bottom of the container.