In my recent posts, I have been exploring the line between useful conventions and enforced dogma in coding. I (I think) coined the terms introjected coding practice, identified coding practice, and integrated coding practice. These terms essentially describe the extent to which a coder believes in the rules that they follow.
The outcome of this thinking has been a new CSS methodology, which provides me with rules that I believe in, whilst addressing the potential problems of maintainability and scalability. This problem feels solved for me at this point. During this process I thought a lot about the two routes I could take towards this solution, abstraction or isolation. Nothing in the end compelled me to move away from my existing preference towards isolation, but perhaps one of the more compelling arguments is the CSS framework.
The position on the line between abstraction and isolation differs from one framework to another. You have some that I would say are somewhere in the middle, like Bootstrap, and Foundation with moderately abstract selectors, such as
.callout. Then there are frameworks that sit at the abstract end, such as Tailwind and Tachyons. These frameworks have selectors such as
.w-1 to specify a width, or
.pa1 to specify padding. My experience with CSS frameworks has been of those that occupy that middle ground. Perhaps as a result, I'm not a huge fan of frameworks. In my experience, you always hit a limit of how far you can bend it to your needs before you are forced to break it. Either that or accept the limitations and make something very generic. The hypothesis that successful approaches to CSS sit at either extreme of the line between abstraction and isolation would suggest that the frameworks that sit at the abstract end offer a more scalable solution. I think that is a reasonable assumption to make. What I'm currently wondering about though, is that other extreme.
Could a CSS framework exist at the isolation end of the line? I don't know of any frameworks that set out to do this. The reason for a lack of this kind of framework seems pretty obvious. Frameworks are all about reusable parts, the opposite of isolation. That said, people do work with frameworks in this way. Some CSS frameworks allows you to access its components via mixins, which opens up the possibility to apply them to multiple, isolated selectors. The problem historically with this was that there was limited scope for working in this way. As I recall only few aspects of Bootstrap, for example, were available as mixins. I can speculate as to why this would be. A mixin is useful for applying the same set of styles to multiple elements; they are simple to use in this way, and don't really require additional documentation. Once you get into complex components, comprised of multiple elements however, then working in this way would suddenly require a lot more knowledge of the framework. To recreate a component, using your own custom selectors would not only require a mixin for each element of that component, but the knowledge of how they relate to each other. You'd not so much be using a framework at this point as refactoring it. I spoke to a colleague who had recently used Bootstrap in this way. The support for mixins is now much better, but his experience confirmed my speculation. You need an intimate knowledge of the framework to use its mixins extensively.
The usefulness of a CSS framework is dependent on how configurable it is. I'd suggest that the further away from abstraction the framework is, the more crucial this becomes. Configuration might be a more viable way to allow for the use of components with custom, isolated selectors. If we wanted to use a moderately abstract component, such as the Bootstrap Card:
<div class="card"><div class="card-body"></div></div>
To apply the styles from this to a less abstract custom selector, let's say
.article-preview. You could provide some sort of configuration that maps the abstract selectors to the custom ones.
.article-preview = .card.article-preview_body = .card-body
This would act as a template for creating the new component, and might be a relatively painless way to gain control over the level of abstraction. Is doing this useful though? To that end, I'm not sure. You could conceivably have
.card applied to multiple isolated selectors, say
.author-bio, then have them diverge later in the cascade, but that's not ideal. I think what you would want is to configure the variables of the component at the time of creating the instance of it. So you would have a config for
article-preview, which would define the selector names, as well as the
card variables. So if one of the features of
card was a border, and you wanted the article previews to have a blue border, you would configure that here. The result would be a new component, essentially isolated to the extent that the core component (card) is configurable. This starts to feel potentially useful, and an interesting idea to explore..