Grow your CSS skills. Land your dream job.

Well Rounded: Compound Shapes in CSS

Published by Guest Author

The following is a guest post by Parker Bennett. Parker is a bit of a regular around here, known for tackling common problems with unique solutions. This time he's back at it creating complex shapes that have image backgrounds, shadows, and curves, yet are flexible.

I love to collaborate on design because it often pushes me to try new things. Recently, I was given a design comp something like this: a rounded compound shape serving as a fixed header, with a textured inset that had an inner shadow all around (a "well"). Well, I thought, how hard could this be?


Curvy!

One answer was super easy, of course: I could have opted to slice up an image and use transparent .png files for the curvy bottom - shadow and all - with an "end-cap" that would allow the right-half to resize responsively (the first column was designed to be fixed-width).

But I wanted more flexibility down the road, so I decided to tackle it using CSS. Here's the approach I wound up taking, Nothing whiz-bangy, just good old CSS:

CodePen is Sassy!

CodePen turned out to be indispensable for all my experimentation, as well as for client review. One big advantage was being able to write in SCSS and quickly see the result: I could try different widths, colors, shadows, border radii, etc., just by changing variables.

I used variables for certain heights and widths, as well as box-shadow and border-radius sizes, then used those to calculate additional sizes - using ratios where it made sense to tie elements of the design together.


Variables rock!

In a Fix

For most projects I use a centered .page-wrap that's a percentage of the browser window. But when you pin your header to the top using position: fixed, it's no longer contained by the page-wrap. One solution is to start the page-wrap after the fixed header, and include a header-wrap that duplicates the width, margins, and padding of the page-wrap (details in this CodePen).

To maintain the illusion of the scrolling content being in a "cut-out" section below, I used overflow: hidden to crop the header's drop shadow at the sides. This meant a second wrapper div, in order to also maintain a little padding between the content and the window frame at small widths (yes, I'm just that anal).

z-index: Leveling Up

Naturally, I wanted to keep the markup lean, and not depend on source order for the layout. I used pseudo elements where they seemed to make sense, and relied a lot on absolute positioning to put things in their place - using z-index to change how they overlapped. Because absolute positioning removes the element from the layout flow, I would need to compensate at times, for example, adding padding to other elements to fill that space.

As I worked out the positioning, I found I could paint myself into a z-index corner thanks to the peculiarities of stacking order: z-index requires positioning to work, un-positioned elements render first, and it's easy to lose track of what elements are constrained by "stacking context" - for example, pseudo-elements can't rise above the z-index of their parent (see demo on CodePen).

I also changed the z-index of shadows, making them a separate pseudo element. This let me tuck shadows behind objects where they needed to be obscured or, as on the sides of the .main scrolling content, raise them up to cover anything that reached the edge so as not break the illusion of depth.

/* elements that reach the edges of .main need to fall
   under edge shadow to maintain "cut-out" illusion */

.well-sides {
  /* child elements absolute */
  position: relative;
  overflow: hidden; }

.well-sides:before, .well-sides:after {
  content: "";
  display: block;
  position: absolute;
  /* higher than any z-index in .main, lower than header */
  z-index: 99;
  /* shadow size */
  top: -20px;
  height: 120%;
  /* shadow size */
  width: 20px;
  background: transparent; }

.well-sides:before {
  left: 0;
  /* negative spread */
  -webkit-box-shadow: inset 20px -20px 20px -20px rgba(0,0,0,0.35);
     -moz-box-shadow: inset 20px -20px 20px -20px rgba(0,0,0,0.35);
          box-shadow: inset 20px -20px 20px -20px rgba(0,0,0,0.35); }

.well-sides:after {
  right: 0;
  /* negative spread */
  -webkit-box-shadow: inset -20px 20px 20px -20px rgba(0,0,0,0.35);
     -moz-box-shadow: inset -20px 20px 20px -20px rgba(0,0,0,0.35);
          box-shadow: inset -20px 20px 20px -20px rgba(0,0,0,0.35); }

