Friday, March 21, 2014

CSS Solar System

So I recently undertook a little project to build a solar system model out of CSS. "Is such a thing even advisable?" is the obvious question. Turns out the internet has already been there in droves:

  • Wow, this is cool.
  • Holy cats, this is neat.

But I had a different idea of making a solar system model that peaks in on the side of a page in the header section, kind of like a company logo. Since the internet had not come up with that exact idea, I went ahead. The one constraint I wanted was to make the design responsive so that the solar system would resize along with the window.

Turns out the answer is just lots and lots of border-radius and position: absolute—same as everything in life:

So, victory? Kind of. Because it took about 250 lines of CSS (with no empties) to effect this. The clincher was getting the planets' and orbit lines' positions right depended on a fair amount of math. A set of equations had to be solved for each planet individually, and the answers hard-coded. I wanted to reduce the amount of CSS, while also making the calculations depend on variables. Then I could easily tweak everything without having to redo 8n calculations every time.

I decided to try out LESS. I dived in and was quickly reducing the line count with just nested rules. The real space-saving came when I replaced the eight individual sets of rules for the planets (sorry, Pluto) with a single loop that solved the equations using variables.

In all the LESS solution took up only 100 lines of code—60% off the CSS. But this bargain takes on a Faustian character when you consider the Less code it took to effect. Stuff like:

.-(@i: 1) when (@i <= length(@planet_names)) {
  @name: e(extract(@planet_names, @i));
  @color: extract(@planet_colors, @i);
  @size: extract(@planet_sizes, @i);
  @orbit-radius: @start-orbit + (@i - 1) * @orbit-increment;
  @orbit-height: @start-orbit-height * @orbit-radius / @start-orbit;
  .planet.@{name} {
    background: fade(@color, @fade-value);
    border-radius: @size;
    height: @size;
    left: @orbit-radius;
    margin-left: -@size / 2;
    position: absolute;
    top: (68px - @size) / 2;
    width: @size;
  .@{name}.orbit {
    height: @orbit-height;
    left: -@orbit-radius;
    position: absolute;
    top: (@height - @orbit-height) / 2;
    width: @orbit-radius * 2;
  .-((@i + 1));
} .-;

Yes, that's really how for loops work. Actually it's not a loop since the Less people did not want to allow mutable l-values, so they made all named entities be constants in their scopes. Thus to get iterative behavior you have to write a recursive mixin that calls itself with the iteration number, and that uses branch execution to trigger the exit condition. And then apparently .- struck them as a really good name for an anonymous mixin, so voilĂ . … No, that doesn't look hacked at all.

Next time I might try out Sass, which combines the hierarchical rules of Less with Jinja-style templating to get variables and loops. Now that makes sense. Templating engines were designed to streamline writing redundant languages using proven programming idioms. Sass uses that to mitigate the tedious bits of CSS, while promoting things like hierarchical rules to the core. ++.

No comments:

Post a Comment