In a previous post exploring rounded corner methods, I covered a technique using absolute positioning for faking rounded corners in browsers greater than IE6. One issue with faking rounded corners is the amount of HTML markup required as well as the hassle of crafting the accompanying CSS. I have created a jQuery plug-in for adding the required markup and a complementary Sass/Compass @mixin for generating the required CSS. The markup generated utilizes jQuery UI theming for the CSS class names. The code is available on GitHub
Summary of the Technique
As covered before, the idea is to use extra markup and CSS to create rounded corners even without support for
border-radius. Of course the technique is also useful for creating fancy corners that
border-radius wouldn't support. The two examples below show a normal
border-radius and a box that is using the absolutely positioned
span technique from the previous article.
The absolute positioning technique requires some additional markup to be added to the page for the corners and sides of the box. Below the initial
.box is shown with no additional markup.
In order to create the illusion of corners, some additional markup must be added to the page. First the content is wrapped in a
.ui-corners-content that is used with
z-index to ensure that the corners never cover up the content of our
border-radius doesn't clip content unless
overflow: hidden; is set. Next, eight
spans are added; one for each corner and side. Below is the markup used for creating the rounded corner illusion.
As you can see above, the extra markup is very straightforward but it's not exactly ideal to have to add this to every box on a website — this is the point of the jQuery plug-in that will be discussed further down.
z-index of the
.ui-corners-content and position each of the corners and sides. As mentioned in the previous article, conflicting positioning properties are used to ensure that the sides stretch the fill width of the
.box. For instance the left side,
.ui-corners-side-left, is given a
top and a
bottom of 10px. Setting the
bottom at the same time has the effect of stretching the
span the entire height because both properties are honored. The 10px spacing is to prevent the sides from overlapping the corners. As mentioned in the previous article, IE6 will ignore the conflicting properties which makes this a poor solution for sites that require IE6 support.
The CSS below will correctly position all of the
spans and set some default heights and widths. In order to actually see something, the base CSS needs to be extended by adding background images and altering the default heights and widths — this is the point of the Compass
@mixin that will be discussed further down.
Although absolutely positioned background images can look like a native
border-radius, they don't actually affect the sizing of the box like a real border does. It's easy to fake the presence of a border by adding
padding to the
.box equal to the width of the border. While it's possible to simply set a transparent border —
border: 10px solid transparent; — setting a border will require additional calculations for the corner and side
spans because border appears outside the box.
Using the jQuery Corners Plug-in
The jQuery Corners plug-in is very straightforward. Exactly as described in the basic markup section above, it wraps the content of the container in a
div and adds eight
spans to the container. Again, starting with the basic box.html file in the basic markup section above, the additional markup can be added as shown below.
Currently the plug-in takes no arguments and does not apply any styling to the page. This is intentional because the styling should be controlled using CSS, similar to most jQuery UI widgets.
The Actual Plug-In
Below is the actual plug-in that adds the markup to the page. As noted above, the plug-in wraps the contents of the target element in a
.ui-corners-content and then appends all of the side and corner
Special care is taken to avoid adding the extra markup to elements that have already been processed. The
.ui-corners class is detected and those elements are skipped. Also, repeated strings are saved in variables to aid in compressing. The script is only 402 characters after running it through YUI Compressor and 267 bytes after gzipping.
Seeing the Plug-in In Action
Below you can see a
.box that has had the corners applied. The base.css file shown above is used for the styling. Background colors have been applied to the side and corner
spans to make them visible. It's also clear that the corners and sides are not overlapping the content.
For more advanced styling it is recommended to use the SASS
@mixin described below.
Using the UI Corners
Although the base.css above will correctly position all of the corners and sides, more calculations are needed for actually using background images. For these purposes the SASS/Compass plug-in provides two
@mixins greatly simplify the creation of CSS for mimicking rounded corners as well as more advanced examples.
Using @mixin corner-images
@mixin takes eight arguments, one for each corner and side. The arguments are ordered clockwise starting with the top-left corner and ending with the left side. Each argument is a Sass list of values. The corners and sides require slightly different values in the list — corners have a height and width while sides only have one dimension that can be specified — however
background-image value is always the first value. All other values in the list can be left at default. Each argument is optional and may be skipped by passing in "auto" or using Sass keyword arguments.
@include corner-images( [$tl], [$t], [$tr], [$r], [$br], [$b], [$bl], [$l] )
$tl— [background-image] [width] [height] [background-position-x] [background-position-y] [left] [top]
$t— [background-image] [height] [background-position-x] [background-position-y] [top] [left] [right]
$tr— [background-image] [width] [height] [background-position-x] [background-position-y] [right] [top]
$r— [background-image] [width] [background-position-x] [background-position-y] [right] [top] [bottom]
$br— [background-image] [width] [height] [background-position-x] [background-position-y] [right] [bottom]
$b— [background-image] [height] [background-position-x] [background-position-y] [bottom] [left] [right]
$bl— [background-image] [width] [height] [background-position-x] [background-position-y] [left] [bottom]
$l— [background-image] [width] [background-position-x] [background-position-y] [left] [top] [bottom]
Using @mixin corner-border-width
@mixin mimics a real border on the
box using padding. As discussed above, setting a transparent border will cause some positioning issues because borders appears outside of the element. This issues is overcome by simply using
padding. For convenience, if real padding is needed it can be passed as the second argument which will be added to the first argument. Both arguments are Sass lists taking the same values as the CSS padding property. Similar to
padding, each argument can take a list of one, two, three or four values.
@include corner-border-width( $border-width, [$padding] )
$border-width— [top-width] [right-width] [bottom-width] [left-width]
$padding— [top-width] [right-width] [bottom-width] [left-width]
The Actual @mixin
Below is the entire
@mixin that is used for generating the CSS. There are the two mixins mentioned above as well as a number of helper functions for manipulation all of the list values. Close inspection reveals that there are a number over default variables available. Passing "default" instead of "auto" for any value will favor the default value if it exists. Auto values are based on the other passed values while default values rely blindly on the default variables. The default variables can be easily overridden. At the bottom of the file, the default styles are visible. Simply including this plugin will generate the default styles as shown in the base.css above.
In order to mimic
border-radius we'll need some images. For this example we'll use eight separate images as described in the previous article. Using the
@mixin this becomes very simple as shown below.
Above is a
.box where an image is used for each corner and side. The
.box also has a
border-width of 1px and
padding of 10px. Below is the CSS code that is generated. Only the code that is distinct form the basic CSS is generated. The "?1313871969" after the images is a cache busting artifact caused by the
@mixins reliance on Compass's
@function and can be turned off by setting the global
$ui-corners-cache-buster variable to false.
One important thing to note is that the images are all measured using the Compass image dimension functions. Because all of list values other than
[background-image] were left to auto, the corners were given a height and width equal to their respective background images. Similarly the sides were given values based on the images. For instance, the left side was given a
top equal to the hight of the top-left image, a
bottom equal to the hight of the bottom-left image and a width equal to the width of the top-left image. By default the sides are given the respective width or height of the nearest corner instead of the height width of their own image. This will help in cases where background colors also need to be applied to the sides to match the desired design.
Basic Sprite Example
Of course include eight separate images just to mimic
border-radius isn't most appealing thing in the world — sprites are a good thing. Currently the sprite needs to be produced beforehand and cannot be generated with Compass's excellent sprite functions. The sprite image from the previous article will be used.
In the example above the same image is passed in for each of the corners and sides. For each corner a
background-position-y are supplied. For the sides the respective
width is specified as "auto" and then
background-position-y are supplied. The above example will generate the CSS below.
Similar to the basic example above, only the rules that differ from the basic.css are generated. When values are identical for groups of selectors they are appropriately grouped. For instance, the
background-image is only specified once. Also of note, the
background-position is no longer the default and is now in the CSS.
Using a sprite in this example will significantly reduce the file sizes. The eight images together total 4.4KB while the sprite image is only 1.1KB. Of course this also removes eight HTTP requests which is always a good thing — ask Google.
Fancy Sprite Example
Of course, this technique is not limited by simply mimicking
border-radius. It's also possible to apply fancier borders that CSS could never do. For instance, imagine creating a box where nearly every Photoshop Layer Style has been applied — Drop Shadow, Inner Shadow, Outer Glow, Inner Glow, Bevel and Emboss and Color Overlay. It would be hard to mimick that without using images.
Above is an image created using the list of Photoshop filters listed above. It has been turned into two different sprites. It's not possible to use a single sprite for all eight corners and sides when the sides are more that 1px solid colors. The top and bottom sides are repeated above and below the corners in the sprite.
In the above Sass, the positioning values are also used to move the shadow and glow outside the original box. For instance, the top-left corner is the first argument in
corner-images and the first item in the list is the fancy sprite. The top left corner image is 18px by 18px and starts at 0px by 18px within the sprite. The first 8px of the corner are actually shadow as are the top 4px. The similar measurements are done for all of the corners and sides. The right and left sides get their own sprite because they didn't fit in the other one. This generates the CSS below.
Below you can see the fancy sprite corners in a live example. It is important to note that the CSS will scale with the box as its content grows. The generated CSS would apply to any size box without any alterations.