WordPress.org

WordPress Developer Blog

Styling accordions in WordPress 6.9

Styling accordions in WordPress 6.9

An Accordion block is officially coming to WordPress. If you’re a theme designer like me, you’re probably breaking out the fireworks already. Boom! Pop! Hooray!

And if you’re anything like me, you’ve likely already been tinkering with the block’s design, creating cool customizations. It’s OK if you haven’t gotten there already. That’s what this guide is for.

In this tutorial, let’s walk through customizing the Accordion block output so that it matches whatever theme that you’re designing.

To test the Accordion block before WordPress 6.9 is released, install the latest version of the Gutenberg plugin or WordPress 6.9 Beta. Also keep up with any changes made to the block during the final stages of the 6.9 release cycle.

A use case: FAQs

Whenever I test new blocks for WordPress, I begin thinking of the various scenarios that I’d use it for. A FAQs page immediately came to mind.

In this tutorial, you’ll learn how to style the Accordion block—and its nested blocks—so that you or your users can quickly build a FAQs page like this:

That’s a screenshot of a personal theme project, so your colors, typography, and other stylistic elements will likely look different. But that’s the point: build what makes sense for your design.

And if you don’t have a theme of your own to tinker with right now, use Twenty Twenty-Five.

To build your FAQs page, begin by creating a new page via the Pages Add Page screen in the WordPress admin. Then add an Accordion block, which will automatically insert a nested Accordion Item, Accordion Heading, and Accordion Panel.

Go ahead and add a few items of your own. Or, if you prefer the easy route, you can copy the block markup from this GitHub Gist and paste it into the editor’s content area.

A look at the default Accordion design

If you added the blocks to the editor from the previous step, you probably noticed that your FAQs page looks much different than the screenshot of the finished design from earlier:

It’s a bare-bones implementation of accordions, and I like that approach. It means that I don’t have to fight quite as many specificity battles as is sometimes necessary when styling Core blocks. It’s nearly a blank canvas for folks like you and I to use whatever brush strokes we’d like on.

The Accordion block is not a single block. There are actually four with a nested structure:

  • Accordion: Primary container block for nested Accordion Items.
  • Accordion Item: Direct child of the Accordion block that contains Accordion Heading and Accordion Panel blocks.
  • Accordion Heading: An <h3> tag that wraps a <button> element for opening/closing the Accordion Panel.
  • Accordion Panel: A container block where you can insert custom blocks, such as Paragraph, Image, and more.

It’s also worth looking at the markup that it produces, as shown below. Note that I redacted some attributes since they’re unrelated to design.

<div class="wp-block-accordion is-layout-flow wp-block-accordion-is-layout-flow">
	<div class="wp-block-accordion-item is-layout-flow wp-block-accordion-item-is-layout-flow is-open">
		<h3 class="wp-block-accordion-heading">
			<button aria-expanded="true" class="wp-block-accordion-heading__toggle">
				<span class="wp-block-accordion-heading__toggle-title">Heading Text</span>
				<span class="wp-block-accordion-heading__toggle-icon" aria-hidden="true">+</span>
			</button>
		</h3>
		<div class="wp-block-accordion-panel is-layout-flow wp-block-accordion-panel-is-layout-flow">
			<!-- Custom Blocks Here. -->
		</div>
	</div>
</div>

You can get some decent designs with theme.json alone, but to really take this to the next level, you’ll likely be using custom CSS. In the next sections, I’ll walk you through how I handle styling everything.

Styling Accordion and nested blocks

To style Accordion and its child blocks, you start with the styles.blocks object in theme.json. It will look similar to this:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 3,
	"styles": {
		"blocks": {
		}
	}
}

You don’t need to add any styles for the wrapping Accordion block itself for these next steps (you’re only styling the nested blocks). But if you needed to, you’d target styles.blocks.core/accordion in your theme.json.

Styling the Accordion Item block

To style the Accordion Item block, you target styles.blocks.core/accordion-item. In the earlier screenshot, this was the most styled piece of the design since it acts as the wrapper for each nested element.

Add this code to the styles.blocks object in theme.json:

"core/accordion-item": {
	"border": {
		"color": "#d5dae2",
		"style": "solid",
		"width": "1px",
		"radius": "12px"
	},
	"color": {
		"background": "#f6f7f9",
		"text": "#343b46"
	},
	"shadow": "0 10px 15px -3px #80000080, 0 4px 6px -4px #80000080",
	"spacing": {
		"blockGap": "0"
	}
}

