Reducing CSS size by removing unused style class names. Objectos 0.5.2 released
Welcome to Objectos Weekly issue #019.
I have released Objectos 0.5.2! Like last week's release, this release contains almost no user facing changes; most of the changes were internal. Here are the list of changes.
In any case, I will show you what I have been working on.
Let's begin.
Before we begin
I use Objectos in production. This very page was generated using Objectos HTML (and a few other Objectos libraries).
Having said that, please know that Objectos is alpha software. In particular:
-
it is far from being stable: deviate slightly from the shown use-case and it will probably fail;
-
API might change substantially between minor/patch releases; and
-
documentation is a work in progress.
Reducing CSS size by removing unused style class names
Objectos HTML provides an API for consuming (or processing) a HTML template. Objectos HTML itself uses it to, for example, generate the formatted HTML file.
There are other use-cases for the API. You can use it to, for example:
-
visit all of the declared
class
attributes of your HTML; and -
collect all of the distinct style class names used in this particular HTML.
With this information you can, e.g., reduce the size of your CSS by removing any unused style rule.
This is how this page is generated, in fact. This is also how the documentation pages are generated. Have a look at the GitHub repository.
Visiting all of the declared style
attributes
As mentioned, Objectos HTML provides an API for processing a HTML template. I am currently working on making this API easier to use. So please be warned that there will be breaking changes, relative to the code shown here, in the upcoming releases.
The current API uses a "push" model.
To consume a template, you implement a Visitor
interface.
Then all events, such as the start or end of a HTML element, are "pushed" to your visitor.
I am currently working to provide a (pseudo-)DOM API instead.
Next is an example on how to collect all of the distinct style class names in a HTML template. It uses the current "push" API:
import java.util.Set;
import java.util.TreeSet;
import objectos.html.tmpl.AttributeName;
import objectos.html.tmpl.StandardAttributeName;
public final class DistinctClassNames extends SimpleVisitor {
private boolean collect;
private final Set<String> names = new TreeSet<>();
@Override
public final void attribute(AttributeName name) {
if (name == StandardAttributeName.CLASS) {
collect = true;
}
}
@Override
public final void attributeFirstValue(String value) {
if (collect) {
names.add(value);
}
}
@Override
public final void attributeNextValue(String value) {
attributeFirstValue(value);
}
@Override
public final void attributeValueEnd() {
collect = false;
}
@Override
public final void documentEnd() {
for (var name : names) {
System.out.println(name);
}
}
@Override
public final void documentStart() {
collect = false;
names.clear();
}
}
So, when the HTML document starts, we reset our state:
@Override
public final void documentStart() {
collect = false;
names.clear();
}
When we visit a class
attribute, we enable the collecting:
@Override
public final void attribute(AttributeName name) {
if (name == StandardAttributeName.CLASS) {
collect = true;
}
}
And we store all of the values of this particular attribute:
@Override
public final void attributeFirstValue(String value) {
if (collect) {
names.add(value);
}
}
@Override
public final void attributeNextValue(String value) {
attributeFirstValue(value);
}
We stop collecting when the attribute ends:
@Override
public final void attributeValueEnd() {
collect = false;
}
We set collect
to false
regardless of its previous state.
Finally, when the document ends, we print all of the collected values:
@Override
public final void documentEnd() {
for (var name : names) {
System.out.println(name);
}
}
As we are using a TreeSet
the printed result will be sorted.
Multiple attribute values?
You may have noticed that, in the previous example, one can visit multiple attribute values.
Just know that, in Objectos HTML, declaring a div
like so:
div(
className("a"),
className("b"),
h1("Hello world!")
);
Generates the following HTML:
<div class="a b">
<h1>Hello world!</h1>
</div>
And generates two 'attribute value' events to the visitor like so:
visitor.attribute(StandardAttributeName.CLASS);
visitor.attributeFirstValue("a");
visitor.attributeNextValue("b");
visitor.attributeValueEnd();
Using our visitor
Let's use our visitor in an example. Consider the following Objectos HTML template:
import objectos.html.HtmlTemplate;
public class Example extends HtmlTemplate {
@Override
protected final void definition() {
doctype();
html(
lang("en"),
className("no-js"),
head(
title("Objectos HTML example")
),
body(
h1(
className("font-large"),
className("font-sans"),
className("text-bold"),
t("Distinct class names")
),
p(
className("font-sans"),
t("Some "),
em(className("text-bold"), t("important")),
t(" info")
)
)
);
}
}
It declares a few class
attributes using the className
instruction.
We can collect all of the distinct style class names using the following code:
public static void main(String... args) {
var sink = new HtmlSink();
var tmpl = new Example();
var distinct = new DistinctClassNames();
sink.toVisitor(tmpl, distinct);
}
When we run this program it prints:
font-large
font-sans
no-js
text-bold
Which are all of the distinct style class names in the HTML document.
Generating the reduced CSS
We now have all of the distinct style class names used in a particular HTML file. We can use this information to filter out style rules from the CSS file.
This blog and the Objectos documentation site uses Objectos CSS. It supports (or shall support) this use-case out-of-the-box.
Objectos CSS is not publicly released yet. But I expect to release it later this year. In the meantime, the source code is available at the GitHub incubator repository.
Until the next issue of Objectos Weekly
So that's it for today. I hope you enjoyed reading.
The source code of all of the examples are in this GitHub repository.
Please send me an e-mail if you have comments, questions or corrections regarding this post.