Enhance CSS with Harmony_Style

As Harmony Framework is greatly shaping up (I'm near beta 1), I would like to present you one of its latest component: Harmony_Style. I developed this component a while ago but never published it. Harmony_Parser originally comes from this package. I decided to integrate it into the framework because I think it's a great fit. But what is Harmony_Style ? In few words, it's an enhance version of CSS. It provides new features over traditional stylesheets. The great thing about it, it's that the syntax does not change so it's easy to learn and compatible will all existing stylesheets.

Nested rules

This is probably one of the most annoying missing feature! You often have to write long selectors where it would be so natural to use nested rules. Using Harmony_Style, simply create rules in other rule definition. The parent selector will be prepended to child rules.

ul.my-list {
    list-style-type: none;
    li {
        margin: 20px;
    }
}

Will output:

ul.my-list {
    list-style-type: none;
}
ul.my-list li {
    margin: 20px;
}

Inheritance

When mutliple rules share the same properties, classes can be used. However in some situations, the targeted element does not have the needed class. Inheritance allows you to add all properties of an existing rule to another one. It's done using the < character, followed by a selector pointing to the rule to copy. Multiple selectors can be used by separating them using comas.

.my-class {
    color: red;
}
#my-element-without-my-class < .my-class {
    margin: 20px;
}

Will output:

.my-class {
    color: red;
}
#my-element-without-my-class {
    color: red;
    margin: 20px;
}

You can of course override any properties.

Constants

Constants allow you to store values that you can reuse in your stylesheets. For example, it can be a list of colors, sizes, fonts... Constants won't appear on the final stylesheet. They start using the dollar sign. There are two types of constants: single value and standard rules. The first one stores only one scalar value. It is defined as follow:

$my-font = arial;
$my-color = #FF0000;

The second type behaves the same way as normal rules:

$sizes {
    small: 10px;
    normal: 12px;
    big: 16px;
}

Rule-like constants can also contain nested rules:

$conf {
    sizes {
        small: 10px;
        /* ... */
    }
    colors {
        red: #FF0000;
        /* ... */
    }
}

You can use constants for inheritance and constants can inherit of any other rules. Now the big question is WTF if I can't use these constants in property values? Here comes embedded queries.

Embedded queries

Embedded queries allow you to fetch any value from any rules. It's normal selectors place between square brackets. It can be used in property and at-rules values.

#my-div {
    color: [$conf colors red];
}
#my-2nd-div {
    color: [#my-div color];
}

Property namespace

This is simply some syntaxic sugar. Some properties have what we could called a namespace or prefix, like font or border. These properties have many "sub" properties like font-size or border-right-color. Rather than always typing the whole property name, you can use curly braces to group them.

#my-div {
    border: {
        size: 2px;
        color: red;
        bottom: {
            size: 0px;
        }
    }
}

Will output:

#my-div {
    border-size: 2px;
    border-color: red;
    border-bottom-size: 0px;
}

Not as cool as previous features I have to say but wait for the next one.

Custom at-rules

Harmony_Style allows you to register custom at-rules. The component comes with three rules: @include, @target and @unpack. @include works like @import but the inclusion is resolved server-side. Furthermore, rules from the included stylesheet can be used in embedded queries and with inheritance in the including stylesheet.

@include url("conf.css");
#my-div {
    color: [$conf colors red];
}

@target allows you to target rules based on HTTP headers. Rules nested in the @target will be rendered only if the HTTP header match the filter. @target takes two parameters: the header's name and a regexp to match with its value. The name can be omitted. If so, the "User-Agent" header will be used. The parameters have to be placed just after the @target. The regexp must be placed between slashes. PHP modifiers can be used after the last slash.

/* will be rendered if the browser is Firefox */
@target /firefox/i {
    ul {
        list-style-type: none;
    }
}
/* will be rendered if the browser language is french */
@target Accept-Language /fr/ {
    #flag-fr {
        display: none;
    }
}

Finally the @unpack at-rule can be used to import nested rules in the global scope. It takes only one parameter, the selector of the rule to unpack. Embedded queries can be used to dynamically unpack rules.

$blue {
    div {
        background-color: blue;
    }
}
@unpack $blue;

Will output:

div {
    background-color: blue;
}

We've seen the different features provided in the language. Harmony_Style also allows you to manipulate stylesheets from PHP. The API to load stylesheets is very simple:

$stylesheet = new Harmony_Style();
$stylesheet->load('style.css');
echo $stylesheet->render();

If any error occurs, an exception will be triggered. There is also a loadCss() method to load from a string rather than a file. Both methods can take as second argument an array of parameters. These parameters can be accessed in the stylesheet using the $params constant.

#my-div {
    color: [$params color];
}
$stylesheet->load('style.css', array('color' => 'red'));

The API also provide two methods to query the stylesheet using selectors: querySelector() and querySelectorAll(). The first one returns the first match rule, the second all match rules. You can also query for properties, simply by appending them at the end of the selector. Full documentation about the API will be available with the release of Harmony. Finally, I would like to show an example of a way to use Harmony_Style: creating themes.

$blue {
    div {
        color: blue;
    }
}
$red {
    div {
        color: red;
    }
}
@unpack [$params theme];

In your php code:

$stylesheet = new Harmony_Style();
$stylesheet->load('style.css', array('theme' => $_GET['theme']));
echo $stylesheet->render();

Only rules from $red or $blue will be rendered depending on the GET parameter named theme! Harmony_Style was not part of the Harmony first preview so it's not available for download on the official website. Thus, I created an archive with all the needed files to try it. You can download it by using the link below.

Download Harmony_Style.zip


comments powered by Disqus

15/12/2008