Boris Schäling

18 March 2009


SVG_roundies: Code-only rounded HTML boxes with SVG

SVG_roundies enables web developers to add rounded corners to HTML boxes in a minute. It is a lightweight Javascript-only library based on the W3C standard SVG and has been tested in Firefox, Opera, Safari and Chrome. SVG_roundies' counterpart for Internet Explorer is DD_roundies. When both libraries are used together rounded corners can be seen in all major browsers. If Javascript is disabled both libraries degrade gracefully as the usual HTML boxes with their sharp corners are displayed. Neither SVG_roundies nor DD_roundies require web developers to change their document structure, create graphics or use special CSS properties.

This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License.


Table of Contents


1. Introduction: DD_roundies' counterpart

SVG_roundies is a port of Drew Diller's excellent DD_roundies library. While DD_roundies uses Microsoft's VML to add rounded corners to boxes and thus only works in Internet Explorer this library is based on the W3C standard SVG.

SVG is supported by Firefox, Opera, Safari and Chrome but not by Internet Explorer. That said in practice you want to use DD_roundies and SVG_roundies together. As SVG_roundies is a port of DD_roundies both libraries provide the very same interface and produce the same results. Only their implementation differs. If you know DD_roundies you can use SVG_roundies in a minute.

Please note that Firefox and Safari support their own CSS properties -moz-border-radius and -webkit-border-radius to add rounded corners to boxes. For those browsers you don't really need to use SVG_roundies. As Opera doesn't support a CSS property for rounded corners yet SVG_roundies is required for this browser.

If you use DD_roundies and SVG_roundies together you can choose if you want to use the CSS properties or a SVG graphic in Firefox and Safari. DD_roundies can set the CSS properties -moz-border-radius and -webkit-border-radius for you.

SVG_roundies does not try to detect the browser and will always create a SVG graphic. If you want to use SVG_roundies only for some browsers you need to detect the browser yourself. As there are many scripts available on the Internet there is no need to invent another browser detection routine for this library.


2. Usage: Rounded corners in 1 minute

You use SVG_roundies the very same way you use DD_roundies:

<html>
  <head>
    <script type="text/javascript" src="SVG_roundies_0.0.2a.js"></script>
    <script type="text/javascript">
      window.addEventListener('load', function() { SVG_roundies.addRule('p', '10px 0px'); }, false); 
    </script>
  </head>
  <body>
    <p style="background-color: red; border: solid 3px blue; font-size: 3em">Hello, world!</p>
  </body>
</html>

In a SVG-capable browser you'll see a red box with a blue border and a rounded top-left and bottom-right corner. In other browsers the result is undefined. That's why you want to detect the browser and version before you call SVG_roundies.addRule()!

If you want to use DD_roundies and SVG_roundies together your code could look like this:

<html>
  <head>
    <script type="text/javascript" src="DD_roundies_0.0.2a.js"></script>
    <script type="text/javascript" src="SVG_roundies_0.0.2a.js"></script>
    <script type="text/javascript">
      function roundify(selector, radius) { 
        if (DD_roundies.IE6 || DD_roundies.IE7 || DD_roundies.IE8) { 
          DD_roundies.addRule(selector, radius); 
        } else { 
          SVG_roundies.addRule(selector, radius); 
        } 
      } 

      if (window.addEventListener) { 
        window.addEventListener('load', function() { roundify('p', '10px 0px'); }, false); 
      } else { 
        window.attachEvent('onload', function() { roundify('p', '10px 0px'); }); 
      } 
    </script>
  </head>
  <body>
    <p style="background-color: red; border: solid 3px blue; font-size: 3em">Hello, world!</p>
  </body>
</html>

Now you'll see a red box with a blue border and a rounded top-left and bottom-right corner in Internet Explorer and in all other browsers.

Please note that while SVG_roundies tries to be compatible with DD_roundies there are a few differences:

  1. SVG_roundies does not accept a third argument in addRule(). It does not try to detect the browser and does not add CSS properties like -moz-border-radius and -webkit-border-radius. It is a SVG-only library.

  2. SVG_roundies does not support changing an <a> element dynamically with a:focus and a:hover. First there is no standardized event to detect hovering. Second Firefox and Opera don't report CSS properties set with a:focus (Safari doesn't even raise an onfocus event).

  3. While you can pass any selector to DD_roundies.addRule() you must pass a tag name to SVG_roundies.addRule(). SVG_roundies uses document.getElementsByTagName() to find the box it should add rounded corners to. It would be nice if SVG_roundies could use document.querySelectorAll(). But this function is not yet available in many browsers. For example Firefox will support it in version 3.1 and Opera in version 10 for the first time. As SVG_roundies should work today and with today's browser versions in the future it uses document.getElementsByTagName() by default. Once document.querySelectorAll() can be assumed to be available in most browsers in the wild the implementation of SVG_roundies will be updated (but don't expect this to happen in 2009).

Not everybody will be happy to pass a tag name as a selector (in fact I want to pass something else most of the time). That's why it is possible to link another function into SVG_roundies. This could be a querySelectorAll() implementation provided by another Javascript library. Or it is a function implemented ourselves:

<html>
  <head>
    <script type="text/javascript" src="SVG_roundies_0.0.2a.js"></script>
    <script type="text/javascript">
      SVG_roundies.querySelectorAll = function(selector) { 
        var headings = document.evaluate('//*[contains(@class, "' + selector.substr(1) + '")]', 
                                         document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null) 
        var results = new Array(); 
        var oneheading; 
        while (oneheading = headings.iterateNext()) 
          results.push(oneheading); 
        return results; 
      } 

      window.addEventListener('load', function() { SVG_roundies.addRule('.roundify', '10px 0px'); }, false); 
    </script>
  </head>
  <body>
    <p class="roundify" style="background-color: red; border: solid 3px blue; font-size: 3em">Hello, world!</p>
  </body>
