TIL: Trapezoid tabs in pure CSS


0 min read

Trapezoid elements plus pure CSS dynamic content. If you ever tried to get one of them done, you know how horrible they are to implement. They require a bit of out-of-the-box thinking, and a lot of tinkering.

My little sister, Marci, asked me for help creating a tab navigation for a website, after she wasn't able to quite nail it after hours of trial and google-foo and trying her best. So I sat down and worked out a solution, which I want to share with the rest of you, too!

The idea was that the tabs can be clicked to navigate to another site, with one tab active at a time. The solution should be template-able, and, using a script, it should also be possible to pre-fill the content for better UX (no site changes necessary).

After googling for a bit, I came across creating shapes in pure CSS. You can easily do so by setting an element's height to 0, and instead use the borders to create the shape. For example, if I set the bottom border height to 100px, and then add a width for transparent side-borders, the bottom-border will become a trapezoid.


With an element-width of zero, it will even look like a triangle. That's because of how the borders are drawn by the browser. They actually don't overlap, but are angled where they meet the other borders.


The next problem was putting text on the trapezoid. I decided to just go for a second element, which I draw on top of the trapezoid border using a negative margin. Last but not least, I had to add a border to the border-trapezoid. For that, I thought it would be pretty cool to not introduce yet another HTML element, but go for a pseudo-element. So I added a :before selector, which I then filled with another trapezoid bottom border, which is just a little bit wider and higher than the original element, and is z-ordered behind the original element. That one would be black to form the tab border. The result is a bordered, trapezoid tab with text on top.

That covers the aesthetics. What's left is the dynamic content. There is a standard trick to that one. Just create radio buttons, hide them and make the tab text labels for the radio buttons. In CSS, it is possible to use a :selected selector, so that we can have a kind of conditional drawing. Add either the + or ~ sibling operator in the mix, and the radio buttons actually can trigger CSS styling on other (sibling) elements - in pure CSS! The only downside is that we need to use sibling elements, which requires careful placement of the content. Also, the content now is in a non-intuitive spot in HTML. It should not be inside the tab-bar, however it's a compromise for this hack.

Wouldn't a script have been easier?

While it's true that we could have put some script in place which allows for a lot simpler solutions (including using a canvas element to draw the tabs), JS is not available everywhere. Especially software for accessibility and bots often cannot not execute JS, leaving the website in a broken, unusable state or leads to bots not being able to discover every part of your website. Bad accessibility may even lead to legal consequences, depending on where you live. Plus, in order to get a high lighthouse-score (which means a better Google ranking), we need to make sure that we don't use JS to render core parts of our website. As such, creating a script-less, working website which can be enhanced with styling and scripts still is important, even today. You can read more about Progressive Enhancement on MDN, Smashing Mag, Shopify and other sites.