Images and CSS3 Gradients

Webkit has a range of css3 properties for working with alpha transparent masks (see webkit.org). Firefox doesn’t support these yet, but if you want to implement a basic linear or radial (non-alpha) gradient mask, you can fake it by blending a gradient with the background colour of your page.

Set your image as the background image of a div, and give the div a width and a height.

div {
    height: 384px;
    width: 283px;
    background: url('lich-king.jpg') no-repeat;
}

Duplicate the background property:

div {
    height: 384px;
    width: 283px;
    background: url('lich-king.jpg') no-repeat;
    background: url('lich-king.jpg') no-repeat;
}

Now paste a linear gradient property before the image value, with a comma after it (the order is important):

div {
    height: 384px;
    width: 283px;
    background: -webkit-gradient(linear,left bottom,left top,color-stop(0, rgba(27,223,72,1)),color-stop(1, rgba(27,223,72,0))), url('lich-king.jpg') no-repeat;
    background: -moz-linear-gradient(center bottom,rgba(27,223,72,1) 0%,rgba(27,223,72,0) 100%), url('lich-king.jpg') no-repeat;
}

See the green colour fading in? That’s the linear gradient we’ve created (inspect the element!). Pretty ugly, huh? Lets look at some practical examples:

Linear gradient blend (right to left)

div {
    height: 384px;
    width: 283px;
    background: -webkit-gradient(linear,right top,left top,color-stop(0, rgba(255,255,255,1)),color-stop(1, rgba(255,255,255,0))), url('lich-king.jpg') no-repeat;
    background: -moz-linear-gradient(right center,rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%), url('lich-king.jpg') no-repeat
}

Radial gradient blend

div {
    height: 384px;
    width: 283px;
    background: -webkit-gradient(radial, 50% 50%, 120, 50% 50%, 140, from(rgba(255,255,255,0)), to(rgba(255,255,255,1))), url('http://nicolahibbert.com/wp-content/uploads/2010/09/lich-king.jpg') no-repeat
    background: -moz-radial-gradient(50% 50% 0deg, circle closest-side, rgba(255,255,255,0), rgba(255,255,255,0) 85%, rgba(255,255,255,1)), url('http://nicolahibbert.com/wp-content/uploads/2010/09/lich-king.jpg') no-repeat;
}

HTML5 Forms Support Detection with JavaScript

In this tutorial, we’ll continue developing our HTML5 form.  In the last tutorial, we styled the form with some of the new CSS3 properties; this time we’re going to detect support for the new HTML5 input types.

We need to detect whether the browser supports the new HTML5 input types natively – if not, we can use this knowledge to fall back to JavaScript where appropriate.  It is important to note that there currently isn’t a lot of browser support for these input types: Opera 10.6 has the most complete support, Safari 5 and Chrome 5 have partial support, Firefox 3.6.8 and IE8 (as usual) are dead in the water.  If you’re dabbling with HTML5 at all, you’ll know that support is patchy at best, and you should treat tutorials like this one as instructional rather than definitive.  So whilst this information is current now, the specification is still undergoing revision (and will be for a while yet!).

Support Detection

If a browser doesn’t support a particular attribute value, it will degrade to the default value text.  In theory, this is how we test for browser support – we create a new input element in JavaScript (in memory only, don’t append it to the DOM), assign to it the attribute value we want to detect support for, and then query the element’s type attribute.  If the attribute retains the value that you assigned to it, the browser supports it.  Otherwise, if it returns the default value, the browser doesn’t support it. Using this method, we can loop through all the new input types and log the results to console to see what we get back:

var inputs = ['search', 'tel', 'url', 'email', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'color', 'range'],
    len = inputs.length;

for (var i = 0; i < len; i++) {
    var input = document.createElement('input');
    input.setAttribute('type', inputs[i]);
    console.log(inputs[i] + ' -> ' + input.type);
}

Notice that I began the above statement with the qualifier “in theory.”  Whilst this method should technically work, some browsers are prone to giving false positives, i.e. asserting that they support a particular input type, when in reality, they don’t.  Take a look at the output produced for the basic test above:

