{"id":72975,"date":"2019-11-19T04:17:42","date_gmt":"2019-11-19T11:17:42","guid":{"rendered":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/"},"modified":"2019-11-19T04:17:42","modified_gmt":"2019-11-19T11:17:42","slug":"make-your-own-expanding-and-contracting-content-panels","status":"publish","type":"post","link":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/","title":{"rendered":"Make Your Own Expanding And Contracting Content Panels"},"content":{"rendered":"<link rel=\"canonical\" href=\"https:\/\/www.smashingmagazine.com\/2019\/11\/expanding-contracting-content-panels\/\"><title>Make Your Own Expanding And Contracting Content Panels<\/title><\/p>\n<article>\n<header>\n<h1>Make Your Own Expanding And Contracting Content Panels<\/h1>\n<address>Ben Frain<\/address>\n<p>                  <time datetime=\"2019-11-19T11:00:00+00:00\">2019-11-19T11:00:00+00:00<\/time><time datetime=\"2019-11-19T11:00:00+00:00\">2019-11-19T11:08:33+00:00<\/time><\/header>\n<p>We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels.<\/p>\n<p>To clarify exactly what we\u2019re talking about, head on over to this example on CodePen:<\/p>\n<figure>\n<p data-height=\"500\" data-theme-id=\"light\" data-slug-hash=\"VwwqdWa\" data-user=\"smashingmag\" data-default-tab=\"result\"><a href=\"https:\/\/codepen.io\/smashingmag\/pen\/VwwqdWa\">Easy show\/hide drawer (Multiples)<\/a> by <a href=\"https:\/\/codepen.io\/benfrain\">Ben Frain<\/a> on <a href=\"https:\/\/codepen.io\/\">CodePen<\/a>.<\/p><figcaption><a href=\"https:\/\/codepen.io\/smashingmag\/pen\/VwwqdWa\">Easy show\/hide drawer (Multiples)<\/a> by <a href=\"https:\/\/codepen.io\/benfrain\">Ben Frain<\/a> on <a href=\"https:\/\/codepen.io\/\">CodePen<\/a>.<\/figcaption><\/figure>\n<p>That is what we\u2019ll be building in this short tutorial.<\/p>\n<p>From a functionality point of view, there are a few ways to achieve the animated open and close that we are looking for. Each approach with its own benefits and trade-offs. I\u2019m going to share the details of my \u2018go-to\u2019 method in detail in this article. Let\u2019s consider possible approaches first.<\/p>\n<h3>Approaches<\/h3>\n<p>There are variations on these techniques, but broadly speaking, the approaches fall into one of three categories:<\/p>\n<ol>\n<li>Animate\/transition the <code>height<\/code> or <code>max-height<\/code> of content.<\/li>\n<li>Use <code>transform: translateY<\/code> to move elements into a new position, giving the illusion of a panel closing and then re-render the DOM once the transform is complete with the elements in their finishing position.<\/li>\n<li>Use a library that does some combination\/variation of 1 or 2!<\/li>\n<\/ol>\n<div data-component=\"FeaturePanel\" data-audience=\"non-subscriber\" data-remove=\"true\"><\/div>\n<h4>Considerations Of Each Approach<\/h4>\n<p>From a performance perspective, using a transform is more effective than animating or transitioning the height\/max-height. With a transform, the moving elements are rasterized and get shifted around by the GPU. This is a cheap and easy operation for a GPU so performance tends to be much better.<\/p>\n<p>The basic steps when using a transform approach are:<\/p>\n<ol>\n<li>Get the height of the content to be collapsed.<\/li>\n<li>Move the content and everything after by the height of the content to be collapsed using <code>transform: translateY(Xpx)<\/code>. Operate the transform with the transition of choice to give a pleasing visual effect.<\/li>\n<li>Use JavaScript to listen to the <code>transitionend<\/code> event. When it fires, <code>display: none<\/code> the content and remove the transform and everything should be in the right place.<\/li>\n<\/ol>\n<p>Doesn\u2019t sound too bad, right?<\/p>\n<p>However, there are a number of considerations with this technique so I tend to avoid it for casual implementations unless performance is absolutely crucial.<\/p>\n<p>For example, with the <code>transform: translateY<\/code> approach you need to consider the <code>z-index<\/code> of the elements. By default, the elements that transform up are after the trigger element in the DOM and therefore appear on-top of the things before them when translated up.<\/p>\n<p>You also need to consider how many things appear <em>after<\/em> the content you want to collapse in the DOM. If you don\u2019t want a big hole in your layout, you might find it easier to use JavaScript to wrap everything you want to move in a container element and just move that. Manageable but we have just introduced more complexity! This is, however, the <em>kind<\/em> of approach I went for when moving players up and down in <a href=\"https:\/\/io.benfrain.com\/\">In\/Out<\/a>. You can see how that was done <a href=\"https:\/\/github.com\/benfrain\/whosin\/blob\/master\/ts\/repositionSlat.ts\">here<\/a>.<\/p>\n<p>For more casual needs, I tend to go with transitioning the <code>max-height<\/code> of the content. This approach doesn\u2019t perform as well as a transform. The reason being that the browser is tweening the height of the collapsing element throughout the transition; that causes a lot of layout calculations which are not as cheap for the host computer.<\/p>\n<p>However, this approach wins from a simplicity point of view. The pay-off of suffering the afore-mentioned computational hit is that the DOM re-flow takes care of the position and geometry of everything. We have very little in the way of calculations to write plus the JavaScript needed to pull it off well is comparatively simple.<\/p>\n<h4>The Elephant In The Room: Details And Summary Elements<\/h4>\n<p>Those with an intimate knowledge of HTML\u2019s elements will know there is a native HTML solution to this problem in the form of the <code>details<\/code> and <code>summary<\/code> elements. Here\u2019s some example markup:<\/p>\n<div>\n<pre><code><details>\n    <summary>Click to open\/close<\/summary>\n    Here is the content that is revealed when clicking the summary...\n<\/details><\/code><\/pre>\n<\/div>\n<p>By default, browsers provide a little disclosure triangle next to the summary element; click the summary and the contents below the summary is revealed.<\/p>\n<p>Great, hey? Details even support the <code>toggle<\/code> event in JavaScript so you can do this kind of thing to perform different things based upon whether it is open or closed (don\u2019t worry if that kind of JavaScript expression seems odd; we\u2019ll get to that in more detail shortly):<\/p>\n<pre><code>details.addEventListener(\"toggle\", () => {\n    details.open ? thisCoolThing() : thisOtherThing();\n})<\/code><\/pre>\n<p>OK, I\u2019m going to halt your excitement right there. The details and summary elements don\u2019t animate. Not by default and it is not currently possible to get them animating\/transitioning open and closed with additional CSS and JavaScript.<\/p>\n<p>If you know otherwise, I\u2019d love to be proved wrong.<\/p>\n<p>Sadly, as we need an opening and closing aesthetic we\u2019ll have to roll up our sleeves and do the best and most accessible job we can with the other tools at our disposal.<\/p>\n<p>Right, with the depressing news out of the way, let\u2019s get on with making this thing happen.<\/p>\n<h4>Markup Pattern<\/h4>\n<p>The basic markup is going to look like this:<\/p>\n<div>\n<pre><code><div class=\"container\">\n    <button type=\"button\" class=\"trigger\">Show\/Hide content<\/button>\n    <div class=\"content\">\n        All the content here\n    <\/div>\n<\/div><\/code><\/pre>\n<\/div>\n<p>We have an outer container to wrap the expander and the first element is the button which serves as a trigger to the action. Notice the type attribute in the button? I always include that as by default a button inside a form will perform a submit. If you find yourself wasting a couple of hours wondering why your form isn\u2019t working and buttons are involved in your form; make sure you check the type attribute!<\/p>\n<p>The next element after the button is the content drawer itself; everything you want to be hiding and showing.<\/p>\n<p>To bring things to life, we will make use of CSS custom properties, CSS transitions, and a little JavaScript.<\/p>\n<div><\/div>\n<h4>Basic Logic<\/h4>\n<p>The basic logic is this:<\/p>\n<ol>\n<li>Let the page load, measure the height of the content.<\/li>\n<li>Set the height of the content onto the container as the value of a CSS Custom Property.<\/li>\n<li>Immediately hide the content by adding an <code>aria-hidden: \"true\"<\/code> attribute to it. Using <code>aria-hidden<\/code> ensures assistive technology knows that content is hidden too.<\/li>\n<li>Wire up the CSS so that the <code>max-height<\/code> of the content class is the value of the custom property.<\/li>\n<li>Pressing our trigger button toggles the aria-hidden property from true to false which in turn toggles the <code>max-height<\/code> of the content between <code>0<\/code> and the height set in the custom property. A transition on that property provides the visual flair \u2014\u00a0adjust to taste!<\/li>\n<\/ol>\n<p><strong>Note:<\/strong> <em>Now, this would be a simple case of toggling a class or attribute if <code>max-height: auto<\/code> equalled the height of the content. Sadly it doesn&#8217;t. Go and shout about that to the W3C <a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/626\">here<\/a>.<\/em><\/p>\n<p>Let\u2019s have a look how that approach manifests in code. Numbered comments show the equivalent logic steps from above in code.<\/p>\n<p>Here is the JavaScript:<\/p>\n<div>\n<pre><code>\/\/ Get the containing element\nconst container = document.querySelector(\".container\");\n\/\/ Get content\nconst content = document.querySelector(\".content\");\n\/\/ 1. Get height of content you want to show\/hide\nconst heightOfContent = content.getBoundingClientRect().height;\n\/\/ Get the trigger element\nconst btn = document.querySelector(\".trigger\");\n\n\/\/ 2. Set a CSS custom property with the height of content\ncontainer.style.setProperty(\"--containerHeight\", `${heightOfContent}px`);\n\n\/\/ Once height is read and set\nsetTimeout(e => {\n    document.documentElement.classList.add(\"height-is-set\");\n    3. content.setAttribute(\"aria-hidden\", \"true\");\n}, 0);\n\nbtn.addEventListener(\"click\", function(e) {\n    container.setAttribute(\"data-drawer-showing\", container.getAttribute(\"data-drawer-showing\") === \"true\" ? \"false\" : \"true\");\n    \/\/ 5. Toggle aria-hidden\n    content.setAttribute(\"aria-hidden\", content.getAttribute(\"aria-hidden\") === \"true\" ? \"false\" : \"true\");\n})<\/code><\/pre>\n<\/div>\n<p>The CSS:<\/p>\n<pre><code>.content {\n  transition: max-height 0.2s;\n  overflow: hidden;\n}\n.content[aria-hidden=\"true\"] {\n  max-height: 0;\n}\n\/\/ 4. Set height to value of custom property\n.content[aria-hidden=\"false\"] {\n  max-height: var(--containerHeight, 1000px);\n}<\/code><\/pre>\n<h4>Points Of Note<\/h4>\n<h5>What about multiple drawers?<\/h5>\n<p>When you have a number of open-and-hide drawers on a page you\u2019ll need to loop through them all as they will likely be differing sizes.<\/p>\n<p>To handle that we will need to do a <code>querySelectorAll<\/code> to get all the containers and then re-run your setting of custom variables for each content inside a <code>forEach<\/code>.<\/p>\n<h5>That setTimeout<\/h5>\n<p>I have a <code>setTimeout<\/code> with <code>0<\/code> duration before setting the container to be hidden. This is arguably unneeded but I use it as a \u2018belt and braces\u2019 approach to ensure the page has rendered first so the heights for the content are available to be read.<\/p>\n<h5>Only fire this when the page is ready<\/h5>\n<p>If you have other stuff going on, you might choose to wrap your drawer code up in a function that gets initialised on page load. For example, suppose the drawer function was wrapped up in a function called <code>initDrawers<\/code> we could do this:<\/p>\n<pre><code>window.addEventListener(\"load\", initDrawers);<\/code><\/pre>\n<p>In fact, we will add that in shortly.<\/p>\n<h5>Additional data-* attributes on the container<\/h5>\n<p>There is a data attribute on the outer container that also gets toggled. This is added in case there is anything that needs to change with the trigger or container as the drawer opens\/closes. For example, perhaps we want to change the color of something or reveal or toggle an icon.<\/p>\n<h5>Default value on the custom property<\/h5>\n<p>There\u2019s a default value set on the custom property in CSS of <code>1000px<\/code>. That\u2019s the bit after the comma inside the value: <code>var(--containerHeight, 1000px)<\/code>. This means if the <code>--containerHeight<\/code> gets screwed up in some way, you should still have a decent transition. You can obviously set that to whatever is suitable to your use case.<\/p>\n<div><\/div>\n<h4>Why Not Just Use A Default Value Of 100000px?<\/h4>\n<p>Given that <code>max-height: auto<\/code> doesn\u2019t transition, you may be wondering why you don\u2019t just opt for a set height of a value greater than you would ever need. For example, 10000000px?<\/p>\n<p>The problem with that approach is that it will always transition from that height. If your transition duration is set to 1 second, the transition will \u2018travel\u2019 10000000px in a second. If your content is only 50px high, you\u2019ll get quite a quick opening\/closing effect!<\/p>\n<h5>Ternary operator for toggles<\/h5>\n<p>We\u2019ve made use of a ternary operator a couple of times to toggle attributes. Some folks hate them but I, <a href=\"https:\/\/css-tricks.com\/in-defense-of-the-ternary-statement\/\">and others<\/a>, love them. They might seem a bit weird and a little \u2018code golf\u2019 at first but once you get used to the syntax, I think they are a more straightforward read than a standard if\/else.<\/p>\n<p>For the uninitiated, a ternary operator is a condensed form of if\/else. They are written so that the thing to check is first, then the <code>?<\/code> separates what to execute if the check is true, and then the <code>:<\/code> to distinguish what should run if the check if false.<\/p>\n<pre><code>isThisTrue ? doYesCode() : doNoCode();<\/code><\/pre>\n<p>Our attribute toggles work by checking if an attribute is set to <code>\"true\"<\/code> and if so, set it to <code>\"false\"<\/code>, otherwise, set it to <code>\"true\"<\/code>.<\/p>\n<h5>What happens on page resize?<\/h5>\n<p>If a user resizes the browser window, there\u2019s a high probability the heights of our content will change. Therefore you might want to re-run setting the height for containers in that scenario. Now we are considering such eventualities, it seems like a good time to refactor things a little.<\/p>\n<p>We can make one function to set the heights and another function to deal with the interactions. Then add two listeners on the window; one for when the document loads, as mentioned above, and then another to listen for the resize event.<\/p>\n<h4>All Together<\/h4>\n<p>With the page load, multiple drawers, and handling resize events, our JavaScript code looks like this:<\/p>\n<div>\n<pre><code>var containers;\nfunction initDrawers() {\n    \/\/ Get the containing elements\n    containers = document.querySelectorAll(\".container\");\n    setHeights();\n    wireUpTriggers();\n    window.addEventListener(\"resize\", setHeights);\n}\n\nwindow.addEventListener(\"load\", initDrawers);\n\nfunction setHeights() {\n    containers.forEach(container => {\n        \/\/ Get content\n        let content = container.querySelector(\".content\");\n        content.removeAttribute(\"aria-hidden\");\n        \/\/ Height of content to show\/hide\n        let heightOfContent = content.getBoundingClientRect().height;\n        \/\/ Set a CSS custom property with the height of content\n        container.style.setProperty(\"--containerHeight\", `${heightOfContent}px`);\n        \/\/ Once height is read and set\n        setTimeout(e => {\n            container.classList.add(\"height-is-set\");\n            content.setAttribute(\"aria-hidden\", \"true\");\n        }, 0);\n    });\n}\n\nfunction wireUpTriggers() {\n    containers.forEach(container => {\n        \/\/ Get each trigger element\n        let btn = container.querySelector(\".trigger\");\n        \/\/ Get content\n        let content = container.querySelector(\".content\");\n        btn.addEventListener(\"click\", function(e) {\n            container.setAttribute(\"data-drawer-showing\", container.getAttribute(\"data-drawer-showing\") === \"true\" ? \"false\" : \"true\");\n            content.setAttribute(\"aria-hidden\", content.getAttribute(\"aria-hidden\") === \"true\" ? \"false\" : \"true\");\n        });\n    });\n}<\/code><\/pre>\n<\/div>\n<p>You can also play with it on CodePen over here:<\/p>\n<figure>\n<p data-height=\"500\" data-theme-id=\"light\" data-slug-hash=\"VwwqdWa\" data-user=\"smashingmag\" data-default-tab=\"result\"><a href=\"https:\/\/codepen.io\/smashingmag\/pen\/VwwqdWa\">Easy show\/hide drawer (Multiples)<\/a> by <a href=\"https:\/\/codepen.io\/benfrain\">Ben Frain<\/a> on <a href=\"https:\/\/codepen.io\/\">CodePen<\/a>.<\/p><figcaption><a href=\"https:\/\/codepen.io\/smashingmag\/pen\/VwwqdWa\">Easy show\/hide drawer (Multiples)<\/a> by <a href=\"https:\/\/codepen.io\/benfrain\">Ben Frain<\/a> on <a href=\"https:\/\/codepen.io\/\">CodePen<\/a>.<\/figcaption><\/figure>\n<h3>Summary<\/h3>\n<p>It\u2019s possible to go on for some time further refining and catering for more and more situations but the basic mechanics of creating a reliable opening and closing drawer for your content should now be within your reach. Hopefully, you are also aware of some of the hazards. The <code>details<\/code> element can\u2019t be animated, <code>max-height: auto<\/code> doesn\u2019t do what you hoped, you can\u2019t reliably add a massive max-height value and expect all content panels to open as expected.<\/p>\n<p>To re-iterate our approach here: measure the container, store it\u2019s height as a CSS custom property, hide the content and then use a simple toggle to switch between <code>max-height<\/code> of 0 and the height you stored in the custom property.<\/p>\n<p>It might not be the absolute best performing method but I have found for most situations it is perfectly adequate and benefits from being comparatively straightforward to implement.<\/p>\n<div>\n  <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/11\/logo-red-16.png?w=900&#038;ssl=1\" alt=\"Smashing Editorial\"><span>(dm, yk, il)<\/span>\n<\/div>\n<\/article>\n<p class=\"wpematico_credit\"><small>Powered by <a href=\"http:\/\/www.wpematico.com\" target=\"_blank\" rel=\"noopener noreferrer\">WPeMatico<\/a><\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Make Your Own Expanding And Contracting Content Panels Make Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00 We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\u2019re talking about, head on over to this example&#8230;<a class=\"moretag\" href=\"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/\"> Read the full article&#8230;<\/a><\/p>\n","protected":false},"author":4,"featured_media":72976,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[371],"tags":[],"class_list":["post-72975","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-user-experience"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Make Your Own Expanding And Contracting Content PanelsMake Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\u2019re talking about, head on over to this example on CodePen:\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Guest Contribution\"\/>\n\t<meta name=\"keywords\" content=\"user experience\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies\" \/>\n\t\t<meta property=\"og:description\" content=\"Make Your Own Expanding And Contracting Content PanelsMake Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\u2019re talking about, head on over to this example on CodePen:\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-11-19T11:17:42+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-11-19T11:17:42+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Make Your Own Expanding And Contracting Content PanelsMake Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\u2019re talking about, head on over to this example on CodePen:\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#article\",\"name\":\"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies\",\"headline\":\"Make Your Own Expanding And Contracting Content Panels\",\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/logo-red-16.png?fit=60%2C80&ssl=1\",\"width\":60,\"height\":80},\"datePublished\":\"2019-11-19T04:17:42-07:00\",\"dateModified\":\"2019-11-19T04:17:42-07:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#webpage\"},\"articleSection\":\"User Experience\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"position\":2,\"name\":\"User Experience\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#listItem\",\"name\":\"Make Your Own Expanding And Contracting Content Panels\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#listItem\",\"position\":3,\"name\":\"Make Your Own Expanding And Contracting Content Panels\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/\",\"name\":\"Guest Contribution\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Guest Contribution\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#webpage\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/\",\"name\":\"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies\",\"description\":\"Make Your Own Expanding And Contracting Content PanelsMake Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00We\\u2019ve called them an \\u2018opening and closing panel\\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\\u2019re talking about, head on over to this example on CodePen:\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/logo-red-16.png?fit=60%2C80&ssl=1\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#mainImage\",\"width\":60,\"height\":80},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/11\\\/make-your-own-expanding-and-contracting-content-panels\\\/#mainImage\"},\"datePublished\":\"2019-11-19T04:17:42-07:00\",\"dateModified\":\"2019-11-19T04:17:42-07:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies","description":"Make Your Own Expanding And Contracting Content PanelsMake Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\u2019re talking about, head on over to this example on CodePen:","canonical_url":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/","robots":"max-image-preview:large","keywords":"user experience","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#article","name":"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies","headline":"Make Your Own Expanding And Contracting Content Panels","author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/11\/logo-red-16.png?fit=60%2C80&ssl=1","width":60,"height":80},"datePublished":"2019-11-19T04:17:42-07:00","dateModified":"2019-11-19T04:17:42-07:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#webpage"},"isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#webpage"},"articleSection":"User Experience"},{"@type":"BreadcrumbList","@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","position":1,"name":"Home","item":"https:\/\/www.taterboy.com\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","position":2,"name":"User Experience","item":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#listItem","name":"Make Your Own Expanding And Contracting Content Panels"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#listItem","position":3,"name":"Make Your Own Expanding And Contracting Content Panels","previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}}]},{"@type":"Organization","@id":"https:\/\/www.taterboy.com\/blog\/#organization","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","url":"https:\/\/www.taterboy.com\/blog\/"},{"@type":"Person","@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author","url":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/","name":"Guest Contribution","image":{"@type":"ImageObject","@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g","width":96,"height":96,"caption":"Guest Contribution"}},{"@type":"WebPage","@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#webpage","url":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/","name":"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies","description":"Make Your Own Expanding And Contracting Content PanelsMake Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\u2019re talking about, head on over to this example on CodePen:","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#breadcrumblist"},"author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"creator":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/11\/logo-red-16.png?fit=60%2C80&ssl=1","@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#mainImage","width":60,"height":80},"primaryImageOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/#mainImage"},"datePublished":"2019-11-19T04:17:42-07:00","dateModified":"2019-11-19T04:17:42-07:00"},{"@type":"WebSite","@id":"https:\/\/www.taterboy.com\/blog\/#website","url":"https:\/\/www.taterboy.com\/blog\/","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","inLanguage":"en-US","publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","og:type":"article","og:title":"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies","og:description":"Make Your Own Expanding And Contracting Content PanelsMake Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\u2019re talking about, head on over to this example on CodePen:","og:url":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/","article:published_time":"2019-11-19T11:17:42+00:00","article:modified_time":"2019-11-19T11:17:42+00:00","twitter:card":"summary","twitter:title":"Make Your Own Expanding And Contracting Content Panels | Design for Immersive Technologies","twitter:description":"Make Your Own Expanding And Contracting Content PanelsMake Your Own Expanding And Contracting Content Panels Ben Frain 2019-11-19T11:00:00+00:002019-11-19T11:08:33+00:00We\u2019ve called them an \u2018opening and closing panel\u2019 so far, but they are also described as expansion panels, or more simply, expanding panels. To clarify exactly what we\u2019re talking about, head on over to this example on CodePen:"},"aioseo_meta_data":{"post_id":"72975","title":null,"description":null,"keywords":null,"keyphrases":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_custom_image_width":null,"og_custom_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"created":"2021-02-07 16:45:28","updated":"2026-09-02 12:34:08","focus_keyword":null,"additional_keywords":null,"truseo_locale":null,"primary_term":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"twitter_image_url":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/\" title=\"User Experience\">User Experience<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tMake Your Own Expanding And Contracting Content Panels\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.taterboy.com\/blog"},{"label":"User Experience","link":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/"},{"label":"Make Your Own Expanding And Contracting Content Panels","link":"https:\/\/www.taterboy.com\/blog\/2019\/11\/make-your-own-expanding-and-contracting-content-panels\/"}],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8wzr5-iZ1","jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/11\/logo-red-16.png?fit=60%2C80&ssl=1","_links":{"self":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/72975","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/comments?post=72975"}],"version-history":[{"count":0,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/72975\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media\/72976"}],"wp:attachment":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media?parent=72975"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/categories?post=72975"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/tags?post=72975"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}