Creating an Animated Gauge with CSS3

Exclusive offer: get 50% off this eBook here
Designing Next Generation Web Projects with CSS3

Designing Next Generation Web Projects with CSS3 — Save 50%

A practical guide to the usage of CSS3 – a journey through properties, tools, and techniques to better understand CSS3 with this book and ebook.

$26.99    $13.50
by Sandro Paganotti | March 2013 | Web Development

In web application development, gauges can be useful for showing complicated or dynamic data in a visual or intuitive way. In this article by Sandro Paganotti, author of  Designing Next Generation Web Projects with CSS3, we'll learn how to create a fully customizable animated gauge that can respond to real-time changes. We'll also discuss techniques to port this type of widget for support in older web browsers. We'll start by learning about a cool SASS enhancement called Compass; this is another way to deal with CSS3 experimental prefixes. The following is the list of the topics we'll discuss:

  • A basic gauge structure

  • Using Compass

  • Using rem

  • Moving the arrow

  • Animating the arrow

  • Dealing with older browsers

(For more resources related to this topic, see here.)

A basic gauge structure

Let's begin with a new project; as usual we need to create an index.html file. This time the markup involved is so small and compact that we can add it right now:

<!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>Go Go Gauges</title> <link rel="stylesheet" type="text/css" href="css/application.css"> </head> <body> <div data-gauge data-min="0" data-max="100" data-percent="50"> <div data-arrow></div> </div> </body> </html>

The gauge widget is identified by the data-gauge attribute and defined with three other custom data attributes; namely, data-min, data-max, and data-percent, which indicate the respective minimum and maximum value of the range and the current arrow position expressed in percentage value.

Within the element marked with the data-gauge attribute, we have defined a div tag that will become the arrow of the gauge.

To start with the styling phase, we first need to equip ourselves with a framework that is easy to use and can give us the opportunity to generate CSS code. We decide to go for SASS so we first need to install Ruby (http://www.ruby-lang.org/en/downloads/) and then enter the following from a command-line terminal:

gem install sass

You would probably need to execute the following command if you are working in Unix/Linux environments:

sudo gem install sass

Installing Compass

For this project we'll also use Compass, a SASS extension able to add some interesting features to our SASS stylesheet.

To install Compass, we have to just enter gem install compass (or sudo gem install compass) in a terminal window. After the installation procedure is over, we have to create a small config.rb file in the root folder of our project using the following code:

# Require any additional compass plugins here. # Set this to the root of your project when deployed: http_path = YOUR-HTTP-PROJECT-PATH css_dir = "css" sass_dir = "scss" images_dir = "img" javascripts_dir = "js" # You can select your preferred output style here (can be overridden via

the command line): # output_style = :expanded or :nested or :compact or :compressed # To enable relative paths to assets via compass helper functions. Uncomment: relative_assets = true # To disable debugging comments that display the original location of your

selectors. Uncomment: # line_comments = false preferred_syntax = :sass

The config.rb file helps Compass to understand the location of the various assets of the project; let's have a look at these options in detail:

  • http_path: This must be set to the HTTP URL related to the project's root folder

  • css_dir: This contains the relative path to the folder where the generated CSS files should be saved

  • sass_dir: This contains the relative path to the folder that contains our .scss files

  • images_dir: This contains the relative path to the folder that holds all the images of the project

  • javascripts_dir: This is similar to images_dir, but for JavaScript files

There are other options available; we can decide whether the output CSS should be compressed or not, or we can ask Compass to use relative paths instead of absolute ones. For a complete list of all the options available, see the documentation at http://compass-style.org/help/tutorials/configuration-reference/.

Next, we can create the folder structure we just described, providing our project with the css, img, js, and scss folders. Lastly, we can create an empty scss/application.scss file and start discovering the beauty of Compass.

CSS reset and vendor prefixes

We can ask Compass to regenerate the CSS file after each update to its SCSS counterpart. To do so, we need to execute the following command from the root of our project using a terminal:

compass watch .

Compass provides an alternative to the Yahoo! reset stylesheet we used in our previous project. To include this stylesheet, all we have to do is add a SASS include directive to our application.scss file:

@import "compass/reset";

If we check css/application.css, the following is the result (trimmed):