This adds border, color, shadow, and spacing styles. The example code above and throughout the tutorial uses several hard-coded CSS values, but in a real-world project, you should be referencing presets in theme.json styles.

If you refresh your editor screen, you should see your changes applied, which is a good start:

Styling the Accordion Heading block

With the Accordion Item wrapper styles in place, it’s time to tackle the Accordion Header so that it looks like the proposed FAQs design.

If you remember from the HTML output I shared earlier, you probably noted that this block is an <h3> element. That means any styles applied to headings or the <h3> element in particular will also apply to this block. In the below code, I’m overwriting my theme’s heading typography to essentially reset it, but you may need more styles for your theme.

Add this code to your styles.blocks object in theme.json:

"core/accordion-heading": {
	"css": "&__toggle { padding: var(--wp--preset--spacing--40) var(--wp--style--block-gap); }",
	"typography": {
		"fontFamily": "inherit",
		"fontSize": "inherit",
		"fontStyle": "inherit",
		"fontWeight": "500",
		"lineHeight": "inherit",
		"textTransform": "inherit"
	}
}

The code contains the css property that adds some padding to the nested toggle button. There’s no way to target this otherwise in theme.json. I also didn’t want the padding on the Accordion Heading block itself since I wanted the button to stretch the full width and height of it.

To learn more about the css attribute in theme.json, check out this tutorial on how to use it.

Now let’s refresh the screen to see how far we’ve come:

Pretty close!

Styling the Accordion Panel block

The final piece to styling via theme.json is to add padding to the Accordion Panel block so that there’s some breathing room between the content and the edges of the container.

Add this code to the styles.blocks object in theme.json:

"core/accordion-panel": {
	"spacing": {
		"padding": {
			"top": "var(--wp--style--block-gap)",
			"bottom": "var(--wp--style--block-gap)",
			"left": "var(--wp--style--block-gap)",
			"right": "var(--wp--style--block-gap)"
		}
	}
}

After refreshing the edit screen, you should see this design:

It’s not a 100% exact match with the original screenshot, but it’s nearly there. Now we need to step outside of theme.json.

Fine-tuning with CSS

The primary missing piece of the design is a different background color that is applied to the toggle button when it’s hovered/focused or when the Accordion Panel is open.

You’ll use a block stylesheet to handle styling in this case. First, create a file named block-accordion.css and place it in your theme’s /assets folder (or your preferred location for custom stylesheets). 

Then, enqueue that stylesheet by adding this code to your theme’s functions.php file:

add_action( 'init', 'themeslug_enqueue_block_styles' );

function themeslug_enqueue_block_styles() {
	wp_enqueue_block_style( 'core/accordion', array(
		'handle' => 'themeslug-block-accordion',
		'src'    => get_theme_file_uri( "assets/block-accordion.css" ),
		'path'   => get_theme_file_path( "assets/block-accordion.css" )
	) );
}

If you’re asking why you’re creating a stylesheet for the Accordion block rather than Accordion Heading, that’s a good question—the toggle button is an element within the Accordion Heading, afterall. In this case, I prefer to keep it all in one stylesheet because each of the accordion-related blocks will always be loaded together. Plus, I’ll walk you through some other styles later in this tutorial, so one stylesheet makes it easier.

Inside the block-accordion.css file, add this CSS:

/* Add toggle button background on open/hover/focus. */
.wp-block-accordion-item.is-open .wp-block-accordion-heading__toggle,
.wp-block-accordion-heading__toggle:hover,
.wp-block-accordion-heading__toggle:focus {
	background: #eceff2; /* Use a preset here. */
}

And voila! You now have a nicely styled Accordion block.

The above code changes the background color to a darker gray when the panel is open or the toggle button is hovered/focused. But you can play around with it a bit more.

You could stick these custom styles inside the css property in theme.json as you did in a previous step, but that method gets a little unwieldy once you’ve added more than a few bits of CSS.

Offering users a design choice with Block Style Variations

One of the enjoyable things about accordions in web design is that you can style them in so many ways. There’s really no limit to what you can do.

Let’s walk through registering an Accordion block style variation named Minimal. The final design will look like this:

Create a style variation JSON file

