Let's review the main use case you present "I want to tweak all buttons on the site, I tweak it in one place":
- How many times do I have to change all the buttons on my app?
- Does changing one class work to change all buttons in practice? Don't I end up having the color overloaded 3 times, cascading over and leaking in 5 others spots?
- Are most of my edits in CSS tweaking existing rules, or piling up more CSS? Usually, in a large codebase, I end up just adding a class instead of tweaking one and figuring out if I broke anything anywhere.
- If I use a component-based approach, like Angular or React, do I really have to change classes everywhere with functional css, or just in the button component?
- Is styling "functional"? Can I always change the styling of a class without changing its semantics - like if I put the primary button the same as the secondary, did the naming help?
Your case may be different and our point of view may also evolve. With functional CSS + component-based approach, I rarely have to use the style inspector - and produce beautiful, maintainable app with 14KB of CSS and no styling bug. All my other attempts using CSS "the right way" lead me to 1MB+ of CSS.
The benefits of CSS to factor style by function seems very infrequent compared to the problems they introduce.
When making a styling change like this, it's typical to change it app-wide.
> - Does changing one class work to change all buttons in practice? Don't I end up having the color overloaded 3 times, cascading over and leaking in 5 others spots?
I can change the border-radius on all of my buttons, and they will all change, and still have their color-specific classes.
> - Are most of my edits in CSS tweaking existing rules, or piling up more CSS? Usually, in a large codebase, I end up just adding a class instead of tweaking one and figuring out if I broke anything anywhere.
This is unfortunately quite common in practice - because maintenance is hard and practices are bad. But it is definitely possible to have robust, reusable css.
> - If I use a component-based approach, like Angular or React, do I really have to change classes everywhere with functional css, or just in the button component?
Phew! A component just for a button. What a twist! I don't think most people are making components for each html element. In fact, I'd say this is pretty redundant and overengineering things in a bad way. Guess what - html has these nifty components called buttons too, and you can also style them. Wouldn't any minor change to your button component require then passing in props (just like classes)? <Button propName={"danger"}><button></button></Button> doesn't really seem better than <button class="btn--danger"></button> to me.
> - Is styling "functional"? Can I always change the styling of a class without changing its semantics - like if I put the primary button the same as the secondary, did the naming help?
Not really sure what you're getting at here.
We use custom components like this at work, and it actually works quite nicely because you get to see all the different "types" of buttons at a glance by looking at the prop types. Using the component looks something like this:
<Button label="Click me!" primary />
I've found that it's helpful for enforcing consistency, so that you have to go out of your way to apply custom styles to a button (which isn't required 95% of the time). Plus, it helps abstract things that don't have anything to do with the <button> HTML element but are often tied together, like being able to automatically make a button a link without having to wrap it in an <a> tag:
<Button label="Home" href="/" />
It's all minor stuff, but it enforces consistency and keeps you from having to think about CSS classes at all most of the time.
Welcome to the world of web components. This is actually really common, especially in React with css modules - this way you can use your custom button without even having to think about the CSS (or any extra complexity like tooltips and accessibility), even from a common library used across projects.
1. it does actually happen but more often than not, individual buttons need some changes.
2. absolutely but that's why we use SCSS to help "cascade" those changes. Unfortunately, those large changes, again, require individual tweaks in some tricky areas.
3. adding classes is always the safer approach. One thing that has helped us is to make sure classes hold little (but valuable!) responsibility.
4. this is an awesome question. So, for us, those little adjustments specified in the post (small margins and so on) were tweaked at component-CSS level while large-changes were kept to our SCSS framework
5. It's basically CSS-in-HTML
So my own experience has taught me a general approach of my own:
1. create a framework for your site in SCSS. This includes basic styles for buttons, input boxes, colors, typography, tables etc. but stay away from more complicated styles (like trying to create CSS for a "profile card")
2. keep "component" CSS inside a component. Profile card CSS goes in profile.component.scss
3. overrides/hacks belong to where those overrides/hacks need to happen.
It's very easy to just make another CSS class everywhere, but that defeats the point of CSS and you might as well just use inline styles.
Clean well maintained CSS makes a huge difference. The problem is your CSS needs to map to semantic meaning of what's going on, and you need to maintain the effort to keep it clean.
> How many times do I have to change all the buttons on my app?
How about how often do you need to change all your 'submit' buttons? The HTML element type is again vastly less important than what each element means.
I had some reservations at first but in practice it's been pretty much universally agreed to be great for us. It removes a huge amounts of css cruft that happens over time (unable to determine which classes are still being used, where, etc), keeps colors, margins, etc. standardized through a centralized config file and prevents you from constantly bouncing back and forth between style and markup files.
<div class="bg-white
Just accept the fact your HTML contains your styling and go with: <div style="background:white"
There was a time when people thought CSS could be used to style some logical HTML: <div class="card-wrapper"
So the day you want to change the color of your card-wrapper from white to green you can change a line in some CSS and not have to go around all your HTML or end with a <div class="bg-white"> with a green background.CSS frameworks feel like a "let's get all the shit CSS was meant to help remove and just put it in divs".
I will just say theory aside, in practice, this has worked out great for our team. In my 5+ years experience, I've constantly run across the issue of legacy code with thousands of classes, referencing potentially non-existent markup and countless one-off tweaks. You can start with the best intentions and organization in your SASS files but eventually things start to get really crufty. Keeping everything in components and keeping styles in the markup alleviates a lot of that.
You just made me realize this is why I always have a hard time every time I try a CSS frameworks.
It always goes the same way. I'm first amaze by all what they offer, how simple and quick a great UI can be made. Then when I try to use it, there's always something wrong and I hate how it goes and I always stop and goes back to the logical ways to do it entirely in CSS.
I guess I should try some CSS preprocessor, they must support heritage of CSS class I guess.
.button-critical{ .btn .btn-red }
Working with CSS is fairly unglamourous. These functional frameworks don't really appear to be a solution to me. What they've done is basically take CSS and create a DSL that has to squeeze into a `class` attribute, without actually adding any extra value than what you would get from writing raw CSS. You don't really get consistency out of it. Just terseness.
This becomes even more bizarre when you see these frameworks used with preprocessors like postcss, because what you end up with then is basically spaghetti.
.some-button {
@compose mt-2 my-4 p3 border-radius flex align-bottom;
margin-right: 32px;
border-radius-bottom-right: 2rem;
&:disabled {
display: none;
}
}
At what point did all of this become an advantage?> when you get into a situation where one of the buttons needs to have slightly more margin-top than the others, then it's easy to fix
You can have this with semantic classnames. In fact, that's exactly what the "cascading"[1] part of "cascading style sheets" was intended to solve.
In my experience working on a web app that sees +80k DAU, the most difficult updates to our UI are ALWAYS in the cases where somebody wasnt cogniscent of the cascade. Or was, and used it when they shouldn’t have.
IMO, theres a lot of pushback to this technique and not a lot of questions into its benefits, but plenty of questions as to why its “just like this” so “why should I use it.”
I encourage you to consider the benefits of an isolated approach to UI, or perhaps try building something with it and see how the method compares to your current approach.
Or, we can all go on assuming that Airbnb, Facebook, IG, et al, have all opted into this approach for no reason
>when you get into a situation where one of the buttons needs to have slightly more margin-top than the others, then it's easy to fix.
It's easy to fix in "regular" css too: just add style="margin-top: 10px;" to the button.
If this is really a one-off situation, then that is totally fine. Functional CSS feels like over-engineering an already-solved problem.
And this is a great example of that theory. If one had to apply the terms "over" or "under" to describe the engineering of the practice the article describes, it seems like "under engineered" would be the more apt description. This is pure brute force programming and design.
What do you do, when the button markup changes? Let's say you need to make all links open in a new window? Then you still need to change 100 buttons. It's even more common for bigger components, like the ProfileCard example. What if the markup of your ProfileCard changes?
Instead of using CSS to define your components, I suggest using some real reusable components, either using templates or JS frameworks (depending on your use case).
hopefully you'd have a Button component in your preferred UI framework, and you'd change it once there.
That's not really related to "functional CSS" though.
<button
href=".."
class="...."
>Button</button>
And when you start fixing more complex components and HTML edge cases, you will have a lot of fun [0]. This is fine for legacy code. But I don't want to deal with this in modern projects, if I can just change a single file and be sure all components are updated. As you said, this is not related to functional CSS, but it solves many disvantages of it.[0] https://stackoverflow.com/questions/1732348/regex-match-open...
const Button = styled('button', {/* styles here*/});
...and you get deduped atomic classes (plus proper source maps, so the debugging experience is actually pretty similar to vanilla CSS)There are also more transparent ways to leverage existing functional css frameworks. For example, Mithril.js[2] lets you use CSS selectors in templates, so you can do things like:
const Button = 'button.some.atomic.classes';
m(Button) // equivalent to <Button /> in JSX
The problem with `class="button button-with-quirks"` has to do with specificity. Yes, you can edit the base class... but then you also need to go hunting for visual regressions anywhere you have a compound class because who knows what might be overwriting your edit and how. Also, your team needs to be extremely disciplined when dealing with 7 slightly different variations of left margin on a button (and usually they are not).The two most important benefits of functional css that the post doesn't go into that are kinda important to the discussion are these:
- atomic styles can be deduped, making for smaller bundle sizes
- they make it nearly impossible to accrue specificity-related technical debt
Many of the downsides alluded in the article are solved by libraries like styletron.
<div class="card notice alert-colors">
Warning: frobnitz is dangling. Please address.
</div>
I gather it's because ridiculously literal styles are the only thing you can offer in an "un-opinionated" functional framework, because you'd otherwise have to engage in some styling.ETA: And even "alert-colors" could probably be "alert" or the like, if I saw the consistent need to bold the text or whatever in alerts.
At my job, we have a mid-size React app where more or less every element has one CSS class with all the styling in it. There used to be more of a framework of reusable styles, but it doesn't get used much any more because it's out of date with what our designers want now. We should fix it, but there's no time because we have to pile on more features to fix later. You know the drill, I'm sure.
But basically, for more or less every HTML tag, there's a custom blob of CSS that's only applied once. This is inline styles, we've just moved them to a different set of files.
I think it is more comparable to using function composition, hence the whole "Functional CSS" moniker. The idea is using multiple atomic "functions" (in this case, in the form of classes) to transform an element by composing.
Think like the pipe operator in Elixir:
other_function() |> new_function() |> baz() |> bar() |> foo()
In any programming language, the order in which you compose functions is important.
In CSS — and in keeping with your analogy — the order in which "functions" are defined is important.
Obviously, they are not 100% comparable. CSS is not a programming language.
In programming, `f . g . h` is not the same as `h . f . g`.
In CSS, `<i class="a b c"></i>` is the same as `<i class="b c a"></i>`.
In programming, the order in which you define functions is not important — it's the order in which you use them which is important.
In CSS, it is the exact opposite.
To further drive the point home: assuming the names of these classes aren't lying, what colour is the text of this element?
<div class="text-red text-blue">foo</div>
It depends on the order in which the classes were defined. This also demonstrates why "pure" is a poor choice of word when describing anything to do with CSS. Nothing here is "pure".
I think this is really the core of it, because CSS is kind of bad at it, and just about every project ships with more powerful HTML templating anyway.
The real kicker:
> "I don't want to have to repeat the same 20 classes on every single button." That's understandable. I will say that there's a chance that repeating those 20 classes can actually be somewhat valuable, because when you get into a situation where one of the buttons needs to have slightly more margin-top than the others, then it's easy to fix.
That's just... bizarre, sorry.
It's infinitely better programming practice to use a "button" CSS class, because then every time you want to tweak all buttons on the site, you tweak it in one place. With 100 buttons in different places, can you imagine the maintenance nightmare?
The author suggests creating button HTML templates instead... but why? That's what CSS already does. And when one button needs more margin or whatever other unique tweak... you just add an additional class so it becomes e.g. class="button button-extra-spaced".
For the life of me I can't see any advantages to the "functional CSS" described here at all -- it looks like a nightmare programming practice.