07 May 2014

On the 14th of June 2013 I've attended the advanced CSS conference CSS Day in Amsterdam. I have selected three of the subjects that I found particularly useful and want to discuss here.
A complete summary of this conference is also available on the blog of Mirella van Teulingen.

Border-radius

Lea Verou spoke about some crazy stuff you can do with border-radius. The interactive slides can be found here, but she herself also warns that it is not as useful without having seen the presentation (the video can be found here) because she was live coding some parts. I've added some demos based on her slides below.

Maybe you already know about CSS3 Shapes; a collection of CSS snippets to make basic shapes in pure CSS, e.g. square, triangles, a speechbubble. With border-radius it is possible to make circles, eggs, semi-circles.

/* an example of a semi-ellipse */
border-radius: 50%/100% 100% 0 0;
See the slide on elliptical curves, and press the right arrow key several times. This can be used to make some really fancy-looking buttons, for instance.

Of course it is possible to combine these shapes with CSS transitions to make simple animations. See this slide for an interactive demo or just try hovering over this box:

.anim {
    border-radius: 50%/100% 100% 0 0;
    transition: 0.5s;
}
.anim:hover {
    border-radius: 50%;
    transition: 0.5s;
}

In the future it might be possible to change the shape of the corner. Now it is always a curve, but with corner-shape it would be possible to choose different types like bevel, scoop and notch. Lea Verou provides code to emulate this behaviour with SVG in the browser. Explore this page for the examples.

By the way, if you're very smitten with mr. border-radius, you can find more about his origin here.

Advanced CSS selectors

Bert Bos, who co-created CSS in 1994, talked about CSS selectors. Unfortunately, he didn't really manage to get to the advanced part before the end of his talk, but he does mention it in his slides.

I have also mentioned the basic hierarchical selectors, e.g. all (*), by id, by class, conjunctions and sibling/child dependent (>,+,~) in my previous post). Another common selector is testing the value of an attribute, for example:

input[type="text"]
for selecting all input element with the value "text" for attribute "type".
We can also do more specific matches with ^= (starts with), $= (ends with), *= (contains), but I would like to pay special attention to ~= and |=.

~= matches a value in a whitespace separated value list. This way you can match e.g. a data attribute with multiple values like you would match a CSS class (which can also be whitespace separated).
It is more specific than *= (contains), because <span data-foo="bar batman"></span> would be matched by span[data-foo*=bat] but not by span[data-foo~=bat].

|= is used specifically for unicode language identifiers. lang|=en will match either lang="en" or any value that starts with "en-". Also note that language can be matched directly with :lang(x).
For more information see this MDN page or these examples:

/* <span data-foo="bar baz bat">
   bar baz bat</span>
   <span data-foo="bar batman">
    bar batman</span>
*/
span[data-foo~=bat] {
    background-color: red;
}   

bar baz bat bar batman

/* <span lang="en-us">color
   <span lang="en-gb">colour
   <span lang="nl-nl">kleur */
span[lang|=en] {
    background-color: blue;
}
:lang(nl) {
    background-color: green;
}

color colour kleur

First-line and first-letter are some pseudo elements that might come in handy:

/* first line bold */
p::first-line {
    font-weight: bold;
}

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

/* first letters styled */
p::first-letter {
    font-family: Verdana, serif;
    font-size: 4em;
    font-style: italic;
}

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris

Bert Bos' slides also mention some "missing" selectors. These are not yet implemented and some are even marked as "at risk", i.e. that they might be removed from the spec altogether.

The pseudo elements ::value and ::choices are both features at risk. The purpose of ::value would be to style the content of an input element that is not contained in another element, e.g. the "bar" part of this example:

<input><label>foo</label>bar</input>

To me it seems marginally useful, since it can be avoided by wrapping bar in e.g. a span. On the other hand ::choices can be used to style radio buttons or dropdowns, which seems very useful, but neither pseudo elements are implemented in Firefox 28.0 nor Chrome 34.

Of course, it is still possible to restyle the radio buttons (at least in Firefox and Chrome) by setting the non-standard property "appearance". The following example works particularly well in Chrome:

.custom-radio input {
    -moz-appearance: none;
    -webkit-appearance: none;
    background: white;
    border-radius: 40%;
    border: 1px solid black;
    display: inline-block;
    height: 20px;
    width: 20px;
}
.custom-radio input:checked {
    background: green;
    border: 2px solid black;
    box-shadow: 
        inset 0 0 0 2px white;
}

For completeness, he also mentions the following selectors, although they are either not supported or still in the proposed state:

  • display: run-in (not supported in Firefox and Chrome), see a demo here.
  • ::marker (proposed, see the spec)
  • ::slot(), ::column(), @region (proposed, see the spec)

Flexbox

The talk by Stephen Hay was an update on the confusing state of the flexbox specs. Flexbox (or flexible box layout) is a new display type. Its goal is to provide an easy and intuitive way to define a layout, amongst others making centered elements and dynamically distributing available space. The problem is that there are several "dialects" (old, mid, new) of the flexbox syntax with varying degrees of support in the browsers. It makes it difficult, but not impossible, to make a cross-browser implementation of a flexbox layout.

Let's first take a look at a typical problem that can be solved with flexbox. To center a div both horizontally as well as vertically in non-flexbox CSS, usually requires a wrapper div set to 100% width and using auto margins:

.wrapper {
    width: 100%;
}
.content {
    background: green;
    margin: 0 auto;
    width: 100px;
}
a centered div

With flexbox it would be possible to define a flexbox containing a flex item. The flexbox may be told to center its flex items. It needs no additional CSS on the flex item:

.wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
}
a centered div

As mentioned, there are different dialects, but Chrome, Firefox, Safari en IE10+ all support it in some way. For now, to make the above example work cross-browser, this is needed:

.wrapper {
    /* activate flexbox display type */
    display: -webkit-box;   /* OLD: Safari,  iOS, Android browser, older WebKit browsers.  */
    display: -moz-box;      /* OLD: Firefox (buggy) */
    display: -ms-flexbox;   /* MID: IE 10 */
    display: -webkit-flex;  /* NEW, Chrome 21–28, Safari 6.1+ */
    display: flex;          /* NEW: IE11, Chrome 29+, Opera 12.1+, Firefox 22+ */

    /* horizontally center contained flex items */
    -webkit-box-pack: center; -moz-box-pack: center;
    -ms-flex-pack: center;
    -webkit-justify-content: center;
    justify-content: center;

    /* vertically center contained flex items */
    -webkit-box-align: center; -moz-box-align: center;
    -ms-flex-align: center;
    -webkit-align-items: center;
    align-items: center;
}

If you use Sass or LESS (which I think you should), you can use mixins to make your life easier. Compass provides the compass/css3/box mixin for Sass.

This centering example comes from this article. It also mentions other usecases, such as distributing the available width over elements that are horizontally aligned. Instead of setting a width to fixed or by percentage, you can set the property "flex" to 1, to divide the available width equally over all elements. Now, if you want one of the elements to take up more width, you could set "flex" to 2, so it will take up 2 "flex units". If this seems unclear take a look at this demo.