Update CSS-SSPP has been retired in favor of the newer, extensible CSS Cacheer.

Last week while digging around an old /tmp/ directory I came across an abandoned project from late 2005. Inspired by CSS-SSC and pre-processing CSS I had been playing around with the idea of nested selectors.

What do I mean by nested selectors? HTML markup consists of nested elements:

<ul> <li>One</li> <li>Two</li> <li>Three</li> </ul>

To apply a style to the nested list-item we write a style definition with flattened selectors:

ul { list-style: none; } ul li { font-size: 0.9em; }

What if our CSS more closely matched the structure of our HTML?

ul { list-style: none; li { font-size: 0.9em; } }

The benefits in this simplified example might not be immediately apparent.

Most CMSes use highly modularized markup. A 1:1 relationship to markup and styles would simplify navigating and sharing the CSS written for those modules (especially when collaborating in a team environment).

Two years later this still seems like a useful idea so on the flight back from An Event Apart Seattle I revisited it and mixed in a few other ideas.

Constants

In addition to nested selectors, server-side constants are back with a slightly different syntax. Constants can be defined two ways. Individually:

@server constant constantName: constantValue;

or en masse:

@server constants { constantName1: constantValue; constantName2: constantValue; constantName3: constantValue; }

Constant values are retrieved the same way regardless of how they are defined.

a { color: constantName; }

will output:

a { color: constantValue; }

Individually defined constants will be overridden by constants defined en masse. Constant names may only contain alpha-numerics; constant values, anything but a semi-colon.

Variables

Variables work the same way as constants except their values can also be passed in via the query string. Variable names begin with a dollar sign. All variables must be pre-defined with a default.

Set individually:

@server variable $variableName: defaultVariableValue;

or en masse:

@server variables { $variableName1: defaultVariableValue; $variableName2: defaultVariableValue; $variableName3: defaultVariableValue; }

Variable values are retrieved the same way regardless of how they are defined.

body { background-color: $variableName; }

will output:

body { background-color: defaultVariableValue; }

if the variable does not appear in the query string. But add the variable name/value pair to the query string (without the dollar sign) like so:

sample.css?variableName=variableValue

and the previous example will output:

body { background-color: variableValue; }

Variable names may only contain alpha-numerics and must start with a dollar sign, variable values anything but a semi-colon. Don’t forget to encode values included in the query string ( # should be %23 in particular).

When variables are present in the query string caching is not used. The cache is deleted and recreated on every request. I may revisit the caching mechanism in the future to address this inefficiency.

Import

Additional stylesheets can be imported using:

@server import url(stylesheetName.css);

The url value can be un-, single- or double-quoted but cannot contain a closing parenthesis. Imported styles can include nested selectors, constants, variables and bases.

Bases and Based-on

Similar to constants, bases allow you to define many property/value pairs and include them in any style definition using the based-on property.

Defining a base:

@server base baseName { property: value; property: value; property: value; property: value; }

Basing another style on a base:

a { based-on: baseName; }

would result in the following output:

a { property: value; property: value; property: value; property: value; }

In the current version of the Server-side Pre-processor bases cannot contain nested selectors.

Caching and Compression

A processed copy is cached in the /cache/ directory. Comments and extraneous whitespace are stripped during processing. All nested selectors are flattened and grouped so that browsers can understand them.

Inline CSS hacks may cease to function. We’re all using conditional comments anyways, right?

Changes to imported CSS files are not detected by the caching mechanism —doing so would require a “shallow” reprocessing of the file—but you can force a recache by adding ?recache to the parent CSS file.

Usage

The CSS Server-side Pre-processor requires PHP, Apache and mod_rewrite. Simply download the sspp-v002.zip archive and unzip. The archive contains one directory and four files:

.htaccess

cache/ .htaccess cacheer.php

cache.php

Add the contents of the archive to your existing style directory. Change the permissions of the /cache/ directory to 777 . Then have at it.

The CSS Server-side Pre-processor is offered as-is, as always. I’ve built in catches to prevent processing non-css files and files above the style directory but the internets is a clever place. Use at your own risk.

How Nested Selectors Work

Briefly, after importing and applying constants and variables, the nested selectors are transformed into a CSSML that looks something like:

<?xml version="1.0" ?> <css> <rule selector="ul"> <property name="list-style" value="none" /> <property name="margin" value="0" /> <property name="padding" value="0" /> <rule selector="li"> <property name="margin" value="0" /> <property name="padding" value="0" /> </rule> </rule> <rule selector="a"> <property name="color" value="#AB6666" /> <property name="text-decoration" value="none" /> <rule selector=":hover"> <property name="color" value="#710101" /> </rule> </rule> </css>

That XML is then parsed into a DOM and transformed into standard CSS. It isn’t pretty but it gets the job done.

(As you may have noticed in the example above, pseudo-selectors like :hover can be nested inside of the element they refer to. This only applies to pseudo-selectors, not different classes of the same element.)

Updates

v004 adds support for nested child and adjacent selectors. The following:

h1 { font-size: 2.0em; + p { margin-top: 0; } }

generates:

h1 { font-size: 2.0em; } h1 + p { margin-top: 0; }

The current version of CSS Server-side Pre-processor has been downloaded 1804 times.