Only Firefox, IE8 and Opera are telling the truth! If you visually check in Safari and Chrome whether the UI improvements or validation functionality is present (using Mike Taylor’s HTML5 Input form, for example), it’s pretty evident that Safari and Chrome are not being entirely honest with us.  So how do we test for support if two of the major browsers are telling porkies?  Well, you have to go a step further and test for both UI improvements and validation functionality manually.

However, support for data validation is currently pretty flakey – that’s interpreting it generously.  It appears that some browsers validate the data properly, whilst others don’t check the data but pass it as valid anyway (see this discussion for details).  This probably won’t be resolved for a long time, so we’re going to validate data with JavaScript – experiments are one thing, but the state of implementation is so fragmented at this point that it’s not actually of any use to us.  So in the next part of this tutorial, validation will be handled by JavaScript exclusively.  What about the UI improvements?  If the UI improvements are supported, we’ll use those, otherwise we’ll provide the functionality ourselves.  Sounds like a plan!

We need to test for these UI improvements to see if they’re present in the browser.  UI improvements can be best described as things that we’d usually have to implement a widget for in Calendar in OperaJavaScript –  like a date picker or calendar – except now they’re implemented by the browser. We test for them by passing a text value to our mock-input, and then checking what gets returned – any new UI functionality shouldn’t be able to hold a text value.  If a string is returned, there’s no UI improvement; if anything else is returned (e.g. a numerical value in the case of range, or an empty value in most other cases) then a UI improvement is present.  One caveat: there are no detectable improvements or validation where the telephone and search inputs are concerned as yet, so we have to eliminate these from the first part of the test.

var inputs = ['search', 'tel', 'url', 'email', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'color', 'range'],
len = inputs.length,
uiSupport = [];

for (var i = 0; i < len; i++) {
    var input = document.createElement('input');
    input.setAttribute('type', inputs[i]);
    var notText = input.type !== 'text';

    if (notText && input.type !== 'search' && input.type !== 'tel') {
        input.value = 'testing';
        if (input.value !== 'testing') {
            console.log(input.type);
        }
    }
}

In the FF and IE consoles, we don’t get any data returned, as expected.  Safari and Chrome return a non-string value for range only.  Opera returns a non-string value for datetime, date, month, week, time, datetime-local, number and range.  The last thing we have to do is store this information in an array so that we can refer to it later, then wrap the whole code block in a function so that the variables have some semblance of a namespace.

(function() {
    var inputs = ['search', 'tel', 'url', 'email', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'color', 'range'],
    len = inputs.length,
    uiSupport = [];

    for (var i = 0; i < len; i++) {
        var input = document.createElement('input');
        input.setAttribute('type', inputs[i]);
        var notText = input.type !== 'text';

        if (notText && input.type !== 'search' && input.type !== 'tel') {
            input.value = 'testing';
            if (input.value !== 'testing') {
                uiSupport.push(input.type);
                // console.log(uiSupport);
            }
        }
    }
})();

uiSupport will store the names of the input type attributes that are supported, so we can use them when we come to create our fallbacks in the next tutorial.

Quick Tip: If you need to detect support for some of the other input attributes – autofocus, for example, or any of the other new HTML5 elements, it’s definately worth checking out Modernizr.  Modernizr has the added benefit of having IE support baked in, so there’s no need to include an additional HTML5 shim to get the new (header, section, etc.) tags working.

Form Styling with CSS3

Today we’re going to continue developing our HTML5 contact form, using CSS.

Extract the inline CSS from the head of the form and paste it into a new document.  Name this new stylesheet style.css and make a reference to it in the head of the form HTML.  At the top of style.css, I’ve imported Eric Meyer’s stylesheet reset (named “reset.css”) and removed the star (*) selector that temporarily reset the margin and padding.  (NB: never use the start selector in production!)  Your stylesheet should now look like this:

@charset "utf-8";
@import "reset.css";

