Blog

38 posts 2009 16 posts 2010 50 posts 2011 28 posts 2012 15 posts 2013 7 posts 2014 10 posts 2015 5 posts 2016 4 posts 2017 7 posts 2018 2 posts 2019 17 posts 2020 7 posts 2021 7 posts 2022 11 posts 2023 7 posts 2024

Create complex RegExps more easily

1 min read 0 comments Report broken page

When I was writing my linear-gradient() to -webkit-gradient() converter, I knew in advance that I would have to use a quite large regular expression to validate and parse the input. Such a regex would be incredibly hard to read and fix potential issues, so I tried to find a way to cut the process down in reusable parts.

Turns out JavaScript regular expression objects have a .source property that can be used in the RegExp constructor to create a new RegExp out of another one. So I wrote a new function that takes a string with identifiers for regexp replacements in and replaces them with the corresponding sub-regexps, taken from an object literal as a second argument:

/**
 * Create complex regexps in an easy-to-read way
 * @param str {String} Final regex with  for replacements
 * @param replacements {Object} Object with the replacements
 * @param flags {String} Just like the flags argument in the RegExp constructor
 */
RegExp.create = function(str, replacements, flags) {
	for(var id in replacements) {
		var replacement = replacements\[id\],
			idRegExp = RegExp(' + id + ', 'gi');

if(replacement.source) { replacement = replacement.source.replace(/^\^|\$$/g, ‘’); }

// Don’t add extra parentheses if they already exist str = str.replace(RegExp(‘\\(’ + idRegExp.source + ‘\\)’, ‘gi’), ‘(’ + replacement + ‘)’);

str = str.replace(idRegExp, ‘(?:’ + replacement + ‘)’); }

return RegExp(str, flags); };

If you don’t like adding a function to the RegExp object, you can name it however you want. Here’s how I used it for my linear-gradient() parser:

self.regex = {};

self.regex.number = /^-?[0-9]*\.?[0-9]+/;self.regex.keyword=/(?:tops+|bottoms+)?(?:right|left)|(?:rights+|lefts+)?(?:top|bottom)/;

self.regex.direction = RegExp.create(‘^(?:|deg|0)$’, { keyword: self.regex.keyword, number: self.regex.number });