/* line 17, ../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass- 0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font: inherit; font-size: 100%; vertical-align: baseline; } /* line 22, ../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass- 0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } ...

Notice also how the generated CSS keeps a reference to the original SCSS; this comes in handy when it's a matter of debugging some unexpected behaviors in our page.

The next @import directive will take care of the CSS3 experimental vendor prefixes. By adding @import "compass/css3" on top of the application.scss file, we ask Compass to provide us with a lot of powerful methods for adding experimental prefixes automatically; for example, the following snippet:

.round { @include border-radius(4px); }

Is compiled into the following:

.round { -moz-border-radius: 4px; -webkit-border-radius: 4px; -o-border-radius: 4px; -ms-border-radius: 4px; -khtml-border-radius: 4px; border-radius: 4px; }

Equipped with this new knowledge, we can now start deploying the project.

Using rem

For this project we want to introduce rem, a measurement unit that is almost equivalent to em, but is always relative to the root element of the page. So, basically we can define a font size on the html element and then all the sizes will be related to it:

html{ font-size: 20px; }

Now, 1rem corresponds to 20px; the problem of this measurement is that some browsers, such as Internet Explorer version 8 or less, don't actually support it. To find a way around this problem, we can use the following two different fallback measurement units:

  • em: The good news is that em, if perfectly tuned, works exactly as rem; the bad news is that this measurement unit is relative to the element's font-size property and is not relative to html. So, if we decide to pursue this method, we then have to take extra care every time we deal with font-size.

  • px: We can use a fixed unit pixel size. The downside of this choice is that in older browsers, we're complicating the ability to dynamically change the proportions of our widget.

In this project, we will use pixels as our unit of measurement. The reason we have decided this is because one of the rem benefits is that we can change the size of the gauge easily by changing the font-size property with media queries. This is only possible where media queries and rem are supported.

Now, we have to find a way to address most of the duplication that would emerge from having to insert every statement containing a space measurement unit twice (rem and px). We can easily solve this problem by creating a SASS mixin within our application.scss file as follows (for more info on SASS mixins, we can refer to the specifications page at http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#mixins):

@mixin px_and_rem($property, $value, $mux){ #{$property}: 0px + ($value * $mux); #{$property}: 0rem + $value; }

So the next time instead of writing the following:

#my_style{ width: 10rem; }

We can instead write:

#my_style{ @include px_and_rem(width, 10, 20); }

In addition to that, we can also save the multiplier coefficient between px and rem in a variable and use it in every call to this function and within the html declaration; let's also add this to application.scss:

$multiplier: 20px; html{ font-size: $multiplier; }

Of course, there are still some cases in which the @mixin directive that we just created doesn't work, and in such situations we'll have to handle this duality manually.

Basic structure of a gauge

Now we're ready to develop at least the basic structure of our gauge, which includes the rounded borders and the minimum and maximum range labels. The following code is what we need to add to application.scss:

div[data-gauge]{ position: absolute; /* width, height and rounded corners */ @include px_and_rem(width, 10, $multiplier); @include px_and_rem(height, 5, $multiplier); @include px_and_rem(border-top-left-radius, 5, $multiplier); @include px_and_rem(border-top-right-radius, 5, $multiplier); /* centering */ @include px_and_rem(margin-top, -2.5, $multiplier); @include px_and_rem(margin-left, -5, $multiplier); top: 50%; left: 50%; /* inset shadows, both in px and rem */ box-shadow: 0 0 #{0.1 * $multiplier} rgba(99,99,99,0.8), 0 0 #{0.1 * $multiplier} rgba(99,99,99,0.8) inset; box-shadow: 0 0 0.1rem rgba(99,99,99,0.8), 0 0 0.1rem rgba(99,99,99,0.8) inset; /* border, font size, family and color */ border: #{0.05 * $multiplier} solid rgb(99,99,99); border: 0.05rem solid rgb(99,99,99); color: rgb(33,33,33); @include px_and_rem(font-size, 0.7, $multiplier); font-family: verdana, arial, sans-serif; /* min label */ &:before{ content: attr(data-min); position: absolute; @include px_and_rem(bottom, 0.2, $multiplier); @include px_and_rem(left, 0.4, $multiplier); } /* max label */ &:after{ content: attr(data-max); position: absolute; @include px_and_rem(bottom, 0.2, $multiplier); @include px_and_rem(right, 0.4, $multiplier); } }

With box-shadow and border, we can't use the px_and_rem mixin, so we duplicated these properties using px first and then rem.

The following screenshot shows the result:

Gauge tick marks

