{"database": "24ways", "rows": [[16, "URL Rewriting for the Fearful", "I think it was Marilyn Monroe who said, \u201cIf you can\u2019t handle me at my worst, please just fix these rewrite rules, I\u2019m getting an internal server error.\u201d Even the blonde bombshell hated configuring URL rewrites on her website, and I think most of us know where she was coming from.\n\nThe majority of website projects I work on require some amount of URL rewriting, and I find it mildly enjoyable \u2014 I quite like a good rewrite rule. I suspect you may not share my glee, so in this article we\u2019re going to go back to basics to try to make the whole rigmarole more understandable.\n\nWhen we think about URL rewriting, usually that means adding some rules to an .htaccess file for an Apache web server. As that\u2019s the most common case, that\u2019s what I\u2019ll be sticking to here. If you work with a different server, there\u2019s often documentation specifically for translating from Apache\u2019s mod_rewrite rules. I even found an automatic converter for nginx.\n\nThis isn\u2019t going to be a comprehensive guide to every URL rewriting problem you might ever have. That would take us until Christmas. If you consider yourself a trial-and-error dabbler in the HTTP 500-infested waters of URL rewriting, then hopefully this will provide a little bit more of a basis to help you figure out what you\u2019re doing. If you\u2019ve ever found yourself staring at the white screen of death after screwing up your .htaccess file, don\u2019t worry. As Michael Jackson once insipidly whined, you are not alone.\n\nThe basics\n\nRewrite rules form part of the Apache web server\u2019s configuration for a website, and can be placed in a number of different locations as part of your virtual host configuration. By far the simplest and most portable option is to use an .htaccess file in your website root. Provided your server has mod_rewrite available, all you need to do to kick things off in your .htaccess file is:\n\nRewriteEngine on\n\nThe general formula for a rewrite rule is:\n\nRewriteRule URL/to/match URL/to/use/if/it/matches [options]\n\nWhen we talk about URL rewriting, we\u2019re normally talking about one of two things: redirecting the browser to a different URL; or rewriting the URL internally to use a particular file. We\u2019ll look at those in turn.\n\nRedirects\n\nRedirects match an incoming URL, and then redirect the user\u2019s browser to a different address. These can be useful for maintaining legacy URLs if content changes location as part of a site redesign. Redirecting the old URL to the new location makes sure that any incoming links, such as those from search engines, continue to work. \n\nIn 1998, Sir Tim Berners-Lee wrote that Cool URIs don\u2019t change, encouraging us all to go the extra mile to make sure links keep working forever. I think that sometimes it\u2019s fine to move things around \u2014 especially to correct bad URL design choices of the past \u2014 provided that you can do so while keeping those old URLs working. That\u2019s where redirects can help.\n\nA redirect might look like this\n\nRewriteRule ^article/used/to/be/here.php$ /article/now/lives/here/ [R=301,L]\n\nRewriting\n\nBy default, web servers closely map page URLs to the files in your site. On receiving a request for http://example.com/about/history.html the server goes to the configured folder for the example.com website, and then goes into the about folder and returns the history.html file.\n\nA rewrite rule changes that process by breaking the direct relationship between the URL and the file system. \u201cWhen there\u2019s a request for /about/history.html\u201d a rewrite rule might say, \u201cuse the file /about_section.php instead.\u201d\n\nThis opens up lots of possibilities for creative ways to map URLs to the files that know how to serve up the page. Most MVC frameworks will have a single rule to rewrite all page URLs to one single file. That file will be a script which kicks off the framework to figure out what to do to serve the page.\n\nRewriteRule ^for/this/url/$ /use/this/file.php [L] \n\nMatching patterns\n\nBy now you\u2019ll have noted the weird ^ and $ characters wrapped around the URL we\u2019re trying to match. That\u2019s because what we\u2019re actually using here is a pattern. Technically, it is what\u2019s called a Perl Compatible Regular Expression (PCRE) or simply a regex or regexp. We\u2019ll call it a pattern because we\u2019re not animals.\n\nWhat are these patterns? If I asked you to enter your credit card expiry date as MM/YY then chances are you\u2019d wonder what I wanted your credit card details for, but you\u2019d know that I wanted a two-digit month, a slash, and a two-digit year. That\u2019s not a regular expression, but it\u2019s the same idea: using some placeholder characters to define the pattern of the input you\u2019re trying to match.\n\nWe\u2019ve already met two regexp characters.\n\n\n\t^\n\tMatches the beginning of a string\n\t$\n\tMatches the end of a string\n\n\nWhen a pattern starts with ^ and ends with $ it\u2019s to make sure we match the complete URL start to finish, not just part of it. There are lots of other ways to match, too:\n\n\n\t[0-9]\n\tMatches a number, 0\u20139. [2-4] would match numbers 2 to 4 inclusive.\n\t[a-z]\n\tMatches lowercase letters a\u2013z\n\t[A-Z]\n\tMatches uppercase letters A\u2013Z\n\t[a-z0-9]\n\tCombining some of these, this matches letters a\u2013z and numbers 0\u20139\n\n\nThese are what we call character groups. The square brackets basically tell the server to match from the selection of characters within them. You can put any specific characters you\u2019re looking for within the brackets, as well as the ranges shown above. \n\nHowever, all these just match one single character. [0-9] would match 8 but not 84 \u2014 to match 84 we\u2019d need to use [0-9] twice.\n\n[0-9][0-9]\n\nSo, if we wanted to match 1984 we could to do this:\n\n[0-9][0-9][0-9][0-9] \n\n\u2026but that\u2019s getting silly. Instead, we can do this:\n\n[0-9]{4}\n\nThat means any character between 0 and 9, four times. If we wanted to match a number, but didn\u2019t know how long it might be (for example, a database ID in the URL) we could use the + symbol, which means one or more.\n\n[0-9]+\n\nThis now matches 1, 123 and 1234567.\n\nPutting it into practice\n\nLet\u2019s say we need to write a rule to match article URLs for this website, and to rewrite them to use /article.php under the hood. The articles all have URLs like this:\n\n2013/article-title/\n\nThey start with a year (from 2005 up to 2013, currently), a slash, and then have a URL-safe version of the article title (a slug), ending in a slash. We\u2019d match it like this:\n\n^[0-9]{4}/[a-z0-9-]+/$\n\nIf that looks frightening, don\u2019t worry. Breaking it down, from the start of the URL (^) we\u2019re looking for four numbers ([0-9]{4}). Then a slash \u2014 that\u2019s just literal \u2014 and then anything lowercase a\u2013z or 0\u20139 or a dash ([a-z0-9-]) one or more times (+), ending in a slash (/$).\n\nPutting that into a rewrite rule, we end up with this:\n\nRewriteRule ^[0-9]{4}/[a-z0-9-]+/$ /article.php\n\nWe\u2019re getting close now. We can match the article URLs and rewrite them to use article.php. Now we just need to make sure that article.php knows which article it\u2019s supposed to display.\n\nCapturing groups, and replacements\n\nWhen rewriting URLs you\u2019ll often want to take important parts of the URL you\u2019re matching and pass them along to the script that handles the request. That\u2019s usually done by adding those parts of the URL on as query string arguments. For our example, we want to make sure that article.php knows the year and the article title we\u2019re looking for. That means we need to call it like this:\n\n/article.php?year=2013&slug=article-title\n\nTo do this, we need to mark which parts of the pattern we want to reuse in the destination. We do this with round brackets or parentheses. By placing parentheses around parts of the pattern we want to reuse, we create what\u2019s called a capturing group. To capture an important part of the source URL to use in the destination, surround it in parentheses.\n\nOur pattern now looks like this, with parentheses around the parts that match the year and slug, but ignoring the slashes:\n\n^([0-9]{4})/([a-z0-9-]+)/$ \n\nTo use the capturing groups in the destination URL, we use the dollar sign and the number of the group we want to use. So, the first capturing group is $1, the second is $2 and so on. (The $ is unrelated to the end-of-pattern $ we used before.)\n\nRewriteRule ^([0-9]{4})/([a-z0-9-]+)/$ /article.php?year=$1&slug=$2 \n\nThe value of the year capturing group gets used as $1 and the article title slug is $2. Had there been a third group, that would be $3 and so on. In regexp parlance, these are called back-references as they refer back to the pattern.\n\nOptions\n\nSeveral brain-taxing minutes ago, I mentioned some options as the final part of a rewrite rule. There are lots of options (or flags) you can set to change how the rule is processed. The most useful (to my mind) are:\n\n\n\tR=301\n\tPerform an HTTP 301 redirect to send the user\u2019s browser to the new URL. A status of 301 means a resource has moved permanently and so it\u2019s a good way of both redirecting the user to the new URL, and letting search engines know to update their indexes.\n\tL\n\tLast. If this rule matches, don\u2019t bother processing the following rules.\n\n\nOptions are set in square brackets at the end of the rule. You can set multiple options by separating them with commas:\n\nRewriteRule ^([0-9]{4})/([a-z0-9-]+)/$ /article.php?year=$1&slug=$2 [L]\n\nor\n\nRewriteRule ^about/([a-z0-9-]+).jsp/$ /about/$1/ [R=301,L] \n\nCommon pitfalls\n\nOnce you\u2019ve built up a few rewrite rules, things can start to go wrong. You may have been there: a rule which looks perfectly good is somehow not matching. One common reason for this is hidden behind that [L] flag. \n\nL for Last is a useful option to tell the rewrite engine to stop once the rule has been matched. This is what it does \u2014 the remaining rules in the .htaccess file are then ignored. However, once a URL has been rewritten, the entire set of rules are then run again on the new URL. If the new URL matches any of the rules, that too will be rewritten and on it goes. \n\nOne way to avoid this problem is to keep your \u2018real\u2019 pages under a folder path that will never match one of your rules, or that you can exclude from the rewrite rules.\n\nUseful snippets\n\nI find myself reusing the same few rules over and over again, just with minor changes. Here are some useful examples to refer back to.\n\nExcluding a directory\n\nAs mentioned above, if you\u2019re rewriting lots of fancy URLs to a collection of real files it can be helpful to put those files in a folder and exclude it from rewrite rules. This helps solve the issue of rewrite rules reapplying to your newly rewritten URL. To exclude a directory, put a rule like this at the top of your file, before your other rules. Our files are in a folder called _source, the dash in the rule means do nothing, and the L flag means the following rules won\u2019t be applied.\n\nRewriteRule ^_source - [L]\n\nThis is also useful for excluding things like CMS folders from your website\u2019s rewrite rules\n\nRewriteRule ^perch - [L] \n\nAdding or removing www from the domain\n\nSome folk like to use a www and others don\u2019t. Usually, it\u2019s best to pick one and go with it, and redirect the one you don\u2019t want. On this site, we don\u2019t use www.24ways.org so we redirect those requests to 24ways.org.\n\nThis uses a RewriteCond which is like an if for a rewrite rule: \u201cIf this condition matches, then apply the following rule.\u201d In this case, it\u2019s if the HTTP HOST (or domain name, basically) matches this pattern, then redirect everything:\n\nRewriteCond %{HTTP_HOST} ^www.24ways.org$ [NC]\nRewriteRule ^(.*)$ http://24ways.org/$1 [R=301,L]\n\nThe [NC] flag means \u2018no case\u2019 \u2014 the match is case-insensitive. The dots in the domain are escaped with a backslash, as a dot is a regular expression character which means match anything, so we escape it because we literally mean a dot in this instance.\n\nRemoving file extensions\n\nSometimes all you need to do to tidy up a URL is strip off the technology-specific file extension, so that /about/history.php becomes /about/history. This is easily achieved with the help of some more rewrite conditions.\n\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteCond %{REQUEST_FILENAME}.php -f\nRewriteRule ^(.+)$ $1.php [L,QSA]\n\nThis says if the file being asked for isn\u2019t a file (!-f) and if it isn\u2019t a directory (!-d) and if the file name plus .php is an actual file (-f) then rewrite by adding .php on the end. The QSA flag means \u2018query string append\u2019: append the existing query string onto the rewritten URL.\n\nIt\u2019s these sorts of more generic catch-all rules that you need to watch out for when your .htaccess gets rerun after a successful match. Without care they can easily rematch the newly rewritten URL.\n\nLogging for when it all goes wrong\n\nAlthough not possible within your .htaccess file, if you have access to your Apache configuration files you can enable rewrite logging. This can be useful to track down where a rule is going wrong, if it\u2019s matching incorrectly or failing to match. It also gives you an overview of the amount of work being done by the rewrite engine, enabling you to rearrange your rules and maximise performance.\n\nRewriteEngine On\nRewriteLog \"/full/system/path/to/rewrite.log\"\nRewriteLogLevel 5\n\nTo be doubly clear: this will not work from an .htaccess file \u2014 it needs to be added to the main Apache configuration files. (I sometimes work using MAMP PRO locally on my Mac, and this can be pasted into the snappily named Customized virtual host general settings box in the Advanced tab for your site.)\n\nThe white screen of death\n\nOne of the most frustrating things when working with rewrite rules is that when you make a mistake it can result in the server returning an HTTP 500 Internal Server Error. This in itself isn\u2019t an error message, of course. It\u2019s more of a notification that an error has occurred. The real error message can usually be found in your Apache error log.\n\nIf you have access to your server logs, check the Apache error log and you\u2019ll usually find a much more descriptive error message, pointing you towards your mistake. (Again, if using MAMP PRO, go to Server, Apache and the View Log button.)\n\nIn conclusion\n\nRewriting URLs can be a bear, but the advantages are clear. Keeping a tidy URL structure, disconnected from the technology or file structure of your site can result in URLs that are easier to use and easier to maintain into the future.\n\nIf you\u2019re redesigning a site, remember that cool URIs don\u2019t change, so budget some time to make sure that any content you move has a rewrite rule associated with it to keep any links working.\n\nFurther reading\n\nTo find out more about URL rewriting and perhaps even learn more about regular expressions, I can recommend the following resources.\n\n\n\tFrom the horse\u2019s mouth, the Apache mod_rewrite documentation\n\tParticularly useful with that documentation is the RewriteRule Flags listing\n\tYou may wish to don sunglasses to follow the otherwise comprehensive Regular-Expressions.info tutorial\n\tFriend of 24 ways, Neil Crosby has a mod_rewrite Beginner\u2019s Guide which I\u2019ve found handy over the years.\n\n\nAs noted at the start, this isn\u2019t a fully comprehensive guide, but I hope it\u2019s useful in finding your feet with a powerful but sometimes annoying technology. Do you have useful snippets you often use on projects? Feel free to share them in the comments.", "2013", "Drew McLellan", "drewmclellan", "2013-12-01T00:00:00+00:00", "https://24ways.org/2013/url-rewriting-for-the-fearful/", "code"], [29, "What It Takes to Build a Website", "In 1994 we lost Kurt Cobain and got the world wide web as a weird consolation prize. In the years that followed, if you\u2019d asked me if I knew how to build a website I\u2019d have said yes, I know HTML, so I know how to build a website. If you\u2019d then asked me what it takes to build a website, I\u2019d have had to admit that HTML would hardly feature.\n\nAmong the design nerdery and dev geekery it\u2019s easy to think that the nuts and bolts of building a page just need to be multiplied up and Ta-da! There\u2019s your website. That can certainly be true with weekend projects and hackery for fun. It works for throwing something together on GitHub or experimenting with ideas on your personal site. But what about working professionally on client projects?\n\nThe web is important, so we need to build it right.\n\nIt\u2019s 2015 \u2013 your job involves people paying you money for building websites. What does it take to build a website and to do it right? What practices should we adopt to make really great, successful and professional web projects in 2015? I put that question to some friends and 24 ways authors to see what they thought.\n\nGetting the tech right\n\nInevitably, it all starts with the technology. We work in a technical medium, after all. From Notepad and WinFTP through to continuous integration and deployment \u2013 how do you build sites?\n\nCreate a stable development environment\n\nThere\u2019s little more likely to send a web developer into a wild panic and a client into a wild rage than making a new site live and things just not working. That\u2019s why it\u2019s important to have realistic development and staging environments that mimic the live server as closely as possible.\n\nAre you in the habit of developing new sites right on the client\u2019s server? Or maybe in a subfolder on your local machine? It\u2019s time to reconsider.\n\nCharlie Perrins writes:\n\n\n\tDon\u2019t work on a live server \u2013 this feels like one of those gear-changing moments for a developer\u2019s growth. Build something that works just as well locally on your own machine as it does on a live server, and capture the differences in the code between the local and live version in a single config file. Ultimately, if you can get all the differences between environments down to a config level then you\u2019ll be in a really good position to automate the deployment process at some point in the future.\n\n\nAnything that creates a significant difference between the development and the live environments has the potential to cause problems you won\u2019t know about until the site goes live \u2013 and at that point the problems are very public and very embarrassing, not to mention unprofessional.\n\nA reasonable solution is to use a tool like MAMP PRO which enables you to set up an individual local website for each project you work on. Importantly, individual sites give you both consistency of paths between development and live, but also the ability to configure server options (like PHP versions and configuration, for example) to match the live site.\n\nBetter yet is to use a virtual machine, managed with a tool such as Vagrant. If you\u2019re interested in learning more about that, we have an article on that subject later in the series.\n\nUse source control\n\nTrent Walton writes:\n\n\n\tWe use source control, and it\u2019s become the centerpiece for how we handle collaboration, enhancements, and issues. It drives our process.\n\n\nI\u2019m hoping by now that you\u2019re either using source control for all your work, or feeling a nagging guilt that you should be. Be it Git, Mercurial, Subversion (name your poison), a revision control system enables you to keep track of changes, revert anything that breaks, and keep rolling backups of your project.\n\nThe benefits only start there, and Charlie Perrins recommends using source control \u201cnot just as a personal backup of your code, but as a way to play nicely with other developers.\u201c\n\nNoting the benefits when collaborating with other developers, he adds:\n\n\n\tGraduating from being the sole architect of your codebase to contributing to a shared codebase is a huge leap for a developer. Perhaps a practical way for people who tend to work on their own to do that would be to submit a pull request or a patch to an open source project or plugin.\u201d\n\n\nRichard Rutter of Clearleft sees clear advantages for the client, too. He recommends using source control \u201cpreferably in some sort of collaborative environment that you can open up or hand over to the client\u201d \u2013 a feature found with hosted services such as GitHub.\n\nIf you\u2019d like to hone your Git skills, Emma Jane Westby wrote Git for Grown-ups in last year\u2019s 24 ways.\n\nDon\u2019t repeat, automate!\n\nTim Kadlec is a big proponent of automating your build process:\n\n\n\tI\u2019ve been hammering that home to every client I\u2019ve had this year. It\u2019s amazing how many companies don\u2019t really have a formal build/deployment process in place. So many issues on the web (performance, accessibility, etc.) can be greatly improved just by having a layer of automation involved.\n\n\tFor example, graphic editing software spits out ridiculously bloated images. Very frequently, that\u2019s what ends up getting put on a site. If you have a build process, you can have the compression automated and start seeing immediate gains for no effort. On a recent project, they were able to shave around 1.5MB from their site weight simply by automating compression.\n\n\nOnce you have your code in source control, some of that automation can be made easier. Brian Suda writes:\n\n\n\tWe have a few bash scripts that run on git commit: they compile the less, jslint and remove white-space, basically the 3 Cs, Compress, Concatenate, Combine. This is now part of our workflow without even realising it.\n\n\nOne great way to get started with a build process is to use a tool like Grunt, and a great way to get started with Grunt is to read Chris Coyier\u2019s Grunt for People Who Think Things Like Grunt are Weird and Hard.\n\nTim reinforces:\n\n\n\tIssues like [image compression]  \u2014  or simple accessibility issues like alt tags on images  \u2014  should never be able to hit a live server. If you can detect it, you can automate it. And if you can automate it, you can free up time for designers and developers to focus on more challenging \u2014 and interesting \u2014 problems.\n\n\nA clear call to arms to tighten up and formalise development and deployment practices. The less that has to be done manually or is susceptible to change, the less that can go wrong when a site is built and deployed. Any procedures that are automated are no longer dependant on a single person\u2019s knowledge, making it easier to build your team or just cope when someone important is out of the office or leaves.\n\nIf you\u2019re interested in kicking the FTP habit and automating your site deployments, we have an article later in the series just for you.\n\nBuild systems, not sites\n\nOne big theme arising this year was that of building websites as systems, not as individual pages.\n\nBrad Frost:\n\n\n\tFor me, teams making websites in 2015 shouldn\u2019t be working on just-another-redesign redesign. People are realizing that in order to make stable, future-friendly, scalable, extensible web experiences they\u2019re going to need to think more systematically. That means crafting deliberate and thoughtful design systems. That means establishing front-end style guides. That means killing the out-dated, siloed, assembly-line waterfall process and getting cross-disciplinary teams working together in meaningful ways. That means treating development as design. That means treating performance as design. That means taking the time out of the day to establish the big picture, rather than aimlessly crawling along quarter by quarter.\n\n\nDesigner and developer Jina Bolton also advocates the use of style guides, and recommends making the guide a project deliverable:\n\n\n\tConsider adding on a style guide/UI library to your project as a deliverable for maintainability and thinking through all UI elements and components.\n\n\nVal Head agrees: \u201cbuild and maintain a style guide for each project\u201d she wrote. On the subject of approaching a redesign, she added:\n\n\n\tA UI inventory goes a long way to helping get your head around what a design system needs in the early stages of a redesign project.\n\n\nSo what about that old chestnut, responsive web design? Should we be making sites responsive by default? How about mobile first?\n\nRichard Rutter:\n\n\n\tThink mobile first unless you have a very good reason not to. Remember to take the client with you on this principle, otherwise it won\u2019t work as a convincing piece of design.\n\n\nTrent Walton adds:\n\n\n\tThe more you can test and sort of skew your perception for what is typical on the web, the better. 4k displays hooked up to 100Mbps connections can make one extremely unsympathetic.\n\n\nThe value of testing with real devices is something Ruth John appreciates. She wrote:\n\n\n\tI still have my own small device lab at home, even though I work permanently for a well-established company (which has a LOT of devices at its disposal) \u2013 it just means I can get a good overview of how things are looking during development.\n\n\nAnd speaking of systems, Mark Norman Francis recommends the use of measuring tools to aid the design process; \u201c[U]se analytics and make decisions from actual data\u201d he suggests, rather than relying totally on intuition.\n\nTim Kadlec adds a word on performance planning:\n\n\n\tI think having a performance budget in place should now be a given on any project. We\u2019ve proven pretty conclusively through a hundred and one case studies that performance matters. And over the last year or so, we\u2019ve really seen a lot of great tools emerge to help track and enforce performance budgets. There\u2019s not really a good excuse for not using one any more.\n\n\nIt\u2019s clear that in the four years since Ethan Marcotte\u2019s Responsive Web Design article the diversity of screen sizes, network connection speeds and input methods has only increased. New web projects should presume visitors will be using anything from a watch up to a big screen desktop display, and from being offline, through to GPRS, 3G and fast broadband.\n\nWill it take more time to design and build for those constraints? Yes, it most likely will. If Internet Explorer is brave enough to ask to be your default browser, you can be brave enough to tell your client they need to build responsively.\n\nWorking collaboratively\n\nA big part of delivering a successful website project is how we work together, both as a design team and a wider project team with the client.\n\nVal Head recommends an open line of communication:\n\n\n\tKeep conversations going. With clients, with teammates. Talking is so important with the way we work now. A good team conversation place, like Slack, is slowly becoming invaluable for me too.\n\n\nRuth John agrees:\n\n\n\tWe\u2019ve recently opened up our lines of communication by using Slack. It has transformed the way we work. We\u2019re easily more productive and collaborative on projects, as well as making it a lot easier for us all to work remotely (including freelancers).\n\n\nShe goes on to point out how tools can be combined to ease team communication without adding further complications:\n\n\n\tWe have a private GitHub organisation (which everyone who works with us is granted access to), which not only holds all our project code but also a team wiki. This has lots of information to get you set up within the team, as well as coding guidelines and best practices and other admin info, like contact numbers/emails for the team.\n\n\nSmall-A agile is also the theme of the day, with Mark Norman Francis suggesting an approach of \u201csmall iterations with constant feedback around individual features, not spec-it-all-first\u201d. He also encourages you to review as you go, at each stage of the project:\n\n\n\tAlways reflect on what went well and what went badly, and how you can learn from that, even if not Doing Agile\u2122. Ultimately \u201cbest practices\u201d should come from learning lessons (both good and bad).\n\n\nRichard Rutter echoes this, warning against working in isolation from the client for too long:\n\n\n\tAvoid big reveals. Your engagement with the client should be participatory. In business no one likes surprises.\n\n\nThis experience rings true for Ruth John who recommends involving real users in the feedback loop, not just the client:\n\n\n\tWe also try and get feedback on what we\u2019re building as soon and as often as we can with our stakeholders/clients and real users.\n\n\nWe should also remember that our role is to serve the client\u2019s needs, not just bill them for whatever we can. Brian Suda adds:\n\n\n\tDon\u2019t sell clients on things they don\u2019t need. We can spout a lot of jargon and scare clients into thinking you are a god. We can do things few can now, but you can\u2019t rip people off because they are unknowledgeable.\n\n\nBut do clients know what they\u2019re getting, even when they see it? Trent Walton has an interesting take:\n\n\n\tWe focus on prototypes over image-based comps at all costs, especially when meetings are involved. It\u2019s much easier to assess a prototype, and too often with image-based comps, discussions devolve into how something might feel when actually live, or how a layout could change to fit a given viewport.\n\n\nVal Head also likes to get work into the browser:\n\n\n\tSketch design ideas with any software you like, but get to the browser as soon as possible.\n\n\nBeyond your immediate team, Emma Jane Westby has advice for looking further afield:\n\n\n\tInvest time into building relationships within your (technical) community. You never know when you might be able to lend a hand; or benefit from someone who\u2019s able to lend theirs.\n\n\nAnd when things don\u2019t go according to plan, Brian Suda has the following advice:\n\n\n\tIf something doesn\u2019t work out, be professional and don\u2019t burn bridges. It will always come back to you.\n\n\nThe best work comes from working collaboratively, not just as a team within an agency or department, but with the client and stakeholders too. If doing your job and chucking it over the fence ever worked, it certainly doesn\u2019t fly any more. You can work in isolation, but doing really great work requires collaboration.\n\nThe business end\n\nWhen you\u2019re building sites professionally, every team member has to think about the business aspects. Estimating time, setting billing rates, and establishing deliverables are all part of the job.\n\nIn 2008, Andrew Clarke gave us the Contract Killer sample contract we could use to establish a working agreement for a web design project. Richard Rutter agrees that contracts are still an essential part of business:\n\n\n\tThey are there for both parties\u2019 protection. Make sure you know what will happen if you decide you don\u2019t want to work with the client any more (it happens) and, of course, what circumstances mean they can stop taking your services.\n\n\nHaving a contract is one thing, but does it adequately protect both you and the client? Emma Jane Westby adds:\n\n\n\tFind a good IP lawyer/legal counsel. I routinely had an IP lawyer read all of my contracts to find loopholes I wouldn\u2019t have noticed. I didn\u2019t always change the contract, but at least I knew what might come back to bite me.\n\n\nSo, you have a contract in place, and know what the project is. Brian Suda recommends keeping track of time and making sure you bill fairly for the hours the project costs you:\n\n\n\tIf I go to a meeting and they are 15 minutes late, the billing clock has already started. They can\u2019t expect me to be in the 1h meeting and not bill for the extra 15\u201330 minutes they wasted. It goes both ways too. You need to do your best to respect their deadlines and time frame \u2013 this is always hard to get right.\n\n\nAs ever, it\u2019s good business to do good business. Perhaps we can at last shed the old image of web designers being snowboarding layabouts and demonstrate to clients that we care as much about conducting professional business as they do.\n\nTime to review\n\nIt\u2019s a lot to take in. Some of these ideas and practices will be familiar, others new and yet to be evaluated. The web moves at a fast pace, and we need to be constantly reexamining our tools, techniques and working practices. The most important thing is not to blindly adopt any and all suggestions, but to carefully look at what the benefits might be and decide how they apply to your work.\n\nCould you benefit from more formalised development and deployment procedures? Would your design projects run more smoothly and have a longer maintainable life if you approached the solution as a componentised system rather than a series of pages? Are your teams structured in a way that enables the most fluid communication, or are there changes you could make? Are your billing procedures and business agreements serving you and your clients in the best way possible?\n\nThe new year is a good time to look at your working practices and see what can be improved, and maybe this time next year you\u2019ll look back and think \u201cthank goodness we don\u2019t work like that any more\u201d.", "2014", "Drew McLellan", "drewmclellan", "2014-12-01T00:00:00+00:00", "https://24ways.org/2014/what-it-takes-to-build-a-website/", "business"], [66, "Solve the Hard Problems", "So, here we find ourselves on the cusp of 2016. We\u2019ve had a good year \u2013 the web is still alive, no one has switched it off yet. Clients still have websites, teenagers still have phone apps, and there continue to be plenty of online brands to meaningfully engage with each day. Good job team, high fives all round.\nAs it\u2019s the time to make resolutions, I wanted to share three small ideas to take into the new year.\nGet good at what you do\n\u201cHow do you get to Carnegie Hall?\u201d the old joke goes. \u201cPractise, practise, practise.\u201d \nWe work in an industry where there is an awful lot to learn. There\u2019s a lot to learn to get started and then once you do, there\u2019s a lot more to learn to keep your skills current. Just when you think you\u2019ve mastered something, it changes.\nThis is true of many industries, of course, but the sheer pace of change for us makes learning not an annual activity, but daily. Learning takes time, and while I\u2019m not convinced that every skill takes the fabled ten thousand hours to master, there is certainly no escaping that to remain current we must reinvest time in keeping our skills up to date.\nPicking where to spend your time\nOne of the hardest aspects of this thing of ours is just choosing what to learn. If you, like me, invested any time in learning the Less CSS preprocessor over the last few years, you\u2019ll probably now be spending your time relearning Sass instead. If you spent time learning Grunt, chances are you\u2019ll now be thinking about whether you should switch to Gulp. It\u2019s not just that there are new types of tools, there are new tools and frameworks to do the things you\u2019re already doing, but, well, differently.\nDeciding what to learn is hard and the costs of backing the wrong horse can seriously mount up; so much so that by the time you\u2019ve learned and then relearned the tools everyone says you need for your job, there\u2019s rarely enough time to spend really getting to know how best to use them.\n\u00a0Practise, practise, practise\nDo you know how you don\u2019t get to Carnegie Hall? By learning a new instrument each week. It takes time and experience to really learn something well. That goes for a new JavaScript framework as much as a violin. If you flit from one shiny new thing to another, you\u2019re destined to produce amateurish work forever.\nLearn the new thing, but then stick with it long enough to get really good at it \u2013 even if Twitter trolls try to convince you it\u2019s not cool. What\u2019s really not cool is living as a forevernoob. \nIf you\u2019re still not sure what to learn, go back to basics. Considering a new CSS or JavaScript framework? Invest that time in learning the underlying CSS or JavaScript really well instead. Those skills will stand the test of time.\nAudience and purpose\nBack when I was in school, my English teacher (a nice Welsh lady, who I appreciate more now than I did back then) used to love to remind us that every piece of writing should have an audience and a purpose. So much so that audience and purpose almost became her catch phrase. For every essay, article or letter, we were reminded to consider who we were writing it for and what we were trying to achieve. \nIt\u2019s something I think about a lot; certainly when writing, but also in almost every other creative endeavour. Asking who is this for and what am I trying to achieve applies equally to designing a logo or website, through to composing music or writing software.\nBeing productive\nIt seems like everyone wants to have a product these days. As someone who used to do client services work and now has a product company, I often talk with people who are interested in taking something they\u2019ve built in-house and turning it into a product. You know the sort of thing: a design agency with its own CMS or project management web app; the very logical thought process of: if this helps our business, maybe others will find it valuable too; the question that inevitably follows: could we turn this into a product?\nWhether consciously or not, the audience and purpose influence nearly every aspect of your creative process. Once written or designed or developed or created, revising a work to change the audience and purpose can be quite a challenge. No matter how much you want to turn the tension-building, atmospheric music for a horror film into a catchy chart hit, it\u2019s going to be a struggle. Yes, it\u2019s music, but that\u2019s neither the audience nor purpose for which it was created.\nThe same is absolutely true for your in-house tools \u2013 those were also designed for a specific audience and purpose. Your in-house CMS would have been designed with an audience of your own development team, who are busy implementing sites for clients. The purpose is to make that team more productive overall, taking into account considerations of maintaining multiple sites on a common codebase, training clients, a more mature and stable platform and all the other benefits of reusing the same code for each project. The audience is your team and the purpose increased productivity.\nThat\u2019s very different from a customer who wants to buy a polished system to use off-the-shelf. If their needs perfectly aligned with yours then they wouldn\u2019t be in the market for your product \u2013 they would have built their own.\nSometimes you hear the advice to \u201cscratch your own itch\u201d when it comes to product design. I don\u2019t completely agree. Got an itch? Great. Find other itchy people and sell them a backscratcher.\nBuilding a product, like designing a website, is a lot of work. It requires knowing your audience and purpose inside out. You can\u2019t fudge it and you can\u2019t just hope you\u2019ll find an audience for some old thing you have lying around.\nAlways consider the audience and purpose for everything you create. It\u2019s often the difference between success and failure.\nSolve the hard problems\nHuman beings have a natural tendency to avoid hard problems. In digital design (websites, software, whatever) the received wisdom is often that we can get 80% of the way towards doing the hard thing by doing something that\u2019s not very hard.\nDo you know what you get at the end of it? Paid. But nothing really great ever happens that way.\nI worked on a client project a while back where one of the big challenges was making full use of the massive image library they had built up over the years. The client had tens of thousands of photographs, along with a fair amount of video and a large MP3 audio library too. If it wasn\u2019t managed carefully, storage sizes would get out of control, content would go unattributed, and everything would get very messy very quickly.\nI could tell from the outset that this aspect of the project was going to be a constant problem. So we tackled it head-on. We designed and built a media management system to hold and process all the assets, and added an API so the content management system could talk to it. Every time the site needed a photo at a new size, it made an API request to the system and everything was handled seamlessly.\nIt was a daunting job to invest all the time and effort in building that dedicated system and API, but it really paid off. Instead of having the constant troubles of a vast library of media, it became one of the strongest parts of the project.\nTurn your hardest problems into your biggest strengths\nThere\u2019s a funny thing about hard problems. The hardest problems are the most fun to solve and have the biggest impact.\nMaybe you\u2019re the sort of person who clocks in for work, does their job and clocks out at 5pm without another thought. But I don\u2019t think you are, because you\u2019re here reading this. If you really love what you do, I don\u2019t think you can be satisfied in your work unless you\u2019re seeking out and working on those hard problems. That\u2019s where the magic is.\n\nThe new year is a helpful time to think about breaking bad habits. Whether it\u2019s smoking a bit less, or going to the gym a bit more, the ticking over of the calendar can provide the motivation for a new start. I have some suggestions for you.\n\nGet good at what you do. Practise your skills and don\u2019t just flit from one shiny thing to the next.\nRemember who you\u2019re doing it for and why. Consider the audience and purpose for everything you create.\nSolve the hard problems. It\u2019s more interesting, more satisfying, and has a greater impact.\n\nAs we move into 2016, these are the things I\u2019m going to continue to work on. Maybe you\u2019d like to join me.", "2015", "Drew McLellan", "drewmclellan", "2015-12-24T00:00:00+00:00", "https://24ways.org/2015/solve-the-hard-problems/", "process"], [80, "HTML5 Video Bumpers", "Video is a bigger part of the web experience than ever before. With native browser support for HTML5 video elements freeing us from the tyranny of plugins, and the availability of faster internet connections to the workplace, home and mobile networks, it\u2019s now pretty straightforward to publish video in a way that can be consumed in all sorts of ways on all sorts of different web devices.\n\nI recently worked on a project where the client had shot some dedicated video shorts to publish on their site. They also had some five-second motion graphics produced to top and tail the videos with context and branding. This pretty common requirement is a great idea on the web, where a user might land at your video having followed a link and be viewing a page without much context.\n\nKnown as bumpers, these short introduction clips help brand a video and make it look a lot more professional.\n\n\n\nAdding bumpers to a video\n\nThe simplest way to add bumpers to a video would be to edit them on to the start and end of the video file itself. Cooking the bumpers into the video file is easy, but should you ever want to update them it can become a real headache. If the branding needs updating, for example, you\u2019d need to re-edit and re-encode all your videos. Not a fun task.\n\nWhat if the bumpers could be added dynamically? That would enable you to use the same bumper for multiple videos (decreasing download time for users who might watch more than one) and to update the bumpers whenever you wanted. You could change them seasonally, update them for special promotions, run different advertising slots, perform multivariate testing, or even target different bumpers to different users.\n\nThe trade-off, of course, is that if you dynamically add your bumpers, there\u2019s a chance that a user in a given circumstance might not see the bumper. For example, if the main video feature was uploaded to YouTube, you\u2019d have no way to control the playback. As always, you need to weigh up the pros and cons and make your choice.\n\nHTML5 bumpers\n\nIf you wanted to dynamically add bumpers to your HTML5 video, how would you go about it? That was the question I found myself needing to answer for this particular client project.\n\nMy initial thought was to treat it just like an image slideshow. If I were building a slideshow that moved between images, I\u2019d use CSS absolute positioning with z-index to stack the images up on top of each other in a pile, with the first image on top. To transition to the second image, I\u2019d use JavaScript to fade the top image out, revealing the second image beneath it.\n\n\n\nNow that video is just a native object in the DOM, just like an image, why not do the same? Stack the videos up with the opening bumper on top, listen for the video\u2019s onended event, and fade it out to reveal the main feature behind. Good idea, right?\n\nWrong\n\nRemember that this is the web. It\u2019s never going to be that easy. The problem here is that many non-desktop devices use native, dedicated video players. Think about watching a video on a mobile phone \u2013 when you play the video, the phone often goes full-screen in its native player, leaving the web page behind. There\u2019s no opportunity to fade or switch z-index, as the video isn\u2019t being viewed in the page. Your page is left powerless. Powerless!\n\n\n\nSo what can we do? What can we control?\n\nThose of us with particularly long memories might recall a time before CSS, when we\u2019d have to use JavaScript to perform image rollovers. As CSS background images weren\u2019t a practical reality, we would use lots of <img> elements, and perform a rollover by modifying the src attribute of the image. \n\nTurns out, this old trick of modifying the source can help us out with video, too. In most cases, modifying the src attribute of a <video> element, or perhaps more likely the src attribute of a source element, will swap from one video to another.\n\nSwappin\u2019 it\n\nLet\u2019s take a deliberately simple example of a super-basic video tag:\n\n<video src=\"mycat.webm\" controls>no fallback coz i is lame, innit.</video>\n\nWe could very simply write a script to find all video tags and give them a new src to show our bumper.\n\n<script>\n\tvar videos, i, l;\n\tvideos = document.getElementsByTagName('video');\n\tfor(i=0, l=videos.length; i<l; i++) {\n\t\tvideos[i].setAttribute('src', 'bumper-in.webm');\n\t}\n</script>\n\nView the example in a browser with WebM support. You\u2019ll see that the video is swapped out for the opening bumper. Great!\n\nBeefing it up\n\nOf course, we can\u2019t just publish video in one format. In practical use, you need a <video> element with multiple <source> elements containing your different source formats.\n\n<video controls>\n    <source src=\"mycat.mp4\" type=\"video/mp4\" />\n    <source src=\"mycat.webm\" type=\"video/webm\" />\n    <source src=\"mycat.ogv\" type=\"video/ogg\" />\n</video>\n\nThis time, our script needs to loop through the sources, not the videos. We\u2019ll use a regular expression replacement to swap out the file name while maintaining the correct file extension.\n\n<script>\n    var sources, i, l, orig;\n    sources = document.getElementsByTagName('source');\n    for(i=0, l=sources.length; i<l; i++) {\n        orig = sources[i].getAttribute('src');\n        sources[i].setAttribute('src', orig.replace(/(w+).(w+)/, 'bumper-in.$2'));\n        // reload the video\n        sources[i].parentNode.load();\n    }\n</script>\n\nThe difference this time is that when changing the src of a <source> we need to call the .load() method on the video to get it to acknowledge the change.\n\nSee the code in action, this time in a wider range of browsers.\n\nBut, my video!\n\nI guess we should get the original video playing again. Keeping the same markup, we need to modify the script to do two things:\n\n\n\tStore the original src in a data- attribute so we can access it later\n\tAdd an event listener so we can detect the end of the bumper playing, and load the original video back in\n\n\nAs we need to loop through the videos this time to add the event listener, I\u2019ve moved the .load() call into that loop. It\u2019s a bit more efficient to call it only once after modifying all the video\u2019s sources.\n\n<script>\nvar videos, sources, i, l, orig;\nsources = document.getElementsByTagName('source');\nfor(i=0, l=sources.length; i<l; i++) {\n    orig = sources[i].getAttribute('src');\n    sources[i].setAttribute('data-orig', orig);\n    sources[i].setAttribute('src', orig.replace(/(w+).(w+)/, 'bumper-in.$2'));\n}\nvideos = document.getElementsByTagName('video');\nfor(i=0, l=videos.length; i<l; i++) {\n    videos[i].load();\n    videos[i].addEventListener('ended', function(){\n        sources = this.getElementsByTagName('source');\n        for(i=0, l=sources.length; i<l; i++) {\n            orig = sources[i].getAttribute('data-orig');\n            if (orig) {\n                sources[i].setAttribute('src', orig);\n            }\n            sources[i].setAttribute('data-orig','');\n        }\n        this.load();\n        this.play();\n    });\n}\n</script>\n\nAgain, view the example to see the bumper play, followed by our spectacular main feature. (That\u2019s my cat, Widget. His interests include sleeping and internet marketing.)\n\nTidying things up\n\nThe final thing to do is add our closing bumper after the main video has played. This involves the following changes:\n\n\n\tWe need to keep track of whether the src has been changed, so we only play the video if it\u2019s changed. I\u2019ve added the modified variable to track this, and it stops us getting into a situation where the video just loops forever.\n\tAdd an else to the event listener, for when the orig is false (so the main feature has been playing) to load in the end bumper. We also check that we\u2019re not already playing the end bumper. Because looping.\n\n\n<script>\nvar videos, sources, i, l, orig, current, modified;\nsources = document.getElementsByTagName('source');\nfor(i=0, l=sources.length; i<l; i++) {\n   orig = sources[i].getAttribute('src');\n   sources[i].setAttribute('data-orig', orig);\n   sources[i].setAttribute('src', orig.replace(/(w+).(w+)/, 'bumper-in.$2'));\n}\nvideos = document.getElementsByTagName('video');\nfor(i=0, l=videos.length; i<l; i++) {\n   videos[i].load();\n   modified = false;\n   videos[i].addEventListener('ended', function(){\n      sources = this.getElementsByTagName('source');\n      for(i=0, l=sources.length; i<l; i++) {\n         orig = sources[i].getAttribute('data-orig');\n         if (orig) {\n            sources[i].setAttribute('src', orig);\n            modified = true;\n         }else{\n            current = sources[i].getAttribute('src');\n            if (current.indexOf('bumper-out')==-1) {\n               sources[i].setAttribute('src', current.replace(/([w]+).(w+)/, 'bumper-out.$2'));\n               modified = true;\n            }else{\n               this.pause();\n               modified = false;\n            }\n         }\n         sources[i].setAttribute('data-orig','');\n      }\n      if (modified) {\n         this.load();\n         this.play();\n      }\n   });\n}\n</script>\n\nYo ho ho, that\u2019s a lot of JavaScript. See it in action \u2013 you should get a bumper, the cat video, and an end bumper.\n\nOf course, this code works fine for demonstrating the principle, but it\u2019s very procedural. Nothing wrong with that, but to do something similar in production, you\u2019d probably want to make the code more modular to ease maintainability. Besides, you may want to use a framework, rather than basic JavaScript. \n\nThe end credits\n\nOne really important principle here is that of progressive enhancement. If the browser doesn\u2019t support JavaScript, the user won\u2019t see your bumper, but they will get the main video. If the browser supports JavaScript but doesn\u2019t allow you to modify the src (as was the case with older versions of iOS), the user won\u2019t see your bumper, but they will get the main video.\n\nIf a search engine or social media bot grabs your page and looks for content, they won\u2019t see your bumper, but they will get the main video \u2013 which is absolutely what you want.\n\nThis means that if the bumper is absolutely crucial, you may still need to cook it into the video. However, for many applications, running it dynamically can work quite well.\n\nAs always, it comes down to three things:\n\n\n\tMeasure your audience: know how people access your site\n\tTest the solution: make sure it works for your audience\n\tPlan for failure: it\u2019s the web and that\u2019s how things work \u2018round these parts\n\n\nBut most of all play around with it, have fun and build something awesome.", "2012", "Drew McLellan", "drewmclellan", "2012-12-01T00:00:00+00:00", "https://24ways.org/2012/html5-video-bumpers/", "code"], [101, "Easing The Path from Design to Development", "As a web developer, I have the pleasure of working with a lot of different designers. There has been a lot of industry discussion of late about designers and developers, focusing on how different we sometimes are and how the interface between our respective phases of a project (that is to say moving from a design phase into production) can sometimes become a battleground.\n\nI don\u2019t believe it has to be a battleground. It\u2019s actually more like being a dance partner \u2013 our steps are different, but as long as we know our own part and have a little knowledge of our partner\u2019s steps, it all goes together to form a cohesive dance. Albeit with less spandex and fewer sequins (although that may depend on the project in question).\n\nAs the process usually flows from design towards development, it\u2019s most important that designers have a little knowledge of how the site is going to be built. At the specialist web development agency I\u2019m part of, we find that designs that have been well considered from a technical perspective help to keep the project on track and on budget.\n\nBased on that experience, I\u2019ve put together my checklist of things that designers should consider before handing their work over to a developer to build.\n\nLayout\n\nOne rookie mistake made by traditionally trained designers transferring to the web is to forget a web browser is not a fixed medium. Unlike designing a magazine layout or a piece of packaging, there are lots of available options to consider. Should the layout be fluid and resize with the window, or should it be set to a fixed width? If it\u2019s fluid, which parts expand and which not? If it\u2019s fixed, should it sit in the middle of the window or to one side?\n\nIf any part of the layout is going to be flexible (get wider and narrower as required), consider how any graphics are affected. Images don\u2019t usually look good if displayed at anything other that their original size, so should they behave? If a column is going to get wider than it\u2019s shown in the Photoshop comp, it may be necessary to provide separate wider versions of any background images.\n\nText size and content volume\n\nA related issue is considering how the layout behaves with both different sizes of text and different volumes of content. Whilst text zooming rather than text resizing is becoming more commonplace as the default behaviour in browsers, it\u2019s still a fundamentally important principal of web design that we are suggesting and not dictating how something should look. Designs must allow for a little give and take in the text size, and how this affects the design needs to be taken into consideration.\n\nKeep in mind that the same font can display differently in different places and platforms. Something as simple as Times will display wider on a Mac than on Windows. However, the main impact of text resizing is the change in how much vertical space copy takes up. This is particularly important where space is limited by the design (making text bigger causes many more problems than making text smaller). Each element from headings to box-outs to navigation items and buttons needs to be able to expand at least vertically, if not horizontally as well. This may require some thought about how elements on the page may wrap onto new lines, as well as again making sure to provide extended versions of any graphical elements.\n\nSimilarly, it\u2019s rare theses days to know exactly what content you\u2019re working with when a site is designed. Many, if not most sites are designed as a series of templates for some kind of content management system, and so designs cannot be tweaked around any specific item of content. Designs must be able to cope with both much greater and much lesser volumes of content that might be thrown in at the lorem ipsum phase.\n\nParticular things to watch out for are things like headings (how do they wrap onto multiple lines) and any user-generated items like usernames. It can be very easy to forget that whilst you might expect something like a username to be 8-12 characters, if the systems powering your site allow for 255 characters they\u2019ll always be someone who\u2019ll go there. Expect them to do so.\n\nAgain, if your site is content managed or not, consider the possibility that the structure might be expanded in the future. Consider how additional items might be added to each level of navigation. Whilst it\u2019s rarely desirable to make significant changes without revisiting the site\u2019s information architecture more thoroughly, it\u2019s an inevitable fact of life that the structure needs a little bit of flexibility to change over time.\n\nInteractions with and without JavaScript\n\nA great number of sites now make good use of JavaScript to streamline the user interface and make everything just that touch more usable. Remember, though, that any developer worth their salt will start by building the interface without JavaScript, get it all working, and then layer that JavaScript on top. This is to allow for users viewing the site without JavaScript available or enabled in their browser.\n\nDesigners need to consider both states of any feature they\u2019re designing \u2013 how it looks and functions with and without JavaScript. If the feature does something fancy with Ajax, consider how the same can be achieved with basic HTML forms, links and intermediary pages. These all need to be designed, because this is how some of your users will interact with the site.\n\nLogged in and logged out states\n\nWhen designing any type of web application or site that has a membership system \u2013 that is to say users can create an account and log into the site \u2013 the design will need to consider how any element is presented in both logged in and logged out states. For some items there\u2019ll be no difference, whereas for others there may be considerable differences.\n\nShould an item be hidden completely not logged out users? Should it look different in some way? Perhaps it should look the same, but prompt the user to log in when they interact with it. If so, what form should that prompt take on and how does the user progress through the authentication process to arrive back at the task they were originally trying to complete?\n\nCouple logged in and logged out states with the possible absence of JavaScript, and every feature needs to be designed in four different states:\n\n\n\tLogged out with JavaScript available\n\tLogged in with JavaScript available\n\tLogged out without JavaScript available\n\tLogged in without JavaScript available\n\n\nFonts\n\nThere are three main causes of war in this world; religions, politics and fonts. I\u2019ve said publicly before that I believe the responsibility for this falls squarely at the feet of Adobe Photoshop. Photoshop, like a mistress at a brothel, parades a vast array of ropey, yet strangely enticing typefaces past the eyes of weak, lily-livered designers, who can\u2019t help but crumble to their curvy charms.\n\nYet, on the web, we have to be a little more restrained in our choice of typefaces. The purest solution is always to make the best use of the available fonts, but this isn\u2019t always the most desirable solution from a design point of view. There are several technical solutions such as techniques that utilise Flash (like sIFR), dynamically generated images and even canvas in newer browsers. Discuss the best approach with your developer, as every different technique has different trade-offs, and this may impact the design in other ways.\n\nMessaging\n\nAny site that has interactive elements, from a simple contact form through to fully featured online software application, involves some kind of user messaging. By this I mean the error messages when something goes wrong and the success and thank-you messages when something goes right. These typically appear as the result of an interaction, so are easy to forget and miss off a Photoshop comp.\n\nFor every form, consider what gets displayed to the user if they make a mistake or miss something out, and also what gets displayed back when the interaction is successful. What do they see and where do the go next?\n\nWith Ajax interactions, the user doesn\u2019t get any visual feedback of the site waiting for a response from the server unless you design it that way. Consider using a \u2018waiting\u2019 or \u2018in progress\u2019 spinner to give the user some visual feedback of any background processes. How should these look? How do they animate?\n\nSimilarly, also consider the big error pages like a 404. With luck, these won\u2019t often be seen, but it\u2019s at the point that they are when careful design matters the most.\n\nForm fields\n\nDepending on the visual style of your site, the look of a browser\u2019s default form fields and buttons can sometimes jar. It\u2019s understandable that many a designer wants to change the way they look. Depending on the browser in question, various things can be done to style form fields and their buttons (although it\u2019s not as flexible as most would like).\n\nA common request is to replace the default buttons with a graphical button. This is usually achievable in most cases, although it\u2019s not easy to get a consistent result across all browsers \u2013 particularly when it comes to vertical positioning and the space surrounding the button. If the layout is very precise, or if space is at a premium, it\u2019s always best to try and live with the browser\u2019s default form controls.\n\nWhichever way you go, it\u2019s important to remember that in general, each form field should have a label, and each form should have a submit button. If you find that your form breaks either of those rules, you should double check.\n\nPractical tips for handing files over\n\nThere are a couple of basic steps that a design can carry out to make sure that the developer has the best chance of implementing the design exactly as envisioned.\n\nIf working with Photoshop of Fireworks or similar comping tool, it helps to group and label layers to make it easy for a developer to see which need to be turned on and off to get to isolate parts of the page and different states of the design. Also, if you don\u2019t work in the same office as your developer (and so they can\u2019t quickly check with you), provide a PDF of each page and state so that your developer can see how each page should look aside from any confusion with quick layers are switched on or off. These also act as a handy quick reference that can be used without firing up Photoshop (which can kill both productivity and your machine).\n\nFinally, provide a colour reference showing the RGB values of all the key colours used throughout the design. Without this, the developer will end up colour-picking from the comps, and could potentially end up with different colours to those you intended. Remember, for a lot of developers, working in a tool like Photoshop is like presenting a designer with an SSH terminal into a web server. It\u2019s unfamiliar ground and easy to get things wrong. Be the expert of your own domain and help your colleagues out when they\u2019re out of their comfort zone. That goes both ways.\n\nIn conclusion\n\nWhen asked the question of how to smooth hand-over between design and development, almost everyone who has experienced this situation could come up with their own list. This one is mine, based on some of the more common experiences we have at edgeofmyseat.com. So in bullet point form, here\u2019s my checklist for handing a design over.\n\n\n\tIs the layout fixed, or fluid?\n\tDoes each element cope with expanding for larger text and more content?\n\tAre all the graphics large enough to cope with an area expanding?\n\tDoes each interactive element have a state for with and without JavaScript?\n\tDoes each element have a state for logged in and logged out users?\n\tHow are any custom fonts being displayed? (and does the developer have the font to use?)\n\tDoes each interactive element have error and success messages designed?\n\tDo all form fields have a label and each form a submit button?\n\tIs your Photoshop comp document well organised?\n\tHave you provided flat PDFs of each state?\n\tHave you provided a colour reference?\n\tAre we having fun yet?", "2008", "Drew McLellan", "drewmclellan", "2008-12-01T00:00:00+00:00", "https://24ways.org/2008/easing-the-path-from-design-to-development/", "process"], [124, "Writing Responsible JavaScript", "Without a doubt, JavaScript has been making something of a comeback in the last year. If you\u2019re involved in client-side development in any way at all, chances are that you\u2019re finding yourself writing more JavaScript now than you have in a long time.\n\nIf you learned most of your JavaScript back when DHTML was all the rage and before DOM Scripting was in vogue, there have been some big shifts in the way scripts are written. Most of these are in the way event handlers are assigned and functions declared. Both of these changes are driven by the desire to write scripts that are responsible page citizens, both in not tying behaviour to content and in taking care not to conflict with other scripts. I thought it may be useful to look at some of these more responsible approaches to learn how to best write scripts that are independent of the page content and are safely portable between different applications.\n\nEvent Handling\n\nBack in the heady days of Web 1.0, if you wanted to have an object on the page react to something like a click, you would simply go ahead and attach an onclick attribute. This was easy and understandable, but much like the font tag or the style attribute, it has the downside of mixing behaviour or presentation in with our content. As we\u2019re learned with CSS, there are big benefits in keeping those layers separate. Hey, if it works for CSS, it should work for JavaScript too.\n\nJust like with CSS, instead of adding an attribute to our element within the document, the more responsible way to do that is to look for the item from your script (like CSS does with a selector) and then assign the behaviour to it. To give an example, take this oldskool onclick use case:\n\n<a id=\"anim-link\" href=\"#\" onclick=\"playAnimation()\">Play the animation</a>\n\nThis could be rewritten by removing the onclick attribute, and instead doing the following from within your JavaScript.\n\ndocument.getElementById('anim-link').onclick = playAnimation;\n\nIt\u2019s all in the timing\n\nOf course, it\u2019s never quite that easy. To be able to attach that onclick, the element you\u2019re targeting has to exist in the page, and the page has to have finished loading for the DOM to be available. This is where the onload event is handy, as it fires once everything has finished loading. Common practise is to have a function called something like init() (short for initialise) that sets up all these event handlers as soon as the page is ready.\n\nBack in the day we would have used the onload attibute on the <body> element to do this, but of course what we really want is:\n\nwindow.onload = init;\n\nAs an interesting side note, we\u2019re using init here rather than init() so that the function is assigned to the event. If we used the parentheses, the init function would have been run at that moment, and the result of running the function (rather than the function itself) would be assigned to the event. Subtle, but important.\n\nAs is becoming apparent, nothing is ever simple, and we can\u2019t just go around assigning our initialisation function to window.onload. What if we\u2019re using other scripts in the page that might also want to listen out for that event? Whichever script got there last would overwrite everything that came before it. To manage this, we need a script that checks for any existing event handlers, and adds the new handler to it. Most of the JavaScript libraries have their own systems for doing this. If you\u2019re not using a library, Simon Willison has a good stand-alone example\n\nfunction addLoadEvent(func) {\n\tvar oldonload = window.onload;\n\tif (typeof window.onload != 'function') {\n\t\twindow.onload = func;\n\t} else {\n\t\twindow.onload = function() {\n\t\t\tif (oldonload) {\n\t\t\t\toldonload();\n\t\t\t}\n\t\t\tfunc();\n\t\t}\n\t}\n}\n\nObviously this is just a toe in the events model\u2019s complex waters. Some good further reading is PPK\u2019s Introduction to Events.\n\nCarving out your own space\n\nAnother problem that rears its ugly head when combining multiple scripts on a single page is that of making sure that the scripts don\u2019t conflict. One big part of that is ensuring that no two scripts are trying to create functions or variables with the same names. Reusing a name in JavaScript just over-writes whatever was there before it.\n\nWhen you create a function in JavaScript, you\u2019ll be familiar with doing something like this.\n\nfunction foo() {\n\t... goodness ...\n}\n\nThis is actually just creating a variable called foo and assigning a function to it. It\u2019s essentially the same as the following.\n\nvar foo = function() {\n\t... goodness ...\n}\n\nThis name foo is by default created in what\u2019s known as the \u2018global namespace\u2019 \u2013 the general pool of variables within the page. You can quickly see that if two scripts use foo as a name, they will conflict because they\u2019re both creating those variables in the global namespace.\n\nA good solution to this problem is to add just one name into the global namespace, make that one item either a function or an object, and then add everything else you need inside that. This takes advantage of JavaScript\u2019s variable scoping to contain you mess and stop it interfering with anyone else.\n\nCreating An Object\n\nSay I was wanting to write a bunch of functions specifically for using on a site called \u2018Foo Online\u2019. I\u2019d want to create my own object with a name I think is likely to be unique to me.\n\nvar FOOONLINE = {};\n\nWe can then start assigning functions are variables to it like so:\n\nFOOONLINE.message = 'Merry Christmas!';\nFOOONLINE.showMessage = function() {\n\talert(this.message);\n};\n\nCalling FOOONLINE.showMessage() in this example would alert out our seasonal greeting. The exact same thing could also be expressed in the following way, using the object literal syntax.\n\nvar FOOONLINE = {\n\tmessage: 'Merry Christmas!',\n\tshowMessage: function() {\n\t\talert(this.message);\n\t}\n};\n\nCreating A Function to Create An Object\n\nWe can extend this idea bit further by using a function that we run in place to return an object. The end result is the same, but this time we can use closures to give us something like private methods and properties of our object.\n\nvar FOOONLINE = function(){\n\tvar message = 'Merry Christmas!';\n\treturn {\n\t\tshowMessage: function(){\n\t\t\talert(message);\n\t\t}\n\t}\n}();\n\nThere are two important things to note here. The first is the parentheses at the end of line 10. Just as we saw earlier, this runs the function in place and causes its result to be assigned. In this case the result of our function is the object that is returned at line 4.\n\nThe second important thing to note is the use of the var keyword on line 2. This ensures that the message variable is created inside the scope of the function and not in the global namespace. Because of the way closure works (which if you\u2019re not familiar with, just suspend your disbelief for a moment) that message variable is visible to everything inside the function but not outside. Trying to read FOOONLINE.message from the page would return undefined.\n\nThis is useful for simulating the concept of private class methods and properties that exist in other programming languages. I like to take the approach of making everything private unless I know it\u2019s going to be needed from outside, as it makes the interface into your code a lot clearer for someone else to read. \n\nAll Change, Please\n\nSo that was just a whistle-stop tour of a couple of the bigger changes that can help to make your scripts better page citizens. I hope it makes useful Sunday reading, but obviously this is only the tip of the iceberg when it comes to designing modular, reusable code.\n\nFor some, this is all familiar ground already. If that\u2019s the case, I encourage you to perhaps submit a comment with any useful resources you\u2019ve found that might help others get up to speed. Ultimately it\u2019s in all of our interests to make sure that all our JavaScript interoperates well \u2013 share your tips.", "2006", "Drew McLellan", "drewmclellan", "2006-12-10T00:00:00+00:00", "https://24ways.org/2006/writing-responsible-javascript/", "code"], [132, "Tasty Text Trimmer", "In most cases, when designing a user interface it\u2019s best to make a decision about how data is best displayed and stick with it. Failing to make a decision ultimately leads to too many user options, which in turn can be taxing on the poor old user. \n\nUnder some circumstances, however, it\u2019s good to give the user freedom in customising their workspace. One good example of this is the \u2018Article Length\u2019 tool in Apple\u2019s Safari RSS reader. Sliding a slider left of right dynamically changes the length of each article shown. It\u2019s that kind of awesomey magic stuff that\u2019s enough to keep you from sleeping. Let\u2019s build one.\n\nThe Setup\n\nLet\u2019s take a page that has lots of long text items, a bit like a news page or like Safari\u2019s RSS items view. If we were to attach a class name to each element we wanted to resize, that would give us something to hook onto from the JavaScript. \n\nExample 1: The basic page.\n\nAs you can see, I\u2019ve wrapped my items in a DIV and added a class name of chunk to them. It\u2019s these chunks that we\u2019ll be finding with the JavaScript. Speaking of which \u2026\n\nOur Core Functions\n\nThere are two main tasks that need performing in our script. The first is to find the chunks we\u2019re going to be resizing and store their original contents away somewhere safe. We\u2019ll need this so that if we trim the text down we\u2019ll know what it was if the user decides they want it back again. We\u2019ll call this loadChunks. \n\nvar loadChunks = function(){\n\tvar everything = document.getElementsByTagName('*');\n\tvar i, l;\n\tchunks\t= [];\n\tfor (i=0, l=everything.length; i<l; i++){\n\t\tif (everything[i].className.indexOf(chunkClass) > -1){\n\t\t\tchunks.push({\n\t\t\t\tref: everything[i],\n\t\t\t\toriginal: everything[i].innerHTML\n\t\t\t});\n\t\t}\n\t}\n};\n\nThe variable chunks is stored outside of this function so that we can access it from our next core function, which is doTrim.\n\nvar doTrim = function(interval) {\n\tif (!chunks) loadChunks();\n\tvar i, l;\n\tfor (i=0, l=chunks.length; i<l; i++){\n\t\tvar a = chunks[i].original.split(' ');\n\t\ta = a.slice(0, interval);\n\t\tchunks[i].ref.innerHTML\t= a.join(' ');\n\t}\n};\n\nThe first thing that needs to be done is to call loadChunks if the chunks variable isn\u2019t set. This should only happen the first time doTrim is called, as from that point the chunks will be loaded.\n\nThen all we do is loop through the chunks and trim them. The trimming itself (lines 6-8) is very simple. We split the text into an array of words (line 6), then select only a portion from the beginning of the array up until the number we want (line 7). Finally the words are glued back together (line 8).\n\nIn essense, that\u2019s it, but it leaves us needing to know how to get the number into this function in the first place, and how that number is generated by the user. Let\u2019s look at the latter case first.\n\nThe YUI Slider Widget\n\nThere are lots of JavaScript libraries available at the moment. A fair few of those are really good. I use the Yahoo! User Interface Library professionally, but have only recently played with their pre-build slider widget. Turns out, it\u2019s pretty good and perfect for this task. \n\nOnce you have the library files linked in (check the docs linked above) it\u2019s fairly straightforward to create yourself a slider.\n\nslider = YAHOO.widget.Slider.getHorizSlider(\"sliderbg\", \"sliderthumb\", 0, 100, 5); \nslider.setValue(50);\nslider.subscribe(\"change\", doTrim);\n\nAll that\u2019s needed then is some CSS to make the slider look like a slider, and of course a few bits of HTML. We\u2019ll see those later.\n\nSee It Working!\n\nRather than spell out all the nuts and bolts of implementing this fairly simple script, let\u2019s just look at in it action and then pick on some interesting bits I\u2019ve added.\n\nExample 2: Try the Tasty Text Trimmer.\n\nAt the top of the JavaScript file I\u2019ve added a small number of settings.\n\nvar chunkClass\t= 'chunk';\nvar minValue\t= 10;\nvar maxValue\t= 100;\nvar multiplier\t= 5;\n\nObvious candidates for configuration are the class name used to find the chunks, and also the some minimum and maximum values. The minValue is the fewest number of words we wish to display when the slider is all the way down. The maxValue is the length of the slider, in this case 100.\n\nMoving the slider makes a call to our doTrim function with the current value of the slider. For a slider 100 pixels long, this is going to be in the range of 0-100. That might be okay for some things, but for longer items of text you\u2019ll want to allow for displaying more than 100 words. I\u2019ve accounted for this by adding in a multiplier \u2013 in my code I\u2019m multiplying the value by 5, so a slider value of 50 shows 250 words. You\u2019ll probably want to tailor the multiplier to the type of content you\u2019re using.\n\nKeeping it Accessible \n\nThis effect isn\u2019t something we can really achieve without JavaScript, but even so we must make sure that this functionality has no adverse impact on the page when JavaScript isn\u2019t available. This is achieved by adding the slider markup to the page from within the insertSliderHTML function.\n\nvar insertSliderHTML = function(){\n\tvar s = '<a id=\"slider-less\" href=\"#less\"><img src=\"icon_min.gif\" width=\"10\" height=\"10\" alt=\"Less text\" class=\"first\" /></a>';\n\ts +=' <div id=\"sliderbg\"><div id=\"sliderthumb\"><img src=\"sliderthumbimg.gif\" /></div></div>';\n\ts +=' <a id=\"slider-more\" href=\"#more\"><img src=\"icon_max.gif\" width=\"10\" height=\"10\" alt=\"More text\" /></a>';\n\tdocument.getElementById('slider').innerHTML = s;\n}\n\nThe other important factor to consider is that a slider can be tricky to use unless you have good eyesight and pretty well controlled motor skills. Therefore we should provide a method of changing the value by the keyboard.\n\nI\u2019ve done this by making the icons at either end of the slider links so they can be tabbed to. Clicking on either icon fires the appropriate JavaScript function to show more or less of the text.\n\nIn Conclusion\n\nThe upshot of all this is that without JavaScript the page just shows all the text as it normally would. With JavaScript we have a slider for trimming the text excepts that can be controlled with the mouse or with a keyboard.\n\nIf you\u2019re like me and have just scrolled to the bottom to find the working demo, here it is again:\n\nTry the Tasty Text Trimmer\n\nTrimmer for Christmas? Don\u2019t say I never give you anything!", "2006", "Drew McLellan", "drewmclellan", "2006-12-01T00:00:00+00:00", "https://24ways.org/2006/tasty-text-trimmer/", "code"], [165, "Transparent PNGs in Internet Explorer 6", "Newer breeds of browser such as Firefox and Safari have offered support for PNG images with full alpha channel transparency for a few years. With the use of hacks, support has been available in Internet Explorer 5.5 and 6, but the hacks are non-ideal and have been tricky to use. With IE7 winning masses of users from earlier versions over the last year, full PNG alpha-channel transparency is becoming more of a reality for day-to-day use.\n\nHowever, there are still numbers of IE6 users out there who we can\u2019t leave out in the cold this Christmas, so in this article I\u2019m going to look what we can do to support IE6 users whilst taking full advantage of transparency for the majority of a site\u2019s visitors.\n\nSo what\u2019s alpha channel transparency?\n\nCast your minds back to the Ghost of Christmas Past, the humble GIF. Images in GIF format offer transparency, but that transparency is either on or off for any given pixel. Each pixel\u2019s either fully transparent, or a solid colour. In GIF, transparency is effectively just a special colour you can chose for a pixel.\n\nThe PNG format tackles the problem rather differently. As well as having any colour you chose, each pixel also carries a separate channel of information detailing how transparent it is. This alpha channel enables a pixel to be fully transparent, fully opaque, or critically, any step in between.\n\nThis enables designers to produce images that can have, for example, soft edges without any of the \u2018halo effect\u2019 traditionally associated with GIF transparency. If you\u2019ve ever worked on a site that has different colour schemes and therefore requires multiple versions of each graphic against a different colour, you\u2019ll immediately see the benefit. \n\nWhat\u2019s perhaps more interesting than that, however, is the extra creative freedom this gives designers in creating beautiful sites that can remain web-like in their ability to adjust, scale and reflow.\n\nThe Internet Explorer problem\n\nUp until IE7, there has been no fully native support for PNG alpha channel transparency in Internet Explorer. However, since IE5.5 there has been some support in the form of proprietary filter called the AlphaImageLoader. Internet Explorer filters can be applied directly in your CSS (for both inline and background images), or by setting the same CSS property with JavaScript. \n\nCSS:\n\nimg {\n\tfilter: progid:DXImageTransform.Microsoft.AlphaImageLoader(...);\n}\n\nJavaScript:\n\nimg.style.filter = \"progid:DXImageTransform.Microsoft.AlphaImageLoader(...)\";\n\nThat may sound like a problem solved, but all is not as it may appear. Firstly, as you may realise, there\u2019s no CSS property called filter in the W3C CSS spec. It\u2019s a proprietary extension added by Microsoft that could potentially cause other browsers to reject your entire CSS rule. \n\nSecondly, AlphaImageLoader does not magically add full PNG transparency support so that a PNG in the page will just start working. Instead, when applied to an element in the page, it draws a new rendering surface in the same space that element occupies and loads a PNG into it. If that sounds weird, it\u2019s because that\u2019s precisely what it is. However, by and large the result is that PNGs with an alpha channel can be accommodated.\n\nThe pitfalls\n\nSo, whilst support for PNG transparency in IE5.5 and 6 is possible, it\u2019s not without its problems.\n\nBackground images cannot be positioned or repeated\n\nThe AlphaImageLoader does work for background images, but only for the simplest of cases. If your design requires the image to be tiled (background-repeat) or positioned (background-position) you\u2019re out of luck. The AlphaImageLoader allows you to set a sizingMethod to either crop the image (if necessary) or to scale it to fit. Not massively useful, but something at least.\n\nDelayed loading and resource use\n\nThe AlphaImageLoader can be quite slow to load, and appears to consume more resources than a standard image when applied. Typically, you\u2019d need to add thousands of GIFs or JPEGs to a page before you saw any noticeable impact on the browser, but with the AlphaImageLoader filter applied Internet Explorer can become sluggish after just a handful of alpha channel PNGs.\n\nThe other noticeable effect is that as more instances of the AlphaImageLoader are applied, the longer it takes to render the PNGs with their transparency. The user sees the PNG load in its original non-supported state (with black or grey areas where transparency should be) before one by one the filter kicks in and makes them properly transparent.\n\nBoth the issue of sluggish behaviour and delayed load only really manifest themselves with volume and size of image. Use just a couple of instances and it\u2019s fine, but be careful adding more than five or six. As ever, test, test, test.\n\nLinks become unclickable, forms unfocusable \n\nThis is a big one. There\u2019s a bug/weirdness with AlphaImageLoader that sometimes prevents interaction with links and forms when a PNG background image is used. This is sometimes reported as a z-index issue, but I don\u2019t believe it is. Rather, it\u2019s an artefact of that weird way the filter gets applied to the document almost outside of the normal render process. \n\nOften this can be solved by giving the links or form elements hasLayout using position: relative; where possible. However, this doesn\u2019t always work and the non-interaction problem cannot always be solved. You may find yourself having to go back to the drawing board.\n\nSidestepping the danger zones\n\nFrankly, it\u2019s pretty bad news if you design a site, have that design signed off by your client, build it and then find out only at the end (because you don\u2019t know what might trigger a problem) that your search field can\u2019t be focused in IE6. That\u2019s an absolute nightmare, and whilst it\u2019s not likely to happen, it\u2019s possible that it might. It\u2019s happened to me. So what can you do?\n\nThe best approach I\u2019ve found to this scenario is\n\n\n\tIsolate the PNG or PNGs that are causing the problem. Step through the PNGs in your page, commenting them out one by one and retesting. Typically it\u2019ll be the nearest PNG to the problem, so try there first. Keep going until you can click your links or focus your form fields.\n\tThis is where you really need luck on your side, because you\u2019re going to have to fake it. This will depend on the design of the site, but some way or other create a replacement GIF or JPEG image that will give you an acceptable result. Then use conditional comments to serve that image to only users of IE older than version 7.\n\n\nA hack, you say? Well, you started it chum.\n\nApplying AlphaImageLoader\n\nBecause the filter property is invalid CSS, the safest pragmatic approach is to apply it selectively with JavaScript for only Internet Explorer versions 5.5 and 6. This helps ensure that by default you\u2019re serving standard CSS to browsers that support both the CSS and PNG standards correct, and then selectively patching up only the browsers that need it. \n\nSeveral years ago, Aaron Boodman wrote and released a script called sleight for doing just that. However, sleight dealt only with images in the page, and not background images applied with CSS. Building on top of Aaron\u2019s work, I hacked sleight and came up with bgsleight for applying the filter to background images instead. That was in 2003, and over the years I\u2019ve made a couple of improvements here and there to keep it ticking over and to resolve conflicts between sleight and bgsleight when used together. However, with alpha channel PNGs becoming much more widespread, it\u2019s time for a new version.\n\nIntroducing SuperSleight\n\nSuperSleight adds a number of new and useful features that have come from the day-to-day needs of working with PNGs.\n\n\n\tWorks with both inline and background images, replacing the need for both sleight and bgsleight\n\tWill automatically apply position: relative to links and form fields if they don\u2019t already have position set. (Can be disabled.)\n\tCan be run on the entire document, or just a selected part where you know the PNGs are. This is better for performance.\n\tDetects background images set to no-repeat and sets the scaleMode to crop rather than scale.\n\tCan be re-applied by any other JavaScript in the page \u2013 useful if new content has been loaded by an Ajax request.\n\n\n  Download SuperSleight \n\nImplementation\n\nGetting SuperSleight running on a page is quite straightforward, you just need to link the supplied JavaScript file (or the minified version if you prefer) into your document inside conditional comments so that it is delivered to only Internet Explorer 6 or older.\n\n<!--[if lte IE 6]>\n\t<script type=\"text/javascript\" src=\"supersleight-min.js\"></script>\n<![endif]-->\n\nSupplied with the JavaScript is a simple transparent GIF file. The script replaces the existing PNG with this before re-layering the PNG over the top using AlphaImageLoaded. You can change the name or path of the image in the top of the JavaScript file, where you\u2019ll also find the option to turn off the adding of position: relative to links and fields if you don\u2019t want that.\n\nThe script is kicked off with a call to supersleight.init() at the bottom. The scope of the script can be limited to just one part of the page by passing an ID of an element to supersleight.limitTo(). And that\u2019s all there is to it.\n\nUpdate March 2008: a version of this script as a jQuery plugin is also now available.", "2007", "Drew McLellan", "drewmclellan", "2007-12-01T00:00:00+00:00", "https://24ways.org/2007/supersleight-transparent-png-in-ie6/", "code"], [166, "Performance On A Shoe String", "Back in the summer, I happened to notice the official Wimbledon All England Tennis Club site had jumped to the top of Alexa\u2019s Movers & Shakers list \u2014 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. \n\nMany sites have similar peaks in traffic, especially when they\u2019re based around scheduled events. No one cares about the site for most of the year, and then all of a sudden \u2013 wham! \u2013 things start getting warm in the data centre. Whilst the thought of chestnuts roasting on an open server has a certain appeal, it\u2019s 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.\n\nTraffic graph from Alexa.com \n\nWhilst 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\u2019ve 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 slashdotted, end up on the front page of Digg or linked to from a similarly high-profile site. You just don\u2019t know when the peaks will be.\n\nIf you\u2019re 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\u2019t 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.\n\nIn this article I\u2019m going to talk about some of the things we\u2019ve learned about keeping 24 ways running throughout December, whilst not spending a fortune on hosting we don\u2019t need for 11 months of each year. We\u2019ve not always got it right, but we\u2019ve learned a lot along the way.\n\nThe Problem\n\nTo 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 virtual dedicated server with a great little hosting company in the UK. We run Apache with PHP 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\u2019s one available not doing anything). Each process takes a bunch of memory, so there\u2019s 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.\n\nWith 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:\n\n\n\tReducing the amount of memory needed by each Apache process\n\tReducing the amount of time each process is needed\n\tReducing the number of requests made to the server\n\n\nYahoo! have published some information on what they call Exceptional Performance, 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.\n\nServer tweaking\n\nIf you\u2019re 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.\n\nFirstly, 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 top and see what\u2019s 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\u2019t use mail (ours doesn\u2019t) make sure it\u2019s shut down and not using resources.\n\nSecondly, 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.\n\nThe final thing to check is that Apache isn\u2019t configured to start more servers than you have memory for. This is usually done by setting the MaxClients 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\u2019t 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\u2019t even log in to sort it out.\n\nThose are the main tidbits I\u2019ve found useful for this site, although it\u2019s worth repeating that entire books have been written on this subject alone.\n\nCaching\n\nAlthough the site is generated with PHP and MySQL, the majority of pages served don\u2019t 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 CPU. Unless a new article or comment is published, the site doesn\u2019t 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.\n\nWe use QuickCache (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 WP-Cache is available for WordPress, but of course this could be done any number of ways, and with any back-end technology.\n\nThe 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.\n\nOutsource your feeds\n\nWe 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\u2019s still 36,000 requests we could easily do without. Each request uses resources and particularly during December, all those requests can add up. \n\nThe simple solution here was to switch our feed over to using FeedBurner. We publish the address of the FeedBurner version of our feed here, so those 36,000 requests a day hit FeedBurner\u2019s servers rather than ours. In addition, we get pretty graphs showing how the subscriber-base is building.\n\n\n\nOff-load big files\n\nLarger 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.\n\nThis year, we started serving most of the images for articles from a subdomain \u2013 media.24ways.org. Rather than pointing to our own server, this subdomain points to an Amazon S3 account where the files are held. It\u2019s easy to pigeon-hole S3 as merely an online backup solution, and whilst not a fully fledged CDN, 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\u2019s so affordable it may not be worth even taking the files back off S3 once December has passed.\n\nI found this article on Scalable Media Hosting with Amazon S3 to be really useful in getting started. I upload the files via a Firefox plugin (mentioned in the article) and then edit the ACL to allow public access to the files. The way S3 enables you to point DNS directly at it means that you\u2019re not tied to always using the service, and that it can be transparent to your users.\n\nIf your site uses photographs, consider uploading them to a service like Flickr 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.\n\nOff-load small files\n\nYou\u2019ll have noticed the pattern by now \u2013 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 Gravatar 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.\n\nEarlier this year Gravatar changed hands and is now run by the same team behind WordPress.com. Those guys clearly know what they\u2019re doing when it comes to high performance, so this year we went back to serving avatars directly from them.\n\nIf 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. \n\nKnow what you\u2019re paying for\n\nThe 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 CPU, memory and disk space.\n\nSo 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\u2019t even have to pay the setup cost on the new server. So it really pays to know what you\u2019re paying for and keep an eye out of ways you can make improvements without needing to spend more money.\n\nFurther steps\n\nThere\u2019s 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 CSS directly and it\u2019s not minified or compressed. But by tackling the big problems first we\u2019ve 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.\n\nOver the last 24 days we\u2019ve served up articles to more than 350,000 visitors without breaking a sweat. On a busy day, that\u2019s been nearly 20,000 visitors in just 24 hours. While in the grand scheme of things that\u2019s not a huge amount of traffic, it can be a lot if you\u2019re not prepared for it. However, with a little planning for the peaks you can help ensure that when the traffic arrives you\u2019re ready to capitalise on it.\n\nOf course, people only visit 24 ways for the wealth of knowledge and experience that\u2019s tied up in the articles here. Therefore I\u2019d 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.", "2007", "Drew McLellan", "drewmclellan", "2007-12-24T00:00:00+00:00", "https://24ways.org/2007/performance-on-a-shoe-string/", "ux"], [181, "Working With RGBA Colour", "When Tim and I were discussing the redesign of this site last year, one of the clear goals was to have a graphical style without making the pages heavy with a lot of images. When we launched, a lot of people were surprised that the design wasn\u2019t built with PNGs. Instead we\u2019d used RGBA colour values, which is part of the CSS3 specification.\n\nWhat is RGBA Colour?\n\nWe\u2019re all familiar with specifying colours in CSS using by defining the mix of red, green and blue light required to achieve our tone. This is fine and dandy, but whatever values we specify have one thing in common \u2014 the colours are all solid, flat, and well, a bit boring.\n\n Flat RGB colours\n\nCSS3 introduces a couple of new ways to specify colours, and one of those is RGBA. The A stands for Alpha, which refers to the level of opacity of the colour, or to put it another way, the amount of transparency. This means that we can set not only the red, green and blue values, but also control how much of what\u2019s behind the colour shows through. Like with layers in Photoshop.\n\nDon\u2019t We Have Opacity Already?\n\nThe ability to set the opacity on a colour differs subtly from setting the opacity on an element using the CSS opacity property. Let\u2019s look at an example.\n\nHere we have an H1 with foreground and background colours set against a page with a patterned background.\n\n Heading with no transparency applied\n\nh1 {\n\tcolor: rgb(0, 0, 0);\n\tbackground-color: rgb(255, 255, 255);\n}\n\nBy setting the CSS opacity property, we can adjust the transparency of the entire element and its contents:\n\n Heading with 50% opacity on the element\n\nh1 {\n\tcolor: rgb(0, 0, 0);\n\tbackground-color: rgb(255, 255, 255);\n\topacity: 0.5;\n}\n\nRGBA colour gives us something different \u2013 the ability to control the opacity of the individual colours rather than the entire element. So we can set the opacity on just the background:\n\n 50% opacity on just the background colour\n\nh1 {\n\tcolor: rgb(0, 0, 0);\n\tbackground-color: rgba(255, 255, 255, 0.5);\n}\n\nOr leave the background solid and change the opacity on just the text:\n\n 50% opacity on just the foreground colour\n\nh1 {\n\tcolor: rgba(0, 0, 0, 0.5);\n\tbackground-color: rgb(255, 255, 255);\n}\n\nThe How-To\n\nYou\u2019ll notice that above I\u2019ve been using the rgb() syntax for specifying colours. This is a bit less common than the usual hex codes (like #FFF) but it makes sense when starting to use RGBA. As there\u2019s no way to specify opacity with hex codes, we use rgba() like so:\n\ncolor: rgba(255, 255, 255, 0.5);\n\nJust like rgb() the first three values are red, green and blue. You can specify these 0-255 or 0%-100%. The fourth value is the opacity level from 0 (completely transparent) to 1 (completely opaque).\n\nYou can use this anywhere you\u2019d normally set a colour in CSS \u2014 so it\u2019s good for foregrounds and background, borders, outlines and so on. All the transparency effects on this site\u2019s current design are achieved this way.\n\nSupporting All Browsers\n\nLike a lot of the features we\u2019ll be looking at in this year\u2019s 24 ways, RGBA colour is supported by a lot of the newest browsers, but not the rest. Firefox, Safari, Chrome and Opera browsers all support RGBA, but Internet Explorer does not.\n\nFortunately, due to the robust design of CSS as a language, we can specify RGBA colours for browsers that support it and an alternative for browsers that do not.\n\nFalling back to solid colour\n\nThe simplest technique is to allow the browser to fall back to using a solid colour when opacity isn\u2019t available. The CSS parsing rules specify that any unrecognised value should be ignored. We can make use of this because a browser without RGBA support will treat a colour value specified with rgba() as unrecognised and discard it.\n\nSo if we specify the colour first using rgb() for all browsers, we can then overwrite it with an rgba() colour for browsers that understand RGBA.\n\nh1 {\n\tcolor: rgb(127, 127, 127);\n\tcolor: rgba(0, 0, 0, 0.5);\n}\n\nFalling back to a PNG\n\nIn cases where you\u2019re using transparency on a background-color (although not on borders or text) it\u2019s possible to fall back to using a PNG with alpha channel to get the same effect. This is less flexible than using CSS as you\u2019ll need to create a new PNG for each level of transparency required, but it can be a useful solution.\n\nUsing the same principal as before, we can specify the background in a style that all browsers will understand, and then overwrite it in a way that browsers without RGBA support will ignore.\n\nh1 {\n\tbackground: transparent url(black50.png);\n\tbackground: rgba(0, 0, 0, 0.5) none;\n}\n\nIt\u2019s important to note that this works because we\u2019re using the background shorthand property, enabling us to set both the background colour and background image in a single declaration. It\u2019s this that enables us to rely on the browser ignoring the second declaration when it encounters the unknown rgba() value.\n\nNext Steps\n\nThe really great thing about RGBA colour is that it gives us the ability to create far more graphically rich designs without the need to use images. Not only does that make for faster and lighter pages, but sites which are easier and quicker to build and maintain. CSS values can also be changed in response to user interaction or even manipulated with JavaScript in a way that\u2019s just not so easy using images.\n\n Opacity can be changed on :hover or manipulated with JavaScript\n\ndiv {\n\tcolor: rgba(255, 255, 255, 0.8);\n\tbackground-color: rgba(142, 213, 87, 0.3);\n}\ndiv:hover {\n\tcolor: rgba(255, 255, 255, 1);\n\tbackground-color: rgba(142, 213, 87, 0.6);\n}\n\nClever use of transparency in border colours can help ease the transition between overlay items and the page behind.\n\n Borders can receive the RGBA treatment, too\n\ndiv {\n\tcolor: rgb(0, 0, 0);\n\tbackground-color: rgb(255, 255, 255);\n\tborder: 10px solid rgba(255, 255, 255, 0.3);\n}\n\nIn Conclusion\n\n\nThat\u2019s a brief insight into RGBA colour, what it\u2019s good for and how it can be used whilst providing support for older browsers. With the current lack of support in Internet Explorer, it\u2019s probably not a technique that commercial designs will want to heavily rely on right away \u2013 simply because of the overhead of needing to think about fallback all the time. \n\nIt is, however, a useful tool to have for those smaller, less critical touches that can really help to finesse a design. As browser support becomes more mainstream, you\u2019ll already be familiar and practised with RGBA and ready to go.", "2009", "Drew McLellan", "drewmclellan", "2009-12-01T00:00:00+00:00", "https://24ways.org/2009/working-with-rgba-colour/", "code"], [208, "All That Glisters", "Tradition has it that at this time of year, families gather together, sit, eat and share stories. It\u2019s an opportunity for the wisdom of the elders to be passed down to the younger members of the tribe. Tradition also has it that we should chase cheese downhill and dunk the nice lady to prove she\u2019s a witch, so maybe let\u2019s not put too much stock in that.\nI\u2019ve been building things on the web professionally for about twenty years, and although the web has changed immeasurably, it\u2019s probably not changed as much as I have. While I can happily say I\u2019m not the young (always right, always arrogant) developer that I once was, unfortunately I\u2019m now an approaching-middle-age developer who thinks he\u2019s always right and on top of it is extremely pompous. What can you do? Nature has devised this system with the distinct advantage of allowing us to always be right, and only ever wrong in the future or in the past. So let\u2019s roll with it.\nIncreasingly, there seems to be a sense of fatigue within our industry. Just when you think you\u2019ve got a handle on whatever the latest tool or technology is, something new comes out to replace it. Suddenly you find that you\u2019ve invested precious time learning something new and it\u2019s already old hat. The pace of change is so rapid, that new developers don\u2019t know where to start, and experienced developers don\u2019t know where it ends. With that in mind, here\u2019s some fireside thoughts from a pompous old developer, that I hope might bring some Christmas comfort.\nReliable and boring beats shiny and new\nThere are so many new tools, frameworks, techniques, styles and libraries to learn. You know what? You don\u2019t have to use them. You\u2019re not a bad developer if you use Grunt even though others have switched to Gulp or Brunch or Webpack or Banana Sandwich. It\u2019s probably misguided to spend lots of project time messing around with build tool fashions when your so last year build tool is already doing what you need.\nJust a little reminder that it\u2019s about 100 times more important what you build than how you build it.\u2014 Chris Coyier (@chriscoyier) December 10, 2017\n\nI think it helps if we understand why so many new solutions exist. Most developers are predisposed to enjoy creating new things more than improving established systems. It\u2019s natural, because it\u2019s actually much easier and more exciting to create something new that works exactly how you think it should be than to improve an existing, imperfect solution. Improving and refactoring a system is hard, and it takes real chops, much more than just building something new.\nThe consequence of this is that new tools appear all the time. A developer will get a fresh new idea of how to tackle a problem \u2013 usually out of dissatisfaction with an existing solution, and figure the best way to implement that idea is to build something new around it. Often, that something new will do the same job as something old that already exists; it will just do it in a different way. Sometimes in a better way. Sometimes, just different.\nxkcd: Standards\nThat\u2019s not to say new tools are bad, and it\u2019s not bad that they exist. We shouldn\u2019t be crushing new ideas, and it\u2019s not wrong to adopt a new solution over an old one, but you know what? There\u2019s no imperative to switch right away. The next time you hit a pain point with your current solution, or have time to re-evaluate, check out what\u2019s new and see how the latest generation of tools and technologies can help. There\u2019s no prize for solving problems you don\u2019t have yet, and heading further into the desert in search of water is a survival tactic, not an aspiration.\nNew is better, but also worse\nSoftware, much like people, is born with a whole lot of potential and not much utility. Newborns \u2014 both digital and meaty \u2014 are exciting and cute but they also lead to sleepless nights and pools of vomit.\nNew technology contains lots of useful new features, but it\u2019s also more likely to contain bugs and be subject to more rapid change. Jumping on a new framework is great, right until there are API changes and you need to refactor your entire project to be able to update. More mature solutions have a higher weight of existing projects on their shoulders, and so the need to maintain backward compatibility is stronger. Things still move forward, but in a more controlled way.\nSo how do we balance the need to move technology forward with the need to provide mature and stable solutions for the projects we work on? I think there\u2019s a couple of good ways to do that.\nGet personal\nUse all the new shiny tools on your side-projects, personal projects, seasonal throw-aways and anywhere where the stakes are low. If you know you can patch around problems without much consequence, go for it. Build your personal blog on a CMS that stores data in the woven bark of a silver birch. Find where it breaks. Find where it excels. Find yourself if you like. When it comes to high-stakes projects, you\u2019ll hopefully have enough experience to know what you\u2019re getting into.\nFocus on the unique problem\nThat\u2019s not to say you should never risk using a new technology for \u2018real\u2019 work. Instead, distinguish the areas of your project where a new technology solves a specifically identified, measurable business objective, verses those where it won\u2019t. \nA brand new web application framework might be fun to use, but are you in the business of solving a web application framework problem?  That new web server made of taffeta might increase static file throughput slightly, but are you in the business of serving static assets, or would it be better to just run up nginx and never have to think about that problem again. (Clue: it\u2019s the nginx one.)\nBut when it comes to building that live sports interface for keeping fans up to date with the blow-by-blow of the big game, that\u2019s where it might make sense to take a risk on an amazing-looking new JavaScript realtime interface framework. That\u2019s the time to run up a breakthrough new message queue server that can deliver jobs to workers via extrasensory perception and keep the score updates flowing instantaneously. \nThose are the risks worth taking, as those new technologies have the potential to help you solve your core problems in a markedly improved way. Unproven technology is worth the risk if it solves a specific business objective. If it doesn\u2019t, don\u2019t make work for yourself - use something mature and stable.\nPick the right tools\nOur job as developers is to solve problems using code, and do so in an effective and responsible way. You\u2019ve been hired to use your expertise in picking the right tools for the job, and a big part of that is weighing up the risk verse the reward of each part of the system. The best tools for the job might be something cutting edge, but \u2018best\u2019 can also mean most stable, reliable or easy-to-hire-for.\nGo out and learn (and create!) new tools and experiment with them. Understand what problems they solve and what the pitfalls are. Use them in production for low-stakes projects to get real experience, and then once you really know their character, then think about using them when the stakes are higher.\nThe rest of the time? The tools you\u2019re using now are solid and proven and you know their capabilities and pitfalls well. They might not always be the fashionable candidate, but they often make for a very solid choice.", "2017", "Drew McLellan", "drewmclellan", "2017-12-24T00:00:00+00:00", "https://24ways.org/2017/all-that-glisters/", "business"], [220, "Finding Your Way with Static Maps", "Since the introduction of the Google Maps service in 2005, online maps have taken off in a way not really possible before the invention of slippy map interaction. Although quickly followed by a plethora of similar services from both commercial and non-commercial parties, Google\u2019s first-mover advantage, and easy-to-use developer API saw Google Maps become pretty much the de facto mapping service.\n\n\n\nIt\u2019s now so easy to add a map to a web page, there\u2019s no reason not to. Dropping an iframe map into your page is as simple as embedding a YouTube video.\n\nBut there\u2019s one crucial drawback to both the solution Google provides for you to drop into your page and the code developers typically implement themselves \u2013 they don\u2019t work without JavaScript.\n\nA bit about JavaScript\n\nBack in October of this year, The Yahoo! Developer Network blog ran some tests to measure how many visitors to the Yahoo! home page didn\u2019t have JavaScript available or enabled in their browser. It\u2019s an interesting test when you consider that the audience for the Yahoo! home page (one of the most visited pages on the web) represents about as mainstream a sample as you\u2019ll find. If there\u2019s any such thing as an \u2018average Web user\u2019 then this is them.\n\nThe results surprised me. It varied from region to region, but at most just two per cent of visitors didn\u2019t have JavaScript running. To be honest, I was expecting it to be higher, but this quote from the article caught my attention:\n\n\n\tWhile the percentage of visitors with JavaScript disabled seems like a low number, keep in mind that small percentages of big numbers are also big numbers.\n\n\nThat\u2019s right, of course, and it got me thinking about what that two per cent means. For many sites, two per cent is the number of visitors using the Opera web browser, using IE6, or using Mobile Safari. \n\nSo, although a small percentage of the total, users without JavaScript can\u2019t just be forgotten about, and catering for them is at the very heart of how the web is supposed to work.\n\nStarting with content in HTML, we layer on presentation with CSS and then enhance interactivity with JavaScript. If anything fails along the way or the network craps out, or a browser just doesn\u2019t support one of the technologies, the user still gets something they can work with. \n\nIt\u2019s progressive enhancement \u2013 also known as doing our jobs properly.\n\nSorry, wasn\u2019t this about maps?\n\nAs I was saying, the default code Google provides, and the example code it gives to developers (which typically just gets followed \u2018as is\u2019) doesn\u2019t account for users without JavaScript. No JavaScript, no content.\n\nWhen adding the ability to publish maps to our small content management system Perch, I didn\u2019t want to provide a solution that only worked with JavaScript. I had to go looking for a way to provide maps without JavaScript, too.\n\nThere\u2019s a simple solution, fortunately, in the form of static map tiles. All the various slippy map services use a JavaScript interface on top of what are basically rendered map image tiles. Dragging the map loads in more image tiles in the direction you want to view. If you\u2019ve used a slippy map on a slow connection, you\u2019ll be familiar with seeing these tiles load in one by one.\n\nThe Static Map API\n\nThe good news is that these tiles (or tiles just like them) can be used as regular images on your site. Google has a Static Map API which not only gives you a handy interface to retrieve a tile for the exact area you need, but also allows you to place pins, and zoom and centre the tile so that the image looks just so. \n\nThis means that you can create a static, non-JavaScript version of your slippy map\u2019s initial (or ideal) state to load into your page as a regular image, and then have the JavaScript map hijack the image and make it slippy.\n\nClearly, that\u2019s not going to be a perfect solution for every map\u2019s requirements. It doesn\u2019t allow for panning, zooming or interrogation without JavaScript. However, for the majority of straightforward map uses online, a static map makes a great alternative for those visitors without JavaScript.\n\n\n\nHere\u2019s the how\n\nRetrieving a static map tile is staggeringly easy \u2013 it\u2019s just a case of forming a URL with the correct arguments and then using that as the src of an image tag.\n\n<img src=\"http://maps.google.com/maps/api/staticmap\n\t?center=Bethlehem+Israel\n\t&zoom=5\n\t&size=540x280\n\t&maptype=satellite\n\t&markers=color:red|31.4211,35.1144\n\t&sensor=false\" \n\twidth=\"540\" height=\"280\" alt=\"Map of Bethlehem, Israel\" />\n\nAs you can see, there are a few key options that we pass along to the base URL. All of these should be familiar to anyone who\u2019s worked with the JavaScript API.\n\n\n\tcenter determines the point on which the map is centred. This can be latitude and longitude values, or simply an address which is then geocoded.\n\tzoom sets the zoom level.\n\tsize is the pixel dimensions of the image you require.\n\tmaptype can be roadmap, satellite, terrain or hybrid.\n\tmarkers sets one or more pin locations. Markers can be labelled, have different colours, and so on \u2013 there\u2019s quite a lot of control available.\n\tsensor states whether you are using a sensor to determine the user\u2019s location. When just embedding a map in a web page, set this to false.\n\n\nThere are many options, including plotting paths and setting the image format, which can all be found in the straightforward documentation.\n\nAdding to your page\n\nIf you\u2019ve worked with the JavaScript API, you\u2019ll know that it needs a container element which you inject the map into:\n\n<div id=\"map\"></div>\n\nAll you need to do is put your static image inside that container:\n\n<div id=\"map\">\n   <img src=\"http://maps.google.com/maps/api/staticmap[...]\" />\n </div>\n\nAnd then, in your JavaScript, find the image and remove it. For example, with jQuery you\u2019d simply use:\n\n$('#map img').remove();\n\nWhy not use a <noscript> element around the image? You could, and that would certainly work fine for browsers that do not support JavaScript. What that won\u2019t cover, however, is the situation where the browser has JavaScript support but, for whatever reason, the JavaScript doesn\u2019t run. This could be due to network issues, an aggressive corporate firewall, or even just a bug in your code. So for that reason, we put the image in for all browsers that show images, and then remove it when the JavaScript is  successfully running.\n\nSee an example in action\n\nAbout rate limits\n\nThe Google Static Map API limits the requests per site viewer \u2013 currently at one thousand distinct maps per day per viewer. So, for most sites you really don\u2019t need to worry about the rate limit. Requests for the same tile aren\u2019t normally counted, as the tile has already been generated and is cached. You can embed the images direct from Google and let it worry about the distribution and caching.\n\nIn conclusion\n\nAs you can see, adding a static map alongside your dynamic map for those users without JavaScript is very easy indeed. There may not be a huge percentage of web visitors browsing without JavaScript but, as we\u2019ve seen, a small percentage of a big number is still a big number. When it\u2019s so easy to add a static map, can you really justify not doing it?", "2010", "Drew McLellan", "drewmclellan", "2010-12-01T00:00:00+00:00", "https://24ways.org/2010/finding-your-way-with-static-maps/", "code"], [264, "Dynamic Social Sharing Images", "Way back when social media was new, you could be pretty sure that whatever you posted would be read by those who follow you. If you\u2019d written a blog post and you wanted to share it with those who follow you, you could post a link and your followers would see it in their streams. Oh heady days! \nWith so many social channels and a proliferation of content and promotions flying past in everyone\u2019s streams, it\u2019s no longer enough to share content on social media, you have to actively sell it if you want it to be seen. You really need to make the most of every opportunity to catch a reader\u2019s attention if you\u2019re trying to get as many eyes as possible on that sweet, sweet social content.\nOne of the best ways to grab attention with your posts or tweets is to include an image. There\u2019s heaps of research that says that having images in your posts helps them stand out to followers. Reports I found showed figures from anything from 35% to 150% improvement from just having image in a post. Unfortunately, the details were surrounded with gross words like engagement and visual marketing assets and so I had to close the page before I started to hate myself too much.\nSo without hard stats to quote, we\u2019ll call it a rule of thumb. The rule of thumb is that posts with images will grab more attention than those without, so it makes sense that when adding pages to a website, you should make sure that they have social media sharing images associated with them.\nAdding sharing images\nThe process for declaring an image to be used in places like Facebook and Twitter is very simple, and at this point is familiar to many of us. You add a meta tag to the head of the page to point to the location of the image to use. When a link to the page is added to a post, the social network will fetch the page, look for the meta tag and then use the image you specified.\n<meta property=\"og:image\" content=\"https://example.com/my_image.jpg\">\nThere\u2019s a good post on this over at CSS-Tricks if you need to bone up on the details of this and other similar meta tags for social media sharing.\nThis is all fine and well for content that has a very obvious choice of image to go along with it, but what if you don\u2019t necessarily have an image? One approach is to use stock photography, but that\u2019s not going to be right for every situation.\nThis was something we faced with 24 ways in 2017. We wanted to add images to the tweets we post each day announcing a new article. Some articles have images, but not all, and there tended not to be any consistency in terms of imagery from one article to the next. We always have an author photograph, but those don\u2019t usually lend themselves directly to being the main \u2018hero\u2019 image for an article.\nPutting his thinking cap on, Paul came up with a design for an image that used the author photo along with a quote extracted from the article.\nOne of the hand-made sharing images from 2017\nEach day we would pick a quote from the article, and Paul would manually compose an image to be uploaded to the site. The results were great, but the whole process was a bit too labour intensive and relied on an individual (Paul) being available each day to do the work. I thought we could probably improve this.\nHatching a new plan\nOne initial idea I came up with was to script the image editor to dynamically build a new image by pulling content from our database. Sketch has plugins available to pull JSON content into a design, and our CMS can easily output JSON data, so that was one possibility.\nThe more I thought about this and how much I wish graphic design tools worked just a little bit more like CSS, the obvious solution hit me. We should just build it with CSS!\nIn fact, as the author name and image already exist in our CMS, and the visual styling is based on the design of the website, couldn\u2019t this just be another page on the site generated by the CMS?\nBreaking it down, I figured the steps needed would be something like:\n\nCreate the CSS to lay out a component that could be turned into an image\nAdd a new field to articles in the CMS to hold a handpicked quote\nBuild a new article template in the CMS to output the author name and quote dynamically for any article\n\u2026 um \u2026 screenshot?\n\nI thought I\u2019d get cracking and see if I could figure out the final steps later.\nBuilding the page\nThe first thing to tackle was the basic HTML and CSS to lay out the components for our image. That bit was really easy, as I just asked Paul to do it. Everyone should have a Paul.\nPaul\u2019s code uses a fixed dimension container in the HTML, set to 600 \u00d7 315px. This is to make it the correct aspect ratio for Facebook\u2019s recommended image size. It\u2019s useful to remember here that it doesn\u2019t need to be responsive or robust, as the page only needs to lay out correctly for a screenshot and a fixed size in a known browser.\nWith the markup and CSS in place, I turned this into a new template. Our CMS can easily display content through any number of templates, so I created a version of the article template that was totally stripped down. It only included the author details and the quote, along with Paul\u2019s markup.\nI also added the quote as a new field on the article in the CMS, so each \u2018image\u2019 could be quickly and easily customised in the editing process.\nI added a new field to the article template to capture the quote.\nWith very little effort, we quickly had a page to dynamically generate our \u2018image\u2019 right from the CMS. You can see any of them by adding /sharing onto the end of an article URL for any 2018 article.\nOur automatically generated layout direct from the CMS\nIt soon became clear that the elusive Step 4 was going to be the tricky part. I can create a small page on the site that looks like an image, but how should I go about turning it into one? An obvious route is to screenshot the page by hand, but that\u2019s going back to some of the manual steps I was trying to eliminate, and also opens up a possibility for errors to be made. But it did lead me to the thought\u2026 how could I automatically take a screenshot?\nEnter Puppeteer\nPuppeteer is a Node.js library that provides a nice API onto Headless Chrome. What is Headless Chrome, you ask? It\u2019s just a version of the Chrome browser than runs from the command line without ever drawing anything to a user interface window. It loads pages, renders CSS, runs JavaScript, pretty much every normal thing that Chrome on the desktop does, but without a clicky user interface.\nHeadless Chrome can be used for all sorts of things such as running automated tests on front-end code after making changes, or\u2026 get this\u2026 rendering pages that can be used for screenshots. The actual process of writing some code to control Chrome and to take the screenshot is where Puppeteer comes in. Puppeteer puts a friendly layer in front of big old scary Chrome to enable us to interact with it using simple JavaScript code running in Node.\nUsing Puppeteer, I can write a small script that will repeatably turn a URL into an image. So simple is it to do this, that\u2019s it\u2019s actually Puppeteer\u2019s \u2018hello world\u2019 example.\nFirst you install Puppeteer. It downloads a compatible headless browser (actually Chromium) as a dependancy, so you don\u2019t need to worry about installing that. At the command line:\nnpm i puppeteer\nThen save a new file as example.js with this code:\nconst puppeteer = require('puppeteer');\n\n(async () => {\n  const browser = await puppeteer.launch();\n  const page = await browser.newPage();\n  await page.goto('https://example.com');\n  await page.screenshot({path: 'example.png'});\n  await browser.close();\n})();\nand then run it using Node:\nnode example.js\nThis will output an image file example.png to disk, which contains a screenshot of, in this case https://example.com. The logic of the code is reasonably easy to follow:\n\nLaunch a browser\nOpen up a new page\nGoto a URL\nTake a screenshot\nClose the browser\n\nThe async function and await keywords are a way to have the script pause and wait for normally asynchronous code to return before proceeding. That\u2019s useful with actions like loading a web page that might take some time to complete. They\u2019re used with Promises, and the effect is to make asynchronous code behave as if it\u2019s synchronous. You can read more about async and await at MDN if you\u2019re interested.\nThat\u2019s a good proof-of-concept using the basic Puppeteer example. I can take a screenshot of a URL! But what happens if I put the URL of my new special page in there?\nOur content is up in the corner of the image with lots of empty space.\nThat\u2019s not great. It\u2019s okay, but not great. It looks like that, by default, Puppeteer takes a screenshot with a resolution of 800 \u00d7 600, so we need to find out how to adjust that. Fortunately, the docs aren\u2019t the worst and I was able to find the page.setViewport() method pretty easily.\nconst puppeteer = require('puppeteer');\n\n(async () => {\n  const browser = await puppeteer.launch();\n  const page = await browser.newPage();\n  await page.goto('https://24ways.org/2018/clip-paths-know-no-bounds/sharing');\n  await page.setViewport({\n    width: 600,\n    height: 315\n  });\n  await page.screenshot({path: 'example.png'});\n  await browser.close();\n})();\nThis worked! The screenshot is now 600 \u00d7 315 as expected. That\u2019s exactly what we asked for. Trouble is, that\u2019s a bit low res and it is nearly 2019 after all. While in those docs, I noticed the deviceScaleFactor option that can be passed to page.setViewport(). Setting that to 2 gives us an image of the same area of the screen, but with twice as many pixels.\n  await page.setViewport({\n    width: 600,\n    height: 315,\n    deviceScaleFactor: 2\n  });\nPerfect! We now have a programmatic way of turning a URL into an image.\nImproving the script\nRather than having a script with a fixed URL in it that outputs an image called example.png, the next step is to make that a bit more dynamic. The aim here is to have a script that we can run with a URL as an argument and have it output an image for that one page. That way we can run it manually, or hook it into part of our site\u2019s build process to automate the generation of the image.\nOur goal is to call the script like this:\nnode shoot-sharing-image.js https://24ways.org/2018/clip-paths-know-no-bounds/\nAnd I want the image to come out with the name clip-paths-know-no-bounds.png. To do that, I need to have my script look for command arguments, and then to split the URL up to grab the slug from it.\n// Get the URL and the slug segment from it\nconst url = process.argv[2];\nconst segments = url.split('/');\n// Get the second-to-last segment (the slug)\nconst slug = segments[segments.length-2];\nWe can then use these variables later in the script, remembering to add sharing back onto the end of the URL to get our dedicated page.\n(async () => {\n  const browser = await puppeteer.launch();\n  const page = await browser.newPage();\n  await page.goto(url + 'sharing');\n  await page.setViewport({\n    width: 600,\n    height: 315,\n    deviceScaleFactor: 2\n  });\n  await page.screenshot({path: slug + '.png'});\n  await browser.close();\n})();\nOnce you\u2019re generating the image with Node, there\u2019s all sorts of things you can do with it. An obvious step is to move it to the correct location within your site or project.\nYou can also run optimisations on the file. I\u2019m using imagemin with pngquant to reduce the file size a little.\nconst imagemin = require('imagemin');\nconst imageminPngquant = require('imagemin-pngquant');\n\nawait imagemin([slug + '.png'], 'build', {\n  plugins: [\n    imageminPngquant({quality: '75-90'})\n  ]\n});\n\nYou can see the completed example as a gist.\nIntegrating it with your CMS\nSo we now have a command we can run to take a URL and generate a custom image for that URL. It\u2019s in a format that can be called by any sort of build script, or triggered from a publishing hook in a CMS. Exactly how you do that is going to depend on the way your site is built and the technology stack you\u2019re using, but it\u2019s likely not too hard as long as you can run a command as part of the process.\nFor 24 ways this year, I\u2019ve been running the script by hand once each article is ready. My script adds the file to a git repo and pushes to a deployment remote that is configured to automatically deploy static assets to our server. Along with our theme of making incremental improvements, next year I\u2019ll look to automate this one step further.\nWe may also look at having a few slightly different layouts to choose from, so that each day isn\u2019t exactly the same as the last. Interestingly, we could even try some A/B tests to see if there\u2019s any particular format of image or type of quote that does a better job of grabbing attention. There are lots of possibilities!\n\nBy using a bit of ingenuity, some custom CMS templates, and the very useful Puppeteer project, we\u2019ve been able to reliably produce dynamic social media sharing images for all of our articles. In doing so, we reduced the dependancy on any individual for producing those images, and opened up a world of possibilities in how we use those images.\nI hope you\u2019ll give it a try!", "2018", "Drew McLellan", "drewmclellan", "2018-12-24T00:00:00+00:00", "https://24ways.org/2018/dynamic-social-sharing-images/", "code"], [271, "Creating Custom Font Stacks with Unicode-Range", "Any web designer or front-end developer worth their salt will be familiar with the CSS @font-face rule used for embedding fonts in a web page. We\u2019ve all used it \u2014 either directly in our code ourselves, or via one of the web font services like Fontdeck, Typekit or Google Fonts.\n\nIf you\u2019re like me, however, you\u2019ll be used to just copying and pasting in a specific incantation of lines designed to get different formats of fonts working in different browsers, and may not have really explored all the capabilities of @font-face properties as defined by the spec.\n\nOne such property \u2014 the unicode-range descriptor \u2014 sounds pretty dull and is easily overlooked. It does, however, have some fairly interesting possibilities when put to use in creative ways.\n\nUnicode-range\n\nThe unicode-range descriptor is designed to help when using fonts that don\u2019t have full coverage of the characters used in a page. By adding a unicode-range property to a @font-face rule it is possible to specify the range of characters the font covers. \n\n@font-face {\n    font-family: BBCBengali;\n    src: url(fonts/BBCBengali.ttf) format(\"opentype\");\n    unicode-range: U+00-FF;\n}\n\nIn this example, the font is to be used for characters in the range of U+00 to U+FF which runs from the unexciting control characters at the start of the Unicode table (symbols like the exclamation mark start at U+21) right through to \u00ff at U+FF \u2013 the extent of the Basic Latin character range.\n\nBy adding multiple @font-face rules for the same family but with different ranges, you can build up complete coverage of the characters your page uses by using different fonts.\n\nWhen I say that it\u2019s possible to specify the range of characters the font covers, that\u2019s true, but what you\u2019re really doing with the unicode-range property is declaring which characters the font should be used for. This becomes interesting, because instead of merely working with the technical constraints of available characters in a given font, we can start picking and choosing characters to use and selectively mix fonts together.\n\nThe best available ampersand\n\nA few years back, Dan Cederholm wrote a post encouraging designers to use the best available ampersand. Dan went on to outline how this can be achieved by wrapping our ampersands in a <span> element with a class applied:\n\n<span class=\"amp\">&</span>\n\nA CSS rule can then be written to select the <span> and apply a different font:\n\nspan.amp {\n    font-family: Baskerville, Palatino, \"Book Antiqua\", serif;\n}\n\nThat\u2019s a perfectly serviceable technique, but the drawbacks are clear \u2014 you have to add extra markup which is borderline presentational, and you also have to be able to add that markup, which isn\u2019t always possible when working with a CMS.\n\nPerhaps we could do this with unicode-range.\n\nA better best available ampersand\n\nThe Unicode code point for an ampersand is U+26, so the ampersand font stack above can be created like so:\n\n@font-face {\n    font-family: 'Ampersand';\n    src: local('Baskerville'), local('Palatino'), local('Book Antiqua');\n    unicode-range: U+26;\n}\n\nWhat we\u2019ve done here is specify a new family called Ampersand and created a font stack for it with the user\u2019s locally installed copies of Baskerville, Palatino or Book Antiqua. We\u2019ve then limited it to a single character range \u2014 the ampersand. Of course, those don\u2019t need to be local fonts \u2014 they could be web font files, too. If you have a font with a really snazzy ampersand, go for your life.\n\nWe can then use that new family in a regular font stack.\n\nh1 {\n    font-family: Ampersand, Arial, sans-serif;\n}\n\nWith this in place, any <h1> elements in our page will use the Ampersand family (Baskerville, Palatino or Book Antiqua) for ampersands, and Arial for all other characters. If the user doesn\u2019t have any of the Ampersand family fonts available, the ampersand will fall back to the next item in the font stack, Arial.\n\nYou didn\u2019t think it was that easy, did you?\n\nOh, if only it were so. The problem comes, as ever, with the issue of browser support. The unicode-range property has good support in WebKit browsers (like Safari and Chrome, and the browsers on most popular smartphone platforms) and in recent versions of Internet Explorer. The big stumbling block comes in the form of Firefox, which has no support at all.\n\nIf you\u2019re familiar with how CSS works when it comes to unsupported properties, you\u2019ll know that if a browser encounters a property it doesn\u2019t implement, it just skips that declaration and moves on to the next. That works perfectly for things like border-radius \u2014 if the browser can\u2019t round off the corners, the declaration is skipped and the user sees square corners instead. Perfect.\n\nLess perfect when it comes to unicode-range, because if no range is specified then the default is that the font is applied for all characters \u2014 the whole range. If you\u2019re using a fancy font for flamboyant ampersands, you probably don\u2019t want that applied to all your text if unicode-range isn\u2019t supported. That would be bad. Really bad.\n\nEnsuring good fallbacks\n\nAs ever, the trick is to make sure that there\u2019s a sensible fallback in place if a browser doesn\u2019t have support for whatever technology you\u2019re trying to use. This is where being a super nerd about understanding the spec you\u2019re working with really pays off.\n\nWe can make use of the rules of the CSS cascade to make sure that if unicode-range isn\u2019t supported we get a sensible fallback font. What would be ideal is if we were able to follow up the @font-face rule with a second rule to override it if Unicode ranges aren\u2019t implemented.\n\n@font-face {\n    font-family: 'Ampersand';\n    src: local('Baskerville'), local('Palatino'), local('Book Antiqua');\n    unicode-range: U+26;\n}\n@font-face {\n    font-family: 'Ampersand';\n    src: local('Arial');\n}\n\nIn theory, this code should make sense for all browsers. For those that support unicode-range the two rules become cumulative. They specify different ranges for the same family, and in WebKit browsers this has the expected result of using Arial for most characters, but Baskerville and friends for the ampersand. For browsers that don\u2019t have support, the second rule should just supersede the first, setting the font to Arial. \n\nUnfortunately, this code causes current versions of Firefox to freak out and use the first rule, applying Baskerville to the entire range. That\u2019s both unexpected and unfortunate. Bad Firefox. On your rug.\n\nIf that doesn\u2019t work, what can we do? Well, we know that if given a unicode-range Firefox will ignore the range and apply the font to all characters. That\u2019s really what we\u2019re trying to achieve. So what if we specified a range for the fallback font, but made sure it only covers some obscure high-value Unicode character we\u2019re never going to use in our page? Then it wouldn\u2019t affect the outcome for browsers that do support ranges.\n\n@font-face {\n    font-family: 'Ampersand';\n    src: local('Baskerville'), local('Palatino'), local('Book Antiqua');\n    unicode-range: U+26;\n}\n@font-face {\n    /* Ampersand fallback font */\n    font-family: 'Ampersand';\n    src: local('Arial');\n    unicode-range: U+270C;\n}\n\nBy specifying a range on the fallback font, Firefox appears to correctly override the first based on the cascade sort order. Browsers that do support ranges take the second rule in addition, and apply Arial for that obscure character we\u2019re not using in any of our pages \u2014 U+270C.\n\nSo we get our nice ampersands in browsers that support unicode-range and, thanks to our styling of an obscure Unicode character, the font falls back to a perfectly acceptable Arial in browsers that do not offer support. Perfect!\n\nThat obscure character, my friends, is what Unicode defines as the VICTORY HAND.\n\n\u270c\n\nSo, how can we use this?\n\nAmpersands are a neat trick, and it works well in browsers that support ranges, but that\u2019s not really the point of all this. Styling ampersands is fun, but they\u2019re only really scratching the surface. Consider more involved examples, such as substituting a different font for numerals, or symbols, or even caps. Things certainly begin to get a bit more interesting.\n\nHow do you know what the codes are for different characters? Richard Ishida has a handy online conversion tool available where you can type in the characters and get the Unicode code points out the other end.\n\nOf course, the fact remains that browser support for unicode-range is currently limited, so any application needs to have fallbacks that you\u2019re still happy for a significant proportion of your visitors to see. In some cases, such as dedicated pages for mobile devices in an HTML-based phone app, this is immediately useful as support in WebKit browsers is already very good. In other cases, you\u2019ll have to use your own best judgement based on your needs and audience.\n\nOne thing to keep in mind is that if you\u2019re using web fonts, the entire font will be downloaded even if only one character is used. That said, the font shouldn\u2019t be downloaded if none of the characters within the Unicode range are present in a given page.\n\nAs ever, there are pros and cons to using unicode-range as well as varied but increasing support in browsers. It remains a useful tool to understand and have in your toolkit for when the right moment comes along.", "2011", "Drew McLellan", "drewmclellan", "2011-12-01T00:00:00+00:00", "https://24ways.org/2011/creating-custom-font-stacks-with-unicode-range/", "code"], [300, "Taking Device Orientation for a Spin", "When The Police sang \u201cDon\u2019t Stand So Close To Me\u201d they weren\u2019t talking about using a smartphone to view a panoramic image on Facebook, but they could have been. For years, technology has driven relentlessly towards devices we can carry around in our pockets, and now that we\u2019re there, we\u2019re expected to take the thing out of our pocket and wave it around in front of our faces like a psychotic donkey in search of its own dangly carrot.\nBut if you can\u2019t beat them, join them.\nA brave new world\nA couple of years back all sorts of specs for new HTML5 APIs sprang up much to our collective glee. Emboldened, we ran a few tests and found they basically didn\u2019t work in anything and went off disheartened into the corner for a bit of a sob.\nTurns out, while we were all busy boohooing, those browser boffins have actually being doing some work, and lo and behold, some of these APIs are even half usable. Mostly literally half usable\u2014we\u2019re still talking about browsers, after all.\nNow, of course they\u2019re all a bit JavaScripty and are going to involve complex methods and maths and science and probably about a thousand dependancies from Github that will fall out of fashion while we\u2019re still trying to locate the documentation, right? Well, no! \nSo what if we actually wanted to use one of these APIs, say to impress our friends with our ability to make them wave their phones in front of their faces (because no one enjoys looking hapless more than the easily-technologically-impressed), how could we do something like that? Let\u2019s find out.\nThe Device Orientation API\nThe phone-wavy API is more formally known as the DeviceOrientation Event Specification. It does a bunch of stuff that basically doesn\u2019t work, but also gives us three values that represent orientation of a device (a phone, a tablet, probably not a desktop computer) around its x, y and z axes. You might think of it as pitch, roll and yaw if you like to spend your weekends wearing goggles and a leather hat.\nThe main way we access these values is through an event listener, which can inform our code every time the value changes. Which is constantly, because you try and hold a phone still and then try and hold the Earth still too.\nThe API calls those pitch, roll and yaw values alpha, beta and gamma. Chocks away:\nwindow.addEventListener('deviceorientation', function(e) {\n    console.log(e.alpha);\n    console.log(e.beta);\n    console.log(e.gamma);\n});\nIf you look at this test page on your phone, you should be able to see the numbers change as you twirl the thing around your body like the dance partner you never had. Wrist strap recommended.\nOne important note\nLike may of these newfangled APIs, Device Orientation is only available over HTTPS. We\u2019re not allowed to have too much fun without protection, so make sure that you\u2019re working on a secure line. I\u2019ve found a quick and easy way to share my local dev environment over TLS with my devices is to use an ngrok tunnel.\nngrok http -host-header=rewrite mylocaldevsite.dev:80\nngrok will then set up a tunnel to your dev site with both HTTP and HTTPS URL options. You, of course, want the HTTPS option.\nRight, where were we?\nMake something to look at\nIt\u2019s all well and good having a bunch of numbers, but they\u2019re no use unless we do something with them. Something creative. Something to inspire the generations. Or we could just build that Facebook panoramic image viewer thing (because most of us are familiar with it and we\u2019re not trying to be too clever here). Yeah, let\u2019s just build one of those.\nOur basic framework is going to be similar to that used for an image carousel. We have a container, constrained in size, and CSS overflow property set to hidden. Into this we place our wide content and use positioning to move the content back and  forth behind the \u2018window\u2019 so that the part we want to show is visible.\nHere it is mocked up with a slider to set the position. When you release the slider, the position updates. (This actually tests best on desktop with your window slightly narrowed.)\nThe details of the slider aren\u2019t important (we\u2019re about to replace it with phone-wavy goodness) but the crucial part is that moving the slider results in a function call to position the image. This takes a percentage value (0-100) with 0 being far left and 100 being far right (or \u2018alt-nazi\u2019 or whatever).\nvar position_image = function(percent) {\n    var pos = (img_W / 100)*percent;\n    img.style.transform = 'translate(-'+pos+'px)'; \n};\nAll this does is figure out what that percentage means in terms of the image width, and set the transform: translate(\u2026); CSS property to move the image. (We use translate because it might be a bit faster to animate than left/right positioning.)\nOk. We can now read the orientation values from our device, and we can programatically position the image. What we need to do is figure out how to convert those raw orientation values into a nice tidy percentage to pass to our function and we\u2019re done. (We\u2019re so not done.)\nThe maths bit\nIf we go back to our raw values test page and make-believe that we have a fascinating panoramic image of some far-off beach or historic monument to look at, you\u2019ll note that the main value that is changing as we swing back and forth is the \u2018alpha\u2019 value. That\u2019s the one we want to track.\nAs our goal here is hey, these APIs are interesting and fun and not let\u2019s build the world\u2019s best panoramic image viewer, we\u2019ll start by making a few assumptions and simplifications:\n\nWhen the image loads, we\u2019ll centre the image and take the current nose-forward orientation reading as the middle.\nMoving left, we\u2019ll track to the left of the image (lower percentage).\nMoving right, we\u2019ll track to the right (higher percentage).\nIf the user spins round, does cartwheels or loads the page then hops on a plane and switches earthly hemispheres, they\u2019re on their own.\n\nNose-forward\nWhen the page loads, the initial value of alpha gives us our nose-forward position. In Safari on iOS, this is normalised to always be 0, whereas most everywhere else it tends to be bound to pointy-uppy north. That doesn\u2019t really matter to us, as we don\u2019t know which direction the user might be facing in anyway \u2014 we just need to record that initial state and then use it to compare any new readings.\nvar initial_position = null;\n\nwindow.addEventListener('deviceorientation', function(e) {\n    if (initial_position === null) {\n        initial_position = Math.floor(e.alpha);\n    };\n\n    var current_position = initial_position - Math.floor(e.alpha);\n});\n(I\u2019m rounding down the values with Math.floor() to make debugging easier - we\u2019ll take out the rounding later.)\nWe get our initial position if it\u2019s not yet been set, and then calculate the current position as a difference between the new value and the stored one.\nThese values are weird\nOne thing you need to know about these values, is that they range from 0 to 360 but then you also get weird left-of-zero values like -2 and whatever. And they wrap past 360 back to zero as you\u2019d expect if you do a forward roll.\nWhat I\u2019m interested in is working out my rotation. If 0 is my nose-forward position, I want a positive value as I turn right, and a negative value as I turn left. That puts the awkward 360-tipping point right behind the user where they can\u2019t see it.\nvar rotation  = current_position;\nif (current_position > 180) rotation = current_position-360;\nWhich way up?\nSince we\u2019re talking about orientation, we need to remember that the values are going to be different if the device is held in portrait on landscape mode. See for yourself - wiggle it like a steering wheel and you get different values. That\u2019s easy to account for when you know which way up the device is, but in true browser style, the API for that bit isn\u2019t well supported. The best I can come up with is:\nvar screen_portrait = false;\nif (window.innerWidth < window.innerHeight) {\n    screen_portrait = true;\n}\nIt works. Then we can use screen_portrait to branch our code:\nif (screen_portrait) {\n        if (current_position > 180) rotation = current_position-360;\n} else {\n        if (current_position < -180) rotation = 360+current_position;\n}\nHere\u2019s the code in action so you can see the values for yourself. If you change screen orientation you\u2019ll need to refresh the page (it\u2019s a demo!).\nLimiting rotation\nNow, while the youth of today are rarely seen without a phone in their hands, it would still be unreasonable to ask them to spin through 360\u00b0 to view a photo. Instead, we need to limit the range of movement to something like 60\u00b0-from-nose in either direction and normalise our values to pan the entire image across that 120\u00b0 range. -60 would be full-left (0%) and 60 would be full-right (100%).\nIf we set max_rotation = 60, that code ends up looking like this:\nif (rotation > max_rotation) rotation = max_rotation;\nif (rotation < (0-max_rotation)) rotation = 0-max_rotation;\n\nvar percent = Math.floor(((rotation + max_rotation)/(max_rotation*2))*100);\nWe should now be able to get a rotation from -60\u00b0 to +60\u00b0 expressed as a percentage. Try it for yourself.\nThe big reveal\nAll that\u2019s left to do is pass that percentage to our image positioning function and would you believe it, it might actually work.\nposition_image(percent);\nYou can see the final result and take it for a spin. Literally.\nSo what have we made here? Have we built some highly technical panoramic image viewer to aid surgeons during life-saving operations using only JavaScript and some slightly questionable mathematics? No, my friends, we have not. Far from it. \nWhat we have made is progress. We\u2019ve taken a relatively newly available hardware API and a bit of simple JavaScript and paired it with existing CSS knowledge and made something that we didn\u2019t have this morning. Something we probably didn\u2019t even want this morning. Something that if you take a couple of steps back and squint a bit might be a prototype for something vaguely interesting. But more importantly, we\u2019ve learned that our browsers are just a little bit more capable than we thought.\nThe web platform is maturing rapidly. There are new, relatively unexplored APIs for doing all sorts of crazy thing that are often dismissed as the preserve of native apps. Like some sort of app marmalade. Poppycock. \nThe web is an amazing, exciting place to create things. All it takes is some base knowledge of the fundamentals, a creative mind and a willingness to learn. We have those! So let\u2019s create things.", "2016", "Drew McLellan", "drewmclellan", "2016-12-24T00:00:00+00:00", "https://24ways.org/2016/taking-device-orientation-for-a-spin/", "code"], [314, "Easy Ajax with Prototype", "There\u2019s little more impressive on the web today than a appropriate touch of Ajax. Used well, Ajax brings a web interface much closer to the experience of a desktop app, and can turn a bear of an task into a pleasurable activity.\n\nBut it\u2019s really hard, right? It involves all the nasty JavaScript that no one ever does often enough to get really good at, and the browser support is patchy, and urgh it\u2019s just so much damn effort. Well, the good news is that \u2013 ta-da \u2013 it doesn\u2019t have to be a headache. But man does it still look impressive. Here\u2019s how to amaze your friends.\n\nIntroducing prototype.js\n\nPrototype is a JavaScript framework by Sam Stephenson designed to help make developing dynamic web apps a whole lot easier. In basic terms, it\u2019s a JavaScript file which you link into your page that then enables you to do cool stuff.\n\nThere\u2019s loads of capability built in, a portion of which covers our beloved Ajax. The whole thing is freely distributable under an MIT-style license, so it\u2019s good to go. What a nice man that Mr Stephenson is \u2013 friends, let us raise a hearty cup of mulled wine to his good name. Cheers! sluurrrrp.\n\nFirst step is to download the latest Prototype and put it somewhere safe. I suggest underneath the Christmas tree.\n\nCutting to the chase\n\nBefore I go on and set up an example of how to use this, let\u2019s just get to the crux. Here\u2019s how Prototype enables you to make a simple Ajax call and dump the results back to the page:\n\nvar url = 'myscript.php';\nvar pars = 'foo=bar';\nvar target = 'output-div';\t\nvar myAjax = new Ajax.Updater(target, url, {method: 'get', parameters: pars});\n\nThis snippet of JavaScript does a GET to myscript.php, with the parameter foo=bar, and when a result is returned, it places it inside the element with the ID output-div on your page.\n\nKnocking up a basic example\n\nSo to get this show on the road, there are three files we need to set up in our site alongside prototype.js. Obviously we need a basic HTML page with prototype.js linked in. This is the page the user interacts with. Secondly, we need our own JavaScript file for the glue between the interface and the stuff Prototype is doing. Lastly, we need the page (a PHP script in my case) that the Ajax is going to make its call too.\n\nSo, to that basic HTML page for the user to interact with. Here\u2019s one I found whilst out carol singing:\n\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n    <title>Easy Ajax</title>\n    <script type=\"text/javascript\" src=\"prototype.js\"></script>\n    <script type=\"text/javascript\" src=\"ajax.js\"></script>\n</head> \n<body>\n    <form method=\"get\" action=\"greeting.php\" id=\"greeting-form\">\n        <div>\n            <label for=\"greeting-name\">Enter your name:</label>\n            <input id=\"greeting-name\" type=\"text\" />\n            <input id=\"greeting-submit\" type=\"submit\" value=\"Greet me!\" />\n        </div>\n        <div id=\"greeting\"></div>\n    </form>\n</body>\n</html>\n\nAs you can see, I\u2019ve linked in prototype.js, and also a file called ajax.js, which is where we\u2019ll be putting our glue. (Careful where you leave your glue, kids.)\n\nOur basic example is just going to take a name and then echo it back in the form of a seasonal greeting. There\u2019s a form with an input field for a name, and crucially a DIV (greeting) for the result of our call. You\u2019ll also notice that the form has a submit button \u2013 this is so that it can function as a regular form when no JavaScript is available. It\u2019s important not to get carried away and forget the basics of accessibility.\n\nMeanwhile, back at the server\n\nSo we need a script at the server which is going to take input from the Ajax call and return some output. This is normally where you\u2019d hook into a database and do whatever transaction you need to before returning a result. To keep this as simple as possible, all this example here will do is take the name the user has given and add it to a greeting message. Not exactly Web 2-point-HoHoHo, but there you have it.\n\nHere\u2019s a quick PHP script \u2013 greeting.php \u2013 that Santa brought me early.\n\n<?php\n     $the_name = htmlspecialchars($_GET['greeting-name']);\n     echo \"<p>Season's Greetings, $the_name!</p>\";\n?>\n\nYou\u2019ll perhaps want to do something a little more complex within your own projects. Just sayin\u2019.\n\nGluing it all together\n\nInside our ajax.js file, we need to hook this all together. We\u2019re going to take advantage of some of the handy listener routines and such that Prototype also makes available. The first task is to attach a listener to set the scene once the window has loaded. He\u2019s how we attach an onload event to the window object and get it to call a function named init():\n\nEvent.observe(window, 'load', init, false);\n\nNow we create our init() function to do our evil bidding. Its first job of the day is to hide the submit button for those with JavaScript enabled. After that, it attaches a listener to watch for the user typing in the name field.\n\nfunction init(){\n     $('greeting-submit').style.display = 'none';\n     Event.observe('greeting-name', 'keyup', greet, false);\n}\n\nAs you can see, this is going to make a call to a function called greet() onkeyup in the greeting-name field. That function looks like this:\n\nfunction greet(){\n     var url = 'greeting.php';\n     var pars = 'greeting-name='+escape($F('greeting-name'));\n     var target = 'greeting';\n     var myAjax = new Ajax.Updater(target, url, {method: 'get', parameters: pars});\n}\n\nThe key points to note here are that any user input needs to be escaped before putting into the parameters so that it\u2019s URL-ready. The target is the ID of the element on the page (a DIV in our case) which will be the recipient of the output from the Ajax call.\n\nThat\u2019s it\n\nNo, seriously. That\u2019s everything. Try the example. Amaze your friends with your 1337 Ajax sk1llz.", "2005", "Drew McLellan", "drewmclellan", "2005-12-01T00:00:00+00:00", "https://24ways.org/2005/easy-ajax-with-prototype/", "code"], [315, "Edit-in-Place with Ajax", "Back on day one we looked at using the Prototype library to take all the hard work out of making a simple Ajax call. While that was fun and all, it didn\u2019t go that far towards implementing something really practical. We dipped our toes in, but haven\u2019t learned to swim yet.\n\nSo here is swimming lesson number one. Anyone who\u2019s used Flickr to publish their photos will be familiar with the edit-in-place system used for quickly amending titles and descriptions on photographs. Hovering over an item turns its background yellow to indicate it is editable. A simple click loads the text into an edit box, right there on the page.\n\n\n\nPrototype includes all sorts of useful methods to help reproduce something like this for our own projects. As well as the simple Ajax GETs we learned how to do last time, we can also do POSTs (which we\u2019ll need here) and a whole bunch of manipulations to the user interface \u2013 all through simple library calls. Here\u2019s what we\u2019re building, so let\u2019s do it.\n\nGetting Started\n\nThere are two major components to this process; the user interface manipulation and the Ajax call itself. Our set-up is much the same as last time (you may wish to read the first article if you\u2019ve not already done so). We have a basic HTML page which links in the prototype.js file and our own editinplace.js. Here\u2019s what Santa dropped down my chimney: \n\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n     \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n <head>\n     <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n    <title>Edit-in-Place with Ajax</title>\n     <link href=\"editinplace.css\" rel=\"Stylesheet\" type=\"text/css\" />\n     <script src=\"prototype.js\" type=\"text/javascript\"></script>\n     <script src=\"editinplace.js\" type=\"text/javascript\"></script>    \n</head> \n<body>\n     <h1>Edit-in-place</h1>\n     <p id=\"desc\">Dashing through the snow on a one horse open sleigh.</p>\n </body>\n </html>\n\nSo that\u2019s our page. The editable item is going to be the <p> called desc. The process goes something like this:\n\n\n\tHighlight the area onMouseOver\n\tClear the highlight onMouseOut\n\tIf the user clicks, hide the area and replace with a <textarea> and buttons\n\tRemove all of the above if the user cancels the operation\n\tWhen the Save button is clicked, make an Ajax POST and show that something\u2019s happening\n\tWhen the Ajax call comes back, update the page with the new content\n\n\nEvents and Highlighting\n\nThe first step is to offer feedback to the user that the item is editable. This is done by shading the background colour when the user mouses over. Of course, the CSS :hover pseudo class is a straightforward way to do this, but for three reasons, I\u2019m using JavaScript to switch class names.\n\n\n\t:hover isn\u2019t supported on many elements in Internet Explorer for Windows\n\tI want to keep control over when the highlight switches off after an update, regardless of mouse position\n\tIf JavaScript isn\u2019t available we don\u2019t want to end up with the CSS suggesting it might be\n\n\nWith this in mind, here\u2019s how editinplace.js starts:\n\nEvent.observe(window, 'load', init, false);\n\n function init(){\n     makeEditable('desc');\n }\n\n function makeEditable(id){\n     Event.observe(id, 'click', function(){edit($(id))}, false);\n     Event.observe(id, 'mouseover', function(){showAsEditable($(id))}, false);\n     Event.observe(id, 'mouseout', function(){showAsEditable($(id), true)}, false);\n }\n\n function showAsEditable(obj, clear){\n     if (!clear){\n          Element.addClassName(obj, 'editable');\n     }else{\n          Element.removeClassName(obj, 'editable');\n     }\n }\n\nThe first line attaches an onLoad event to the window, so that the function init() gets called once the page has loaded. In turn, init() sets up all the items on the page that we want to make editable. In this example I\u2019ve just got one, but you can add as many as you like.\n\nThe function madeEditable() attaches the mouseover, mouseout and click events to the item we\u2019re making editable. All showAsEditable does is add and remove the class name editable from the object. This uses the particularly cunning methods Element.addClassName() and Element.removeClassName() which enable you to cleanly add and remove effects without affecting any styling the object may otherwise have.\n\nOh, remember to add a rule for .editable to your style sheet:\n\n.editable{\n     color: #000;\n     background-color: #ffffd3;\n }\n\nThe Switch\n\nAs you can see above, when the user clicks on an editable item, a call is made to the function edit(). This is where we switch out the static item for a nice editable textarea. Here\u2019s how that function looks.\n\nfunction edit(obj){\n     Element.hide(obj);\n\n     var textarea ='<div id=\"' + obj.id + '_editor\">\n          <textarea id=\"' + obj.id + '_edit\" name=\"' + obj.id + '\" rows=\"4\" cols=\"60\">'\n          + obj.innerHTML + '</textarea>';\n\n     var button = '<input id=\"' + obj.id + '_save\" type=\"button\" value=\"SAVE\" /> OR \n          <input id=\"' + obj.id + '_cancel\" type=\"button\" value=\"CANCEL\" /></div>';\n\n     new Insertion.After(obj, textarea+button);\n\n     Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false);\n     Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false);\n\n }\n\nThe first thing to do is to hide the object. Prototype comes to the rescue with Element.hide() (and of course, Element.show() too). Following that, we build up the textarea and buttons as a string, and then use Insertion.After() to place our new editor underneath the (now hidden) editable object.\n\nThe last thing to do before we leave the user to edit is it attach listeners to the Save and Cancel buttons to call either the saveChanges() function, or to cleanUp() after a cancel.\n\nIn the event of a cancel, we can clean up behind ourselves like so:\n\nfunction cleanUp(obj, keepEditable){\n     Element.remove(obj.id+'_editor');\n     Element.show(obj);\n     if (!keepEditable) showAsEditable(obj, true);\n }\n\nSaving the Changes\n\nThis is where all the Ajax fun occurs. Whilst the previous article introduced Ajax.Updater() for simple Ajax calls, in this case we need a little bit more control over what happens once the response is received. For this purpose, Ajax.Request() is perfect. We can use the onSuccess and onFailure parameters to register functions to handle the response.\n\nfunction saveChanges(obj){\n     var new_content = escape($F(obj.id+'_edit'));\n\n     obj.innerHTML = \"Saving...\";\n     cleanUp(obj, true);\n\n     var success = function(t){editComplete(t, obj);}\n     var failure = function(t){editFailed(t, obj);}\n\n     var url = 'edit.php';\n     var pars = 'id=' + obj.id + '&content=' + new_content;\n     var myAjax = new Ajax.Request(url, {method:'post',\n          postBody:pars, onSuccess:success, onFailure:failure});\n }\n\n function editComplete(t, obj){\n     obj.innerHTML = t.responseText;\n     showAsEditable(obj, true);\n }\n\n function editFailed(t, obj){\n     obj.innerHTML = 'Sorry, the update failed.';\n     cleanUp(obj);\n }\n\nAs you can see, we first grab in the contents of the textarea into the variable new_content. We then remove the editor, set the content of the original object to \u201cSaving\u2026\u201d to show that an update is occurring, and make the Ajax POST.\n\nIf the Ajax fails, editFailed() sets the contents of the object to \u201cSorry, the update failed.\u201d Admittedly, that\u2019s not a very helpful way to handle the error but I have to limit the scope of this article somewhere. It might be a good idea to stow away the original contents of the object (obj.preUpdate = obj.innerHTML) for later retrieval before setting the content to \u201cSaving\u2026\u201d. No one likes a failure \u2013 especially a messy one.\n\nIf the Ajax call is successful, the server-side script returns the edited content, which we then place back inside the object from editComplete, and tidy up.\n\nMeanwhile, back at the server\n\nThe missing piece of the puzzle is the server-side script for committing the changes to your database. Obviously, any solution I provide here is not going to fit your particular application. For the purposes of getting a functional demo going, here\u2019s what I have in PHP.\n\n<?php\n     $id = $_POST['id'];\n     $content = $_POST['content'];\n     echo htmlspecialchars($content);\n ?>\n\nNot exactly rocket science is it? I\u2019m just catching the content item from the POST and echoing it back. For your application to be useful, however, you\u2019ll need to know exactly which record you should be updating. I\u2019m passing in the ID of my <div>, which is not a fat lot of use. You can modify saveChanges() to post back whatever information your app needs to know in order to process the update.\n\nYou should also check the user\u2019s credentials to make sure they have permission to edit whatever it is they\u2019re editing. Basically the same rules apply as with any script in your application.\n\nLimitations\n\nThere are a few bits and bobs that in an ideal world I would tidy up. The first is the error handling, as I\u2019ve already mentioned. The second is that from an idealistic standpoint, I\u2019d rather not be using innerHTML. However, the reality is that it\u2019s presently the most efficient way of making large changes to the document. If you\u2019re serving as XML, remember that you\u2019ll need to replace these with proper DOM nodes.\n\nIt\u2019s also important to note that it\u2019s quite difficult to make something like this universally accessible. Whenever you start updating large chunks of a document based on user interaction, a lot of non-traditional devices don\u2019t cope well. The benefit of this technique, though, is that if JavaScript is unavailable none of the functionality gets implemented at all \u2013 it fails silently. It is for this reason that this shouldn\u2019t be used as a complete replacement for a traditional, universally accessible edit form. It\u2019s a great time-saver for those with the ability to use it, but it\u2019s no replacement.\n\nSee it in action\n\nI\u2019ve put together an example page using the inert PHP script above. That is to say, your edits aren\u2019t committed to a database, so the example is reset when the page is reloaded.", "2005", "Drew McLellan", "drewmclellan", "2005-12-23T00:00:00+00:00", "https://24ways.org/2005/edit-in-place-with-ajax/", "code"], [318, "Auto-Selecting Navigation", "In the article Centered Tabs with CSS Ethan laid out a tabbed navigation system which can be centred on the page. A frequent requirement for any tab-based navigation is to be able to visually represent the currently selected tab in some way.\n\nIf you\u2019re using a server-side language such as PHP, it\u2019s quite easy to write something like class=\"selected\" into your markup, but it can be even simpler than that.\n\nLet\u2019s take the navigation div from Ethan\u2019s article as an example.\n\n<div id=\"navigation\">\n     <ul>\n          <li><a href=\"#\"><span>Home</span></a></li>\n          <li><a href=\"#\"><span>About</span></a></li>\n          <li><a href=\"#\"><span>Our Work</span></a></li>\n          <li><a href=\"#\"><span>Products</span></a></li>\n          <li class=\"last\"><a href=\"#\"><span>Contact Us</span></a></li>\n     </ul>\n </div>\n\nAs you can see we have a standard unordered list which is then styled with CSS to look like tabs. By giving each tab a class which describes it\u2019s logical section of the site, if we were to then apply a class to the body tag of each page showing the same, we could write a clever CSS selector to highlight the correct tab on any given page. \n\nSound complicated? Well, it\u2019s not a trivial concept, but actually applying it is dead simple.\n\nModifying the markup\n\nFirst thing is to place a class name on each li in the list:\n\n<div id=\"navigation\">\n     <ul>\n          <li class=\"home\"><a href=\"#\"><span>Home</span></a></li>\n          <li class=\"about\"><a href=\"#\"><span>About</span></a></li>\n          <li class=\"work\"><a href=\"#\"><span>Our Work</span></a></li>\n          <li class=\"products\"><a href=\"#\"><span>Products</span></a></li>\n          <li class=\"last contact\"><a href=\"#\"><span>Contact Us</span></a></li>\n     </ul>\n </div>\n\nThen, on each page of your site, apply the a matching class to the body tag to indicate which section of the site that page is in. For example, on your About page:\n\n<body class=\"about\">...</body>\n\nWriting the CSS selector\n\nYou can now write a single CSS rule to match the selected tab on any given page. The logic is that you want to match the \u2018about\u2019 tab on the \u2018about\u2019 page and the \u2018products\u2019 tab on the \u2018products\u2019 page, so the selector looks like this:\n\nbody.home #navigation li.home,\n body.about #navigation li.about,\n body.work #navigation li.work,\n body.products #navigation li.products,\n body.contact #navigation li.contact{\n      ... whatever styles you need to show the tab selected ...\n } \n\nSo all you need to do when you create a new page in your site is to apply a class to the body tag to say which section it\u2019s in. The CSS will do the rest for you \u2013 without any server-side help.", "2005", "Drew McLellan", "drewmclellan", "2005-12-10T00:00:00+00:00", "https://24ways.org/2005/auto-selecting-navigation/", "code"], [336, "Practical Microformats with hCard", "You\u2019ve probably heard about microformats over the last few months. You may have even read the easily digestible introduction at Digital Web Magazine, but perhaps you\u2019ve not found time to actually implement much yet. That\u2019s understandable, as it can sometimes be difficult to see exactly what you\u2019re adding by applying a microformat to a page. Sure, you\u2019re semantically enhancing the information you\u2019re marking up, and the Semantic Web is a great idea and all, but what benefit is it right now, today? \n\nWell, the answer to that question is simple: you\u2019re adding lots of information that can be and is being used on the web here and now. The big ongoing battle amongst the big web companies if one of territory over information. Everyone\u2019s grasping for as much data as possible. Some of that information many of us are cautious to give away, but a lot of is happy to be freely available. Of the data you\u2019re giving away, it makes sense to give it as much meaning as possible, thus enabling anyone from your friends and family to the giant search company down the road to make the most of it.\n\nOk, enough of the waffle, let\u2019s get working.\n\nIntroducing hCard\n\nYou may have come across hCard. It\u2019s a microformat for describing contact information (or really address book information) from within your HTML. It\u2019s based on the vCard format, which is the format the contacts/address book program on your computer uses. All the usual fields are available \u2013 name, address, town, website, email, you name it.\n\nIf you\u2019re running Firefox and Greasemonkey (or if you can, just to try this out), install this user script. What it does is look for instances of the hCard microformat in a page, and then add in a link to pass any hCards it finds to a web service which will convert it to a vCard. Take a look at the About the author box at the bottom of this article. It\u2019s a hCard, so you should be able to click the icon the user script inserts and add me to your Outlook contacts or OS X Address Book with just a click.\n\nSo microformats are useful after all. Free microformats all round!\n\nImplementing hCard\n\nThis is the really easy bit. All the hCard microformat is, is a bunch of predefined class names that you apply to the markup you\u2019ve probably already got around your contact information. Let\u2019s take the example of the About the author box from this article. Here\u2019s how the markup looks without hCard:\n\n<div class=\"bio\">\n     <h3>About the author</h3>\n     <p>Drew McLellan is a web developer, author and no-good swindler from \n     just outside London, England. At the \n     <a href=\"http://www.webstandards.org/\">Web Standards Project</a> he works \n     on press, strategy and tools. Drew keeps a \n     <a href=\"http://www.allinthehead.com/\">personal weblog</a> covering web \n     development issues and themes.</p>\n</div>\n\nThis is a really simple example because there\u2019s only two key bits of address book information here:- my name and my website address. Let\u2019s push it a little and say that the Web Standards Project is the organisation I work for \u2013 that gives us Name, Company and URL.\n\nTo kick off an hCard, you need a containing object with a class of vcard. The div I already have with a class of bio is perfect for this \u2013 all it needs to do is contain the rest of the contact information.\n\nThe next thing to identify is my name. hCard uses a class of fn (meaning Full Name) to identify a name. As is this case there\u2019s no element surrounding my name, we can just use a span. These changes give us:\n\n<div class=\"bio vcard\">\n     <h3>About the author</h3>\n     <p><span class=\"fn\">Drew McLellan</span> is a web developer...\n\nThe two remaining items are my URL and the organisation I belong to. The class names designated for those are url and org respectively. As both of those items are links in this case, I can apply the classes to those links. So here\u2019s the finished hCard.\n\n<div class=\"bio vcard\">\n     <h3>About the author</h3>\n     <p><span class=\"fn\">Drew McLellan</span> is a web developer, author and \n     no-good swindler from just outside London, England. \n     At the <a class=\"org\" href=\"http://www.webstandards.org/\">Web Standards Project</a> \n     he works on press, strategy and tools. Drew keeps a \n     <a class=\"url\" href=\"http://www.allinthehead.com/\">personal weblog</a> covering web \n     development issues and themes.</p>\n</div>\n\nOK, that was easy. By just applying a few easy class names to the HTML I was already publishing, I\u2019ve implemented an hCard that right now anyone with Greasemonkey can click to add to their address book, that Google and Yahoo! and whoever else can index and work out important things like which websites are associated with my name if they so choose (and boy, will they so choose), and in the future who knows what. In terms of effort, practically nil.\n\nWhere next?\n\nSo that was a trivial example, but to be honest it doesn\u2019t really get much more complex even with the most pernickety permutations. Because hCard is based on vCard (a mature and well thought-out standard), it\u2019s all tried and tested. Here\u2019s some good next steps.\n\n\n\tPlay with the hCard Creator\n\tTake a deep breath and read the spec\n\tStart implementing hCard as you go on your own projects \u2013 it takes very little time\n\n\nhCard is just one of an ever-increasing number of microformats. If this tickled your fancy, I suggest subscribing to the microformats site in your RSS reader to keep in touch with new developments.\n\nWhat\u2019s the take-away?\n\nThe take-away is this. They may sound like just more Web 2-point-HoHoHo hype, but microformats are a well thought-out, and easy to implement way of adding greater depth to the information you publish online. They have some nice benefits right away \u2013 certainly at geek-level \u2013 but in the longer term they become much more significant. We\u2019ve been at this long enough to know that the web has a long, long memory and that what you publish today will likely be around for years. But putting the extra depth of meaning into your documents now you can help guard that they\u2019ll continue to be useful in the future, and not just a bunch of flat ASCII.", "2005", "Drew McLellan", "drewmclellan", "2005-12-06T00:00:00+00:00", "https://24ways.org/2005/practical-microformats-with-hcard/", "code"]], "truncated": false, "columns": ["rowid", "title", "contents", "year", "author", "author_slug", "published", "url", "topic"], "query": {"sql": "select rowid, * from articles where \"author_slug\" = :p0 order by rowid limit 101", "params": {"p0": "drewmclellan"}}, "query_ms": 2.0542144775390625}