self.regex.color = RegExp.create(‘(?:||)’, { keyword: /^(?:red|tan|grey|gray|lime|navy|blue|teal|aqua|cyan|gold|peru|pink|plum|snow|[a-z]{5,20})/,func:RegExp.create((?:rgb|hsl)a?((?:s\*', { number: self.regex.number }), hex: /^#(?:[0-9a-f]{1,2}){3}$/ });

self.regex.percentage = RegExp.create(‘^(?:%|0)$’, { number: self.regex.number });

self.regex.length = RegExp.create(‘|0’, { number: self.regex.number, unit: /%|px|mm|cm|in|em|rem|en|ex|ch|vm|vw|vh/ });

self.regex.colorStop = RegExp.create(‘\\s*?’, { color: self.regex.color, length: self.regex.length }, ‘g’);

self.regex.linearGradient = RegExp.create(‘^linear-gradient\\(\\s*(?:()\\s*,)?\\s*(\\s*(?:,\\s*\\s*)+)\\)$’, { direction: self.regex.direction, colorStop: self.regex.colorStop }, ‘i’);

(self in this case was a local variable, not the window object)


Convert standard gradient syntax to -webkit-gradient and others

1 min read 0 comments Report broken page

Screenshot of the demoI hate -webkit-gradient() with a passion. Its syntax is cumbersome and it’s really limited: No angle support, no <length>s in color stop positions, no implied color stop positions, no elliptical gradients… So, I was really happy, when Webkit implemented the standard syntax this January. However, we’re still stuck with the horrid -webkit-gradient() for quite a while, since older Webkit browsers that don’t support it are widely used at this time.

Today, I decided to finally spare myself the hassle of converting my standard gradient syntax to -webkit-gradient() by hand. Tasks like that shouldn’t be handled by a human. So, I coded a little script to do the chore. Hope it helps you too: View demo

It currently only supports linear gradients, but I plan to add radial ones in the future. Also, when I get around to cleaning up the code a bit, I’ll add it on Github.

(Hope I didn’t leave in any very stupid bug, it’s really late here and I’m half asleep.)


Beveled corners & negative border-radius with CSS3 gradients

1 min read 0 comments Report broken page

Just found out how to do beveled corners and simulate negative border radius without images, by utilizing CSS gradients once again. It’s amazing how many CSS problems can be solved with gradients alone. Read the text in the dabblet below to find out how (or just check the code):

It also falls back to a solid color background if CSS gradients are not supported. It will work on Firefox 3.6+, Chrome, Safari, Opera 11.10+ and IE10+.

PS: For my twitter friends, I had already written this when the robbers came and I was about to post it. I might have been really calm, but not as much as making CSS experiments the same day I was robbed and threatened by a gun :P


On CSS preprocessors

4 min read 0 comments Report broken page

Lately there has been a rise in the usage of CSS preprocessors such as LESS and SASS, which makes sense given the simultaneous increase of CSS3 usage. I’ve frequently argued with fellow front-end web developers about whether they should be used or not and I decided to finally put my thoughts in writing.

To start, I can fully understand the advantage of using such preprocessors over vanilla CSS3. I hate listing all the vendor prefixes, and not being able to use variables, mixins or nesting just like the next web developer. All this syntactic sugar can simplify your workflow by a great deal and make writing CSS3 incredibly fun. However, I still refrain from using them, and I’ll explain why below.

Losing track of CSS filesize

When I’m writing CSS, I try to keep the filesize as small as possible. I’m not a filesize hypochondriac, I try to balance filesize and readability and I prefer to err on the side of the latter. I’m not one of those people that will use #000 instead of black just to save a byte and I use lots of indents and newlines (later minification takes care of that). However, in cases when the readability impact is small and the filesize impact is large (and minification won’t help), I will do the optimization.

For example, consider the following case: Let’s suppose you have 3 rules (#foo, #bar and #baz) that will both use the same CSS rotate transformation, among other CSS declarations. Using a mixin is simple (using the LESS syntax in this example):

.rotate (@degrees: 10deg) { -moz-transform: rotate(@degrees); -ms-transform: rotate(@degrees); -o-transform: rotate(@degrees); -webkit-transform: rotate(@degrees); transform: rotate(@degrees); }

#foo { font-size: 150%; .rotate(40deg); }

#bar { background: silver; .rotate(40deg); }

#baz { background: white; .rotate(40deg); }

Sweet, huh? And only 370 bytes. However, what the end user downloads is this beast:

#foo { font-size: 150%; -moz-transform: rotate(40deg); -ms-transform: rotate(40deg); -o-transform: rotate(40deg); -webkit-transform: rotate(40deg); transform: rotate(40deg); }

#bar { background: silver; -moz-transform: rotate(40deg); -ms-transform: rotate(40deg); -o-transform: rotate(40deg); -webkit-transform: rotate(40deg); transform: rotate(40deg); }

#baz { background: white; -moz-transform: rotate(40deg); -ms-transform: rotate(40deg); -o-transform: rotate(40deg); -webkit-transform: rotate(40deg); transform: rotate(40deg); }

which is almost double the filesize (600 bytes). It could have easily been this:

#foo, #bar, #baz { -moz-transform: rotate(40deg); -ms-transform: rotate(40deg); -o-transform: rotate(40deg); -webkit-transform: rotate(40deg); transform: rotate(40deg); }

#foo { font-size: 150%; }

#bar { background: silver; }

#baz { background: white; }

which at 290 bytes, is even smaller than the first one. The differences would be even bigger if you had to specify a different transform-origin.

Of course you can still do such optimizations when using CSS preprocessors, but since you don’t have the ugliness in front of you and the file you’re working with remains small, it’s easy to forget and just do what’s easy. You lose sight of the big picture. But it’s the big picture (or big file, in this case ;)) that your users eventually download.

Same goes for nesting: Instead of actually putting some thought into the selectors you choose, you can just nest and let the preprocessor sort it out, usually in the straightforward but unavoidably verbose way.

LESS is better in this aspect, since it also offers a client-side version, so the user downloads the small file you wrote, and all the expansion is done in their machine. However, this has the (big, IMO) disadvantage that all your CSS becomes dependent on JavaScript to work and that your users have to download the LESS code, which isn’t that small: 33KB minified which is way larger than most stylesheets (granted, if you gzip, it will be smaller, but this is true for stylesheets as well).

Maintenance woes

Eventually, CSS will start supporting all this sweetness. Tab Atkins has already drafted a proposal and soon Webkit nightlies will implement the functionality. After that, I think it’s safe to assume that within 2 years Firefox and Opera will also implement the (by then) standard and within 1-2 more even IE. Then we’ll need another 2-3 years to be able to start using it (adoption rates of new browser versions will have increased too). This means that in as little as 6 years, we might be able to use CSS variables, mixins and nesting in vanilla CSS. All the code written for today’s preprocessors will eventually have to be rewritten. Maybe even sooner, since when a standard is published, I think it’s safe to assume (or hope) that the new versions of CSS preprocessors will deprecate their old syntax and start supporting and recommending the standard way, effectively becoming polyfills (which I definitely support). So, coding for a CSS preprocessor today feels a bit like building castles on sand.

Debugging woes (thanks to Jesper Ek)

Preprocessors make debugging CSS harder, since the CSS you see in Web Inspectors like Firebug or Dragonfly is not the CSS you wrote. The line numbers don’t match any more and the CSS itself is different. A lighter form of the same problem also occurs with minifiers, but you can delay using them until you’re done with the site. With CSS preprocessors, you have to use them from the beginning if you want to really take advantage of them.

Also, when I develop my CSS, I want to be able to instantly preview the changes in the file by just refreshing the browser. With preprocessors this becomes harder (although not impossible).

Generic concerns with such abstractions

With every new syntax, comes more effort required by someone to start working on our code. We either have to only collaborate with people proficient in the CSS preprocessor of our choice, or teach them its syntax. So we are either restricted in our choice of collaborators or need to spend extra time for training, both of which are nuisances.

Also, what happens if the preprocessor stops being updated? Granted, most (if not all) are open source, but the community’s interest might shift to something else. Many open source projects have eventually died due to lack of interest. And let’s not forget the law of leaky abstractions

Yes, both concerns are valid for every framework, in every language, but at least PHP frameworks or JavaScript libraries are more needed than CSS preprocessors, so it’s a tradeoff is that’s worth it. For CSS preprocessors, I’m not so sure.

Conclusion & disclaimer

I have to admit that even though I’ve read quite a bit on CSS preprocessors and talked with fellow web developers about them, I don’t have hands-on experience with them. Maybe I will change my mind if I actually do so. Besides, I think that if someone uses a CSS preprocessor carefully, with knowledge of the points mentioned above, it can actually turn out to be beneficial. However personally, I prefer to wait at least until they start supporting the (future) standard syntax, whenever that happens.


WD @media talk subject change

1 min read 0 comments Report broken page

I recently changed my Web Directions @media talk title & abstract to something more specialized. Instead of discussing under-hyped CSS3 features in general I will only focus on one CSS3 feature (more hyped than the ones I was planning to show, but all the hype is only about very basic use cases): CSS3 Gradients:

Mastering CSS3 Gradients

With most browsers adding increasing support, and the simplicity of providing fallbacks for those that don’t, CSS3 gradients are something we can start to use right now. They benefit our users with faster websites and ourselves with more time in our hands to spend in other things, since they are easy to create, edit and update. A very powerful feature that can also be utilized for a surprising number of design effects, even ones that don’t resemble gradients at all. In this talk, Lea will explore CSS3 gradients in great depth and it’s almost guaranteed that no matter your expertise level, you will walk out having learned new things.

I tested a draft of this talk with a meetup group in Oslo (Framsia) and it went very well. I got reviews like “I was amazed that you managed to speak almost an hour of CSS3 gradients and still keep the crowd interested” (thanks Legendre!). Even Bruce Lawson, who happened to be there, told me he didn’t know like 70% of the material presented! :)

I’m looking forward to it since it’s a topic I’m passionate about, and I hope to see you there! Don’t forget that you can use the coupon code WDVEROU when registering to take £50 off the current price.

PS: I don’t like the title very much, so if you have anything more witty to suggest, feel free. ;)


Custom <select> drop downs with CSS3

2 min read 0 comments Report broken page

The CSS3 Basic UI module defines pointer-events as:

The pointer-events property allows authors to control whether or when an element may be the target of user pointing device (pointer, e.g. mouse) events. This property is used to specify under which circumstance (if any) a pointer event should go “through” an element and target whatever is “underneath” that element instead. This also applies to other “hit testing” behaviors such as dynamic pseudo-classes (:hover, :active, :focus), hyperlinks, and Document.elementFromPoint().

The property was originally SVG-only, but eventually browsers and the W3C adopted a more limited version for HTML elements too.

It can be used in many use cases that weren’t possible before (or the solution was overly complicated), one of them being to create custom-looking <select> drop downs, by overlaying an element over the native drop down arrow (to create the custom one) and disallowing pointer events on it. Here’s a quick example:

-webkit-appearance: none was needed in Webkit to turn off the native OSX appearance (in OSX and maybe Safari on Windows, I didn’t test that). However, since that also removes the native drop down arrow, our custom arrow now obscures part of the text, so we had to add a 30px padding-right to the select element, only in Webkit. You can easily detect if pointer-events is supported via JS and only apply this it if it is (eg by adding or removing a class from the body element):

if(!(‘pointerEvents’ in document.body.style)) { … }

However, there is one caveat in this: Opera does include pointerEvents in HTML elements as well, but it does not actually support the property on HTML. There’s a more elaborate feature detection script here as a Modernizr plugin (but the code is quite short, so you can adapt it to your needs).

Also, don’t try to replicate the behavior in JavaScript for browsers that don’t support this: it’s impossible to open a <select> drop down with JavaScript. Or, to put it differently, if you manage to do it, you’ll probably be the first to. Everything I could think of failed and I spent hours yesterday searching for a way, but no avail.

References


Checkerboard pattern with CSS3

1 min read 0 comments Report broken page

A while ago, I wrote a post on creating simple patterns with CSS3 gradients. A common pattern I was unable to create was that of a regular, non-rotated checkerboard. However, I noticed today that by giving a different background-position to every triangle in the pattern tile, a checkerboard can be easily created:

View in Gecko or Webkit. Webkit seems to have an odd rendering bug, so it needed a background-size override and it still doesn’t look perfect. Oh well, reported the bug and moved on.


Incrementable length values in text fields

1 min read 0 comments Report broken page

I always loved that Firebug and Dragonfly feature that allows you to increment or decrement a <length> value by pressing the up and down keyboard arrows when the caret is over it. I wished my Front Trends slides supported it in the editable examples, it would make presenting so much easier. So, I decided to implement the functionality, to use it in my next talk.

If you still have no idea what I’m talking about, you can see a demo here: View demo

You may configure it so that it only does that when modifiers (alt, ctrl and/or shift) are used by providing a second argument to the constructor and/or change the units supported by filling in the third argument. However, bear in mind that holding down the Shift key will make it increment by ±10 instead of ±1 and that’s not configurable (it would add too much unneeded complexity, I’m not even sure whether it’s a good idea to make the other thing configurable either).

You may download it or fork it from it’s Github repo.

And if you feel creative, you may improve it by fixing an Opera bug I gave up on: When the down arrow is pressed, the caret moves to the end of the string, despite the code telling it not to.


Convert PHP serialized data to Unicode

1 min read 0 comments Report broken page

I recently had to convert a database of a large Greek website from single-byte Greek to Unicode (UTF-8). One of the problems I faced was the stored PHP serialized data: As PHP stores the length of the data (in bytes) inside the serialized string, the stored serialized strings could not be unserialized after the conversion.

I didn’t want anyone to go through the frustration I went through while searching for a solution, so here is a little function I wrote to recount the string lengths, since I couldn’t find anything on this:

function recount_serialized_bytes($text) {
	mb_internal_encoding("UTF-8");
	mb_regex_encoding("UTF-8");

mb_ereg_search_init($text, ‘s:[0-9]+:"’);

$offset = 0;

while(preg_match(‘/s:([0-9]+):"/u’, $text, $matches, PREG_OFFSET_CAPTURE, $offset) || preg_match(‘/s:([0-9]+):"/u’, $text, matches,PREGOFFSETCAPTURE,++offset)) { $number = $matches[1][0]; $pos = $matches[1][1];

digits = strlen(&quot;number"); poschars=mbstrlen(substr(text, 0, $pos)) + 2 + $digits;

str=mbsubstr(text, $pos_chars, $number);

newnumber=strlen(str); newdigits=strlen(new_number);

if($number != $new_number) { // Change stored number text=substrreplace(text, $new_number, $pos, $digits); $pos += $new_digits - $digits; }

$offset = $pos + 2 + $new_number; }

return $text; }

My initial approach was to do it with regular expressions, but the PHP serialized data format is not a regular language and cannot be properly parsed with regular expressions. All approaches fail on edge cases, and I had lots of edge cases in my data (I even had nested serialized strings!).

Note that this will only work when converting from single-byte encoded data, since it assumes the stored lengths are the string lengths in characters. Admittedly, it’s not my best code, it could be optimized in many ways. It was something I had to write quickly and was only going to be used by me in a one-time conversion process. However, it works smoothly and has been tested with lots of different serialized data. I know that not many people will find it useful, but it’s going to be a lifesaver for the few ones that need it.


Styling elements based on sibling count

1 min read 0 comments Report broken page

The original idea belongs to André Luís, but I think it could be improved to be much less verbose.

André’s solution is like this:

/* one item */
li:nth-child(1):nth-last-child(1) {
	width: 100%;
}

/* two items */ li:nth-child(1):nth-last-child(2), li:nth-child(2):nth-last-child(1) { width: 50%; }

/* three items */ li:nth-child(1):nth-last-child(3), li:nth-child(2):nth-last-child(2), li:nth-child(3):nth-last-child(1) { width: 33.3333%; }

/* four items */ li:nth-child(1):nth-last-child(4), li:nth-child(2):nth-last-child(3), li:nth-child(3):nth-last-child(2), li:nth-child(4):nth-last-child(1) { width: 25%; }

It’s based on the relationship between :nth-child and :nth-last-child. As you can see, the number of total rules is O(N) and the number of selectors in every rule is also O(N).

However, what you really want, is to just target the first element. The others can be targeted with just a sibling selector. With my improvement, the number of total rules is still O(N), but the number of selectors in every rule becomes just 2, making this trick practical for far larger numbers of children:

/* one item */ li:first-child:nth-last-child(1) { width: 100%; }

/* two items */ li:first-child:nth-last-child(2), li:first-child:nth-last-child(2) ~ li { width: 50%; }

/* three items */ li:first-child:nth-last-child(3), li:first-child:nth-last-child(3) ~ li { width: 33.3333%; }

/* four items */ li:first-child:nth-last-child(4), li:first-child:nth-last-child(4) ~ li { width: 25%; }

And here’s a fiddle to prove it:

Yes, I know that with Flexbox and the other layout modules, techniques such as these are soon becoming obsolete, but I think they are still useful right now. I’m also aware that you can emulate this particular example with table display modes, but a) Table display modes have other implications that are sometimes undesirable and b) Widths are just an example, you could come up with other ways to style the elements based on their total count, which can’t be emulated by CSS tables.


I’m speaking at @media Web Directions ’11!

1 min read 0 comments Report broken page

Just a quick note to let you know I’m speaking at this year’s @media Web Directions conference, which will take place during May 26–27 in London, UK. I’m very excited about this, since I always considered @media one of the top front-end conferences in the industry :)

The title and abstract of my talk is as follows:

CSS3 at the Outer Rim

By now most of you know how to use the core CSS3 features in your designs to embed custom fonts and easily create rounded corners, drop shadows, and scalable designs with media queries. But there is still a large area of CSS3 that remains unexplored by most web designers and developers. In this talk Lea will present many CSS3 features that are useful but underrated, as well as uncommon ways of utilising the CSS3 features you already know about, in order to do much more with even fewer images and less code.

Although it’s on the design track, I expect it to appeal to both developers and designers.

You can use the coupon code WDVEROU to take £50 off the current price. ;)

Hope to see you there! :D


Yet another redesign

1 min read 0 comments Report broken page

I had grown sick of my previous blog style and its various bugs (since it was put together in just a few hours), so I decided to make a new, more minimalistic one. Best viewed in browsers that support CSS gradients, like Firefox, Safari and Chrome. I also finally got around to making a logo for myself, although I’m not sure I’ll keep it. I also switched to HTML5, using Toolbox as a base.

I want to make a few more changes, but I have to go to sleep sometime :p

I also started using DISQUS for the blog comments. I like it when a blog I read has it (since it offers a few features I find convenient, like comment editing for instance), so I wanted to offer it to my readers too. It’s a shame that in some of their buttons they haven’t added the standard CSS3 border-radius declarations, but only the prefixed proprietary ones, so they’re square in Opera (and probably IE9). I’m fed up with seeing this in websites, TOPSY’s widget also does it. However, their carelessness will backfire soon, when browsers stop supporting the prefixed versions *evil grin*


Checkerboard, striped & other background patterns with CSS3 gradients

2 min read 0 comments Report broken page

Screenshot of the CSS3 patterns I came up withYou’re probably familiar with CSS3 gradients by now, including the closer to the standard Mozilla syntax and the ugly verbose Webkit one. I assume you know how to add multiple color stops, make your gradients angled or create radial gradients. What you might not be aware of, is that CSS3 gradients can be used to create many kinds of commonly needed patterns, including checkered patterns, stripes and more.

View demo (Works in Webkit, Firefox 3.6+, Opera 11.50+ and IE10+)

The main idea behind the technique is the following, taken from the CSS3 Images spec:

If multiple color-stops have the same position, they produce an infinitesimal transition from the one specified first in the rule to the one specified last. In effect, the color suddenly changes at that position rather than smoothly transitioning.

I guess this makes it obvious how to create the tile for the stripes (unless you’ve never created a striped background before, but teaching you this is beyond the scope of this post). For example the gradient for the horizontal stripes is:

background-color: #0ae; background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(.5, rgba(255, 255, 255, .2)), color-stop(.5, transparent), to(transparent)); background-image: -moz-linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent); background-image: -o-linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent); background-image: linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent);

Why transparent instead of the actual colors we want? For flexibility. background-color serves two purposes here: Setting the color of half the stripes and serving as a fallback for browsers that don’t support gradients.

However, without anything else, the tile will occupy the whole container. To control the size of each tile, you can use background-size:

-webkit-background-size: 50px 50px; -moz-background-size: 50px 50px; background-size: 50px 50px;

To create the picnic-style pattern, you just overlay horizontal stripes on vertical stripes.

The hardest one to figure out was the checkered pattern. It consists of two 45° linear gradients and two -45° linear gradients, each containing ¼ of the dark squares. I still haven’t managed to think of a way to create a regular checkerboard (not at 45°) without needing an unacceptably large number of gradients. It will be very easily possible if conical gradients start being supported (currently they’re not even in the spec yet).

Can you think of any other popular patterns that can be created with CSS3 and no images? If so, let me know with a comment. Cheers! :)

Added afterwards: Other patterns

There are far more pattern designs possible with CSS3 gradients than I originally thought. For more details, see this later post.


rgba.php v1.2: Improved URL syntax, now at Github

1 min read 0 comments Report broken page

I wrote the first version of rgba.php as a complement to an article on RGBA that I posted on Februrary 2009. Many people seemed to like the idea and started using it. With their valuable input, I made many changes and released v.1.1 (1.1.1 shortly after I posted the article due to another little fix) on October 2009. More than a year after, quite a lot of people still ask me about it and use it, so I decided to make a github repo for it and release a new version, with a much easier to use syntax for the URL, which lets you just copy and paste the color instead of rewriting it:

background: url(‘rgba.php/rgba(255, 255, 255, 0.3)’); background: rgba(255, 255, 255, 0.3);

instead of:

background: url(‘rgba.php?r=255&g=255&b=255&a=30’); background: rgba(255, 255, 255, 0.3);

I also made a quick about/demo page for it. Enjoy :)