How to handle tick marks? One method would be by using images, but another interesting alternative is to benefit from multiple background support and create those tick marks out of gradients. For example, to create a vertical mark, we can use the following within the div[data-gauge] selector:

linear-gradient(0deg, transparent 46%,
rgba(99, 99, 99, 0.5) 47%, rgba(99, 99, 99, 0.5) 53%, transparent 54%)

Basically, we define a very small gradient between transparent and another color in order to obtain the tick mark. That's the first step, but we're yet to deal with the fact that each tick mark must be defined with a different angle. We can solve this problem by introducing a SASS function that takes the number of tick marks to print and iterates up to that number while also adjusting the angles of each mark. Of course, we also have to take care of experimental vendor prefixes, but we can count on Compass for that.

The following is the function. We can create a new file called scss/_gauge.scss for this and other gauge-related functions; the leading underscore is to tell SASS to not create a .css file out of this .scss file, because it will be included in a separate file.

@function gauge-tick-marks($n, $rest){ $linear: null; @for $i from 1 through $n { $p: -90deg + 180 / ($n+1) * $i; $linear: append($linear, linear-gradient( $p, transparent 46%, rgba (99,99,99,0.5) 47%, rgba(99,99,99,0.5) 53%, transparent 54%), comma); } @return append($linear, $rest); }

We start with an empty string adding the result of calling the linear-gradient Compass function, which handles experimental vendor prefixes, with an angle that varies based on the current tick mark index.

To test this function out, we first need to include _gauge.scss in application.scss:

@import "gauge.scss";

Next, we can insert the function call within the div[data-gauge] selector in application.scss, specifying the number of tick marks required:

@include background(gauge-tick-marks(11,null));

The background function is also provided by Compass and it is just another mechanism to deal with experimental prefixes. Unfortunately, if we reload the projects the results are far from expected:

Although we can see a total of 11 stripes, they are of the wrong sizes and in the wrong position. To resolve this, we will create some functions to set the correct values for background-size and background-position.

Dealing with background size and position

Let's start with background-size, the easiest. Since we want each of the tick marks to be exactly 1rem in size, we can proceed by creating a function that prints 1rem 1rem as many times as the number of the passed parameter; so let's add the following code to _gauge.scss:

@function gauge-tick-marks-size($n, $rest){ $sizes: null; @for $i from 1 through $n { $sizes: append($sizes, 1rem 1rem, comma); } @return append($sizes, $rest, comma); }

We already noticed the append function; an interesting thing to know about it is that the last parameter of this function lets us decide if some letter must be used to concatenate the strings being created. One of the available options is comma, which perfectly suits our needs.

Now, we can add a call to this function within the div[data-gauge] selector:

background-size: gauge-tick-marks-size(11, null);

And the following is the result:

Now the tick marks are of the right size, but they are displayed one above the other and are repeated all across the element. To avoid this behavior, we can simply add background-repeat: no-repeat just below the previous instruction:

background-repeat: no-repeat;

On the other hand, to handle the position of the tick marks we need another SASS function; this time it's a little more complex and involves a bit of trigonometry. Each gradient must be placed in the function of its angle—x is the cosine of that angle and y the sine. The sin and cos functions are provided by Compass, we need just to handle the shift, because they are referred to the center of the circle whereas our css property's origin is in the upper-left corner:

@function gauge-tick-marks-position($n, $rest){ $positions: null; @for $i from 1 through $n { $angle: 0deg + 180 / ($n+1) * $i; $px: 100% * ( cos($angle) / 2 + 0.5 ); $py: 100% * (1 - sin($angle)); $positions: append($positions, $px $py, comma); } @return append($positions, $rest, comma); }

Now we can go ahead and add a new line inside the div[data-gauge] selector:

background-position: gauge-tick-marks-position(11, null);

And here's the much-awaited result:

The next step is to create a @mixin directive to hold these three functions together, so we can add the following to _gauge.scss:

@mixin gauge-background($ticks, $rest_gradient, $rest_size, $rest_position)
{ @include background-image( gauge-tick-marks($ticks, $rest_gradient) ); background-size: gauge-tick-marks-size($ticks, $rest_size); background-position: gauge-tick-marks-position($ticks, $rest_position); background-repeat: no-repeat; }

And replace what we placed inside div[data-gauge] in this article with a single invocation:

@include gauge-background(11, null, null, null );

We've also left three additional parameters to define extra values for background, background-size, and background-position, so we can, for example, easily add a gradient background:

