I'm currently working with a team developing a bunch of web components, supposed to be the basis for a current and also upcoming web applications developed in the customer's company.
I want these components to support color themeing; the first use-case for this being supporting a dark mode.
I have defined the customer's corporate design colors in a file colorpalette.css
(the colors are just an example):
:root {
--color-blue: #004994;
--color-light-blue: #0095db;
--color-light-green: #afca06;
--color-light-grey: #9c9c9c;
--color-green: #51ae31;
--color-grey: #555555;
--color-orange: #f39200;
--color-red: #d40f14;
--color-turquoise: #008c8e;
--color-violet: #b80d78;
--color-yellow: #ffcc00;
--color-white: #ffffff;
--color-black: #000000;
}
I'm making sure all other files using or defining colors only use colors from this file. This leads to a file colors.css
where I start defining different colors per theme:
@import "./colorpalette.css";
:root {
--bg-color-button: var(--color-light-grey);
--text-color-button: var(--color-black);
--border-color-button: var(--color-grey);
}
:root[data-theme=dark] {
--bg-color-button: var(--color-grey);
--text-color-button: var(--color-white);
--border-color-button: var(--color-light-grey);
}
So far, I'm satisfied with this approach.
Now I'm starting to add different semantic versions of these buttons: .btn-danger
, .btn-success
, etc. which are supposed to use color as a means of transporting these semantics.
And here is where I'm uncertain as of which way to pursue to achieve this, as I see 2 alternatives:
Create new variables for these semantics, carrying the semantic in their name:
:root {
--bg-color-button-success: var(--color-light-green);
--text-color-button-success: var(--color-black);
--border-color-button-success: var(--color-green);
}
and use these in the button style definitions:
.btn {
background-color: var(--bg-color-button);
color: var(--text-color-button);
border-color: var(--border-color-button);
}
.btn.btn-success {
background-color: var(--bg-color-button-success);
color: var(--text-color-button-success);
border-color: var(--border-color-button-sucess);
}
colors.css
and button.css
.The other approach could be redefining the existing variables in the specified context:
.btn {
background-color: var(--bg-color-button);
color: var(--text-color-button);
border-color: var(--border-color-button);
}
.btn.btn-success {
--bg-color-button: var(--color-light-green);
--text-color-button: var(--color-black);
--border-color-button: var(--color-green);
}
button.css
.colorpalette.css
and colors.css
.I've tried to sum up the pros and cons I see for both approaches.
Which would be other reasons to prefer one over the other way?
Please consider the following before eventually voting to close:
I know many might see this question as off-topic because it attracts opinionated answers, but I'm sure this question has alot of practical relevance for many developers as css custom properties have only recently become the de-facto standard, which usually results in best practices emerging only slowly. So opinionated answers along with reasons for these opinions are exactly what I'm asking here intentionally.
This is also in no way a duplicate of Best Practices - CSS Theming because that question is from 2010 where css custom properties didn't exist.
In my opinion, the priority requirement of a theme system is easily changing the appearance. So your first solution would be suitable for this case. Let's talk about its cons.
This approach obviously results in a myriad of variables, as we need to define a separate variable for each place where we need to apply a different color.
More variables are needed. Cause they serve different purposes. And when you re-design your theme, it will help you quickly change the appearance without traveling into all the components files. And you only need to care about it just in case re-design the theme. So I think it should not be counted as cons.
If you later want to introduce additional button variations, you'd have to edit both
colors.css
andbutton.css
.
Again, I don't consider this is cons because you just need to add some extra code in 2 files. And these codes will not affect anything that exists before.
But you forgot to mention the biggest con of this approach. It is you need to rewrite the CSS rules. What's happens if we use a gradient background? The code will be something like that:
.btn {
background: linear-gradient(to left, var(--text-color-button-1), var(--text-color-button-2) 50%, var(--text-color-button-3) 75%, var(--text-color-button-4) 75%);
color: var(--text-color-button);
border-color: var(--border-color-button);
}
.btn.btn-success {
background: linear-gradient(to left, var(--text-color-button-success-1), var(--text-color-button-success-2) 50%, var(--text-color-button-success-3) 75%, var(--text-color-button-success-4) 75%);
color: var(--text-color-button-success);
border-color: var(--border-color-button-sucess);
}
It will be a nightmare if we want to change the number 50% to another. So let's combine your two approaches into one, to fix their cons.
This is the solution for those who want to quick scanning:
color.css
--color-red-100: red;
--color-red-500: red;
--color-red-900: red;
--color-green-100: green;
--color-green-500: green;
--color-green-900: green;
--color-bg-button-1: var(--color-red-100);
--color-bg-button-2: var(--color-red-500);
--color-bg-button-3: var(--color-red-900);
--color-bg-button-4: var(--color-red-100);
--color-bg-button-success-1: var(--color-green-100);
--color-bg-button-success-2: var(--color-green-500);
--color-bg-button-success-3: var(--color-green-900);
--color-bg-button-success-4: var(--color-green-100);
button.css
.btn {
background: linear-gradient(to left, var(--text-color-button-1), var(--text-color-button-2) 50%, var(--text-color-button-3) 75%, var(--text-color-button-4) 75%);
}
.btn.btn-success {
--text-color-button-1: var(--color-bg-button-success-1);
--text-color-button-2: var(--color-bg-button-success-2);
--text-color-button-3: var(--color-bg-button-success-3);
--text-color-button-4: var(--color-bg-button-success-4);
}
In this approach:
For me, the best practice should be put in a specific context, so this solution is just fit in case your first priority is easily re-design the theme. If you just want to use CSS variables to easily reuse and don't have any plan to change the value of the variables so the OP's 2nd solution should be picked for fast programming.