Tag editing UIs

3 min read 0 comments Report broken page

I had to build the edit tags interface for an application I’m working on, so I took a good look at how these are implemented across many popular applications nowadays. It seems there are a few patterns that are used over and over, and I’m unsure which one is the most preferable by users, they all have their advantages and disadvantages. In this post I’m going to describe these patterns and list some of the pros and cons I think they have. For simplicity, I will focus on the tag editing interface itself, ignoring any tag suggestions and other extra features.

Pattern #1: Input field to add new tags, delete button for existing ones

Used by: Wordpress, flickr, foursquareScreenshot of Wordpress' tagging UI

Pros:

  • One click deletion of tags

Cons:

  • Impossible to edit a tag, you have to remove it and add the corrected version
  • Hard to delete many tags at once
  • Disconnected new and existing tags, making it hard to get the bigger picture

foursquare’s implementation was the worst I’ve tested: There’s no (discoverable?) way to delete or edit a tag and when you add one via the text field it doesn’t get cleared which is confusing because it makes it seem like an edit tags field although it’s an add tags field, as I found out the hard way (by creating a “pizza, pasta” tag instead of 2 tags: pizza and pasta).

Pattern #2: One text field to edit, delete or add new tags

Used by: delicious, Google reader, stackoverflow, redditScreenshot of delicious' tagging UI

