{"id":7381,"date":"2025-10-13T10:56:32","date_gmt":"2025-10-13T15:56:32","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=7381"},"modified":"2025-10-13T13:16:19","modified_gmt":"2025-10-13T18:16:19","slug":"modern-css-round-out-tabs","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/modern-css-round-out-tabs\/","title":{"rendered":"Modern CSS Round-Out Tabs"},"content":{"rendered":"\n<p>Quite a while back <a href=\"https:\/\/css-tricks.com\/tabs-with-round-out-borders\/\">I made a set of &#8220;round out&#8221; tabs<\/a>, where the literal tab part of the UI would connect to the content below with a rounded edge that flared <em>out<\/em> as it connected. A bit tricky of a situation, even now! <\/p>\n\n\n\n<p>That old school solution used <em>four<\/em> additional elements per tab. Two to place a square on the bottom edges of the tab, and then larger circles to hide everything but the flared part.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"2040\" height=\"714\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-13-at-8.49.08-AM.png?resize=2040%2C714&#038;ssl=1\" alt=\"Illustration showing a tab design with rounded edges, featuring a central tab with additional shapes for visual effects. The background consists of different shades and shapes, emphasizing the tab structure.\" class=\"wp-image-7409\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-13-at-8.49.08-AM.png?w=2040&amp;ssl=1 2040w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-13-at-8.49.08-AM.png?resize=300%2C105&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-13-at-8.49.08-AM.png?resize=1024%2C358&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-13-at-8.49.08-AM.png?resize=768%2C269&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-13-at-8.49.08-AM.png?resize=1536%2C538&amp;ssl=1 1536w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>Here&#8217;s that (again: <strong>old!<\/strong>) demo:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_0199cf55-f6e9-7f24-ade9-98342e83a0ea\" src=\"\/\/codepen.io\/editor\/anon\/embed\/0199cf55-f6e9-7f24-ade9-98342e83a0ea?height=450&amp;theme-id=1&amp;slug-hash=0199cf55-f6e9-7f24-ade9-98342e83a0ea&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed 0199cf55-f6e9-7f24-ade9-98342e83a0ea\" title=\"CodePen Embed 0199cf55-f6e9-7f24-ade9-98342e83a0ea\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Let&#8217;s Use <code>shape()<\/code> Instead<\/h2>\n\n\n\n<p>I&#8217;m so hyped on <code>shape()<\/code>. It&#8217;s an amazing addition to CSS, giving us a primitive that can draw, well, anything you could draw with a pen tool.<\/p>\n\n\n\n<p>In our case we&#8217;re going to use the <code>shape()<\/code> primitive with <code>clip-path<\/code> to carve a tab shape out of a rectangle. No extra elements!<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.tab<\/span> {\n  <span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(\n    \/* do commands to cut out a tab shape *\/\n  );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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 <code>shape()<\/code> function takes all these <a href=\"https:\/\/css-tricks.com\/css-shape-commands\/\">commands<\/a> to do the drawing. Depending on how complex a thing you are trying to do, the syntax is fairly human-readable.<\/p>\n\n\n\n<p>Let&#8217;s slowly walk through hand-building this tab shape. It&#8217;ll be extra cool because:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>It&#8217;s not completely fixed shape. Parts of it can be fixed coordinates, and other parts can be flexible. You&#8217;ll see, it&#8217;s awesome.<\/li>\n\n\n\n<li>We can variablize it, meaning we can adjust the look on the fly.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">1) Starting Out!<\/h3>\n\n\n\n<p>Elements start out as rectangles. Ours are going to be horizontally longer rectangles just by virtue of them having text in them pushing them that direction. Then a bit of padding pushing those inline edges more than the block side edges.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" 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-class\">.tab<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">display<\/span>: inline-block; <span class=\"hljs-comment\">\/* So &lt;a&gt; will take padding *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">0.5rem<\/span> <span class=\"hljs-number\">2rem<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">white-space<\/span>: nowrap; <span class=\"hljs-comment\">\/* a wrapped tab will look silly *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(\n<\/span><\/span><mark class='shcb-loc'><span>    from bottom left,\n<\/span><\/mark><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-2\"><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>We wanna start carving away at this tab with <code>clip-path<\/code> from the bottom left corner, so here we go.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"589\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.43.45-AM.png?resize=1024%2C589&#038;ssl=1\" alt=\"\" class=\"wp-image-7387\" style=\"width:559px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.43.45-AM.png?resize=1024%2C589&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.43.45-AM.png?resize=300%2C173&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.43.45-AM.png?resize=768%2C442&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.43.45-AM.png?w=1102&amp;ssl=1 1102w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">2) The First Curve!<\/h3>\n\n\n\n<p>Right away we need to curve into the tab shape. This is beautiful right away, as this is the &#8220;round out&#8221; part that is hard to pull off. Ain&#8217;t no <code>border-shape<\/code> can really help us here, we&#8217;re fancy people. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"473\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.55.44-AM.png?resize=1024%2C473&#038;ssl=1\" alt=\"\" class=\"wp-image-7389\" style=\"width:636px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.55.44-AM.png?resize=1024%2C473&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.55.44-AM.png?resize=300%2C139&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.55.44-AM.png?resize=768%2C355&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.55.44-AM.png?resize=1536%2C710&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-6.55.44-AM.png?w=1796&amp;ssl=1 1796w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\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-class\">.tab<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  ...\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>    from bottom left,\n<\/span><\/span><mark class='shcb-loc'><span>    curve to <span class=\"hljs-number\">10px<\/span> calc(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>) with <span class=\"hljs-number\">10px<\/span> <span class=\"hljs-number\">100%<\/span>,\n<\/span><\/mark><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<h3 class=\"wp-block-heading\">3) Straight up!<\/h3>\n\n\n\n<p>We could use syntax (<code>line<\/code>) here saying &#8220;draw a straight line to these new coordinates&#8221;, but I think it&#8217;s more satisfying here to use syntax (<code>vline<\/code>) saying &#8220;whatever horizontal coordinate you&#8217;re at doesn&#8217;t matter, just draw to this new vertical coordinate&#8221;.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"501\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.02.08-AM.png?resize=1024%2C501&#038;ssl=1\" alt=\"\" class=\"wp-image-7391\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.02.08-AM.png?resize=1024%2C501&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.02.08-AM.png?resize=300%2C147&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.02.08-AM.png?resize=768%2C376&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.02.08-AM.png?w=1292&amp;ssl=1 1292w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\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 shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-selector-class\">.tab<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  ...\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>    from bottom left,\n<\/span><\/span><span class='shcb-loc'><span>    curve to <span class=\"hljs-number\">10px<\/span> calc(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>) with <span class=\"hljs-number\">10px<\/span> <span class=\"hljs-number\">100%<\/span>,\n<\/span><\/span><mark class='shcb-loc'><span>    vline to <span class=\"hljs-number\">10px<\/span>\n<\/span><\/mark><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-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<h3 class=\"wp-block-heading\">4) Curve to the Top!<\/h3>\n\n\n\n<p>We&#8217;ll use the same <code>curve<\/code> command here as the first curve, where we specify where we&#8217;re going and a point the curve should use to sorta pull toward. <\/p>\n\n\n\n<p>Honestly I tried using <code>arc<\/code> commands here first (like <code>arc to 20px 10px of 20%<\/code>) but by default the arc curved &#8220;the wrong way&#8221; making a bite shape and I didn&#8217;t really get what <code>20%<\/code> meant. I&#8217;m absolutely sure it&#8217;s possible and maybe a smidge easier, I just thought <code>curve<\/code> made more sense to me.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"561\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.09.12-AM.png?resize=1024%2C561&#038;ssl=1\" alt=\"\" class=\"wp-image-7393\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.09.12-AM.png?resize=1024%2C561&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.09.12-AM.png?resize=300%2C164&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.09.12-AM.png?resize=768%2C421&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.09.12-AM.png?w=1416&amp;ssl=1 1416w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" 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-class\">.tab<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  ...\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>    from bottom left,\n<\/span><\/span><span class='shcb-loc'><span>    curve to <span class=\"hljs-number\">10px<\/span> calc(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>) with <span class=\"hljs-number\">10px<\/span> <span class=\"hljs-number\">100%<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    vline to <span class=\"hljs-number\">10px<\/span>,\n<\/span><\/span><mark class='shcb-loc'><span>    curve to <span class=\"hljs-number\">20px<\/span> <span class=\"hljs-number\">0<\/span> with <span class=\"hljs-number\">10px<\/span> <span class=\"hljs-number\">0<\/span>,\n<\/span><\/mark><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-5\"><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<h3 class=\"wp-block-heading\">5) Moving to the Other Side!<\/h3>\n\n\n\n<p>This is my favorite point on the whole shape. <\/p>\n\n\n\n<p>Again instead of specifying an exact coordinate, we&#8217;re just saying draw horizontally from wherever you are to 20px away from the right edge. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"479\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.11.20-AM.png?resize=1024%2C479&#038;ssl=1\" alt=\"\" class=\"wp-image-7394\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.11.20-AM.png?resize=1024%2C479&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.11.20-AM.png?resize=300%2C140&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.11.20-AM.png?resize=768%2C360&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.11.20-AM.png?w=1508&amp;ssl=1 1508w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>We don&#8217;t know how far away the last point and this new point are away from each other. They could be 200px away, 117.23px away, 0px away, the line could even draw <em>to the left<\/em> because the element is so narrow. That&#8217;s good. We&#8217;re drawing a shape here with points that are a combination of fixed positions (e.g. 10px from the top!) and flexible positions (20px away from whatever the right edge is!). <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" 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-class\">.tab<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  ...\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>    from bottom left,\n<\/span><\/span><span class='shcb-loc'><span>    curve to <span class=\"hljs-number\">10px<\/span> calc(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>) with <span class=\"hljs-number\">10px<\/span> <span class=\"hljs-number\">100%<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    vline to <span class=\"hljs-number\">10px<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    curve to <span class=\"hljs-number\">20px<\/span> <span class=\"hljs-number\">0<\/span> with <span class=\"hljs-number\">10px<\/span> <span class=\"hljs-number\">0<\/span>,\n<\/span><\/span><mark class='shcb-loc'><span>    hline to <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">20px<\/span>),\n<\/span><\/mark><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-6\"><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<h3 class=\"wp-block-heading\">6) Draw the Rest of the Owl<\/h3>\n\n\n\n<p>From here, I think you get the point. We&#8217;re going to:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Curve back downward.<\/li>\n\n\n\n<li>Draw the vertical line.<\/li>\n\n\n\n<li>Curve to complete the round-out. <\/li>\n<\/ol>\n\n\n\n<p>We don&#8217;t need to draw a line back to the start of the shape. That&#8217;s just implied magically. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"353\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.21.12-AM.png?resize=1024%2C353&#038;ssl=1\" alt=\"\" class=\"wp-image-7396\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.21.12-AM.png?resize=1024%2C353&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.21.12-AM.png?resize=300%2C103&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.21.12-AM.png?resize=768%2C265&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.21.12-AM.png?w=1422&amp;ssl=1 1422w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" 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-class\">.tab<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  ...\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>    from bottom left,\n<\/span><\/span><span class='shcb-loc'><span>    curve to <span class=\"hljs-number\">10px<\/span> calc(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>) with <span class=\"hljs-number\">10px<\/span> <span class=\"hljs-number\">100%<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    vline to <span class=\"hljs-number\">10px<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    curve to <span class=\"hljs-number\">20px<\/span> <span class=\"hljs-number\">0<\/span> with <span class=\"hljs-number\">10px<\/span> <span class=\"hljs-number\">0<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    hline to <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">20px<\/span>),\n<\/span><\/span><mark class='shcb-loc'><span>    curve to <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>) <span class=\"hljs-number\">10px<\/span> with <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>) <span class=\"hljs-number\">0<\/span>,\n<\/span><\/mark><mark class='shcb-loc'><span>    vline to <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>),\n<\/span><\/mark><mark class='shcb-loc'><span>    curve to <span class=\"hljs-number\">100%<\/span> <span class=\"hljs-number\">100%<\/span> with <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - <span class=\"hljs-number\">10px<\/span>) <span class=\"hljs-number\">100%<\/span>\n<\/span><\/mark><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-7\"><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>That complete&#8217;s our shape! The white areas here are what is &#8220;cut away&#8221; leaving the yellow area (just for visualization):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"412\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.25.19-AM.png?resize=1024%2C412&#038;ssl=1\" alt=\"\" class=\"wp-image-7398\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.25.19-AM.png?resize=1024%2C412&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.25.19-AM.png?resize=300%2C121&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.25.19-AM.png?resize=768%2C309&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.25.19-AM.png?w=1452&amp;ssl=1 1452w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>The <code>padding<\/code> we&#8217;ve set in the inline direction (<code>2rem<\/code>) is plenty to survive from being clipped away, as we&#8217;re only clipping ~<code>10px<\/code> away.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Variablizing Things<\/h2>\n\n\n\n<p><em>Hmmmmmmm.<\/em><\/p>\n\n\n\n<p>Notice we used <code>10px<\/code> and awful lot in our <code>shape()<\/code>. We used a couple of <code>20px<\/code> values too, and the intention was clearly &#8220;twice as much as that other value&#8221;. So we could get away with setting a custom property to <code>10px<\/code> and using that repetitively. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.tab<\/span> {\n  <span class=\"hljs-attribute\">--tabGirth<\/span>: <span class=\"hljs-number\">12px<\/span>;\n\n  <span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(\n    from bottom left,\n    curve to var(--tabGirth) <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - var(--tabGirth)) with\n      <span class=\"hljs-built_in\">var<\/span>(--tabGirth) <span class=\"hljs-number\">100%<\/span>,\n    vline to <span class=\"hljs-built_in\">var<\/span>(--tabGirth),\n    curve to <span class=\"hljs-built_in\">calc<\/span>(var(--tabGirth) * <span class=\"hljs-number\">2<\/span>) <span class=\"hljs-number\">0<\/span> with <span class=\"hljs-built_in\">var<\/span>(--tabGirth) <span class=\"hljs-number\">0<\/span>,\n    hline to <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - calc(var(--tabGirth) * <span class=\"hljs-number\">2<\/span>)),\n    curve to <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - var(--tabGirth)) <span class=\"hljs-built_in\">var<\/span>(--tabGirth) with\n      <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - var(--tabGirth)) <span class=\"hljs-number\">0<\/span>,\n    vline to <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - var(--tabGirth)),\n    curve to <span class=\"hljs-number\">100%<\/span> <span class=\"hljs-number\">100%<\/span> with <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> - var(--tabGirth)) <span class=\"hljs-number\">100%<\/span>\n  );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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<h2 class=\"wp-block-heading\">The Modern Demo<\/h2>\n\n\n\n<p>I added a few doo-dads to the final demo. The hover and active states push the tabs down a little with <code>translate<\/code>, for instance. That&#8217;s nothing to write home about, but then I wanted to rudimentary <code>overflow: auto<\/code> behavior so the non-wrapping tabs didn&#8217;t blow out horizontally, and it led to this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"872\" height=\"184\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.37.17-AM.png?resize=872%2C184&#038;ssl=1\" alt=\"\" class=\"wp-image-7403\" style=\"width:527px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.37.17-AM.png?w=872&amp;ssl=1 872w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.37.17-AM.png?resize=300%2C63&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/Screenshot-2025-10-12-at-7.37.17-AM.png?resize=768%2C162&amp;ssl=1 768w\" sizes=\"auto, (max-width: 872px) 100vw, 872px\" \/><figcaption class=\"wp-element-caption\">The horizontal scrollbar is what I wanted, but the vertical scrollbar is like: no.<\/figcaption><\/figure>\n\n\n\n<p>So I enjoyed the fact that can now (sorta) do single-directional overflow control:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/*\n  Allow horizontal scrollbars, but\n  hide vertical overflow\n*\/<\/span>\n<span class=\"hljs-selector-tag\">overflow-inline<\/span>: <span class=\"hljs-selector-tag\">auto<\/span>;\n<span class=\"hljs-selector-tag\">overflow-block<\/span>: <span class=\"hljs-selector-tag\">clip<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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>I also used <a href=\"https:\/\/github.com\/yairEO\/knobs\">Knobs<\/a> to give a UI control to the CSS variable <code>--tabGirth<\/code> so you can see how the tabs look with different values. The <em>more<\/em> girth almost the <em>smaller<\/em> the tabs look, because we need to &#8220;cut away&#8221; more of the tab.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_0199caa8-5073-7e79-8002-e30d5532a6c4\" src=\"\/\/codepen.io\/editor\/anon\/embed\/0199caa8-5073-7e79-8002-e30d5532a6c4?height=450&amp;theme-id=1&amp;slug-hash=0199caa8-5073-7e79-8002-e30d5532a6c4&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed 0199caa8-5073-7e79-8002-e30d5532a6c4\" title=\"CodePen Embed 0199caa8-5073-7e79-8002-e30d5532a6c4\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>There is a smidge of other trickery in there like getting shadows via <code>filter<\/code> on a parent element, that even work with the <code>clip-path<\/code>. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fallbacks<\/h2>\n\n\n\n<p>Not every browser <a href=\"https:\/\/caniuse.com\/mdn-css_types_basic-shape_shape\">supports <code>shape()<\/code><\/a> at the time of this writing (there is even <a href=\"https:\/\/bsky.app\/profile\/anatudor.bsky.social\/post\/3m2t27dpcks2a\">sub-support issues of syntax features<\/a>). <\/p>\n\n\n\n<p>But that doesn&#8217;t mean we have to deliver them entirely rectangular tabs. A <code>@supports<\/code> test allows us to deliver a fallback just fine. We just need to pass in a valid <code>shape<\/code> syntax (you can&#8217;t just do <code>shape()<\/code>). <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.tab<\/span> {\n  ...\n  \n  @supports not (<span class=\"hljs-attribute\">clip-path<\/span>: <span class=\"hljs-built_in\">shape<\/span>(from top left, hline to <span class=\"hljs-number\">0<\/span>)) {\n    <span class=\"hljs-comment\">\/* less padding needed inline *\/<\/span>\n    padding-inline: <span class=\"hljs-number\">1rem<\/span>; \n    \n    <span class=\"hljs-comment\">\/* top rounding *\/<\/span>\n    <span class=\"hljs-attribute\">border-start-start-radius<\/span>: <span class=\"hljs-built_in\">var<\/span>(--tabGirth);\n    <span class=\"hljs-attribute\">border-start-end-radius<\/span>: <span class=\"hljs-built_in\">var<\/span>(--tabGirth);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><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<h2 class=\"wp-block-heading\">Accessibility of Tabs<\/h2>\n\n\n\n<p>The tabs are built from anchor links that jump-link to the related content. When JavaScript is active, they get what I think are the correct roles and aria-* attributes. The aria-attributes are updated when I think is the appropriate time to the appropriate values. <\/p>\n\n\n\n<p>But I&#8217;m sure this isn&#8217;t fully correct. Just having anchor links here means the arrow keys to change tabs don&#8217;t work, which I think is a general requirement of tabs. So anyway this is mostly about the <em>design<\/em> of the tabs and you&#8217;d be better off consulting <a href=\"https:\/\/www.w3.org\/WAI\/ARIA\/apg\/patterns\/tabs\/\">elsewhere<\/a> for perfectly accessible implementations of the behavior. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Other Examples<\/h2>\n\n\n\n<p>I looked around at a number of older examples and a lot of them involve pseudo or extra elements and have aged like milk. Despite the modern browser support requirements here, I expect the above will age much better, as will these more modern takes below:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Temani Afif: <a href=\"https:\/\/css-tip.com\/rounded-tab\/\">Rounded tabs with inner curves<\/a><\/li>\n\n\n\n<li>Ana Tudor: <a href=\"https:\/\/codepen.io\/thebabydino\/pen\/azOgOKE\">Concave header component<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>We can use `shape()` to carve away the edges of an element to look like a folder tab. By hand.<\/p>\n","protected":false},"author":1,"featured_media":7410,"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":[7,352,411],"class_list":["post-7381","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-shape","tag-tabs"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/10\/tabs.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7381","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\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=7381"}],"version-history":[{"count":20,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7381\/revisions"}],"predecessor-version":[{"id":7415,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/7381\/revisions\/7415"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/7410"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=7381"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=7381"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=7381"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}