Using CSS Shapes for Interesting User Controls and Navigation

Avatar of Preethi
Preethi on (Updated on )

Straight across or down, that’s the proverbial order for user controls on a screen. Like a list of menu items. But what if we change that to a more fluid layout with bends, curves, and nooks? We can pull it off with just a few lines of code. In the age of modern minimalistic designs, curved layouts for the user controls add just the right amount of pep to a web design.

And coding them couldn’t be more easier, thanks to CSS Shapes.

CSS Shapes (notably, the shape-outside property) is a standard that assigns geometric shapes to float elements. The content then wraps around the floated element along the boundaries of those shapes.

The use cases for this standard are usually showcased as designs for textual, editorial content — where plain text flow along the shapes floating at their sides. However, in this post, in place of just plain text, we use user controls to see how these shapes can breathe some fluid silhouettes into their layouts.

For the first demo, here’s a design that can be used in product pages, where any product-related action controls can be aligned along the shape of the product itself.

<form>
  <img src="./img/bottle.png" alt="A tall refreshing and unopened bottle of Coca-Cola, complete with red bottle cap and the classic Coke logo in white.">
    <div>
      <input type="radio" id="one" name="coke" checked>
      <label for="one">One Bottle</label>
    </div>
    <div>
      <input type="radio" id="six" name="coke">
      <label for="six">Six Pack</label>
    </div>
    <div>
      <input type="radio" id="twelve" name="coke">
      <label for="twelve">Twelve Pack</label>
    </div>
    <div>
      <input type="radio" id="crate" name="coke">
      <label for="crate">Entire Crate</label>
    </div>
  </form>
img {
  height: 600px;
  float: left;
  shape-outside: url("bottle.png");
  filter: brightness(1.5);
}
input {
  -webkit-appearance: none;
  appearance: none;
  width: 25px;
  height: 25px;
  margin-left: 20px;
  box-sizing: content-box;
  border: 10px solid #231714;
  border-radius: 50%;
  background: linear-gradient(45deg, pink, beige);
  cursor: pointer;
}

The image of the bottle is floated left and given a shape boundary using the shape-outside property. The image itself is referenced for the shape.

Note: Only images with transparent backgrounds can produce shapes according to the silhouettes of the images.

The default style of the radio buttons is replaced with a custom style. Once the browser applies the shape to the floated image, the radio buttons automatically align themselves along the shape of the bottle.

Like this, we don’t have to bother with individually assigning positions for each radio button to create such a design. Any buttons later added will automatically be aligned with the buttons before them according to the shape of the bottle.

Here’s another example, inspired by the Wikipedia homepage. This is a perfect example of the sort of unconventional main menu layouts we’re looking at.

Screenshot of the Wikipedia home page, displaying the site logo above a world globe made out of puzzle pieces. Links to various languages float around the globe's edge, like English, Spanish, German, in blue. Each link has a light grey count of how many articles are available in each language.

It’s not too crazy to make with shape-outside:

<div>
  <img src="earth.png">
  <div class="left">
    <a href="#">Formation</a><br>
    <a href="#">Atmosphere</a><br>
    <a href="#">Heat</a><br>
    <a href="#">Gravitation</a>
  </div>
</div>
<div>
  <img src="earth.png">
  <div class="right">
    <a href="#">Moon</a><br>
    <a href="#">Climate</a><br>
    <a href="#">Rotation</a><br>
    <a href="#">Orbit</a>
  </div>
</div>
img {
  height: 250px;
  float: left;
  shape-outside: circle(40%);
}

/* stack both sets of menus on the same grid cell */
main > div { grid-area: 1/1; } 

/* one set of menus is flipped and moved sideways over the other */
.right { transform: rotatey(180deg) translatex(250px); }

/* links inside the flipped set of menus are rotated back */
.right > a { 
  display: inline-block; 
  transform: rotateY(180deg) translateX(-40px); 
}

/* hide one of the images */
main > div:nth-of-type(2) img { visibility: hidden; }

An element only ever floats left or right. There’s no center floating element where content wraps around both the sides. In order to achieve the design where links wrap on both the sides of the image, I made two sets of links and flipped one of the sets horizontally. I used the same image with a circle() CSS shape value in both the sets so the shapes match even after the rotation. The text of the links of the flipped set will appear upside down sideways, so it’s rotated back.

Although both the images can sit on top of each other with no visible overflow, it’s best to hide one of them with either opacity or visibility property.

The third example is a bit lively thanks to the use of one of the dynamic HTML elements, <details>. This demo is a good example for designs to show extra information on products and such which by default are hidden to the users.

<img src="diamond.png">
<details>
  <summary>Click to know more!</summary>
  <ul>
    <li>The diamond is now known as the Sancy
    <li>It comprises two back-to-back crowns
    <li>It's likely of Indian origin
  </ul>
</details>
img {
  height: 200px;
  float: left;
  shape-outside: url("diamond.png");
  shape-margin: 20px;
}
summary {
  background: red;
  color: white;
  cursor: pointer;
  font-weight: bold;
  width: 80%; 
  height: 30px;
  line-height: 30px;
}

The image is floated left and is given a CSS shape that’s same as the image. The shape-margin property adds margin space around the shape assigned to the floated element. When the <summary> element is clicked, the parent <details> element reveals its content that automatically wraps along the shape of the floated diamond image.

The content of the <details> element doesn’t necessarily have to be a list, like in the demo. Any inline content would wrap along the floated image’s shape.

The final example works with a polygon shape instead of images or simple shapes like circle and ellipse. Polygons give us more angular geometric shapes that can easily be bent by adding just another coordinate in the shape.

<img src="nasa.png">
<div><!-- triangle --></div>
<ul>
  <li><a href="#">Home</a>
  <li><a href="#">Projects</a>
  <li><a href="#">Shop</a>
  <li><a href="#">Contact</a>
  <li><a href="#">Media</a>
</ul>
div {
  width: 0;
  height: 0;
  --d:  200px;
  /* red triangle */
  border-right: var(--d) solid transparent;
  border-bottom: var(--d) solid transparent;
  border-left: var(--d) solid red;
  float: left;
  /* triangle CSS shape */
  shape-outside: polygon(0 0, var(--d) 0, 0 var(--d));
}
ul {
  list-style: none;
  padding-top: 25px;
}

A left-floated red triangle is created using border properties. To create a triangular CSS shape that matches the red triangle, we’re using the polygon function as a value for the shape-outside property. The value for the polygon() function is the three coordinates of the triangle, separated by commas. The links automatically align around the floated triangle, forming a slanted menu layout down the triangle’s hypotenuse.

As you can see, even for a simple diagonal layout of user controls, CSS Shapes do a nice job adding a little pizzazz to a design. Using CSS Shapes is a much better option than rotating a line of user controls — the alignment of the individual controls and text also rotate, creating layout weirdness. By contrast, a CSS Shape simply lays out the individual controls along the provided shape’s boundary.