Pros:

  • Lets the user edit tags too, in addition to adding and deleting
  • Easy to delete many tags at once
  • All tags at one place

Cons:

  • More cumbersome to delete a tag
  • A bit more prone to mistakes than guided interfaces

Pattern #3: Hybrid approach: Text field for all, existing tags seem to be inside and have a delete button

Used by: last.fmScreenshot of last.fm's tagging UI

Pros:

  • All tags in one place
  • One click deletion
  • Easy to delete many tags too

Cons:

  • There’s no editing in last.fm’s implementation, but the pattern easily allows for that, for example by using contentEditable on the tag s

last.fm chooses to implement this by faking the tags being inside an input field: Technically they’re implemented just like in pattern #1 above, with the difference that they visually appear to be inside the same box and every time a user inserts a comma (which is the tag separator) the tag they just typed is removed from the text field and a new link with a delete button is created just before the text field, which is much smaller than it looks.

Which pattern is the best?

As with most UI questions, I don’t think there’s a definite answer to that. It heavily depends on the audience too: A more technically inclined user might be more comfortable with the 2nd approach since it’s the least restrictive one. The average casual internet user might prefer the 3rd approach. I don’t think there’s any case where pattern #1 is better than pattern #3, except when development time is a concern (pattern #1 is a bit easier to implement, although #2 is the easiest of all).

Another pattern?

My initial attempt for the application I’m building was to use a hybrid approach of #2 and #3: When the user clicked on “Edit tags”, the tag container would get a contentEditable attribute and the idea was that every time a comma or any other non-permitted character would be inserted a new tag would be created (or if we were in the middle of one, it would get split into 2). That would have all the advantages of #2 and #3, except one-click deletion. It would also have the advantage that the user is directly editing the interface, which is usually a good idea usability-wise. I hate to admit I gave up on it for the time being, because it proved harder to implement than it seemed and I had to move on, so I went with #2. I might revisit it sometime in the future though if I still think it’s a good idea and nobody has done so by then.