Web developers need to know about browser usage in order to make rational choices for browser testing and for choosing which features to rely on. With jQuery's recent choice to phase out legacy IE support and the continued mainstreaming of mobile browsers, there's a renewed focus on which browsers it makes sense to support.
Since the beginning of the year, IE9 has been gaining ground while IE8 has been plummeting. This is probably due to a combination of Microsoft's much-touted update policy and the continued success of Windows 7. Meanwhile, Chrome has continued to see tremendous growth — nearly 7% in only a year. But, in the past 12 months, IE9 was the big winner with a 13% increase.
Mobile is the other major success story of the last year. Combining iPad with the mobile statistics, mobile has grown more than 3% in the last year.
Read about the data below.
As usual, Firefox, Safari and Opera have remained fairly static. Overall, Firefox has dropped a few percentage points, likely due to users switching to Chrome. Firefox has been hovering around 20% for several years and has bounced back from slumps before. Safari's market share seems to be linked to the market share of OSX, which also tends not to fluctuate much.
The only two browser segments that are seeing overall positive trending are Chrome and Mobile. While Chrome continues to steal from Firefox, the bigger victim appears to be IE. It's reasonable to assume that mobile is diluting the market share for all browsers equally.
While IE overall has seen a large decline, IE9 has seen huge gains. In fact, all of the “modern” browsers have seen dramatic increases, while all of the “legacy” browsers have started to decline. IE6, IE7 and IE8 have all seen their market share drop by half in the last year.
Legacy Firefox has plummeted nearly 7%. The modern versions of Firefox have made modest gains, although not enough to offset the decline of the legacy versions, causing an overall decline (as shown above).
This is all great news for web developers. Not only are modern browsers releasing great new technologies, users are actually adopting these browsers en masse.
The pie-chart above shows the combined mobile and desktop browser market share for June 2012. The top browsers in the US are Chrome, IE9, Firefox, and IE8. When combined together, mobile has a greater market share than Safari, Opera, legacy Firefox and IE6/7 combined. We're now in an interesting period where it makes more sense to focus developer resources on mobile platforms rather than legacy versions of IE.
The good news is that IE6 is nearly invisible in the United States now. Above, you can see it has around 0.3% market share. Even the NetMarketshare data — NetMarketshare is typically more generous to IE than StatCounter Global Stats — that's used on IE6 Countdown shows IE6 at 0.6% in the US. By this time next year IE will likely be an afterthought in every market except China. Even in China, IE6 has been taking a severe nosedive in the past 12 months.
IE7's downward trend has been fairly constant and predictable, losing an average of 0.33% per month — more than half of its market share in the last 12 months. By this time next year IE7 will undoubtedly be well below the 1% mark.
Mobile devices have made great strides in the past few years. The mobile browsers are just as capable as any of the other modern browsers. And the devices feel just as snappy as a regular (budget) computer. The days of crappy, Java-based browsers and cumbersome experiences are long gone and users have taken notice. It's fast becoming a requirement to consider mobile devices as first-class citizens. In the debate over responsive design vs. m-dot websites, usage trends are pointing towards responsive design.
The chart above shows how all of the A-grade browsers measure up. With the inclusion of IE7, the A-grade browsers above cover nearly 95% of browsers. A-grade browsers are the browsers you're willing to promise the client their site will be tested in. For my money, the most obvious choices are browsers that real people are using. It's sensible to ignore browsers with a negligible market share (less than 1%) and once a browser drops below 5%, it should be marked for removal. It's usually really obvious where to draw the line.
With the exception of IE7/8 (and Safari), every other browser in the list is trending upwards. While Safari is staying relatively stagnant, IE8 has been declining rapidly. Since December, it has lost an average of 2% market share every month. It seems to have leveled off temporarily but it would be reasonable to expect IE8 to drop to around 5% in the next 12 months.
A safe rule is that any browser under 2% is on its way out, and any browser under 1% is too small to focus on. The amount of developer effort doesn't match the size of the audience. And, usually the less popular browsers have enough in common with the A-grade browsers that users will still be able to use the site (although that's not the case for IE6). For instance, even though you shouldn't really ever bother testing in Opera in the US, Opera is highly likely to render your site perfectly. Similarly, legacy versions of Firefox typically look just fine even if you only test in the latest Firefox. In essence, these B-grade platforms can be safely ignored without having any major impact on the overall site experience.
Here's what I've been giving my clients:
Yahoo! keeps an updated Graded Browser Support document that lists the browsers that YUI supports. It's a good starting point for any solid testing strategy. jQuery Mobile also keeps a GBS, but I would advise against relying on this mobile-oriented GBS unless you're on a mobile-only project.
Now is the time to start dropping IE7. It's below 2% nationally and it's continuing to fall. Meanwhile, iPhone, iPad and Android have gained enough market share to start trending as top-shelf browsers.
Already, these mobile browsers are outperforming desktop browsers like Opera, and IE6/7. This is significant because the other mobile browsers, like Blackberry at 0.33%, have abysmal market share in the US. Even Opera Mobile, which has a good worldwide market share, has only a 0.16% market share in the US when compared against all browsers.
However, iPad, iPhone and Android are fully capable browsers on fully capable devices. Now is the time to start testing each of these platforms as A-Grade browsers.
As I mentioned in the intro, jQuery has announced that the next major release of jQuery will come in two flavors. One flavor will drop legacy IE altogether. For most people this is a non-starter, and jQuery knows better than to leave regular folks out in the cold. The other flavor supports IE6/7/8. However, the jQuery team is likely reading the same tea-leaves that I am and can see that before next major version of jQuery releases (early 2013), IE7 will like be passing the 1% threshold and IE8 will have a greatly diminished market share.
Between now and 2013 there's some major things happening. First, there'll be a presidential election. Second, Windows 8 will be shipping. And Windows 8 will ship with IE 10 pre-installed. Windows 7 users are also likely to get IE 10 as an automatic update. This will make 2013 another exciting transition year for IE, where users of legacy XP and Vista computers upgrade to the new OS, and IE10.
The jQuery team is claiming that the first pure “legacy free” version of jQuery won't really arrive until 2014. By that time, assuming Microsoft's marketing team can sell enough $40 copies of Windows 8, IE8 should be approaching the magic 1% threshold, and sailing out of our collective memories forever.
The above data shows a few exciting things. First, the majority of the A-grade browsers are modern browsers with good HTML5/CSS3 support. Second, the only browsers showing an overall decline are the legacy browsers. The only legacy browsers on the list are IE8, which is in rapid decline, and IE7, which is nearly dead.
Second, mobile browsers have arrived and are no longer easy to ignore. While the national data shows mobile at nearly 13%, I've seen the Google Analytics for clients with mobile usage approaching 30%!
The above stats are culled from the StatCounter Global Stats. Their data isn't perfect (read the FAQ), but it's a good starting point for understanding the current browser landscape. Ultimately, your own Google Analytics data should be used for analyzing the right browsers to test for your site.
All of the stats above are for the United States region. Many articles using StatCounter stats use the worldwide stats but those can be very misleading. For instance, Europe is very different from Africa, which is very different from Asia, and so on.
The whole point of browser stats is to gain insight into what browsers your site's visitors are using. To that end, most site's visitors overwhelmingly come from a specific region. Mixing all of them together as “worldwide” isn't actually useful for any specific project. Even if you have a global site, you usually have a dedicated team for each region.
I live in the US and the majority of the sites I work on are for a US audience. If you live somewhere else, you can easily pull the data from StatCounter for your country.
I've combined the desktop and the mobile data into one chart. The resulting spreadsheet has been published so that you can see the raw data. StatCounter keeps mobile and desktop browsers separate, so combining them means multiplying the percentages by the Mobile vs. Desktop ratios. Because of this, the values shown above will differ slightly from the Partially Combined Browser Versions on the StatCounter site.
All versions of Chrome are combined into a single stat because that browser auto-updates, and the differences between versions are negligible. The same is true of Firefox 4+, Safari and Opera. All of these browsers have aggressive updating policies and the differences between the versions is largely irrelevant because the majority of users upgrade their browsers quickly.
The different versions of IE are listed separately because of the significant differences between each release. IE also lacks a comprehensive updating policy, which means older browsers keep their market share for much longer.
Older versions of Firefox (below version 4) are also lumped together. Although those versions lacked an aggressive auto update feature, legacy Firefox versions are too numerous to call out separately. It's more interesting to track Firefox's market share before and after Firefox 4. That's when Firefox started rapidly releasing new versions, similar to Chrome.
To the best of my knowledge these devices are extremely similar and, as far as the browser is concerned, they are identical devices. For the purposes of browser market share, these two devices should be combined.
]]>If you don't like the srcset
solution being proposed, or you simply need something that works in today's browsers and doesn't use JavaScript, read below for a potential solution to the responsive image problem.
This article covers a method for using CSS to handle responsive images instead of using JavaScript. There are two use-cases discussed in-depth below. Part 1 covers how to create an responsive image using only CSS. Part 2 covers how to create a proportionally scaled responsive images using only CSS.
Example: See a live example of a CSS-only responsive image.
Example: See a live example of a CSS-only responsive image that scales proportionally.
Here's a quick preview of the technique. This method for responsive images uses CSS in a <style>
tag to make a <span>
behave like an image. This method has broad browser support, is accessible and does not require JavaScript.
First is some generic CSS that belongs in a global stylesheet and can be used for all responsive images. The CSS is for making the <span>
behave more like a native <img>
. The key to this is display: inline-block
and background-size: 100%
.
|
NOTE: please see below for an explanation of this CSS.
Second is the base HTML for all responsive images. Essentially the HTML below will replace each <img>
tag that needs to respond to media queries. Of course the media queries themselves should be placed inside the <style>
tag.
|
NOTE: The media queries are explained in step 4 below.
There are many good articles about handling responsive images and they all have the same goal of supporting the types of media query wizardry that can be accomplished in CSS for swapping out images. While most of those solutions utilize JavaScript to swap the src
of an <img>
, I'm proposing that using CSS background images is probably good enough and avoids most of the pitfalls of the other techniques. It's fairly easy to make a <span>
with a background image behave exactly like a real <img>
with some basic CSS, primarily relying on display: inline-block
and background-size: 100%
.
Of course, moving every in-page image on your site to your global stylesheets is impractical and ill-advised. The images we're talking about are part of the content and have no place in your site's global styles; they're very likely to be managed by a CMS. The solution is to embed the styles in the HTML by placing a <style>
tag in the <body>
instead of in the global stylesheet.
A recent draft of HTML5 spec would allow for <style>
tags in the <body>
as long as they carry a scoped
attribute (although this currently won't validate). Using a <style>
tag this way is only marginally different than the usage of <source>
as proposed in the picturefill method and it's certainly less confusing than the srcset proposal. Using CSS carries with it the benefit of a familiar, flexible syntax and broad browser support. This also avoids relying on an emerging and contentious standard.
To start, we need a regular <img>
that we wish was responsive. The example below is just that: a boring old image that doesn't do anything special.
|
Example: See a live example even though this isn't responsive and is just a regular image.
In order to fake an image using CSS, we need to start with a <span>
. Because a <span>
is inline by default, display: inline-block
is supported on that element in IE6 and IE7. There's long been a usable hasLayout hack that makes IE6 and IE7 apply inline-block to any element but — to me at least — a <span>
is already closer to the image layout we want before any styles have been applied. For now we'll place the alt text inside the <span>
but we'll fix that in the next step.
Below you can see a simple <span>
with an .image
class ready to be styled.
|
Example: See a live example although there's not much to see yet.
NOTE: You could use a fictional tag like <picture>
instead of <span>
but that would require a shim for IE6, IE7 and IE8. You could also use <div>
instead of <span>
without any adverse effects.
ARIA supplies a role="img"
for a container that visually represents an image. Applying this will help ARIA-enabled browsers understand what this <span>
is used for. ARIA also specifies that the role="img"
is not labeled by its contents so we need to move the alt text to the aria-label
attribute.
Below you can see the <span>
with an ARIA role
applied and the alt text moved to an ARIA label.
|
Example: See a live example although there's still not much to see.
Now that we've got the mark-up for an accessible image, we need to make it behave like an image using some CSS. This CSS is generic to all .image
elements and can be placed in your global stylesheets.
First we'll use Compass to generate our generic styles. Although Compass isn't available in a browser, it's a good start for generating the default styles needed for our new .image
elements. Compass already has some mixins for cross-browser inline-block, squishing text and vendor-prefixed background-size. This helps immensely because they've already done the research for us to make this work seamlessly across all browsers. (If you're unfamiliar, learn how to get started with Sass and Compass.)
display: inline-block
makes the <span>
behave like an <img>
. Inline-block allows the element to follow the document flow like an inline element but it can also have dimension (height and width) like a block element. Here's an article about inline-block.background-size: 100%
forces the background image to stretch to the full size of the element so that it behaves like a real image. Read about background-size
on MDN.background-position: 50% 50%
helps out browsers that don't support background-size
by keeping the image centered in the .image
.background-repeat: no-repeat
keeps the background from repeating.Below is the example SCSS code that was used to generate the generic CSS needed for this technique. Again, generating styles with Compass is an unnecessary step in this case but it was included as a shortcut explanation for all of the crazy styles required for cross-browser support.
|
Once compiled, the SCSS code above looks like the following CSS. The generic styles for .image
can be shared for all responsive images on the page. That helps make the specific styles for the individual responsive images more compact and readable. The code below is ready to use and should be placed in your site's global stylesheets.
|
Example: See a live example although there's yet again not much to see.
NOTE: IE6, IE7 and IE8 (and some other legacy browsers) don't support background-size
. This only affects those browser's ability to scale the image. It will work just fine for a fixed-dimension image that is shown at its default size. See background-size support on When Can I Use. It's important to note that support for media queries closely matches support for background-size
. Basically it's safe to assume that a browser that doesn't support media queries also doesn't support background-size and vice versa. So your fall-back styles in the next step should take that limitation into account.
Now it's time to apply the background image to our <span>
. We don't want to place these styles in the global stylesheet because our image is specific to the content of our page. The rule of thumb is that if you'd normally use an <img>
you probably want it to be in-page using this technique. For each image we'll be placing a <style>
element just above it. Each image also needs a unique ID to make sure our styles are applied to the correct image. Although placing a <style>
element in the body is not technically allowed in HTML, it actually works just fine in all browsers.
For each media query we need to provide a background image and a height and width. The result is very similar to the mark-up for the Picturefill solution proposed for the <picture>
tag. Although using a <style>
tag is decidedly more verbose — CSS is not known for brevity — it doesn't require the weird duplicative HTML comments <picture>
requires for full browser support. Plus, a pure CSS method doesn't require any JavaScript whatsoever.
The example below shows the <style>
element just above our <span>
with all of the styles for our responsive image. In this example the fall-back is the desktop version but otherwise a mobile first approach is used in the media queries. The media queries below are simple examples and your project will undoubtedly require different break points.
background-image
as well as the height
and width
because the browser can't guess the size of a background image the way it can with regular <img>
elements.display: inline-block
and background-size: 100%
are handled by the global CSS styles described in step 3 above.<span>
must be given a unique ID to ensure that styles are applied correctly.
|
Example: See a live example; now we're in business.
NOTE: Although the vast majority of examples on the web recommend adding the smallest, mobile optimized image as the fall-back, I prefer to supply the default desktop image to the browsers that don't support media queries. The only browsers that get the fall-back image are browsers that don't support media queries, and those browsers are almost exclusively IE6, IE7 and IE8. Supplying the mobile image as the fall-back is silly considering that there aren't any mobile browsers with any noticeable marketshare that require the fall-back. The mobile browsers we're targeting all support media queries.
In general, I'm of the opinion that browsers that don't natively support responsive design should simply be served the vanilla desktop experience, since that's almost certainly what those users were expecting anyway.
To make ourselves feel better about using invalid markup, let's add scoped
to our style tag to at least conform to the new HTML5 standard. This won't make our code validate any better than it would have otherwise and support for the scoped
attribute is currently non-existent.
The lack of support for scoped
means that we'll have to keep using the id
in our styles instead of being able to use more generic .image
to add the background image and dimensions. That's ok though; we're only adding in the scoped attribute to appear to be writing valid markup and to hopefully prepare for future compatibility and validity. WebKit already has experimental support and other browsers are sure to follow.
Scoped <style>
elements need a wrapper element to define, well…the scope. If we use an inline element like a <span>
as the wrapper it will automatically shrink-wrap our responsive image and not have any negative effect on our layout. (**UPDATE:** It appears that inline elements are not valid style scopes)
Below you can see the identical markup from step 4 with the addition of the scoped
attribute and a new .image-scope
element. The .image-scope
element doesn't require any additional styles because it is a <span>
and is display: inline
already.
|
Example: See a live example that looks exactly the same as step 4.
NOTE: Because of the lack of browser support for scoped
, this step is completely optional and only serves to make the mark-up slightly more compliant. Even then, using scoped
is pretty controversial — there's severe backwards compatibility issues — and you may wish to skip it altogether.
When working with responsive design, it's becoming increasingly popular to use fluid layouts to ensure that the content is fitting the screen properly. When using a normal <img>
it's easy to support scalable dimensions by defining only the width of the image with a percentage and letting the browser proportionally scale the image height. Although it requires some ingenuity, the responsive image techniques described above can also be combined with proportional scaling.
Below is an example of how this is handled with a normal image. The <img>
will scale proportionally, meaning the width will always match the width of its container. The height will always be the appropriate height. This way the image will never appear stretched or distorted. This trick is useful for fluid layouts where you don't know the exact width of the column.
|
Example: See a live example even though this isn't responsive and is just a regular image. It does scale proportionally though.
Background images can't affect layout, which forces us to define a fixed height and width to use the responsive techniques outlined above. So it's not immediately obvious how to proportionally scale the height of the <span>
. The solution is to use a percentage padding-top
on an inner element to provide the proportionate height (see padding on MDN).
For padding
, percentages are defined relative to the parent element's width. Somewhat counter-intuitively, padding-top
is also defined as a percentage of the parent element's width — not the height as you might expect. Using padding-top
on an inner element allows the height to be proportionally scaled.
Below is the .image
as described above with an additional .inner
element.
|
Example: See a live example although it doesn't scale yet.
The .inner
element needs to be set to display: block
so that it can have dimensions applied. The height: 0
is required because it gets its height from padding that is applied in the <style>
element. The CSS below should be added to the generic CSS created in step 3 above.
|
Example: See a live example although it still doesn't scale.
As mentioned above, the height of the .inner
element comes from a percentage applied to padding-top
. The percentage is calculated by dividing the height of the source image by the width. For instance, if an image is 200 pixels wide and 100 pixels tall, the padding-top
would be 50% (100px / 200px * 100% = 50%).
The example below allows the responsive image to scale proportionally.
|
Example: See a live example that should scale proportionally as expected in browsers that support background-size
.
NOTE: As mentioned above, proportional scaling will only work in browsers that support background-size
. Unsupported browsers will simply show the background at full-size, centered inside the <span>
. If the <span>
is smaller than the image in IE8 and below, the image will appear clipped.
This idea is in its infancy right now but I think it may be a good alternative to the many JavaScript-based solutions out there. The positive side is that it makes it simple to supply media queries for creating responsive/responsive images and it doesn't use any JavaScript at all. The negative side is that the markup isn't 100% valid and the CSS can get a little verbose. At the same time, none of the other proposed solutions have a particularly clean syntax either. The <picture>
tag requires multiple <source>
tags, each with their own media query and relies on JavaScript. The srcset
proposal uses an alternate syntax to media queries, is not easy to understand and also will require JavaScript to function cross-browser.
The biggest concern is how to roll out responsive images over a large organization. What happens if a large corporation upgrades their website with a new responsive design to take full advantage of the rapidly growing mobile market? Should they roll out the <picture>
tag or the srcset
solutions? Should they roll out a complicated <style scoped>
solution like what's proposed above? No matter what path is chosen, the result is going to be slightly messy because there are no CMS that currently provide a nice solution for managing responsive images across a large organization.
Although most people are probably not going to be comfortable faking images using a <span>
and an embedded <style>
tag, I believe the immediate browser support and predictable behavior of this solution make it a compelling addition to the responsive image conversation.
Although this grid plugin is inspired by the 1KB CSS Grid, it's not simply the Sass version of that grid. So I've opted to rename the grid to simply “Grid” and gobbled up the “compass-grid-plugin” gem name because some Austrian guy already has “compass-grid” (it's helpful that all of the other Compass grids use “-plugin” in the gem name also). That's the unimaginative name I'm going with until someone asks me to use a name less confusingly generic. I could maybe be convinced to call it “Pancakes” or “Simple.”
The point of Grid is to make grid math easier and make working with grids much more flexible. Old-school CSS-only grids require classes to be added to the markup and do not make working with the grid very easy. What happens when you need padding or a border on your column? What happens when the designer needs to cheat the grid in a few places? Compass Grid makes all of these situations much easier to handle because the measurements are not baked into the CSS and don't require hard-coded classes in the markup.
There's a few other notable Compass grids. Of course Compass has always shipped with Blueprint pre-installed. There's also a port of 960.gs as well as a relatively new entrant, Susy. Someone has also ported Columnal to Compass. The original author of the 1KB CSS Grid has created The Semantic Grid System to take advantage of Sass/Less and remove the need for classes in the markup. All of these Compass-powered grids are great and you should use them! But they all are a little more complicated than “just a grid” (with the exception of The Semantic Grid System). At a glance, I think the main differences between most other grids and Grid are as follows:
$plus
argument.
|
You can include it in any existing Compass project by adding it to your config.rb file.
|
|
You can install the grid plugin as part of a new Compass project.
While the original grid was pointedly not fluid, this new version does support fluid percentage-based grids. The original 1KB CSS Grid series by Tyler Tate pointed out that nesting fluid grids can be difficult. Very similar to Susy and Columnal, the fluid grid requires context in order to nest columns. This is because of the relative nature of percentages: 25% is a totally meaningless number by itself.
Just like the previous article, we'll use a relatively simple 3-column layout for our page. The HTML below is a basic web page with a header
, footer
, and main area. For this example only the main area will contain columns. The .main-column
will have two columns nested inside of it.
|
Before diving into the fluid grid, it's important to see how it works with the static grid because the two are highly related. Below is the SCSS for a normal fixed grid similar to the example from the previous post]. Borders have been added to some of the elements to demonstrate how to use the grid correction features using the $plus
argument available in all of the functions.
|
@include grid-column(6, -2px);
subtracts 2px from the calculated column width to account for the borders on the column. A six-columns-wide column is normally 460px but using the $plus
argument of -2px, it becomes 458px.section
elements in the .main-column
are set to be rows, the section.hero
doesn't have any sub-columns in this example so the normal negative margins are removed.The fluid grid is set up similarly to the fixed grid, except the word “grid” is replaced by the word “fluid” in all of the mixins and functions. The fluid grid relies on the fixed grid for most of the width calculations and it also uses the same configuration options. The nested columns, #right-column
and #content
, require additional parameters to provide context because they are nested in the #main-column
.
|
width
, margin-left/right
and padding-left/right
, should be provided in percentages as well to make sure everything scales well with the browser.fluid-gutters
mixin returns left and right margins for a column.
fluid-width
function, combined with the grid-column-width
function can be used to calculate margins as well.margin: 0 fluid-width($grid-gutter-width/2, grid-column-width(9, $grid-gutter-width));
.row > *
, it's important to remember that a row's width is the width of its containing column plus the $grid-gutter-width
.
grid-column-width(9)
grid-column-width(9, $grid-gutter-width)
box-sizing: border-box
applied. You can read more about box-sizing on MDN.
For those that are curious, the CSS from the examples above looks like this.
|
|
Likely because the jQuery developers are busy with more important things, the documentation for actually setting up your environment to properly develop for jQuery is a little sparse. In the case of a Windows setup the documentation is also a little outdated and possibly overkill.
The links provided to Node.js on the jQuery GitHub page are to old binary builds of Node.js for Windows. Node.js was recently updated with a full installer for Windows thanks to Microsoft. You can download Node.js for Widows here.
If you install the full version of msysGit you shouldn't need to also install GNU Make (more details below). More importantly, When you install GNU Make by itself it actually doesn't work. The jQuery make file is filled with Linux-specific shell commands that Windows doesn't support natively so the build will always fail. It's actually not possible to fix it by re-writing the Makefile to be Windows compatible because Windows batch files aren't as full-featured as a Linux shell script.
Cygwin is a "collection of tools which provide a Linux look and feel environment for Windows." It's nothing too magical; it basically ports common command-line programs to Windows. For instance, normally in a Windows command prompt to get a director listing you'd have to type dir
but on Linux you type ls
and the results looks different. What Cygwin does is makes the ls
command (and many others) work correctly on Windows. It doesn't, however, make Windows magically become Linux. The primary benefit is that programs that rely on Linux command-line tools (but don't require Linux specifically) can run on Windows using Cygwin. However, Cygwin is a bit overkill for our purposes. We don't need a "complete UNIX/POSIX environment on Windows."
Cygwin isn't necessary if you install the full version of msysGit (more details below).
WAMP is a great tool for getting up and running with Apache, MySQL and PHP very quickly. There's no doubt that WAMP is extremely popular. However, for the sole purpose of running the jQuery unit tests it's a bit overkill. For instance, there's no need for MySQL at all and Apache is just one way to run PHP in Windows.
The msysGit download page is needlessly confusing with a range of different packages available. The two important versions are the "Full installer for official Git for Windows" and the "Full installer (self-contained) if you want to hack on Git." The primary difference between the two is that the self-contained version is "complete, in the sense that you just need to install msysGit, and then you can build Git without installing any 3rd-party software." The differences are explained in the msysGit wiki.
The part about being "complete" is key because, similar to Cygwin, the developers of msysGit have re-created a handful of required Linux commands and made them compatible with Windows (actually the MinGW guys did it). This essentially means that installing msysGit gives you something similar to what Cygwin offers. More importantly, you can use those commands for building jQuery on Windows without installing anything as large or complex as Cygwin. MsysGit actually includes a full version of GNU Make as well so there's no reason to download GNU Make separately.
The net install version is different than the version used in the Github Instructions for installing Git on Windows. If you've already installed that version it's ok. You can leave it in place and install this version as well. What we're really after is the GNU Make that comes with the netinstall of msysGit.
TortoiseGIT is totally optional but it is nearly as full-featured as TortoiseSVN and is an extremely easy way to work with Git on Windows. I would highly recommend it for anyone new to Git, particularly if you've used SVN in the past.
The Node.js installer is really straight-forward. After installing you may need to restart your command prompt to make the node
command available.
Both Git and Node.js need to be added to the your windows environment paths.
IIS comes with Windows and can be easily installed by following the IIS instructions provided by Microsoft. However, Microsoft also offers a video tutorial for configuring FastCGI with IIS and PHP and even has a quick install program available to install PHP with IIS.
Once PHP is installed in IIS, all that's left is to create a new site in the "Internet Information Services (IIS) Manager" that points to the jQuery source directory.
The MinGW binaries will enable the `make` command and allow the Linux-specific shell commands to work flawlessly on Windows. However, the jQuery Makefile has a line that tries to find Node.js which causes the build script to think that Node.js is missing from your system. This is easy enough to correct.
|
|
After following the steps above you should be able to execute commands in the Windows Command Prompt similar to Unix-like environments. This should make it trivial to work with the jQuery source code. The basic steps above could be used to make development of almost any Linux-centric project easier on Windows.
The following shows Node.js, GNU Make, Git and PHP available from the windows command-prompt.
If you have some weird reason to handle arbitrary units in your JavaScript library, the code demonstrated below should offer the most complete solution possible in the smallest possible package.
The basic goal is to create a small library that will make it possible to convert any valid value to pixels. This makes it possible to do further calculations using that pixel value.
|
Converting between length units is not exactly straight-forward because each unit can have different pixel value based on a variety of uncontrollable conditions. Factors such as font properties (em, ch, ex), the OS settings for your display DPI (in, mm, cm, pt, pc) or even the dimensions of the browser (vh, vw, vm) can change the pixel value of a unit. Because there is no direct conversion function available to JavaScript it's necessary to rely on manipulating the style
attribute on the element in-order-to trick the browser into doing the conversion for you.
The easiest units to convert are the absolute units. These are units that presumably represent real, fixed lengths (but actually don't). In the the real world there is no such thing as a relative inch — an inch is always an inch regardless of the settings on your monitor. On computers it's a little different; the absolute lengths are based on the DPI setting. On the vast majority of computers this is set to 96, which means that 1 inch usually equals 96 pixels. However, this can sometimes change: it's not entirely uncommon for the DPI to be set to 120 which would make 1 inch equal to 120 pixels. This is also already different on mobile devices like the iPhone 4 or newer (although the iPhone currently lies and claims to be 163DPI).
Because DPI is the deciding factor in absolute lengths, it's useful to define the conversions relative to the inch; this makes all of the other conversions easier (thankfully a pixel is always a pixel — except on iPhones). If you know the pixels in an inch, you can easily know the pixels in a pica.
As the name might suggest, font-relative units are dependent on the font settings of the element itself. The most commonly used is the em unit. One em equals the font-size
of the element (except in the case of the font-size
property where it means the font-size
of the parent element). The ex and ch units are rarely used and equal the height of lower-case "x" and the width of a "0" respectively. New to CSS3 — and much more useful than ex — is the rem unit which equals the em of the html
element. Converting rem and em to pixels is relatively straight-forward — all you really need is the element's (or the parent or html
element's) font-size
— but ex and ch require using the style
attribute as we'll see later.
Presumably useful for flexible layouts, these units are calculated relative to the browser dimensions and are currently only supported in IE9 and Opera. The three units are vh (viewport height/100) vw (viewport width/100) and vm (the smaller of vh or vw). These units change every time the browser is resized.
Perhaps the most complex unit to convert is percentage. The relative length for a percentage can differ based on the CSS property but it is usually based on the width of the parent element. For instance, a width
of 50% means 50% of the width of the parent element. For things like padding
and margin
it can get confusing because these also always mean the width of the parent; counter-intuitively, padding-top
of 50% means 50% of the width of the parent element as well, not the height. The best way to figure out what percentage is referring to is to look the property up on the MDN website.
As mentioned above, converting units can be done reliably* by setting the value using the style
property and reading it back using getComputedStyle
**. The getComputedStyle
function always*** returns values using pixels.
*sometimes **in supported browsers ***usually
|
The code above is the bare minimum necessary to convert pixel units but it has a few problems that will be discussed later. But first, let's look at what we're doing in the code. In the the toPx
function, we can convert any unit to pixels by assigning the value to the elements style
property, reading that value back as using the curCSS
function (which uses getComputedStyle
) and then changing the element's style
back.
This method of checking a property, changing it, measuring it and changing it back is commonly referred to as the "awesome hack by Dean Edwards". (The Dean Edwards hack relies on the proprietary runtimeStyle
property because his comment was originally meant to address an IE-specific issue).
Using getComputedStyle
isn't supported in IE 8 and below; they use currentStyle
instead, and it works slightly differently. Specifically, currentStyle
won't convert anything to pixels; it just returns raw values (known as the specified value). For instance, if the fontSize
is set to 10mm, IE8 and below will return it as 10mm instead of 37.795276px. This problem is discussed in more detail below.
In the example above the default property is set to width
("the awesome hack by Dean Edwards" uses the left
property) because in non-IE browsers left
cannot be set on a position: static
element. Width will reliably return pixel results even when the element is display: inline
in any browser. It should be noted that in IE 8 and below, left
can be reliably set and retrieved on any element regardless of its position
property.
If browsers were perfect, the code above would work perfectly. However there are a number of bugs that need to be accounted for. Surprisingly, most of these bugs occur in WebKit, not IE.
WebKit will not convert percentages when they are applied to margin
, top
, bottom
, left
, or right
. There is a bug report for the position properties but it isn't clear that the WebKit team intends to fix it. The MDN page for getComputedStyle
explains that the function should report the used value and not the computed value (which is confusing to say the least). The primary difference between the two is that a computed value can sometimes be a percentage (in the case of margin
, top
, bottom
, left
, right
, text-indent
and others). WebKit is apparently using the computed value in getComputedStyle
which the bug tracker suggests is due to a disagreement about the spec. All other browsers are using the used value.
A ticket in the jQuery bug tracker proposes a fix for WebKet returning a percentage for margin. However, as noted above, this bug also applies to positioning properties as well (and text-indent
). The commit for fixing percentage margins in jQuery specifically addresses margins but it could easily be extended to support the other affected properties.
IE8 and below don't support the getComputedStyle
function so the non-standard currentStyle
property must be used. Unlike getComputedStyle
, which is supposed to return the used value, currentStyle
returns the specified value (the MSDN manual calls this the "cascaded value"). The specified value is the value that should be assigned to the property but before it has been converted to an absolute unit. This is more useful than the style
property, which only exposes the inline values for the element; currentStyle
will return values specified in CSS as well as inherited values.
Thankfully IE 8 and below support a few non-standard properties that always return pixel values. The most commonly used property is pixelLeft (this is used in "the awesome hack"), however pixelWidth and pixelHeight work just as well. Opera also supports the non-standard pixel properties. WebKit supports them only for absolute units and Firefox doesn't support them at all. Interestingly, IE9 doesn't support them anymore because they were deprecated in an earlier version of IE.
Every unit (except percentages) is the same pixel value on an element no matter which property it is applied to (except fontSize
). For instance, a marginLeft
of 1em is the same length as a lineHeight
of 1em. Because of this fact, it is reasonably safe to use the special pixel values to convert units in IE 8 and below.
There is no direct way to convert the fontSize
because it could be relative to the parent elements fontSize
in the case of em and percentages. A jQuery bug about differing values for fontSize
points to a fix where using a left
of 1em will always equal the element's current fontSize
.
runtimeStyle
DoesAs noted above, "the awesome hack by Dean Edwards" uses runtimeStyle
for pixel conversions in IE. This IE-specific property is higher than the style
property in the CSS cascade which means that a runtimeStyle
value supersedes a style
value. By copying the currentStyle
value to the runtimeStyle
, "the awesome hack" avoids any potential for a FOUC. This works because changes to the style
property aren't rendered in the browser when a runtimeStyle
is set and the special pixel properties don't pay attention to the runtimeStyle
(because they belong to the style
object), so they can still be used to return the pixel value of the style
property. The runtimeStyle
is only available in IE and isn't useful for unit conversion beyond trying to fix FOUC issues if they appear in IE 8 or below.
Now that we understand the problems, we need to set about fixing them! The code below contains a patched curCSS
function.
|
The code above adds several fixes to the curCSS
function to correct for the issues mentioned above. It also adds a check to see if the browser is incorrectly returning the current value instead of the used value.
fontSize
property by converting the left
of 1em to pixels.top
and bottom
, compute the parent element's inner height and return the percentage.margin
, left
, right
, text-indent
) convert to pixels using the width
property.top
on an element that is position: static
.top
on an element that is position: static
.Now that we've fixed all of the bugs with curCSS
, it's time to make toPx
a little smarter. For instance, absolute units will never change under any circumstances once they've been calculated. This means that once you know how many pixels are in an inch, converting an inch unit requires only simple algebra and you can skip "the awesome hack" altogether. The same goes for em and rem: once you know the correct fontSize
in pixels, a little algebra finishes the job, no "the awesome hack" necessary. This actually covers the majority of cases for units and leaves only percentages as the primary need for mucking with the element's style
property.
|
The final code above makes a few changes to the toPx
function as well as pre-computing all of the conversions for the absolute units.
toPx
function and then using using the pixels-per-inch value to store the other conversions.fontSize
from and get the fontSize
in pixels using the curCSS
function and use that as the conversion.Nothing? It depends.
Anyone familiar with jQuery knows that $.css
will return the correct value for a property on an element but jQuery doesn't directly expose a method for converting units arbitrarily on an element. jQuery will convert units when calculating the start and end values for an animation (and it uses a version of "the awesome hack" to do it) but the conversion performed is limited in scope.
It's not possible to rely on jQuery (1.7.1 as of this writing) to handle unit conversion because it doesn't expose those methods to the API. Further, the optimizations for converting absolute, rem and em units without using "the awesome hack" should provide some speed improvements on any code that needs to rely on conversions. If you are writing a plugin, particularly a polyfill that requires some calculations to be done on a CSS property, properly converting units will allow users to specify values using the units that make sense to them instead of forcing everything to be specified in pixels.
JavaScript plug-ins don't usually do full-featured unit conversions because the code is typically too large to justify inclusion. However, the Final.js file above will minify to around 1.24KB (774 bytes gzipped) using YUI Compressor, 1.2KB (758 bytes gzipped) using uglify.js or 1.16KB (745 bytes gzipped) using Google Closure Compiler. The Google Closure Compiler minified version can be downloaded here.
The GitHub repo for Length and Angle units will hold the latest code and also contains a straight-forward Angle conversion library.
]]>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 div
using 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 .box
— border-radius
doesn't clip content unless overflow: hidden;
is set. Next, eight span
s 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.
In order to optimize performance and simplify the JavaScript plug-in, all of the styling is done using CSS. The base CSS will set the 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 top
and 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 span
s 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 span
s because border appears outside the box.
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 span
s 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.
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 span
s.
|
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.
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 span
s 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.
@mixin
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 @mixin
s: corner-images
and corner-border-width
. These @mixin
s greatly simplify the creation of CSS for mimicking rounded corners as well as more advanced examples.
The 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]The 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]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.
|
border-radius
ExampleIn 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 @mixin
s reliance on Compass's image-url
@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.
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 width
, height
, background-position-x
and background-position-y
are supplied. For the sides the respective height
or width
is specified as "auto" and then background-position-x
and 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.
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.
]]>@mixin
that relies on Compass to easily create CSS3 box-shadow
s from the values found in Photoshop.
UPDATE: a Github repo and Ruby Gem have been created.
Above you can see the Drop Shadow dialog in Photoshop. There is an article online that does a great job of breaking down what each of the properties does. Once it’s understood how Photoshop uses the values it’s easy to translate those values into CSS3 box-shadow
values.
#000
) will produce the same shadow in “Normal” and “Multiply”.rgba()
. rgba()
is currently not supported in CSS3PIE for box-shadow
.<offset-x>
and <offset-y>
values of box-shadow
and can be calculated with a little trigonometry.<offset-x>
and <offset-y>
.<spread-radius>
value of box-shadow
.<spread-radius>
plus the <blur-radius>
.box-shadow
has no equivalent values.Now that we have a basic understanding of what Photoshop is doing, let’s look at the values that box-shadow
accepts and see how they compare.
<offset-x>
and <offset-y>
<blur-radius>
<spread-radius>
<color>
rgba()
.As mentioned before, although Photoshop and CSS3 represent the values differently, they are actually analogous to each other.
Photoshop uses Angle and Distance, which can be used to calculate the <offset-x>
and <offset-y>
. Imagine a right triangle where the <offset-x>
and the <offset-y>
formed the right angle. The Distance would be the hypotenuse and the Angle is the inner angle. There are online triangle calculators for this basic trigonometry. Again, the <offset-y>
and <offset-x>
are the opposite and adjacent sides, the Distance is the hypotenuse.
Using this basic understanding of triangles it becomes easy to calculate the offsets using the Photoshop Drop Shadow values. As the online triangle calculator linked above points out, Sin(Θ) = Opposite / Hypotenuse
. More importantly, Opposite = Sin(Θ) * Hypotenuse
. The same is true of the adjacent side: Adjacent = Cos(Θ) * Hypotenuse
. And if you’re remembering your trig properly — or looking at the cheat sheet — you know that only two values are needed to calculate any other two. Thankfully, basic trig functions were added to Compass in version 0.11.
For those who are still totally lost:
<offset-y>
== Opposite;<offset-x>
== Adjacent; The <blur-radius>
and <spread-radius>
are not quite as straightforward but are equally easy to calculate. In Photoshop, the Size attribute represents the total length of the shadow and the Spread represents the percentage of the shadow that is a solid color — the rest is blurred. In CSS the spread is added to the blur for the total shadow size. With this in mind, <blur-radius>
is equal to the Photoshop Size minus the <spread-radius>
and the <spread-radius>
equals the Photoshop Size multiplied by the Photoshop Spread percentage. Again, the code example above bears this out.
@mixin
Below is a Sass @mixin
that takes values from Photoshop, converts them, and creates a CSS3 box-shadow
.
|
The code above relies on the Compass box-shadow()
@mixin
to generate the correct CSS rules with all of the correct vendor prefixes.
It’s worth noting that two additional @mixin
s are shown in the code above. @mixin photoshop-inner-shadow
is for Photoshops Inner Shadow Layer Style, which is analogous to the inset
value for box-shadow
. @mixin photoshop-text-shadow
is for text-shadow
which is implemented in Photoshop as a Drop Shadow applied to a text layer. It’s also worth noting that CSS does not support a Spread on text-shadow
s.
Using the @mixin
is quite simple. Assume that we want to recreate the above Photoshop image in CSS. This image was created with the default Blend Mode, Color and Opacity. The Angle is 120°, the Distance is 10px, the Spread is 50% and the Size is 10px. As the example below shows, all that is needed is to plug in the values as they appear in Photoshop into the @mixin
.
|
The @mixin
will automatically convert the values and create the corresponding CSS rules as shown in the example below. It is important to note that the Angle and Spread values should always be unitless while the Distance and Size values require px as the unit.
|
Often overlooked in the hype over the browser wars is the role that the OS plays in all of this. Windows 7 is now the most popular OS in the United States; it passed Windows XP in April. It's set to become the number one operating system in the world sometime in October or November this year. Many commentators chastised Microsoft for not releasing a version of IE9 for Windows XP but that doesn't seem to have been a bad strategy for Microsoft. IE9 is already making impressive market share gains although Windows XP is likely to be a major operating system on the worldwide market for some time, it will likely still have around 30% market share by the time of the 2012 presidential elections. That means that the long tail of decline for the older IE versions is likely to be kept alive by the stubborn decline of Windows XP.
Although a lot of emphasis is often placed on particular browser features or the new technologies of HTML5, it's just as important to look at the real landscape of browsers in the marketplace — this indicates when developers can actually start using a feature. Websites like When can I use… do an excellent job of tracking features and mapping them to current browsers.
Below you can see the market share of browsers in the US. The various flavors of IE have been separated out because they are so radically different from each other while versions of FireFox, Chrome and Safari have been merged because they are all reasonably up to date, and reasonably similar.
In the US, IE9 usage has risen quite dramatically since the year began, to 10% while IE8 has plunged. IE7 continues to drop rapidly and is now just over 5% in the US. The other big success story is Google Chrome, reaching 16% in the united states.
Worldwide it's a slightly different story. Mostly because of Europe, Firefox and Chrome have much more impressive overall numbers. IE9 is catching on more slowly, largely because of IE's lower overall usage in Europe. Safari's market is largely tied to OSX which is much less popular worldwide than in the US.
In the US, Windows 7 surpassed Windows XP in April of this year and it will likely happen worldwide in November. Tracking Windows 7 adoption is important because IE6 and 7 are not available for Windows 7. Windows Vista never really caught on and the release of Windows 7 has decimated Vista's market share. Vista is set to drop to 8% by the end of the year and, if trends continue, it will be hanging out with desktop Linux by the end of 2012 — around 1% or 2%.
The chart above shows the global market share of the 4 top operating systems with trend lines drawn out for the rest of 2011. The lines for Windows 7 and Windows XP cross some time in November.
Trend lines have been added to the chart above for the rest of 2011. As the chart above shows, the release of IE9 has had a significant impact on IE8's market share — IE9 is climbing almost as fast as IE8 is falling. IE7 has also begun a dramatic fall since IE9's release in March. IE7 is on track to fall below 3% by the end of the year — this is pretty remarkable considering it started the year with nearly 12%. Chrome continues to make significant gains with the majority of its share coming from IE and is set to end 2011 with around 20% market share. Firefox has been in a slow decline for quite a while, losing around 3% a year since peaking in 2009 (around the time of Chrome 2's release).
While Safari's numbers are basically stagnant and follow in line with OSX's market penetration, Safari for iPad and iPhone continue to grow and have begun showing up in the top 10 browsers on Global Stats, beating out IE6 in the US. Those numbers will continue to grow and become competitive with IE6 and IE7. This means that in 2012 — and likely well before that — it will make sense to weigh between iOS/Android support versus IE6/IE7 support. Yahoo! has already updated their GBS list to include mobile browsers. It may seem like a false choice to drop IE6/IE7 in favor of mobile support however, considering they likely take an equal amount of extra developer time, it makes sense to support the edge case with a growing market share instead of a shrinking one. Judging by new CSS frameworks like Mobile Boilerplate, 320 and Up and Skeleton, mobile is actually becoming a real consideration for front-end developers.
At the same time, the new focus on mobile caused a co-worker of mine to gripe, "Android is the new IE6." After reviewing the jQuery Mobile GBS it's clear that mobile is still a wild frontier. This is likely why Yahoo! is still only supporting iOS and Android. Of course the stats tend to bear that out as well; iOS and Android dominate the mobile handset market and blackberry is in a sharp decline.
Most developers have been on this page for months or even years, but it is now obvious that IE6 will be hanging out with browsers like Firefox 3.0 and 3.5 Or Safari 4 by the end of the year. The only reason anyone had for supporting it this long was corporate clients that were reluctant to upgrade. From my experience, many corporate clients have begun the process of upgrading form Windows XP/IE6 to Windows Vista/IE7 — because if they upgraded to Windows 7 and IE9 the world would end.
People (me) were calling for abandoning IE6 once it hit 5% in the US. IE7 is now at that level and it has started to show the same consistent pattern of decline that IE6 was showing. IE7 has actually been declining much faster than IE6 ever did, similar to the decline of Windows XP versus Windows Vista. Halfway through 2011 IE7 has already dropped from nearly 12% to just over 5%. IE7 will probably adopt the slow death of dropping it's market share by more than half every year going forward, similar to what IE6 is doing now. This all means that by the time 2012 rolls around, IE7 will be a breath away from dropping off the required browsers list for most developers.
It's kind of a shame to see IE7 so close to obsolescence so soon after most developers were finally able to drop IE6. The developers that I work with regularly get excited when I tell them, "No IE6 testing, only IE7." Only having to test for IE7 is a sweet sigh of relief, but it's still a painful experience. Sadly IE7 can't be dropped right away because of corporate clients but by the end of the year, things will be a different story. With the rapid release of IE10 coming in the fall and the likely hot-on-its-heels announcement of IE11, corporate IT will have to rethink it's strategy of dictating specific IE versions within their network.
]]>canvas
— which doesn't use CSS at all — is more difficult.
To solve these issues I've created two new jQuery plug-ins: jQuery Tween and jQuery Curve.
Tween simply uses the existing jQuery animation functions to manage the animation frames but leaves the application of the animation to the user. This essentially means that instead of animating a specific CSS property, tween simply fires a callback function. This is very similar to using the step callback option for jQuery animate but it removes some of the calculation overhead. Tween makes it easy to animate something like a canvas
element which jQuery animate doesn't handle by default.
In the example above, a sine wave is drawn on a canvas
over 3 seconds. The first argument in the tween plug in is a step function while all other arguments match the default jQuery animation function. The step function receives 2 arguments, the current value and the fx
object. When using the tween function, the current value passed is always equal to the current animation time. When using jQuery animate the current value is the value of the CSS property being animated. As the example above shows, the tween plug-in makes it much easier to animate something besides a standard CSS property.
Conceptually, the tween plug-in is similar to animating a custom property with jQuery's built-in animate function. The example below shows the exact same behavior as the example above using the native jQuery animate with a custom property instead of using the tween function.
While using animate directly is similar to tween, animate causes jQuery to perform a series of property lookups and calculations on initialization that are skipped with tween. Additionally, tween adds a special step function to the fx
object that overrides jQuery's default behavior to avoid any additional calculations on each step of the animation.
It's also worth noting that the normal jQuery animate calls the step function once for each property being animated. So if you're animating five properties (height, width, etc.) with the native jQuery animate function, a step function is called five times for each step of the animation. The tween plug-in avoids these extra function calls.
The curve plug-in contains a handful of useful functions for plotting curves as a function of time and works well with the tween plug-in. The curves supported natively are circle, ellipse, sine, and bezier curves. It would be possible to define additional curves quite easily by extending the curve object. Each function takes a similar set of options and returns x and y coordinates.
The example above uses a curve function to draw a sine wave. The curve function actually returns 3 values in the array: x, y and the angle of the tangent line. The tangent angle can be used to rotate an object to follow the curve. As you can see, the curve function makes it easier to calculate a sine wave as a function of time.
jQuery animate is using a simple concept of percentage of completion to perform animations. In a simple 1 second animation — let's animate the height property — jQuery will look at the current height value and the final height and calculate the difference. Next jQuery will calculate the current time and the duration — again, 1 second in this example — and start an interval timer. At each interval the percentage of the duration is calculated. That percentage is essentially the current time divided by the final time. The percentage is then altered by the easing function to speed up or slow down time before it is applied to the value in question.
Consider animating the height from 0px to 10px over 1 second. At 500ms the animation is 50% completed. In a step function the fx
object reflects this as an fx.state
of 0.5. The fx.pos
will be slightly different based on the easing function; but with the default easing it is 0.4999. The current height is the start value plus the difference multiplied by the current position. This means 0px + 10px * 0.4999 which is 4.999px. Of course the differences between fx.state
and fx.pos
can be more dramatic depending on the fx.state
. For instance, an fx.state
of 0.25 corresponds to an fx.pos
of 0.1464 using the default easing.
The value of fx.pos
is essentially a percentage of completion and is important for plotting curves. Any curve, like a circle, has a start and an end point. Simple trigonometry can be used to calculate any point on a circle at a certain time. This makes it trivial to use such a function for drawing a curves as the example below shows.
The example above isn't animating the circle, it's drawing it as a series of line segments using a loop. The current position on the circle is calculated for each iteration of the loop. Even though it isn't animated, the circle is still drawn as a function of time. NOTE: if all you want to do is draw a circle on a canvas
there's easier ways.
In addition to the x and y coordinates, the curve functions can also return a tangent angle which can be used to rotate the element in order for it to follow the curve. The tangent is returned in radians. In the example below, the angle of the rocket ship angle is set to the tangent angle so it will appear to follow the sine wave as it moves.
The example below shows each of the supported curves in action. The tween plug-in is used to animate and rotate a rocket ship along a curve while simultaneously drawing the exact same curve on a canvas
. This demonstrates the utility of separating the jQuery animate functionality from straight CSS operations — animating a div
and drawing on a canvas
at the same time.
text-shadow
property that allows for, well, shadows to be placed on text. As of February 2011, text-shadow
is only supported in 48% of browsers in use. Internet Explorer 8 and below do not support text-shadow
; surprisingly Internet Explorer 9 doesn't either. Because the majority (90%) of non-IE browsers already supports text-shadow
s natively, it's most interesting to try and polyfill IE. I've created a Textshadow jQuery plug-in for just this purpose. Read below for an overly thorough explanation of hacking IE filters to get a convincing text shadow.
The goal is to create a text-shadow
polyfill for IE 9 and below and other browsers that lack support. It must look similar to the implementation in supported browsers and follow the W3C specification as closely as possible.
Below is the example of the goal: the native text-shadow as rendered in Chrome 9. The shadow is red to add the greatest contrast between the black text and the white background.
|
For these examples the simple HTML above is used. The .box
is used for forcing the text to wrap and the funky b
and i
elements in the word "shadow" are for testing how shadows are applied to child content.
|
None of the browsers that support text-shadow
require a vendor prefix, making text-shadow
extremely easy to implement. As noted above, this will not work in IE including version 9.
While IE doesn't directly support the CSS3 text-shadow
property, it does support proprietary filters. Of course these filters have drawbacks; namely, IE 8 and below apply them very slowly and they have poorly documented features. For instance, the IE filters will apply to text unless the element has a background applied; the filters will also apply to borders if they are present. Another issue that will become clear later is that IE renders the text with alpha-transparent artifacts that make the text harder to read.
There are a few IE filters that seem promising. Most often the shadow filter is proposed as an alternative to native text-shadow
although the most compelling articles use a combination of blur and glow to achieve a text-shadow
effect (another great example only uses blur).
The example above is the proprietary shadow filter in Internet Explorer. This filter takes the original color and fades it to transparent. The filter takes three arguments for color
, direction
and strength
. Direction is taken as an angle. To approximate a 2px X and 2px Y offset, a direction of 135 degrees (135 degrees is 90 degrees plus 45 degrees (duh?)) and strength of 2 is chosen.
color
- hex color without leading hash (#) character.direction
- angle in degrees. Zero degrees is straight up.strength
- length in pixels without any units.
|
Changing the text to white will reveal some of the artifacting issues mentioned above. There are partially black pixels dotted around the perimeter of the text. This is the result of IE using a black matte behind the alpha transparent pixels in the text. I haven't fully experimented with how to remove this artifacting but it will likely involve turning off font smoothing in IE if it's possible.
strength
(like 2px) the shadow looks similar to the native text-shadow
. With a higher strength
it becomes apparent that the shadow strength is not an offset.Dropshadow will make a copy of the text and offset it behind the original text. This is very similar to the W3C spec for text-shadow
with the exception of blur being unsupported. As shown above, the shadow is not anti-aliased.
color
- hex color without leading hash (#) character.offX
- length in pixels without units for the offset along the X axis.offY
- length in pixels without units for the offset along the Y axis.
|
With white text it is evident that dropshadow suffers from the same black pixel artifacting as shadow.
Glow is yet another filter that could be used to replace text-shadow
; however, it's obvious that glow isn't very much like text-shadow
at all. But it could be used to make text stand out from a background, which is essentially what text-shadow
is used for. The color gradient effect is similar to the shadow filter but for glow, the color is omni-directional.
It should be noted that the glow affects the positioning of the text. A 2px glow pushes the text down 2px and to the right 2px. A 20px glow pronounces the issue, predictably pushing the text 20px down and to the right. The other filters, shadow and dropshadow, exhibit the same behavior when the shadow is projected up or to the left. This can cause unexpected cross-browser display issues.
color
- hex color without leading hash (#) character.strength
- length in pixels without any units.
|
Again, white text reveals the same distracting artifacting as shadow and dropshadow.
text-shadow
.Blur is perhaps the most frustrating filter because it is both exactly what is needed and not at all appropriate. The blur effect is very similar to the blur other browsers use for text-shadow
. But of course, the text is rendered unreadable. It should also be noted that the blur offsets the text the same way glow does. For this example the text is shifted 2 pixels down and to the right by the blur. The larger the pixelRadius
is, the larger the unintended offset will be, similar to the glow filter.
pixelRadius
- length in pixels without any units. This is equivalent to the blur-radius argument for text-shadow
.makeShadow
- turns the text black no matter what color it was originally. Never use this.shadowOpacity
- only applies when makeShadow is set to true. Sets opacity and expects a value between 0 and 1. Never use this.
|
Using a few span
tags and duplicating the text allows for the blurred text to be placed behind the original text. By wrapping the original text in a span
, duplicating the original text and adding it as a second span
, and wrapping both in a third span
, it becomes simple to position the blurred text as a shadow. As is shown above, using absolute positioning for the shadow can have undesired results. The absolute positioned span
is collapsed to be as wide as the longest word. However, it is clear that the word "Some" has an appropriate text shadow. It is possible to support shadow color simply by setting the color
property on the blurred text.
|
The .shadow
wrapper is given a position: relative
to allow for proper positioning of the .shadow-copy
. The .shadow-copy
is given absolute positioning and a z-index
of 1. The .shadow-orig
is given position: relative
and a z-index
of 2 in order to place it above the .shadow-copy
. The top
and left
properties can be applied to .shadow-copy
to approximate the X and Y text-shadow
offsets. In this case, top
and left
are set to zero because the blur filter already shifts the shadow by 2px as described above.
Instead of wrapping a long string of text in the span
elements, it is more reliable to wrap individual words. This ensures that the .shadow-copy
is always an appropriate width without having to set a hard width
property in the CSS. Setting a hard width on the span
would cause issues if the text-size
were to change or the containing block changed size dynamically.
|
The above is an example where the simple threesome of span
elements has been applied to every individual word. Note that in the word "shadow", a span
is nested inside each b
and i
element as well.
I have created a simple jQuery plugin called jQuery Textshadow that automatically wraps each word in the span
s as shown above and applies the appropriate blur filter and positioning for the shadow. It even supports RGBA and HSLA by converting the values to a hex color and applying the alpha filter to the shadow. The plugin requires that a small CSS file also be included on the page in order to set up some base styles for the spans.
|
Currently, the plug-in assumes it is being applied in IE only and doesn't do any feature detection. It should be applied using a tool like Modernizr or IE conditional comments. It's not particularly speedy, especially if there are dozens of tags that need the shadow applied. The slowest part is actually wrapping the text nodes with the replacement HTML. It's acceptably fast in Internet Explorer 9 and noticeably slow in Internet Explorer 8.
Curiously, Internet Explorer 9 also applies a cleaner shadow that is closer to text shadow in other browsers. The blur in IE 8 is noticeably muddier than the browsers that support text-shadow
. See the images below for comparison.
border-radius
or multiple backgrounds and of course those approaches won't work in Internet Explorer 8 or below. Ironically, one of the best places to find some of the old school CSS solutions is on the MSDN page describing border-radius
. For modern browsers, the border-radius
property will do a good job of rounding corners. To support all browsers some clever techniques will need to be used.
To successfully approximate border-radius
, the content of the box should appear as it does in a regular box with a border applied.
border-radius
supportborder-radius
supportAs mentioned above, border-radius
is supported in roughly 50% of browsers globally (as of February, 2011). The other 50% of browsers are IE 8 and below.
|
Above is the simple box that we want to give rounded corners. The goal is to have it look something like the picture below.
|
Again, in all browsers (except IE 8 and below) the above CSS will add rounded corners to any .box
. According to the Mozilla border-radius
documentation the -moz-
and -webkit-
prefixes are for older versions of Firefox and Safari respectively. IE 9, Firefox 4, Safari 5 and Chrome 4+ all support the standard W3C border-radius
property without the vendor prefix.
With the above goals in mind a straight-forward solution might be to force IE 8 and below into line using some creative JavaScript-based technique that won't require any images to be created.
CSS3 PIE an extremely clever polyfill technique that reads and processes the CSS file using a DHTML behavior in Internet Explorer to find the border-radius
rules and then generates a VML object, placing it behind the target element. A tool like CSS3 PIE can be used to support rounded corners in IE 8 and below. The big drawback to CSS3 PIE is that it works best on static content and is slow to render. Because the VML object is placed behind the box, it must remove all of the visible styles from the box including border and background images. Those visible styles are then applied to the VML object.
View the page above in IE 8 or below and the corners should look correct. Refresh (hit F5) repeatedly and see that the corners do not appear instantly. Inspect the HTML in IE 8 or below (hit F12) and see the VML object. It's worth noting that CSS3 PIE isn't particularly slower than any other similar polyfill.
There are also tools like jQuery Corner and Curvy Corners that will use native border-radius
in supported browsers and use a series of 1px tall div
elements to polyfill in other browsers. This is a popular solution to the problem that works by adding in extra div
elements. Each div
is given a right and left border the same color as the page behind the element and the border widths are altered to create appearance of a rounded corner.
If native border-radius
isn't an option (and polyfills aren't stable enough), then it will take several images to do the trick. In some cases a sprite can be used. For the examples below we'll try to create the same 1px red border with a 12px border-radius
from above. Rounded corner images can easily be created at a number of on-line generators. My favorite is the Rounded Corner Image Generator.
As is evident by the top, bottom, left and right images, when the border is a simple 1px solid color, those images may not be necessary and could be replaced by actual borders. For these examples images were used anyway because it's more common that the borders are complex and need to be images — particularly when shadows are involved.
Most rounded corner techniques are a variation on the classic Onion Skinned Drop Shadow article or the Sliding Doors article (part 2). For instance, one of the techniques from the MSDN article uses a variant of the sliding doors technique. These techniques are effective but they're not particularly easy to implement. They're also limited because they try to accommodate IE6 which has a fair share of documented CSS issues. There is no shortage of cross browser rounded corner articles. The majority mention either using a method similar to CSS3 PIE or jQuery Corner above or they recommend using an image + HTML + CSS solution like the ones below.
div
tags can be tedious and difficult to replicate across many pages on a large site.div
s (top, bottom, left, right, top-left, top-right, bottom-right, bottom-top) to properly imitate the capabilities of border-radius
.Onion skinning is the process of adding multiple wrapping div
elements to a .box
element and then applying a different background image to each of the layers. This technique was originally developed for creating drop shadows but can easily be adapted to creating rounded corners.
div
elements.content
needs to have a height set if it is applied to a fixed-height box
|
The HTML markup above shows how onion skinning would work on the original simple box from the beginning of the article. Each layer of the onion contains the background image for one of the sides or corners. It's possible to re-use the .box
and .content
elements which results in only 7 extra wrapping div
elements. The sides are wrapped first so that the corners can cover them up later.
|
The accompanying onion skinned CSS is very straightforward. Each layer gets its own background image and it is positioned appropriately. The sides repeat and the corners do not.
.box
height (200px in this case) should be duplicated on the .content
padding-top
and padding-bottom
need to be set on the .content
The sliding doors technique uses a content area sandwiched between a top and a bottom element. The top and a bottom each contain elements for left and right corners. This technique allows for great flexibility with using image sprites and allows the corners to be fully alpha transparent if desired. The markup appears to be much more sensible but the CSS is much more complicated.
|
In the sliding doors example, a few extra div
elements are added to the top and bottom of the box and the content is wrapped in some left and right div
elements. This allows the left and right backgrounds to grow with the height of the content. In a case where the left and right border is a simple solid color these wrappers may be eliminated and the border applied directly to the .content
.
|
To correctly position all of the pieces, the box is given a top and bottom padding the same height as the corner (12px in this case). For a fixed height box this padding should be subtracted from the original box height. Then the .top
and .bottom
containers are given a top and bottom margin of -12px respectively (the same as the top and bottom padding on the box). The .top
and .bottom
containers are also given left and right margins the same as the width of the corners (again, 12px in this case). The corners are then absolutely positioned into the empty space created by giving them a -12px left and right position respectively.
The .left
and .right
wrappers wrap the .content
and operate more like the onion skinning techniques. The right div
is given a 1px top and bottom padding to account for collapsing margins and the .content
is given a negative margin to pull its edges to the edge of the box. Adding position relative and a z-index ensures that the .content
is above the top and bottom div
containers to avoid content clipping.
The left and right wrappers are particularly challenging because of the need to apply a 100% height to ensure they are the correct height in conjunction with the 1px top and bottom border applied to prevent collapsing margins from affecting the layout. This isn't an issue in IE 6 and 7 because they collapse the top margin even when there's a border. But for the other browsers the 1px border combined with a 100% height on the .right
wrapper makes it 2px too tall. By using an alternate method for spacing in IE 6 and IE 7, the padding can be negated by adjusting the box-sizing
.
A solution that is not often posited is using absolute positioning to place all of the corners and sides on the box. This solution takes advantage of how most browsers (except IE 6) handle conflicting right
and left
properties on an element. For instance, a rule like .top {position: absolute; left: 12px; right: 12px;}
effectively stretches .top
across the full width of its nearest positioned parent. IE 6 ignores the conflicting rules which makes it more difficult to support that browser with this technique.
|
In the example above, the 8 corner and side spans are added to the top of the .box
and the content of the box is wrapped in a .content
div
. The .content
is to allow for proper layering so that the corners are below the content.
|
The CSS for absolute positioning is really straight-forward. The sides and corners are all set to be position: absolute
and given z-index: 1
while the .box
and .content
are set to position: relative
and the .content
is given z-index: 2
. The corner span
s are positioned in the appropriate corner and the sides are positioned on the appropriate side. The key trick is giving, for instance, the .top
a left: 12px
and a right: 12px
. The apparently conflicting riles actually stretch the span
the full width of the .box
. The same is done with the .left
and .right
sides, giving them both top
and bottom
properties to stretch them the full height of the .box
. The 12px is to allow space for the corners to avoid overlap.
As mentioned at the start of this article, border-radius
can be used to add rounded corners to a .box
.
It's now possible to use multiple backgrounds in newer browsers including Firefox 3.6+, IE 9, Safari and Chrome. Multiple backgrounds are layered in in the order they are added in the CSS with the first image being the top-most layer. Using this technique for corners might seem slightly silly given the existence of border-radius
in every browser that supports multiple backgrounds, however there are times when the native border-radius
cannot accomplish the same thing as images.
border-radius
cannot support
|
While CSS3 PIE is promising, it is still difficult to use on large scale sites. The JavaScript-based polyfill techniques don't seem to work very well in IE 8 and below at the moment (this is extremely unfortunate). Most of the techniques for applying backgrounds require ugly markup or intensely difficult CSS to accomplish. I personally prefer the Absolute Positioning technique as it is the easiest to implement and offers some support of Legacy browsers. The fact that IE 6 is not supported is not a huge issue given that IE 6 is no longer necessary to support for most projects (4.6% globally, 2% in the US and Europe as of February 2011).
In the future I intend to create a simple jQuery plug-in that will apply the absolute positioning technique to a .box
.
border-radius
— Supported in only 50% of browsers as of February 2011UPDATE: a Github repo has been created for the 1KB-SCSS-Grid.
UPDATE: a follow up article, Compass Grid Plugin and A New Fluid Grid Option, has been posted.
UPDATE: the Github repo has been renamed to compass-grid-plugin and a Ruby Gem has been created.
|
The above CSS is very similar to the original source with a few stylistic differences (dashes instead of underscores) and the removal of the overflow: hidden
clearfix hack and IE6 hacks. My intention is to use the grid with HTML5 Boilerplate which has a better clearfix built-in (overflow: hidden
isn't appealing as a clearfix hack). I've also chosen to remove IE6 from my list of supported browsers for any project that doesn't require it.
overflow: hidden
clearfix hacks
.row
to .page
.row .row
to .row
.box
for containers within columns
It's not immediately obvious how to piece this all together but the concepts are pretty simple.
.grid-#
is for column widths
.column
is for floating columns
.row
is a column container (columns are always nested in a row)
.page
is the main page wrapper with a full width and is horizontally centered.
|
Above is a typical page using the new HTML5 container tags and based on the example HTML5 Boilerplate markup. This example is shown with hard-coded class names in the HTML. As expected, there's a page wrapper (#container
), header
, #main
and footer
containers, and some columns in the main area. This page would create a typical 2 column layout (#left-column
and #main-column
). The #main-column
, as shown, has a .hero
area that spans the full width and a content area is further sub-divided into 2 columns (#content
and #right-column
).
On a typical site, the header
and footer
would probably contain their own columns and rows. The .hero
is used for promotional space at the top of content. This is a typical design choice and might contain a banner or a slideshow or some introductory copy.
Personally I find the inclusion of the class names in the markup to be a little ugly. The most offensive part is the grid-3
and grid-6
, etc. This is barely different than hard-coding the width with an inline style. I prefer to use SASS mixins to remove those class names from the markup altogether and control it directly from the CSS.
|
Writing the same thing in SASS opens up a world of possibilities. Using mixins allows the column measurements to be easily changed and opens up the possibility of removing the extra class names from the markup. This SASS file was used to generate the CSS at the top of the article.
$column-width: 60px;
is the width of a sinlge column
$gutter-width: 20px;
is the space between two columns
$columns: 12;
is the total number of columns
grid
and grid-plus
are simply used for setting a width on a column. grid-plus
can be used to account for padding or otherwise arbitrarily altering the standard column width. This is especially useful when dealing with designers that like grids but like violating them even more.
|
The example above shows 3 equivalent ways to specify a 6-column-wide column. The shortcut grid-column
mixin makes it easy to create a standard column. But in the case where a column might require border
or padding
, the grid-plus
mixin can be used to account for the difference.
@include grid(<number>);
is for column widths
@include grid-plus(<number>, <length>);
is for columns that need a non-standard width
@include grid-column;
is for floating columns
@include grid-column(<number>);
is a shortcut, the same as calling grid(<number>)
and grid-column
consecutively
@include grid-column-empty(<number>, <position>);
adds margin to create empty space either before or after the column
@include grid-row;
is a column container
@include grid-page;
is the main page wrapper with a full width and is horizontally centered
@include grid-page(<number>);
creates a page container that is less than the default page width; useful for modal pop-ups
Using the ID's and class names already in the markup, it's possible to utilize the mixins that were created earlier to achieve the exact same effect. I typically do this in a layout file that contains all of the column layouts for the various templates in the site.
|
#container
is specified as a grid-page
header
, #main
and footer
are set up as grid-row(true)
.clearfix
to those container
grid-row(true)
is used for rows contained directly within a grid-page
which ommits the negative margins that would otherwise be applied.
#left-column
and #right column
are specified as 3-columns wide
#main-column
is 9 columns wide. More importantly, all section
tags are specified as grid-row
, allowing them to contain columns.
#content
column is 6 columns wide.
From the above example it should be clear how to use mixins to apply the grid styles directly through CSS without needing to hard-code the grid related class names into the markup.
Because SASS must be compiled to CSS for it to work in a browser, it's useful to see what CSS is being generated. The file below is what the layout.scss example file generated.
|
|
SASS makes it possible to clean up the HTML and remove the extra classes.
]]>