Tabs

Advanced Custom Fields

If working with ACF, you will want to utilize the following fields:

Repeater Field: tabs

Fields:

  • Text: tab_title
  • WYSIWYG: tab_content

Markup

HTML

<div class="Tabs">

    <nav class="Tabs-nav">
        <a href="#" class="Tabs-link is-active">Tab 1</a>
        <a href="#" class="Tabs-link">Tab 2</a>
    </nav><!-- //.Tabs-nav -->
    
    <div class="Tab is-active">
        Tab 1 Content
    </div><!-- //.Tab -->
    
    <div class="Tab">
        Tab 2 Content
    </div><!-- //.Tab -->
    
</div><!-- //.Tabs -->

PHP

<?php
$nav_count = 0;
$tab_count = 0;
?>
<?php if ( have_rows( 'tabs' ) ): ?>
    
    <div class="Tabs">
    
        <?php while( have_rows( 'tabs' ) ): the_row(); ?>
            <nav class="Tabs-nav">
                <a href="#" class="Tabs-link <?php echo ( $nav_count == 0 ? 'is-active': '' ); ?>"><?php the_sub_field( 'tab_title' ); ?></a>
            </nav><!-- //.Tabs-nav -->
            <?php $nav_count++; ?>
        <?php endwhile; ?>
        
        <?php while( have_rows( 'tabs' ) ): the_row(); ?>
            <div class="Tab <?php echo ( $tab_count == 0 ? 'is-active': '' ); ?>">
                <?php the_sub_field( 'tab_content' ); ?>
            </div><!-- //.Tab -->
            <?php $tab_count++; ?>
        <?php endwhile; ?>
            
    </div><!-- //.Tabs -->

<?php endif; ?>

Twig

<div class="Tabs">
    
    <nav class="Tabs-nav">

        {% for tab in post.get_field( 'tabs' ) %}
            <a href="#" class="Tabs-link {{ (loop.first) ? 'is-active' : '' }}">{{tab.tab_title}}</a>
        {% endfor %}

    </nav><!-- //.Tabs-nav -->
    
    {% for tab in post.get_field( 'tabs' ) %}
        <div class="Tab {{ (loop.first) ? 'is-active' : '' }}">
            {{tab.tab_content}}
        </div><!-- //.Tab -->
    {% endfor %}
    
</div><!-- //.Tabs -->

Styles

CSS

.Tabs-link {
    background: #f9f9f9;
    color: #ccc;
    display: inline-block;
    padding: 10px 15px;
    text-decoration: none;
    transition: all 0.3s ease-in-out;
}

.Tabs-link:hover,
.Tabs-link.is-active {
    background: #eee;
    color: #222;
}

.Tab {
    border: 2px solid #eee;
    display: none;
    padding: 30px 15px;
}

.Tab.is-active {
    display: block;
}

Sass

.Tabs-link {
    background: #f9f9f9;
    color: #ccc;
    display: inline-block;
    padding: 10px 15px;
    text-decoration: none;
    transition: all 0.3s ease-in-out;

    &:hover,
    &.is-active {
        background: #eee;
        color: #222;
    }
}

.Tab {
    border: 2px solid #eee;
    display: none;
    padding: 30px 15px;

    &.is-active {
        display: block;
    }
}

Javascript

Plugin

/**
 * Objectiv Tabs
 *
 */

( function() {
    'use strict';

    // Let's make sure that we have an Objectiv object
    if ( 'object' !== typeof window.Objectiv) {
        window.Objectiv = {};
    }

    window.Objectiv.tabs = function(options) {
        // Make sure that a target is passed into the options object
        if ('undefined' === typeof options.target)
            return false;

        // Set up the variables
        var tabs = document.querySelector(options.target),
            tabLinks = tabs.getElementsByClassName('Tabs-link'),
            tabContainers = tabs.getElementsByClassName('Tab'),
            activeIndex = 0;
        
        // If we don't have tabs, jump out
        if (!tabs) return;

        // Click handler function to go to tab on click
        function tabClickHandler(link, index) {
            link.addEventListener('click', function(e) {
                e.preventDefault();
                goToTab(index);
            });
        }   

        // Add appropriate classes based on the current index
        function goToTab(index) {
            if (index !== activeIndex && index >= 0 && index <= tabLinks.length) {
                tabLinks[activeIndex].classList.remove('is-active');
                tabLinks[index].classList.add('is-active');
                tabContainers[activeIndex].classList.remove('is-active');
                tabContainers[index].classList.add('is-active');

                tabLinks[activeIndex].setAttribute( 'aria-selected', 'false' );
                tabLinks[activeIndex].setAttribute( 'aria-expanded', 'false' );
                tabContainers[activeIndex].setAttribute( 'aria-hidden', 'true' );

                tabLinks[index].setAttribute( 'aria-selected', 'true' );
                tabLinks[index].setAttribute( 'aria-expanded', 'true' );
                tabContainers[index].setAttribute( 'aria-hidden', 'false' );

                
                activeIndex = index;
            }

            
        }

        // Loop through the links and set the clickhandler
        Array.prototype.map.call(tabLinks, function(value, index) {
            var link = value;
            tabClickHandler(link, index);

            if (link.classList.contains('is-active')) {
                link.setAttribute( 'aria-selected', 'true' );
                link.setAttribute( 'aria-expanded', 'true' );
            } 
        });

        // Loop through containers and add appropriate attributes
        Array.prototype.map.call(tabContainers, function(value, index) {
            var content = value;
            
            content.setAttribute( 'role', 'tabpanel' );
            if (content.classList.contains('is-active')) {
                content.setAttribute( 'aria-hidden', 'false' );
            } else {
                content.setAttribute( 'aria-hidden', 'true' );
            }
        });
    }
})();

Usage

Objectiv.tabs({
	target: '.Tabs', // ID (or class) of accordion container
});