</html>

The box is now found through the class roundify and will have again a rounded top-left and bottom-right corner.

If you implement your own function I strongly recommend to design the interface similar to document.querySelectorAll(). This will make it easier to update your code in the future and use the browser supplied implementation of document.querySelectorAll() one day.


3. Support: Tested browsers and known issues

SVG_roundies tries to support Firefox, Opera, Safari, Chrome and any other SVG-capable browser on all platforms they are supported on. As IE does not support SVG you should use DD_roundies to add rounded corners for this browser.

The current version 0.0.2a has been tested with:

Table 1. Known issues
Known issues Browsers affected
If two boxes with rounded corners are nested and the outer box has a border Opera moves the inner box to the wrong position. The recommended work-around is obviously to make sure the outer box doesn't have a border. - As it seems like this is really a bug in Firefox, Safari and Chrome. I've filed those bugs to Bugzilla@Mozilla and WebKit Bugzilla. The current version of SVG_roundies behaves as if this is a bug in Opera though (as the presentation in Opera is wrong). Opera 9.x, Opera 10 alpha
If you change CSS properties of a box dynamically SVG_roundies is automatically notified and updates the SVG graphic accordingly. However this does not work in Safari (which doesn't fire the event DOMAttrModified) neither in Chrome (probably for the same reason). Safari 4 Public Beta, Chrome 1.0.154.48
If you change CSS properties dynamically and reset padding for example the change is reflected in the SVG graphic. However if you have another box with rounded corners on the same page the second SVG graphic is not updated. This is a problem when the size is changed (because of setting padding for example) and all elements on the page move. Other SVG graphics which are all absolutely positioned don't move though because they don't know they should. DD_roundies uses the onmove event which is only supported by Internet Explorer. Any ideas which event handlers could be used in Firefox/Opera/Safari/Chrome? all
Adding rounded corners to the elements <table>, <tr>, <td> and <option> is disabled as it doesn't work in any browser correctly (it's disabled in DD_roundies, too). all

4. MIT-License: SVG_roundies is free

Just like DD_roundies the library SVG_roundies is also free under the MIT license.

Copyright (c) 2008-2009 Drew Diller, Boris Schaeling

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

5. Download: Get SVG_roundies

You can download a compressed and uncompressed version of SVG_roundies. The current version is 0.0.2a (which is also the first version of SVG_roundies).

If you wonder about the strange version number: I use the same version number as Drew does for DD_roundies. If you use DD_roundies 0.0.2a you know that SVG_roundies 0.0.2a supports the very same interface and produces the very same results.


6. Internals: Changes to document structure and CSS properties

Let's suppose you want to add rounded corners to <p> which is formatted like this:

<p style="background-color: red; border: solid 3px blue; font-size: 3em">Hello, world!</p>

SVG_roundies dynamically creates a SVG graphic and adds it to the document structure. In order to create the right SVG graphic it will look for certain CSS properties. If the background color of <p> is red after all you want the box with rounded corners to be red, too.

It should be noted that you can set as many CSS properties as you like. You don't need to take SVG_roundies into consideration when you design your pages! SVG_roundies will work out of the box and does not require you to add, remove or reset CSS properties. Even if SVG_roundies can't find the CSS properties it looks for the library will work as expected. If you don't set a background color for example the rounded box will be transparent.

SVG_roundies will look for the following CSS properties:

SVG_roundies creates a SVG graphic depending on the values of those CSS properties. The library will insert a <svg> element before <p> and set various SVG attributes and CSS properties. Here is what's happening exactly:

  1. A new <svg> element is inserted before <p>. The CSS property position is set to 'absolute'. The z-index is the same as of <p>.

  2. The CSS property position of <p> is set to 'relative' if it is set to 'static'. This is required to move <p> to the front of <svg>. Otherwise the SVG graphic hides <p> and you can't read the text.

  3. The CSS property background-color of <p> is set to 'transparent'. The background-color is used to add and set the fill attribute in the first <path> element (there are two <path> elements in <svg>). (If a background-image is used or an <img> element should get rounded corners it is a bit more complicated as a few more SVG elements have to be inserted.)

  4. The CSS property border-color of <p> is set to 'transparent'. The border-color is used to add and set the fill attribute in the second <path> element (set to 'none' if no border-color has been set).

  5. The CSS properties left, top, right and bottom are set for <svg>. As the CSS property position has been set to 'absolute' before this moves <svg> to the very same position of <p>.

  6. The paths are calculated. The attribute d of the two <path> elements is set.

  7. The value of the CSS property opacity of <p> is set for the attribute opacity of <svg>.

As you've seen SVG_roundies sets and changes some CSS properties of <p>:

If you have Javascript code which needs to use the original values of those CSS properties you should run this code before you call SVG_roundies.addRule(). If you need to use the original values later in a dynamic web application you must save the values somewhere when your web application starts.

The new document structure looks like this (the question marks are automatically calculated values):

<svg xmlns="http://www.w3.org/2000/svg" style="position: absolute; z-index: auto; left: ?px; top: ?px; width: ?px; height: ?px" opacity="1">
  <path fill="red" d="?"/>
  <path fill="blue" d="?"/>
</svg>
<p style="background-color: transparent; border-color: transparent; border-width: 3px; position: relative; font-size: 3em">Hello, world!</p>