A tab section is used to organize related content into separate tabs within a section. It combines a title, optional description, and optional call-to-action with a tabbed interface that can contain various types of content blocks.
The tab section pattern is composed of the following elements:
| Element | Description |
|---|---|
| Title (required) | Main heading text (h2) |
| Description | Optional description text |
| Call-to-Action | Optional CTA buttons/links |
| Tabs (required) | Tabs that hold a variety of content blocks |
The tab section pattern supports responsive grid layouts that adapt to different screen sizes.
Note: the blocks supported vary by layout. See content blocks for details.
The full-width layout spans the entire content area with the tabs taking up all available space.
The 50/50 layout creates a two-column grid that splits evenly on large screens. The first column contains the title (and optional description/CTA), while the second column (50% width) contains the tabs.
The 25/75 layout creates an asymmetrical two-column layout where the first column (25%) contains the title (and optional description/CTA), and the second column (75%) contains the tabs.
By default, the tab section has a default horizontal rule at the top.
You can customize this or disable it entirely using the top_rule_variant parameter.
Supported variants:
"default" - Standard horizontal rule"muted" - Lighter/muted horizontal rule"none" - No rule displayedTab sections support customizable padding options using the section pattern. By default, the pattern is wrapped in a regular section with default padding.
For different spacing needs, you may instead use:
"default" - p-section for standard spacing (default)"deep" - p-section--deep for maximum spacing"shallow" - p-section--shallow for reduced spacingPlease refer to the section documentation for more guidance on padding selection.
The title is the main heading of the section.
By default, it is an <h2> heading.
You may customize the heading level and/or wrap the title in a link.
The title can be made into a link by providing link attributes.
{
"title": {
"text": "Your title here",
"link_attrs": {
"href": "#"
}
}
}
text: The title text to displaylink_attrs: HTML attributes for the anchor element (href, class, etc.)heading_level: Optional. Heading level (2, 3, or 4). Default: 2You can customize the heading level of the title.
{
"title": {
"text": "Your title here",
"heading_level": 3
}
}
Add a description to provide context or additional information.
{
"description": {
"content": "Your description text here",
"type": "text"
}
}
content: The description text or HTML contenttype: Optional. Either "text" (default) or "html". Text content is wrapped in <p> tags, HTML content is
rendered as-is.Add CTA buttons and/or links to encourage user action.
{
"cta": {
"primary": {
"content_html": "Primary button text",
"attrs": {
"href": "link-url",
"class": "optional-css-class"
}
},
"secondaries": [
{
"content_html": "Secondary button text",
"attrs": {
"href": "link-url"
}
}
],
"link": {
"content_html": "Link text",
"attrs": {
"href": "link-url"
}
}
}
}
primary: Optional primary button configurationsecondaries: Optional array of secondary button configurations (typically 0-2)link: Optional text link configurationEach CTA configuration accepts:
content_html: The inner HTML of the CTA itemattrs: HTML attributes for the button/link. If href is present, the element will be an <a> tag, otherwise a
<button> tag.Tab sections use a flexible block model where each tab contains a single content block. The type of block determines what configuration is required.
Note: Different layouts support different content block types. When you provide a tab with an unsupported block type for the selected layout, it will be silently skipped. Always ensure your content blocks match your chosen layout:
| Block Type | Full-width | 50/50 | 25/75 |
|---|---|---|---|
| Quote | ✓ | ✗ | ✗ |
| Linked Logo | ✓ | ✓ | ✓ |
| Logo Block | ✓ | ✓ | ✓ |
| Divided Section | ✗ | ✓ | ✗ |
| Blog | ✓ | ✓ | ✓ |
| Basic Section | ✗ | ✓ | ✗ |
Block type can be chosen with the tabs[].type property.
tabs.item contains the block's configuration, which varies from block to block and is demonstrated in the JSON snippets below.
Quote blocks display testimonials or highlighted quotes with optional attribution and images.
{
"signpost": {
"image": {
"html": "<img src=\"image-url\" alt=\"Logo\">"
}
},
"contents": {
"citation": {
"name": "Person name",
"organisation": "Organization",
"title": "Job title"
},
"body": {
"size": "large",
"text": "The quote text here"
},
"cta": {
"html": "<a href=\"#\">Learn more ›</a>"
},
"image": {
"html": "<img src=\"image-url\" alt=\"\">"
}
}
}
signpost (Object, Optional) – Signpost configuration with imagecontents (Object, Required) – Quote contents with:citation (Object, Optional) - Attribution details:name (String, Optional) – Person nameorganisation (String, Optional) – Organization nametitle (String, Optional) – Job titlebody (Object, Optional) – Quote body with:size: "small", "medium", or "large"text: The quote textcta (Object, Optional): CTA configuration with:html (String, required): HTML content for the CTA linkimage (Object, Optional): Image configuration with:html (String, required): HTML content for the imageThe linked logo block displays a set of clickable logos.
{
"links": [
{
"href": "link-url",
"text": "Link text",
"label": "Aria label",
"image_attrs": {
"src": "logo-url",
"alt": "alt-text",
"width": "200",
"height": "100"
}
}
]
}
links (Array, Required) - Array of link objects. Each link has:href: URL the logo links totext: Text for the linklabel: Aria label for accessibilityimage_attrs: Image attributes (src, alt, width, height, etc.)The logo block displays a cloud of logos.
{
"is_fixed_width": true,
"logos": [
{
"attrs": {
"src": "logo-url",
"alt": "alt-text"
},
"has_line_break_after": false
}
]
}
is_fixed_width (Boolean, Optional) - Whether to wrap logos in a fixed-width container. Default: truelogos (Array, Required) - Array of logo objects with:attrs: Image attributes (src, alt, class, etc.)has_line_break_after: Whether to force a line break after this logoThe divided section block displays divided section content blocks.
{
"blocks": [
{
"type": "description",
"item": {
"type": "text",
"content": "Content here"
}
}
]
}
blocks (Array, Required) - Array of content blocks following the divided section block API.The blog block displays a grid of blog articles.
Blog articles may be defined statically (as shown below), or pulled dynamically. See the blog docs for more information.
{
"articles": [
{
"title": {
"text": "How to enable Real-time Ubuntu on your machine",
"link_attrs": {
"href": "#"
}
},
"description": {
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
},
"metadata": {
"authors": [
{
"text": "John Doe",
"link_attrs": {
"href": "#"
}
}
],
"date": {
"text": "15 March 2025"
}
}
},
{
"title": {
"text": "How to enable Real-time Ubuntu on your machine",
"link_attrs": {
"href": "#"
}
},
"description": {
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
},
"metadata": {
"authors": [
{
"text": "John Doe",
"link_attrs": {
"href": "#"
}
}
],
"date": {
"text": "15 March 2025"
}
}
}
]
}
articles (Array, Required) - Array of article objectstemplate_config (Object, Optional) - Additional template configuration for dynamically pulling blog articles. See dynamic blog for details.The basic section block allows you to embed basic section content blocks.
{
"items": [
{
"type": "description",
"item": {
"type": "text",
"content": "Content here"
}
},
{# .. other items, following the basic section content block API #}
]
}
items (Array, Required) - Array of basic section content blocks. See basic section content blocks for details.The vf_tab_section Jinja macro can be used to generate a tab section pattern. The API for the macro is shown below.
| Name | Required? | Type | Default | Description |
|---|---|---|---|---|
title
|
Yes |
Object
|
N/A
|
Title configuration object with text and optional link_attrs and heading_level
|
title.text
|
Yes |
string
|
N/A
|
The main title text (rendered as h2 by default) |
title.link_attrs
|
No |
Object
|
N/A
|
Attributes of an anchor element, as a dictionary. See attribute forwarding docs for more info. |
title.heading_level
|
No |
number
|
2
|
Heading level for the title (2, 3, or 4) |
description
|
No |
Object
|
{}
|
Description configuration object with content and optional type.
|
description.content
|
No |
string
|
""
|
Description text or HTML content |
description.type
|
No |
"text" | "html"
|
"text"
|
Content type. "text" wraps content in <p>, "html" renders as-is. |
cta
|
No |
Object
|
{}
|
Call-to-action configuration with primary, secondaries, and/or link. Only displayed in 50/50 and 25/75 layouts.
|
layout
|
No |
One of:'full-width','50-50','25-75'
|
'50-50'
|
Layout variant for the section. Different layouts allow different content block types. |
padding
|
No |
One of:'deep','shallow','default'
|
'default'
|
Padding variant for the entire section. See section pattern for details. |
top_rule_variant
|
No |
One of:'default','muted','none'
|
'default'
|
Variant of horizontal rule to display at the top of the section. |
tabs
|
Yes |
Array<Object>
|
N/A
|
Array of tab configurations. See Content Blocks for structure details. Unsupported blocks for the selected layout will be silently skipped. |
tabs[].type
|
Yes |
One of:'quote','linked-logo','logo-block','divided-section','blog','basic-section'
|
N/A
|
The content block type for this tab. Availability depends on the layout parameter.
|
tabs[].item
|
Yes |
Object
|
N/A
|
Configuration specific to the block type. See Content Blocks for details on each type. |
tabs[].tab_html
|
Yes |
string
|
""
|
HTML content for the tab label. |
attrs
|
No |
Object
|
N/A
|
Attributes to apply to the section element, as a dictionary. See attribute forwarding docs for more info. |
To import the Tab Section Jinja macro, copy the following import statement into your Jinja template.
{% from "_macros/vf_tab-section.jinja" import vf_tab_section %}
View the building with Jinja macros guide for macro installation instructions.
The tabs JS module must be added to the page in order for tabs to function.
Please follow the Vanilla JS module installation instructions. The specific import depends on your build process:
If using Webpack to bundle Vanilla JS, import and initialize the tabs component on your page:
// Import the 'tabs' module
import {tabs} from 'vanilla-framework/js';
// Initialize the tabs component
tabs.initTabs('[role="tablist"]');
If not using Webpack or a similar bundler, copy the module to your static directory in a build stage, and add the tabs module directly to your page:
<script type="module" src="/static/js/modules/vanilla-framework/js/tabs.js"></script>
Since Patterns leverage many other parts of Vanilla in their composition and content, we recommend importing the entirety of Vanilla for full support.