rowid,title,contents,year,author,author_slug,published,url,topic 213,Accessibility Through Semantic HTML,"Working on Better, a tracker blocker, I spend an awful lot of my time with my nose in other people’s page sources. I’m mostly there looking for harmful tracking scripts, but often notice the HTML on some of the world’s most popular sites is in a sad state of neglect. What does neglected HTML look like? Here’s an example of the markup I found on a news site just yesterday. There’s a bit of text, a few links, and a few images. But mostly it’s div elements.
Some text more text
divs and spans, why do we use them so much? While I find tracking scripts completely inexcusable, I do understand why people write HTML like the above. As developers, we like to use divs and spans as they’re generic elements. They come with no associated default browser styles or behaviour except that div displays as a block, and span displays inline. If we make our page up out of divs and spans, we know we’ll have absolute control over styles and behaviour cross-browser, and we won’t need a CSS reset. Absolute control may seem like an advantage, but there’s a greater benefit to less generic, more semantic elements. Browsers render semantic elements with their own distinct styles and behaviours. For example, button looks and behaves differently from a. And ul is different from ol. These defaults are shortcuts to a more usable and accessible web. They provide consistent and well-tested components for common interactions. Semantic elements aid usability A good example of how browser defaults can benefit the usability of an element is in the as a popover-style menu. On a touchscreen, Safari overlays the same menu over the lower half of the screen as a “picker view.” Option menu in Safari on macOS. Option menu picker in Safari on iOS. The iOS picker is a much better experience than struggling to pick from a complicated interface inside the page. The menu is shown more clearly than in the confined space on the page, which makes the options easier to read. The required swipe and tap gestures are consistent with the rest of the operating system, making the expected interaction easier to understand. The whole menu is scaled up, meaning the gestures don’t need such fine motor control. Good usability is good accessibility. When we choose to use a div or span over a more semantic HTML element, we’re also doing hard work the browser could be doing for us. We don’t need to tie ourselves in knots making a custom div into a keyboard navigable option menu. Using select passes the bulk of the responsibility over to the browser.  Letting the browser do most of the work is also more future-friendly. More devices, with different expected interactions, will be released in the future. When that happens, the devices’ browsers can adapt our sites according to those interactions. Then we can spend our time doing something more fun than rewriting cross-browser JavaScript for each new device. HTML’s impact on accessibility Assistive technology also uses semantic HTML to understand how best to convey each element to its user. For screen readers Semantic HTML gives context to screen readers. Screen readers are a type of assistive technology that reads the content of the screen to the person using it. All sites have a linear page source. Sighted visitors can use visual cues on the page to navigate to their desired content in a non-linear fashion. As screen readers output audio (and sometimes braille), those visual cues aren’t usable in the same way. Screen readers provide alternative means of navigation, enabling people to jump between different types of content, such as links, forms, headings, lists, and paragraphs. If all our content is marked up using divs and spans, we’re not giving screen readers a chance to index the valuable content. For keyboard navigation Keyboard-only navigation is also aided by semantic HTML. Forms, option menus, navigation, video, and audio are particularly hard for people relying on a keyboard to access. For instance, option menus and navigation can be very fiddly if you need to use a mouse to hover a menu open and move to select the desired item at the same time.  Again, we can leave much of the interaction to the browser through semantic HTML. Semantic form elements can convey if a check box has been checked, or which label is associated with which input field. These default behaviours can make the difference between a person being able to use a form or leaving the site out of frustration. Did I convince you yet? I hope so. Let’s finish with some easy guidelines to follow. 1. Use the most semantic HTML element for the job When you reach for a div, first check if there’s a better element to do the job. What is the role of that element? How should a person be interacting with the element? Are you using class names like nav, header, or main? There are HTML5 elements for those sections! Using specific elements can also make writing CSS simpler, and ensure a consistent design with minimal effort. 2. Separate structure and style Don’t choose HTML elements based on how they’re styled in your CSS. Nowadays, common practice is to use class names rather than elements for CSS selectors. You’re unlikely to wrap all your page content in an

