Jeffrey Zeldman once said, “Usability is like love. You have to care, you have to listen, and you have to be willing to change. You’ll make mistakes along the way, but that’s where growth and forgiveness come in.” Steve Jobs once said, “Design is a funny word. Some people think design means how it looks. But of course, if you dig deeper, it’s really how it works.” If you think of design as a tool it becomes a much more powerful skill set and by putting the user first you can transform your product into something truly remarkable.
This article will be an introduction to the human-centered design process. I’ll tell a personal story in which I built a challenged family member a device to help them communicate more efficient and effortlessly and I’ll share lessons I learned from the failures and successes along the way. Before the story starts, I’d like you to keep one thing in mind. The definition of User-Centered Design.
“Usability is like love. You have to care, you have to listen, and you have to be willing to change. You’ll make mistakes along the way, but that’s where growth and forgiveness come in.”
Dustin is my 30-year-old brother. He was born with little to no oxygen in his body and underwent two open-heart surgeries before his 10th birthday. He is also autistic. As a result of those health issues combined with the autism, he is not able to speak. Many wrongly confuse his silence with a lack of knowledge and understanding. But Dustin knows what’s going on. He probably has more thoughts than you and me; he just can’t express them fully.
Growing up, I have always seen how much Dustin has struggled to communicate a simple word to anyone in our family, much less a random stranger. He makes only a few sounds, and only one person on Earth mildly understands what he’s trying to say — a magnificent woman I call Mom. I can only imagine the way he feels when she is not at his side to speak for him. So, I’ve always said that if a genie were to grant me three wishes, I would need just one: to make my brother “normal.” But, as time has passed and Dustin and I have gotten older, I have come to realize that “normal” does not exist, and if it does, then maybe he is a bit more normal than me.
Fast forward 20 years, to my last year of graduate school. I was six months away from moving across the country. With this knowledge I had, I wanted to design a tool that would help my brother to communicate his needs and that would relieve a little stress on my family as a whole.
Using my skills as an experience designer and having worked in web development, I planned to build Dustin a web app that would live on an iPad. In its simplest form, he would be able to tap the picture associated with his immediate need, and that tap gesture would trigger an audio file to speak the need to my parents. Simple, right? Everything was set, so I began sketching away and looking for inspiration to get started.
Little did I know that I hadn’t been thinking about how Dustin would actually utilize the functionality of the app. One night at my parents’ house for dinner, the idea popped into my head. Dustin grabbed my phone and started tapping randomly. As I watched him continue to struggle with the phone, I realized that I had already failed. My idea was already dead. That was when I grasped the true importance of a discovery phase in any project.
Observe, research, test, repeat. A “day in the life” of your end user persona is a helpful exercise to do in the research or discovery phase. With persona mapping, you — the designer — choose one representative user and design to their daily needs. The user may be fictitious or real, but most of us use a fictitious persona who brings together attributes from a larger segment. Mapping out this persona’s daily routine during a 24-hour period will help us to pinpoint holes in their routine where your product would be most useful to them, and possibly also help us to find ways to improve other aspects of their daily life in the process. In web design, a day-in-the-life exercise can be beneficial when the budget or time for focus groups is limited. It provides opportunities for key findings on a product’s structure. Jeffrey Rubin identifies a few usability objectives 5 6 :
For a company to succeed, it should frequently test its product-related predictions or prototypes with users. This will help to validate or contradict any assumptions made during the previous design phases. In subsequent design phases, we use the insights and learning gathered during user testing to pivot the company’s business model or the product’s utility and features.
Pivoting is scary, but don’t be afraid. More often than not, when all of your research and testing is leaning towards it, pivoting the product is the best thing to do. All of the hard work you’ve put into the first round might seem like a waste at first, but think of it as an investment into a better and more user-centric product, one that will now better cater to its consumers and that will prolong the product’s life cycle.
Luckily, I was able to reign in my excitement and focus on the user quickly. As I did more research on autism spectrum disorder (ASD), I recognized behaviors in Dustin that I had always seen but never understood. The majority of people with ASD have difficulty processing everyday sensory information such as sounds, touch, sights and smells. This is usually called sensory integration difficulties, or sensory sensitivity. As the National Autistic Society of the UK explains 7 8 , it can profoundly affect a person’s life.
With that in mind, I began to observe Dustin and track his behaviors. I interviewed his closest caregivers at home, showed him different styles of icons (thanks, NounProject 8 9 ) and tested colors, palettes and textures. I had to take many variables into consideration. The communication device had to be simple to use, be durable, be visually inviting, use some sort of icon language and have a texture similar to what he is used to in his everyday life. This was important because most people affected by autism prefer order and need a routine schedule.
Something else I had to keep in mind was that Dustin would not be the only user. Communication requires two parties. So, I started to view my mom not as a caregiver, but as a user. This revealed the struggles she has faced and enabled me to compare the needs of both end users and create something that bridges this communication barrier.
“Dustin and I communicate pretty well, but that’s after 30 years of being together all day, every day. Sometimes it takes a few minutes of Dustin struggling to express what he wants or needs. I get scared and wonder what he would do if something happened to me? The way we communicate is strange and no one, not even your dad, gets it.”
– Missy Reamer (Mom)
Another factor was thrown my way. My mom mentioned that she was worried for the day when they might not be together, and even worried about the simple pleasure of a vacation. The device had to not only be able to communicate to my mom in a timely manner, but also be interchangeable for other caregivers upon request. To achieve this, I decided to use SMS or, as the general public calls it, text messaging. I had worked on previous projects using SMS, so I was familiar with programming it onto the Arduino board.
You might be wondering what an Arduino board is? It is a small microcontroller whose hardware is an open-sourced board designed around an 8-bit Atmel AVR microcontroller or a 32-bit Atmel ARM. In other words, these various boards serve different purposes but all generally enable you to build interactive objects that serve various functions through APIs and Arduino libraries.
Although there are multiple ways to supply the 5 volts needed to power an Arduino, the best method for my purpose was to connect it to a computer via USB, so that anyone with minimal computer skills would be able to easily plug it in and simply change or add a phone number connected to the device for the day, week, etc. Once I had completed the programming, it would become a simple plug-and-play device.
Now that I knew how the device would work, it was time to move on to the look and feel of the exterior. The surface of the device was blue, and the shell for the electronics was already made. I took it to my parents’ home to show it off to everyone, and Dustin seemed really uninterested and kind of turned off. Frustration set in. Come on, work with me here, Dustin!
Then, another pivotal moment. I noticed Dustin standing in front of the mirror staring at himself. Remembering back to our childhood, I know that he had always been drawn to shiny metallic things: the toy guns he used to play with, the micro machine cars that were strewn around the house, his obsession with new cars, the bouncy new balls he got out of the 25-cent machine. I was onto something. I showed him the metal top of a project enclosure from RadioShack 9 10 , and he loved it.
A metallic surface was the perfect answer because it is smooth to the touch and the all-white external shell is soft on his eyes. This was another lesson. If your product is changing as a result of user observation or feedback, then it’s changing for the better.
The more you ask yourself “Why” while designing an experience, the more successful it will be. “Why” is a powerful question that I’ve gotten into the habit of asking myself when designing experiences. “Why is this here?” “Why should it animate like this?” “Why does the end user care?” Why, why, why. Always have a reason for the design decisions you make.
At this point, the product had gone through quite an evolution. It started as a web app concept that requires the physical presence of a caregiver, and it evolved into a device with push buttons that sends text messages to the recipient in real time. Using an Arduino Yun 13 14 and the Temboo library 14 15 allowed me to get free SMS service through a Wi-Fi connection. With the exterior shell for the electronics in place, it was time to roll out functionality for the SMS delivery. Sending an SMS requires some sort of trigger or button.
The choice of arcade buttons was on purpose because their large size would minimize errors and incorrect button hits. Having a tactility that is familiar to Dustin and comfortable for him was also important. Because he has always had a passion for and deep understanding of video games, the arcade-style buttons make perfect sense. On top of each button is an icon representing the six particular needs in his day-to-day life that stood out for my mom in my interview with her.
I had to keep in mind some other important factors, too. The product had to have all of the following:
The requirements above are necessary, all for different reasons. Because it is a physical device with no screens, I had to create the simplest way to let Dustin know that an SMS has gone through successfully. This is where the red and green LED lights come in, as well as the recognizable sound that the arcade buttons trigger in their hit states. Still thinking of my parents as secondary users, I had to make sure that the device was low on energy consumption and easy to move because Dustin is not always in one place. In this process, known as future-proofing, the creator thinks of all possible scenarios in which the device might be used in future.
If you always think five years ahead (or more), your iteration will be golden. Users of your product are forever evolving, as are their needs. If a product doesn’t iterate and evolve, then its relevance will fade away. My observation of the user (Dustin) operating this device over the next five years will be the basis for future iteration.
Learn from Blockbuster. A big brand let a little startup (heard of Netflix?) creep in their backyard, pitch a tent and camp out. Soon enough, that tent transformed into a shed, which then transformed into a guest house, and eventually the startup demolished the existing house and constructed its own — all this because Blockbuster did not evolve its product. Sorry, Blockbuster, I used to love you.
Back to Dustin. The way the product stands now is not how it will remain, for this is a forever evolving experience and I plan to continue to further it. Future plans are to build a back-end CMS where, after my brother and parents have learned a mode of communication for certain needs together, they can swap the meanings and icons of particular buttons. If we do not have an icon that represents a particular need, we will quickly create an icon for it. This way, the library of icons will grow in an efficient and user-centered way. For a more personal experience, the user could upload a photo into a graphical OLED button for quick communication. Refinement of the construction of the device itself is also planned.
This device was designed and built just for my brother, but variations could help thousands of people who are challenged by speech or motor-function disabilities. In a world where we can customize our Nike’s for only $100, why does it cost upwards of a thousand dollars for someone with a life-altering disability to have what many fortunate people are born with, speech?
I think variations of this device could help many others. I anticipate creating dozens of use cases and design icons for future users to load onto their devices when connected via Bluetooth, micro-USB or Wi-Fi.
In designing this experience for Dustin, I’ve learned much more about him than I had previously known. Putting user-centered design truly into practice takes research, followed by iteration and testing. Iteration is the key to making any product, experience or service useful. But that experience means little without love: love for what you do and respect for who you are doing it for. I hope that this has inspired or at least has gotten you thinking about how you can push your skills to help the people around you, as well as shed some light on some basic principles of user experience design that are too often overlooked.
The source code and schematics for this project are available on Github 34 . Feel free to interpret this project in your own way to help someone you know.
To conclude, one of my favorite quotes, from author James Jesse Garrett:
“User-centered design means understanding what your users need, how they think, and how they behave — and incorporating that understanding into every aspect of your process.”
(cc, al, ml)
The post The More You Fail, The Greater Your Success: A User-Centered Design Case Study appeared first on Smashing Magazine.
HTML5 introduced a bunch of new tags, one of which is <details>
. This element is a solution for a common UI component: a collapsible block. Almost every framework, including Bootstrap and jQuery UI, has its own plugin for a similar solution, but none conform to the HTML5 specification — probably because most were around long before <details>
got specified and, therefore, represent different approaches. A standard element allows everyone to use the same markup for a particular type of content. That’s why creating a robust polyfill makes sense1.
Disclaimer: This is quite a technical article, and while I’ve tried to minimize the code snippets, the article still contains quite a few of them. So, be prepared!
I’m not2 the first person3 to try to implement such a polyfill. Unfortunately, all other solutions exhibit one or another problem:
<details>
into the DOM and be done with it, and not have to fiddle with JavaScript to get it going.open
attribute<details>
comes with the open
attribute, which is used to toggle the visibility of the contents of <details>
.toggle
event is missingdetails
element has changed its open
state. Ideally, it should be a vanilla DOM event.In this article we’ll use better-dom5 to make things simpler. The main reason is the live extensions6 feature, which solves the problem of invoking the initialization function for dynamic content. (For more information, read my detailed article about live extensions7.) Additionally, better-dom outfits live extensions with a set of tools that do not (yet) exist in vanilla DOM but that come in handy when implementing a polyfill like this one.
details
element in Safari 8 (View large version18149)Let’s take a closer look at all of the hurdles we have to overcome to make <details>
available in browsers that don’t support it.
To start, we need to declare a live extension for the "details"
selector. What if the browser already supports the element natively? Then we’ll need to add some feature detection. This is easy with the optional second argument condition
, which prevents the logic from executing if its value is equal to false
:
// Invoke extension only if there is no native support
var open = DOM.create("details").get("open");
DOM.extend("details", typeof open !== "boolean", {
constructor: function() {
console.log("initialize <details>…");
}
});
As you can see, we are trying to detect native support by checking for the open
property, which obviously only exists in browsers that recognize <details>
.
What sets DOM.extend
11 apart from a simple call like document.querySelectorAll
is that the constructor
function executes for future content, too. And, yes, it works with any library for manipulating the DOM:
// You can use better-dom…
DOM.find("body").append(
"<details><summary>TITLE</summary><p>TEXT</p></details>");
// => logs "initialize <details>…"
// or any other DOM library, like jQuery…
$("body").append(
"<details><summary>TITLE</summary><p>TEXT</p></details>");
// => logs "initialize <details>…"
// or even vanilla DOM.
document.body.insertAdjacentElement("beforeend",
"<details><summary>TITLE</summary><p>TEXT</p></details>");
// => logs "initialize <details>…"
In the following sections, we’ll replace the console.log
call with a real implementation.
<summary>
BehaviorThe <details>
element may take <summary>
as a child element.
The first summary element child of details, if one is present, represents an overview of details. If no child summary element is present, then the user agent should provide its own legend (for example, “Details”).
Let’s add mouse support. A click on the <summary>
element should toggle the open
attribute on the parent <details>
element. This is what it looks like using better-dom:
DOM.extend("details", typeof open !== "boolean", {
constructor: function() {
this
.children("summary:first-child")
.forEach(this.doInitSummary);
},
doInitSummary: function(summary) {
summary.on("click", this.doToggleOpen);
},
doToggleOpen: function() {
// We’ll cover the open property value later.
this.set("open", !this.get("open"));
}
});
The children
method returns a JavaScript array of elements (not an array-like object as in vanilla DOM). Therefore, if no <summary>
is found, then the doInitSummary
function is not executed. Also, doInitSummary
and doToggleOpen
are private functions12, they always are invoked for the current element. So, we can pass this.doInitSummary
to Array#forEach
without extra closures, and everything will execute correctly there.
Having keyboard support in addition to mouse support is good as well. But first, let’s make <summary>
a focusable element. A typical solution is to set the tabindex
attribute to 0
:
doInitSummary: function(summary) {
// Makes summary focusable
summary.set("tabindex", 0);
…
}
Now, the user hitting the space bar or the “Enter” key should toggle the state of <details>
. In better-dom, there is no direct access to the event object. Instead, we need to declare which properties to grab using an extra array argument:
doInitSummary: function(summary) {
…
summary.on("keydown", ["which"], this.onKeyDown);
}
Note that we can reuse the existing doToggleOpen
function; for a keydown
event, it just makes an extra check on the first argument. For the click event handler, its value is always equal to undefined
, and the result will be this:
doInitSummary: function(summary) {
summary
.set("tabindex", 0)
.on("click", this.doToggleOpen)
.on("keydown", ["which"], this.doToggleOpen);
},
doToggleOpen: function(key) {
if (!key || key === 13 || key === 32) {
this.set("open", !this.get("open"));
// Cancel form submission on the ENTER key.
return false;
}
}
Now we have a mouse- and keyboard-accessible <details>
element.
<summary>
Element Edge CasesThe <summary>
element introduces several edge cases that we should take into consideration:
<summary>
Is a Child But Not the First Childsummary
element is not the first child. (View large version18149)Browser vendors have tried to fix such invalid markup by moving <summary>
to the position of the first child visually, even when the element is not in that position in the flow of the DOM. I was confused by such behavior, so I asked the W3C for clarification15. The W3C confirmed that <summary>
must be the first child of <details>
. If you check the markup in the screenshot above on Nu Markup Checker16, it will fail with the following error message:
Error: Element summary not allowed as child of element details in this context. […] Contexts in which element summary may be used: As the first child of a details element.
My approach is to move the <summary>
element to the position of the first child. In other words, the polyfill fixes the invalid markup for you:
doInitSummary: function(summary) {
// Make sure that summary is the first child
if (this.child(0) !== summary) {
this.prepend(summary);
}
…
}
<summary>
Element Is Not Presentsummary
element is not present (View large version18149)As you can see in the screenshot above, browser vendors insert “Details” as a legend into <summary>
in this case. The markup stays untouched. Unfortunately, we can’t achieve the same without accessing the shadow DOM19, which unfortunately has weak support20 at present. Still, we can set up <summary>
manually to comply with standards:
constructor: function() {
…
var summaries = this.children("summary");
// If no child summary element is present, then the
// user agent should provide its own legend (e.g. "Details").
this.doInitSummary(
summaries[0] || DOM.create("summary>`Details`"));
}
open
PropertyIf you try the code below in browsers that support <details>
natively and in others that don’t, you’ll get different results:
details.open = true;
// <details> changes state in Chrome and Safari
details.open = false;
// <details> state changes back in Chrome and Safari
In Chrome and Safari, changing the value of open
triggers the addition or removal of the attribute. Other browsers do not respond to this because they do not support the open
property on the <details>
element.
Properties are different from simple values. They have a pair of getter and setter functions that are invoked every time you read or assign a new value to the field. And JavaScript has had an API to declare properties since version 1.5.
The good news is that one old browser we are going to use with our polyfill, Internet Explorer (IE) 8, has partial support for the Object.defineProperty
function. The limitation is that the function works only on DOM elements. But that is exactly what we need, right?
There is a problem, though. If you try to set an attribute with the same name in the setter function in IE 8, then the browser will stack with infinite recursion and crashes. In old versions of IE, changing an attribute will trigger the change of an appropriate property and vice versa:
Object.defineProperty(element, "foo", {
…
set: function(value) {
// The line below triggers infinite recursion in IE 8.
this.setAttribute("foo", value);
}
});
So you can’t modify the property without changing an attribute there. This limitation has prevented developers from using the Object.defineProperty
for quite a long time.
The good news is that I’ve found a solution.
Before describing the solution, I’d like to give some background on one feature of the HTML and CSS parser in browsers. In case you weren’t aware, these parsers are case-insensitive. For example, the rules below will produce the same result (i.e. a base red for the text on the page):
body { color: red; }
/* The rule below will produce the same result. */
BODY { color: red; }
The same goes for attributes:
el.setAttribute("foo", "1");
el.setAttribute("FOO", "2");
el.getAttribute("foo"); // => "2"
el.getAttribute("FOO"); // => "2"
Moreover, you can’t have uppercased and lowercased attributes with the same name. But you can have both on a JavaScript object, because JavaScript is case-sensitive:
var obj = {foo: "1", FOO: "2"};
obj.foo; // => "1"
obj.FOO; // => "2"
Some time ago, I found that IE 8 supports the deprecated legacy argument lFlags
21 for attribute methods, which allows you to change attributes in a case-sensitive manner:
lFlags
[in, optional]
- Type: Integer
- Integer that specifies whether to use a case-sensitive search to locate the attribute.
Remember that the infinite recursion happens in IE 8 because the browser is trying to update the attribute with the same name and therefore triggers the setter function over and over again. What if we use the lFlags
argument to get and set the uppercased attribute value:
// Defining the "foo" property but using the "FOO" attribute
Object.defineProperty(element, "foo", {
get: function() {
return this.getAttribute("FOO", 1);
},
set: function(value) {
// No infinite recursion!
this.setAttribute("FOO", value, 1);
}
});
As you might expect, IE 8 updates the uppercased field FOO
on the JavaScript object, and the setter function does not trigger a recursion. Moreover, the uppercased attributes work with CSS too — as we stated in the beginning, that parser is case-insensitive.
open
AttributeNow we can define an open
property that works in every browser:
var attrName = document.addEventListener ? "open" : "OPEN";
Object.defineProperty(details, "open", {
get: function() {
var attrValue = this.getAttribute(attrName, 1);
attrValue = String(attrValue).toLowerCase();
// Handle boolean attribute value
return attrValue === "" || attrValue === "open";
}
set: function(value) {
if (this.open !== value) {
console.log("firing toggle event");
}
if (value) {
this.setAttribute(attrName, "", 1);
} else {
this.removeAttribute(attrName, 1);
}
}
});
Check how it works:
details.open = true;
// => logs "firing toggle event"
details.hasAttribute("open"); // => true
details.open = false;
// => logs "firing toggle event"
details.hasAttribute("open"); // => false
Excellent! Now let’s do similar calls, but this time using *Attribute
methods:
details.setAttribute("open", "");
// => silence, but fires toggle event in Chrome and Safari
details.removeAttribute("open");
// => silence, but fires toggle event in Chrome and Safari
The reason for such behavior is that the relationship between the open
property and the attribute should be bidirectional. Every time the attribute is modified, the open
property should reflect the change, and vice versa.
The simplest cross-browser solution I’ve found for this issue is to override the attribute methods on the target element and invoke the setters manually. This avoids bugs and the performance penalty of legacy propertychange
22 and DOMAttrModified
23 events. Modern browsers support MutationObservers
24, but that doesn’t cover our browser scope.
Obviously, walking through all of the steps above when defining a new attribute for a DOM element wouldn’t make sense. We need a utility function for that which hides cross-browser quirks and complexity. I’ve added such a function, named defineAttribute
25, in better-dom.
The first argument is the name of the property or attribute, and the second is the get
and set
object. The getter function takes the attribute’s value as the first argument. The setter function accepts the property’s value, and the returned statement is used to update the attribute. Such a syntax allows us to hide the trick for IE 8 where an uppercased attribute name is used behind the scenes:
constructor: function() {
…
this.defineAttribute("open", {
get: this.doGetOpen,
set: this.doSetOpen
});
},
doGetOpen: function(attrValue) {
attrValue = String(attrValue).toLowerCase();
return attrValue === "" || attrValue === "open";
},
doSetOpen: function(propValue) {
if (this.get("open") !== propValue) {
this.fire("toggle");
}
// Adding or removing boolean attribute "open"
return propValue ? "" : null;
}
Having a true polyfill for the open
attribute simplifies our manipulation of the <details>
element’s state. Again, this API is framework-agnostic:
// You can use better-dom…
DOM.find("details").set("open", false);
// or any other DOM library, like jQuery…
$("details").prop("open", true);
// or even vanilla DOM.
document.querySelector("details").open = false;
The CSS part of the polyfill is simpler. It has some basic style rules:
summary:first-child ~ * {
display: none;
}
details[open] > * {
display: block;
}
/* Hide native indicator and use pseudo-element instead */
summary::-webkit-details-marker {
display: none;
}
I didn’t want to introduce any extra elements in the markup, so obvious choice is to style the ::before
pseudo-element. This pseudo-element is used to indicate the current state of <details>
(according to whether it is open or not). But IE 8 has some quirks, as usual — namely, with updating the pseudo-element state. I got it to work properly only by changing the content
property’s value itself:
details:before {
content: '\25BA';
…
}
details[open]:before {
content: '\25BC';
}
For other browsers, the zero-border trick will draw a font-independent CSS triangle. With a double-colon syntax for the ::before
pseudo-element, we can apply rules to IE 9 and above:
details::before {
content: '';
width: 0;
height: 0;
border: solid transparent;
border-left-color: inherit;
border-width: 0.25em 0.5em;
…
transform: rotate(0deg) scale(1.5);
}
details[open]::before {
content: '';
transform: rotate(90deg) scale(1.5);
}
The final enhancement is a small transition on the triangle. Unfortunately, Safari does not apply it for some reason (perhaps a bug), but it degrades well by ignoring the transition completely:
details::before {
…
transition: transform 0.15s ease-out;
}
You can find the full source code on Github27.
Some time ago, I started using transpilers in my projects, and they are great. Transpilers enhance source files. You can even code in a completely different language, like CoffeeScript instead of JavaScript or LESS instead of CSS etc. However, my intention in using them is to decrease unnecessary noise in the source code and to learn new features in the near future. That’s why transpilers do not go against any standards in my projects — I’m just using some extra ECMAScript 6 (ES6) stuff and CSS post-processors (Autoprefixer28 being the main one).
Also, to speak about bundling, I quickly found that distributing *.css
files along with *.js
is slightly annoying. In searching for a solution, I found HTML Imports29, which aims to solve this kind of problem in the future. At present, the feature has relatively weak browser support30. And, frankly, bundling all of that stuff into a single HTML file is not ideal.
So, I built my own approach for bundling: better-dom has a function, DOM.importStyles
31, that allows you to import CSS rules on a web page. This function has been in the library since the beginning because DOM.extend
uses it internally. Since I use better-dom and transpilers in my code anyway, I created a simple gulp task:
gulp.task("compile", ["lint"], function() {
var jsFilter = filter("*.js");
var cssFilter = filter("*.css");
return gulp.src(["src/*.js", "src/*.css"])
.pipe(cssFilter)
.pipe(postcss([autoprefixer, csswring, …]))
// need to escape some symbols
.pipe(replace(/\\|"/g, "\\$&"))
// and convert CSS rules into JavaScript function calls
.pipe(replace(/([^{]+)\{([^}]+)\}/g,
"DOM.importStyles(\"$1\", \"$2\");\n"))
.pipe(cssFilter.restore())
.pipe(jsFilter)
.pipe(es6transpiler())
.pipe(jsFilter.restore())
.pipe(concat(pkg.name + ".js"))
.pipe(gulp.dest("build/"));
});
To keep it simple, I didn’t put in any optional steps or dependency declarations (see the full source code32). In general, the compilation task contains the following steps:
DOM.importStyles
calls.*.js
file.And it works! I have transpilers that make my code clearer, and the only output is a single JavaScript file. Another advantage is that, when JavaScript is disabled, those style rules are completely ignored. For a polyfill like this, such behavior is desirable.
As you can see, developing a polyfill is not the easiest challenge. On the other hand, the solution can be used for a relatively long time: standards do not change often and have been discussed at length behind the scenes. Also everyone is using the same language and is connecting with the same APIs which is a great thing.
With the common logic moved into utility functions, the source code is not very complex33. This means that, at present, we really lack advanced tools to make robust polyfills that work close to native implementations (or better!). And I don’t see good libraries for this yet, unfortunately.
Libraries such as jQuery, Prototype and MooTools are all about providing extra sugar for working with the DOM. While sugar is great, we also need more utility functions to build more robust and unobtrusive polyfills. Without them, we might end up with a ton of plugins that are hard to integrate in our projects. May be it’s time to move into this direction?
Another technique that has arisen recently is Web Components34. I’m really excited by tools like the shadow DOM, but I’m not sure if custom elements35 are the future of web development. Moreover, custom elements can introduce new problems if everyone starts creating their own custom tags for common uses. My point is that we need to learn (and try to improve) the standards first before introducing a new HTML element. Fortunately, I’m not alone in this; Jeremy Keith, for one, shares a similar view36.
Don’t get me wrong. Custom elements are a nice feature, and they definitely have use cases in some areas. I look forward to them being implemented in all browsers. I’m just not sure if they’re a silver bullet for all of our problems.
To reiterate, I’d encourage creating more robust and unobtrusive polyfills. And we need to build more advanced tools to make that happen more easily. The example with <details>
shows that achieving such a goal today is possible. And I believe this direction is future-proof and the one we need to move in.
(al)
The post Making A Complete Polyfill For The HTML5 Details Element appeared first on Smashing Magazine.
“Supercell tornado at West Point Nebraska.” via Reddit.
So all you classically trained chefs out there, don't hate me for this.
The post One-Pot 45 Minute Coq au Vin with Brown Butter Sage Mashed Potatoes. appeared first on Half Baked Harvest.
It's 40' long from nose to tail, is composed of 190 bones, is billed as "museum grade" and comes with an assembly crew that will stage it in any "anatomically possible" pose. His name is Stan.