form { width: 460px; padding: 20px; color: #333333; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0 auto }
fieldset { border: 0; padding: 10px; margin-bottom: 10px; background: #F6F6F6 }
legend { padding: 5px 10px }
label, span { float: left; clear: left; display: block; width: 180px; padding-right: 20px; text-align: right }
input, textarea, select { float: left; width: 200px }
.submit { width: 100px; float: right; margin-right: 33px }
select { width: 206px }
p { overflow: hidden; margin-bottom: 10px }
.contact-method label { clear: none; width: auto }
.contact-method input { float: none; width: 20px }

We’re just going to make a quick couple of changes before we start.

Firstly, add ‘height: 26px; line-height: 26px’ to the ‘label, span’ selector to vertically centre the text within the input labels.

Secondly, there’s a bit of a bug in IE concerning legends and fieldsets – IE bleeds the background colour of the fieldset behind the legend.  To recify this, we’re going to add a little bit more margin to the bottom of the fieldset, and position the legend absolutely within it.

Unfortunately, positioning the legends absolutely within a fieldset that has padding triggers a bug in Firefox.  This bug causes Firefox to calculate the legend’s position incorrectly, so to fix this, we’re going to remove the padding from the top of the fieldset, and add that padding to the first paragraph in the fieldset instead.  Add “padding-top: 0” to the fieldset attribute, and “p:first-of-type { padding-top: 25px }” underneath the fieldset style.  I rarely see :first-of-type used out in the wild, as :first-child can often be used in a similar context.  However, :first-of-type selects the element that is the first sibling of that type within it’s parent, rather than just selecting the first child.  Very useful!

Now that we’ve fixed that, we’ve broken IE again by removing the padding. Add the hack “padding-top: 25px9” to the fieldset selector and we should be good to go.

With these changes, your css should look like this:

form { width: 460px; padding: 20px; color: #333333; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0 auto }
fieldset { position: relative; padding: 10px; padding-top: 0; padding-top: 25px9; margin-bottom: 30px; background: #F6F6F6 }
legend { padding: 6px 12px; position: absolute; left: 10px; top: -11px }
label, span { float: left; clear: left; display: block; width: 180px; padding-right: 20px; text-align: right; height: 26px; line-height: 26px }
input, textarea, select { float: left; width: 200px }
.submit { width: 100px; float: right; margin-right: 33px }
select { width: 206px }
p { overflow: hidden; margin-bottom: 10px }
p:first-of-type { padding-top: 25px }
.contact-method label { clear: none; width: auto }
.contact-method input { float: none; width: 20px }

Now that we have a base template for our form, we can start adding styles.

Border Radius

We’re going to add a rounded corner to the fieldsets, legends and submit button, add background and text colours to the legends, and change the style of the submit button. At the moment, we still have to use vendor prefixes for border-radius, so this style declaration usually consists of three lines:

#style {
    -webkit-border-radius: <value>;
    -moz-border-radius: <value>;
    border-radius: <value>;
}

Edit your css using the styles highlighted below.

form { width: 460px; padding: 20px; color: #333333; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0 auto }
fieldset { position: relative; padding: 10px; padding-top: 0; padding-top: 25px9; margin-bottom: 30px; background: #F6F6F6; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px }
legend { padding: 5px 10px; background-color: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px }
label, span { float: left; clear: left; display: block; width: 180px; padding-right: 20px; text-align: right; height: 26px; line-height: 26px }
input, textarea, select { float: left; width: 200px }
.submit { width: 100px; float: right; margin-right: 38px; border: 0; padding: 5px 10px; background: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px }
select { width: 202px }
p { overflow: hidden; margin-bottom: 10px }
p:first-of-type { padding-top: 25px }
.contact-method label { clear: none; width: auto }
.contact-method input { float: none; width: 20px }

The last fieldset on the form should look like this:

I think it’s easy to go overboard with border-radius, so we’ll leave that bit there for now.  Next up, background gradients.

Background Gradients

We have to use vendor prefixes to enable this to work in Firefox and Webkit.  Unlike border radius, each vendor uses a different syntax for their style declaration, so it can be a little confusing.  There are more properties associated with setting a background gradient style, but I’ve listed the most common ones below.

Firefox has linear gradient and radial gradient values…

#style {
    background-image: -moz-linear-gradient( [<point> || <angle>,]? <stop>, <stop> [, <stop>]* );
    background-image: -moz-radial-gradient( [<position> || <angle>,]? [<shape> || <size>,]? <stop>, <stop>[, <stop>]* )
}

…whereas Webkit allows you to specify whether the gradient is a linear or radial one within “-webkit-gradient”:

#style {
    background-image: -webkit-gradient(<type>, <point> [, <radius>]?, <point> [, <radius>]? [, <stop>]*)
}

Note that you can also specify these gradients within the “background” shorthand property.

For our form, we’ll be using a grey border on the input boxes, and a slight gradient on the fieldsets.

form { width: 460px; padding: 20px; color: #333333; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0 auto }
fieldset { position: relative; padding: 10px; padding-top: 0; padding-top: 25px9; margin-bottom: 30px; background: #F6F6F6; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; background: -webkit-gradient(linear, left top, left bottom, from(#EEEEEE), to(#FFFFFF)); background: -moz-linear-gradient(center top, #EFEFEF, #FFFFFF 100%) }
legend { padding: 6px 12px; position: absolute; left: 10px; top: -36px; background-color: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px }
label, span { float: left; clear: left; display: block; width: 180px; padding-right: 20px; text-align: right; height: 26px; line-height: 26px }
input, textarea, select { float: left; width: 200px; border: 1px solid #d9d9d9 }
.submit { width: 100px; float: right; margin-right: 37px; border: 0; padding: 5px 10px; background: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px }
textarea { background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), color-stop(1%, #EEEEEE), to(#FFFFFF)) }
select { width: 202px }
p { overflow: hidden; margin-bottom: 10px }
p:first-of-type { padding-top: 25px }
.contact-method label { clear: none; width: auto }
.contact-method input { float: none; width: 20px }

After these styles have been added, you should see something that looks like the image below.

Next we’re going to add a drop shadow to make the bottom corners of the fieldsets a little more prominent.

Box Shadow

Box shadow is similar to border-radius syntax-wise, you need to specify two vendor prefix properties and the official property as defined in the CSS3 spec.  Border-radius takes 3 lengths: the horizontal offset, the vertical offset, and the blur radius; and a colour.

#style {
    -webkit-box-shadow: <horz>, <vert>, <blur>, <color>;
    -moz-box-shadow: <horz>, <vert>, <blur>, <color>;
    box-shadow: <horz>, <vert>, <blur>, <color>;
}

We’re going to add a box shadow to the fieldsets, the legends, and the submit button, using the values highlighted below.  Notice that we’re also adding 4px margin to prevent the overflow: auto property on the parent paragraph tag from clipping the shadow on the bottom of the submit button.

form { width: 460px; padding: 20px; color: #333333; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0 auto }
fieldset { position: relative; padding: 10px; padding-top: 0; padding-top: 25px9; margin-bottom: 30px; background: #F6F6F6; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; background: -webkit-gradient(linear, left top, left bottom, from(#EFEFEF), to(#FFFFFF)); background: -moz-linear-gradient(center top, #EFEFEF, #FFFFFF 100%); box-shadow: 3px 3px 10px #ccc; -moz-box-shadow: 3px 3px 10px #ccc; -webkit-box-shadow: 3px 3px 10px #ccc }
legend { padding: 6px 12px; position: absolute; left: 10px; top: -11px; background-color: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; box-shadow: 2px 2px 4px #888; -moz-box-shadow: 2px 2px 4px #888; -webkit-box-shadow: 2px 2px 4px #888 }
label, span { float: left; clear: left; display: block; width: 180px; padding-right: 20px; text-align: right; height: 26px; line-height: 26px }
input, textarea, select { float: left; width: 200px; border: 1px solid #d9d9d9 }
.submit { width: 100px; float: right; margin-right: 37px; border: 0; padding: 5px 10px; background: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; box-shadow: 2px 2px 4px #888; -moz-box-shadow: 2px 2px 4px #888; -webkit-box-shadow: 2px 2px 4px #888; margin-bottom: 4px }
textarea { background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), color-stop(1%, #EEEEEE), to(#FFFFFF)) }
select { width: 202px }
p { overflow: hidden; margin-bottom: 10px }
p:first-of-type { padding-top: 25px }
.contact-method label { clear: none; width: auto }
.contact-method input { float: none; width: 20px }

Very Useful Tip: You can also use rgba values with box shadow, achieving an effect similar to the one I described in my CSS3 Alpha Transparent Gradients post.

Text Shadow

Next we’ll add a bit of text shadow on the legends and submit button to make them stand out a little more.  The text shadow property is pretty much the same as box shadow.  It takes 3 values and a colour, that is, the x value of the shadow, the y value of the shadow, the blur radius, and a colour.  However, this time we don’t have to use vendor prefixes, as native property is supported in all current browsers.

#style {
    text-shadow: <horz>, <vert>, <blur>, <color>;
}

Add the highlighted lines below to your css.

form { width: 460px; padding: 20px; color: #333333; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0 auto }
fieldset { position: relative; padding: 10px; padding-top: 0; padding-top: 25px9; margin-bottom: 30px; background: #F6F6F6; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; background: -webkit-gradient(linear, left top, left bottom, from(#EFEFEF), to(#FFFFFF)); background: -moz-linear-gradient(center top, #EFEFEF, #FFFFFF 100%); box-shadow: 3px 3px 10px #ccc; -moz-box-shadow: 3px 3px 10px #ccc; -webkit-box-shadow: 3px 3px 10px #ccc }
legend { padding: 6px 12px; position: absolute; left: 10px; top: -11px; background-color: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; box-shadow: 2px 2px 4px #888; -moz-box-shadow: 2px 2px 4px #888; -webkit-box-shadow: 2px 2px 4px #888; text-shadow: 1px 1px 1px #333 }
label, span { float: left; clear: left; display: block; width: 180px; padding-right: 20px; text-align: right; height: 26px; line-height: 26px }
input, textarea, select { float: left; width: 200px; border: 1px solid #d9d9d9 }
.submit { width: 100px; float: right; margin-right: 37px; border: 0; padding: 5px 10px; background: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; box-shadow: 2px 2px 4px #888; -moz-box-shadow: 2px 2px 4px #888; -webkit-box-shadow: 2px 2px 4px #888; margin-bottom: 4px; text-shadow: 1px 1px 1px #333 }
textarea { background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), color-stop(1%, #EEEEEE), to(#FFFFFF)) }
input:focus, textarea:focus, select:focus { background: white; border-color: #666 }
select { width: 202px }
p { overflow: hidden; margin-bottom: 10px }
p:first-of-type { padding-top: 25px }
.contact-method label { clear: none; width: auto }
.contact-method input { float: none; width: 20px }

Focus Highlighting

We’re going to add one last style, to make the input boxes more prominent when they’re in focus…

nput:focus, textarea:focus, select:focus { border-color: #666 }

…and one last tweak, to make the input boxes a little bigger.

input, textarea, select { padding: 3px; float: left; width: 200px; border: 1px solid #d9d9d9 }
select { width: 208px }

The final CSS should look like this:

form { width: 460px; padding: 20px; color: #333333; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0 auto }
fieldset { position: relative; padding: 10px; padding-top: 0; margin-bottom: 30px; background: #F6F6F6; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; background: -webkit-gradient(linear, left top, left bottom, from(#EFEFEF), to(#FFFFFF)); background: -moz-linear-gradient(center top, #EFEFEF, #FFFFFF 100%); box-shadow: 3px 3px 10px #ccc; -moz-box-shadow: 3px 3px 10px #ccc; -webkit-box-shadow: 3px 3px 10px #ccc }
legend { padding: 6px 12px; position: absolute; left: 10px; top: -11px; background-color: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; box-shadow: 2px 2px 4px #888; -moz-box-shadow: 2px 2px 4px #888; -webkit-box-shadow: 2px 2px 4px #888; text-shadow: 1px 1px 1px #333 }
label, span { float: left; clear: left; display: block; width: 180px; padding-right: 20px; text-align: right; height: 26px; line-height: 26px }
input, textarea, select { padding: 3px; float: left; width: 200px; border: 1px solid #d9d9d9 }
.submit { width: 100px; float: right; margin-right: 37px; border: 0; padding: 5px 10px; background: #4F709F; color: white; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; box-shadow: 2px 2px 4px #888; -moz-box-shadow: 2px 2px 4px #888; -webkit-box-shadow: 2px 2px 4px #888; margin-bottom: 4px; text-shadow: 1px 1px 1px #333 }
textarea { background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), color-stop(1%, #EEEEEE), to(#FFFFFF)) }
input:focus, textarea:focus, select:focus { background: white; border-color: #666 }
select { width: 208px }
p { overflow: hidden; margin-bottom: 10px }
p:first-of-type { padding-top: 25px }
.contact-method label { clear: none; width: auto }
.contact-method input { float: none; width: 20px }

View Demo

In the next tutorial, I’ll show you how to test for browser support of the new input types.