<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-gb"><title type="text">24 ways</title>
<subtitle type="text">to impress your friends</subtitle>

<link rel="alternate" type="text/html" href="http://24ways.org/" />
<id>tag:24ways.org,2005:7e1577ceca7ea22e28af5c35ee62927a</id>
<generator uri="http://textpattern.com/" version="4.0.5">Textpattern</generator>
<updated>2008-07-03T12:18:11Z</updated>
<author>
		<name>Drew McLellan</name>
		<email>feeds@allinthehead.com</email>
		<uri>http://24ways.org/</uri>
</author>
<link rel="self" href="http://feeds.feedburner.com/24ways" type="application/atom+xml" /><entry>
		<author>
			<name>Drew McLellan</name>
		</author>
		<published>2007-12-24T00:00:04Z</published>
		<updated>2007-12-24T12:13:58Z</updated>
		<title type="html">Performance On A Shoe String</title>
		<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/24ways/~3/258873386/performance-on-a-shoe-string" />
		<id>tag:24ways.org,2007-12-23:7e1577ceca7ea22e28af5c35ee62927a/d41e666c15dba7ecf2d14d641670a0c9</id>
		<category term="performance" />
		
		<content type="html">
&lt;p&gt;Back in the summer, I happened to notice the official &lt;a href="http://www.wimbledon.org/"&gt;Wimbledon All England Tennis Club&lt;/a&gt; site had jumped to the top of Alexa&amp;#8217;s &lt;a href="http://www.alexa.com/site/ds/movers_shakers?lang=en"&gt;Movers &amp;#38; Shakers&lt;/a&gt; list &amp;#8212; a list that tracks sites that have had the biggest upturn or downturn in traffic. The lawn tennis championships were underway, and so traffic had leapt from almost nothing to crazy-busy in a no time at all. &lt;/p&gt;

	&lt;p&gt;Many sites have similar peaks in traffic, especially when they&amp;#8217;re based around scheduled events. No one cares about the site for most of the year, and then all of a sudden &amp;#8211; wham! &amp;#8211; things start getting warm in the data centre. Whilst the thought of chestnuts roasting on an open server has a certain appeal, it&amp;#8217;s less attractive if you care about your site being available to visitors. Take a look at this Alexa traffic graph showing traffic patterns for superbowl.com at the beginning of each year, and wimbledon.org in the month of July.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/24/graph1.png" width="609" height="301" alt="Traffic graph comparing superbowl.com and wimbledon.org" /&gt;&lt;span class="caption"&gt;Traffic graph from &lt;a href="http://www.alexa.com/data/details/traffic_details/wimbledon.org?site0=wimbledon.org&amp;#38;site1=superbowl.com&amp;#38;y=r&amp;#38;z=3&amp;#38;h=300&amp;#38;w=610&amp;#38;range=6y&amp;#38;size=Medium"&gt;Alexa.com&lt;/a&gt; &lt;/span&gt;&lt;/p&gt;

	&lt;p&gt;Whilst not on the same scale or with such dramatic peaks, we have a similar pattern of traffic here at 24ways.org. Over the last three years we&amp;#8217;ve seen a dramatic pick up in traffic over the month of December (as would be expected) and then a much lower, although steady load throughout the year. What we do have, however, is the luxury of knowing when the peaks will be. For a normal site, be that a blog, small scale web app, or even a small corporate site, you often just cannot predict when you might get &lt;a href="http://slashdot.org/"&gt;slashdotted&lt;/a&gt;, end up on the front page of &lt;a href="http://digg.com/"&gt;Digg&lt;/a&gt; or linked to from a similarly high-profile site. You just don&amp;#8217;t know when the peaks will be.&lt;/p&gt;

	&lt;p&gt;If you&amp;#8217;re a big commercial enterprise like the Super Bowl, scaling up for that traffic is simply a cost of doing business. But for most of us, we can&amp;#8217;t afford to have massive capacity sat there unused for 90% of the year. What you have to do instead is work out how to deal with as much traffic as possible with the modest resources you have.&lt;/p&gt;

	&lt;p&gt;In this article I&amp;#8217;m going to talk about some of the things we&amp;#8217;ve learned about keeping 24 ways running throughout December, whilst not spending a fortune on hosting we don&amp;#8217;t need for 11 months of each year. We&amp;#8217;ve not always got it right, but we&amp;#8217;ve learned a lot along the way.&lt;/p&gt;

	&lt;h3&gt;The Problem&lt;/h3&gt;

	&lt;p&gt;To know how to deal with high traffic, you need to have a basic idea of what happens when a request comes into a web server. 24 ways is hosted on a single small &lt;em&gt;virtual dedicated&lt;/em&gt; server with a great little hosting company in the UK. We run Apache with &lt;span class="caps"&gt;PHP&lt;/span&gt; and MySQL all on that one server. When a request comes in a new Apache process is started to deal with the request (or assigned if there&amp;#8217;s one available not doing anything). Each process takes a bunch of memory, so there&amp;#8217;s a finite number of processes that you can run, and therefore a finite number of pages you can serve at once before your server runs out of memory.&lt;/p&gt;

	&lt;p&gt;With our budget based on whatever is left over after beer, we need to get best performance we can out of the resources available. As the goal is to serve as many pages as quickly as possible, there are several approaches we can take:&lt;/p&gt;

	&lt;ol&gt;
		&lt;li&gt;Reducing the amount of memory needed by each Apache process&lt;/li&gt;
		&lt;li&gt;Reducing the amount of time each process is needed&lt;/li&gt;
		&lt;li&gt;Reducing the number of requests made to the server&lt;/li&gt;
	&lt;/ol&gt;

	&lt;p&gt;Yahoo! have published some information on what they call &lt;a href="http://developer.yahoo.com/performance/"&gt;Exceptional Performance&lt;/a&gt;, which is well worth reading, and compliments many of my examples here. The Yahoo! guidelines very much look at things from a user perspective, which is always important.&lt;/p&gt;

	&lt;h3&gt;Server tweaking&lt;/h3&gt;

	&lt;p&gt;If you&amp;#8217;re in the position of being able to change your server configuration (our set-up gives us root access to what is effectively a virtual machine) there are some basic steps you can take to maximise the available memory and reduce the memory footprint. Without getting too boring and technical (whole books have been written on this) there are a couple of things to watch out for.&lt;/p&gt;

	&lt;p&gt;Firstly, check what processes you have running that you might not need. Every megabyte of memory that you free up might equate to several thousand extra requests being served each day, so take a look at &lt;code&gt;top&lt;/code&gt; and see what&amp;#8217;s using up your resources. Quite often a machine configured as a web server will have some kind of mail server running by default. If your site doesn&amp;#8217;t use mail (ours doesn&amp;#8217;t) make sure it&amp;#8217;s shut down and not using resources.&lt;/p&gt;

	&lt;p&gt;Secondly, have a look at your Apache configuration and particularly what modules are loaded. The method for doing this varies between versions of Apache, but again, every module loaded increases the amount of memory that each Apache process requires and therefore limits the number of simultaneous requests you can deal with.&lt;/p&gt;

	&lt;p&gt;The final thing to check is that Apache isn&amp;#8217;t configured to start more servers than you have memory for. This is usually done by setting the &lt;code&gt;MaxClients&lt;/code&gt; directive. When that limit is reached, your site is going to stop responding to further requests. However, if all else goes well that threshold won&amp;#8217;t be reached, and if it does it will at least stop the weight of the traffic taking the entire server down to a point where you can&amp;#8217;t even log in to sort it out.&lt;/p&gt;

	&lt;p&gt;Those are the main tidbits I&amp;#8217;ve found useful for this site, although it&amp;#8217;s worth repeating that entire books have been written on this subject alone.&lt;/p&gt;

	&lt;h3&gt;Caching&lt;/h3&gt;

	&lt;p&gt;Although the site is generated with &lt;span class="caps"&gt;PHP&lt;/span&gt; and MySQL, the majority of pages served don&amp;#8217;t come from the database. The process of compiling a page on-the-fly involves quite a few trips to the database for content, templates, configuration settings and so on, and so can be slow and require a lot of &lt;span class="caps"&gt;CPU&lt;/span&gt;. Unless a new article or comment is published, the site doesn&amp;#8217;t actually change between requests and so it makes sense to generate each page once, save it to a file and then just serve all following requests from that file.&lt;/p&gt;

	&lt;p&gt;We use &lt;a href="http://sourceforge.net/projects/quickcache"&gt;QuickCache&lt;/a&gt; (or rather a plugin based on it) for this. The plugin integrates with our publishing system (Textpattern) to make sure the cache is cleared when something on the site changes. A similar plugin called &lt;a href="http://wordpress.org/extend/plugins/wp-cache/"&gt;WP-Cache&lt;/a&gt; is available for WordPress, but of course this could be done any number of ways, and with any back-end technology.&lt;/p&gt;

	&lt;p&gt;The important principal here is to reduce the time it takes to serve a page by compiling the page once and serving that cached result to subsequent visitors. Keep away from your database if you can.&lt;/p&gt;

	&lt;h3&gt;Outsource your feeds&lt;/h3&gt;

	&lt;p&gt;We get around 36,000 requests for our feed each day. That really only works out at about 7,000 subscribers averaging five-and-a-bit requests a day, but it&amp;#8217;s still 36,000 requests we could easily do without. Each request uses resources and particularly during December, all those requests can add up. &lt;/p&gt;

	&lt;p&gt;The simple solution here was to switch our feed over to using &lt;a href="http://www.feedburner.com/"&gt;FeedBurner&lt;/a&gt;. We publish the address of the FeedBurner version of our feed here, so those 36,000 requests a day hit FeedBurner&amp;#8217;s servers rather than ours. In addition, we get pretty graphs showing how the subscriber-base is building.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/24/graph2.png" width="482" height="122" alt="Our FeedBurner traffic graph" /&gt;&lt;/p&gt;

	&lt;h3&gt;Off-load big files&lt;/h3&gt;

	&lt;p&gt;Larger files like images or downloads pose a problem not in bandwidth, but in the time it takes them to transfer. A typical page request is very quick, a few seconds at the most, resulting in the connection being freed up promptly. Anything that keeps a connection open for a long time is going to start killing performance very quickly.&lt;/p&gt;

	&lt;p&gt;This year, we started serving most of the images for articles from a subdomain &amp;#8211; media.24ways.org. Rather than pointing to our own server, this subdomain points to an &lt;a href="http://aws.amazon.com/s3"&gt;Amazon S3&lt;/a&gt; account where the files are held. It&amp;#8217;s easy to pigeon-hole S3 as merely an online backup solution, and whilst not a fully fledged &lt;a href="http://en.wikipedia.org/wiki/Content_Delivery_Network"&gt;&lt;span class="caps"&gt;CDN&lt;/span&gt;&lt;/a&gt;, S3 works very nicely for serving larger media files. The roughly 20GB of files served this month have cost around $5 in Amazon S3 charges. That&amp;#8217;s so affordable it may not be worth even taking the files back off S3 once December has passed.&lt;/p&gt;

	&lt;p&gt;I found this article on &lt;a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1073&amp;#38;ref=featured"&gt;Scalable Media Hosting with Amazon S3&lt;/a&gt; to be really useful in getting started. I upload the files via a Firefox plugin (mentioned in the article) and then edit the &lt;acronym title="Access Control List"&gt;&lt;span class="caps"&gt;ACL&lt;/span&gt;&lt;/acronym&gt; to allow public access to the files. The way S3 enables you to point &lt;span class="caps"&gt;DNS&lt;/span&gt; directly at it means that you&amp;#8217;re not tied to always using the service, and that it can be transparent to your users.&lt;/p&gt;

	&lt;p&gt;If your site uses photographs, consider uploading them to a service like &lt;a href="http://flickr.com/"&gt;Flickr&lt;/a&gt; and serving them directly from there. Many photo sharing sites are happy for you to link to images in this way, but do check the acceptable use policies in case you need to provide a credit or link back.&lt;/p&gt;

	&lt;h3&gt;Off-load small files&lt;/h3&gt;

	&lt;p&gt;You&amp;#8217;ll have noticed the pattern by now &amp;#8211; get rid of as much traffic as possible. When an article has a lot of comments and each of those comments has an avatar along with it, a great many requests are needed to fetch each of those images. In 2006 we started using &lt;a href="http://gravatar.com/"&gt;Gravatar&lt;/a&gt; for avatars, but their servers were slow and were holding up page loads. To get around this we started caching the images on our server, but along with that came the burden of furnishing all the image requests.&lt;/p&gt;

	&lt;p&gt;Earlier this year Gravatar changed hands and is now run by the same team behind &lt;a href="http://www.wordpress.com/"&gt;WordPress.com&lt;/a&gt;. Those guys clearly know what they&amp;#8217;re doing when it comes to high performance, so this year we went back to serving avatars directly from them.&lt;/p&gt;

	&lt;p&gt;If your site uses avatars, it really makes sense to use a service like Gravatar where your users probably already have an account, and where the image requests are going to be dealt with for you. &lt;/p&gt;

	&lt;h3&gt;Know what you&amp;#8217;re paying for&lt;/h3&gt;

	&lt;p&gt;The server account we use for 24 ways was opened in November 2005. When we first hit the front page of Digg in December of that year, we upgraded the server with a bit more memory, but other than that we were still running on that 2005 spec for two years. Of course, the world of technology has moved on in those years, prices have dropped and specs have improved. For the same amount we were paying for that 2005 spec server, we could have an account with twice the &lt;span class="caps"&gt;CPU&lt;/span&gt;, memory and disk space.&lt;/p&gt;

	&lt;p&gt;So in November of this year I took out a new account and transferred the site from the old server to the new. In that single step we were prepared for dealing with twice the amount of traffic, and because of a special offer at the time I didn&amp;#8217;t even have to pay the setup cost on the new server. So it really pays to know what you&amp;#8217;re paying for and keep an eye out of ways you can make improvements without needing to spend more money.&lt;/p&gt;

	&lt;h3&gt;Further steps&lt;/h3&gt;

	&lt;p&gt;There&amp;#8217;s nearly always more that can be done. For example, there are some media files (particularly for older articles) that are not on S3. We also serve our &lt;span class="caps"&gt;CSS&lt;/span&gt; directly and it&amp;#8217;s not &lt;a href="http://24ways.org/2007/minification-a-christmas-diet"&gt;minified or compressed&lt;/a&gt;. But by tackling the big problems first we&amp;#8217;ve managed to reduce load on the server and at the same time make sure that the load being placed on the server can be dealt with in the most frugal way.&lt;/p&gt;

	&lt;p&gt;Over the last 24 days we&amp;#8217;ve served up articles to more than 350,000 visitors without breaking a sweat. On a busy day, that&amp;#8217;s been nearly 20,000 visitors in just 24 hours. While in the grand scheme of things that&amp;#8217;s not a huge amount of traffic, it can be a lot if you&amp;#8217;re not prepared for it. However, with a little planning for the peaks you can help ensure that when the traffic arrives you&amp;#8217;re ready to capitalise on it.&lt;/p&gt;

	&lt;p&gt;Of course, people only visit 24 ways for the wealth of knowledge and experience that&amp;#8217;s tied up in the articles here. Therefore I&amp;#8217;d like to take the opportunity to thank all our authors this year who have given their time as a gift to the community, and to wish you all a very happy Christmas.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://feeds.feedburner.com/~a/24ways?a=f6j2Yq"&gt;&lt;img src="http://feeds.feedburner.com/~a/24ways?i=f6j2Yq" border="0"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/24ways?a=BBid1I"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=BBid1I" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=KelqBI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=KelqBI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=A2rtbi"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=A2rtbi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=ovDvSI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=ovDvSI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/24ways/~4/258873386" height="1" width="1"/&gt;</content>
		<summary type="html">
&lt;p&gt;&lt;a href="http://allinthehead.com/"&gt;Drew McLellan&lt;/a&gt; rounds off our series with a look at the challenges facing a site that needs to cope with occasional peaks in traffic without spending out on high-performance hosting that&amp;#8217;s not needed for the majority of the time. Come behind the scenes at 24 ways and see how we keep the site online through the month of December each year. Happy Christmas to all, and to all a good-night!&lt;/p&gt;
</summary>
<feedburner:origLink>http://24ways.org/2007/performance-on-a-shoe-string</feedburner:origLink></entry>
<entry>
		<author>
			<name>Brian Oberkirch</name>
		</author>
		<published>2007-12-23T00:00:21Z</published>
		<updated>2007-12-23T00:00:21Z</updated>
		<title type="html">A Gift Idea For Your Users:  Respect, Yo</title>
		<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/24ways/~3/258873388/a-gift-idea-for-your-users-respect-yo" />
		<id>tag:24ways.org,2007-12-22:7e1577ceca7ea22e28af5c35ee62927a/ef339c8b3150ca4b5efaf5e9d6ab679f</id>
		<category term="design" />
		
		<content type="html">
&lt;p&gt;If, indeed, it &lt;em&gt;is&lt;/em&gt; the thought that counts, maybe we should pledge to make more thoughtful design decisions.  In addition to wowing people who use the Web sites we build with novel features, nuanced aesthetics and the new new thing, maybe we should also thread some subtle things throughout our work that let folks know:  &lt;em&gt;hey, I&amp;#8217;m feeling ya.  We&amp;#8217;re simpatico.  I hear you loud and clear.&lt;/em&gt;&lt;/p&gt;

	&lt;p&gt;It&amp;#8217;s not just holiday spirit that moves me to talk this way.  As good as people are, we need more than the horizon of karma to overcome that invisible demon, inertia.  Makers of the Web, respectful design practices aren&amp;#8217;t just the right thing, they are good for business.  Even if your site is the one and only place to get experience x, y or zed, you don&amp;#8217;t rub someone&amp;#8217;s face in it.  You keep it free flowing, you honor the possible back and forth of a healthy transaction, you are Johnny Appleseed with the humane design cues.  You make it clear that you are in it for the long haul.&lt;/p&gt;

	&lt;p&gt;A peek back:&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/23/feeling-lucky.png" width="419" height="60" alt="Google's I'm Feeling Lucky search box" /&gt;&lt;/p&gt;

	&lt;p&gt;Think back to what search (and  strategy) was like before Google launched a super clean page with &amp;#8220;I&amp;#8217;m Feeling Lucky&amp;#8221; button.  Aggregation was the order of the day (just go back and review all the &amp;#8216;strategic alliances&amp;#8217; that were announced daily.)  Yet the &lt;span class="caps"&gt;GOOG&lt;/span&gt; comes along with this zen layout (nope, we&amp;#8217;re not going to try to make you look at one of our media properties) and a bold, brash, teleport-me-straight-to-the-first-search-result button.  It could have been titled &amp;#8220;We&amp;#8217;re Feeling Cocky&amp;#8221;.  These were radical design decisions that reset how people thought about search services.  Oh, you mean I can just find what I want and get on with it?&lt;/p&gt;

	&lt;p&gt;It&amp;#8217;s maybe even more impressive today, after the &lt;span class="caps"&gt;GOOG&lt;/span&gt; has figured out how to monetize attention better than anyone.  &amp;#8220;I&amp;#8217;m Feeling Lucky&amp;#8221; is still there.  No doubt, &lt;a href="http://valleywag.com/tech/google/im-feeling-lucky-button-costs-google-110-million-per-year-324927.php"&gt;it costs the company millions&lt;/a&gt;.  But by leaving a little money on the table, they keep the basic bargain they started to strike in 1997.  We&amp;#8217;re going to get you where you want to go as quickly as possible.&lt;/p&gt;

	&lt;p&gt;Where are the places we might make the same kind of impact in our work?  Here are a few ideas:&lt;/p&gt;

	&lt;h3&gt;Respect People&amp;#8217;s Time&lt;/h3&gt;

	&lt;p&gt;As more services become more integrated with our lives, this will only become more important.  How can you make it clear that you respect the time a user has granted you?&lt;/p&gt;

	&lt;h4&gt;User-Oriented Defaults&lt;/h4&gt;

	&lt;p&gt;Default design may be the psionic tool in your belt.  Unseen, yet pow-er-ful.  Look at your defaults.  Who are they set up to benefit?  Are you depending on users not checking off boxes so you can feel ok about sending them email they really don&amp;#8217;t want?  Are you depending on users not checking off boxes so you tilt privacy values in ways most beneficial for your &lt;acronym title="Search Engine Result Pages"&gt;&lt;span class="caps"&gt;SERP&lt;/span&gt;&lt;/acronym&gt;s?  Are you making it a little too easy for 3rd party applications to run buckwild through your system?&lt;/p&gt;

	&lt;p&gt;There&amp;#8217;s being right and then there&amp;#8217;s being awesome.  Design to the spirit of the agreement and not the letter.&lt;/p&gt;

	&lt;p&gt;See this?&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/23/prefs.png" width="405" height="508" alt="FaceBook's communication preferences" /&gt;&lt;/p&gt;

	&lt;p&gt;Make sure that&amp;#8217;s &lt;em&gt;really&lt;/em&gt; the experience you think people want.  Whenever I see a service that defaults to &lt;strong&gt;not&lt;/strong&gt; opting me in their newsletter just because I bought a t shirt from them, you can be sure that I trust them that much more.  And they are likely to see me again.&lt;/p&gt;

	&lt;h4&gt;Reduce, Reuse&lt;/h4&gt;

	&lt;p&gt;It&amp;#8217;s likely that people using your service will have data and profile credentials elsewhere.  You should really think hard about how you can let them repurpose some of that work within your system.  Can you let them reduce the number of logins/passwords they have to manage by supporting &lt;a href="http://openid.net/"&gt;OpenID&lt;/a&gt;?  Can you let them reuse profile information from another service by &lt;a href="http://www.brianoberkirch.com/2007/08/02/deeelightful-making-profile-import-a-snap/"&gt;slurping in (or even subscribing) to hCards&lt;/a&gt;?  Can you give them a leg up by reusing &lt;a href="http://microformats.org/wiki/hcard-xfn-supporting-friends-lists"&gt;a friends list they make available to you&lt;/a&gt;?  (Note:  please avoid &lt;a href="http://adactio.com/journal/1357"&gt;the anti-pattern of inviting your user to upload all her credential data&lt;/a&gt; from 3rd party sites into your system.)&lt;/p&gt;

	&lt;p&gt;This is a much larger issue, and if you&amp;#8217;d like to get involved, &lt;a href="http://microformats.org/wiki/social-network-portability"&gt;have a look at the wiki&lt;/a&gt;, and dive in.&lt;/p&gt;

	&lt;h4&gt;Make it simple to leave&lt;/h4&gt;

	&lt;p&gt;Oh, this drives me bonkers.  Again, the more simple you make it to increase or decrease involvement in your site, or to just opt-out altogether, the better.  This example from &lt;a href="http://www.basecamphq.com"&gt;Basecamp&lt;/a&gt; is instructive:&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/23/acct-settings.png" alt="Basecamp account options" height="307" width="715" /&gt;&lt;/p&gt;

	&lt;p&gt;At a glance, I can see what the implications are of choosing a different type of account.  I can also move between account levels with one click.  Finally, I can cancel the service easily.  No hoop jumping.  Also, it should be simple for users to take data with them or delete it.&lt;/p&gt;

	&lt;h3&gt;Let Them Have Fun&lt;/h3&gt;

	&lt;p&gt;Don&amp;#8217;t overlook opportunities for pleasure.  Even the most mundane tasks can be made more enjoyable.  Check out one of my favorite pieces of interaction design from this past year:&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/23/news-feed-eq.png" alt="FaceBook's slider settings" height="248" width="638" /&gt;&lt;/p&gt;

	&lt;p&gt;Holy knob fiddling, Batman!  What a great way to get people to play with preference settings:  an equalizer metaphor.  Those of a certain age will recall how fun it was to make patterns with your uncle&amp;#8217;s stereo EQ.  I think this is a delightful way to encourage people to optimize their own experience of the news feed feature.  Given the killer nature of this feature, it was important for Facebook to make it easy to fine tune.&lt;/p&gt;

	&lt;p&gt;I&amp;#8217;d also point you to &lt;a href="http://blogs.atlassian.com/rebelutionary/archives/2007/09/flickr_supports_talk_like_a_pirate_day_1.html"&gt;Flickr&amp;#8217;s Talk Like A Pirate Day Easter egg&lt;/a&gt; as another example of design that delights.  What a huge amount of work for a one-off, totally optional way to experience the site.  And what fun.  And how true to its brand persona.  Brill.&lt;/p&gt;

	&lt;h3&gt;Anti-hype&lt;/h3&gt;

	&lt;p&gt;Don&amp;#8217;t talk so much.  Rather, ship and sample.  Release code, tell the right users.  See what happens.   Make changes.  Extend the circle a bit by showing some other folks.  Repeat.&lt;/p&gt;

	&lt;p&gt;The more you hype coming features, the more you talk about what isn&amp;#8217;t yet, the more you build unrealistic expectations.  Your genius can&amp;#8217;t possibly match our collective dreaming.  Disappointment is inevitable.  Yet, if you craft the right melody and make it simple for people to hum your tune, it will spread.  Give it time.  Listen.&lt;/p&gt;

	&lt;h3&gt;Speak the Language of the Tribe&lt;/h3&gt;

	&lt;p&gt;It&amp;#8217;s respectful to speak in a human way.  Not that you have to get all zOMGWTFBBQ!&lt;img src="http://24ways.org/1" alt="" /&gt; in your messaging.  People respond when you speak to them in a way that sounds natural.  Natural will, of course, vary according to context.  Again, listen in and people will signal the speech that works in that group for those tasks.  Reveal those cues in your interface work and you&amp;#8217;ll have powerful proof that actual people are working on your Web site.&lt;/p&gt;

	&lt;p&gt;This example of &lt;a href="http://www.pownce.com"&gt;Pownce&lt;/a&gt;&amp;#8216;s gender selector is the kind of thing I&amp;#8217;m talking about:&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/23/pownce-lingo.png" alt="Pownce's gender options" width="171" height="221" /&gt;&lt;/p&gt;

	&lt;p&gt;Now, this doesn&amp;#8217;t mean you should mimic the lingo on some cool kidz site.  Your service doesn&amp;#8217;t need to have a massage when it&amp;#8217;s down.  Think about what works for you and your tribe.  Excellent advice here from Feedburner&amp;#8217;s Dick Costolo on &lt;a href="http://www.burningdoor.com/askthewizard/2007/11/have_a_company_voice.html"&gt;finding a voice for your service&lt;/a&gt;.  Also, &lt;a href="http://www.muledesign.com"&gt;Mule Design&amp;#8217;s&lt;/a&gt; Erika Hall has an &lt;a href="http://weblog.muledesign.com/2007/10/copy_is_interface.php"&gt;excellent talk on improving your word fu&lt;/a&gt;.&lt;/p&gt;

	&lt;h3&gt;Pass the mic, yo&lt;/h3&gt;

	&lt;p&gt;Here is a crazy idea:  you could ask your users what they want.  Maybe you could even use this input to figure out what they &lt;em&gt;really&lt;/em&gt; want.  Tools abound.  Comments, wikis, forums, surveys.  Embed the sexy new &lt;a href="http://getsatisfaction.com"&gt;Get Satisfaction&lt;/a&gt; widget and have a dynamic &lt;span class="caps"&gt;FAQ&lt;/span&gt; running.&lt;/p&gt;

	&lt;p&gt;The point is that you make it clear to people that they have a means of shaping the service with you.  And you must showcase in some way that you are listening, evaluating and taking action against some of that user input.&lt;/p&gt;

	&lt;p&gt;You get my drift.  There are any number of ways we can show respect to those who gift us with their time, data, feedback, attention, evangelism, money.  Big things are in the offing.  I can feel the love already.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://feeds.feedburner.com/~a/24ways?a=HFrAk9"&gt;&lt;img src="http://feeds.feedburner.com/~a/24ways?i=HFrAk9" border="0"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/24ways?a=w6wUcI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=w6wUcI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=qdzkYI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=qdzkYI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=nRvJCi"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=nRvJCi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=JithLI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=JithLI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/24ways/~4/258873388" height="1" width="1"/&gt;</content>
		<summary type="html">
&lt;p&gt;&lt;a href="http://brianoberkirch.com"&gt;Brian Oberkirch&lt;/a&gt; gives a little respect back to his users, and suggests some way in which you might be able to do the same. Like a plate full of brussel sprouts, your users deserve a little respect, you can&amp;#8217;t just rush in there. Not that I consider my users to be in any way like stupid old brussel sprouts. Oh no. What a mess. &lt;/p&gt;
</summary>
<feedburner:origLink>http://24ways.org/2007/a-gift-idea-for-your-users-respect-yo</feedburner:origLink></entry>
<entry>
		<author>
			<name>Molly E. Holzschlag</name>
		</author>
		<published>2007-12-22T00:00:09Z</published>
		<updated>2007-12-22T00:00:30Z</updated>
		<title type="html">How Media Studies Can Massage Your Message</title>
		<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/24ways/~3/258873389/how-media-studies-can-massage-your-message" />
		<id>tag:24ways.org,2007-12-21:7e1577ceca7ea22e28af5c35ee62927a/04fc808fa18b7b31adb6d9437b610b1d</id>
		<category term="craft" />
		
		<content type="html">
&lt;p&gt;A young web designer once told his teacher &amp;#8216;just get to the meat already.&amp;#8217; He was frustrated with what he called the &amp;#8216;history lessons and name-dropping&amp;#8217; aspects of his formal college course. He just wanted to learn how to build Web sites, not examine the reasons why.&lt;/p&gt;

	&lt;p&gt;Technique and theory are both integrated and necessary portions of a strong education. The student&amp;#8217;s perspective has direct value, but also holds a distinct sorrow: Knowing the how without the why creates a serious problem. Without these surrounding insights we cannot tap into the influence of the history and evolved knowledge that came before. We cannot properly analyze, criticize, evaluate and innovate beyond the scope of technique.&lt;/p&gt;

	&lt;p&gt;History holds the key to transcending former mistakes. Philosophy encourages us to look at different points of view. Studying media and social history empowers us as Web workers by bringing together myriad aspects of humanity in direct relation to the environment of society and technology. Having an understanding of media, communities, communication arts as well as logic, language and computer savvy are all core skills of the best of web designers in today&amp;#8217;s medium.&lt;/p&gt;

	&lt;h3&gt;Controlling the Message&lt;/h3&gt;

	&lt;blockquote&gt;
		&lt;p&gt;&amp;#8216;The computer can&amp;#8217;t tell you the emotional story. It can give you the exact mathematical design, but what&amp;#8217;s missing is the eyebrows.&amp;#8217; &amp;#8211; Frank Zappa&lt;/p&gt;
	&lt;/blockquote&gt;

	&lt;p&gt;Media is meant to express an idea. The great media theorist Marshall McLuhan suggests that not only is media interesting because it&amp;#8217;s about the expression of ideas, but that the media itself actually shapes the way a given idea is perceived.  This is what McLuhan meant when he uttered those famous words: &amp;#8216;The medium is the message.&amp;#8217;&lt;/p&gt;

	&lt;p&gt;If instead of actually serving a steak to a vegetarian friend, what might a painting of the steak mean instead? Or a sculpture of a cow? Depending upon the form of media in question, the message is altered.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/22/cow.jpg" width="450" height="300" alt="A cow and a steak" /&gt;&lt;span class="caption"&gt;Figure 1&lt;/span&gt;&lt;/p&gt;

	&lt;p&gt;Must we know the history of cows to appreciate the steak on our plate? Perhaps not, but if we begin to examine how that meat came to be on the plate, the social, cultural and ideological associations of that cow, we begin to understand the complexity of both medium and message. A piece of steak on my plate makes me happy. A vegetarian friend from India might disagree and even find that that serving her a steak was very insensitive.&lt;/p&gt;

	&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; Getting the message right involves understanding that message in order to direct it to your audience accordingly.&lt;/p&gt;

	&lt;h3&gt;A Separate Piece&lt;/h3&gt;

	&lt;p&gt;If we revisit the student who only wants technique, while he might become extremely adept at the rendering of an idea, without an understanding of the medium, how is he to have greater control over how that idea is perceived?  Ultimately, his creativity is limited and his perspective narrowed, and the teacher has done her student a disservice without challenging him, particularly in a scholastic environment, to think in liberal, creative and ultimately innovative terms.&lt;/p&gt;

	&lt;p&gt;For many years, web pundits including myself have promoted the idea of separation as a core concept within creating effective front-end media for the Web. By this, we&amp;#8217;ve meant literal separation of the technologies and documents: Markup for content; &lt;span class="caps"&gt;CSS&lt;/span&gt; for presentation; &lt;span class="caps"&gt;DOM&lt;/span&gt; Scripting for behavior. While the message of separation was an important part of understanding and teaching best practices for manageable, scalable sites, that separation is really just a separation of pieces, not of entire disciplines.&lt;/p&gt;

	&lt;p&gt;For in fact, the medium of the Web is an integrated one. That means each part of the desired message must be supported by the media silos within a given site.  The visual designer must study the color, space, shape and placement of visual objects (including type) into a meaningful expression. The underlying markup is ideally written as semantically as possible, promote the meaning of the content it describes. Any interaction and functionality must make the process of the medium support, not take away from, the meaning of the site or Web application.  &lt;/p&gt;

	&lt;h3&gt;Examination: The Glass Bead Game&lt;/h3&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/22/glassbead.jpg" width="264" height="405" alt="Two screenshots of CoreWave's historic 'Glass Bead Game." /&gt;&lt;span class="caption"&gt;Figure 2&lt;/span&gt;&lt;/p&gt;

	&lt;p&gt;Figure 2 shows two screenshots of CoreWave&amp;#8217;s historic &amp;#8216;Glass Bead Game.&amp;#8217; Fashioned after Herman Hesse&amp;#8217;s novel of the same name, the game was an exploration of how ideas are connected to other ideas via multiple forms of media. It was created for the Web in 1996 using server-side randomization with .htmlx files in order to allow players to see how random associations are in fact not random at all.&lt;/p&gt;

	&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; We can use the medium itself to explore creative ideas, to link us from one idea to the next, and to help us better express those ideas to our audiences.&lt;/p&gt;

	&lt;h3&gt;Computers and Human Interaction&lt;/h3&gt;

	&lt;p&gt;Since our medium involves computers and human interaction, it does us well to look to foundations of computers and reason. Not long ago I was chatting with Jared Spool on IM about this and that, and he asked me &amp;#8216;So how do you feel about that?&amp;#8217; This caused me no end of laughter and I instantly quipped back &amp;#8216;It&amp;#8217;s okay by me &lt;span class="caps"&gt;ELIZA&lt;/span&gt;.&amp;#8217; We both enjoyed the joke, but then I tried to share it with another dare I say younger colleague, and the reference was lost.&lt;/p&gt;

	&lt;p&gt;Raise your hand if you got the reference! Some of you will, but many people who come to the Web medium do not get the benefit of such historical references because we are not formally educated in them. Joseph Weizenbaum created the &lt;span class="caps"&gt;ELIZA&lt;/span&gt; program, which emulates a Rogerian Therapist, in 1966. It was an early study of computers and natural human language. I was a little over 2 years old, how about you?&lt;/p&gt;

	&lt;h3&gt;Conversation with Eliza&lt;/h3&gt;

	&lt;p&gt;There are fortunately a number of &lt;span class="caps"&gt;ELIZA&lt;/span&gt; emulators on the Web. I found one at &lt;a href="http://www.chayden.net/eliza/Eliza.html"&gt;http://www.chayden.net/eliza/Eliza.html&lt;/a&gt; that actually contains the source code (in Java) that makes up the &lt;span class="caps"&gt;ELIZA&lt;/span&gt; script.&lt;/p&gt;

	&lt;p&gt;Figure 3 shows a screen shot of the interaction. &lt;span class="caps"&gt;ELIZA&lt;/span&gt; first welcomes me, says &amp;#8216;Hello, How do you do. Please state your problem&amp;#8217; and we continue in a short loop of conversation, the computer using cues from my answers to create new questions and leading fragments of conversation.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/22/eliza-web.jpg" width="607" height="218" alt="Chat log between a human and ELIZA" /&gt;&lt;span class="caption"&gt;Figure 3&lt;/span&gt;&lt;/p&gt;

	&lt;p&gt;Albeit a very limited demonstration of how humans could interact with a computer in 1966, it&amp;#8217;s amusing to play with now and compare it to something as richly interactive as the Microsoft Surface (Figure 4). Here, we see clear Lucite blocks that display projected video. Each side of the block has a different view of the video, so not only does one have to match up the images as they are moving, but do so in the proper directionality.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/22/surface.jpg" width="500" height="375" alt="Microsoft Surface" /&gt;&lt;span class="caption"&gt;Figure 4&lt;/span&gt;&lt;/p&gt;

	&lt;p&gt;&lt;strong&gt;Takeway:&lt;/strong&gt; the better we know our environment, the more we can alter it to emulate, expand and even supersede our message.&lt;/p&gt;

	&lt;h3&gt;Leveraging Holiday Cheer&lt;/h3&gt;

	&lt;p&gt;Since most of us at least have a few days off for the holidays now that Christmas is upon us, now&amp;#8217;s a perfect time to reflect on ones&amp;#8217; environment and examine the messages within it. Convince your spouse to find you a few audio books for stocking stuffers. Find interactive games to play with your kids and observe them, and yourself, during the interaction. Pour a nice egg-nog and sit down with a copy of Marshall McLuhan&amp;#8217;s &amp;#8216;The Medium is the Massage.&amp;#8217; Leverage that holiday cheer and here&amp;#8217;s to a prosperous, interactive new year.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://feeds.feedburner.com/~a/24ways?a=F46x6q"&gt;&lt;img src="http://feeds.feedburner.com/~a/24ways?i=F46x6q" border="0"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/24ways?a=O62vqI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=O62vqI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=kHiGtI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=kHiGtI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=rPMgki"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=rPMgki" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=ejvtwI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=ejvtwI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/24ways/~4/258873389" height="1" width="1"/&gt;</content>
		<summary type="html">
&lt;p&gt;&lt;a href="http://molly.com/"&gt;Molly E. Holzschlag&lt;/a&gt; discusses the importance of the medium to the message, and how a better understanding of the theory and just technique can help in developing our craft. After all, a piece of chocolate is a piece of chocolate. But press it into a coin and wrap it in golden foil, and that&amp;#8217;s Christmas right there people.&lt;/p&gt;
</summary>
<feedburner:origLink>http://24ways.org/2007/how-media-studies-can-massage-your-message</feedburner:origLink></entry>
<entry>
		<author>
			<name>Brian Fling</name>
		</author>
		<published>2007-12-21T00:00:12Z</published>
		<updated>2007-12-21T00:00:12Z</updated>
		<title type="html">Mobile 2.0</title>
		<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/24ways/~3/258873390/mobile-2.0" />
		<id>tag:24ways.org,2007-12-20:7e1577ceca7ea22e28af5c35ee62927a/83be91b2dccad1f0fc58678b9ddebc73</id>
		<category term="mobile" />
		
		<content type="html">
&lt;h3&gt;Thinking 2.0&lt;/h3&gt;

	&lt;p&gt;As web geeks, we have a thick skin towards jargon. We all know that &amp;#8220;Web 2.0&amp;#8221; has been done to death. At Blue Flavor we even have a jargon bucket to penalize those who utter such painfully overused jargon with a cash deposit. But Web 2.0 is a term that has lodged itself into the conscience of the masses. This is actually a good thing.&lt;/p&gt;

	&lt;p&gt;The 2.0 suffix was able to succinctly summarize all that was wrong with the Web during the dot-com era as well as the next evolution of an evolving media. While the core technologies actually stayed basically the same, the principles, concepts, interactions and contexts were radically different.&lt;/p&gt;

	&lt;p&gt;With that in mind, this Christmas I want to introduce to you the concept of Mobile 2.0. While not exactly a new concept in the &lt;a href="http://www.readwriteweb.com/archives/understanding_mobile_2.php"&gt;mobile community&lt;/a&gt;, it is relatively unknown in the web community. And since the foundation of Mobile 2.0 is the web, I figured it was about time for you to get to know each other.&lt;/p&gt;

	&lt;h3&gt;It&amp;#8217;s the Carriers&amp;#8217; world. We just live in it.&lt;/h3&gt;

	&lt;p&gt;Before getting into Mobile 2.0, I thought first I should introduce you to its older brother. You know the kind, the kid with emotional problems that likes to beat up on you and your friends for absolutely no reason. That is the mobile of today.&lt;/p&gt;

	&lt;p&gt;The mobile ecosystem is a very complicated space often and incorrectly compared to the Web. If the Web was a freewheeling hippie &amp;#8212; believing in freedom of information and the unity of man through communities &amp;#8212; then Mobile is the cutthroat capitalist &amp;#8212; out to pillage and plunder for the sake of the almighty dollar. Where the Web is relatively easy to publish to and ultimately make a buck, Mobile is wrought with layers of complexity, politics and obstacles. &lt;/p&gt;

	&lt;p&gt;I can think of no better way to summarize these challenges than the testimony of Jason Devitt to the United States Congress in what is now being referred to as the &amp;#8220;iPhone Hearing.&amp;#8221; Jason is the co-founder and &lt;span class="caps"&gt;CEO&lt;/span&gt; of &lt;a href="http://skydeck.com/"&gt;SkyDeck&lt;/a&gt; a new wireless startup and former &lt;span class="caps"&gt;CEO&lt;/span&gt; of Vindigo an early pioneer in mobile content.&lt;/p&gt;

	&lt;p&gt;&lt;object type="application/x-shockwave-flash" data="http://www.youtube.com/v/bzIqhOI5J2w&amp;rel=1" width="425" height="355"&gt;&lt;param name="movie" value="http://www.youtube.com/v/bzIqhOI5J2w&amp;rel=1" /&gt;&lt;/object&gt;&lt;/p&gt;

	&lt;p&gt;As Jason points out, the mobile ecosystem is a closed door environment controlled by the carriers, forcing the independent publisher to compete or succumb to the will of corporate behemoths.&lt;/p&gt;

	&lt;p&gt;But that is all about to change.&lt;/p&gt;

	&lt;h3&gt;Introducing Mobile 2.0&lt;/h3&gt;

	&lt;p&gt;Mobile 2.0 is term used by the mobile community to describe the current revolution happening in mobile. It describes the convergence of mobile and web services, adding portability, ubiquitous connectivity and location-aware services to add physical context to information found on the Web.&lt;/p&gt;

	&lt;p&gt;It&amp;#8217;s an important term that looks toward the future. Allowing us to imagine the possibilities that mobile technology has long promised but has yet to deliver. It imagines a world where developers can publish mobile content without the current constraints of the mobile ecosystem.&lt;/p&gt;

	&lt;p&gt;Like the transition from Web 1.0 to 2.0, it signifies the shift away from corporate or brand-centered experiences to user-centered experiences. A focus on richer interactions, driven by user goals. Moving away from proprietary technologies to more open and standard ones, more akin to the Web. And most importantly (from our perspective as web geeks) a shift away from kludgy one-off mobile applications toward using the Web as a platform for content and services.&lt;/p&gt;

	&lt;p&gt;This means the world of the Web and the world of Mobile are coming together faster than you can say &lt;span class="caps"&gt;ARPU&lt;/span&gt; (Average Revenue Per User, a staple mobile term to you webbies). And this couldn&amp;#8217;t come at a better time. The importance of understanding and addressing user context is quickly becoming a crucial consideration to every interactive experience as the number of ways we access information on the Web increases.&lt;/p&gt;

	&lt;p&gt;Mobile enables the power of the Web, the collective information of millions of people, inherit payment channels and access to just about every other mass media to literally be overlaid on top of the physical world, in context to the person viewing it. &lt;/p&gt;

	&lt;p&gt;Anyone who can&amp;#8217;t imagine how the influence of mobile technology can&amp;#8217;t transform how we perform even the simplest of daily tasks needs to get away from their desktop and see the new evolution of information.&lt;/p&gt;

	&lt;h3&gt;The Instigators&lt;/h3&gt;

	&lt;p&gt;But what will make Mobile 2.0 move from idillic concept to a hardened market reality in 2008 will be four key technologies. Its my guess that you know each them already.&lt;/p&gt;

	&lt;h4&gt;1. Opera&lt;/h4&gt;

	&lt;p&gt;Opera is like the little train that could. They have been a driving force on moving the Web as we know it on to mobile handsets. Opera technology has proven itself to be highly adaptable, finding itself preloaded on over 40 million handsets, available on televisions sets through Nintendo Wii or via the Nintendo DS.&lt;/p&gt;

	&lt;h4&gt;2. WebKit&lt;/h4&gt;

	&lt;p&gt;Many were surprised when Apple chose to use &lt;span class="caps"&gt;KHTML&lt;/span&gt; instead of Gecko (the guts of Firefox) to power their Safari rendering engine. But WebKit has quickly evolved to be a powerful and flexible browser in the mobile context. WebKit has been in Nokia smartphones for a few years now, is the technology behind Mobile Safari in the iPhone and the iPod Touch and is the default web technology in Google&amp;#8217;s open mobile platform effort, Android.&lt;/p&gt;

	&lt;h4&gt;3. The iPhone&lt;/h4&gt;

	&lt;p&gt;The iPhone has finally brought the concepts and principles of Mobile 2.0 into the forefront of consumers minds and therefore developers&amp;#8217; minds as well. Over 500 web applications have been written specifically for the iPhone since its launch. It&amp;#8217;s completely unheard of to see so many applications built for the mobile context in such a short period of time.&lt;/p&gt;

	&lt;h4&gt;4. &lt;span class="caps"&gt;CSS&lt;/span&gt; &amp;#38; Javascript&lt;/h4&gt;

	&lt;p&gt;Web 2.0 could not exist without the rich interactions offered by &lt;span class="caps"&gt;CSS&lt;/span&gt; and Javascript, and Mobile 2.0 is no different. &lt;span class="caps"&gt;CSS&lt;/span&gt; and Javascript support across multiple phones historically has been, well&amp;#8230; to put it positively&amp;#8230; utter crap.&lt;/p&gt;

	&lt;p&gt;Javascript finally allows developers to create interesting interactions that support user goals and the mobile context. Specially, &lt;span class="caps"&gt;AJAX&lt;/span&gt; allows us to finally shed the days of bloated Java applications and focus on portable and flexible web applications. While &lt;span class="caps"&gt;CSS&lt;/span&gt; &amp;#8212; namely CSS3 &amp;#8212; allows us to create designs that are as beautiful as they are economical with bandwidth and load times.&lt;/p&gt;

	&lt;p&gt;With &lt;a href="http://www.getleaflets.com"&gt;Leaflets&lt;/a&gt;, a collection of iPhone optimized web apps we created, we heavily relied on CSS3 to cache and reuse design elements over and over, minimizing download times while providing an elegant and user-centered design.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/21/leaflets2.jpg" width="700" height="225" alt="Leaflets" /&gt;&lt;/p&gt;

	&lt;h3&gt;In Conclusion&lt;/h3&gt;

	&lt;p&gt;It is the combination of all these instigators that is significantly decreasing the bar to mobile publishing. The market as Jason Devitt describes it, will begin to fade into the background. And maybe the world of mobile will finally start looking more like the Web that we all know and love.&lt;/p&gt;

	&lt;p&gt;So after the merriment and celebration of the holiday is over and you look toward the new year to refresh and renew, I hope that you take a seriously consider the mobile medium. &lt;/p&gt;

	&lt;p&gt;By this time next year, it is predicted that one-third of humanity will be using mobile devices to access the Web.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://feeds.feedburner.com/~a/24ways?a=OOOiF4"&gt;&lt;img src="http://feeds.feedburner.com/~a/24ways?i=OOOiF4" border="0"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/24ways?a=cLJH0I"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=cLJH0I" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=r95q3I"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=r95q3I" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=xphsoi"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=xphsoi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=OqsImI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=OqsImI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/24ways/~4/258873390" height="1" width="1"/&gt;</content>
		<summary type="html">
&lt;p&gt;&lt;a href="http://blueflavor.com/pages/about/bios/brian_fling/"&gt;Brian Fling&lt;/a&gt; offers his insight into the state of the mobile web as we hurtle towards 2008. Just as Web 2.0 taught us to rethink our assumptions about the web, Mobile 2.0 looks to do the same. There&amp;#8217;s a little something to mull over as you munch your mince pies and move onto bottle of Sherry 2.0.&lt;/p&gt;
</summary>
<feedburner:origLink>http://24ways.org/2007/mobile-2.0</feedburner:origLink></entry>
<entry>
		<author>
			<name>Eric Meyer</name>
		</author>
		<published>2007-12-20T00:00:14Z</published>
		<updated>2007-12-20T13:05:10Z</updated>
		<title type="html">Diagnostic Styling</title>
		<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/24ways/~3/258873391/diagnostic-styling" />
		<id>tag:24ways.org,2007-12-19:7e1577ceca7ea22e28af5c35ee62927a/5440ad31516d681935aef095779570e1</id>
		<category term="css" />
		
		<content type="html">
&lt;p&gt;We&amp;#8217;re all used to using &lt;span class="caps"&gt;CSS&lt;/span&gt; to make our designs live and breathe, but there&amp;#8217;s another way to use &lt;span class="caps"&gt;CSS&lt;/span&gt;: to find out where our markup might be choking on missing accessibility features, targetless links, and just plain missing content.  &lt;/p&gt;

	&lt;p&gt;Note: the techniques discussed here mostly work in Firefox, Safari, and Opera, but not Internet Explorer.  I&amp;#8217;ll explain why that&amp;#8217;s not really a problem near the end of the article &amp;#8212; and no, the reason is &lt;em&gt;not&lt;/em&gt; &amp;#8220;everyone should just ignore IE anyway&amp;#8221;.&lt;/p&gt;

	&lt;h3&gt;Basic Diagnostics&lt;/h3&gt;

	&lt;p&gt;To pick a simple example, suppose you want to call out all holdover &lt;code&gt;font&lt;/code&gt; and &lt;code&gt;center&lt;/code&gt; elements in a site.  Simple: you just add the following to your styles.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;font, center {outline: 5px solid red;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;You could take it further and add in a nice lime background or some such, but big thick red outlines should suffice.  Now you&amp;#8217;ll be able to see the offenders wherever as you move through the site.  (Of course, if you do this on your public server, everyone else will see the outlines too.  So this is probably best done on a development server or local copy of the site.)&lt;/p&gt;

	&lt;p&gt;Not everyone may be familiar with &lt;a href="http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines"&gt;outlines&lt;/a&gt;, which were introduced in CSS2, so a word on those before we move on.  Outlines are much like borders, except outlines don&amp;#8217;t affect layout.  Eh?  Here&amp;#8217;s a comparison.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/20/fig1.png" width="243" height="109" alt="Figure 1" /&gt;&lt;/p&gt;

	&lt;p&gt;On the left, you have a border.  On the right, an outline.  The border takes up layout space, pushing other content around and generally being a nuisance.  The outline, on the other hand, just draws into quietly into place.  In most current browsers, it will overdraw any content already onscreen, and will be overdrawn by any content placed later &amp;#8212; which is why it overlaps the images above it, and is overlapped by those below it.&lt;/p&gt;

	&lt;p&gt;Okay, so we can outline deprecated elements like &lt;code&gt;font&lt;/code&gt; and &lt;code&gt;center&lt;/code&gt;.  Is that all?  Oh no.&lt;/p&gt;

	&lt;h3&gt;Attribution&lt;/h3&gt;

	&lt;p&gt;Let&amp;#8217;s suppose you also want to find any instances of inline style &amp;#8212; that is, use of the &lt;code&gt;style&lt;/code&gt; attribute on elements in the markup.  This is generally discouraged (outside of &lt;span class="caps"&gt;HTML&lt;/span&gt; e-mails, which I&amp;#8217;m not going to get anywhere near), as it&amp;#8217;s just another side of the same coin of using &lt;code&gt;font&lt;/code&gt;: baking the presentation into the document structure instead of putting it somewhere more manageable.  So:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;*[style], font, center {outline: 5px solid red;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;Adding that &lt;a href="http://www.w3.org/TR/CSS21/selector.html#attribute-selectors"&gt;attribute selector&lt;/a&gt; to the rule&amp;#8217;s grouped selector means that we&amp;#8217;ll now be outlining any element with a &lt;code&gt;style&lt;/code&gt; attribute.&lt;/p&gt;

	&lt;p&gt;There&amp;#8217;s a lot more that attribute selectors will let use diagnose.  For example, we can highlight any images that have empty &lt;code&gt;alt&lt;/code&gt; or &lt;code&gt;title&lt;/code&gt; text.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;img[alt=&amp;quot;&amp;quot;] {border: 3px dotted red;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img[title=&amp;quot;&amp;quot;] {outline: 3px dotted fuchsia;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;Now, you may wonder why one of these rules calls for a border, and the other for an outline.  That&amp;#8217;s because I want them to &amp;#8220;add together&amp;#8221; &amp;#8212; that is, if I have an image which possesses both &lt;code&gt;alt&lt;/code&gt; and &lt;code&gt;title&lt;/code&gt;, and the values of both are empty, then I want it to be doubly marked.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/20/fig2.png" width="311" height="86" alt="Figure 2" /&gt;&lt;/p&gt;

	&lt;p&gt;See how the middle image there has both red and fuchsia dots running around it?  (And am I the only one who sorely misses the &lt;em&gt;actual circular dots&lt;/em&gt; drawn by IE5/Mac?)  That&amp;#8217;s due to its markup, which we can see here in a fragment showing the whole table row.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;th scope=&amp;quot;row&amp;quot;&amp;gt;empty title&amp;lt;/th&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;td&amp;gt;&amp;lt;img src=&amp;quot;comic.gif&amp;quot; title=&amp;quot;&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;td&amp;gt;&amp;lt;img src=&amp;quot;comic.gif&amp;quot; title=&amp;quot;&amp;quot; alt=&amp;quot;&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;td&amp;gt;&amp;lt;img src=&amp;quot;comic.gif&amp;quot; title=&amp;quot;&amp;quot; alt=&amp;quot;comical&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;/tr&amp;gt;&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;Right, that&amp;#8217;s all well and good, but it misses a rather more serious situation: the selector &lt;code&gt;img[alt=&amp;#34;&amp;#34;]&lt;/code&gt; won&amp;#8217;t match an &lt;code&gt;img&lt;/code&gt; element that doesn&amp;#8217;t even have an &lt;code&gt;alt&lt;/code&gt; attribute.  How to tackle this problem?&lt;/p&gt;

	&lt;h3&gt;Not a Problem&lt;/h3&gt;

	&lt;p&gt;Well, if you want to select something based on a negative, you need a negative selector.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;img:not([alt]) {border: 5px solid red;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;This is really quite a break from the rest of &lt;span class="caps"&gt;CSS&lt;/span&gt; selection, which is all positive: &amp;#8220;select anything that has these characteristics&amp;#8221;.  With &lt;code&gt;:not()&lt;/code&gt;, we have the ability to say (in supporting browsers) &amp;#8220;select anything that &lt;em&gt;hasn&amp;#8217;t&lt;/em&gt; these characteristics&amp;#8221;.  In the above example, only &lt;code&gt;img&lt;/code&gt; elements that do &lt;code&gt;not&lt;/code&gt; have an &lt;code&gt;alt&lt;/code&gt; attribute will be selected.  So we expand our list of image-related rules to read:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;img[alt=&amp;quot;&amp;quot;] {border: 3px dotted red;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img[title=&amp;quot;&amp;quot;] {outline: 3px dotted fuchsia;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img:not([alt]) {border: 5px solid red;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img:not([title]) {outline: 5px solid fuchsia;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;With the following results:&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/20/fig3.png" width="306" height="191" alt="Figure 3" /&gt;&lt;/p&gt;

	&lt;p&gt;We could expand this general idea to pick up tables who lack a summary, or have an empty &lt;code&gt;summary&lt;/code&gt; attribute.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;table[summary=&amp;quot;&amp;quot;] {outline: 3px dotted red;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;table:not([summary]) {outline: 5px solid red;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;When it comes to selecting header cells that lack the proper scope, however, we have a trickier situation.  Finding headers with no &lt;code&gt;scope&lt;/code&gt; attribute is easy enough, but what about those that have a &lt;code&gt;scope&lt;/code&gt; attribute with an incorrect value?&lt;/p&gt;

	&lt;p&gt;In this case, we actually need to pull an on-off maneuver.  This has us setting &lt;em&gt;all&lt;/em&gt; &lt;code&gt;th&lt;/code&gt; elements to have a highlight style, and then turn it off for the elements that meet our criteria.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;th {border: 2px solid red;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;th[scope=&amp;quot;col&amp;quot;], th[scope=&amp;quot;row&amp;quot;] {border: none;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;This was necessary because of the way &lt;span class="caps"&gt;CSS&lt;/span&gt; selectors work.  For example, consider this:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;th:not([scope=&amp;quot;col&amp;quot;]), th:not([scope=&amp;quot;row&amp;quot;]) {border: 2px solid red;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;That would select&amp;#8230;&lt;strong&gt;all &lt;code&gt;th&lt;/code&gt; elements, regardless of their attrributes&lt;/strong&gt;.  That&amp;#8217;s because every &lt;code&gt;th&lt;/code&gt; element doesn&amp;#8217;t have a scope of &lt;code&gt;col&lt;/code&gt;, doesn&amp;#8217;t have a scope of &lt;code&gt;row&lt;/code&gt;, or doesn&amp;#8217;t have either.  There&amp;#8217;s no escaping this selector o&amp;#8217; doom!&lt;/p&gt;

	&lt;p&gt;This limitation arises because &lt;code&gt;:not()&lt;/code&gt; is limited to containing a single &amp;#8220;thing&amp;#8221; within its parentheses.  You can&amp;#8217;t, for example, say &amp;#8220;select all elements except those that are images which descend from list items&amp;#8221;.  Reportedly, this limitation was imposed to make browser implementation of &lt;code&gt;:not()&lt;/code&gt; easier.&lt;/p&gt;

	&lt;p&gt;Still, we can make good use of &lt;code&gt;:not()&lt;/code&gt; in the service of further diagnosing.  Calling out links in trouble is a breeze:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;a[href]:not([title]) {border: 5px solid red;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a[title=&amp;quot;&amp;quot;] {outline: 3px dotted red;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a[href=&amp;quot;#&amp;quot;] {background: lime;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a[href=&amp;quot;&amp;quot;] {background: fuchsia;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/20/fig4.png" width="212" height="204" alt="Figure 4" /&gt;&lt;/p&gt;

	&lt;p&gt;Here we have a set that will call our attention to links missing &lt;code&gt;title&lt;/code&gt; information, as well as links that have no valid target, whether through a missing &lt;span class="caps"&gt;URL&lt;/span&gt; or a JavaScript-driven page where there are no link fallbacks in the case of missing or disabled JavaScript (&lt;code&gt;href=&amp;#34;#&amp;#34;&lt;/code&gt;).&lt;/p&gt;

	&lt;h3&gt;And What About IE?&lt;/h3&gt;

	&lt;p&gt;As I said at the beginning, much of what I covered here doesn&amp;#8217;t work in Internet Explorer, most particularly &lt;code&gt;:not()&lt;/code&gt; and &lt;code&gt;outline&lt;/code&gt;.  &lt;em&gt;(Oh, so basically everything?  -Ed.)&lt;/em&gt;&lt;/p&gt;

	&lt;p&gt;I can&amp;#8217;t do much about the latter.  For the former, however, it&amp;#8217;s possible to hack your way around the problem by doing some layered on-off stuff.  For example, for images, you replace the previously-shown rules with the following:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;img {border: 5px solid red;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img[alt][title] {border-width: 0;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img[alt] {border-color: fuchsia;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img[alt], img[title] {border-style: double;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img[alt=&amp;quot;&amp;quot;][title],&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img[alt][title=&amp;quot;&amp;quot;] {border-width: 3px;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;img[alt=&amp;quot;&amp;quot;][title=&amp;quot;&amp;quot;] {border-style: dotted;}&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;It won&amp;#8217;t have exactly the same set of effects, given the inability to use both borders and outlines, but will still highlight troublesome images.&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/20/fig5.png" width="306" height="191" alt="Figure 5" /&gt;&lt;/p&gt;

	&lt;p&gt;It&amp;#8217;s also the case that IE6 and earlier lack support for even attribute selectors, whereas IE7 added pretty much all the attribute selector types there are, so the previous code block won&amp;#8217;t have any effect previous to IE7.&lt;/p&gt;

	&lt;p&gt;In a broader sense, though, these kinds of styles probably aren&amp;#8217;t going to be used in the wild, as it were.  Diagnostic styles are something only you see as you work on a site, so you can make sure to use a browser that supports outlines and &lt;code&gt;:not()&lt;/code&gt; when you&amp;#8217;re diagnosing.  The fact that IE users won&amp;#8217;t see these styles is irrelevant since users of any browser probably won&amp;#8217;t be seeing these styles.&lt;/p&gt;

	&lt;p&gt;Personally, I always develop in Firefox anyway, thanks to its ability to become a full-featured &lt;acronym title="Integrated Development Environment"&gt;&lt;span class="caps"&gt;IDE&lt;/span&gt;&lt;/acronym&gt; through the addition of extensions like &lt;a href="http://www.getfirebug.com/"&gt;Firebug&lt;/a&gt; and the &lt;a href="http://chrispederick.com/work/web-developer/"&gt;Web Developer Toolbar&lt;/a&gt;.&lt;/p&gt;

	&lt;h3&gt;Yeah, About That&amp;#8230;&lt;/h3&gt;

	&lt;p&gt;It&amp;#8217;s true that much of what I describe in this article is available in the &lt;acronym title="Web Developer Toolbar"&gt;&lt;span class="caps"&gt;WDT&lt;/span&gt;&lt;/acronym&gt;.  I feel there are two advantages to writing your own set of diagnostic styles.&lt;/p&gt;

	&lt;ol&gt;
		&lt;li&gt;When you write your own styles, you can define exactly what the visual results will be, and how they will interact.  The &lt;span class="caps"&gt;WDT&lt;/span&gt; doesn&amp;#8217;t let you make its outlines thicker or change their colors.&lt;/li&gt;
		&lt;li&gt;You can combine a bunch of diagnostics into a single set of rules and add it to your site&amp;#8217;s style sheet during the diagnostic portion, thus ensuring they persist as you surf around.  This can be done in the &lt;span class="caps"&gt;WDT&lt;/span&gt;, but it isn&amp;#8217;t as easy (and, at least for me, not as reliable).&lt;/li&gt;
	&lt;/ol&gt;

	&lt;p&gt;It&amp;#8217;s also true that a markup validator will catch many of the errors I mentioned, such as missing &lt;code&gt;alt&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt; attributes.  For some, that&amp;#8217;s sufficient.  But it won&amp;#8217;t catch everything diagnostic styles can, like empty &lt;code&gt;alt&lt;/code&gt; values or untargeted links, which are perfectly valid, syntactically speaking.&lt;/p&gt;

	&lt;h3&gt;Diagnosis Complete?&lt;/h3&gt;

	&lt;p&gt;I hope this has been a fun look at the concept of diagnostic styling as well as a quick introduction into possibly new concepts like &lt;code&gt;:not()&lt;/code&gt; and outlines.  This isn&amp;#8217;t all there is to say, of course: there is plenty more that could be added to a diagnostic style sheet.  And everyone&amp;#8217;s diagnostics will be different, tuned to meet each person&amp;#8217;s unique situation.&lt;/p&gt;

	&lt;p&gt;Mostly, though, I hope this small exploration triggers some creative thinking about the use of &lt;span class="caps"&gt;CSS&lt;/span&gt; to do more than just lay out pages and colorize text.  Given the familiarity we acquire with &lt;span class="caps"&gt;CSS&lt;/span&gt;, it only makes sense to use it wherever it might be useful, and setting up visible diagnostic flags is just one more place for it to help us.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://feeds.feedburner.com/~a/24ways?a=GENLBk"&gt;&lt;img src="http://feeds.feedburner.com/~a/24ways?i=GENLBk" border="0"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/24ways?a=WSzMYI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=WSzMYI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=JtTz4I"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=JtTz4I" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=I1Iaai"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=I1Iaai" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=plTDRI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=plTDRI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/24ways/~4/258873391" height="1" width="1"/&gt;</content>
		<summary type="html">
&lt;p&gt;&lt;a href="http://meyerweb.com/"&gt;Eric Meyer&lt;/a&gt; describes a technique for using &lt;span class="caps"&gt;CSS&lt;/span&gt; as a diagnostic tool for finding potential problems or issues within a page. Going beyond the styling for the delivery of a site, the use of &lt;span class="caps"&gt;CSS&lt;/span&gt; as an author-time development tool holds many possibilities. Now if only there was a selector to show me which presents I&amp;#8217;ve forgotten to buy this year.&lt;/p&gt;
</summary>
<feedburner:origLink>http://24ways.org/2007/diagnostic-styling</feedburner:origLink></entry>
<entry>
		<author>
			<name>Jonathan Snook</name>
		</author>
		<published>2007-12-19T00:00:25Z</published>
		<updated>2007-12-19T00:00:25Z</updated>
		<title type="html">Christmas Is In The AIR</title>
		<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/24ways/~3/258873392/christmas-is-in-the-air" />
		<id>tag:24ways.org,2007-12-18:7e1577ceca7ea22e28af5c35ee62927a/81c250345a33e1dffa7b5c282f273a97</id>
		<category term="adobe-air" />
		
		<content type="html">
&lt;p&gt;That&amp;#8217;s right, Christmas is coming up fast and there&amp;#8217;s plenty of things to do. Get the tree and lights up, get the turkey, buy presents and who know what else. And what about Santa? He&amp;#8217;s got a list. I&amp;#8217;m pretty sure he&amp;#8217;s checking it twice.&lt;/p&gt;

	&lt;p&gt;Sure, we could use an existing list making web site or even a desktop widget. But we&amp;#8217;re geeks! What&amp;#8217;s the fun in that? Let&amp;#8217;s build our own to-do list application and do it with Adobe &lt;span class="caps"&gt;AIR&lt;/span&gt;!&lt;/p&gt;

	&lt;h3&gt;What&amp;#8217;s Adobe &lt;span class="caps"&gt;AIR&lt;/span&gt;?&lt;/h3&gt;

	&lt;p&gt;&lt;a href="http://labs.adobe.com/technologies/air/"&gt;Adobe &lt;span class="caps"&gt;AIR&lt;/span&gt;&lt;/a&gt;, formerly codenamed Apollo, is a runtime environment that runs on both Windows and &lt;span class="caps"&gt;OSX&lt;/span&gt; (with Linux support to follow). This runtime environment lets you build desktop applications using Adobe technologies like Flash and Flex. Oh, and &lt;span class="caps"&gt;HTML&lt;/span&gt;. That&amp;#8217;s right, you web standards lovin&amp;#8217; maniac. You can build desktop applications that can run cross-platform using the trio of technologies, &lt;span class="caps"&gt;HTML&lt;/span&gt;, &lt;span class="caps"&gt;CSS&lt;/span&gt; and JavaScript.&lt;/p&gt;

	&lt;p&gt;If you&amp;#8217;ve tried developing with &lt;span class="caps"&gt;AIR&lt;/span&gt; before, you&amp;#8217;ll need to get re-familiarized with the latest beta release as many things have changed since the last one (such as the &lt;span class="caps"&gt;API&lt;/span&gt; and restrictions within the sandbox.)&lt;/p&gt;

	&lt;h3&gt;To get started&lt;/h3&gt;

	&lt;p&gt;To get started in building an &lt;span class="caps"&gt;AIR&lt;/span&gt; application, you&amp;#8217;ll need two basic things:&lt;/p&gt;

	&lt;ol&gt;
		&lt;li&gt;&lt;a href="http://labs.adobe.com/downloads/air.html"&gt;The &lt;span class="caps"&gt;AIR&lt;/span&gt; runtime&lt;/a&gt;. The runtime is needed to run any &lt;span class="caps"&gt;AIR&lt;/span&gt;-based application.&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://labs.adobe.com/downloads/airsdk.html"&gt;The &lt;span class="caps"&gt;SDK&lt;/span&gt;&lt;/a&gt;. The software development kit gives you all the pieces to test your application. Unzip the &lt;span class="caps"&gt;SDK&lt;/span&gt; into any folder you wish.&lt;/li&gt;
	&lt;/ol&gt;

	&lt;p&gt;You&amp;#8217;ll also want to get your hands on the &lt;a href="http://livedocs.adobe.com/labs/air/1/jslr/index.html"&gt;JavaScript &lt;span class="caps"&gt;API&lt;/span&gt; documentation&lt;/a&gt; which you&amp;#8217;ll no doubt find yourself getting into before too long. (&lt;a href="http://download.macromedia.com/pub/labs/air/air_b3_docs_html_121207.zip"&gt;You can download it&lt;/a&gt;, too.)&lt;/p&gt;

	&lt;p&gt;Also of interest, some development environments have support for &lt;span class="caps"&gt;AIR&lt;/span&gt; built right in. &lt;a href="http://www.aptana.com/air/"&gt;Aptana&lt;/a&gt; doesn&amp;#8217;t have support for beta 3 yet but I suspect it&amp;#8217;ll be available shortly.&lt;/p&gt;

	&lt;p&gt;Within the &lt;span class="caps"&gt;SDK&lt;/span&gt;, there are two main tools that we&amp;#8217;ll use: one to test the application (&lt;span class="caps"&gt;ADL&lt;/span&gt;) and another to build a distributable package of our application (&lt;span class="caps"&gt;ADT&lt;/span&gt;). I&amp;#8217;ll get into this some more when we get to that stage of development.&lt;/p&gt;

	&lt;h3&gt;Building our To-do list application&lt;/h3&gt;

	&lt;p&gt;The first step to building an application within &lt;span class="caps"&gt;AIR&lt;/span&gt; is to create an &lt;span class="caps"&gt;XML&lt;/span&gt; file that defines our default application settings. I call mine &lt;code&gt;application.xml&lt;/code&gt;, mostly because Aptana does that by default when creating a new &lt;span class="caps"&gt;AIR&lt;/span&gt; project. It makes sense though and I&amp;#8217;ve stuck with it. Included in the templates folder of the &lt;span class="caps"&gt;SDK&lt;/span&gt; is an example &lt;span class="caps"&gt;XML&lt;/span&gt; file that you can use.&lt;/p&gt;

	&lt;p&gt;The first key part to this after specifying things like the application ID, version, and filename, is to specify what the default content should be within the content tags. Enter in the name of the &lt;span class="caps"&gt;HTML&lt;/span&gt; file you wish to load. Within this &lt;span class="caps"&gt;HTML&lt;/span&gt; file will be our application.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;content&amp;gt;ui.html&amp;lt;/content&amp;gt;&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;Create a new &lt;span class="caps"&gt;HTML&lt;/span&gt; document and name it &lt;code&gt;ui.html&lt;/code&gt; and place it in the same directory as the &lt;code&gt;application.xml&lt;/code&gt; file. The first thing you&amp;#8217;ll want to do is copy over the &lt;code&gt;AIRAliases.js&lt;/code&gt; file from the frameworks folder of the &lt;span class="caps"&gt;SDK&lt;/span&gt; and add a link to it within your &lt;span class="caps"&gt;HTML&lt;/span&gt; document.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;AIRAliases.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;The aliases create shorthand links to all of the Flash-based &lt;span class="caps"&gt;API&lt;/span&gt;s.&lt;/p&gt;

	&lt;p&gt;Now is probably a good time to explain how to debug your application.&lt;/p&gt;

	&lt;h4&gt;Debugging our application&lt;/h4&gt;

	&lt;p&gt;So, with our &lt;span class="caps"&gt;XML&lt;/span&gt; file created and &lt;span class="caps"&gt;HTML&lt;/span&gt; file started, let&amp;#8217;s try testing our &amp;#8216;application&amp;#8217;. We&amp;#8217;ll need the &lt;span class="caps"&gt;ADL&lt;/span&gt; application located in &lt;span class="caps"&gt;BIN&lt;/span&gt; folder of the &lt;span class="caps"&gt;SDK&lt;/span&gt; and tell it to run the &lt;code&gt;application.xml&lt;/code&gt; file.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;/path/to/adl /path/to/application.xml&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;You can also just drag the &lt;span class="caps"&gt;XML&lt;/span&gt; file onto &lt;span class="caps"&gt;ADL&lt;/span&gt; and it&amp;#8217;ll accomplish the same thing. If you just did that and noticed that your blank application didn&amp;#8217;t load, you&amp;#8217;d be correct. It&amp;#8217;s running but isn&amp;#8217;t visible. Which at this point means you&amp;#8217;ll have to shut down the &lt;span class="caps"&gt;ADL&lt;/span&gt; process. Sorry about that!&lt;/p&gt;

	&lt;h4&gt;Changing the visibility&lt;/h4&gt;

	&lt;p&gt;You have two ways to make your application visible. You can do it automatically by setting the placing &lt;code&gt;true&lt;/code&gt; in the &lt;code&gt;visible&lt;/code&gt; tag within the &lt;code&gt;application.xml&lt;/code&gt; file.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;visible&amp;gt;true&amp;lt;/visible&amp;gt;&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;The other way is to do it programmatically from within your application. You&amp;#8217;d want to do it this way if you had other startup tasks to perform before showing the interface. To turn the UI on programmatically, simple set the &lt;code&gt;visible&lt;/code&gt; property of &lt;code&gt;nativeWindow&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;nativeWindow.visible = true;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;h4&gt;Sandbox Security&lt;/h4&gt;

	&lt;p&gt;Now that we have an application that we can see when we start it, it&amp;#8217;s time to build the to-do list application. In doing so, you&amp;#8217;d probably think that using a JavaScript library is a really good idea — and it can be but there are some limitations within &lt;span class="caps"&gt;AIR&lt;/span&gt; that have to be considered.&lt;/p&gt;

	&lt;p&gt;An &lt;span class="caps"&gt;HTML&lt;/span&gt; document, by default, runs within the application sandbox. You have full access to the &lt;span class="caps"&gt;AIR&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt;s but once the &lt;code&gt;onload&lt;/code&gt; event of the window has fired, you&amp;#8217;ll have a limited ability to make use of &lt;code&gt;eval&lt;/code&gt; and other dynamic script injection approaches. This limits the ability of external sources from gaining access to everything the &lt;span class="caps"&gt;AIR&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; offers, such as database and local file system access. You&amp;#8217;ll still be able to make use of &lt;code&gt;eval&lt;/code&gt; for evaluating &lt;span class="caps"&gt;JSON&lt;/span&gt; responses, which is probably the most important if you wish to consume &lt;span class="caps"&gt;JSON&lt;/span&gt;-based services.&lt;/p&gt;

	&lt;p&gt;If you wish to create a greater wall of security between &lt;span class="caps"&gt;AIR&lt;/span&gt; and your &lt;span class="caps"&gt;HTML&lt;/span&gt; document loading in external resources, you can &lt;a href="http://livedocs.adobe.com/labs/air/1/devappshtml/help.html?content=QuckStart_Sandbox_Bridge_HTML_1.html"&gt;create a child sandbox&lt;/a&gt;. We won&amp;#8217;t need to worry about it for our application so I won&amp;#8217;t go any further into it but definitely keep this in mind.&lt;/p&gt;

	&lt;h4&gt;Finally, our application&lt;/h4&gt;

	&lt;p&gt;Getting tired of all this preamble? Let&amp;#8217;s actually build our to-do list application. I&amp;#8217;ll use jQuery because it&amp;#8217;s small and should suit our needs nicely. Let&amp;#8217;s begin with some structure:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;text&amp;quot; value=&amp;quot;&amp;quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;button&amp;quot; id=&amp;quot;add&amp;quot; value=&amp;quot;Add&amp;quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;&amp;lt;ul id=&amp;quot;list&amp;quot;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/6.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/6.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;Now we need to wire up that button to actually add a new item to our to-do list.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$(document).ready(function(){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// make sure the application is visible&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;nativeWindow.visible = true;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;$('#add').click(function(){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;var t = $('#text').val();&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;if(t)&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;// use DOM methods to create the new list item&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;var li = document.createElement('li');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;// the extra space at the end creates a buffer between the text&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;// and the delete link we're about to add&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;li.appendChild(document.createTextNode(t + ' '));&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;// create the delete link&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;var del = document.createElement('a');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;// this makes it a true link. I feel dirty doing this.&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;del.setAttribute('href', '#');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;del.addEventListener('click', function(evt){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab4"&gt;&lt;code&gt;this.parentNode.parentNode.removeChild(this.parentNode);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;});&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;del.appendChild(document.createTextNode('[del]'));&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;li.appendChild(del);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;// append everything to the list&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;$('#list').append(li);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;//reset the text box&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;$('#text').val('');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;})&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;});&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/7.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/7.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;And just like that, we&amp;#8217;ve got a to-do list! That&amp;#8217;s it! Just never close your application and you&amp;#8217;ll remember everything. Okay, that&amp;#8217;s not very practical. You need to have some way of storing your to-do items until the next time you open up the application.&lt;/p&gt;

	&lt;h3&gt;Storing Data&lt;/h3&gt;

	&lt;p&gt;You&amp;#8217;ve essentially got 4 different ways that you can store data:&lt;/p&gt;

	&lt;ul&gt;
		&lt;li&gt;Using the local database. &lt;span class="caps"&gt;AIR&lt;/span&gt; comes with &lt;span class="caps"&gt;SQLL&lt;/span&gt;ite built in. That means you can create tables and insert, update and select data from that database just like on a web server.&lt;/li&gt;
		&lt;li&gt;Using the file system. You can also create files on the local machine. You have access to a few folders on the local system such as the documents folder and the desktop.&lt;/li&gt;
		&lt;li&gt;Using &lt;code&gt;EcryptedLocalStore&lt;/code&gt;. I like using the &lt;code&gt;EcryptedLocalStore&lt;/code&gt; because it allows you to easily save key/value pairs and have that information encrypted. All this within just a couple lines of code.&lt;/li&gt;
		&lt;li&gt;Sending the data to a remote &lt;span class="caps"&gt;API&lt;/span&gt;. Our to-do list could &lt;a href="http://www.rememberthemilk.com/services/api/"&gt;sync up with Remember the Milk&lt;/a&gt;, for example.&lt;/li&gt;
	&lt;/ul&gt;

	&lt;p&gt;To demonstrate some persistence, we&amp;#8217;ll use the file system to store our files. In addition, we&amp;#8217;ll let the user specify where the file should be saved. This way, we can create multiple to-do lists, keeping them separate and organized.&lt;/p&gt;

	&lt;p&gt;The application is now broken down into 4 basic tasks:&lt;/p&gt;

	&lt;ol&gt;
		&lt;li&gt;Load data from the file system.&lt;/li&gt;
		&lt;li&gt;Perform any interface bindings.&lt;/li&gt;
		&lt;li&gt;Manage creating and deleting items from the list.&lt;/li&gt;
		&lt;li&gt;Save any changes to the list back to the file system.&lt;/li&gt;
	&lt;/ol&gt;

	&lt;h4&gt;Loading in data from the file system&lt;/h4&gt;

	&lt;p&gt;When the application starts up, we&amp;#8217;ll prompt the user to select a file or specify a new to-do list. Within &lt;span class="caps"&gt;AIR&lt;/span&gt;, there are 3 main file objects: &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;FileMode&lt;/code&gt;, and &lt;code&gt;FileStream&lt;/code&gt;. &lt;code&gt;File&lt;/code&gt; handles file and path names, &lt;code&gt;FileMode&lt;/code&gt; is used as a parameter for the &lt;code&gt;FileStream&lt;/code&gt; to specify whether the file should be read-only or for write access. The &lt;code&gt;FileStream&lt;/code&gt; object handles all the read/write activity.&lt;/p&gt;

	&lt;p&gt;The &lt;code&gt;File&lt;/code&gt; object has a number of shortcuts to default paths like the documents folder, the desktop, or even the application store. In this case, we&amp;#8217;ll specify the documents folder as the default location and then use the &lt;code&gt;browseForSave&lt;/code&gt; method to prompt the user to specify a new or existing file. If the user specifies an existing file, they&amp;#8217;ll be asked whether they want to overwrite it.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;var store = air.File.documentsDirectory;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;var fileStream = new air.FileStream();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;store.browseForSave(&amp;quot;Choose To-do List&amp;quot;);&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/8.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/8.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;Then we add an event listener for when the user has selected a file. When the file is selected, we check to see if the file exists and if it does, read in the contents, splitting the file on new lines and creating our list items within the interface.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;store.addEventListener(air.Event.SELECT, fileSelected);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;function fileSelected()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;air.trace(store.nativePath);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// load in any stored data&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;var byteData = new air.ByteArray();&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;if(store.exists)&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;fileStream.open(store, air.FileMode.READ);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;fileStream.readBytes(byteData, 0, store.size);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;fileStream.close();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;if(byteData.length &amp;gt; 0)&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;var s = byteData.readUTFBytes(byteData.length);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;oldlist = s.split(&amp;quot;\r\n&amp;quot;);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;// create todolist items&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;for(var i=0; i &amp;lt; oldlist.length; i++)&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab4"&gt;&lt;code&gt;createItem(oldlist[i], (new Date()).getTime() + i );&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/9.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/9.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;h4&gt;Perform Interface Bindings&lt;/h4&gt;

	&lt;p&gt;This is similar to before where we set the click event on the Add button but we&amp;#8217;ve moved the code to save the list into a separate function.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;$('#add').click(function(){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;var t = $('#text').val();&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;if(t){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3 cmnt"&gt;&lt;code&gt;// create an ID using the time&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;createItem(t, (new Date()).getTime() );&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;})&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/10.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/10.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;h4&gt;Manage creating and deleting items from the list&lt;/h4&gt;

	&lt;p&gt;The list management is now in its own function, similar to before but with some extra information to identify list items and with calls to save our list after each change.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;function createItem(t, id)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;if(t.length == 0) return;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// add it to the todo list&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;todolist[id] = t;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// use DOM methods to create the new list item&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;var li = document.createElement('li');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// the extra space at the end creates a buffer between the text&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// and the delete link we're about to add&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;li.appendChild(document.createTextNode(t + ' '));&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// create the delete link&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;var del = document.createElement('a');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// this makes it a true link. I feel dirty doing this.&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;del.setAttribute('href', '#');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;del.addEventListener('click', function(evt){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;var id = this.id.substr(1);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;delete todolist[id]; // remove the item from the list&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;this.parentNode.parentNode.removeChild(this.parentNode);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;saveList();&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;});&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;del.appendChild(document.createTextNode('[del]'));&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;del.id = 'd' + id;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;li.appendChild(del);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// append everything to the list&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;$('#list').append(li);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;//reset the text box&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;$('#text').val('');&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;saveList();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/11.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/11.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;h4&gt;Save changes to the file system&lt;/h4&gt;

	&lt;p&gt;Any time a change is made to the list, we update the file. The file will always reflect the current state of the list and we&amp;#8217;ll never have to click a save button. It just iterates through the list, adding a new line to each one.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;function saveList(){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;if(store.isDirectory) return;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;var packet = '';&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;for(var i in todolist)&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;packet += todolist[i] + '\r\n';&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;var bytes = new air.ByteArray();&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;bytes.writeUTFBytes(packet);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;fileStream.open(store, air.FileMode.WRITE);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;fileStream.writeBytes(bytes, 0, bytes.length);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;fileStream.close();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/12.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/12.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;One important thing to mention here is that we check if the store is a directory first. The reason we do this goes back to our &lt;code&gt;browseForSave&lt;/code&gt; call. If the user cancels the dialog without selecting a file first, then the store points to the &lt;code&gt;documentsDirectory&lt;/code&gt; that we set it to initially. Since we haven&amp;#8217;t specified a file, there&amp;#8217;s no place to save the list.&lt;/p&gt;

	&lt;p&gt;Hopefully by this point, you&amp;#8217;ve been thinking of some cool ways to pimp out your list. Now we need to package this up so that we can let other people use it, too.&lt;/p&gt;

	&lt;h3&gt;Creating a Package&lt;/h3&gt;

	&lt;p&gt;Now that we&amp;#8217;ve created our application, we need to package it up so that we can distribute it. This is a two step process. The first step is to create a code signing certificate (or you can pay for one from &lt;a href="http://24ways.org/https://www.thawte.com/process/retail/new_devel"&gt;Thawte&lt;/a&gt; which will help authenticate you as an &lt;span class="caps"&gt;AIR&lt;/span&gt; application developer).&lt;/p&gt;

	&lt;p&gt;To create a self-signed certificate, run the following command. This will create a &lt;span class="caps"&gt;PFX&lt;/span&gt; file that you&amp;#8217;ll use to sign your application.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;adt -certificate -cn todo24ways 1024-RSA todo24ways.pfx mypassword&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/13.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/13.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;After you&amp;#8217;ve done that, you&amp;#8217;ll need to create the package with the certificate&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;adt -package -storetype pkcs12 -keystore todo24ways.pfx todo24ways.air application.xml .&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/14.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/14.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;The important part to mention here is the period at the end of the command. We&amp;#8217;re telling it to package up all files in the current directory.&lt;/p&gt;

	&lt;p&gt;After that, just run the &lt;span class="caps"&gt;AIR&lt;/span&gt; file, which will install your application and run it.&lt;/p&gt;

	&lt;h3&gt;Important things to remember about &lt;span class="caps"&gt;AIR&lt;/span&gt;&lt;/h3&gt;

	&lt;p&gt;When developing an &lt;span class="caps"&gt;HTML&lt;/span&gt; application, the rendering engine is Webkit. You&amp;#8217;ll thank your lucky stars that you aren&amp;#8217;t struggling with cross-browser issues. (My personal favourites are multiple backgrounds and border radius!)&lt;/p&gt;

	&lt;p&gt;Be mindful of memory leaks. Things like Ajax calls and event binding can cause applications to slowly leak memory over time. Web pages are normally short lived but desktop applications are often open for hours, if not days, and you may find your little desktop application taking up more memory than anything else on your machine!&lt;/p&gt;

	&lt;p&gt;The WebKit runtime itself can also be a memory hog, usually taking about 15MB just for itself. If you create multiple &lt;span class="caps"&gt;HTML&lt;/span&gt; windows, it&amp;#8217;ll add another 15MB to your memory footprint. Our little to-do list application shouldn&amp;#8217;t be much of a concern, though.&lt;/p&gt;

	&lt;p&gt;The other important thing to remember is that you&amp;#8217;re still essentially running within a Flash environment. While you probably won&amp;#8217;t notice this working in small applications, the moment you need to move to multiple windows or need to accomplish stuff beyond what &lt;span class="caps"&gt;HTML&lt;/span&gt; and JavaScript can give you, the need to understand some of the Flash-based elements will become more important.&lt;/p&gt;

	&lt;p&gt;Lastly, the other thing to remember is that &lt;span class="caps"&gt;HTML&lt;/span&gt; links will load within the &lt;span class="caps"&gt;AIR&lt;/span&gt; application. If you want a link to open in the users web browser, you&amp;#8217;ll need to capture that event and handle it on your own. The following code takes the &lt;code&gt;HREF&lt;/code&gt; from a clicked link and opens it in the default web browser.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;air.navigateToURL(new air.URLRequest(this.href));&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/christmas-is-in-the-air/15.txt" title="Download the above code as a textfile"&gt;/code/christmas-is-in-the-air/15.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;h3&gt;Only the beginning&lt;/h3&gt;

	&lt;p&gt;Of course, this is only the beginning of what you can do with Adobe &lt;span class="caps"&gt;AIR&lt;/span&gt;. You don&amp;#8217;t have the same level of control as building a native desktop application, such as being able to launch other applications, but you do have more control than what you could have within a web application. Check out the &lt;a href="http://www.adobe.com/devnet/air/ajax/"&gt;Adobe &lt;span class="caps"&gt;AIR&lt;/span&gt; Developer Center for &lt;span class="caps"&gt;HTML&lt;/span&gt; and Ajax&lt;/a&gt; for tutorials and other resources.&lt;/p&gt;

	&lt;p&gt;Now, go forth and create your desktop applications and hopefully you finish all your shopping before Christmas!&lt;/p&gt;

	&lt;p&gt;&lt;strong&gt;&lt;a href="http://media.24ways.org/2007/19/IntroductionToAIR.zip" rel="supporting-files"&gt;Download the example files.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://feeds.feedburner.com/~a/24ways?a=IioHmn"&gt;&lt;img src="http://feeds.feedburner.com/~a/24ways?i=IioHmn" border="0"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/24ways?a=4HtySI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=4HtySI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=1u4iEI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=1u4iEI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=oh9aMi"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=oh9aMi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=vSuVOI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=vSuVOI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/24ways/~4/258873392" height="1" width="1"/&gt;</content>
		<summary type="html">
&lt;p&gt;&lt;a href="http://snook.ca/"&gt;Jonathan Snook&lt;/a&gt; introduces us to the world of Adobe &lt;span class="caps"&gt;AIR&lt;/span&gt; and talks us through using standard web technologies such as &lt;span class="caps"&gt;HTML&lt;/span&gt;, &lt;span class="caps"&gt;CSS&lt;/span&gt; and JavaScript to build a run-anywhere desktop application. I used to think I could run anywhere, but after sprinting through the town centre naked, the antisocial behaviour order has rather put a stop to all that. &lt;/p&gt;
</summary>
<feedburner:origLink>http://24ways.org/2007/christmas-is-in-the-air</feedburner:origLink></entry>
<entry>
		<author>
			<name>Christian Heilmann</name>
		</author>
		<published>2007-12-18T00:03:27Z</published>
		<updated>2007-12-18T07:09:02Z</updated>
		<title type="html">Keeping JavaScript Dependencies At Bay</title>
		<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/24ways/~3/258873393/keeping-javascript-dependencies-at-bay" />
		<id>tag:24ways.org,2007-12-17:7e1577ceca7ea22e28af5c35ee62927a/56bd8574b9b2e60a8316fb731b69eb39</id>
		<category term="javascript" />
		
		<content type="html">
&lt;div id="webkrauts"&gt;&lt;a href="http://www.webkrauts.de/2007/12/18/grosse-javascript-applikationen-leicht-gemacht/" title="This article available in German at webkrauts.de" rel="translation"&gt;&lt;img src="http://24ways.org/img/webkrauts.gif" alt="This article available in German at webkrauts.de" width="96" height="96" /&gt;&lt;/a&gt;&lt;/div&gt;

	&lt;p&gt;As we are writing more and more complex JavaScript applications we run into issues that have hitherto (god I love that word) not been an issue. The first decision we have to make is what to do when planning our app: one big massive JS file or a lot of smaller, specialised files separated by task. &lt;/p&gt;

	&lt;p&gt;Personally, I tend to favour the latter, mainly because it allows you to work on components in parallel with other developers without lots of clashes in your version control. It also means that your application will be more lightweight as you only include components on demand.&lt;/p&gt;

	&lt;h3&gt;Starting with a global object&lt;/h3&gt;

	&lt;p&gt;This is why it is a good plan to start your app with one single object that also becomes the namespace for the whole application, say for example &lt;code&gt;myAwesomeApp&lt;/code&gt;:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;var myAwesomeApp = {};&lt;/code&gt;&lt;/li&gt;

&lt;/ol&gt;


	&lt;p&gt;You can nest any necessary components into this one and also make sure that you check for dependencies like &lt;span class="caps"&gt;DOM&lt;/span&gt; support right up front.&lt;/p&gt;

	&lt;h3&gt;Adding the components&lt;/h3&gt;

	&lt;p&gt;The other thing to add to this main object is a components object, which defines all the components that are there and their file names.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;var myAwesomeApp = {&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;components :{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;formcheck:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'formcheck.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;dynamicnav:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'dynamicnav.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;gallery:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'gallery.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;lightbox:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'lightbox.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/keeping-javascript-dependencies-at-bay/2.txt" title="Download the above code as a textfile"&gt;/code/keeping-javascript-dependencies-at-bay/2.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;Technically you can also omit the loaded properties, but it is cleaner this way. The next thing to add is an &lt;code&gt;addComponent&lt;/code&gt; function that can load your components on demand by adding new &lt;code&gt;SCRIPT&lt;/code&gt; elements to the head of the documents when they are needed.&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;var myAwesomeApp = {&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;components :{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;formcheck:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'formcheck.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;dynamicnav:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'dynamicnav.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;gallery:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'gallery.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;lightbox:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'lightbox.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;addComponent:function(component){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;var c = this.components[component];&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;if(c &amp;amp;&amp;amp; c.loaded === false){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;var s = document.createElement('script');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;s.setAttribute('type', 'text/javascript');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;s.setAttribute('src',c.url);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;document.getElementsByTagName('head')[0].appendChild(s);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/keeping-javascript-dependencies-at-bay/3.txt" title="Download the above code as a textfile"&gt;/code/keeping-javascript-dependencies-at-bay/3.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;This allows you to add new components on the fly when they are not defined:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;if(!myAwesomeApp.components.gallery.loaded){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;myAwesomeApp.addComponent('gallery');&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/keeping-javascript-dependencies-at-bay/4.txt" title="Download the above code as a textfile"&gt;/code/keeping-javascript-dependencies-at-bay/4.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;h3&gt;Verifying that components have been loaded&lt;/h3&gt;

	&lt;p&gt;However, this is not safe as the file might not be available. To make the dynamic adding of components safer each of the components should have a callback at the end of them that notifies the main object that they indeed have been loaded:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;var myAwesomeApp = {&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;components :{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;formcheck:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'formcheck.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;dynamicnav:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'dynamicnav.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;gallery:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'gallery.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;lightbox:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'lightbox.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;addComponent:function(component){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;var c = this.components[component];&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;if(c &amp;amp;&amp;amp; c.loaded === false){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;var s = document.createElement('script');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;s.setAttribute('type', 'text/javascript');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;s.setAttribute('src',c.url);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;document.getElementsByTagName('head')[0].appendChild(s);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;componentAvailable:function(component){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;this.components[component].loaded = true;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/keeping-javascript-dependencies-at-bay/5.txt" title="Download the above code as a textfile"&gt;/code/keeping-javascript-dependencies-at-bay/5.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;For example the &lt;code&gt;gallery.js&lt;/code&gt; file should call this notification as a last line:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;myAwesomeApp.gallery = function(){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1 cmnt"&gt;&lt;code&gt;// [... other code ...]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;}();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;myAwesomeApp.componentAvailable('gallery');&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/keeping-javascript-dependencies-at-bay/6.txt" title="Download the above code as a textfile"&gt;/code/keeping-javascript-dependencies-at-bay/6.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;h3&gt;Telling the implementers when components are available&lt;/h3&gt;

	&lt;p&gt;The last thing to add (actually as a courtesy measure for debugging and implementers) is to offer a listener function that gets notified when the component has been loaded:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;var myAwesomeApp = {&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;components :{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;formcheck:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'formcheck.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;dynamicnav:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'dynamicnav.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;gallery:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'gallery.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;lightbox:{&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;url:'lightbox.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;addComponent:function(component){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;var c = this.components[component];&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;if(c &amp;amp;&amp;amp; c.loaded === false){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;var s = document.createElement('script');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;s.setAttribute('type', 'text/javascript');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;s.setAttribute('src',c.url);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;document.getElementsByTagName('head')[0].appendChild(s);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;},&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;componentAvailable:function(component){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;this.components[component].loaded = true;&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;if(this.listener){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab3"&gt;&lt;code&gt;this.listener(component);&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/keeping-javascript-dependencies-at-bay/7.txt" title="Download the above code as a textfile"&gt;/code/keeping-javascript-dependencies-at-bay/7.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;This allows you to write a main listener function that acts when certain components have been loaded, for example:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;myAwesomeApp.listener = function(component){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;if(component === 'gallery'){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;   showGallery();&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/keeping-javascript-dependencies-at-bay/8.txt" title="Download the above code as a textfile"&gt;/code/keeping-javascript-dependencies-at-bay/8.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;h3&gt;Extending with other components&lt;/h3&gt;

	&lt;p&gt;As the main object is public, other developers can extend the components object with own components and use the listener function to load dependent components. Say you have a bespoke component with data and labels in extra files:&lt;/p&gt;

 &lt;ol class="code"&gt;
&lt;li&gt;&lt;code&gt;myAwesomeApp.listener = function(component){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;if(component === 'bespokecomponent'){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;myAwesomeApp.addComponent('bespokelabels');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;if(component === 'bespokelabels'){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;myAwesomeApp.addComponent('bespokedata');&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;if(component === 'bespokedata'){&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab2"&gt;&lt;code&gt;myAwesomeApp,bespokecomponent.init();&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;myAwesomeApp.components.bespokecomponent = {&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;url:'bespoke.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;myAwesomeApp.components.bespokelabels = {&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;url:'bespokelabels.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;myAwesomeApp.components.bespokedata = {&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;url:'bespokedata.js',&lt;/code&gt;&lt;/li&gt;
&lt;li class="tab1"&gt;&lt;code&gt;loaded:false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;};&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;myAwesomeApp.addComponent('bespokecomponent');&lt;/code&gt;&lt;/li&gt;
&lt;li class="source"&gt;Source:  &lt;a href="http://24ways.org/code/keeping-javascript-dependencies-at-bay/9.txt" title="Download the above code as a textfile"&gt;/code/keeping-javascript-dependencies-at-bay/9.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


	&lt;p&gt;Following this practice you can write pretty complex apps and still have full control over what is available when. You can also extend this to allow for &lt;span class="caps"&gt;CSS&lt;/span&gt; files to be added on demand.&lt;/p&gt;

	&lt;h3&gt;Influences&lt;/h3&gt;

	&lt;p&gt;If you like this idea and wondered if someone already uses it, take a look at the &lt;a href="http://developer.yahoo.com/yui"&gt;Yahoo! User Interface library&lt;/a&gt;, and especially at the &lt;a href="http://developer.yahoo.com/yui/yahoo/"&gt;&lt;span class="caps"&gt;YAHOO&lt;/span&gt;_config option of the global &lt;span class="caps"&gt;YAHOO&lt;/span&gt;.js object&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://feeds.feedburner.com/~a/24ways?a=LlpmX1"&gt;&lt;img src="http://feeds.feedburner.com/~a/24ways?i=LlpmX1" border="0"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/24ways?a=t1INEI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=t1INEI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=OZZvGI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=OZZvGI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=mjTQQi"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=mjTQQi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/24ways?a=BoDzsI"&gt;&lt;img src="http://feeds.feedburner.com/~f/24ways?i=BoDzsI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/24ways/~4/258873393" height="1" width="1"/&gt;</content>
		<summary type="html">
&lt;p&gt;&lt;a href="http://www.wait-till-i.com/"&gt;Christian Heilmann&lt;/a&gt; delves into the world of JavaScript application dependencies with a smart technique to help manage the loading of parts of your application as and when needed.  Just as Santa only loads the presents he needs for each trip into his sleigh, keep your loading lean and the reindeer will thank you.&lt;/p&gt;
</summary>
<feedburner:origLink>http://24ways.org/2007/keeping-javascript-dependencies-at-bay</feedburner:origLink></entry>
<entry>
		<author>
			<name>Richard Rutter</name>
		</author>
		<published>2007-12-17T00:02:23Z</published>
		<updated>2007-12-17T00:41:26Z</updated>
		<title type="html">Increase Your Font Stacks With Font Matrix</title>
		<link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/24ways/~3/258873394/increase-your-font-stacks-with-font-matrix" />
		<id>tag:24ways.org,2007-12-16:7e1577ceca7ea22e28af5c35ee62927a/3f2e377f801f6aca72b13e01efbbde6a</id>
		<category term="typography" />
		
		<content type="html">
&lt;p&gt;Web pages built in plain old &lt;span class="caps"&gt;HTML&lt;/span&gt; and &lt;span class="caps"&gt;CSS&lt;/span&gt; are displayed using only the fonts installed on users&amp;#8217; computers (&lt;code&gt;@font-face&lt;/code&gt; implementations excepted). To enable this, &lt;span class="caps"&gt;CSS&lt;/span&gt; provides the &lt;a href="http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family"&gt;font-family property&lt;/a&gt; for specifying fonts in order of preference (often known as a &lt;a href="http://www.safalra.com/web-design/typography/web-safe-fonts-myth/"&gt;font stack&lt;/a&gt;). For example:&lt;/p&gt;

	&lt;p&gt;&lt;code&gt;h1 {font-family: &amp;#39;Egyptienne F&amp;#39;, Cambria, Georgia, serif}&lt;/code&gt;&lt;/p&gt;

	&lt;p&gt;So in the above rule, headings will be displayed in &lt;a href="http://icanhaz.com/EgyptienneF"&gt;Egyptienne F&lt;/a&gt;. If Egyptienne F is not available then Cambria will be used, failing that Georgia or the final fallback default serif font. This everyday bit of &lt;span class="caps"&gt;CSS&lt;/span&gt; will be common knowledge among all 24 ways readers.&lt;/p&gt;

	&lt;p&gt;It is also a commonly held belief that the only fonts we can rely on being installed on users&amp;#8217; computers are the &lt;a href="http://en.wikipedia.org/wiki/Core_fonts_for_the_Web"&gt;core web fonts&lt;/a&gt; of Arial, Times New Roman, Verdana, Georgia and friends. But is that really true?&lt;/p&gt;

	&lt;p&gt;If you look in the fonts folder of your computer, or even your Mum&amp;#8217;s computer, then you are likely to find a whole load of fonts besides the core ones. This is because many software packages automatically install extra typefaces. For example, Office 2003 installs over 100 additional fonts. Admittedly not all of these fonts are particularly refined, and not all are suitable for the Web. However they still do increase your options. &lt;/p&gt;

	&lt;h3&gt;The Matrix&lt;/h3&gt;

	&lt;p&gt;I have put together &lt;a href="http://media.24ways.org/2007/17/fontmatrix.html"&gt;a matrix of (western) fonts&lt;/a&gt; showing which are installed with Mac and Windows operating systems, which are installed with various versions of Microsoft Office, and which are installed with Adobe Creative Suite.&lt;/p&gt;

	&lt;p&gt;&lt;a href="http://media.24ways.org/2007/17/fontmatrix.html"&gt;&lt;img src="http://media.24ways.org/2007/17/fontmatrix-small.png" alt="Thumbnail of font matrix" /&gt;&lt;/a&gt;&lt;/p&gt;

	&lt;p&gt;The matrix is available for download as an &lt;a href="http://media.24ways.org/2007/17/fontmatrix.xls"&gt;Excel file&lt;/a&gt; and as a &lt;a href="http://media.24ways.org/2007/17/fontmatrix.csv"&gt;&lt;span class="caps"&gt;CSV&lt;/span&gt;&lt;/a&gt;.  There are no readily available statistics regarding the penetration of Office or Creative Suite, but you can probably take an educated guess based on your knowledge of your readers.&lt;/p&gt;

	&lt;p&gt;The idea of the matrix is that use can use it to help construct your font stack. First of all pick the font you&amp;#8217;d really like for your text &amp;#8211; this doesn&amp;#8217;t have to be in the matrix. Then pick the generic family (serif, sans-serif, cursive, fantasy or monospace) and a font from each of the operating systems. Then pick any suitable fonts from the Office and Creative Suite lists.&lt;/p&gt;

	&lt;p&gt;For example, you may decide your headings should be in the increasingly ubiquitous Clarendon. This is a serif type face. At OS-level the most similar is arguably Georgia. Adobe CS2 comes with Century Old Style which has a similar feel. Century Schoolbook is similar too, and is installed with all versions of Office. Based on this your font stack becomes:&lt;/p&gt;

	&lt;p&gt;&lt;code&gt;font-family: &amp;#39;Clarendon Std&amp;#39;, &amp;#39;Century Old Style Std&amp;#39;, &amp;#39;Century Schoolbook&amp;#39;, Georgia, serif&lt;/code&gt;&lt;/p&gt;

	&lt;p&gt;&lt;img src="http://media.24ways.org/2007/17/ClarendonLTStd.png" alt="Clarendon" /&gt;&lt;br /&gt;
&lt;img src="http://media.24ways.org/2007/17/CenturyOldStyle.png" alt="Century Old Style" /&gt;&lt;br /&gt;
&lt;img src="http://media.24ways.org/2007/17/CenturySchoolbook.png" alt="Century Schoolbook" /&gt;&lt;br /&gt;
&lt;img src="http://media