Experiment: Pure CSS camera icon

Pure CSS icons are always fun to make, so after coming across this one-layer camera icon on Dribbble I deemed it time to don my CSS cap and get to work recreating it!

Notable techniques used in this experiment:


The final result:


Humble beginnings

In order to make this icon easier to digest, I'll begin by splitting it up into three sections: the camera housing, the lens, and the glare. The camera housing is simple and is used to house the camera (who'd have thought?), along with giving some 3D depth to the icon with some gradient and box shadow magick.

I'm going to start from the bottom up, so first up is the base layer. I started off by making a rounded square, like so:

.icon {
  background-color: #fff;
  border-radius: 12%;
  height: 240px;
  width: 240px;
}

Next, I added some depth with box shadowing. One shadow, 0 0 4px #000, was used as a soft border to lift the icon from the page (a physical border looked too sharp and ruined the illusion) with another, 0 16px 20px -7px rgba(0, 0, 0, 0.75) , being used as a hard shadow to give the effect of the icon standing up.

Adding these in results in the following code:

.icon {
  background-color: #fff;
  border-radius: 12%;
  box-shadow: 0 16px 20px -7px rgba(0, 0, 0, 0.75), 0 0 4px #000;
  height: 240px;
  width: 240px;
}

Finally the lighting is put in place. This was, without a doubt, the most time consuming part of the build. It's seemingly impossible to have a short colour stop on a radial gradient without having an extremely small radius (if that's not the case, let me know!), so inspiration was taken from real physics. Shadowing was added with another box shadow, inset 0 -120px 130px #747e82, which covers the majority of the layer, with the shine being achieved by the white background not being covered by shadow! Last but not least, a faint inlay inset 0 0 30px rgba(255, 255, 255, 0.75) was to be added around the edges, and with it, an increasing sense of depth.

.icon {
  background-color: #fff;
  border-radius: 12%;
  box-shadow: 0 16px 20px -7px rgba(0, 0, 0, 0.75), 0 0 4px #000, inset 0 -120px 130px #747e82, inset 0 0 30px rgba(255, 255, 255, 0.75);
  height: 240px;
  width: 240px;
}

View the completed layer below:


Adding the lens

If you're afraid of radial gradients then look away now as the lens is chock full of them! The lens element is divided into two sections: the outer circle, and the lens itself, which contains the housing and the glass.

Starting off with the outer circle - which is essentially a simple gradient with some box shadow to give the illusion of a recessed element - let's make a quick circle and centre it:

.lens {
  $border-width: 1px;
  $size: 73%;

  border: $border-width solid #1d2227;
  @include border-radius(50%);
  height: $size;
  position: absolute;
  top: calc((100% - ) / 2);
  left: calc((100% - ) / 2);
  width: $size;
}

Now to add the gradient:

@include background-image(
  /* Lens background */
  radial-gradient(circle farthest-corner at bottom center, #6d7376 60%, #000 70%)
);

And as with the icon's base, the shading is done with box shadow, due to the radial gradient limitations.

@include box-shadow((
  inset 0 80px 80px #000,
  0 2px 0 rgba(#fff, 0.5),
  0 -3px 1px rgba(#000, 0.25),
  inset 0 -2px 2px #000
));

Moving onto the lens itself, we will add another simple circle for the housing, with a subtle inner shadow to add depth:

.lens:before {
  $size: 75%;

  @include background-image(
    radial-gradient(circle farthest-corner at top left, rgba(#fff, 0.7), #000 50%)
  );
  @include border-radius(50%);
  @include box-shadow((
    inset 5px -10px 40px #000,
  ));
  content: '';
  display: block;
  height: $size;
  left: 50% - ($size / 2);
  position: absolute;
  top: 50% - ($size / 2);
  width: $size;
  z-index: 1;
}

And now the glass. This is a result of three gradients (an extension of the lens flare, lighting and the main background gradient respectively) and some more box shadow depth. The last box shadow is used as a white inner shadow, and can be seen at the bottom of the element.

&:after {
  @include background-image(
    radial-gradient(rgba(#2273a2, 0.5) 0%, rgba(#2273a2, 0.5) 40%, #3065a6 48%, transparent 50%),
    radial-gradient(circle farthest-corner at top left, rgba(#fff, 0.3), transparent 50%),
    radial-gradient(circle farthest-corner at top center, #00192d, lighten(#00192d, 8%))
  );
  @include border-radius(50%);
  border: 1px solid #002954;
  @include box-shadow((
    1px 1px 0px rgba(#fff, 0.2),

    -1px -1px 0 rgba(#000, 0.25),
    -2px -2px 0 rgba(#fff, 0.03),
    3px 3px 0 rgba(#fff, 0.03),

    inset 10px -30px 20px -20px rgba(#fff, 0.25)
  ));
  content: '';
  display: block;
  height: 50%;
  left: calc(25% - 1px);
  position: absolute;
  top: calc(25% - 1px);
  width: 50%;
  z-index: 2;
}

The icon so far:

Everything is better with lens flare

What would a camera be without some lens flare? Without further ado, let's move onto the final piece of our camera icon.

.flare {
  $size: 21%;

  @include background-image(
    radial-gradient(#dfffff 15%, #c3ffff 16%, #a4e8ff 21%, #004f80 100%)
  );
  @include border-radius(50%);
  @include box-shadow((
    /* Glare shading */
    inset 0 -2px 8px rgba(#004f82, 1),
    inset -2px 0 8px rgba(#004f82, 1),

    /* Drop shadow for 3d glare effect */
    0 1px 2px rgba(#004c7c, 0.5)
  ));
  height: $size;
  position: absolute;
  top: 50% - ($size / 2);
  left: 50% - ($size / 2);
  width: $size;
  z-index: 3;
}

The flare gradient uses multiple colour stops to achieve a more 3-dimensional effect, with a few more shadows to complete the shading. The two outer flares are positioned absolutely and are simply semi-transparent circles, like so:

/* Large lens flare */
&:before {
  background-color: rgba(#fff, 0.15);
  @include border-radius(50%);
  content: '';
  display: block;
  left: 10px;
  top: 10px;
  height: 53px;
  position: absolute;
  width: 53px;
}

/* Small lens flare */
&:after {
  background-color: rgba(#fff, 0.16);
  @include border-radius(50%);
  content: '';
  display: block;
  height: 30px;
  top: 21px;
  left: 21px;
  position: absolute;
  width: 30px;
}


Afterthought

Of course, this is just an experiment to test the capabilities of CSS and is completely impractical to use. In the real world, this would be much better achieved using SVG or something similar. However, a lot was learnt in regards to the gradient syntax and also certain shadowing techniques. Whether or not I'll use them is another matter, however!

About the author
Profile pic
Joe McKie is a professional freelance web developer/designer from Nottingham, UK with a specialty in large-scale web applications and SaaS products. When he's trying to work, he can often be found being crushed under the weight of his rather large "lap dog".