@include gauge-background(11, radial-gradient(50% 100%, circle, rgb(255,255,255), rgb(230,230,230)), cover, center center );

And following is the screenshot:

Creating the arrow

To create an arrow we can start by defining the circular element in the center of the gauge that holds the arrow. This is easy and doesn't introduce anything really new; here's the code that needs to be nested within the div[data-gauge] selector:

div[data-arrow]{ position: absolute; @include px_and_rem(width, 2, $multiplier); @include px_and_rem(height, 2, $multiplier); @include px_and_rem(border-radius, 5, $multiplier); @include px_and_rem(bottom, -1, $multiplier); left: 50%; @include px_and_rem(margin-left, -1, $multiplier); box-sizing: border-box; border: #{0.05 * $multiplier} solid rgb(99,99,99); border: 0.05rem solid rgb(99,99,99); background: #fcfcfc; }

The arrow itself is a more serious business; the basic idea is to use a linear gradient that adds a color only to half the element starting from its diagonal. Then we can rotate the element in order to move the pointed end at its center. The following is the code that needs to be placed within div[data-arrow]:

&:before{ position: absolute; display: block; content: ''; @include px_and_rem(width, 4, $multiplier); @include px_and_rem(height, 0.5, $multiplier); @include px_and_rem(bottom, 0.65, $multiplier); @include px_and_rem(left, -3, $multiplier); background-image: linear-gradient(83.11deg, transparent, transparent 49%, orange 51%, orange); background-image: -webkit-linear-gradient(83.11deg, transparent, transparent 49%, orange 51%, orange); background-image: -moz-linear-gradient(83.11deg, transparent, transparent 49%, orange 51%, orange); background-image: -o-linear-gradient(83.11deg, transparent,
transparent 49%, orange 51%, orange); @include apply-origin(100%, 100%); @include transform2d( rotate(-3.45deg)); box-shadow: 0px #{-0.05 * $multiplier} 0 rgba(0,0,0,0.2); box-shadow: 0px -0.05rem 0 rgba(0,0,0,0.2); @include px_and_rem(border-top-right-radius, 0.25, $multiplier); @include px_and_rem(border-bottom-right-radius, 0.35, $multiplier); }

To better understand the trick behind this implementation, we can temporarily add border: 1px solid red within the &:before selector to the result and zoom a bit:

Moving the arrow

Now we want to position the arrow to the correct angle depending on the data-percent attribute value. To do so we have to take advantage of the power of SASS. In theory the CSS3 specification would allow us to valorize some properties using values taken from attributes, but in practice this is only possible while dealing with the content property.

So what we're going to do is create a @for loop from 0 to 100 and print in each iteration a selector that matches a defined value of the data-percent attribute. Then we'll set a different rotate() property for each of the CSS rules.

The following is the code; this time it must be placed within the div[data-gauge] selector:

@for $i from 0 through 100 { $v: $i; @if $i < 10 { $v: '0' + $i; } &[data-percent='#{$v}'] > div[data-arrow]{ @include transform2d(rotate(#{180deg * $i/100})); } }

If you are too scared about the amount of CSS generated, then you can decide to adjust the increment of the gauge, for example, to 10:

@for $i from 0 through 10 { &[data-percent='#{$i*10}'] > div[data-arrow]{ @include transform2d(rotate(#{180deg * $i/10})); } }

And the following is the result:

 

 

Designing Next Generation Web Projects with CSS3 A practical guide to the usage of CSS3 – a journey through properties, tools, and techniques to better understand CSS3 with this book and ebook.
Published: January 2013
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Animating the gauge

We can now animate the arrow using CSS transitions. Basically, we have to tell the browser that the transform property needs to be animated; the necessary SASS code is a bit longer than expected because Compass is not yet able to prefix the transition property and its value (https://github.com/chriseppstein/compass/issues/289), so we have to do it manually:

-webkit-transition: -webkit-transform 0.5s; -moz-transition: -moz-transform 0.5s; -ms-transition: -ms-transform 0.5s; -o-transition: -o-transform 0.5s; transition: transform 0.5s;

After we have placed these CSS instructions inside the div[data-arrow] selector, we'll notice that if we change the data-percentage property, for example, by using Chrome and its development console, the arrow responds with a smooth animation.

Overall indicator

Some gauges present a color indicator, usually from green to red, associated with the position of the arrow; we can work out a similar result. First of all we need to define two new custom data attributes, one that indicates the percentage at which the indicator switches from green to orange and the other where the percentage switches from orange to red. Here it is:

<div data-gauge data-min="0" data-max="100" data-percent="50" data- orange-from="60" data-red-from="90"> <div data-arrow></div> </div>

Then we need to specify a default background color, let's say green, within div[data-gauge]:

background-color: green;

Next, we redefine the background gradient to leave the first 25 percent of the circumference transparent; in this way we can display (and control) the underlying color, so let's rewrite the gauge-background call:

@include gauge-background(11, radial-gradient(50% 100%, circle, rgba(255,255,255,0), rgba(255,255,255,0) 25%, rgb(255,255,255) 25%, rgb(230,230,230)), cover, center center );

Now we can use another Sass loop to change the background-color property respecting the value defined in the attributes. Since we're going to implement a loop nested in the previous loop, we have to be careful not to increase the size of the resulting CSS too much.

To achieve this let's consider only the 10s of the data-orange-from and data-red-from data attributes. What we need to do is basically write a CSS rule that activates the red or orange background color if the data-percentage property is greater than or equal to data-orange-from or data-red-from.

The following is the complete loop, including the previous loop we used to move the arrow:

@for $i from 0 through 100 { $v: $i; @if $i < 10 { $v: '0' + $i; } &[data-percent='#{$v}'] > div[data-arrow]{ @include transform2d(rotate(#{180deg * $i/100})); } @for $k from 0 through 10 { @if $i >= $k * 10 { &[data-percent='#{$v}'][data-orange-from^='#{$k}']{ background-color: orange; } &[data-percent='#{$v}'][data-red-from^='#{$k}']{ background-color: red; } } } }

And following is the result:

Reducing the size of the CSS

We can reduce the size of the generated CSS by asking Compass to not add a comment before each rule pointing to the corresponding SASS rule. If we want to do that, simply add line_comments = false to our config.rb file and then stop and relaunch compass watch in the project's root folder.

Adding some trembling

As an additional feature we can add an option to let the arrow tremble a bit when it nears 100 percent. We can achieve this behavior by adding a small animation if an extra data-trembling attribute is present:

<div data-gauge data-min="0" data-max="100" data-percent="50"
data-orange-from="60" data-red-from="90" data-trembling>

Unfortunately, Compass doesn't provide CSS3 animation mixins out of the box, so we have to install a Compass plugin that can help us with that. In this case, the plugin is called compass-animation (https://github.com/ericam/compass-animation), created by Eric Meyer (http://eric.andmeyer.com/). This is how it's installed:

gem install animation –pre

Or as follows:

sudo gem install animation –-pre

And then we have to include the plugin both when calling compass watch:

compass watch . –r animation

And in the header of application.scss:

@import "animation";

Well done! Now we're ready to define a really simple animation that modifies the rotating angle of the arrow causing the trembling effect we're looking for. Let's add a few lines of code at the end of application.scss:

@include keyframes(trembling) { 0% { @include transform2d( rotate(-5.17deg)); } 100% { @include transform2d( rotate(-1.725deg)); } }

Then we need to add a new rule within div[data-gauge] that activates this animation if data-trembling is present and data-percentage starts with 8 or 9 or is equal to 100:

&[data-trembling][data-percent^='8'] > div[data-arrow]:before, &[data-trembling][data-percent^='9'] > div[data-arrow]:before, &[data-trembling][data-percent='100'] > div[data-arrow]:before{ @include animation(trembling 0.2s infinite linear alternate); }

Unfortunately, due to some yet-to-be-resolved bugs in WebKit-based browsers that prevent animations from being applied to before and after pseudo-selectors, at the time of writing only Firefox correctly implements this behavior:

Displaying the gauge value

If we make a small edit to our HTML code, we can easily display the current gauge value:

<div data-gauge data-min="0" data-max="100" data-percent="50" data-orange-from="60" data-red-from="90" data-trembling> <span>50</span> <div data-arrow></div> </div>

And following is the code to add within the div[data-gauge] selector:

span{ display: block; color: #DDD; @include px_and_rem(font-size, 1.5, $multiplier); text-align: center; @include px_and_rem(width, 10, $multiplier); @include px_and_rem(height, 5, $multiplier); @include px_and_rem(line-height, 5, $multiplier); }

The result:

Graceful degradation

To keep this widget meaningful also for those browsers that do not support background gradients, we have to handle a different representation for the arrow. To detect where this feature is missing, we can use Modernizr by creating a custom build (http://modernizr.com/download/) that check only for gradient support:

<script src ="js/modernizr.js"></script>

Then we can go for a solid background color; the arrow will, of course, become a rectangle but we'll save the meaning of the widget; let's add this rule at the bottom of application.scss:

.no-cssgradients div[data-gauge]{ div[data-arrow]:before{ background-color: orange; @include transform2d( rotate(0deg)); box-shadow: none; border-radius: 0; } }

And following is the result:

We can go a step forward by using Compass' ability to translate gradients into Base64-encoded SVG and use them as fallback background images where native gradients are not supported. Unfortunately, this doesn't work with gradients that express an angle using numeric values, such as 23deg, so we will not be able to reproduce tick marks. We can however ask Compass to convert the radial-gradient property we use for background. The following are the properties we need to add inside the .no-cssgradients div[data-gauge] rule:

background-image: -svg(radial-gradient(50% 100%, circle, rgba (255,255,255,0), rgba(255,255,255,0) 35%, rgb(255,255,255) 35%, rgb (230,230,230))); background-size: cover; background-position: auto;

And the following is the result, much closer to the original gauge:

Implementing the gauge in Internet Explorer 8

If we want to support Internet Explorer 8, then we need to address the lack of both the border-radius and transform properties.

For border-radius we can use a JavaScript-based polyfill such as CSS3 Pie, and we can download this polyfill from its website, http://css3pie.com/, and then copy PIE.js in our project's js folder. Next, we can include this JavaScript file from index.html along with the latest version of jQuery and js/application.js, an empty file we are going to use in a while:

<!--[if IE 8]> <script src = "http://code.jquery.com/jquery-1.8.0.min.js"></script> <script src = "js/PIE.js"></script> <script src = "js/application.js"></script> <![endif]-->

Usually CSS3 Pie automatically detects how to enhance a given element by identifying the CSS3 properties to emulate. In this case, however, we have used border-top-left-radius and border-top-right-radius whereas CSS3 Pie only supports the general border-radius. We can find a way around this by adding a special border-radius property prefixed with –pie inside the div[data-gauge] rule:

-pie-border-radius: #{5 * $multiplier} #{5 * $multiplier} 0px 0px;

Next, we have to activate CSS3 Pie by inserting a few lines of JavaScript code inside js/application.js:

$(function() { if (window.PIE) { $('div[data-gauge]').each(function() { PIE.attach(this); }); } });

And following is the result:

Now if we want to activate the arrow rotation, we need to emulate the transform property. To achieve this behavior, we can use jquery.transform.js (https://github.com/louisremi/jquery.transform.js) by Louis-Rémi Babé (http://twitter.com/louis_remi).

After downloading the library, we need to copy jquery.transform2d.js into the js folder of our project. Then we add the necessary script element in index.html. In order to add a different class to the html element when the browser is Internet Explorer 8, we will use IE conditional comments to add a different class to the html element. The following is the result:

<!doctype html> <!--[if IE 8]> <html class="ie8" > <![endif]--> <!--[if !IE]> --> <html> <!-- <![endif]--> <head> <title>Go Go Gauges</title> <script src ="js/modernizr.js"></script> <link rel="stylesheet" type="text/css" href="css/application.css"> <!--[if IE 8]> <script src ="http://code.jquery.com/jquery-1.8.0.min.js"></script> <script src ="js/jquery.transform2d.js"></script> <script src ="js/PIE.js"></script> <script src ="js/application.js"></script> <![endif]--> </head> <!-- ...rest of index.html ... -->

jquery.transform2d.js adds the ability to trigger the transform property even on Internet Explorer 8 thereby enhancing the css function provided by jQuery; the following is an example:

$(elem).css('transform', 'translate(50px, 30px) rotate(25deg) scale(2,.5) skewX(-35deg)');

So, we can try to add a few more JavaScript lines of code by calling the preceding function; this transforms js/application.js as follows:

$(function() { if (window.PIE) { $('div[data-gauge]').each(function() { PIE.attach(this); var angle = Math.round(180 * parseInt($(this).attr
('data-percent'),10)/100); $('div[data-arrow]',$(this)).css({ 'transform': 'rotate(' + angle + 'deg)' });
}); } });

Unfortunately, the results are not as good as expected:

The problem is that the div[data-arrow]:before element is clipped within its parent. This can be resolved by drawing a white disk (now a square) under the arrow, and resizing div[data-arrow] to be as large as the whole widget with a transparent background and no borders in order to easily contain the arrow.

To do so we can use the .ie8 class to add some properties only when the browser is Internet Explorer 8. Let's append a few lines of code to application.scss.

.ie8 div[data-gauge]{ div[data-arrow]{ width: #{10 * $multiplier}; height: #{10 * $multiplier}; margin-top: #{-5 * $multiplier}; margin-left: #{-5 * $multiplier}; top: 50%; left: 50%; background: transparent; border: none; &:before{ bottom: 50%; margin-bottom: #{-0.25 * $multiplier}; left: #{1 * $multiplier}; } } }

And finally, following is the working result:

Compass and Internet Explorer 10

The latest version of Compass at the time of writing (0.12.0) doesn't add the -ms- experimental prefix to linear-gradient and radial-gradient. To find a way around this problem and make the gauge work smoothly also on IE10, we have to apply some modifications to our .scss code. In particular, we need to change the gauge-tick-marks function inside _gauge.scss as follows:

@function gauge-tick-marks($n, $rest, $ms){ $linear: null; @for $i from 1 through $n { $p: -90deg + 180 / ($n+1) * $i; $gradient: null; @if $ms == true { $gradient: -ms-linear-gradient( $p, transparent 46%,
rgba(99,99,99,0.5) 47%, rgba(99,99,99,0.5) 53%, transparent 54%); } @else{ $gradient: linear-gradient( $p, transparent 46%,
rgba(99,99,99,0.5)
47%,rgba(99,99,99,0.5) 53%, transparent 54%); }
$linear: append($linear, $gradient, comma); } @if $ms == true { @return append($linear, #{'-ms-' + $rest} ); } @else{ @return append($linear, $rest); } }

We also need to change the gauge-background mixin, also inside _gauge.scss:

@mixin gauge-background($ticks, $rest_gradient, $rest_size, $rest_position)
{ @include background-image( gauge-tick-marks($ticks, $rest_gradient, false) ); background-image: gauge-tick-marks($ticks, $rest_gradient, true); background-size: gauge-tick-marks-size($ticks, $rest_size); background-position: gauge-tick-marks-position($ticks, $rest_position); background-repeat: no-repeat; }

And finally we have to add an extra CSS line in application.scss inside :before within div[data-arrow]:

background-image: -ms-linear-gradient(83.11deg, transparent, transparent 49%, orange 51%, orange);

After making these small modifications, we can also appreciate this widget using Internet Explorer 10:

Summary

Drawing a gauge can be more difficult than expected; this is more truer if we also take care of keeping support for older browsers. In this article, we learned how to install and work with Compass, create complex CSS using the power of the SASS syntax, and deal with graceful degradation and polyfill techniques.

Resources for Article :


Further resources on this subject:


Designing Next Generation Web Projects with CSS3 A practical guide to the usage of CSS3 – a journey through properties, tools, and techniques to better understand CSS3 with this book and ebook.
Published: January 2013
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Sandro Paganotti

Sandro Paganotti is a web developer, a Google Developers Expert in HTML5, a technical writer, and an HTML5/CSS3 teacher with a passion for Ruby and cutting-edge frontend technologies. He enjoys finding clever and pragmatic solutions to ambitious projects at Comparto Web, the company he co-founded in 2012.

He's also a funding member of WEBdeBs, a local community of web enthusiasts, and a speaker at local and national conferences.

Books From Packt


Designing Next Generation Web Projects with CSS3
Designing Next Generation Web Projects with CSS3

HTML5 Games Development by Example: Beginner’s Guide
HTML5 Games Development by Example: Beginner’s Guide

Dreamweaver CS5.5 Mobile and Web Development with HTML5, CSS3, and jQuery
Dreamweaver CS5.5 Mobile and Web Development with HTML5, CSS3, and jQuery

Responsive Web Design with HTML5 and CSS3
Responsive Web Design with HTML5 and CSS3

HTML5 Enterprise Application Development
HTML5 Enterprise Application Development

Instant Migration to HTML5 and CSS3 How-to
Instant Migration to HTML5 and CSS3 How-to

Instant SASS CSS How-to
Instant SASS CSS How-to

Responsive Web Design by Example
Responsive Web Design by Example


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software