WordPress 6.6 introduced block style variations via JSON, making it easier than ever to create custom styles for any block. Using this technique, you will learn how to build a style variation for the Accordion block.

Create a new file named accordion-minimal.json in your theme’s styles folder (or in a subfolder, such as styles/block, if you prefer).

I prefer to prefix single-block style variation slugs with the block name. This makes it easier for me to know which block they belong to. It also reduces custom CSS since I can reference the class directly without also targeting the block. For example, .is-style-accordion-minimal is cleaner than .wp-block-accordion.is-style-minimal.

Add this code to your accordion-minimal.json file:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 3,
	"title": "Minimal",
	"slug": "accordion-minimal",
	"blockTypes": [ "core/accordion" ],
	"styles": {
		"css": "& .wp-block-accordion-item { margin-top: 0; } & > :first-child { border-top: none; }",
		"spacing": {
			"blockGap": "0"
		},
		"blocks": {
			"core/accordion-item": {
				"border": {
					"radius": "0",
					"bottom": {
						"width": "0"
					},
					"left": {
						"width": "0"
					},
					"right": {
						"width": "0"
					}
				},
				"color": {
					"background": "transparent"
				},
				"shadow": "none"
			},
			"core/accordion-panel": {
				"spacing": {
					"padding": {
						"top": "0",
						"left": "0",
						"right": "0"
					}
				}
			}
		}
	}
}

Essentially, this is just resetting many of the styles in the default block design for your theme. But I did want to point out the css property:

"css": "& .wp-block-accordion-item { margin-top: 0; } & > :first-child { border-top: none; }",

This is doing two things:

  • Removes the top margin from the Accordion Item. You might be thinking that spacing.blockGap should’ve done this. In theory, it should, but there is currently a bug preventing it from applying to block style variations.
  • Removes the top border from the first child (Accordion Item) to create the “divider” effect. 

I like to keep these styles in the JSON file rather than in a separate CSS file since they are directly related to the other styles defined in the JSON.

Add custom CSS

Even though this is a more minimal design than the first, it has extensive toggle and toggle icon styles. And there’s no good way to target those items via JSON. That means you need to use a little custom CSS.

Open your assets/block-accordion.css file and add this code to it:

/* Reset heading toggle padding. */
.is-style-accordion-minimal .wp-block-accordion-heading__toggle {
	padding-left: 0;
	padding-right: 0;
}

/* Reset heading toggle background on open/hover/focus. */
.is-style-accordion-minimal .wp-block-accordion-item.is-open .wp-block-accordion-heading__toggle,
.is-style-accordion-minimal .wp-block-accordion-heading__toggle:hover,
.is-style-accordion-minimal .wp-block-accordion-heading__toggle:focus {
	background: transparent;
}

/* Define toggle icon design. */
.is-style-accordion-minimal .wp-block-accordion-heading__toggle-icon {
	background: #f6f7f9; /* Use a preset here. */
	display: inline-block;
	padding: 0.5rem;
	line-height: 1;
	text-align: center;
	border-radius: calc(infinity * 1px);
}

/* Define the toggle icon background on open/hover/focus. */
.is-style-accordion-minimal .wp-block-accordion-item.is-open .wp-block-accordion-heading__toggle-icon,
.is-style-accordion-minimal .wp-block-accordion-heading__toggle:hover .wp-block-accordion-heading__toggle-icon,
.is-style-accordion-minimal .wp-block-accordion-heading__toggle:focus .wp-block-accordion-heading__toggle-icon {
	background: #eceff2; /* Use a preset here. */
}

It looks like more code than it really is. Mostly, it needs to target the different states the panel might be in: open, toggle hover, and toggle focus. Aside from some more extensive customization of the icon, it’s mostly minimal adjustments.

Accordion-based patterns

The previous steps should get you started on your creative journey with the Accordion block. Beyond that, just have fun and tinker with the design. 

But there is something you can do to take this to the next level for yourself, clients, or theme users: build patterns with the Accordion block! Let’s take what I covered throughout this tutorial and apply it to a FAQs pattern, using the Minimal block style variation:

Patterns make it so much easier to hit the ground running with these more complex nested block use cases.

Create a new faqs-accordion.php file inside your theme’s patterns folder. Then add this code:

<?php

