{"id":9143,"date":"2026-03-30T10:24:00","date_gmt":"2026-03-30T15:24:00","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=9143"},"modified":"2026-03-30T10:24:01","modified_gmt":"2026-03-30T15:24:01","slug":"the-drill-down-menu-with-details-and-scope","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/the-drill-down-menu-with-details-and-scope\/","title":{"rendered":"The Drill-Down Menu with Details and @scope"},"content":{"rendered":"\n<p>We&#8217;re going to build a set of nested <code>&lt;details&gt;<\/code> elements that behave like nested <em>menus.<\/em> Click a top-level link, drill down into a submenu, which can drill down into another submenu, etc. Think of them as nesting dolls&#8230; without the shrinking size. We take off a shell and get a new doll from inside.<\/p>\n\n\n\n<p>This type of navigation pattern could be used to save space, be entertaining to use, or actually be helpful to users who know they need to follow a narrow path of questions to get where they need to go. <\/p>\n\n\n\n<p>Here&#8217;s a live demo:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_qEamPyp\" src=\"\/\/codepen.io\/anon\/embed\/qEamPyp?height=500&amp;theme-id=1&amp;slug-hash=qEamPyp&amp;default-tab=result\" height=\"500\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed qEamPyp\" title=\"CodePen Embed qEamPyp\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Let\u2019s see how to make this one, starting with the layout.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Nested Layout<\/h2>\n\n\n\n<p>A set of cascading, nested menus forms the foundation of this user interface. You can build it in your preferred way \u2014 you just need the nested <code>&lt;details><\/code> elements so users can open and close submenus. We&#8217;ll be targeting those states in CSS.<\/p>\n\n\n\n<p>The <code>&lt;details&gt;<\/code> element is an interactive HTML element that can be <strong><em>opened<\/em><\/strong> <strong>and<\/strong> <strong><em>closed<\/em><\/strong> to show or hide additional content as needed, making it perfect for this drill-down UI. <\/p>\n\n\n\n<p>The <code>&lt;summary&gt;<\/code> element within <code>&lt;details&gt;<\/code> becomes the clickable interactive element which opens all the other content within.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">\/* A three-level menu *\/\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">details<\/span>&gt;<\/span> \/* Level 1 *\/\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">summary<\/span>&gt;<\/span>Data Settings<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">summary<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">details<\/span>&gt;<\/span> \/* Level 2 *\/\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">summary<\/span>&gt;<\/span>Data Roaming<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">summary<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">details<\/span>&gt;<\/span> \/* Level 3 *\/\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">summary<\/span>&gt;<\/span>Domestic Roaming<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">summary<\/span>&gt;<\/span>\n      <span class=\"hljs-comment\">&lt;!-- content --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">details<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- More in Level 3 --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">details<\/span>&gt;<\/span>\n  <span class=\"hljs-comment\">&lt;!-- More in Level 2 --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">details<\/span>&gt;<\/span>\n<span class=\"hljs-comment\">&lt;!-- More in Level 1 --&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">The Donut Scope<\/h2>\n\n\n\n<p>The two-state <code>&lt;details&gt;<\/code>, by itself, can show and hide its content, but when one of the <code>&lt;details&gt;<\/code> is open, the rest of them on the same level should not be visible. Although there are other ways to reach those <code>&lt;details&gt;<\/code>s to be removed, a very easy way to make it happen is by using <code>@scope<\/code> in CSS.<\/p>\n\n\n\n<p>The <code>@scope<\/code> rule in CSS allows us to <strong>search for elements in a subtree defined by a scope root (an ancestor element) and a scope limit<\/strong> (a descendant element). This is similar to the limits of integration in calculus, where calculations are performed within a specific range defined by upper and lower bounds.<\/p>\n\n\n\n<p>The starting element of your subtree is like the upper bound (scope root). The ending element, a descendant, is like the lower bound (scope limit). The scope limit is optional and unaffected by style rules. Only the scope root and elements between it and the limit are affected.<\/p>\n\n\n\n<p>Here\u2019s an example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>Legal Provisions for Account Security<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>Section 4.1: User Accountability...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>The User is solely and exclusively...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>Section 4.2: Notification of...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>In the event of an actual or suspected breach...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"plain-language-summary\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>Plain Language Summary<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>We want to keep your account safe, so you...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"legal-clause\"<\/span>&gt;<\/span>Legal Clause 4.1: User..<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>If you lose access to your account...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-selector-tag\">main<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">font<\/span>: <span class=\"hljs-number\">0.8rem<\/span> monospace;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><mark class='shcb-loc'><span><span class=\"hljs-keyword\">@scope<\/span> (.plain-language-summary) to (.legal-clause) {\n<\/span><\/mark><span class='shcb-loc'><span>  * {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">color<\/span>: navy;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">font-family<\/span>: poppins;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_vEXeLpY\" src=\"\/\/codepen.io\/anon\/embed\/vEXeLpY?height=650&amp;theme-id=1&amp;slug-hash=vEXeLpY&amp;default-tab=result\" height=\"650\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed vEXeLpY\" title=\"CodePen Embed vEXeLpY\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>The above document contains legal clauses styled in <code>monospace<\/code> font and default black color. A \u2018plain language summary\u2019 in <code>poppins<\/code> font and <code>navy<\/code> color outlines the document. The summary includes a legal clause that must retain the default monospace font and black color of the legal clauses.<\/p>\n\n\n\n<p>Instead of having to reset the legal clause\u2019s style inside the summary, <code>@scope<\/code> is used to style the <code>.plain-language-summary'<\/code>s (scope root) content while excluding any <code>.legal-clause<\/code>es (scope limit) in it.<\/p>\n\n\n\n<p>Being able to punch away elements not needed inward and outward \u2014 hence the term <a href=\"https:\/\/www.stubbornella.org\/2011\/10\/08\/scope-donuts\/\">\u2018donut\u2019<\/a> \u2014 is what we need for the drill-down menu. <strong>When an inner menu opens, its outer menu and everything between the outer and inner one has to be erased, leaving only the open inner menu\u2019s content on the screen.<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@scope<\/span> (:has(&gt;details&#91;open])) to (details&#91;open]) {\n  * {  <span class=\"hljs-comment\">\/* or details, summary { *\/<\/span>\n    <span class=\"hljs-attribute\">display<\/span>: none;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The outer menu with the open inner menu (<code>:has(&gt;details[open])<\/code>) is the scope root. The open inner menu (<code>details[open]<\/code>) is the scope limit. All the <code>&lt;details&gt;<\/code> and <code>&lt;summary&gt;<\/code> elements between them are removed. Here\u2019s the example once more:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_qEamPyp\" src=\"\/\/codepen.io\/anon\/embed\/qEamPyp?height=450&amp;theme-id=1&amp;slug-hash=qEamPyp&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed qEamPyp\" title=\"CodePen Embed qEamPyp\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Even if you nest details elements, you can ensure only one level of them is open at a time, making a menu you can drill down (and up!) from.<\/p>\n","protected":false},"author":20,"featured_media":9153,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[243,7,211],"class_list":["post-9143","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-scope","tag-css","tag-details"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/03\/drilldown-menu.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9143","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=9143"}],"version-history":[{"count":7,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9143\/revisions"}],"predecessor-version":[{"id":9157,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9143\/revisions\/9157"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/9153"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=9143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=9143"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=9143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}