element because you want all the text to be big and bold. Still, it can be easy to choose an HTML element because it will be the easiest to style. Focusing on content without style will help us choose the most semantic HTML element without that temptation. For example, you could add a class of .btn to a div to make it look like a button. But we all know that only a button will really behave like a button. 3. Use progressive enhancement for enhanced functionality Airbnb and Groupon recently proved we’re not past the laziness of “this site only works in X browser.” Baffling disregard for the open web aside, making complex interactive experiences work cross-browser and cross-device is not easy. We can use progressive enhancement to layer fancy or unsupported features on top of a baseline “it works” experience.  We should build the baseline experience on a foundation of accessible, semantic HTML. Then, if you really want to add a specific feature for a proprietary browser, you can layer that on top, without breaking the underlying experience. 4. Test your work Validators are always valuable for checking the browser will be able to correctly interpret your markup. Document outline checkers can be valuable for testing your structure, but be aware that the HTML5 document outline is not actually implemented in browsers. Once you’ve got something resembling a web page, test the experience! Ensure that semantic HTML element you chose looks and behaves in a predictable manner consistent with its use across the web. Test cross-browser, test cross-device, and test with assistive technology. Testing with assistive technology is not as expensive as it used to be, you can even use your smartphone for testing on iOS and Android. Your visitors will thank you! Further reading Accessibility For Everyone by Laura Kalbag HTML5 Doctor HTML5 Accessibility An overview of HTML5 Semantics HTML reference on MDN  Heydon Pickering’s Inclusive Design Checklist The Paciello Group’s Inclusive Design Principles",2017,Laura Kalbag,laurakalbag,2017-12-15T00:00:00+00:00,https://24ways.org/2017/accessibility-through-semantic-html/,code 234,An Introduction to CSS 3-D Transforms,"Ladies and gentlemen, it is the second decade of the third millennium and we are still kicking around the same 2-D interface we got three decades ago. Sure, Apple debuted a few apps for OSX 10.7 that have a couple more 3-D flourishes, and Microsoft has had that Flip 3D for a while. But c’mon – 2011 is right around the corner. That’s Twenty Eleven, folks. Where is our 3-D virtual reality? By now, we should be zipping around the Metaverse on super-sonic motorbikes. Granted, the capability of rendering complex 3-D environments has been present for years. On the web, there are already several solutions: Flash; three.js in ; and, eventually, WebGL. Finally, we meagre front-end developers have our own three-dimensional jewel: CSS 3-D transforms! Rationale Like a beautiful jewel, 3-D transforms can be dazzling, a true spectacle to behold. But before we start tacking 3-D diamonds and rubies to our compositions like Liberace‘s tailor, we owe it to our users to ask how they can benefit from this awesome feature. An entire application should not take advantage of 3-D transforms. CSS was built to style documents, not generate explorable environments. I fail to find a benefit to completing a web form that can be accessed by swivelling my viewport to the Sign-Up Room (although there have been proposals to make the web just that). Nevertheless, there are plenty of opportunities to use 3-D transforms in between interactions with the interface, via transitions. Take, for instance, the Weather App on the iPhone. The application uses two views: a details view; and an options view. Switching between these two views is done with a 3-D flip transition. This informs the user that the interface has two – and only two – views, as they can exist only on either side of the same plane. Flipping from details view to options view via a 3-D transition Also, consider slide shows. When you’re looking at the last slide, what cues tip you off that advancing will restart the cycle at the first slide? A better paradigm might be achieved with a 3-D transform, placing the slides side-by-side in a circle (carousel) in three-dimensional space; in that arrangement, the last slide obviously comes before the first. 3-D transforms are more than just eye candy. We can also use them to solve dilemmas and make our applications more intuitive. Current support The CSS 3D Transforms module has been out in the wild for over a year now. Currently, only Safari supports the specification – which includes Safari on Mac OS X and Mobile Safari on iOS. The support roadmap for other browsers varies. The Mozilla team has taken some initial steps towards implementing the module. Mike Taylor tells me that the Opera team is keeping a close eye on CSS transforms, and is waiting until the specification is fleshed out. And our best friend Internet Explorer still needs to catch up to 2-D transforms before we can talk about the 3-D variety. To make matters more perplexing, Safari’s WebKit cousin Chrome currently accepts 3-D transform declarations, but renders them in 2-D space. Chrome team member Paul Irish, says that 3-D transforms are on the horizon, perhaps in one of the next 8.0 releases. This all adds up to a bit of a challenge for those of us excited by 3-D transforms. I’ll give it to you straight: missing the dimension of depth can make degradation a bit ungraceful. Unless the transform is relatively simple and holds up in non-3D-supporting browsers, you’ll most likely have to design another solution. But what’s another hurdle in a steeplechase? We web folk have had our mettle tested for years. We’re prepared to devise multiple solutions. Here’s the part of the article where I mention Modernizr, and you brush over it because you’ve read this part of an article hundreds of times before. But seriously, it’s the best way to test for CSS 3-D transform support. Use it. Even with these difficulties mounting up, trying out 3-D transforms today is the right move. The CSS 3-D transforms module was developed by the same team at Apple that produced the CSS 2D Transforms and Animation modules. Both specifications have since been adopted by Mozilla and Opera. Transforming in three-dimensions now will guarantee you’ll be ahead of the game when the other browsers catch up. The choice is yours. You can make excuses and pooh-pooh 3-D transforms because they’re too hard and only snobby Apple fans will see them today. Or, with a tip of the fedora to Mr Andy Clarke, you can get hard-boiled and start designing with the best features out there right this instant. So, I bid you, in the words of the eternal Optimus Prime… Transform and roll out. Let’s get coding. Perspective To activate 3-D space, an element needs perspective. This can be applied in two ways: using the transform property, with the perspective as a functional notation: -webkit-transform: perspective(600); or using the perspective property: -webkit-perspective: 600; See example: Perspective 1. The red element on the left uses transform: perspective() functional notation; the blue element on the right uses the perspective property These two formats both trigger a 3-D space, but there is a difference. The first, functional notation is convenient for directly applying a 3-D transform on a single element (in the previous example, I use it in conjunction with a rotateY transform). But when used on multiple elements, the transformed elements don’t line up as expected. If you use the same transform across elements with different positions, each element will have its own vanishing point. To remedy this, use the perspective property on a parent element, so each child shares the same 3-D space. See Example: Perspective 2. Each red box on the left has its own vanishing point within the parent container; the blue boxes on the right share the vanishing point of the parent container The value of perspective determines the intensity of the 3-D effect. Think of it as a distance from the viewer to the object. The greater the value, the further the distance, so the less intense the visual effect. perspective: 2000; yields a subtle 3-D effect, as if we were viewing an object from far away. perspective: 100; produces a tremendous 3-D effect, like a tiny insect viewing a massive object. By default, the vanishing point for a 3-D space is positioned at its centre. You can change the position of the vanishing point with perspective-origin property. -webkit-perspective-origin: 25% 75%; See Example: Perspective 3. 3-D transform functions As a web designer, you’re probably well acquainted with working in two dimensions, X and Y, positioning items horizontally and vertically. With a 3-D space initialised with perspective, we can now transform elements in all three glorious spatial dimensions, including the third Z dimension, depth. 3-D transforms use the same transform property used for 2-D transforms. If you’re familiar with 2-D transforms, you’ll find the basic 3D transform functions fairly similar. rotateX(angle) rotateY(angle) rotateZ(angle) translateZ(tz) scaleZ(sz) Whereas translateX() positions an element along the horizontal X-axis, translateZ() positions it along the Z-axis, which runs front to back in 3-D space. Positive values position the element closer to the viewer, negative values further away. The rotate functions rotate the element around the corresponding axis. This is somewhat counter-intuitive at first, as you might imagine that rotateX will spin an object left to right. Instead, using rotateX(45deg) rotates an element around the horizontal X-axis, so the top of the element angles back and away, and the bottom gets closer to the viewer. See Example: Transforms 1. 3-D rotate() and translate() functions around each axis There are also several shorthand transform functions that require values for all three dimensions: translate3d(tx,ty,tz) scale3d(sx,sy,sz) rotate3d(rx,ry,rz,angle) Pro-tip: These foo3d() transform functions also have the benefit of triggering hardware acceleration in Safari. Dean Jackson, CSS 3-D transform spec author and main WebKit dude, writes (to Thomas Fuchs): In essence, any transform that has a 3D operation as one of its functions will trigger hardware compositing, even when the actual transform is 2D, or not doing anything at all (such as translate3d(0,0,0)). Note this is just current behaviour, and could change in the future (which is why we don’t document or encourage it). But it is very helpful in some situations and can significantly improve redraw performance. For the sake of simplicity, my demos will use the basic transform functions, but if you’re writing production-ready CSS for iOS or Safari-only, make sure to use the foo3d() functions to get the best rendering performance. Card flip We now have all the tools to start making 3-D objects. Let’s get started with something simple: flipping a card. Here’s the basic markup we’ll need:
1
2
The .container will house the 3-D space. The #card acts as a wrapper for the 3-D object. Each face of the card has a separate element: .front; and .back. Even for such a simple object, I recommend using this same pattern for any 3-D transform. Keeping the 3-D space element and the object element(s) separate establishes a pattern that is simple to understand and easier to style. We’re ready for some 3-D stylin’. First, apply the necessary perspective to the parent 3-D space, along with any size or positioning styles. .container { width: 200px; height: 260px; position: relative; -webkit-perspective: 800; } Now the #card element can be transformed in its parent’s 3-D space. We’re combining absolute and relative positioning so the 3-D object is removed from the flow of the document. We’ll also add width: 100%; and height: 100%;. This ensures the object’s transform-origin will occur in the centre of .container. More on transform-origin later. Let’s add a CSS3 transition so users can see the transform take effect. #card { width: 100%; height: 100%; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transition: -webkit-transform 1s; } The .container’s perspective only applies to direct descendant children, in this case #card. In order for subsequent children to inherit a parent’s perspective, and live in the same 3-D space, the parent can pass along its perspective with transform-style: preserve-3d. Without 3-D transform-style, the faces of the card would be flattened with its parents and the back face’s rotation would be nullified. To position the faces in 3-D space, we’ll need to reset their positions in 2-D with position: absolute. In order to hide the reverse sides of the faces when they are faced away from the viewer, we use backface-visibility: hidden. #card figure { display: block; position: absolute; width: 100%; height: 100%; -webkit-backface-visibility: hidden; } To flip the .back face, we add a basic 3-D transform of rotateY(180deg). #card .front { background: red; } #card .back { background: blue; -webkit-transform: rotateY(180deg); } With the faces in place, the #card requires a corresponding style for when it is flipped. #card.flipped { -webkit-transform: rotateY(180deg); } Now we have a working 3-D object. To flip the card, we can toggle the flipped class. When .flipped, the #card will rotate 180 degrees, thus exposing the .back face. See Example: Card 1. Flipping a card in three dimensions Slide-flip Take another look at the Weather App 3-D transition. You’ll notice that it’s not quite the same effect as our previous demo. If you follow the right edge of the card, you’ll find that its corners stay within the container. Instead of pivoting from the horizontal centre, it pivots on that right edge. But the transition is not just a rotation – the edge moves horizontally from right to left. We can reproduce this transition just by modifying a couple of lines of CSS from our original card flip demo. The pivot point for the rotation occurs at the right side of the card. By default, the transform-origin of an element is at its horizontal and vertical centre (50% 50% or center center). Let’s change it to the right side: #card { -webkit-transform-origin: right center; } That flip now needs some horizontal movement with translateX. We’ll set the rotation to -180deg so it flips right side out. #card.flipped { -webkit-transform: translateX(-100%) rotateY(-180deg); } See Example: Card 2. Creating a slide-flip from the right edge of the card Cube Creating 3-D card objects is a good way to get started with 3-D transforms. But once you’ve mastered them, you’ll be hungry to push it further and create some true 3-D objects: prisms. We’ll start out by making a cube. The markup for the cube is similar to the card. This time, however, we need six child elements for all six faces of the cube:
1
2
3
4
5
6
Basic position and size styles set the six faces on top of one another in the container. .container { width: 200px; height: 200px; position: relative; -webkit-perspective: 1000; } #cube { width: 100%; height: 100%; position: absolute; -webkit-transform-style: preserve-3d; } #cube figure { width: 196px; height: 196px; display: block; position: absolute; border: 2px solid black; } With the card, we only had to rotate its back face. The cube, however, requires that five of the six faces to be rotated. Faces 1 and 2 will be the front and back. Faces 3 and 4 will be the sides. Faces 5 and 6 will be the top and bottom. #cube .front { -webkit-transform: rotateY(0deg); } #cube .back { -webkit-transform: rotateX(180deg); } #cube .right { -webkit-transform: rotateY(90deg); } #cube .left { -webkit-transform: rotateY(-90deg); } #cube .top { -webkit-transform: rotateX(90deg); } #cube .bottom { -webkit-transform: rotateX(-90deg); } We could remove the first #cube .front style declaration, as this transform has no effect, but let’s leave it in to keep our code consistent. Now each face is rotated, and only the front face is visible. The four side faces are all perpendicular to the viewer, so they appear invisible. To push them out to their appropriate sides, they need to be translated out from the centre of their positions. Each side of the cube is 200 pixels wide. From the cube’s centre they’ll need to be translated out half that distance, 100px. #cube .front { -webkit-transform: rotateY(0deg) translateZ(100px); } #cube .back { -webkit-transform: rotateX(180deg) translateZ(100px); } #cube .right { -webkit-transform: rotateY(90deg) translateZ(100px); } #cube .left { -webkit-transform: rotateY(-90deg) translateZ(100px); } #cube .top { -webkit-transform: rotateX(90deg) translateZ(100px); } #cube .bottom { -webkit-transform: rotateX(-90deg) translateZ(100px); } Note here that the translateZ function comes after the rotate. The order of transform functions is important. Take a moment and soak this up. Each face is first rotated towards its position, then translated outward in a separate vector. We have a working cube, but we’re not done yet. Returning to the Z-axis origin For the sake of our users, our 3-D transforms should not distort the interface when the active panel is at its resting position. But once we start pushing elements off their Z-axis origin, distortion is inevitable. In order to keep 3-D transforms snappy, Safari composites the element, then applies the transform. Consequently, anti-aliasing on text will remain whatever it was before the transform was applied. When transformed forward in 3-D space, significant pixelation can occur. See Example: Transforms 2. Looking back at the Perspective 3 demo, note that no matter how small the perspective value is, or wherever the transform-origin may be, the panel number 1 always returns to its original position, as if all those funky 3-D transforms didn’t even matter. To resolve the distortion and restore pixel perfection to our #cube, we can push the 3-D object back, so that the front face will be positioned back to the Z-axis origin. #cube { -webkit-transform: translateZ(-100px); } See Example: Cube 1. Restoring the front face to the original position on the Z-axis Rotating the cube To expose any face of the cube, we’ll need a style that rotates the cube to expose any face. The transform values are the opposite of those for the corresponding face. We toggle the necessary class on the #box to apply the appropriate transform. #cube.show-front { -webkit-transform: translateZ(-100px) rotateY(0deg); } #cube.show-back { -webkit-transform: translateZ(-100px) rotateX(-180deg); } #cube.show-right { -webkit-transform: translateZ(-100px) rotateY(-90deg); } #cube.show-left { -webkit-transform: translateZ(-100px) rotateY(90deg); } #cube.show-top { -webkit-transform: translateZ(-100px) rotateX(-90deg); } #cube.show-bottom { -webkit-transform: translateZ(-100px) rotateX(90deg); } Notice how the order of the transform functions has reversed. First, we push the object back with translateZ, then we rotate it. Finishing up, we can add a transition to animate the rotation between states. #cube { -webkit-transition: -webkit-transform 1s; } See Example: Cube 2. Rotating the cube with a CSS transition Rectangular prism Cubes are easy enough to generate, as we only have to worry about one measurement. But how would we handle a non-regular rectangular prism? Let’s try to make one that’s 300 pixels wide, 200 pixels high, and 100 pixels deep. The markup remains the same as the #cube, but we’ll switch the cube id for #box. The container styles remain mostly the same: .container { width: 300px; height: 200px; position: relative; -webkit-perspective: 1000; } #box { width: 100%; height: 100%; position: absolute; -webkit-transform-style: preserve-3d; } Now to position the faces. Each set of faces will need their own sizes. The smaller faces (left, right, top and bottom) need to be positioned in the centre of the container, where they can be easily rotated and then shifted outward. The thinner left and right faces get positioned left: 100px ((300 − 100) ÷ 2), The stouter top and bottom faces get positioned top: 50px ((200 − 100) ÷ 2). #box figure { display: block; position: absolute; border: 2px solid black; } #box .front, #box .back { width: 296px; height: 196px; } #box .right, #box .left { width: 96px; height: 196px; left: 100px; } #box .top, #box .bottom { width: 296px; height: 96px; top: 50px; } The rotate values can all remain the same as the cube example, but for this rectangular prism, the translate values do differ. The front and back faces are each shifted out 50 pixels since the #box is 100 pixels deep. The translate value for the left and right faces is 150 pixels for their 300 pixels width. Top and bottom panels take 100 pixels for their 200 pixels height: #box .front { -webkit-transform: rotateY(0deg) translateZ(50px); } #box .back { -webkit-transform: rotateX(180deg) translateZ(50px); } #box .right { -webkit-transform: rotateY(90deg) translateZ(150px); } #box .left { -webkit-transform: rotateY(-90deg) translateZ(150px); } #box .top { -webkit-transform: rotateX(90deg) translateZ(100px); } #box .bottom { -webkit-transform: rotateX(-90deg) translateZ(100px); } See Example: Box 1. Just like the cube example, to expose a face, the #box needs to have a style to reverse that face’s transform. Both the translateZ and rotate values are the opposites of the corresponding face. #box.show-front { -webkit-transform: translateZ(-50px) rotateY(0deg); } #box.show-back { -webkit-transform: translateZ(-50px) rotateX(-180deg); } #box.show-right { -webkit-transform: translateZ(-150px) rotateY(-90deg); } #box.show-left { -webkit-transform: translateZ(-150px) rotateY(90deg); } #box.show-top { -webkit-transform: translateZ(-100px) rotateX(-90deg); } #box.show-bottom { -webkit-transform: translateZ(-100px) rotateX(90deg); } See Example: Box 2. Rotating the rectangular box with a CSS transition Carousel Front-end developers have a myriad of choices when it comes to content carousels. Now that we have 3-D capabilities in our browsers, why not take a shot at creating an actual 3-D carousel? The markup for this demo takes the same form as the box, cube and card. Let’s make it interesting and have a carousel with nine panels.
1
2
3
4
5
6
7
8
9
Now, apply basic layout styles. Let’s give each panel of the #carousel 20 pixel gaps between one another, done here with left: 10px; and top: 10px;. The effective width of each panel is 210 pixels. .container { width: 210px; height: 140px; position: relative; -webkit-perspective: 1000; } #carousel { width: 100%; height: 100%; position: absolute; -webkit-transform-style: preserve-3d; } #carousel figure { display: block; position: absolute; width: 186px; height: 116px; left: 10px; top: 10px; border: 2px solid black; } Next up: rotating the faces. This #carousel has nine panels. If each panel gets an equal distribution on the carousel, each panel would be rotated forty degrees from its neighbour (360 ÷ 9). #carousel figure:nth-child(1) { -webkit-transform: rotateY(0deg); } #carousel figure:nth-child(2) { -webkit-transform: rotateY(40deg); } #carousel figure:nth-child(3) { -webkit-transform: rotateY(80deg); } #carousel figure:nth-child(4) { -webkit-transform: rotateY(120deg); } #carousel figure:nth-child(5) { -webkit-transform: rotateY(160deg); } #carousel figure:nth-child(6) { -webkit-transform: rotateY(200deg); } #carousel figure:nth-child(7) { -webkit-transform: rotateY(240deg); } #carousel figure:nth-child(8) { -webkit-transform: rotateY(280deg); } #carousel figure:nth-child(9) { -webkit-transform: rotateY(320deg); } Now, the outward shift. Back when we were creating the cube and box, the translate value was simple to calculate, as it was equal to one half the width, height or depth of the object. With this carousel, there is no size we can automatically use as a reference. We’ll have to calculate the distance of the shift by other means. Drawing a diagram of the carousel, we can see that we know only two things: the width of each panel is 210 pixels; and the each panel is rotated forty degrees from the next. If we split one of these segments down its centre, we get a right-angled triangle, perfect for some trigonometry. We can determine the length of r in this diagram with a basic tangent equation: There you have it: the panels need to be translated 288 pixels in 3-D space. #carousel figure:nth-child(1) { -webkit-transform: rotateY(0deg) translateZ(288px); } #carousel figure:nth-child(2) { -webkit-transform: rotateY(40deg) translateZ(288px); } #carousel figure:nth-child(3) { -webkit-transform: rotateY(80deg) translateZ(288px); } #carousel figure:nth-child(4) { -webkit-transform: rotateY(120deg) translateZ(288px); } #carousel figure:nth-child(5) { -webkit-transform: rotateY(160deg) translateZ(288px); } #carousel figure:nth-child(6) { -webkit-transform: rotateY(200deg) translateZ(288px); } #carousel figure:nth-child(7) { -webkit-transform: rotateY(240deg) translateZ(288px); } #carousel figure:nth-child(8) { -webkit-transform: rotateY(280deg) translateZ(288px); } #carousel figure:nth-child(9) { -webkit-transform: rotateY(320deg) translateZ(288px); } If we decide to change the width of the panel or the number of panels, we only need to plug in those two variables into our equation to get the appropriate translateZ value. In JavaScript terms, that equation would be: var tz = Math.round( ( panelSize / 2 ) / Math.tan( ( ( Math.PI * 2 ) / numberOfPanels ) / 2 ) ); // or simplified to var tz = Math.round( ( panelSize / 2 ) / Math.tan( Math.PI / numberOfPanels ) ); Just like our previous 3-D objects, to show any one panel we need only apply the reverse transform on the carousel. Here’s the style to show the fifth panel: -webkit-transform: translateZ(-288px) rotateY(-160deg); See Example: Carousel 1. By now, you probably have two thoughts: Rewriting transform styles for each panel looks tedious. Why bother doing high school maths? Aren’t robots supposed to be doing all this work for us? And you’re absolutely right. The repetitive nature of 3-D objects lends itself to scripting. We can offload all the monotonous transform styles to our dynamic script, which, if done correctly, will be more flexible than the hard-coded version. See Example: Carousel 2. Conclusion 3-D transforms change the way we think about the blank canvas of web design. Better yet, they change the canvas itself, trading in the flat surface for voluminous depth. My hope is that you took at least one peak at a demo and were intrigued. We web designers, who have rejoiced for border-radius, box-shadow and background gradients, now have an incredible tool at our disposal in 3-D transforms. They deserve just the same enthusiasm, research and experimentation we have seen on other CSS3 features. Now is the perfect time to take the plunge and start thinking about how to use three dimensions to elevate our craft. I’m breathless waiting for what’s to come. See you on the flip side.",2010,David DeSandro,daviddesandro,2010-12-14T00:00:00+00:00,https://24ways.org/2010/intro-to-css-3d-transforms/,code 211,Automating Your Accessibility Tests,"Accessibility is one of those things we all wish we were better at. It can lead to a bunch of questions like: how do we make our site better? How do we test what we have done? Should we spend time each day going through our site to check everything by hand? Or just hope that everyone on our team has remembered to check their changes are accessible? This is where automated accessibility tests can come in. We can set up automated tests and have them run whenever someone makes a pull request, and even alongside end-to-end tests, too. Automated tests can’t cover everything however; only 20 to 50% of accessibility issues can be detected automatically. For example, we can’t yet automate the comparison of an alt attribute with an image’s content, and there are some screen reader tests that need to be carried out by hand too. To ensure our site is as accessible as possible, we will still need to carry out manual tests, and I will cover these later. First, I’m going to explain how I implemented automated accessibility tests on Elsevier’s ecommerce pages, and share some of the lessons I learnt along the way. Picking the right tool One of the hardest, but most important parts of creating our automated accessibility tests was choosing the right tool. We began by investigating aXe CLI, but soon realised it wouldn’t fit our requirements. It couldn’t check pages that required a visitor to log in, so while we could test our product pages, we couldn’t test any customer account pages. Instead we moved over to Pa11y. Its beforeScript step meant we could log into the site and test pages such as the order history. The example below shows the how the beforeScript step completes a login form and then waits for the login to complete before testing the page: beforeScript: function(page, options, next) { // An example function that can be used to make sure changes have been confirmed before continuing to run Pa11y function waitUntil(condition, retries, waitOver) { page.evaluate(condition, function(err, result) { if (result || retries < 1) { // Once the changes have taken place continue with Pa11y testing waitOver(); } else { retries -= 1; setTimeout(function() { waitUntil(condition, retries, waitOver); }, 200); } }); } // The script to manipulate the page must be run with page.evaluate to be run within the context of the page page.evaluate(function() { const user = document.querySelector('#login-form input[name=""email""]'); const password = document.querySelector('#login-form input[name=""password""]'); const submit = document.querySelector('#login-form input[name=""submit""]'); user.value = 'user@example.com'; password.value = 'password'; submit.click(); }, function() { // Use the waitUntil function to set the condition, number of retries and the callback waitUntil(function() { return window.location.href === 'https://example.com'; }, 20, next); }); } The waitUntil callback allows the test to be delayed until our test user is successfully logged in. Another thing to consider when picking a tool is the type of error messages it produces. aXe groups all elements with the same error together, so the list of issues is a lot easier to read, and it’s easier to identify the most commons problems. For example, here are some elements that have insufficient colour contrast: Violation of ""color-contrast"" with 8 occurrences! Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds. Correct invalid elements at: - #maincontent > .make_your_mark > div:nth-child(2) > p > span > span - #maincontent > .make_your_mark > div:nth-child(4) > p > span > span - #maincontent > .inform_your_decisions > div:nth-child(2) > p > span > span - #maincontent > .inform_your_decisions > div:nth-child(4) > p > span > span - #maincontent > .inform_your_decisions > div:nth-child(6) > p > span > span - #maincontent > .inform_your_decisions > div:nth-child(8) > p > span > span - #maincontent > .inform_your_decisions > div:nth-child(10) > p > span > span - #maincontent > .inform_your_decisions > div:nth-child(12) > p > span > span For details, see: https://dequeuniversity.com/rules/axe/2.5/color-contrast aXe also provides links to their site where they discuss the best way to fix the problem. In comparison, Pa11y lists each individual error which can lead to a very verbose list. However, it does provide helpful suggestions of how to fix problems, such as suggesting an alternative shade of a colour to use: • Error: This element has insufficient contrast at this conformance level. Expected a contrast ratio of at least 4.5:1, but text in this element has a contrast ratio of 2.96:1. Recommendation: change text colour to #767676. ⎣ WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail ⎣ #maincontent > div:nth-child(10) > div:nth-child(8) > p > span > span ⎣ Featured products: Integrating into our build pipeline We decided the perfect time to run our accessibility tests would be alongside our end-to-end tests. We have a Jenkins job that detects changes to our staging site and then triggers the end-to-end tests, and in turn our accessibility tests. Our Jenkins job retrieves the contents of a GitHub repository containing our Pa11y script file and npm package manifest. Once Jenkins has cloned the repository, it installs any dependencies and executes the tests via: npm install && npm test Bundling the URLs to be tested into our test script means we don’t have a command line style test where we list each URL we wish to test in the Jenkins CLI. It’s an effective method but can also be cluttered, and obscure which URLs are being tested. In the middle of the office we have a monitor displaying a Jenkins dashboard and from this we can see if the accessibility tests are passing or failing. Everyone in the team has access to the Jenkins logs and when the build fails they can see why and fix the issue. Fixing the issues As mentioned earlier, Pa11y can generate a long list of areas for improvement which can be very verbose and quite overwhelming. I recommend going through the list to see which issues occur most frequently and fix those first. For example, we initially had a lot of errors around colour contrast, and one shade of grey in particular. By making this colour darker, the number of errors decreased, and we could focus on the remaining issues. Another thing I like to do is to tackle the quick fixes, such as adding alt text to images. These are small things that allow us to make an impact instantly, giving us time to fix more detailed concerns such as addressing tabindex issues, or speaking to our designers about changing the contrast of elements on the site. Manual testing Adding automated tests to check our site for accessibility is great, but as I mentioned earlier, this can only cover 20-50% of potential issues. To improve on this, we need to test by hand too, either by ourselves or by asking others. One way we can test our site is to throw our mouse or trackpad away and interact with the site using only a keyboard. This allows us to check items such as tab order, and ensure menu items, buttons etc. can be used without a mouse. The commands may be different on different operating systems, but there are some great guides online for learning more about these. It’s tempting to add alt text and aria-labels to make errors go away, but if they don’t make any sense, what use are they really? Using a screenreader we can check that alt text accurately represents the image. This is also a great way to double check that our ARIA roles make sense, and that they correctly identify elements and how to interact with them. When testing our site with screen readers, it’s important to remember that not all screen readers are the same and some may interact with our site differently to others. Consider asking a range of people with different needs and abilities to test your site, too. People experience the web in numerous ways, be they permanent, temporary or even situational. They may interact with your site in ways you hadn’t even thought about, so this is a good way to broaden your knowledge and awareness. Tips and tricks One of our main issues with Pa11y is that it may find issues we don’t have the power to solve. A perfect example of this is the one pixel image Facebook injects into our site. So, we wrote a small function to go though such errors and ignore the ones that we cannot fix. const test = pa11y({ .... hideElements: '#ratings, #js-bigsearch', ... }); const ignoreErrors: string[] = [ '', '', '' ]; const filterResult = result => { if (ignoreErrors.indexOf(result.context) > -1) { return false; } return true; }; Initially we wanted to focus on fixing the major problems, so we added a rule to ignore notices and warnings. This made the list or errors much smaller and allowed us focus on fixing major issues such as colour contrast and missing alt text. The ignored notices and warnings can be added in later after these larger issues have been resolved. const test = pa11y({ ignore: [ 'notice', 'warning' ], ... }); Jenkins gotchas While using Jenkins we encountered a few problems. Sometimes Jenkins would indicate a build had passed when in reality it had failed. This was because Pa11y had timed out due to PhantomJS throwing an error, or the test didn’t go past the first URL. Pa11y has recently released a new beta version that uses headless Chrome instead of PhantomJS, so hopefully these issues will less occur less often. We tried a few approaches to solve these issues. First we added error handling, iterating over the array of test URLs so that if an unexpected error happened, we could catch it and exit the process with an error indicating that the job had failed (using process.exit(1)). for (const url of urls) { try { console.log(url); let urlResult = await run(url); urlResult = urlResult.filter(filterResult); urlResult.forEach(result => console.log(result)); } catch (e) { console.log('Error:', e); process.exit(1); } } We also had issues with unhandled rejections sometimes caused by a session disconnecting or similar errors. To avoid Jenkins indicating our site was passing with 100% accessibility, when in reality it had not executed any tests, we instructed Jenkins to fail the job when an unhandled rejection or uncaught exception occurred: process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at:', p, 'reason:', reason); process.exit(1); }); process.on('uncaughtException', (err) => { console.log('Caught exception: ${err}n'); process.exit(1); }); Now it’s your turn That’s it! That’s how we automated accessibility testing for Elsevier ecommerce pages, allowing us to improve our site and make it more accessible for everyone. I hope our experience can help you automate accessibility tests on your own site, and bring the web a step closer to being accessible to all.",2017,Seren Davies,serendavies,2017-12-07T00:00:00+00:00,https://24ways.org/2017/automating-your-accessibility-tests/,code 182,Breaking Out The Edges of The Browser,"HTML5 contains more than just the new entities for a more meaningful document, it also contains an arsenal of JavaScript APIs. So many in fact, that some APIs have outgrown the HTML5 spec’s backyard and have been sent away to grow up all on their own and been given the prestigious honour of being specs in their own right. So when I refer to (bendy finger quote) “HTML5”, I mean the HTML5 specification and a handful of other specifications that help us authors build web applications. Examples of those specs I would include in the umbrella term would be: geolocation, web storage, web databases, web sockets and web workers, to name a few. For all you guys and gals, on this special 2009 series of 24 ways, I’m just going to focus on data storage and offline applications: boldly taking your browser where no browser has gone before! Web Storage The Web Storage API is basically cookies on steroids, a unhealthy dosage of steroids. Cookies are always a pain to work with. First of all you have the problem of setting, changing and deleting them. Typically solved by Googling and blindly relying on PPK’s solution. If that wasn’t enough, there’s the 4Kb limit that some of you have hit when you really don’t want to. The Web Storage API gets around all of the hoops you have to jump through with cookies. Storage supports around 5Mb of data per domain (the spec’s recommendation, but it’s open to the browsers to implement anything they like) and splits in to two types of storage objects: sessionStorage – available to all pages on that domain while the window remains open localStorage – available on the domain until manually removed Support Ignoring beta browsers for our support list, below is a list of the major browsers and their support for the Web Storage API: Latest: Internet Explorer, Firefox, Safari (desktop & mobile/iPhone) Partial: Google Chrome (only supports localStorage) Not supported: Opera (as of 10.10) Usage Both sessionStorage and localStorage support the same interface for accessing their contents, so for these examples I’ll use localStorage. The storage interface includes the following methods: setItem(key, value) getItem(key) key(index) removeItem(key) clear() In the simple example below, we’ll use setItem and getItem to store and retrieve data: localStorage.setItem('name', 'Remy'); alert( localStorage.getItem('name') ); Using alert boxes can be a pretty lame way of debugging. Conveniently Safari (and Chrome) include database tab in their debugging tools (cmd+alt+i), so you can get a visual handle on the state of your data: Viewing localStorage As far as I know only Safari has this view on stored data natively in the browser. There may be a Firefox plugin (but I’ve not found it yet!) and IE… well that’s just IE. Even though we’ve used setItem and getItem, there’s also a few other ways you can set and access the data. In the example below, we’re accessing the stored value directly using an expando and equally, you can also set values this way: localStorage.name = ""Remy""; alert( localStorage.name ); // shows ""Remy"" The Web Storage API also has a key method, which is zero based, and returns the key in which data has been stored. This should also be in the same order that you set the keys, for example: alert( localStorage.getItem(localStorage.key(0)) ); // shows ""Remy"" I mention the key() method because it’s not an unlikely name for a stored value. This can cause serious problems though. When selecting the names for your keys, you need to be sure you don’t take one of the method names that are already on the storage object, like key, clear, etc. As there are no warnings when you try to overwrite the methods, it means when you come to access the key() method, the call breaks as key is a string value and not a function. You can try this yourself by creating a new stored value using localStorage.key = ""foo"" and you’ll see that the Safari debugger breaks because it relies on the key() method to enumerate each of the stored values. Usage Notes Currently all browsers only support storing strings. This also means if you store a numeric, it will get converted to a string: localStorage.setItem('count', 31); alert(typeof localStorage.getItem('count')); // shows ""string"" This also means you can’t store more complicated objects natively with the storage objects. To get around this, you can use Douglas Crockford’s JSON parser (though Firefox 3.5 has JSON parsing support baked in to the browser – yay!) json2.js to convert the object to a stringified JSON object: var person = { name: 'Remy', height: 'short', location: 'Brighton, UK' }; localStorage.setItem('person', JSON.stringify(person)); alert( JSON.parse(localStorage.getItem('person')).name ); // shows ""Remy"" Alternatives There are a few solutions out there that provide storage solutions that detect the Web Storage API, and if it’s not available, fall back to different technologies (for instance, using a flash object to store data). One comprehensive version of this is Dojo’s storage library. I’m personally more of a fan of libraries that plug missing functionality under the same namespace, just as Crockford’s JSON parser does (above). For those interested it what that might look like, I’ve mocked together a simple implementation of sessionStorage. Note that it’s incomplete (because it’s missing the key method), and it could be refactored to not using the JSON stringify (but you would need to ensure that the values were properly and safely encoded): // requires json2.js for all browsers other than Firefox 3.5 if (!window.sessionStorage && JSON) { window.sessionStorage = (function () { // window.top.name ensures top level, and supports around 2Mb var data = window.top.name ? JSON.parse(window.top.name) : {}; return { setItem: function (key, value) { data[key] = value+""""; // force to string window.top.name = JSON.stringify(data); }, removeItem: function (key) { delete data[key]; window.top.name = JSON.stringify(data); }, getItem: function (key) { return data[key] || null; }, clear: function () { data = {}; window.top.name = ''; } }; })(); } Now that we’ve cracked the cookie jar with our oversized Web Storage API, let’s have a look at how we take our applications offline entirely. Offline Applications Offline applications is (still) part of the HTML5 specification. It allows developers to build a web app and have it still function without an internet connection. The app is access via the same URL as it would be if the user were online, but the contents (or what the developer specifies) is served up to the browser from a local cache. From there it’s just an everyday stroll through open web technologies, i.e. you still have access to the Web Storage API and anything else you can do without a web connection. For this section, I’ll refer you to a prototype demo I wrote recently of a contrived Rubik’s cube (contrived because it doesn’t work and it only works in Safari because I’m using 3D transforms). Offline Rubik’s cube Support Support for offline applications is still fairly limited, but the possibilities of offline applications is pretty exciting, particularly as we’re seeing mobile support and support in applications such as Fluid (and I would expect other render engine wrapping apps). Support currently, is as follows: Latest: Safari (desktop & mobile/iPhone) Sort of: Firefox‡ Not supported: Internet Explorer, Opera, Google Chrome ‡ Firefox 3.5 was released to include offline support, but in fact has bugs where it doesn’t work properly (certainly on the Mac), Minefield (Firefox beta) has resolved the bug. Usage The status of the application’s cache can be tested from the window.applicationCache object. However, we’ll first look at how to enable your app for offline access. You need to create a manifest file, which will tell the browser what to cache, and then we point our web page to that cache: For the manifest to be properly read by the browser, your server needs to serve the .manifest files as text/manifest by adding the following to your mime.types: text/cache-manifest manifest Next we need to populate our manifest file so the browser can read it: CACHE MANIFEST /demo/rubiks/index.html /demo/rubiks/style.css /demo/rubiks/jquery.min.js /demo/rubiks/rubiks.js # version 15 The first line of the manifest must read CACHE MANIFEST. Then subsequent lines tell the browser what to cache. The HTML5 spec recommends that you include the calling web page (in my case index.html), but it’s not required. If I didn’t include index.html, the browser would cache it as part of the offline resources. These resources are implicitly under the CACHE namespace (which you can specify any number of times if you want to). In addition, there are two further namespaces: NETWORK and FALLBACK. NETWORK is a whitelist namespace that tells the browser not to cache this resource and always try to request it through the network. FALLBACK tells the browser that whilst in offline mode, if the resource isn’t available, it should return the fallback resource. Finally, in my example I’ve included a comment with a version number. This is because once you include a manifest, the only way you can tell the browser to reload the resources is if the manifest contents changes. So I’ve included a version number in the manifest which I can change forcing the browser to reload all of the assets. How it works If you’re building an app that makes use of the offline cache, I would strongly recommend that you add the manifest last. The browser implementations are very new, so can sometimes get a bit tricky to debug since once the resources are cached, they really stick in the browser. These are the steps that happen during a request for an app with a manifest: Browser: sends request for your app.html Server: serves all associated resources with app.html – as normal Browser: notices that app.html has a manifest, it re-request the assets in the manifest Server: serves the requested manifest assets (again) Browser: window.applicationCache has a status of UPDATEREADY Browser: reloads Browser: only request manifest file (which doesn’t show on the net requests panel) Server: responds with 304 Not Modified on the manifest file Browser: serves all the cached resources locally What might also add confusion to this process, is that the way the browsers work (currently) is if there is a cache already in place, it will use this first over updated resources. So if your manifest has changed, the browser will have already loaded the offline cache, so the user will only see the updated on the next reload. This may seem a bit convoluted, but you can also trigger some of this manually through the applicationCache methods which can ease some of this pain. If you bind to the online event you can manually try to update the offline cache. If the cache has then updated, swap the updated resources in to the cache and the next time the app loads it will be up to date. You could also prompt your user to reload the app (which is just a refresh) if there’s an update available. For example (though this is just pseudo code): addEvent(applicationCache, 'updateready', function () { applicationCache.swapCache(); tellUserToRefresh(); }); addEvent(window, 'online', function () { applicationCache.update(); }); Breaking out of the Browser So that’s two different technologies that you can use to break out of the traditional browser/web page model and get your apps working in a more application-ny way. There’s loads more in the HTML5 and non-HTML5 APIs to play with, so take your Christmas break to check them out!",2009,Remy Sharp,remysharp,2009-12-02T00:00:00+00:00,https://24ways.org/2009/breaking-out-the-edges-of-the-browser/,code 223,Calculating Color Contrast,"Some websites and services allow you to customize your profile by uploading pictures, changing the background color or other aspects of the design. As a customer, this personalization turns a web app into your little nest where you store your data. As a designer, letting your customers have free rein over the layout and design is a scary prospect. So what happens to all the stock text and images that are designed to work on nice white backgrounds? Even the Mac only lets you choose between two colors for the OS, blue or graphite! Opening up the ability to customize your site’s color scheme can be a recipe for disaster unless you are flexible and understand how to find maximum color contrasts. In this article I will walk you through two simple equations to determine if you should be using white or black text depending on the color of the background. The equations are both easy to implement and produce similar results. It isn’t a matter of which is better, but more the fact that you are using one at all! That way, even with the craziest of Geocities color schemes that your customers choose, at least your text will still be readable. Let’s have a look at a range of various possible colors. Maybe these are pre-made color schemes, corporate colors, or plucked from an image. Now that we have these potential background colors and their hex values, we need to find out whether the corresponding text should be in white or black, based on which has a higher contrast, therefore affording the best readability. This can be done at runtime with JavaScript or in the back-end before the HTML is served up. There are two functions I want to compare. The first, I call ’50%’. It takes the hex value and compares it to the value halfway between pure black and pure white. If the hex value is less than half, meaning it is on the darker side of the spectrum, it returns white as the text color. If the result is greater than half, it’s on the lighter side of the spectrum and returns black as the text value. In PHP: function getContrast50($hexcolor){ return (hexdec($hexcolor) > 0xffffff/2) ? 'black':'white'; } In JavaScript: function getContrast50(hexcolor){ return (parseInt(hexcolor, 16) > 0xffffff/2) ? 'black':'white'; } It doesn’t get much simpler than that! The function converts the six-character hex color into an integer and compares that to one half the integer value of pure white. The function is easy to remember, but is naive when it comes to understanding how we perceive parts of the spectrum. Different wavelengths have greater or lesser impact on the contrast. The second equation is called ‘YIQ’ because it converts the RGB color space into YIQ, which takes into account the different impacts of its constituent parts. Again, the equation returns white or black and it’s also very easy to implement. In PHP: function getContrastYIQ($hexcolor){ $r = hexdec(substr($hexcolor,0,2)); $g = hexdec(substr($hexcolor,2,2)); $b = hexdec(substr($hexcolor,4,2)); $yiq = (($r*299)+($g*587)+($b*114))/1000; return ($yiq >= 128) ? 'black' : 'white'; } In JavaScript: function getContrastYIQ(hexcolor){ var r = parseInt(hexcolor.substr(0,2),16); var g = parseInt(hexcolor.substr(2,2),16); var b = parseInt(hexcolor.substr(4,2),16); var yiq = ((r*299)+(g*587)+(b*114))/1000; return (yiq >= 128) ? 'black' : 'white'; } You’ll notice first that we have broken down the hex value into separate RGB values. This is important because each of these channels is scaled in accordance to its visual impact. Once everything is scaled and normalized, it will be in a range between zero and 255. Much like the previous ’50%’ function, we now need to check if the input is above or below halfway. Depending on where that value is, we’ll return the corresponding highest contrasting color. That’s it: two simple contrast equations which work really well to determine the best readability. If you are interested in learning more, the W3C has a few documents about color contrast and how to determine if there is enough contrast between any two colors. This is important for accessibility to make sure there is enough contrast between your text and link colors and the background. There is also a great article by Kevin Hale on Particletree about his experience with choosing light or dark themes. To round it out, Jonathan Snook created a color contrast picker which allows you to play with RGB sliders to get values for YIQ, contrast and others. That way you can quickly fiddle with the knobs to find the right balance. Comparing results Let’s revisit our color schemes and see which text color is recommended for maximum contrast based on these two equations. If we use the simple ’50%’ contrast function, we can see that it recommends black against all the colors except the dark green and purple on the second row. In general, the equation feels the colors are light and that black is a better choice for the text. The more complex ‘YIQ’ function, with its weighted colors, has slightly different suggestions. White text is still recommended for the very dark colors, but there are some surprises. The red and pink values show white text rather than black. This equation takes into account the weight of the red value and determines that the hue is dark enough for white text to show the most contrast. As you can see, the two contrast algorithms agree most of the time. There are some instances where they conflict, but overall you can use the equation that you prefer. I don’t think it is a major issue if some edge-case colors get one contrast over another, they are still very readable. Now let’s look at some common colors and then see how the two functions compare. You can quickly see that they do pretty well across the whole spectrum. In the first few shades of grey, the white and black contrasts make sense, but as we test other colors in the spectrum, we do get some unexpected deviation. Pure red #FF0000 has a flip-flop. This is due to how the ‘YIQ’ function weights the RGB parts. While you might have a personal preference for one style over another, both are justifiable. In this second round of colors, we go deeper into the spectrum, off the beaten track. Again, most of the time the contrasting algorithms are in sync, but every once in a while they disagree. You can select which you prefer, neither of which is unreadable. Conclusion Contrast in color is important, especially if you cede all control and take a hands-off approach to the design. It is important to select smart defaults by making the contrast between colors as high as possible. This makes it easier for your customers to read, increases accessibility and is generally just easier on the eyes. Sure, there are plenty of other equations out there to determine contrast; what is most important is that you pick one and implement it into your system. So, go ahead and experiment with color in your design. You now know how easy it is to guarantee that your text will be the most readable in any circumstance.",2010,Brian Suda,briansuda,2010-12-24T00:00:00+00:00,https://24ways.org/2010/calculating-color-contrast/,code 147,Christmas Is In The AIR,"That’s right, Christmas is coming up fast and there’s plenty of things to do. Get the tree and lights up, get the turkey, buy presents and who know what else. And what about Santa? He’s got a list. I’m pretty sure he’s checking it twice. Sure, we could use an existing list making web site or even a desktop widget. But we’re geeks! What’s the fun in that? Let’s build our own to-do list application and do it with Adobe AIR! What’s Adobe AIR? Adobe AIR, formerly codenamed Apollo, is a runtime environment that runs on both Windows and OSX (with Linux support to follow). This runtime environment lets you build desktop applications using Adobe technologies like Flash and Flex. Oh, and HTML. That’s right, you web standards lovin’ maniac. You can build desktop applications that can run cross-platform using the trio of technologies, HTML, CSS and JavaScript. If you’ve tried developing with AIR before, you’ll need to get re-familiarized with the latest beta release as many things have changed since the last one (such as the API and restrictions within the sandbox.) To get started To get started in building an AIR application, you’ll need two basic things: The AIR runtime. The runtime is needed to run any AIR-based application. The SDK. The software development kit gives you all the pieces to test your application. Unzip the SDK into any folder you wish. You’ll also want to get your hands on the JavaScript API documentation which you’ll no doubt find yourself getting into before too long. (You can download it, too.) Also of interest, some development environments have support for AIR built right in. Aptana doesn’t have support for beta 3 yet but I suspect it’ll be available shortly. Within the SDK, there are two main tools that we’ll use: one to test the application (ADL) and another to build a distributable package of our application (ADT). I’ll get into this some more when we get to that stage of development. Building our To-do list application The first step to building an application within AIR is to create an XML file that defines our default application settings. I call mine application.xml, mostly because Aptana does that by default when creating a new AIR project. It makes sense though and I’ve stuck with it. Included in the templates folder of the SDK is an example XML file that you can use. The first key part to this after specifying things like the application ID, version, and filename, is to specify what the default content should be within the content tags. Enter in the name of the HTML file you wish to load. Within this HTML file will be our application. ui.html Create a new HTML document and name it ui.html and place it in the same directory as the application.xml file. The first thing you’ll want to do is copy over the AIRAliases.js file from the frameworks folder of the SDK and add a link to it within your HTML document. The aliases create shorthand links to all of the Flash-based APIs. Now is probably a good time to explain how to debug your application. Debugging our application So, with our XML file created and HTML file started, let’s try testing our ‘application’. We’ll need the ADL application located in BIN folder of the SDK and tell it to run the application.xml file. /path/to/adl /path/to/application.xml You can also just drag the XML file onto ADL and it’ll accomplish the same thing. If you just did that and noticed that your blank application didn’t load, you’d be correct. It’s running but isn’t visible. Which at this point means you’ll have to shut down the ADL process. Sorry about that! Changing the visibility You have two ways to make your application visible. You can do it automatically by setting the placing true in the visible tag within the application.xml file. true The other way is to do it programmatically from within your application. You’d want to do it this way if you had other startup tasks to perform before showing the interface. To turn the UI on programmatically, simple set the visible property of nativeWindow to true. Sandbox Security Now that we have an application that we can see when we start it, it’s time to build the to-do list application. In doing so, you’d probably think that using a JavaScript library is a really good idea — and it can be but there are some limitations within AIR that have to be considered. An HTML document, by default, runs within the application sandbox. You have full access to the AIR APIs but once the onload event of the window has fired, you’ll have a limited ability to make use of eval and other dynamic script injection approaches. This limits the ability of external sources from gaining access to everything the AIR API offers, such as database and local file system access. You’ll still be able to make use of eval for evaluating JSON responses, which is probably the most important if you wish to consume JSON-based services. If you wish to create a greater wall of security between AIR and your HTML document loading in external resources, you can create a child sandbox. We won’t need to worry about it for our application so I won’t go any further into it but definitely keep this in mind. Finally, our application Getting tired of all this preamble? Let’s actually build our to-do list application. I’ll use jQuery because it’s small and should suit our needs nicely. Let’s begin with some structure: Now we need to wire up that button to actually add a new item to our to-do list. And just like that, we’ve got a to-do list! That’s it! Just never close your application and you’ll remember everything. Okay, that’s not very practical. You need to have some way of storing your to-do items until the next time you open up the application. Storing Data You’ve essentially got 4 different ways that you can store data: Using the local database. AIR comes with SQLLite built in. That means you can create tables and insert, update and select data from that database just like on a web server. Using the file system. You can also create files on the local machine. You have access to a few folders on the local system such as the documents folder and the desktop. Using EcryptedLocalStore. I like using the EcryptedLocalStore because it allows you to easily save key/value pairs and have that information encrypted. All this within just a couple lines of code. Sending the data to a remote API. Our to-do list could sync up with Remember the Milk, for example. To demonstrate some persistence, we’ll use the file system to store our files. In addition, we’ll let the user specify where the file should be saved. This way, we can create multiple to-do lists, keeping them separate and organized. The application is now broken down into 4 basic tasks: Load data from the file system. Perform any interface bindings. Manage creating and deleting items from the list. Save any changes to the list back to the file system. Loading in data from the file system When the application starts up, we’ll prompt the user to select a file or specify a new to-do list. Within AIR, there are 3 main file objects: File, FileMode, and FileStream. File handles file and path names, FileMode is used as a parameter for the FileStream to specify whether the file should be read-only or for write access. The FileStream object handles all the read/write activity. The File object has a number of shortcuts to default paths like the documents folder, the desktop, or even the application store. In this case, we’ll specify the documents folder as the default location and then use the browseForSave method to prompt the user to specify a new or existing file. If the user specifies an existing file, they’ll be asked whether they want to overwrite it. var store = air.File.documentsDirectory; var fileStream = new air.FileStream(); store.browseForSave(""Choose To-do List""); Then we add an event listener for when the user has selected a file. When the file is selected, we check to see if the file exists and if it does, read in the contents, splitting the file on new lines and creating our list items within the interface. store.addEventListener(air.Event.SELECT, fileSelected); function fileSelected() { air.trace(store.nativePath); // load in any stored data var byteData = new air.ByteArray(); if(store.exists) { fileStream.open(store, air.FileMode.READ); fileStream.readBytes(byteData, 0, store.size); fileStream.close(); if(byteData.length > 0) { var s = byteData.readUTFBytes(byteData.length); oldlist = s.split(“\r\n”); // create todolist items for(var i=0; i < oldlist.length; i++) { createItem(oldlist[i], (new Date()).getTime() + i ); } } } } Perform Interface Bindings This is similar to before where we set the click event on the Add button but we’ve moved the code to save the list into a separate function. $('#add').click(function(){ var t = $('#text').val(); if(t){ // create an ID using the time createItem(t, (new Date()).getTime() ); } }) Manage creating and deleting items from the list The list management is now in its own function, similar to before but with some extra information to identify list items and with calls to save our list after each change. function createItem(t, id) { if(t.length == 0) return; // add it to the todo list todolist[id] = t; // use DOM methods to create the new list item var li = document.createElement('li'); // the extra space at the end creates a buffer between the text // and the delete link we're about to add li.appendChild(document.createTextNode(t + ' ')); // create the delete link var del = document.createElement('a'); // this makes it a true link. I feel dirty doing this. del.setAttribute('href', '#'); del.addEventListener('click', function(evt){ var id = this.id.substr(1); delete todolist[id]; // remove the item from the list this.parentNode.parentNode.removeChild(this.parentNode); saveList(); }); del.appendChild(document.createTextNode('[del]')); del.id = 'd' + id; li.appendChild(del); // append everything to the list $('#list').append(li); //reset the text box $('#text').val(''); saveList(); } Save changes to the file system Any time a change is made to the list, we update the file. The file will always reflect the current state of the list and we’ll never have to click a save button. It just iterates through the list, adding a new line to each one. function saveList(){ if(store.isDirectory) return; var packet = ''; for(var i in todolist) { packet += todolist[i] + '\r\n'; } var bytes = new air.ByteArray(); bytes.writeUTFBytes(packet); fileStream.open(store, air.FileMode.WRITE); fileStream.writeBytes(bytes, 0, bytes.length); fileStream.close(); } One important thing to mention here is that we check if the store is a directory first. The reason we do this goes back to our browseForSave call. If the user cancels the dialog without selecting a file first, then the store points to the documentsDirectory that we set it to initially. Since we haven’t specified a file, there’s no place to save the list. Hopefully by this point, you’ve been thinking of some cool ways to pimp out your list. Now we need to package this up so that we can let other people use it, too. Creating a Package Now that we’ve created our application, we need to package it up so that we can distribute it. This is a two step process. The first step is to create a code signing certificate (or you can pay for one from Thawte which will help authenticate you as an AIR application developer). To create a self-signed certificate, run the following command. This will create a PFX file that you’ll use to sign your application. adt -certificate -cn todo24ways 1024-RSA todo24ways.pfx mypassword After you’ve done that, you’ll need to create the package with the certificate adt -package -storetype pkcs12 -keystore todo24ways.pfx todo24ways.air application.xml . The important part to mention here is the period at the end of the command. We’re telling it to package up all files in the current directory. After that, just run the AIR file, which will install your application and run it. Important things to remember about AIR When developing an HTML application, the rendering engine is Webkit. You’ll thank your lucky stars that you aren’t struggling with cross-browser issues. (My personal favourites are multiple backgrounds and border radius!) Be mindful of memory leaks. Things like Ajax calls and event binding can cause applications to slowly leak memory over time. Web pages are normally short lived but desktop applications are often open for hours, if not days, and you may find your little desktop application taking up more memory than anything else on your machine! The WebKit runtime itself can also be a memory hog, usually taking about 15MB just for itself. If you create multiple HTML windows, it’ll add another 15MB to your memory footprint. Our little to-do list application shouldn’t be much of a concern, though. The other important thing to remember is that you’re still essentially running within a Flash environment. While you probably won’t notice this working in small applications, the moment you need to move to multiple windows or need to accomplish stuff beyond what HTML and JavaScript can give you, the need to understand some of the Flash-based elements will become more important. Lastly, the other thing to remember is that HTML links will load within the AIR application. If you want a link to open in the users web browser, you’ll need to capture that event and handle it on your own. The following code takes the HREF from a clicked link and opens it in the default web browser. air.navigateToURL(new air.URLRequest(this.href)); Only the beginning Of course, this is only the beginning of what you can do with Adobe AIR. You don’t have the same level of control as building a native desktop application, such as being able to launch other applications, but you do have more control than what you could have within a web application. Check out the Adobe AIR Developer Center for HTML and Ajax for tutorials and other resources. Now, go forth and create your desktop applications and hopefully you finish all your shopping before Christmas! Download the example files.",2007,Jonathan Snook,jonathansnook,2007-12-19T00:00:00+00:00,https://24ways.org/2007/christmas-is-in-the-air/,code 192,Cleaner Code with CSS3 Selectors,"The parts of CSS3 that seem to grab the most column inches on blogs and in articles are the shiny bits. Rounded corners, text shadow and new ways to achieve CSS layouts are all exciting and bring with them all kinds of possibilities for web design. However what really gets me, as a developer, excited is a bit more mundane. In this article I’m going to take a look at some of the ways our front and back-end code will be simplified by CSS3, by looking at the ways we achieve certain visual effects now in comparison to how we will achieve them in a glorious, CSS3-supported future. I’m also going to demonstrate how we can use these selectors now with a little help from JavaScript – which can work out very useful if you find yourself in a situation where you can’t change markup that is being output by some server-side code. The wonder of nth-child So why does nth-child get me so excited? Here is a really common situation, the designer would like the tables in the application to look like this: Setting every other table row to a different colour is a common way to enhance readability of long rows. The tried and tested way to implement this is by adding a class to every other row. If you are writing the markup for your table by hand this is a bit of a nuisance, and if you stick a row in the middle you have to change the rows the class is applied to. If your markup is generated by your content management system then you need to get the server-side code to add that class – if you have access to that code. Striping every other row - using classes
Name Cards sent Cards received Cards written but not sent
Ann 40 28 4
Joe 2 27 29
Paul 5 35 2
Louise 65 65 0
View Example 1 This situation is something I deal with on almost every project, and apart from being an extra thing to do, it just isn’t ideal having the server-side code squirt classes into the markup for purely presentational reasons. This is where the nth-child pseudo-class selector comes in. The server-side code creates a valid HTML table for the data, and the CSS then selects the odd rows with the following selector: tr:nth-child(odd) td { background-color: #86B486; } View Example 2 The odd and even keywords are very handy in this situation – however you can also use a multiplier here. 2n would be equivalent to the keyword ‘odd’ 3n would select every third row and so on. Browser support Sadly, nth-child has pretty poor browser support. It is not supported in Internet Explorer 8 and has somewhat buggy support in some other browsers. Firefox 3.5 does have support. In some situations however, you might want to consider using JavaScript to add this support to browsers that don’t have it. This can be very useful if you are dealing with a Content Management System where you have no ability to change the server-side code to add classes into the markup. I’m going to use jQuery in these examples as it is very simple to use the same CSS selector used in the CSS to target elements with jQuery – however you could use any library or write your own function to do the same job. In the CSS I have added the original class selector to the nth-child selector: tr:nth-child(odd) td, tr.odd td { background-color: #86B486; } Then I am adding some jQuery to add a class to the markup once the document has loaded – using the very same nth-child selector that works for browsers that support it. View Example 3 We could just add a background colour to the element using jQuery, however I prefer not to mix that information into the JavaScript as if we change the colour on our table rows I would need to remember to change it both in the CSS and in the JavaScript. Doing something different with the last element So here’s another thing that we often deal with. You have a list of items all floated left with a right hand margin on each element constrained within a fixed width layout. If each element has the right margin applied the margin on the final element will cause the set to become too wide forcing that last item down to the next row as shown in the below example where I have used a grey border to indicate the fixed width. Currently we have two ways to deal with this. We can put a negative right margin on the list, the same width as the space between the elements. This means that the extra margin on the final element fills that space and the item doesn’t drop down. The last item is different
View Example 4 The other solution will be to put a class on the final element and in the CSS remove the margin for this class. ul.gallery li.last { margin-right: 0; } This second solution may not be easy if the content is generated from server-side code that you don’t have access to change. It could all be so different. In CSS3 we have marvellously common-sense selectors such as last-child, meaning that we can simply add rules for the last list item. ul.gallery li:last-child { margin-right: 0; } View Example 5 This removed the margin on the li which is the last-child of the ul with a class of gallery. No messing about sticking classes on the last item, or pushing the width of the item out wit a negative margin. If this list of items repeated ad infinitum then you could also use nth-child for this task. Creating a rule that makes every 3rd element margin-less. ul.gallery li:nth-child(3n) { margin-right: 0; } View Example 6 A similar example is where the designer has added borders to the bottom of each element – but the last item does not have a border or is in some other way different. Again, only a class added to the last element will save you here if you cannot rely on using the last-child selector. Browser support for last-child The situation for last-child is similar to that of nth-child, in that there is no support in Internet Explorer 8. However, once again it is very simple to replicate the functionality using jQuery. Adding our .last class to the last list item. $(""ul.gallery li:last-child"").addClass(""last""); We could also use the nth-child selector to add the .last class to every third list item. $(""ul.gallery li:nth-child(3n)"").addClass(""last""); View Example 7 Fun with forms Styling forms can be a bit of a trial, made difficult by the fact that any CSS applied to the input element will effect text fields, submit buttons, checkboxes and radio buttons. As developers we are left adding classes to our form fields to differentiate them. In most builds all of my text fields have a simple class of text whereas I wouldn’t dream of adding a class of para to every paragraph element in a document. Syling form fields

Send your Christmas list to Santa

View Example 8 Attribute selectors provide a way of targeting elements by looking at the attributes of those elements. Unlike the other examples in this article which are CSS3 selectors, the attribute selector is actually a CSS2.1 selector – it just doesn’t get much use because of lack of support in Internet Explorer 6. Using attribute selectors we can write rules for text inputs and form buttons without needing to add any classes to the markup. For example after removing the text and button classes from my text and submit button input elements I can use the following rules to target them: form input[type=""text""] { border: 1px solid #333; padding: 0.2em; width: 400px; } form input[type=""submit""]{ border: 1px solid #333; background-color: #eee; color: #000; padding: 0.1em; } View Example 9 Another problem that I encounter with forms is where I am using CSS to position my labels and form elements by floating the labels. This works fine as long as I want all of my labels to be floated, however sometimes we get a set of radio buttons or a checkbox, and I don’t want the label field to be floated. As you can see in the below example the label for the checkbox is squashed up into the space used for the other labels, yet it makes more sense for the checkbox to display after the text. I could use a class on this label element however CSS3 lets me to target the label attribute directly by looking at the value of the for attribute. label[for=""fOptIn""] { float: none; width: auto; } Being able to precisely target attributes in this way is incredibly useful, and once IE6 is no longer an issue this will really help to clean up our markup and save us from having to create all kinds of special cases when generating this markup on the server-side. Browser support The news for attribute selectors is actually pretty good with Internet Explorer 7+, Firefox 2+ and all other modern browsers all having support. As I have already mentioned this is a CSS2.1 selector and so we really should expect to be able to use it as we head into 2010! Internet Explorer 7 has slightly buggy support and will fail on the label example shown above however I discovered a workaround in the Sitepoint CSS reference comments. Adding the selector label[htmlFor=""fOptIn""] to the correct selector will create a match for IE7. IE6 does not support these selector but, once again, you can use jQuery to plug the holes in IE6 support. The following jQuery will add the text and button classes to your fields and also add a checks class to the label for the checkbox, which you can use to remove the float and width for this element. $('form input[type=""submit""]').addClass(""button""); $('form input[type=""text""]').addClass(""text""); $('label[for=""fOptIn""]').addClass(""checks""); View Example 10 The selectors I’ve used in this article are easy to overlook as we do have ways to achieve these things currently. As developers – especially when we have frameworks and existing code that cope with these situations – it is easy to carry on as we always have done. I think that the time has come to start to clean up our front and backend code and replace our reliance on classes with these more advanced selectors. With the help of a little JavaScript almost all users will still get the full effect and, where we are dealing with purely visual effects, there is definitely a case to be made for not worrying about the very small percentage of people with old browsers and no JavaScript. They will still receive a readable website, it may just be missing some of the finesse offered to the modern browsing experience.",2009,Rachel Andrew,rachelandrew,2009-12-20T00:00:00+00:00,https://24ways.org/2009/cleaner-code-with-css3-selectors/,code 253,Clip Paths Know No Bounds,"CSS Shapes are getting a lot of attention as browser support has increased for properties like shape-outside and clip-path. There are a few ways that we can use CSS Shapes, in particular with the clip-path property, that are not necessarily evident at first glance. The basics of a clip path Before we dig into specific techniques to expand on clip paths, we should first take a look at a basic shape and clip-path. Clip paths can apply a CSS Shape such as a circle(), ellipse(), inset(), or the flexible polygon() to any element. Everywhere in the element that is not within the bounds of our shape will be visually removed. Using the polygon shape function, for example, we can create triangles, stars, or other straight-edged shapes as on Bennett Feely’s Clippy. While fixed units like pixels can be used when defining vertices/points (where the sides meet), percentages will give more flexibility to adapt to the element’s dimensions. See the Pen Clip Path Box by Dan Wilson (@danwilson) on CodePen. So for an octagon, we can set eight x, y pairs of percentages to define those points. In this case we start 30% into the width of the box for the first x and at the top of the box for the y and go clockwise. The visible area becomes the interior of the shape made by connecting these points with straight lines. clip-path: polygon( 30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30% ); A shape with less vertices than the eye can see It’s reasonable to look at the polygon() function and assume that we need to have one pair of x, y coordinates for every point in our shape. However, we gain some flexibility by thinking outside the box — or more specifically when we think outside the range of 0% - 100%. Our element’s box model will be the ultimate boundary for a clip-path, but we can still define points that exist beyond that natural box for an element. See the Pen CSS Shapes Know No Bounds by Dan Wilson (@danwilson) on CodePen. By going beyond the 0% - 100% range we can turn a polygon with three points into a quadrilateral, a pentagon, or a hexagon. In this example the shapes used are all similar triangles defining three points, but due to exceeding the bounds for our element box we visually see one triangle and two pentagons. Our earlier octagon can similarly be made with only four points. See the Pen Octagon with four points by Dan Wilson (@danwilson) on CodePen. Multiple shapes, one clip path We can lean on this power of going beyond the bounds of our element to also create more than one visual shape with a single polygon(). See the Pen Multiple shapes from one clip-path by Dan Wilson (@danwilson) on CodePen. Depending on how we lay it out we can make each shape directly, but since we know we can move around in the space beyond the element’s box, we can draw extra lines to help us get where we need to go next as needed. It can also help us in slicing an element. Combined with CSS Variables, we can work with overlapping elements and clip each one into alternating strips. This example is two elements, each divided into a few rectangles. See the Pen 24w: Sliced Icon by Dan Wilson (@danwilson) on CodePen. Different shapes with fill rules A polygon() is not just a collection of points. There is one more key piece to its puzzle according to the specification — the Fill Rule. The default value we have been using so far is nonzero, and the second option is evenodd. These two values help determine what is considered inside and outside the shape. See the Pen A Star Multiways by Dan Wilson (@danwilson) on CodePen. As lines intersect we can get into situations where pieces seemingly on the inside can be considered outside the shape boundary. When using the evenodd fill rule, we can determine if a given point is inside or outside the boundary by drawing a ray from the point in any direction. If the ray crosses an even number of the clip path’s lines, the point is considered outside, and if it crosses an odd number the point is inside. Order of operations It is important to note that there are many CSS properties that affect the final composited appearance of an element via CSS Filters, Blend Modes, and more. These compositing effects are applied in the order: CSS Filters (e.g. filter: blur(2px)) Clipping (e.g. what this article is about) Masking (Clipping’s cousin) Blend Modes (e.g. mix-blend-mode: multiply) Opacity This means if we want to have a star shape and blur it, the blur will happen before the clip. And since blurs are most noticeable around the edge of an element box, the effect might be completely lost since we have clipped away the element’s box edges. See the Pen Order of Filter + Clip by Dan Wilson (@danwilson) on CodePen. If we want the edges of the star to be blurred, we do have the option to wrap our clipped element in a blurred parent element. The inner element will be rendered first (with its star clip) and then the parent will blur its contents normally. Revealing content with animation CSS Shapes can be transitioned and animated, allowing us to animate the visual area of our element without affecting the content within. For example, we can start with visually hidden content (fully clipped) and grow the clip path to reveal the content within. The important caveat for polygon() is that the number of points need to be the same for each keyframe, as well as the fill rule. Otherwise the browser will not have enough information to interpolate the intermediate values. See the Pen Clip Path Shape Reveal by Dan Wilson (@danwilson) on CodePen. Don’t keep CSS Shapes in a box Clip paths give us some interesting new possibilities, especially when we think of them as more than just basic shapes. We may be heavily modifying the visual representation of our elements with clip-path, but the underlying content remains unchanged and accessible which makes this property fairly powerful.",2018,Dan Wilson,danwilson,2018-12-20T00:00:00+00:00,https://24ways.org/2018/clip-paths-know-no-bounds/,code 8,Coding Towards Accessibility,"“Can we make it AAA-compliant?” – does this question strike fear into your heart? Maybe for no other reason than because you will soon have to wade through the impenetrable WCAG documentation once again, to find out exactly what AAA-compliant means? I’m not here to talk about that. The Web Content Accessibility Guidelines are a comprehensive and peer-reviewed resource which we’re lucky to have at our fingertips. But they are also a pig to read, and they may have contributed to the sense of mystery and dread with which some developers associate the word accessibility. This Christmas, I want to share with you some thoughts and some practical tips for building accessible interfaces which you can start using today, without having to do a ton of reading or changing your tools and workflow. But first, let’s clear up a couple of misconceptions. Dreary, flat experiences I recently built a front-end framework for the Post Office. This was a great gig for a developer, but when I found out about my client’s stringent accessibility requirements I was concerned that I’d have to scale back what was quite a complex set of visual designs. Sites like Jakob Neilsen’s old workhorse useit.com and even the pioneering GOV.UK may have to shoulder some of the blame for this. They put a premium on usability and accessibility over visual flourish. (Although, in fairness to Mr Neilsen, his new site nngroup.com is really quite a snazzy affair, comparatively.) Of course, there are other reasons for these sites’ aesthetics — and it’s not because of the limitations of the form. You can make an accessible site look as glossy or as plain as you want it to look. It’s always our own ingenuity and attention to detail that are going to be the limiting factors. Synecdoche We must always guard against the tendency to assume that catering to screen readers means we have the whole accessibility ballgame covered. There’s so much more to accessibility than assistive technology, as you know. And within the field of assistive technology there are plenty of other devices for us to consider. Planning to accommodate all these users and devices can be daunting. When I first started working in this field I thought that the breadth of technology was prohibitive. I didn’t even know what a screen reader looked like. (I assumed they were big and heavy, perhaps like an old typewriter, and certainly they would be expensive and difficult to fathom.) This is nonsense, of course. Screen reader emulators are readily available as browser extensions and can be activated in seconds. Chromevox and Fangs are both excellent and you should download one or the other right now. But the really good news is that you can emulate many other types of assistive technology without downloading a byte. And this is where we move from misconceptions into some (hopefully) useful advice. The mouse trap The simplest and most effective way to improve your abilities as a developer of accessible interfaces is to unplug your mouse. Keyboard operation has its own WCAG chapter, because most users of assistive technology are navigating the web using only their keyboards. You can go some way towards putting yourself into their shoes so easily — just by ditching a peripheral. Learning this was a lightbulb moment for me. When I build interfaces I am constantly flicking between code and the browser, testing or viewing the changes I have made. Now, instead of checking a new element once, I check it twice: once with my mouse and then again without. Don’t just :hover The reality is that when you first start doing this you can find your site becomes unusable straightaway. It’s easy to lose track of which element is in focus as you hit the tab key repeatedly. One of the easiest changes you can make to your coding practice is to add :focus and :active pseudo-classes to every hover state that you write. I’m still amazed at how many sites fail to provide a decent focus state for links (and despite previous 24 ways authors in 2007 and 2009 writing on this same issue!). You may find that in some cases it makes sense to have something other than, or in addition to, the hover state on focus, but start with the hover state that your designer has taken the time to provide you with. It’s a tiny change and there is no downside. So instead of this: .my-cool-link:hover { background-color: MistyRose ; } …try writing this: .my-cool-link:hover, .my-cool-link:focus, .my-cool-link:active { background-color: MistyRose ; } I’ve toyed with the idea of making a Sass mixin to take care of this for me, but I haven’t yet. I worry that people reading my code won’t see that I’m explicitly defining my focus and active states so I take the hit and write my hover rules out longhand. JavaScript can play, too This was another revelation for me. Keyboard-only navigation doesn’t necessitate a JavaScript-free experience, and up-to-date screen readers can execute JavaScript. So we’re able to create complex JavaScript-driven interfaces which all users can interact with. Some of the hard work has already been done for us. First, there are already conventions around keyboard-driven interfaces. Think about the last time you viewed a photo album on Facebook. You can use the arrow keys to switch between photos, and the escape key closes whichever lightbox-y UI thing Facebook is showing its photos in this week. Arrow keys (up/down as well as left/right) for progression through content; Escape to back out of something; Enter or space bar to indicate a positive intention — these are established keyboard conventions which we can apply to our interfaces to improve their accessiblity. Of course, by doing so we are improving our interfaces in general, giving all users the option to switch between keyboard and mouse actions as and when it suits them. Second, this guy wants to help you out. Hans Hillen is a developer who has done a great deal of work around accessibility and JavaScript-powered interfaces. Along with The Paciello Group he has created a version of the jQuery UI library which has been fully optimised for keyboard navigation and screen reader use. It’s a fantastic reference which I revisit all the time I’m not a huge fan of the jQuery UI library. It’s a pain to style and the code is a bit bloated. So I’ve not used this demo as a code resource to copy wholesale. I use it by playing with the various components and seeing how they react to keyboard controls. Each component is also fully marked up with the relevant ARIA roles to improve screen reader announcement where possible (more on this below). Coding for accessibility promotes good habits This is a another observation around accessibility and JavaScript. I noticed an improvement in the structure and abstraction of my code when I started adding keyboard controls to my interface elements. Your code has to become more modular and event-driven, because any number of events could trigger the same interaction. A mouse-click, the Enter key and the space bar could all conceivably trigger the same open function on a collapsed accordion element. (And you want to keep things DRY, don’t you?) If you aren’t already in the habit of separating out your interface functionality into discrete functions, you will be soon. var doSomethingCool = function(){ // Do something cool here. } // Bind function to a button click - pretty vanilla $('.myCoolButton').on('click', function(){ doSomethingCool(); return false; }); // Bind the same function to a range of keypresses $(document).keyup(function(e){ switch(e.keyCode) { case 13: // enter case 32: // spacebar doSomethingCool(); break; case 27: // escape doSomethingElse(); break; } }); To be honest, if you’re doing complex UI stuff with JavaScript these days, or if you’ve been building any responsive interfaces which rely on JavaScript, then you are most likely working with an application framework such as Backbone, Angular or Ember, so an abstraced and event-driven application structure will be familar to you. It should be super easy for you to start helping out your keyboard-only users if you aren’t already — just add a few more event bindings into your UI layer! Manipulating the tab order So, you’ve adjusted your mindset and now you test every change to your codebase using a keyboard as well as a mouse. You’ve applied all your hover states to :focus and :active so you can see where you’re tabbing on the page, and your interactive components react seamlessly to a mixture of mouse and keyboard commands. Feels good, right? There’s another level of optimisation to consider: manipulating the tab order. Certain DOM elements are naturally part of the tab order, and others are excluded. Links and input elements are the main elements included in the tab order, and static elements like paragraphs and headings are excluded. What if you want to make a static element ‘tabbable’? A good example would be in an expandable accordion component. Each section of the accordion should be separated by a heading, and there’s no reason to make that heading into a link simply because it’s interactive.

Tyrannosaurus

Tyrannosaurus; meaning ""tyrant lizard""...

Utahraptor

Utahraptor is a genus of theropod dinosaurs...

Dromiceiomimus

Ornithomimus is a genus of ornithomimid dinosaurs...

Adding the heading elements to the tab order is trivial. We just set their tabindex attribute to zero. You could do this on the server or the client. I prefer to do it with JavaScript as part of the accordion setup and initialisation process. $('.accordion-widget h3').attr('tabindex', '0'); You can apply this trick in reverse and take elements out of the tab order by setting their tabindex attribute to −1, or change the tab order completely by using other integers. This should be done with great care, if at all. You have to be sure that the markup you remove from the tab order comes out because it genuinely improves the keyboard interaction experience. This is hard to validate without user testing. The danger is that developers will try to sweep complicated parts of the UI under the carpet by taking them out of the tab order. This would be considered a dark pattern — at least on my team! A farewell ARIA This is where things can get complex, and I’m no expert on the ARIA specification: I feel like I’ve only dipped my toe into this aspect of coding for accessibility. But, as with WCAG, I’d like to demystify things a little bit to encourage you to look into this area further yourself. ARIA roles are of most benefit to screen reader users, because they modify and augment screen reader announcements. Let’s take our dinosaur accordion from the previous section. The markup is semantic, so a screen reader that can’t handle JavaScript will announce all the content within the accordion, no problem. But modern screen readers can deal with JavaScript, and this means that all the lovely dino information beneath each heading has probably been hidden on document.ready, when the accordion initialised. It might have been hidden using display:none, which prevents a screen reader from announcing content. If that’s as far as you have gone, then you’ve committed an accessibility sin by hiding content from screen readers. Your user will hear a set of headings being announced, with no content in between. It would sound something like this if you were using Chromevox: > Tyrannosaurus. Heading Three. > Utahraptor. Heading Three. > Dromiceiomimus. Heading Three. We can add some ARIA magic to the markup to improve this, using the tablist role. Start by adding a role of tablist to the widget, and roles of tab and tabpanel to the headings and paragraphs respectively. Set boolean values for aria-selected, aria-hidden and aria-expanded. The markup could end up looking something like this.

Utahraptor

Utahraptor is a genus of theropod dinosaurs...

Now, if a screen reader user encounters this markup they will hear the following: > Tyrannosaurus. Tab not selected; one of three. > Utahraptor. Tab not selected; two of three. > Dromiceiomimus. Tab not selected; three of three. You could add arrow key events to help the user browse up and down the tab list items until they find one they like. Your accordion open() function should update the ARIA boolean values as well as adding whatever classes and animations you have built in as standard. Your users know that unselected tabs are meant to be interacted with, so if a user triggers the open function (say, by hitting Enter or the space bar on the second item) they will hear this: > Utahraptor. Selected; two of three. The paragraph element for the expanded item will not be hidden by your CSS, which means it will be announced as normal by the screen reader. This kind of thing makes so much more sense when you have a working example to play with. Again, I refer you to the fantastic resource that Hans Hillen has put together: this is his take on an accessible accordion, on which much of my example is based. Conclusion Getting complex interfaces right for all of your users can be difficult — there’s no point pretending otherwise. And there’s no substitute for user testing with real users who navigate the web using assistive technology every day. This kind of testing can be time-consuming to recruit for and to conduct. On top of this, we now have accessibility on mobile devices to contend with. That’s a huge area in itself, and it’s one which I have not yet had a chance to research properly. So, there’s lots to learn, and there’s lots to do to get it right. But don’t be disheartened. If you have read this far then I’ll leave you with one final piece of advice: don’t wait. Don’t wait until you’re building a site which mandates AAA-compliance to try this stuff out. Don’t wait for a client with the will or the budget to conduct the full spectrum of user testing to come along. Unplug your mouse, and start playing with your interfaces in a new way. You’ll be surprised at the things that you learn and the issues you uncover. And the next time an true accessibility project comes along, you will be way ahead of the game.",2013,Charlie Perrins,charlieperrins,2013-12-03T00:00:00+00:00,https://24ways.org/2013/coding-towards-accessibility/,code 162,Conditional Love,"“Browser.” The four-letter word of web design. I mean, let’s face it: on the good days, when things just work in your target browsers, it’s marvelous. The air smells sweeter, birds’ songs sound more melodious, and both your design and your code are looking sharp. But on the less-than-good days (which is, frankly, most of them), you’re compelled to tie up all your browsers in a sack, heave them into the nearest river, and start designing all-imagemap websites. We all play favorites, after all: some will swear by Firefox, Opera fans are allegedly legion, and others still will frown upon anything less than the latest WebKit nightly. Thankfully, we do have an out for those little inconsistencies that crop up when dealing with cross-browser testing: CSS patches. Spare the Rod, Hack the Browser Before committing browsercide over some rendering bug, a designer will typically reach for a snippet of CSS fix the faulty browser. Historically referred to as “hacks,” I prefer Dan Cederholm’s more client-friendly alternative, “patches”. But whatever you call them, CSS patches all work along the same principle: supply the proper property value to the good browsers, while giving higher maintenance other browsers an incorrect value that their frustrating idiosyncratic rendering engine can understand. Traditionally, this has been done either by exploiting incomplete CSS support: #content { height: 1%; // Let's force hasLayout for old versions of IE. line-height: 1.6; padding: 1em; } html>body #content { height: auto; // Modern browsers get a proper height value. } or by exploiting bugs in their rendering engine to deliver alternate style rules: #content p { font-size: .8em; /* Hide from Mac IE5 \*/ font-size: .9em; /* End hiding from Mac IE5 */ } We’ve even used these exploits to serve up whole stylesheets altogether: @import url(""core.css""); @media tty { i{content:""\"";/*"" ""*/}} @import 'windows-ie5.css'; /*"";} }/* */ The list goes on, and on, and on. For every browser, for every bug, there’s a patch available to fix some rendering bug. But after some time working with standards-based layouts, I’ve found that CSS patches, as we’ve traditionally used them, become increasingly difficult to maintain. As stylesheets are modified over the course of a site’s lifetime, inline fixes we’ve written may become obsolete, making them difficult to find, update, or prune out of our CSS. A good patch requires a constant gardener to ensure that it adds more than just bloat to a stylesheet, and inline patches can be very hard to weed out of a decently sized CSS file. Giving the Kids Separate Rooms Since I joined Airbag Industries earlier this year, every project we’ve worked on has this in the head of its templates: The first element is, simply enough, a link element that points to the project’s main CSS file. No patches, no hacks: just pure, modern browser-friendly style rules. Which, nine times out of ten, will net you a design that looks like spilled eggnog in various versions of Internet Explorer. But don’t reach for the mulled wine quite yet. Immediately after, we’ve got a brace of conditional comments wrapped around two other link elements. These odd-looking comments allow us to selectively serve up additional stylesheets just to the version of IE that needs them. We’ve got one for IE 6 and below: And another for IE7 and above: Microsoft’s conditional comments aren’t exactly new, but they can be a valuable alternative to cooking CSS patches directly into a master stylesheet. And though they’re not a W3C-approved markup structure, I think they’re just brilliant because they innovate within the spec: non-IE devices will assume that the comments are just that, and ignore the markup altogether. This does, of course, mean that there’s a little extra markup in the head of our documents. But this approach can seriously cut down on the unnecessary patches served up to the browsers that don’t need them. Namely, we no longer have to write rules like this in our main stylesheet: #content { height: 1%; // Let's force hasLayout for old versions of IE. line-height: 1.6; padding: 1em; } html>body #content { height: auto; // Modern browsers get a proper height value. } Rather, we can simply write an un-patched rule in our core stylesheet: #content { line-height: 1.6; padding: 1em; } And now, our patch for older versions of IE goes in—you guessed it—the stylesheet for older versions of IE: #content { height: 1%; } The hasLayout patch is applied, our design’s repaired, and—most importantly—the patch is only seen by the browser that needs it. The “good” browsers don’t have to incur any added stylesheet weight from our IE patches, and Internet Explorer gets the conditional love it deserves. Most importantly, this “compartmentalized” approach to CSS patching makes it much easier for me to patch and maintain the fixes applied to a particular browser. If I need to track down a bug for IE7, I don’t need to scroll through dozens or hundreds of rules in my core stylesheet: instead, I just open the considerably slimmer IE7-specific patch file, make my edits, and move right along. Even Good Children Misbehave While IE may occupy the bulk of our debugging time, there’s no denying that other popular, modern browsers will occasionally disagree on how certain bits of CSS should be rendered. But without something as, well, pimp as conditional comments at our disposal, how do we bring the so-called “good browsers” back in line with our design? Assuming you’re loving the “one patch file per browser” model as much as I do, there’s just one alternative: JavaScript. function isSaf() { var isSaf = (document.childNodes && !document.all && !navigator.taintEnabled && !navigator.accentColorName) ? true : false; return isSaf; } function isOp() { var isOp = (window.opera) ? true : false; return isOp; } Instead of relying on dotcom-era tactics of parsing the browser’s user-agent string, we’re testing here for support for various DOM objects, whose presence or absence we can use to reasonably infer the browser we’re looking at. So running the isOp() function, for example, will test for Opera’s proprietary window.opera object, and thereby accurately tell you if your user’s running Norway’s finest browser. With scripts such as isOp() and isSaf() in place, you can then reasonably test which browser’s viewing your content, and insert additional link elements as needed. function loadPatches(dir) { if (document.getElementsByTagName() && document.createElement()) { var head = document.getElementsByTagName(""head"")[0]; if (head) { var css = new Array(); if (isSaf()) { css.push(""saf.css""); } else if (isOp()) { css.push(""opera.css""); } if (css.length) { var link = document.createElement(""link""); link.setAttribute(""rel"", ""stylesheet""); link.setAttribute(""type"", ""text/css""); link.setAttribute(""media"", ""screen, projection""); for (var i = 0; i < css.length; i++) { var tag = link.cloneNode(true); tag.setAttribute(""href"", dir + css[0]); head.appendChild(tag); } } } } } Here, we’re testing the results of isSaf() and isOp(), one after the other. For each function that returns true, then the name of a new stylesheet is added to the oh-so-cleverly named css array. Then, for each entry in css, we create a new link element, point it at our patch file, and insert it into the head of our template. Fire it up using your favorite onload or DOMContentLoaded function, and you’re good to go. Scripteat Emptor At this point, some of the audience’s more conscientious ‘scripters may be preparing to lob figgy pudding at this author’s head. And that’s perfectly understandable; relying on JavaScript to patch CSS chafes a bit against the normally clean separation we have between our pages’ content, presentation, and behavior layers. And beyond the philosophical concerns, this approach comes with a few technical caveats attached: Browser detection? So un-133t. Browser detection is not something I’d typically recommend. Whenever possible, a proper DOM script should check for the support of a given object or method, rather than the device with which your users view your content. It’s JavaScript, so don’t count on it being available. According to one site, roughly four percent of Internet users don’t have JavaScript enabled. Your site’s stats might be higher or lower than this number, but still: don’t expect that every member of your audience will see these additional stylesheets, and ensure that your content’s still accessible with JS turned off. Be a constant gardener. The sample isSaf() and isOp() functions I’ve written will tell you if the user’s browser is Safari or Opera. As a result, stylesheets written to patch issues in an old browser may break when later releases repair the relevant CSS bugs. You can, of course, add logic to these simple little scripts to serve up version-specific stylesheets, but that way madness may lie. In any event, test your work vigorously, and keep testing it when new versions of the targeted browsers come out. Make sure that a patch written today doesn’t become a bug tomorrow. Patching Firefox, Opera, and Safari isn’t something I’ve had to do frequently: still, there have been occasions where the above script’s come in handy. Between conditional comments, careful CSS auditing, and some judicious JavaScript, browser-based bugs can be handled with near-surgical precision. So pass the ‘nog. It’s patchin’ time.",2007,Ethan Marcotte,ethanmarcotte,2007-12-15T00:00:00+00:00,https://24ways.org/2007/conditional-love/,code 320,DOM Scripting Your Way to Better Blockquotes,"Block quotes are great. I don’t mean they’re great for indenting content – that would be an abuse of the browser’s default styling. I mean they’re great for semantically marking up a chunk of text that is being quoted verbatim. They’re especially useful in blog posts.

Progressive Enhancement, as a label for a strategy for Web design, was coined by Steven Champeon in a series of articles and presentations for Webmonkey and the SxSW Interactive conference.

Notice that you can’t just put the quoted text directly between the
tags. In order for your markup to be valid, block quotes may only contain block-level elements such as paragraphs. There is an optional cite attribute that you can place in the opening
tag. This should contain a URL containing the original text you are quoting:

Progressive Enhancement, as a label for a strategy for Web design, was coined by Steven Champeon in a series of articles and presentations for Webmonkey and the SxSW Interactive conference.

Great! Except… the default behavior in most browsers is to completely ignore the cite attribute. Even though it contains important and useful information, the URL in the cite attribute is hidden. You could simply duplicate the information with a hyperlink at the end of the quoted text:

Progressive Enhancement, as a label for a strategy for Web design, was coined by Steven Champeon in a series of articles and presentations for Webmonkey and the SxSW Interactive conference.

source

But somehow it feels wrong to have to write out the same URL twice every time you want to quote something. It could also get very tedious if you have a lot of quotes. Well, “tedious” is no problem to a programming language, so why not use a sprinkling of DOM Scripting? Here’s a plan for generating an attribution link for every block quote with a cite attribute: Write a function called prepareBlockquotes. Begin by making sure the browser understands the methods you will be using. Get all the blockquote elements in the document. Start looping through each one. Get the value of the cite attribute. If the value is empty, continue on to the next iteration of the loop. Create a paragraph. Create a link. Give the paragraph a class of “attribution”. Give the link an href attribute with the value from the cite attribute. Place the text “source” inside the link. Place the link inside the paragraph. Place the paragraph inside the block quote. Close the for loop. Close the function. Here’s how that translates to JavaScript: function prepareBlockquotes() { if (!document.getElementsByTagName || !document.createElement || !document.appendChild) return; var quotes = document.getElementsByTagName(""blockquote""); for (var i=0; i tags. You can style the attribution link using CSS. It might look good aligned to the right with a smaller font size. If you’re looking for something to do to keep you busy this Christmas, I’m sure that this function could be greatly improved. Here are a few ideas to get you started: Should the text inside the generated link be the URL itself? If the block quote has a title attribute, how would you take its value and use it as the text inside the generated link? Should the attribution paragraph be placed outside the block quote? If so, how would you that (remember, there is an insertBefore method but no insertAfter)? Can you think of other instances of useful information that’s locked away inside attributes? Access keys? Abbreviations?",2005,Jeremy Keith,jeremykeith,2005-12-05T00:00:00+00:00,https://24ways.org/2005/dom-scripting-your-way-to-better-blockquotes/,code 31,Dealing with Emergencies in Git,"The stockings were hung by the chimney with care, In hopes that version control soon would be there. This summer I moved to the UK with my partner, and the onslaught of the Christmas holiday season began around the end of October (October!). It does mean that I’ve had more than a fair amount of time to come up with horrible Git analogies for this article. Analogies, metaphors, and comparisons help the learner hook into existing mental models about how a system works. They only help, however, if the learner has enough familiarity with the topic at hand to make the connection between the old and new information. Let’s start by painting an updated version of Clement Clarke Moore’s Christmas living room. Empty stockings are hung up next to the fireplace, waiting for Saint Nicholas to come down the chimney and fill them with small treats. Holiday treats are scattered about. A bowl of mixed nuts, the holiday nutcracker, and a few clementines. A string of coloured lights winds its way up an evergreen. Perhaps a few of these images are familiar, or maybe they’re just settings you’ve seen in a movie. It doesn’t really matter what the living room looks like though. The important thing is to ground yourself in your own experiences before tackling a new subject. Instead of trying to brute-force your way into new information, as an adult learner constantly ask yourself: ‘What is this like? What does this remind me of? What do I already know that I can use to map out this new territory?’ It’s okay if the map isn’t perfect. As you refine your understanding of a new topic, you’ll outgrow the initial metaphors, analogies, and comparisons. With apologies to Mr. Moore, let’s give it a try. Getting Interrupted in Git When on the roof there arose such a clatter! You’re happily working on your software project when all of a sudden there are freaking reindeer on the roof! Whatever you’ve been working on is going to need to wait while you investigate the commotion. If you’ve got even a little bit of experience working with Git, you know that you cannot simply change what you’re working on in times of emergency. If you’ve been doing work, you have a dirty working directory and you cannot change branches, or push your work to a remote repository while in this state. Up to this point, you’ve probably dealt with emergencies by making a somewhat useless commit with a message something to the effect of ‘switching branches for a sec’. This isn’t exactly helpful to future you, as commits should really contain whole ideas of completed work. If you get interrupted, especially if there are reindeer on the roof, the chances are very high that you weren’t finished with what you were working on. You don’t need to make useless commits though. Instead, you can use the stash command. This command allows you to temporarily set aside all of your changes so that you can come back to them later. In this sense, stash is like setting your book down on the side table (or pushing the cat off your lap) so you can go investigate the noise on the roof. You aren’t putting your book away though, you’re just putting it down for a moment so you can come back and find it exactly the way it was when you put it down. Let’s say you’ve been working in the branch waiting-for-st-nicholas, and now you need to temporarily set aside your changes to see what the noise was on the roof: $ git stash After running this command, all uncommitted work will be temporarily removed from your working directory, and you will be returned to whatever state you were in the last time you committed your work. With the book safely on the side table, and the cat safely off your lap, you are now free to investigate the noise on the roof. It turns out it’s not reindeer after all, but just your boss who thought they’d help out by writing some code on the project you’ve been working on. Bless. Rolling your eyes, you agree to take a look and see what kind of mischief your boss has gotten themselves into this time. You fetch an updated list of branches from the remote repository, locate the branch your boss had been working on, and checkout a local copy: $ git fetch $ git branch -r $ git checkout -b helpful-boss-branch origin/helpful-boss-branch You are now in a local copy of the branch where you are free to look around, and figure out exactly what’s going on. You sigh audibly and say, ‘Okay. Tell me what was happening when you first realised you’d gotten into a mess’ as you look through the log messages for the branch. $ git log --oneline $ git log By using the log command you will be able to review the history of the branch and find out the moment right before your boss ended up stuck on your roof. You may also want to compare the work your boss has done to the main branch for your project. For this article, we’ll assume the main branch is named master. $ git diff master Looking through the commits, you may be able to see that things started out okay but then took a turn for the worse. Checking out a single commit Using commands you’re already familiar with, you can rewind through history and take a look at the state of the code at any moment in time by checking out a single commit, just like you would a branch. Using the log command, locate the unique identifier (commit hash) of the commit you want to investigate. For example, let’s say the unique identifier you want to checkout is 25f6d7f. $ git checkout 25f6d7f Note: checking out '25f6d7f'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using @-b@ with the checkout command again. Example: $ git checkout -b new_branch_name HEAD is now at 25f6d7f... Removed first paragraph. This is usually where people start to panic. Your boss screwed something up, and now your HEAD is detached. Under normal circumstances, these words would be a very good reason to panic. Take a deep breath. Nothing bad is going to happen. Being in a detached HEAD state just means you’ve temporarily disconnected from a known chain of events. In other words, you’re currently looking at the middle of a story (or branch) about what happened – and you’re not at the endpoint for this particular story. Git allows you to view the history of your repository as a timeline (technically it’s a directed acyclic graph). When you make commits which are not associated with a branch, they are essentially inaccessible once you return to a known branch. If you make commits while you’re in a detached HEAD state, and then try to return to a known branch, Git will give you a warning and tell you how to save your work. $ git checkout master Warning: you are leaving 1 commit behind, not connected to any of your branches: 7a85788 Your witty holiday commit message. If you want to keep them by creating a new branch, this may be a good time to do so with: $ git branch new_branch_name 7a85788 Switched to branch 'master' Your branch is up-to-date with 'origin/master'. So, if you want to save the commits you’ve made while in a detached HEAD state, you simply need to put them on a new branch. $ git branch saved-headless-commits 7a85788 With this trick under your belt, you can jingle around in history as much as you’d like. It’s not like sliding around on a timeline though. When you checkout a specific commit, you will only have access to the history from that point backwards in time. If you want to move forward in history, you’ll need to move back to the branch tip by checking out the branch again. $ git checkout helpful-boss-branch You’re now back to the present. Your HEAD is now pointing to the endpoint of a known branch, and so it is no longer detached. Any changes you made while on your adventure are safely stored in a new branch, assuming you’ve followed the instructions Git gave you. That wasn’t so scary after all, now, was it? Back to our reindeer problem. If your boss is anything like the bosses I’ve worked with, chances are very good that at least some of their work is worth salvaging. Depending on how your repository is structured, you’ll want to capture the good work using one of several different methods. Back in the living room, we’ll use our bowl of nuts to illustrate how you can rescue a tiny bit of work. Saving just one commit About that bowl of nuts. If you’re like me, you probably had some favourite kinds of nuts from an assorted collection. Walnuts were generally the most satisfying to crack open. So, instead of taking the entire bowl of nuts and dumping it into a stocking (merging the stocking and the bowl of nuts), we’re just going to pick out one nut from the bowl. In Git terms, we’re going to cherry-pick a commit and save it to another branch. First, checkout the main branch for your development work. From this branch, create a new branch where you can copy the changes into. $ git checkout master $ git checkout -b rescue-the-boss From your boss’s branch, helpful-boss-branch locate the commit you want to keep. $ git log --oneline helpful-boss-branch Let’s say the commit ID you want to keep is e08740b. From your rescue branch, use the command cherry-pick to copy the changes into your current branch. $ git cherry-pick e08740b If you review the history of your current branch again, you will see you now also have the changes made in the commit in your boss’s branch. At this point you might need to make a few additional fixes to help your boss out. (You’re angling for a bonus out of all this. Go the extra mile.) Once you’ve made your additional changes, you’ll need to add that work to the branch as well. $ git add [filename(s)] $ git commit -m ""Building on boss's work to improve feature X."" Go ahead and test everything, and make sure it’s perfect. You don’t want to introduce your own mistakes during the rescue mission! Uploading the fixed branch The next step is to upload the new branch to the remote repository so that your boss can download it and give you a huge bonus for helping you fix their branch. $ git push -u origin rescue-the-boss Cleaning up and getting back to work With your boss rescued, and your bonus secured, you can now delete the local temporary branches. $ git branch --delete rescue-the-boss $ git branch --delete helpful-boss-branch And settle back into your chair to wait for Saint Nicholas with your book, your branch, and possibly your cat. $ git checkout waiting-for-st-nicholas $ git stash pop Your working directory has been returned to exactly the same state you were in at the beginning of the article. Having fun with analogies I’ve had a bit of fun with analogies in this article. But sometimes those little twists on ideas can really help someone pick up a new idea (git stash: it’s like when Christmas comes around and everyone throws their fashion sense out the window and puts on a reindeer sweater for the holiday party; or git bisect: it’s like trying to find that one broken light on the string of Christmas lights). It doesn’t matter if the analogy isn’t perfect. It’s just a way to give someone a temporary hook into a concept in a way that makes the concept accessible while the learner becomes comfortable with it. As the learner’s comfort increases, the analogies can drop away, making room for the technically correct definition of how something works. Or, if you’re like me, you can choose to never grow old and just keep mucking about in the analogies. I’d argue it’s a lot more fun to play with a string of Christmas lights and some holiday cheer than a directed acyclic graph anyway.",2014,Emma Jane Westby,emmajanewestby,2014-12-02T00:00:00+00:00,https://24ways.org/2014/dealing-with-emergencies-in-git/,code 246,Designing Your Site Like It’s 1998,"It’s 20 years to the day since my wife and I started Stuff & Nonsense, our little studio and my outlet for creative ideas on the web. To celebrate this anniversary—and my fourteenth contribution to 24 ways— I’d like to explain how I would’ve developed a design for Planes, Trains and Automobiles, one of my favourite Christmas films. My design for Planes, Trains and Automobiles is fixed at 800px wide. Developing a framework I’ll start by using frames to set up the framework for this new website. Frames are individual pages—one for navigation, the other for my content—pulled together to form a frameset. Space is limited on lower-resolution screens, so by using frames I can ensure my navigation always remains visible. I can include any number of frames inside a element. I add two rows to my ; the first is for my navigation and is 50px tall, the second is for my content and will resize to fill any available space. As I don’t want frame borders or any space between my frames, I set frameborder and framespacing attributes to 0: […] Next I add the source of my two frame documents. I don’t want people to be able to resize or scroll my navigation, so I add the noresize attribute to that frame: I do want links from my navigation to open in the content frame, so I give each a name so I can specify where I want links to open: The framework for this website is simple as it contains only two horizontal rows. Should I need a more complex layout, I can nest as many framesets—and as many individual documents—as I need: Letterbox framesets were common way to deal with multiple screen sizes. In a letterbox, the central frameset had a fixed height and width, while the frames on the top, right, bottom, and left expanded to fill any remaining space. Handling older browsers Sadly not every browser supports frames, so I should send a helpful message to people who use older browsers asking them to upgrade. Happily, I can do that using noframes content: <body> <p>This page uses frames, but your browser doesn’t support them. Please upgrade your browser.</p> </body> Forcing someone back into a frame Sometimes, someone may follow a link to a page from a portal or search engine, or they might attempt to open it in a new window or tab. If that page properly belongs inside a , people could easily miss out on other parts of a design. This short script will prevent this happening and because it’s vanilla Javascript, it doesn’t require a library such as jQuery: Laying out my page Before starting my layout, I add a few basic background and colour styles. I must include these attributes in every page on my website: I want absolute control over how people experience my design and don’t want to allow it to stretch, so I first need a which limits the width of my layout to 800px. The align attribute will keep this
in the centre of someone’s screen:
[…]
Although they were developed for displaying tabular information, the cells and rows which make up the element make it ideal for the precise implementation of a design. I need several tables—often nested inside each other—to implement my design. These include tables for a banner and three rows of content:
[…]
[…]
[…]
[…]
The width of the first table—used for my banner—is fixed to match the logo it contains. As I don’t need borders, padding, or spacing between these cells, I use attributes to remove them:
The next table—which contains the largest image, introduction, and a call-to-action—is one of the most complex parts of my design, so I need to ensure its layout is pixel perfect. To do that I add an extra row at the top of this table and fill each of its cells with tiny transparent images: The height and width of these “shims” or “spacers” is only 1px but they will stretch to any size without increasing their weight on the page. This makes them perfect for performant website development. For the hero of this design, I splice up the large image into three separate files and apply each slice as a background to the table cells. I also match the height of those cells to the background images:   […]   I use tables and spacer images throughout the rest of this design to lay out the various types of content with perfect precision. For example, to add a single-pixel border around my two columns of content, I first apply a blue background to an outer table along with 1px of cellspacing, then simply nest an inner table—this time with a white background—inside it:
[…]
Adding details Tables are fabulous tools for laying out a page, but they’re also useful for implementing details on those pages. I can use a table to add a gradient background, rounded corners, and a shadow to the button which forms my “Buy the DVD” call-to-action. First, I splice my button graphic into three slices; two fixed-width rounded ends, plus a narrow gradient which stretches and makes this button responsive. Then, I add those images as backgrounds and use spacers to perfectly size my button:
Buy the DVD
I use those same elements to add details to headlines and lists too. Adding a “bullet” to each item in a list needs only two additional table cells, a circular graphic, and a spacer:
    Directed by John Hughes
Implementing a typographic hierarchy So far I’ve explained how to use frames, tables, and spacers to develop a layout for my content, but what about styling that content? I use elements to change the typeface from the browser’s default to any font installed on someone’s device: Planes, Trains and Automobiles is a comedy film […] To adjust the size of those fonts, I use the size attribute and a value between the smallest (1) and the largest (7) where 3 is the browser’s default. I use a size of 4 for this headline and 2 for the text which follows: Steve Martin An American actor, comedian, writer, producer, and musician. When I need to change the typeface, perhaps from a sans-serif like Arial to a serif like Times New Roman, I must change the value of the face attribute on every element on all pages on my website. NB: I use as many
elements as needed to create space between headlines and paragraphs. View the final result (and especially the source.) My modern day design for Planes, Trains and Automobiles. I can imagine many people reading this and thinking “This is terrible advice because we don’t develop websites like this in 2018.” That’s true. We have the ability to embed any number of web fonts into our products and websites and have far more control over type features, leading, ligatures, and sizes: font-variant-caps: titling-caps; font-variant-ligatures: common-ligatures; font-variant-numeric: oldstyle-nums; Grid has simplified the implementation of even the most complex compound grid down to just a few lines of CSS: body { display: grid; grid-template-columns: 3fr 1fr 2fr 2fr 1fr 3fr; grid-template-rows: auto; grid-column-gap: 2vw; grid-row-gap: 1vh; } Flexbox has made it easy to develop flexible components such as navigation links: nav ul { display: flex; } nav li { flex: 1; } Just one line of CSS can create multiple columns of fluid type: main { column-width: 12em; } CSS Shapes enable text to flow around irregular shapes including polygons: [src*=""main-img""] { float: left; shape-outside: polygon(…); } Today, we wouldn’t dream of using images and a table to add a gradient, rounded corners, and a shadow to a button or link, preferring instead: .btn { background: linear-gradient(#8B1212, #DD3A3C); border-radius: 1em; box-shadow: 0 2px 4px 0 rgba(0,0,0,0.50), inset 0 -1px 1px 0 rgba(0,0,0,0.50); } CSS Custom Properties, feature and media queries, filters, pseudo-elements, and SVG; the list of advances in HTML, CSS, and other technologies goes on. So does our understanding of how best to use them by separating content, structure, presentation, and behaviour. As 2018 draws to a close, we’re certain we know how to design and develop products and websites better than we did at the end of 1998. Strange as it might seem looking back, in 1998 we were also certain our techniques and technologies were the best for the job. That’s why it’s dangerous to believe with absolute certainty that the frameworks and tools we increasingly rely on today—tools like Bootstrap, Bower, and Brunch, Grunt, Gulp, Node, Require, React, and Sass—will be any more relevant in the future than elements, frames, layout tables, and spacer images are today. I have no prediction for what the web will be like twenty years from now. However, I want to believe we’ll build on what we’ve learned during these past two decades about the importance of accessibility, flexibility, and usability, and that the mistakes we made while infatuated by technologies won’t be repeated. Head over to my website if you’d like to read about how I’d implement my design for ‘Planes, Trains and Automobiles’ today.",2018,Andy Clarke,andyclarke,2018-12-23T00:00:00+00:00,https://24ways.org/2018/designing-your-site-like-its-1998/,code 256,Develop Your Naturalist Superpowers with Observable Notebooks and iNaturalist,"We’re going to level up your knowledge of what animals you might see in an area at a particular time of year - a skill every naturalist* strives for - using technology! Using iNaturalist and Observable Notebooks we’re going to prototype seasonality graphs for particular species in an area, and automatically create a guide to what animals you might see in each month. *(a Naturalist is someone who likes learning about nature, not someone who’s a fan of being naked, that’s a ‘Naturist’… different thing!) Looking for critters in rocky intertidal habitats One of my favourite things to do is going rockpooling, or as we call it over here in California, ‘tidepooling’. Amounting to the same thing, it’s going to a beach that has rocks where the tide covers then uncovers little pools of water at different times of the day. All sorts of fun creatures and life can be found in this ‘rocky intertidal habitat’ A particularly exciting creature that lives here is the Nudibranch, a type of super colourful ‘sea slug’. There are over 3000 species of Nudibranch worldwide. (The word “nudibranch” comes from the Latin nudus, naked, and the Greek βρανχια / brankhia, gills.) ​ They are however quite tricky to find! Even though they are often brightly coloured and interestingly shaped, some of them are very small, and in our part of the world in the Bay Area in California their appearance in our rockpools is seasonal. We see them more often in Summer months, despite the not-as-low tides as in our Winter and Spring seasons. My favourite place to go tidepooling here is Pillar Point in Half Moon bay (at other times of the year more famously known for the surf competition ‘Mavericks’). The rockpools there are rich in species diversity, of varied types and water-coverage habitat zones as well as being relatively accessible. ​ I was rockpooling at Pillar Point recently with my parents and we talked to a lady who remarked that she hadn’t seen any Nudibranchs on her visit this time. I realised that having an idea of what species to find where, and at what time of year is one of the many superpower goals of every budding Naturalist. Using technology and the croudsourced species observations of the iNaturalist community we can shortcut our way to this superpower! Finding nearby animals with iNaturalist We’re going to be getting our information about what animals you can see in Pillar Point using iNaturalist. iNaturalist is a really fun platform that helps connect people to nature and report their findings of life in the outdoors. It is also a community of nature-loving people who help each other identify and confirm those observations. iNaturalist is a project run as a joint initiative by the California Academy of Sciences and the National Geographic Society. I’ve been using iNaturalist for over two years to record and identify plants and animals that I’ve found in the outdoors. I use their iPhone app to upload my pictures, which then uses machine learning algorithms to make an initial guess at what it is I’ve seen. The community is really active, and I often find someone else has verified or updated my species guess pretty soon after posting. This process is great because once an observation has been identified by at least two people it becomes ‘verified’ and is considered research grade. Research grade observations get exported and used by scientists, as well as being indexed by the Global Biodiversity Information Facility, GBIF. ​ iNaturalist has a great API and API explorer, which makes interacting and prototyping using iNaturalist data really fun. For example, if you go to the API explorer and expand the Observations : Search and fetch section and then the GET /observations API, you get a selection of input boxes that allow you to play with options that you can then pass to the API when you click the ‘Try it out’ button. ​ You’ll then get a URL that looks a bit like https://api.inaturalist.org/v1/observations?captive=false &geo=true&verifiable=true&taxon_id=47113&lat=37.495461&lng=-122.499584 &radius=5&order=desc&order_by=created_at which you can call and interrrogate using a programming language of your choice. If you would like to see an all-JavaScript application that uses the iNaturalist API, take a look at OwlsNearMe.com which Simon and I built one weekend earlier this year. It gets your location and shows you all iNaturalist observations of owls near you and lists which species you are likely to see (not adjusted for season). Rapid development using Observable Notebooks We’re going to be using Observable Notebooks to prototype our examples, pulling data down from iNaturalist. I really like using visual notebooks like Observable, they are great for learning and building things quickly. You may be familiar with Jupyter notebooks for Python which is similar but takes a bit of setup to get going - I often use these for prototyping too. Observable is amazing for querying and visualising data with JavaScript and since it is a hosted product it doesn’t require any setup at all. You can follow along and play with this example on my Observable notebook. If you create an account there you can fork my notebook and create your own version of this example. Each ‘notebook’ consists of a page with a column of ‘cells’, similar to what you get in a spreadsheet. A cell can contain Markdown text or JavaScript code and the output of evaluating the cell appears above the code that generated it. There are lots of tutorials out there on Observable Notebooks, I like this code introduction one from Observable (and D3) creator Mike Bostock. Developing your Naturalist superpowers If you have an idea of what plants and critters you might see in a place at the time you visit, you can hone in on what you want to study and train your Naturalist eye to better identify the life around you. For our example, we care about wildlife we can see at Pillar Point, so we need a way of letting the iNaturalist API know which area we are interested in. We could use a latitide, longitude and radius for this, but a rectangular bounding box is a better shape for the reef. We can use this tool to draw the area we want to search within: boundingbox.klokantech.com ​ The tool lets you export the bounding box in several forms using the dropdown at the bottom left under the map givese We are going to use the ‘DublinCore’ format as it’s closest to the format needed by the iNaturalist API. westlimit=-122.50542; southlimit=37.492805; eastlimit=-122.492738; northlimit=37.499811 A quick map primer: The higher the latitude the more north it is The lower the latitude the more south it is Latitude 0 = the equator The higher the longitude the more east it is of Greenwich The lower the longitude the more west it is of Greenwich Longitude 0 = Greenwich In the iNaturalst API we want to use the parameters nelat, nelng, swlat, swlng to create a query that looks inside a bounding box of Pillar Point near Half Moon Bay in California: nelat = highest latitude = north limit = 37.499811 nelng = highest longitude = east limit = -122.492738 swlat = smallest latitude = south limit = 37.492805 swlng = smallest longitude = west limit = 122.50542 As API parameters these look like this: ?nelat=37.499811&nelng=-122.492738&swlat=37.492805&swlng=122.50542 These parameters in this format can be used for most of the iNaturalist API methods. Nudibranch seasonality in Pillar Point We can use the iNaturalist observation_histogram API to get a count of Nudibranch observations per week-of-year across all time and within our Pillar Point bounding box. In addition to the geographic parameters that we just worked out, we are also sending the taxon_id of 47113, which is iNaturalists internal number associated with the Nudibranch taxon. By using this we can get all species which are under the parent ‘Order Nudibranchia’. Another useful piece of naturalist knowledge is understanding the biological classification scheme of Taxanomic Rank - roughly, when a species has a Latin name of two words eg ‘Glaucus Atlanticus’ the first Latin word is the ‘Genus’ like a family name ‘Glaucus’, and the second word identifies that particular species, like a given name ‘Atlanticus’. The two Latin words together indicate a specific species, the term we use colloquially to refer to a type of animal often differs wildly region to region, and sometimes the same common name in two countries can refer to two different species. The common names for the Glaucus Atlanticus (which incidentally is my favourite sea slug) include: sea swallow, blue angel, blue glaucus, blue dragon, blue sea slug and blue ocean slug! Because this gets super confusing, Scientists like using this Latin name format instead. The following piece of code asks the iNaturalist Histogram API to return per-week counts for verified observations of Nudibranchs within our Pillar Point bounding box: pillar_point_counts_per_week = fetch( ""https://api.inaturalist.org/v1/observations/histogram?taxon_id=47113&nelat=37.499811&nelng=-122.492738&swlat=37.492805&swlng=-122.50542&date_field=observed&interval=week_of_year&verifiable=true"" ).then(response => { return response.json(); }) Our next step is to take this data and draw a graph! We’ll be using Vega-Lite for this, which is a fab JavaScript graphing libary that is also easy and fun to use with Observable Notebooks. (Here is a great tutorial on exploring data and drawing graphs with Observable and Vega-Lite) The iNaturalist API returns data that looks like this: { ""total_results"": 53, ""page"": 1, ""per_page"": 53, ""results"": { ""week_of_year"": { ""1"": 136, ""2"": 20, ""3"": 150, ""4"": 65, ""5"": 186, ""6"": 74, ""7"": 47, ""8"": 87, ""9"": 64, ""10"": 56, But for our Vega-Lite graph we need data that looks like this: [{ ""week"": ""01"", ""value"": 136 }, { ""week"": ""02"", ""value"": 20 }, ...] We can convert what we get back from the API to the second format using a loop that iterates over the object keys: objects_to_plot = { let objects = []; Object.keys(pillar_point_counts_per_week.results.week_of_year).map(function(week_index) { objects.push({ week: `Wk ${week_index.toString()}`, observations: pillar_point_counts_per_week.results.week_of_year[week_index] }); }) return objects; } We can then plug this into Vega-Lite to draw us a graph: vegalite({ data: {values: objects_to_plot}, mark: ""bar"", encoding: { x: {field: ""week"", type: ""nominal"", sort: null}, y: {field: ""observations"", type: ""quantitative""} }, width: width * 0.9 }) It’s worth noting that we have a lot of observations of Nudibranchs particularly at Pillar Point due in no small part to the intertidal monitoring research that Alison Young and Rebecca Johnson facilitate for the California Achademy of Sciences. So, what if we want to look for the seasonality of observations of a particular species of adorable sea slug? We want our interface to have a select box with a list of all the species you might find at any time of year. We can do this using the species_counts API to create us an object with the iNaturalist species ID and common & Latin names. pillar_point_nudibranches = { let api_results = await fetch( ""https://api.inaturalist.org/v1/observations/species_counts?taxon_id=47113&nelat=37.499811&nelng=-122.492738&swlat=37.492805&swlng=-122.50542&date_field=observed&verifiable=true"" ).then(r => r.json()) let species_list = api_results.results.map(i => ({ value: i.taxon.id, label: `${i.taxon.preferred_common_name} (${i.taxon.name})` })); return species_list } We can create an interactive select box by importing code from Jeremy Ashkanas’ Observable Notebook: add import {select} from ""@jashkenas/inputs"" to a cell anywhere in our notebook. Observable is magic: like a spreadsheet, the order of the cells doesn’t matter - if one cell is referenced by any other cell then when that cell updates all the other cells refresh themselves. You can also import and reference one notebook from another! viewof select_species = select({ title: ""Which Nudibranch do you want to see seasonality for?"", options: [{value: """", label: ""All the Nudibranchs!""}, ...pillar_point_nudibranches], value: """" }) Then we go back to our old favourite, the histogram API just like before, only this time we are calling it with the value created by our select box ${select_species} as taxon_id instead of the number 47113. pillar_point_counts_per_month_per_species = fetch( `https://api.inaturalist.org/v1/observations/histogram?taxon_id=${select_species}&nelat=37.499811&nelng=-122.492738&swlat=37.492805&swlng=-122.50542&date_field=observed&interval=month_of_year&verifiable=true` ).then(r => r.json()) Now for the fun graph bit! As we did before, we re-format the result of the API into a format compatible with Vega-Lite: objects_to_plot_species_month = { let objects = []; Object.keys(pillar_point_counts_per_month_per_species.results.month_of_year).map(function(month_index) { objects.push({ month: (new Date(2018, (month_index - 1), 1)).toLocaleString(""en"", {month: ""long""}), observations: pillar_point_counts_per_month_per_species.results.month_of_year[month_index] }); }) return objects; } (Note that in the above code we are creating a date object with our specific month in, and using toLocalString() to get the longer English name for the month. Because the JavaScript Date object counts January as 0, we use month_index -1 to get the correct month) And we draw the graph as we did before, only now if you interact with the select box in Observable the graph will dynamically update! vegalite({ data: {values: objects_to_plot_species_month}, mark: ""bar"", encoding: { x: {field: ""month"", type: ""nominal"", sort:null}, y: {field: ""observations"", type: ""quantitative""} }, width: width * 0.9 }) Now we can see when is the best time of year to plan to go tidepooling in Pillar Point if we want to find a specific species of Nudibranch. ​ This tool is great for planning when we to go rockpooling at Pillar Point, but what about if you are going this month and want to pre-train your eye with what to look for in order to impress your friends with your knowledge of Nudibranchs? Well… we can create ourselves a dynamic guide that you can with a list of the species, their photo, name and how many times they have been observed in that month of the year! Our select box this time looks as follows, simpler than before but assigning the month value to the variable selected_month. viewof selected_month = select({ title: ""When do you want to see Nudibranchs?"", options: [ { label: ""Whenever"", value: """" }, { label: ""January"", value: ""1"" }, { label: ""February"", value: ""2"" }, { label: ""March"", value: ""3"" }, { label: ""April"", value: ""4"" }, { label: ""May"", value: ""5"" }, { label: ""June"", value: ""6"" }, { label: ""July"", value: ""7"" }, { label: ""August"", value: ""8"" }, { label: ""September"", value: ""9"" }, { label: ""October"", value: ""10"" }, { label: ""November"", value: ""11"" }, { label: ""December"", value: ""12"" }, ], value: """" }) We then can use the species_counts API to get all the relevant information about which species we can see in month=${selected_month}. We’ll be able to reference this response object and its values later with the variable we just created, eg: all_species_data.results[0].taxon.name. all_species_data = fetch( `https://api.inaturalist.org/v1/observations/species_counts?taxon_id=47113&month=${selected_month}&nelat=37.499811&nelng=-122.492738&swlat=37.492805&swlng=-122.50542&verifiable=true` ).then(r => r.json()) You can render HTML directly in a notebook cell using Observable’s html tagged template literal:

If you go to Pillar Point ${ {"""": """", ""1"":""in January"", ""2"":""in Febrary"", ""3"":""in March"", ""4"":""in April"", ""5"":""in May"", ""6"":""in June"", ""7"":""in July"", ""8"":""in August"", ""9"":""in September"", ""10"":""in October"", ""11"":""in November"", ""12"":""in December"", }[selected_month] } you might see…

${all_species_data.results.map(s => `

${s.taxon.name}

Seen ${s.count} times

`)}
These few lines of HTML are all you need to get this exciting dynamic guide to what Nudibranchs you will see in each month! ​ Play with it yourself in this Observable Notebook. Conclusion I hope by playing with these examples you have an idea of how powerful it can be to prototype using Observable Notebooks and how you can use the incredible crowdsourced community data and APIs from iNaturalist to augment your naturalist skills and impress your friends with your new ‘knowledge of nature’ superpower. Lastly I strongly encourage you to get outside on a low tide to explore your local rocky intertidal habitat, and all the amazing critters that live there. Here is a great introduction video to tidepooling / rockpooling, by Rebecca Johnson and Alison Young from the California Academy of Sciences.",2018,Natalie Downe,nataliedowne,2018-12-18T00:00:00+00:00,https://24ways.org/2018/observable-notebooks-and-inaturalist/,code 89,"Direction, Distance and Destinations","With all these new smartphones in the hands of lost and confused owners, we need a better way to represent distances and directions to destinations. The immediate examples that jump to mind are augmented reality apps which let you see another world through your phone’s camera. While this is interesting, there is a simpler way: letting people know how far away they are and if they are getting warmer or colder. In the app world, you can easily tap into the phone’s array of sensors such as the GPS and compass, but what people rarely know is that you can do the same with HTML. The native versus web app debate will never subside, but at least we can show you how to replicate some of the functionality progressively in HTML and JavaScript. In this tutorial, we’ll walk through how to create a simple webpage listing distances and directions of a few popular locations around the world. We’ll use JavaScript to access the device’s geolocation API and also attempt to access the compass to get a heading. Both of these APIs are documented, to be included in the W3C geolocation API specification, and can be used on both desktop and mobile devices today. To get started, we need a list of a few locations around the world. I have chosen the highest mountain peak on each continent so you can see a diverse set of distances and directions. Mountain °Latitude °Longitude Kilimanjaro -3.075833 37.353333 Vinson Massif -78.525483 -85.617147 Puncak Jaya -4.078889 137.158333 Everest 27.988056 86.925278 Elbrus 43.355 42.439167 Mount McKinley 63.0695 -151.0074 Aconcagua -32.653431 -70.011083 Source: Wikipedia We can put those into an HTML list to be styled and accessed by JavaScript to create some distance and directions calculations. The next thing we need to do is check to see if the browser and operating system have geolocation support. To do this we test to see if the function is available or not using a single JavaScript if statement. The if statement will be false if geolocation support is not present, and then it is up to you to do something else instead as a fallback. For this example, we’ll do nothing since our page should work as is and only get progressively better if more functionality is available. The if statement will be true if there is support and therefore will continue inside the curly brackets to try to get the location. This should prompt the reader to accept or deny the request to get their location. If they say no, the second function callback is processed, in this case a function called geo_error; whereas if the location is available, it fires the geo_success function callback. The function geo_error(){ } isn’t that exciting. You can handle this in any way you see fit. The success function is more interesting. We get a position object passed into the function which contains a series of exciting attributes, namely the latitude and longitude of the device’s current location. function geo_success(position){ gLat = position.coords.latitude; gLon = position.coords.longitude; } Now, in the variables gLat and gLon we have the user’s approximate geographical position. We can use this information to start to calculate some distances between where they are and all the destinations. At the time of writing, you can also get position.coords.heading, but on Windows and iOS devices this returned NULL. In the future, if and when this is supported, this is also where you can easily grab the compass information. Inside the geo_success function, we want to loop through the HTML to get all of the mountain peaks’ latitudes and longitudes and compute the distance. ... $('.geo').each(function(){ // Get the lat/lon from the HTML tLat = $(this).find('.lat').html() tLon = $(this).find('.lon').html() // compute the distances between the current location and this points location dist = distance(tLat,tLon,gLat,gLon); // set the return values into something useful d = parseInt(dist[0]*10)/10; a = parseFloat(dist[1]); // display the value in the HTML and style the arrow $(this).find('.distance').html(d+' km away'); $(this).find('.direction').css('-webkit-transform','rotate(-' + a + 'deg)'); // store the arc for later use if compass is available $(this).attr('data-arc',a); } In the variable d we have the distance between the current location and the location of the mountain peak based on the Haversine Formula. The variable a is the arc, which has a value from 0 to 359.99. This will be useful later if we have compass support. Given these two values we have a distance and a heading to style the HTML. The next thing we want to do is check to see if the device has a compass and then get access to the the current heading. As we’ll see, there are several ways to do this, some of which work on certain devices but not others. The W3C geolocation spec says that, along with the coordinates, there are several other attributes: accuracy; altitude; and heading. Heading is the direction to true north, which is different than magnetic north! WebKit and Windows return NULL for the heading value, but WebKit has an experimental method to fetch the heading. If you get into accessing these sensors, you’ll have to try to catch a few of these methods to finally get a value. Assuming you do, we can move on to the more interesting display opportunities. In an ideal world, this would succeed and set a variable we’ll call compassHeading to get a value between 0 and 359.99 degrees. Now we know which direction north is, we also know the direction relative to north of the path to our destination, so we can can subtract the two values to get an arrow to display on the screen. But we’re not finished yet: we also need to get the device’s orientation (landscape or portrait) and subtract the correct amount from the angle for the arrow. Once we have a value, we can use CSS to rotate the arrow the correct number of degrees. -webkit-transform: rotate(-180deg) Not all devices support a standard way to access compass information, so in the meantime we need to use a work around. On iOS, you can use the experimental event method e.webkitCompassHeading. We want the compass to update in real time as the device is moved around, so we’ll put this inside an event listener. window.addEventListener('deviceorientation', function(e) { // Loop through all the locations on the page $('.geo').each(function(){ // get the arc value from north we computed and stored earlier destination_arc = parseInt($(this).attr('data-arc')) compassHeading = e.webkitCompassHeading + window.orientation + destination_arc; // find the arrow element and rotate it accordingly $(this).find('.direction').css('-webkit-transform','rotate(-' + compassHeading + 'deg)'); } } As the device is rotated, the compass arrow will constantly be updated. If you want to see an example, you can have a look at this page which shows the distances to all the peaks on each continent. With progressive enhancement, we slowly layer on additional functionality as we go. The reader will first see the list of locations with a latitude and longitude. If the device is capable and permissions allow, it will then compute the distance. If a compass is available, with the correct permissions it will then add the final layer which is direction. You should consider this code a stub for your projects. If you are making a hyperlocal webpage with restaurant locations, for example, then consider adding these features. Knowing not only how far away a place is, but also the direction can be hugely important, and since the compass is always active, it acts as a guide to the location. Future developments Improvements to this could include setting a timer and recalling the navigator.geolocation.getCurrentPosition() function and updating the distances. I chose very distant mountains so kilometres made sense, but you can divide again by 1,000 to convert to metres if you are dealing with much nearer places. Walking or driving would change the distances so the ability to refresh would be important. It is outside the scope of this article, but if you manage to get this HTML to work offline, then you can make a nice web app which sits on your devices’ homescreens and works even without an internet connection. This could be ideal for travellers in an unknown city looking for your destination. Just with offline storage, base64 encoding and data URIs, it is possible to embed plenty of design and functionality into a small offline webpage. Now you know how to use JavaScript to look up a destination’s location and figure out the distance and direction – never get lost again.",2012,Brian Suda,briansuda,2012-12-19T00:00:00+00:00,https://24ways.org/2012/direction-distance-and-destinations/,code 288,Displaying Icons with Fonts and Data- Attributes,"Traditionally, bitmap formats such as PNG have been the standard way of delivering iconography on websites. They’re quick and easy, and it also ensures they’re as pixel crisp as possible. Bitmaps have two drawbacks, however: multiple HTTP requests, affecting the page’s loading performance; and a lack of scalability, noticeable when the page is zoomed or viewed on a screen with a high pixel density, such as the iPhone 4 and 4S. The requests problem is normally solved by using CSS sprites, combining the icon set into one (physically) large image file and showing the relevant portion via background-position. While this works well, it can get a bit fiddly to specify all the positions. In particular, scalability is still an issue. A vector-based format such as SVG sounds ideal to solve this, but browser support is still patchy. The rise and adoption of web fonts have given us another alternative. By their very nature, they’re not only scalable, but resolution-independent too. No need to specify higher resolution graphics for high resolution screens! That’s not all though: Browser support: Unlike a lot of new shiny techniques, they have been supported by Internet Explorer since version 4, and, of course, by all modern browsers. We do need several different formats, however! Design on the fly: The font contains the basic graphic, which can then be coloured easily with CSS – changing colours for themes or :hover and :focus styles is done with one line of CSS, rather than requiring a new graphic. You can also use CSS3 properties such as text-shadow to add further effects. Using -webkit-background-clip: text;, it’s possible to use gradient and inset shadow effects, although this creates a bitmap mask which spoils the scalability. Small file size: specially designed icon fonts, such as Drew Wilson’s Pictos font, can be as little as 12Kb for the .woff font. This is because they contain fewer characters than a fully fledged font. You can see Pictos being used in the wild on sites like Garrett Murray’s Maniacal Rage. As with all formats though, it’s not without its disadvantages: Icons can only be rendered in monochrome or with a gradient fill in browsers that are capable of rendering CSS3 gradients. Specific parts of the icon can’t be a different colour. It’s only appropriate when there is an accompanying text to provide meaning. This can be alleviated by wrapping the text label in a tag (I like to use rather than , due to the fact that it’s smaller and isn’ t being used elsewhere) and then hiding it from view with text-indent:-999em. Creating an icon font can be a complex and time-consuming process. While font editors can carry out hinting automatically, the best results are achieved manually. Unless you’re adept at creating your own fonts, you’re restricted to what is available in the font. However, fonts like Pictos will cover the most common needs, and icons are most effective when they’re using familiar conventions. The main complaint about using fonts for icons is that it can mean adding a meaningless character to our markup. The good news is that we can overcome this by using one of two methods – CSS generated content or the data-icon attribute – in combination with the :before and :after pseudo-selectors, to keep our markup minimal and meaningful. Our simple markup looks like this: View Basket Note the multiple class attributes. Next, we’ll import the Pictos font using the @font-face web fonts property in CSS: @font-face { font-family: 'Pictos'; src: url('pictos-web.eot'); src: local('☺'), url('pictos-web.woff') format('woff'), url('pictos-web.ttf') format('truetype'), url('pictos-web.svg#webfontIyfZbseF') format('svg'); } This rather complicated looking set of rules is (at the time of writing) the most bulletproof way of ensuring as many browsers as possible load the font we want. We’ll now use the content property applied to the :before pseudo-class selector to generate our icon. Once again, we’ll use those multiple class attribute values to set common icon styles, then specific styles for .basket. This helps us avoid repeating styles: .icon { font-family: 'Pictos'; font-size: 22px: } .basket:before { content: ""$""; } What does the :before pseudo-class do? It generates the dollar character in a browser, even when it’s not present in the markup. Using the generated content approach means our markup stays simple, but we’ll need a new line of CSS, defining what letter to apply to each class attribute for every icon we add. data-icon is a new alternative approach that uses the HTML5 data- attribute in combination with CSS attribute selectors. This new attribute lets us add our own metadata to elements, as long as its prefixed by data- and doesn’t contain any uppercase letters. In this case, we want to use it to provide the letter value for the icon. Look closely at this markup and you’ll see the data-icon attribute. View Basket We could add others, in fact as many as we like. Favourites History Location Then, we need just one CSS attribute selector to style all our icons in one go: .icon:before { content: attr(data-icon); /* Insert your fancy colours here */ } By placing our custom attribute data-icon in the selector in this way, we can enable CSS to read the value of that attribute and display it before the element (in this case, the anchor tag). It saves writing a lot of CSS rules. I can imagine that some may not like the extra attribute, but it does keep it out of the actual content – generated or not. This could be used for all manner of tasks, including a media player and large simple illustrations. See the demo for live examples. Go ahead and zoom the page, and the icons will be crisp, with the exception of the examples that use -webkit-background-clip: text as mentioned earlier. Finally, it’s worth pointing out that with both generated content and the data-icon method, the letter will be announced to people using screen readers. For example, with the shopping basket icon above, the reader will say “dollar sign view basket”. As accessibility issues go, it’s not exactly the worst, but could be confusing. You would need to decide whether this method is appropriate for the audience. Despite the disadvantages, icon fonts have huge potential.",2011,Jon Hicks,jonhicks,2011-12-12T00:00:00+00:00,https://24ways.org/2011/displaying-icons-with-fonts-and-data-attributes/,code 188,Don't Lose Your :focus,"For many web designers, accessibility conjures up images of blind users with screenreaders, and the difficulties in making sites accessible to this particular audience. Of course, accessibility covers a wide range of situations that go beyond the extreme example of screenreader users. And while it’s true that making a complex site accessible can often be a daunting prospect, there are also many small things that don’t take anything more than a bit of judicious planning, are very easy to test (without having to buy expensive assistive technology), and can make all the difference to certain user groups. In this short article we’ll focus on keyboard accessibility and how careless use of CSS can potentially make your sites completely unusable. Keyboard Access Users who for whatever reason can’t use a mouse will employ a keyboard (or keyboard-like custom interface) to navigate around web pages. By default, they will use TAB and SHIFT + TAB to move from one focusable element (links, form controls and area) of a page to the next. Note: in OS X, you’ll first need to turn on full keyboard access under System Preferences > Keyboard and Mouse > Keyboard Shortcuts. Safari under Windows needs to have the option Press Tab to highlight each item on a webpage in Preferences > Advanced enabled. Opera is the odd one out, as it has a variety of keyboard navigation options – the most relevant here being spatial navigation via Shift+Down, Shift+Up, Shift+Left, and Shift+Right). But I Don’t Like Your Dotted Lines… To show users where they are within a page, browsers place an outline around the element that currently has focus. The “problem” with these default outlines is that some browsers (Internet Explorer and Firefox) also display them when a user clicks on a focusable element with the mouse. Particularly on sites that make extensive use of image replacement on links with “off left” techniques this can create very unsightly outlines that stretch from the replaced element all the way to the left edge of the browser. Outline bleeding off to the left (image-replacement example from carsonified.com) There is a trivial workaround to prevent outlines from “spilling over” by adding a simple overflow:hidden, which keeps the outline in check around the clickable portion of the image-replaced element itself. Outline tamed with overflow:hidden But for many designers, even this is not enough. As a final solution, many actively suppress outlines altogether in their stylesheets. Controversially, even Eric Meyer’s popular reset.css – an otherwise excellent set of styles that levels the playing field of varying browser defaults – suppresses outlines. html, body, div, span, applet, object, iframe ... { ... outline: 0; ... } /* remember to define focus styles! */ :focus { outline: 0; } Yes, in his explanation (and in the CSS itself) Eric does remind designers to define relevant styles for :focus… but judging by the number of sites that seem to ignore this (and often remove the related comment from the stylesheet altogether), the message doesn’t seem to have sunk in. Anyway… hurrah! No more unsightly dotted lines on our lovely design. But what about keyboard users? Although technically they can still TAB from one element to the next, they now get no default cue as to where they are within the page (one notable exception here is Opera, where the outline is displayed regardless of stylesheets)… and if they’re Safari users, they won’t even get an indication of a link’s target in the status bar, like they would if they hovered over it with the mouse. Only Suppress outline For Mouse Users Is there a way to allow users navigating with the keyboard to retain the standard outline behaviour they’ve come to expect from their browser, while also ensuring that it doesn’t show display for mouse users? Testing some convoluted style combinations After playing with various approaches (see Better CSS outline suppression for more details), the most elegant solution also seemed to be the simplest: don’t remove the outline on :focus, do it on :active instead – after all, :active is the dynamic pseudo-class that deals explicitly with the styles that should be applied when a focusable element is clicked or otherwise activated. a:active { outline: none; } The only minor issues with this method: if a user activates a link and then uses the browser’s back button, the outline becomes visible. Oh, and old versions of Internet Explorer notoriously get confused by the exact meaning of :focus, :hover and :active, so this method fails in IE6 and below. Personally, I can live with both of these. Note: at the last minute before submitting this article, I discovered a fatal flaw in my test. It appears that outline still manages to appear in the time between activating a link and the link target loading (which in hindsight is logical – after activation, the link does indeed receive focus). As my test page only used in-page links, this issue never came up before. The slightly less elegant solution is to also suppress the outline on :hover. a:hover, a:active { outline: none; } In Conclusion Of course, many web designers may argue that they know what’s best, even for their keyboard-using audience. Maybe they’ve removed the default outline and are instead providing some carefully designed :focus styles. If they know for sure that these custom styles are indeed a reliable alternative for their users, more power to them… but, at the risk of sounding like Jakob “blue underlined links” Nielsen, I’d still argue that sometimes the default browser behaviours are best left alone. Complemented, yes (and if you’re already defining some fancy styles for :hover, by all means feel free to also make them display on :focus)… but not suppressed.",2009,Patrick Lauke,patricklauke,2009-12-09T00:00:00+00:00,https://24ways.org/2009/dont-lose-your-focus/,code 326,Don't be eval(),"JavaScript is an interpreted language, and like so many of its peers it includes the all powerful eval() function. eval() takes a string and executes it as if it were regular JavaScript code. It’s incredibly powerful and incredibly easy to abuse in ways that make your code slower and harder to maintain. As a general rule, if you’re using eval() there’s probably something wrong with your design. Common mistakes Here’s the classic misuse of eval(). You have a JavaScript object, foo, and you want to access a property on it – but you don’t know the name of the property until runtime. Here’s how NOT to do it: var property = 'bar'; var value = eval('foo.' + property); Yes it will work, but every time that piece of code runs JavaScript will have to kick back in to interpreter mode, slowing down your app. It’s also dirt ugly. Here’s the right way of doing the above: var property = 'bar'; var value = foo[property]; In JavaScript, square brackets act as an alternative to lookups using a dot. The only difference is that square bracket syntax expects a string. Security issues In any programming language you should be extremely cautious of executing code from an untrusted source. The same is true for JavaScript – you should be extremely cautious of running eval() against any code that may have been tampered with – for example, strings taken from the page query string. Executing untrusted code can leave you vulnerable to cross-site scripting attacks. What’s it good for? Some programmers say that eval() is B.A.D. – Broken As Designed – and should be removed from the language. However, there are some places in which it can dramatically simplify your code. A great example is for use with XMLHttpRequest, a component of the set of tools more popularly known as Ajax. XMLHttpRequest lets you make a call back to the server from JavaScript without refreshing the whole page. A simple way of using this is to have the server return JavaScript code which is then passed to eval(). Here is a simple function for doing exactly that – it takes the URL to some JavaScript code (or a server-side script that produces JavaScript) and loads and executes that code using XMLHttpRequest and eval(). function evalRequest(url) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { eval(xmlhttp.responseText); } } xmlhttp.open(""GET"", url, true); xmlhttp.send(null); } If you want this to work with Internet Explorer you’ll need to include this compatibility patch.",2005,Simon Willison,simonwillison,2005-12-07T00:00:00+00:00,https://24ways.org/2005/dont-be-eval/,code 314,Easy Ajax with Prototype,"There’s 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. But it’s 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’s just so much damn effort. Well, the good news is that – ta-da – it doesn’t have to be a headache. But man does it still look impressive. Here’s how to amaze your friends. Introducing prototype.js Prototype is a JavaScript framework by Sam Stephenson designed to help make developing dynamic web apps a whole lot easier. In basic terms, it’s a JavaScript file which you link into your page that then enables you to do cool stuff. There’s 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’s good to go. What a nice man that Mr Stephenson is – friends, let us raise a hearty cup of mulled wine to his good name. Cheers! sluurrrrp. First step is to download the latest Prototype and put it somewhere safe. I suggest underneath the Christmas tree. Cutting to the chase Before I go on and set up an example of how to use this, let’s just get to the crux. Here’s how Prototype enables you to make a simple Ajax call and dump the results back to the page: var url = 'myscript.php'; var pars = 'foo=bar'; var target = 'output-div'; var myAjax = new Ajax.Updater(target, url, {method: 'get', parameters: pars}); This 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. Knocking up a basic example So 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. So, to that basic HTML page for the user to interact with. Here’s one I found whilst out carol singing: Easy Ajax
As you can see, I’ve linked in prototype.js, and also a file called ajax.js, which is where we’ll be putting our glue. (Careful where you leave your glue, kids.) Our basic example is just going to take a name and then echo it back in the form of a seasonal greeting. There’s a form with an input field for a name, and crucially a DIV (greeting) for the result of our call. You’ll also notice that the form has a submit button – this is so that it can function as a regular form when no JavaScript is available. It’s important not to get carried away and forget the basics of accessibility. Meanwhile, back at the server So 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’d 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. Here’s a quick PHP script – greeting.php – that Santa brought me early. Season's Greetings, $the_name!

""; ?> You’ll perhaps want to do something a little more complex within your own projects. Just sayin’. Gluing it all together Inside our ajax.js file, we need to hook this all together. We’re 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’s how we attach an onload event to the window object and get it to call a function named init(): Event.observe(window, 'load', init, false); Now 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. function init(){ $('greeting-submit').style.display = 'none'; Event.observe('greeting-name', 'keyup', greet, false); } As 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: function greet(){ var url = 'greeting.php'; var pars = 'greeting-name='+escape($F('greeting-name')); var target = 'greeting'; var myAjax = new Ajax.Updater(target, url, {method: 'get', parameters: pars}); } The key points to note here are that any user input needs to be escaped before putting into the parameters so that it’s 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. That’s it No, seriously. That’s 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’t go that far towards implementing something really practical. We dipped our toes in, but haven’t learned to swim yet. So here is swimming lesson number one. Anyone who’s 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. Prototype 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’ll need here) and a whole bunch of manipulations to the user interface – all through simple library calls. Here’s what we’re building, so let’s do it. Getting Started There 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’ve not already done so). We have a basic HTML page which links in the prototype.js file and our own editinplace.js. Here’s what Santa dropped down my chimney: Edit-in-Place with Ajax

Edit-in-place

Dashing through the snow on a one horse open sleigh.

So that’s our page. The editable item is going to be the

called desc. The process goes something like this: Highlight the area onMouseOver Clear the highlight onMouseOut If the user clicks, hide the area and replace with a '; var button = ' OR '; new Insertion.After(obj, textarea+button); Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false); Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false); } The 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. The 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. In the event of a cancel, we can clean up behind ourselves like so: function cleanUp(obj, keepEditable){ Element.remove(obj.id+'_editor'); Element.show(obj); if (!keepEditable) showAsEditable(obj, true); } Saving the Changes This 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. function saveChanges(obj){ var new_content = escape($F(obj.id+'_edit')); obj.innerHTML = ""Saving...""; cleanUp(obj, true); var success = function(t){editComplete(t, obj);} var failure = function(t){editFailed(t, obj);} var url = 'edit.php'; var pars = 'id=' + obj.id + '&content=' + new_content; var myAjax = new Ajax.Request(url, {method:'post', postBody:pars, onSuccess:success, onFailure:failure}); } function editComplete(t, obj){ obj.innerHTML = t.responseText; showAsEditable(obj, true); } function editFailed(t, obj){ obj.innerHTML = 'Sorry, the update failed.'; cleanUp(obj); } As 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 “Saving…” to show that an update is occurring, and make the Ajax POST. If the Ajax fails, editFailed() sets the contents of the object to “Sorry, the update failed.” Admittedly, that’s 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 “Saving…”. No one likes a failure – especially a messy one. If 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. Meanwhile, back at the server The 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’s what I have in PHP. Not exactly rocket science is it? I’m just catching the content item from the POST and echoing it back. For your application to be useful, however, you’ll need to know exactly which record you should be updating. I’m passing in the ID of my

, 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. You should also check the user’s credentials to make sure they have permission to edit whatever it is they’re editing. Basically the same rules apply as with any script in your application. Limitations There are a few bits and bobs that in an ideal world I would tidy up. The first is the error handling, as I’ve already mentioned. The second is that from an idealistic standpoint, I’d rather not be using innerHTML. However, the reality is that it’s presently the most efficient way of making large changes to the document. If you’re serving as XML, remember that you’ll need to replace these with proper DOM nodes. It’s also important to note that it’s 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’t cope well. The benefit of this technique, though, is that if JavaScript is unavailable none of the functionality gets implemented at all – it fails silently. It is for this reason that this shouldn’t be used as a complete replacement for a traditional, universally accessible edit form. It’s a great time-saver for those with the ability to use it, but it’s no replacement. See it in action I’ve put together an example page using the inert PHP script above. That is to say, your edits aren’t 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 238,Everything You Wanted To Know About Gradients (And a Few Things You Didn’t),"Hello. I am here to discuss CSS3 gradients. Because, let’s face it, what the web really needed was more gradients. Still, despite their widespread use (or is it overuse?), the smartly applied gradient can be a valuable contributor to a designer’s vocabulary. There’s always been a tension between the inherently two-dimensional nature of our medium, and our desire for more intensity, more depth in our designs. And a gradient can evoke so much: the splay of light across your desk, the slow decrease in volume toward the end of your favorite song, the sunset after a long day. When properly applied, graded colors bring a much needed softness to our work. Of course, that whole ‘proper application’ thing is the tricky bit. But given their place in our toolkit and their prominence online, it really is heartening to see we can create gradients directly with CSS. They’re part of the draft images module, and implemented in two of the major rendering engines. Still, I’ve always found CSS gradients to be one of the more confusing aspects of CSS3. So if you’ll indulge me, let’s take a quick look at how to create CSS gradients—hopefully we can make them seem a bit more accessible, and bring a bit more art into the browser. Gradient theory 101 (I hope that’s not really a thing) Right. So before we dive into the code, let’s cover a few basics. Every gradient, no matter how complex, shares a few common characteristics. Here’s a straightforward one: I spent seconds hours designing this gradient. I hope you like it. At either end of our image, we have a final color value, or color stop: on the left, our stop is white; on the right, black. And more color-rich gradients are no different: (Don’t ever really do this. Please. I beg you.) It’s visually more intricate, sure. But at the heart of it, we have just seven color stops (red, orange, yellow, and so on), making for a fantastic gradient all the way. Now, color stops alone do not a gradient make. Between each is a transition point, the fail-over point between the two stops. Now, the transition point doesn’t need to fall exactly between stops: it can be brought closer to one stop or the other, influencing the overall shape of the gradient. A tale of two syntaxes Armed with our new vocabulary, let’s look at a CSS gradient in the wild. Behold, the simple input button: There’s a simple linear gradient applied vertically across the button, moving from a bright sunflowerish hue (#FAA51A, for you hex nuts in the audience) to a much richer orange (#F47A20). And here’s the CSS that makes it happen: input[type=submit] { background-color: #F47A20; background-image: -moz-linear-gradient( #FAA51A, #F47A20 ); background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #FAA51A), color-stop(1, #F47A20) ); } I’ve borrowed David DeSandro’s most excellent formatting suggestions for gradients to make this snippet a bit more legible but, still, the code above might have turned your stomach a bit. And that’s perfectly understandable—heck, it sort of turned mine. But let’s step through the CSS slowly, and see if we can’t make it a little less terrifying. Verbose WebKit is verbose Here’s the syntax for our little gradient on WebKit: background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #FAA51A), color-stop(1, #F47A20) ); Woof. Quite a mouthful, no? Well, here’s what we’re looking at: WebKit has a single -webkit-gradient property, which can be used to create either linear or radial gradients. The next two values are the starting and ending positions for our gradient (0 0 and 0 100%, respectively). Linear gradients are simply drawn along the path between those two points, which allows us to change the direction of our gradient simply by altering its start and end points. Afterward, we specify our color stops with the oh-so-aptly named color-stop parameter, which takes the stop’s position on the gradient (0 being the beginning, and 100% or 1 being the end) and the color itself. For a simple two-color gradient like this, -webkit-gradient has a bit of shorthand notation to offer us: background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#FAA51A), to(#FAA51A) ); from(#FAA51A) is equivalent to writing color-stop(0, #FAA51A), and to(#FAA51A) is the same as color-stop(1, #FAA51A) or color-stop(100%, #FAA51A)—in both cases, we’re simply declaring the first and last color stops in our gradient. Terse Gecko is terse WebKit proposed its syntax back in 2008, heavily inspired by the way gradients are drawn in the canvas specification. However, a different, leaner syntax came to the fore, eventually appearing in a draft module specification in CSS3. Naturally, because nothing on the web was meant to be easy, this is the one that Mozilla has implemented. Here’s how we get gradient-y in Gecko: background-image: -moz-linear-gradient( #FAA51A, #F47A20 ); Wait, what? Done already? That’s right. By default, -moz-linear-gradient assumes you’re trying to create a vertical gradient, starting from the top of your element and moving to the bottom. And, if that’s the case, then you simply need to specify your color stops, delimited with a few commas. I know: that was almost… painless. But the W3C/Mozilla syntax also affords us a fair amount of flexibility and control, by introducing features as we need them. We can specify an origin point for our gradient: background-image: -moz-linear-gradient(50% 100%, #FAA51A, #F47A20 ); As well as an angle, to give it a direction: background-image: -moz-linear-gradient(50% 100%, 45deg, #FAA51A, #F47A20 ); And we can specify multiple stops, simply by adding to our comma-delimited list: background-image: -moz-linear-gradient(50% 100%, 45deg, #FAA51A, #FCC, #F47A20 ); By adding a percentage after a given color value, we can determine its position along the gradient path: background-image: -moz-linear-gradient(50% 100%, 45deg, #FAA51A, #FCC 20%, #F47A20 ); So that’s some of the flexibility implicit in the W3C/Mozilla-style syntax. Now, I should note that both syntaxes have their respective fans. I will say that the W3C/Mozilla-style syntax makes much more sense to me, and lines up with how I think about creating gradients. But I can totally understand why some might prefer WebKit’s more verbose approach to the, well, looseness behind the -moz syntax. À chacun son gradient syntax. Still, as the language gets refined by the W3C, I really hope some consensus is reached by the browser vendors. And with Opera signaling that it will support the W3C syntax, I suppose it falls on WebKit to do the same. Reusing color stops for fun and profit But CSS gradients aren’t all simple colors and shapes and whatnot: by getting inventive with individual color stops, you can create some really complex, compelling effects. Tim Van Damme, whose brain, I believe, should be posthumously donated to science, has a particularly clever application of gradients on The Box, a site dedicated to his occasional podcast series. Now, there are a fair number of gradients applied throughout the UI, but it’s the feature image that really catches the eye. You see, there’s nothing that says you can’t reuse color stops. And Tim’s exploited that perfectly. He’s created a linear gradient, angled at forty-five degrees from the top left corner of the photo, starting with a fully transparent white (rgba(255, 255, 255, 0)). At the halfway mark, he’s established another color stop at an only slightly more opaque white (rgba(255, 255, 255, 0.1)), making for that incredibly gradual brightening toward the middle of the photo. But then he has set another color stop immediately on top of it, bringing it back down to rgba(255, 255, 255, 0) again. This creates that fantastically hard edge that diagonally bisects the photo, giving the image that subtle gloss. And his final color stop ends at the same fully transparent white, completing the effect. Hot? I do believe so. Rocking the radials We’ve been looking at linear gradients pretty exclusively. But I’d be remiss if I didn’t at least mention radial gradients as a viable option, including a modest one as a link accent on a navigation bar: And here’s the relevant CSS: background: -moz-radial-gradient(50% 100%, farthest-side, rgb(204, 255, 255) 1%, rgb(85, 85, 85) 15%, rgba(85, 85, 85, 0) ); background: -webkit-gradient(radial, 50% 100%, 0, 50% 100%, 15, from(rgb(204, 255, 255)), to(rgba(85, 85, 85, 0)) ); Now, the syntax builds on what we’ve already learned about linear gradients, so much of it might be familiar to you, picking out color stops and transition points, as well as the two syntaxes’ reliance on either a separate property (-moz-radial-gradient) or parameter (-webkit-gradient(radial, …)) to shift into circular mode. Mozilla introduces another stand-alone property (-moz-radial-gradient), and accepts a starting point (50% 100%) from which the circle radiates. There’s also a size constant defined (farthest-side), which determines the reach and shape of our gradient. WebKit is again the more verbose of the two syntaxes, requiring both starting and ending points (50% 100% in both cases). Each also accepts a radius in pixels, allowing you to control the skew and breadth of the circle. Again, this is a fairly modest little radial gradient. Time and article length (and, let’s be honest, your author’s completely inadequate grasp of geometry) prevent me from covering radial gradients in much more detail, because they are incredibly powerful. For those interested in learning more, I can’t recommend the references at Mozilla and Apple strongly enough. Leave no browser behind But no matter the kind of gradients you’re working with, there is a large swathe of browsers that simply don’t support gradients. Thankfully, it’s fairly easy to declare a sensible fallback—it just depends on the kind of fallback you’d like. Essentially, gradient-blind browsers will disregard any properties containing references to either -moz-linear-gradient, -moz-radial-gradient, or -webkit-gradient, so you simply need to keep your fallback isolated from those properties. For example: if you’d like to fall back to a flat color, simply declare a separate background-color: .nav { background-color: #000; background-image: -moz-linear-gradient(rgba(0, 0, 0, 0), rgba(255, 255, 255, 0.45)); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(rgba(0, 0, 0, 0)), to(rgba(255, 255, 255, 0.45))); } Or perhaps just create three separate background properties. .nav { background: #000; background: #000 -moz-linear-gradient(rgba(0, 0, 0, 0), rgba(255, 255, 255, 0.45)); background: #000 -webkit-gradient(linear, 0 0, 0 100%, from(rgba(0, 0, 0, 0)), to(rgba(255, 255, 255, 0.45))); } We can even build on this to fall back to a non-gradient image: .nav { background: #000 url(""faux-gradient-lol.png"") repeat-x; background: #000 -moz-linear-gradient(rgba(0, 0, 0, 0), rgba(255, 255, 255, 0.45)); background: #000 -webkit-gradient(linear, 0 0, 0 100%, from(rgba(0, 0, 0, 0)), to(rgba(255, 255, 255, 0.45))); } No matter the approach you feel most appropriate to your design, it’s really just a matter of keeping your fallback design quarantined from its CSS3-ified siblings. (If you’re feeling especially masochistic, there’s even a way to get simple linear gradients working in IE via Microsoft’s proprietary filters. Of course, those come with considerable performance penalties that even Microsoft is quick to point out, so I’d recommend avoiding those. And don’t tell Andy Clarke I told you, or he’ll probably unload his Derringer at me. Or something.) Go forth and, um, gradientify! It’s entirely possible your head’s spinning. Heck, mine is, but that might be the effects of the ’nog. But maybe you’re wondering why you should care about CSS gradients. After all, images are here right now, and work just fine. Well, there are some quick benefits that spring to mind: fewer HTTP requests are needed; CSS3 gradients are easily made scalable, making them ideal for variable widths and heights; and finally, they’re easily modifiable by tweaking a few CSS properties. Because, let’s face it, less time spent yelling at Photoshop is a very, very good thing. Of course, CSS-generated gradients are not without their drawbacks. The syntax can be confusing, and it’s still under development at the W3C. As we’ve seen, browser support is still very much in flux. And it’s possible that gradients themselves have some real performance drawbacks—so test thoroughly, and gradient carefully. But still, as syntaxes converge, and support improves, I think generated gradients can make a compelling tool in our collective belts. The tasteful design is, of course, entirely up to you. So have fun, and get gradientin’.",2010,Ethan Marcotte,ethanmarcotte,2010-12-22T00:00:00+00:00,https://24ways.org/2010/everything-you-wanted-to-know-about-gradients/,code 249,Fast Autocomplete Search for Your Website,"Every website deserves a great search engine - but building a search engine can be a lot of work, and hosting it can quickly get expensive. I’m going to build a search engine for 24 ways that’s fast enough to support autocomplete (a.k.a. typeahead) search queries and can be hosted for free. I’ll be using wget, Python, SQLite, Jupyter, sqlite-utils and my open source Datasette tool to build the API backend, and a few dozen lines of modern vanilla JavaScript to build the interface. Try it out here, then read on to see how I built it. First step: crawling the data The first step in building a search engine is to grab a copy of the data that you plan to make searchable. There are plenty of potential ways to do this: you might be able to pull it directly from a database, or extract it using an API. If you don’t have access to the raw data, you can imitate Google and write a crawler to extract the data that you need. I’m going to do exactly that against 24 ways: I’ll build a simple crawler using wget, a command-line tool that features a powerful “recursive” mode that’s ideal for scraping websites. We’ll start at the https://24ways.org/archives/ page, which links to an archived index for every year that 24 ways has been running. Then we’ll tell wget to recursively crawl the website, using the --recursive flag. We don’t want to fetch every single page on the site - we’re only interested in the actual articles. Luckily, 24 ways has nicely designed URLs, so we can tell wget that we only care about pages that start with one of the years it has been running, using the -I argument like this: -I /2005,/2006,/2007,/2008,/2009,/2010,/2011,/2012,/2013,/2014,/2015,/2016,/2017 We want to be polite, so let’s wait for 2 seconds between each request rather than hammering the site as fast as we can: --wait 2 The first time I ran this, I accidentally downloaded the comments pages as well. We don’t want those, so let’s exclude them from the crawl using -X ""/*/*/comments"". Finally, it’s useful to be able to run the command multiple times without downloading pages that we have already fetched. We can use the --no-clobber option for this. Tie all of those options together and we get this command: wget --recursive --wait 2 --no-clobber -I /2005,/2006,/2007,/2008,/2009,/2010,/2011,/2012,/2013,/2014,/2015,/2016,/2017 -X ""/*/*/comments"" https://24ways.org/archives/ If you leave this running for a few minutes, you’ll end up with a folder structure something like this: $ find 24ways.org 24ways.org 24ways.org/2013 24ways.org/2013/why-bother-with-accessibility 24ways.org/2013/why-bother-with-accessibility/index.html 24ways.org/2013/levelling-up 24ways.org/2013/levelling-up/index.html 24ways.org/2013/project-hubs 24ways.org/2013/project-hubs/index.html 24ways.org/2013/credits-and-recognition 24ways.org/2013/credits-and-recognition/index.html ... As a quick sanity check, let’s count the number of HTML pages we have retrieved: $ find 24ways.org | grep index.html | wc -l 328 There’s one last step! We got everything up to 2017, but we need to fetch the articles for 2018 (so far) as well. They aren’t linked in the /archives/ yet so we need to point our crawler at the site’s front page instead: wget --recursive --wait 2 --no-clobber -I /2018 -X ""/*/*/comments"" https://24ways.org/ Thanks to --no-clobber, this is safe to run every day in December to pick up any new content. We now have a folder on our computer containing an HTML file for every article that has ever been published on the site! Let’s use them to build ourselves a search index. Building a search index using SQLite There are many tools out there that can be used to build a search engine. You can use an open-source search server like Elasticsearch or Solr, a hosted option like Algolia or Amazon CloudSearch or you can tap into the built-in search features of relational databases like MySQL or PostgreSQL. I’m going to use something that’s less commonly used for web applications but makes for a powerful and extremely inexpensive alternative: SQLite. SQLite is the world’s most widely deployed database, even though many people have never even heard of it. That’s because it’s designed to be used as an embedded database: it’s commonly used by native mobile applications and even runs as part of the default set of apps on the Apple Watch! SQLite has one major limitation: unlike databases like MySQL and PostgreSQL, it isn’t really designed to handle large numbers of concurrent writes. For this reason, most people avoid it for building web applications. This doesn’t matter nearly so much if you are building a search engine for infrequently updated content - say one for a site that only publishes new content on 24 days every year. It turns out SQLite has very powerful full-text search functionality built into the core database - the FTS5 extension. I’ve been doing a lot of work with SQLite recently, and as part of that, I’ve been building a Python utility library to make building new SQLite databases as easy as possible, called sqlite-utils. It’s designed to be used within a Jupyter notebook - an enormously productive way of interacting with Python code that’s similar to the Observable notebooks Natalie described on 24 ways yesterday. If you haven’t used Jupyter before, here’s the fastest way to get up and running with it - assuming you have Python 3 installed on your machine. We can use a Python virtual environment to ensure the software we are installing doesn’t clash with any other installed packages: $ python3 -m venv ./jupyter-venv $ ./jupyter-venv/bin/pip install jupyter # ... lots of installer output # Now lets install some extra packages we will need later $ ./jupyter-venv/bin/pip install beautifulsoup4 sqlite-utils html5lib # And start the notebook web application $ ./jupyter-venv/bin/jupyter-notebook # This will open your browser to Jupyter at http://localhost:8888/ You should now be in the Jupyter web application. Click New -> Python 3 to start a new notebook. A neat thing about Jupyter notebooks is that if you publish them to GitHub (either in a regular repository or as a Gist), it will render them as HTML. This makes them a very powerful way to share annotated code. I’ve published the notebook I used to build the search index on my GitHub account. ​ Here’s the Python code I used to scrape the relevant data from the downloaded HTML files. Check out the notebook for a line-by-line explanation of what’s going on. from pathlib import Path from bs4 import BeautifulSoup as Soup base = Path(""/Users/simonw/Dropbox/Development/24ways-search"") articles = list(base.glob(""*/*/*/*.html"")) # articles is now a list of paths that look like this: # PosixPath('...24ways-search/24ways.org/2013/why-bother-with-accessibility/index.html') docs = [] for path in articles: year = str(path.relative_to(base)).split(""/"")[1] url = 'https://' + str(path.relative_to(base).parent) + '/' soup = Soup(path.open().read(), ""html5lib"") author = soup.select_one("".c-continue"")[""title""].split( ""More information about"" )[1].strip() author_slug = soup.select_one("".c-continue"")[""href""].split( ""/authors/"" )[1].split(""/"")[0] published = soup.select_one("".c-meta time"")[""datetime""] contents = soup.select_one("".e-content"").text.strip() title = soup.find(""title"").text.split("" ◆"")[0] try: topic = soup.select_one( '.c-meta a[href^=""/topics/""]' )[""href""].split(""/topics/"")[1].split(""/"")[0] except TypeError: topic = None docs.append({ ""title"": title, ""contents"": contents, ""year"": year, ""author"": author, ""author_slug"": author_slug, ""published"": published, ""url"": url, ""topic"": topic, }) After running this code, I have a list of Python dictionaries representing each of the documents that I want to add to the index. The list looks something like this: [ { ""title"": ""Why Bother with Accessibility?"", ""contents"": ""Web accessibility (known in other fields as inclus..."", ""year"": ""2013"", ""author"": ""Laura Kalbag"", ""author_slug"": ""laurakalbag"", ""published"": ""2013-12-10T00:00:00+00:00"", ""url"": ""https://24ways.org/2013/why-bother-with-accessibility/"", ""topic"": ""design"" }, { ""title"": ""Levelling Up"", ""contents"": ""Hello, 24 ways. Iu2019m Ashley and I sell property ins..."", ""year"": ""2013"", ""author"": ""Ashley Baxter"", ""author_slug"": ""ashleybaxter"", ""published"": ""2013-12-06T00:00:00+00:00"", ""url"": ""https://24ways.org/2013/levelling-up/"", ""topic"": ""business"" }, ... My sqlite-utils library has the ability to take a list of objects like this and automatically create a SQLite database table with the right schema to store the data. Here’s how to do that using this list of dictionaries. import sqlite_utils db = sqlite_utils.Database(""/tmp/24ways.db"") db[""articles""].insert_all(docs) That’s all there is to it! The library will create a new database and add a table to it called articles with the necessary columns, then insert all of the documents into that table. (I put the database in /tmp/ for the moment - you can move it to a more sensible location later on.) You can inspect the table using the sqlite3 command-line utility (which comes with OS X) like this: $ sqlite3 /tmp/24ways.db sqlite> .headers on sqlite> .mode column sqlite> select title, author, year from articles; title author year ------------------------------ ------------ ---------- Why Bother with Accessibility? Laura Kalbag 2013 Levelling Up Ashley Baxte 2013 Project Hubs: A Home Base for Brad Frost 2013 Credits and Recognition Geri Coady 2013 Managing a Mind Christopher 2013 Run Ragged Mark Boulton 2013 Get Started With GitHub Pages Anna Debenha 2013 Coding Towards Accessibility Charlie Perr 2013 ... There’s one last step to take in our notebook. We know we want to use SQLite’s full-text search feature, and sqlite-utils has a simple convenience method for enabling it for a specified set of columns in a table. We want to be able to search by the title, author and contents fields, so we call the enable_fts() method like this: db[""articles""].enable_fts([""title"", ""author"", ""contents""]) Introducing Datasette Datasette is the open-source tool I’ve been building that makes it easy to both explore SQLite databases and publish them to the internet. We’ve been exploring our new SQLite database using the sqlite3 command-line tool. Wouldn’t it be nice if we could use a more human-friendly interface for that? If you don’t want to install Datasette right now, you can visit https://search-24ways.herokuapp.com/ to try it out against the 24 ways search index data. I’ll show you how to deploy Datasette to Heroku like this later in the article. If you want to install Datasette locally, you can reuse the virtual environment we created to play with Jupyter: ./jupyter-venv/bin/pip install datasette This will install Datasette in the ./jupyter-venv/bin/ folder. You can also install it system-wide using regular pip install datasette. Now you can run Datasette against the 24ways.db file we created earlier like so: ./jupyter-venv/bin/datasette /tmp/24ways.db This will start a local webserver running. Visit http://localhost:8001/ to start interacting with the Datasette web application. If you want to try out Datasette without creating your own 24ways.db file you can download the one I created directly from https://search-24ways.herokuapp.com/24ways-ae60295.db Publishing the database to the internet One of the goals of the Datasette project is to make deploying data-backed APIs to the internet as easy as possible. Datasette has a built-in command for this, datasette publish. If you have an account with Heroku or Zeit Now, you can deploy a database to the internet with a single command. Here’s how I deployed https://search-24ways.herokuapp.com/ (running on Heroku’s free tier) using datasette publish: $ ./jupyter-venv/bin/datasette publish heroku /tmp/24ways.db --name search-24ways -----> Python app detected -----> Installing requirements with pip -----> Running post-compile hook -----> Discovering process types Procfile declares types -> web -----> Compressing... Done: 47.1M -----> Launching... Released v8 https://search-24ways.herokuapp.com/ deployed to Heroku If you try this out, you’ll need to pick a different --name, since I’ve already taken search-24ways. You can run this command as many times as you like to deploy updated versions of the underlying database. Searching and faceting Datasette can detect tables with SQLite full-text search configured, and will add a search box directly to the page. Take a look at http://search-24ways.herokuapp.com/24ways-b607e21/articles to see this in action. ​ SQLite search supports wildcards, so if you want autocomplete-style search where you don’t need to enter full words to start getting results you can add a * to the end of your search term. Here’s a search for access* which returns articles on accessibility: http://search-24ways.herokuapp.com/24ways-ae60295/articles?_search=acces%2A A neat feature of Datasette is the ability to calculate facets against your data. Here’s a page showing search results for svg with facet counts calculated against both the year and the topic columns: http://search-24ways.herokuapp.com/24ways-ae60295/articles?_search=svg&_facet=year&_facet=topic Every page visible via Datasette has a corresponding JSON API, which can be accessed using the JSON link on the page - or by adding a .json extension to the URL: http://search-24ways.herokuapp.com/24ways-ae60295/articles.json?_search=acces%2A Better search using custom SQL The search results we get back from ../articles?_search=svg are OK, but the order they are returned in is not ideal - they’re actually being returned in the order they were inserted into the database! You can see why this is happening by clicking the View and edit SQL link on that search results page. This exposes the underlying SQL query, which looks like this: select rowid, * from articles where rowid in ( select rowid from articles_fts where articles_fts match :search ) order by rowid limit 101 We can do better than this by constructing a custom SQL query. Here’s the query we will use instead: select snippet(articles_fts, -1, 'b4de2a49c8', '8c94a2ed4b', '...', 100) as snippet, articles_fts.rank, articles.title, articles.url, articles.author, articles.year from articles join articles_fts on articles.rowid = articles_fts.rowid where articles_fts match :search || ""*"" order by rank limit 10; You can try this query out directly - since Datasette opens the underling SQLite database in read-only mode and enforces a one second time limit on queries, it’s safe to allow users to provide arbitrary SQL select queries for Datasette to execute. There’s a lot going on here! Let’s break the SQL down line-by-line: select snippet(articles_fts, -1, 'b4de2a49c8', '8c94a2ed4b', '...', 100) as snippet, We’re using snippet(), a built-in SQLite function, to generate a snippet highlighting the words that matched the query. We use two unique strings that I made up to mark the beginning and end of each match - you’ll see why in the JavaScript later on. articles_fts.rank, articles.title, articles.url, articles.author, articles.year These are the other fields we need back - most of them are from the articles table but we retrieve the rank (representing the strength of the search match) from the magical articles_fts table. from articles join articles_fts on articles.rowid = articles_fts.rowid articles is the table containing our data. articles_fts is a magic SQLite virtual table which implements full-text search - we need to join against it to be able to query it. where articles_fts match :search || ""*"" order by rank limit 10; :search || ""*"" takes the ?search= argument from the page querystring and adds a * to the end of it, giving us the wildcard search that we want for autocomplete. We then match that against the articles_fts table using the match operator. Finally, we order by rank so that the best matching results are returned at the top - and limit to the first 10 results. How do we turn this into an API? As before, the secret is to add the .json extension. Datasette actually supports multiple shapes of JSON - we’re going to use ?_shape=array to get back a plain array of objects: JSON API call to search for articles matching SVG The HTML version of that page shows the time taken to execute the SQL in the footer. Hitting refresh a few times, I get response times between 2 and 5ms - easily fast enough to power a responsive autocomplete feature. A simple JavaScript autocomplete search interface I considered building this using React or Svelte or another of the myriad of JavaScript framework options available today, but then I remembered that vanilla JavaScript in 2018 is a very productive environment all on its own. We need a few small utility functions: first, a classic debounce function adapted from this one by David Walsh: function debounce(func, wait, immediate) { let timeout; return function() { let context = this, args = arguments; let later = () => { timeout = null; if (!immediate) func.apply(context, args); }; let callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; We’ll use this to only send fetch() requests a maximum of once every 100ms while the user is typing. Since we’re rendering data that might include HTML tags (24 ways is a site about web development after all), we need an HTML escaping function. I’m amazed that browsers still don’t bundle a default one of these: const htmlEscape = (s) => s.replace( />/g, '>' ).replace( /Autocomplete search

And now the autocomplete implementation itself, as a glorious, messy stream-of-consciousness of JavaScript: // Embed the SQL query in a multi-line backtick string: const sql = `select snippet(articles_fts, -1, 'b4de2a49c8', '8c94a2ed4b', '...', 100) as snippet, articles_fts.rank, articles.title, articles.url, articles.author, articles.year from articles join articles_fts on articles.rowid = articles_fts.rowid where articles_fts match :search || ""*"" order by rank limit 10`; // Grab a reference to the const searchbox = document.getElementById(""searchbox""); // Used to avoid race-conditions: let requestInFlight = null; searchbox.onkeyup = debounce(() => { const q = searchbox.value; // Construct the API URL, using encodeURIComponent() for the parameters const url = ( ""https://search-24ways.herokuapp.com/24ways-866073b.json?sql="" + encodeURIComponent(sql) + `&search=${encodeURIComponent(q)}&_shape=array` ); // Unique object used just for race-condition comparison let currentRequest = {}; requestInFlight = currentRequest; fetch(url).then(r => r.json()).then(d => { if (requestInFlight !== currentRequest) { // Avoid race conditions where a slow request returns // after a faster one. return; } let results = d.map(r => `

${htmlEscape(r.title)}

${htmlEscape(r.author)} - ${r.year}

${highlight(r.snippet)}

`).join(""""); document.getElementById(""results"").innerHTML = results; }); }, 100); // debounce every 100ms There’s just one more utility function, used to help construct the HTML results: const highlight = (s) => htmlEscape(s).replace( /b4de2a49c8/g, '' ).replace( /8c94a2ed4b/g, '' ); This is what those unique strings passed to the snippet() function were for. Avoiding race conditions in autocomplete One trick in this code that you may not have seen before is the way race-conditions are handled. Any time you build an autocomplete feature, you have to consider the following case: User types acces Browser sends request A - querying documents matching acces* User continues to type accessibility Browser sends request B - querying documents matching accessibility* Request B returns. It was fast, because there are fewer documents matching the full term The results interface updates with the documents from request B, matching accessibility* Request A returns results (this was the slower of the two requests) The results interface updates with the documents from request A - results matching access* This is a terrible user experience: the user saw their desired results for a brief second, and then had them snatched away and replaced with those results from earlier on. Thankfully there’s an easy way to avoid this. I set up a variable in the outer scope called requestInFlight, initially set to null. Any time I start a new fetch() request, I create a new currentRequest = {} object and assign it to the outer requestInFlight as well. When the fetch() completes, I use requestInFlight !== currentRequest to sanity check that the currentRequest object is strictly identical to the one that was in flight. If a new request has been triggered since we started the current request we can detect that and avoid updating the results. It’s not a lot of code, really And that’s the whole thing! The code is pretty ugly, but when the entire implementation clocks in at fewer than 70 lines of JavaScript, I honestly don’t think it matters. You’re welcome to refactor it as much you like. How good is this search implementation? I’ve been building search engines for a long time using a wide variety of technologies and I’m happy to report that using SQLite in this way is genuinely a really solid option. It scales happily up to hundreds of MBs (or even GBs) of data, and the fact that it’s based on SQL makes it easy and flexible to work with. A surprisingly large number of desktop and mobile applications you use every day implement their search feature on top of SQLite. More importantly though, I hope that this demonstrates that using Datasette for an API means you can build relatively sophisticated API-backed applications with very little backend programming effort. If you’re working with a small-to-medium amount of data that changes infrequently, you may not need a more expensive database. Datasette-powered applications easily fit within the free tier of both Heroku and Zeit Now. For more of my writing on Datasette, check out the datasette tag on my blog. And if you do build something fun with it, please let me know on Twitter.",2018,Simon Willison,simonwillison,2018-12-19T00:00:00+00:00,https://24ways.org/2018/fast-autocomplete-search-for-your-website/,code 209,Feeding the Audio Graph,"In 2004, I was given an iPod. I count this as one of the most intuitive pieces of technology I’ve ever owned. It wasn’t because of the the snazzy (colour!) menus or circular touchpad. I loved how smoothly it fitted into my life. I could plug in my headphones and listen to music while I was walking around town. Then when I got home, I could plug it into an amplifier and carry on listening there. There was no faff. It didn’t matter if I could find my favourite mix tape, or if my WiFi was flakey - it was all just there. Nowadays, when I’m trying to pair my phone with some Bluetooth speakers, or can’t find my USB-to-headphone jack, or even access any music because I don’t have cellular reception; I really miss this simplicity. The Web Audio API I think the Web Audio API feels kind of like my iPod did. It’s different from most browser APIs - rather than throwing around data, or updating DOM elements - you plug together a graph of audio nodes, which the browser uses to generate, process, and play sounds. The thing I like about it is that you can totally plug it into whatever you want, and it’ll mostly just work. So, let’s get started. First of all we want an audio source.