Cutting Corners

Unfortunately, there's no practical way to create an object with a concave curve in CSS. Where the two rounded rectangles joined, I used a "patch" to cover the inset box-shadow "seam" and extend the textured background into the area of the inner corner. Then I overlapped it with a rounded white corner, creating the negative space as a positive. Finally, I added a normal box-shadow to the rounded corner (cropped by overflow: hidden) to blend it in with the inset box-shadow.


Outset shadow in "patch" had to be "opacified" a bit to match inset.


The inner circle

For the other inner curves I positioned a transparent square with one rounded corner and a thick white border on either side to complete the illusion. I used the border-radius and box-shadow sizes as variables to determine the size and placement of the corners.

How It All Stacks Up

Here's how I put the pieces together:

Slideshow here. If reading through syndication, see the real blog post to see the slideshow.

Wrapping Up

It's true I like a good challenge, but working it out this way also gave me a lot of flexibility during the process, and a head start on a mobile responsive layout (straightening the curvy bottom at smaller sizes, truncating the menu, etc.). At any rate, I hope you found some useful bits in my way-too-thorough exploration. If you have any questions, comments, or corrections, drop me a line: parker@parkerbennett.com.

Comments

  1. Mind …. blown. Simply awesome solution that now makes me want to refactor a bunch of code!!

  2. Permalink to comment#

    Very clever, but it’s not a technique that anybody is going to use on a regular basis. It feels nasty and hacky.

    This is definitely a problem that needs solving and css doesn’t currently provide a solution.

    • John
      Permalink to comment#

      Gotta agree with you – definitely feels nasty and hacky.

    • Ed
      Permalink to comment#

      It’s complex, but I wouldn’t consider anything here a ‘hack’ (in the relevant sense of the term, namely using a tool to achieve something it wasn’t designed for). But yeah, this does serve to highlight a few places where the current state of CSS is sorely lacking.

      WebKit has the awesome drop-shadow filter, which gets around the overlapping shadow issue (it will draw the shadow taking pseudo-elements into consideration, see this CodePen).

  3. gowda24
    Permalink to comment#

    Awsome solution.

  4. Useful Trick :)

  5. Permalink to comment#

    I don’t know if ‘hacky’ is quite fair – I just can’t feel comfortable with all the extraneous non-semantic markup.

  6. Wow what a great post. I’ve been experimenting with css to create complex rounded edges a lot like this. Thanks so much for the post. This is just what I was looking for!

  7. jake
    Permalink to comment#

    Great post! Digging the ‘How It All Stacks Up’ to see the progression. Thanks!

  8. Another awesome post.
    Thank you so much Chris!!!

  9. This front end work is hands down brutally creative, it’s mind blowing. Hat tip to you Mr. Parker Bennett.

    But I have to agree with Jon Hobbs and G Givan above, it’s just not easy to overlook the “…extraneous non-semantic markup.” as G Givan says.

    The other side of the coin is the actual design you were given: this is a clear example of a Graphic Designer doing Web Design. Totally “unrealistic” and undermined design. That designer is one of the luckiest people on the planet because he/she had you, an incredibly knowledgeable developer, implement his/her design.

    This whole thing you did here reminds me of the days when I was in college and I was learning design (for print) and we used CorelDRAW (yeah, no need for Illustrator at all ;]) and we were all using white boxes to hide things around and whatnot. When you looked at the designs in wireframe mode, you could see all these rectangles all over the design (especially on the sides covering elements that were bleeding). Only to find out later on the amazing power of Powerclips :).

    CSS has still so much to learn…

  10. Technically speaking, rad. Practically speaking, not rad.

    If I were to see a design like that with the beveled edge look and rounded corners without seeing the code, I would have immediately assumed it was a sliced up PSD rocking some transparent PNGs.

  11. Michael
    Permalink to comment#

    Reminds me of something I created at University in 2005. I would never design a site like that ever again :)

    So much awkward space. I don’t want CSS to much this layout easier to create… just a step backwards in my opinion.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".