Some Disjointed Thoughts on Styling

I’ve been thinking a lot about the problems we have with styling and modularity. For those unaware, CSS (and even to a greater degree Less) are sort of a square hole / round peg problem. They don’t fit nicely with the modlet structure we all love. Both were designed with the idea of styling a page, not styling a component. This is the root source of our problems.

I don’t have a firm solution in mind at the moment, when I do I’ll probably put that here. But I thought why not share with the community some of my ideas anyways! Maybe someone will have some better ideas than mine.

These are scattered thoughts so please forgive me.

Compile time vs. runtime variables

Less and Sass and probably every other compile-to-css language uses compile-time variables, mixins, and functions. Less and Sass lets you define variables/mixins and functions in the same files that contain styling rules.

This is hard for us, in order to compile styles we need those variables/mixins at compile time, so you need to import them. But if you import them you might also be importing styling rules. So you can quite easily wind up with duplicate styling rules and all other sorts of messes.

CSS doesn’t support mixins or functions (yet). It does support variables though. CSS variables are awesome! You can change a variable at runtime to retheme your application on the fly. That’s amazing!

What’s even more amazing is that CSS doesn’t get “compiled” so it doesn’t matter if the variable exists in the file where you use it:

.sidebar {
  color: var(--main-fg-color, #fff);
}

CSS doesn’t care if you defined --main-fg-color or not, it will just ignore it if not.

This makes CSS very appealing to people who like modlets. The downside is that many browsers don’t CSS variables yet.

That’s ok, though, you can have compile them in your production build process. Modlets is about development, if you can get the benefit of modlets during development it’s not so bad if dev doesn’t work perfectly well in IE11.

The downside is that mixins and functions aren’t there yet. Mixins (@apply rules) are being worked on so there’s hope!

My point of this section is that I favor a CSS-based solution (maybe one that extends CSS) over choosing a compile-to-css language.

What is a modlet’s “main”?

StealJS and just about every other solution out there treats JavaScript as the lower common denominator with regard to web assets. I think this is wrong. HTML is the lowest common denominator. HTML is the format that can load all other formats that a browser recognizes. That’s why I think HTML Imports make a lot of sense. A HTML import file might look something like this:

<link rel="stylesheet" href="./tabs.css">
<script type="text/stache" id="myTemplate">
  <div class="foo">
    {{bar}}
  </div>
</script>
<script type="steal-module">
  import Component from "can-component";
  import stache from "can-stache";

  Component.extend({
    tag: "my-tabs",
    view: stache.from("myTemplate"),
    ...
  });
</script>

An HTML file that is your modlet’s main could contain it’s JavaScript (or link to it), it’s CSS (or link to it) and its Stache (or link to it). We have something like this today with done-component but it’s not nearly as powerful as you can’t include arbitrary HTML or reference the HTML in that file using document.querySelector and friends.

The argument against HTML Imports is that they are only supported by Chrome but I don’t find that to be a convincing argument given that importing CSS in JavaScript isn’t supported by any browser; it’s something we implement ourselves. Given that we have to extend the module system anyways, we might as well choose the right format, and HTML is a better choice than JavaScript for modlets.

An alternative idea would be defining a manifest file for a modlet, something like this:

{
  "script": "./tabs.js",
  "style": "./tabs.css"
}

When perhaps you could load from other files like:

import "~/components/tabs.json!modlet"

Or something to that effect. The downside here is that writing a manifest file is a burden on developers.

Why does the choice of modlet’s main matter? Are you talking about in terms of generator choices? That seems like a separate topic?

It takes the responsibility off of Steal for loading CSS if you are using <link> instead, the browser would be loading it like the browser always does. So it would eliminate the expectation that a lot of people have that, for example, @import in CSS works like import in JavaScript.

Personally, I wouldn’t want steal to lose that responsibility. Building CSS for production is as important as building JavaScript. Especially as we steer people into modlets.

I haven’t explained myself very well, I don’t mean to imply that steal-tools shouldn’t bundle CSS.

Let’s take a step back and look at a perception problem that Steal has. People think that they can’t use Sass with Steal unless a Steal plugin is built for it (which is a hard problem because it’s not built for browser usage in mind). This isn’t true at all, you can compile your Sass to CSS and load the CSS through Steal. Similarly people think that they can’t use Babel plugins with StealJS, and for the same reason as with Sass, it’s possible.

So something I struggle with is, how do we deal with tooling that doesn’t work well in the browser. One choice is to try and support everything. In my opinion this is a losing battle. But we should still try to do our best. But when it comes down to it, if we force people to choose between Sass and Steal, or Babel and Steal, or TypeScript or Steal, Steal is going to lose that battle most of the time.

So we need a strategy for how we deal with this. In my opinion the best way to tackle this is to make Steal be able the lowest levels of the stack. Steal should be, primarily, about the resources that the browser can load. That is, JavaScript, CSS, HTML, images, fonts, etc.

So, to me HTML modules mean that everything that’s loadable by a browser can be part of a modlet. We get CSS for free. Everything else, like preprocessors for CSS / JS, are add-ons.

Sorry to come in to this so late, but are you saying you want to move the responsibility of transpiling JS (TypeScript -> js, Babel js -> browserjs) and CSS (less -> css, sass -> css) out of steal?

So steal would just be responsible for loading html, images, css and js and all the other stuff, like transpiling and such, should be done before loading the resources?

I wouldn’t go that far, I just want to make sure we have a good strategy to support transpiled code that is transpiled outside of Steal. So that developers don’t feel shackled by the constraints of Steal/the browser.