/**
 * Title: FAQs
 * Slug: themeslug/faqs-accordion
 * Description: Outputs an accordion of configurable FAQ items.
 * Categories: text
 * Keywords: faq, accordion, toggle, questions, answers
 * Viewport Width: 640
 * Block Types: core/accordion
 */

// Prevent direct access.
defined('ABSPATH') || exit;

?>
<!-- wp:accordion {"className":"is-style-accordion-minimal"} -->
<div role="group" class="wp-block-accordion is-style-accordion-minimal">

	<?php foreach (range(1, 4) as $number): ?>

		<!-- wp:accordion-item -->
		<div class="wp-block-accordion-item">
			<!-- wp:accordion-heading -->
			<h3 class="wp-block-accordion-heading"><button class="wp-block-accordion-heading__toggle"><span class="wp-block-accordion-heading__toggle-title"><?= esc_html( sprintf( __( 'Question %d', 'themeslug' ), $number ) ); ?></span><span class="wp-block-accordion-heading__toggle-icon" aria-hidden="true">+</span></button></h3>
			<!-- /wp:accordion-heading -->

			<!-- wp:accordion-panel -->
			<div role="region" class="wp-block-accordion-panel">
				<!-- wp:paragraph -->
				<p><?php esc_html_e( 'Answer goes here.', 'themeslug' ); ?></p>
				<!-- /wp:paragraph -->
			</div>
			<!-- /wp:accordion-panel -->
		</div>
		<!-- /wp:accordion-item -->

	<?php endforeach; ?>

</div>
<!-- /wp:accordion -->

You’ll notice that I used a foreach loop with PHP’s range() function to output four Accordion Item blocks. This keeps the pattern file clean without unnecessarily duplicating code. Plus, you can quickly change the second parameter inside range() to output the number of items that you want.

Give the pattern a try. You’ll find it under the Text category in the inserter in the editor.

Then go out and build additional patterns. And build even more Accordion block style variations. There’s so much you can do with it. I know I’m going to have a lot of fun with this block in the coming weeks, months, and years.

Update: If you’d like to add Schema.org microdata (structured data) for FAQs, read this Snippet on filtering the output with the HTML API.

Props to @bph and @welcher for feedback and review on this post.

10 responses to “Styling accordions in WordPress 6.9”

  1. BlackStar Avatar

    How to change tags? Use in button tags for question that not very useful for each times.

    1. Justin Tadlock Avatar

      A button element is appropriate for an accessible accordion. I would not recommend changing it, but if you really wanted to, you could filter the output via the render_block hook.

      You could do the same with the icon that you asked about below if you wanted to or just hide it and use your own icon via CSS.

      1. Andry Avatar
        Andry

        I’m not talking about the tag, I’m talking about the tag. It’s wrong here. H3 is a semantic tag and we can’t use it anywhere we want. This element will change the SEO of the page.

        by default has type=”submit”… So if this accordion is in a tag, and someone accidentally clicks on this element, they will be submitted to the form…

        + I don’t see aria labels. Blind people will not see correct description of element

        1. Justin Tadlock Avatar

          You could also change the heading tag with the same filter. But I encourage you to file any issues during this beta testing phase so that they can be addressed before 6.9 is released.

  2. BlackStar Avatar

    How change letter “+” to own custom icon ?

  3. leemon Avatar

    Hi Justin,

    Thanks for this incredibly detailed breakdown! The new Accordion block and the ability to customize it so thoroughly with theme.json and custom CSS is great.

    I noticed the guide focuses on visual styling, which is very helpful. I was wondering if there are any plans or built-in mechanisms to easily animate the Accordion Panel’s expand/collapse action? Specifically, something like a CSS slide effect for the height when the panel opens and closes, to make the user experience smoother.

    Since you walk through adding custom CSS, do you know if the block’s current implementation makes it feasible for theme developers to easily implement a CSS transition for a smooth slide-down effect?

    Keep up the great work!

  4. Jay Avatar
    Jay

    And what about adding FAQ structured data via inline microdata to this?
    https://developers.google.com/search/docs/appearance/structured-data/faqpage

    1. Justin Tadlock Avatar

      For FAQs structured data, here’s a Gist example of doing that. I’d probably just wrap it up in a pattern.

    2. Justin Tadlock Avatar

      I’ve also published a snippet here on the Developer Blog for this: Schema.org microdata for Accordion block FAQs.

Leave a Reply

Your email address will not be published. Required fields are marked *