Bringing CSS to the Next Level

Should we control markup and logic with CSS…? Provoking article incoming!

Donald Pipowitch
5 min readApr 22, 2014

Recently I read an interesting article by acko.net about HTML and CSS and why he thinks that the DOM is broken. Besides an interesting alternative to the current usage of the <math> element he made this very interesting statement:

[W]hy should a table have a font size? Only the text inside the table can have a font size, the table is just a box with layout that contains other boxes. Why don’t we write table text { size: 16px; } instead of table { font-size: 16px; }? Text nodes exist today.

Well because that’s how HTML’s <font> tag worked. Instead of just making a selector for text nodes, they gave all the other elements font properties. They didn’t get rid of font tags, they made them invisible and put one inside each DOM node.

And he uses this example to illustrate his statement:

<html><font>
<body><font>
<h1><font>Hello World</font></h1>
<p><font>Welcome to the future.</font></p>
</font></body>
</font></html>

Let this sink for a while… I don’t know if this is perfectly correct from a technical point of view, but it sounds very logical and helps to understand how CSS works. But what does that mean? Well, it means that CSS alters the DOM behind the scenes. At least it adds new content to the existing DOM. That shouldn’t sound very new as we do this on a daily basis with :before and :after, but you wouldn’t expect it here.

I searched for another example and stopped at the CSS properties counter-reset and counter-numbering:

body {
counter-reset: chapter; /* Create a chapter counter scope */
}
h1:before {
content: “Chapter “ counter(chapter) “. “;
counter-increment: chapter; /* Add 1 to chapter */
}
h1 {
counter-reset: section; /* Set section to 0 */
}
h2:before {
content: counter(chapter) “.” counter(section) “ “;
counter-increment: section;
}

(Source)

What happens here? We aren’t only altering the DOM, we are also using some obscure logic via CSS. This is what happens behind the scenes:

                    <!-- ”chapter” counter|”section” counter -->
<body> <!-- {chapter=0 | -->
<h1>About CSS</h1> <!-- chapter++ (=1) | {section=0 -->
<h2>CSS 2</h2> <!-- | section++ (=1) -->
<h2>CSS 2.1</h2> <!-- | section++ (=2) -->
<h1>Style</h1> <!-- chapter++ (=2) |}{ section=0 -->
</body> <!-- | } -->

(Source)

If we control some markup and logic with CSS, that means we are using HTML and JavaScript through CSS, right? Nearly.
Think about this: If table { font-size: 16px; } puts a hidden <font> element inside every <table> element, maybe counter-increment appends a hidden jQuery plugin called counter to your <body> element! (But please don’t take this literally.)
It could look like this:

<body>
<script>
// counter prepends some template before h1 and h2
jQuery(‘h1').counter({
content: 'Chapter {{ counter(chapter) }}. ',
increment: ‘chapter’,
reset: ‘body’
});
jQuery(‘h2').counter({
content: '{{ counter(chapter) }}.{{ counter(section) }} ',
increment: ‘section’,
reset: ‘h1'
});
</script>
</body>

Some questions arise:

  1. Why is this even allowed? Isn’t CSS just for styles?
  2. Why am I allowed to inspect :after/:before elements (or Shadow DOM), but not the logic behind this?
  3. Any why can’t I use this API for my own jQuery plugins?!

Haven’t we all written jQuery plugins using some passed configuration objects before? Why can’t we use CSS for configuration like CSS counters?
I don’t say that this is good or bad, but we as web developers should be allowed to do the same as browser vendors. You can decide yourself, if this is good or not. (Probably not.) But I definitely say, that I would at least want to try that out for myself! At least we are the web developers who want to move the web forward. Who want an extensible web with Web Components all over the place!
Even if you would never use such code in production, it would be very useful for web developers to test future specs and create polyfills for these.

Lets forget that CSS is for styles for a moment. Lets treat CSS as a YAML-like configuration file with knowledge about the DOM and selectors. Would that even be used by anyone? Sure!
Not just browser vendors use this, but look into GitHubs Atom editor and how the configure keymaps. They use“DOM-aware” “CSON or JSON files”. That would certainly work with CSS syntax which are DOM-aware by default. Why did they have to use CSON for this? Because we don’t have the APIs to read and write custom CSS properties (yet)!

Maybe this wasn’t the best example, so let us move forward and switch from jQuery examples to Angular examples. This is a timepicker from AngularStrap:

<input type=”text”
ng-model=”selectedTimeAsNumber”
data-time-format=”HH:mm”
bs-timepicker>

Why can’t we remove the data-time-format attribute from the <input> element and put it into our CSS?

input[bs-timepicker] {
data-time-format: ‘HH:mm’;
}

We could configure all our timepickers in this way with a single line of code instead of using an attribute on every single timepicker element. (Maybe our timepicker directive lets us set default values, but what if not? Or if we would like to use a more complicated selector like .special-page input[bs-timepicker]?)

Another example: This time we don’t use a timepicker, but a simple <current-date> directive which does nothing but display current date. Than we could do this:

custom-time {
/* 9/3/10 */
data-time-format: ‘M/d/yy’;
}
@media (min-width: 480px) {
custom-time {
/* Friday, September 3, 2010 */
data-time-format: ‘EEEE, MMMM d,y’;
}
}

Boom! We get responsive configuration for directives for free just by utilizing the conventions which already exist. Isn’t that great? And it shouldn’t look to unfamiliar to place configuration like this into the CSS. What’s the difference in using

custom-time {
data-time-format: ‘M/d/yy’;
}

or

ol {
list-style-type: decimal;
}

? We just set some formatting option…

And if we would write <current-date data-time-format=”MMM d, y”> somewhere in our markup, it would override the settings inside the CSS just like inline declared styles would.

I said “Lets forget that CSS is for styles for a moment.” before. We could basically do that already. Ever heard of the CSS property pointer-events? It has almost nothing to do with styles. It basically converts something like

/* CSS */
p {
pointer-events: none;
}

to something like

// JavaScript, pseudo code
jQuery(‘p’).on(‘click touch mousemove’, function(e) {
e.preventDefault();
});

So, what do you think? Does this look useful? Maybe not attractive at the first glance, but definitely useful.

So please browser vendors:

  • Let me use custom CSS properties.
  • Let me inspect the logic behind custom and builtin CSS properties.

And dear frameworks like Angular, jQuery or Polymer:

  • Let me configure your directives, plugins or Web Components via CSS properties.

And now I await angry comments for these blasphemous thoughts…

--

--

Donald Pipowitch

117% ginger ;) Interested in web technology and OSS. Not a native speaker —feel free to correct typos :)