Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use custom properties (CSS variables) in bootstrap #26596

Closed
tobi-or-not-tobi opened this issue May 25, 2018 · 76 comments
Closed

Use custom properties (CSS variables) in bootstrap #26596

tobi-or-not-tobi opened this issue May 25, 2018 · 76 comments

Comments

@tobi-or-not-tobi
Copy link

tobi-or-not-tobi commented May 25, 2018

I like sass variables and they're great during buildtime. But projects that need a microfrontend architecture (i.e. webcomponents) tend not to rebuild components. They rather reuse existing components and provide the style context to it.

Using CSS variables will allow to provide runtime configuration for styling, such as colors. CSS variables can work in conjunction of SASS.
I'm happy with a few the first couple of CSS variables in bootstrap, but as already mentioned they're only exposed and not used by bootstrap itself. This means, if a project likes to override these CSS variables at runtime, there's no effect with the bootstrap components themself.

Is there anything against going into the CSS variable direction? Browser support is really great and the fallback option will keep non-supported browsers happy.

I understand this is a big change though. It woudl work like this:

Intialize CSS vars. We use use double-double fallback to ensure we have a fallack and do not need to repeately add the fallback everywhere.

:root {
    --primary: var(--primary-color, #{$primary});
}

Then for each and every component that uses the primary color, we could use the --primary variable instead. For example on a button, it would go like this (just a few lines that i'm using in my sandbox project, so its not so relevant to bootstrap sources):

.btn {
    &-primary {
        background-color: var(--primary);
        border-color: var(--primary);
        &:hover,
        &:not(:disabled):not(.disabled):active {
            background-color: var(--primary-darken);
            border-color: var(--primary-darken);
        }
    }
}

This would allow to change the color at runtime.

@MartijnCuppens
Copy link
Member

Bootstrap supports IE10 & IE11 which don't support CSS Variables (Custom Properties), I'm afraid we can't do this right now.

@tobi-or-not-tobi
Copy link
Author

Understood that support for IE10/11 still is important. What about introducing a fallback. We could have a mixin that would generate both the fallback as well as the custom property.

@mixin customProperty($name, $value) {
  
    // fallback for older browser who do not support custom properties / css variables
    #{$name}: #{$value};

    // create a css variable (this might override the variable on any of the ancestor elements)
    --#{$name}: #{$value};

    // use the css variable on the property
    #{$name}: var(--#{$name}, #{$value});
}

Alternatively we could use postcss but I'm personally not so much in favor of that since it doesn't allow to use those properties outside the root scope.

@MartijnCuppens
Copy link
Member

Could you provide a demo of how you would use that mixin?

@tobi-or-not-tobi
Copy link
Author

Hi @MartijnCuppens,
I'd use it like this:

In a selector call the mixin:

.any-selector {
  @include customProperty('color', #{$primary});
}

This would give the following output, with $primary set to #ffffff;

.any-selector {
  color: #ffffff
  --color: #ffffff;
  color: var(--color, #ffffff);
}

In addition, we could make the fallback optional, by introducing a sass variable.

btw, it's just a quick example.

@MartijnCuppens
Copy link
Member

Hmm, and what is the benefit of that? Aren't you always overwriting the variable?

@tobi-or-not-tobi
Copy link
Author

Custom properties will give a lot of benefits to the code base (you might want to have a look at https://www.youtube.com/watch?v=2an6-WVPuJU), but in the context of this ticket I'm mainly interesting in extendibility. Especially for projects that run in the cloud and should allow to override some variables (i.e. primary color) without a rebuild of the code.

Besides extensibility (which is big word for just configuring the vars), it also allows for easy upgradability.

@andresgalante
Copy link
Collaborator

Hi @tobi-or-not-tobi I love CSS variables, but @MartijnCuppens has a good point, since they are not supported on IE11, it'd be a huge effort to change every declaration to be a mixin, not to mention that we the sass abstraction would get too far away from real CSS in my opinion.

I know this will not solve your usecase, but the first step we are taking towers custom properties is exposing our color pallet as CSS variables:
https://github.com/twbs/bootstrap/blob/v4-dev/scss/_root.scss

@mdo Maybe we can add using css variables as an idea for v5?

@tobi-or-not-tobi
Copy link
Author

Thanks @andresgalante. I'm aware of the variables being exposed (and that's great), but it unfortunately doesn't allow to override them at runtime. The compatibility concerns make sense though.

@normux
Copy link

normux commented May 30, 2018

What about using something like that: https://github.com/malyw/css-vars. Abstraction not too far from CSS, and there is option to generate and load separate file for browsers which do not support CSS variables.

@MartijnCuppens
Copy link
Member

MartijnCuppens commented May 30, 2018

I'm afraid we can't just use a mixin to mimic the exact behaviour of CSS variables. Also bootstrap uses a lot of darken() and lighten() sass functions, which can't be applied to CSS variables. There is also the color-yiq function which determines the text color based on the background-color of buttons (that's why the text color of the warning button is dark on the bootstrap docs) which won't work anymore.

If we find a solution for this, we could think about generously implementing them in v5 only if IE support is dropped.

(btw, sorry for being the party pooper here 😉)

@tobi-or-not-tobi
Copy link
Author

The lack of darken, lighten and color-yig like functions in vanilla CSS doesn't need to be a show stopper here imho. Those functions can and should still be used at build time, and could be in addition exposed as color variants, i.e. --primary-darken.

Library consumers can configure those variants at build time as they're used to, but in case they want to use runtime configuration (or extend without rebuilding), they will be responsible for configuring those custom properties technique that suits them. Javascript comes to mind, but even existing HSL color system would already work. Whenever CSS color-mod comes around, it will be more convenient.

(btw, sorry for being so persistent here 😉 – I understand the concerns, but want to think in solutions before dropping the whole idea altogether)

@tobi-or-not-tobi
Copy link
Author

@NormSukhrob I guess we first need to wait whether bootstrap wants to move into this direction or not. Then the technical implementation can be done, personally I think using an external dependency for such a small thing is a bit of an overkill, but its good inspiration.

@normux
Copy link

normux commented Jun 1, 2018

It was just an example of some flexible solution.

CSS variables great modern feature, and as most popular frontend library, bootstrap shouldn't escape usage of it. IMHO

As suggested, developers could have a choice using vars in runtime or just in build time. Yes, some workarounds or better solutions will be needed, like your suggestion using *-darken or with additional *-darken, *-lighten maps which can be by default computed from darken() and lighten() functions for build time option in separate computed variables file, which can be different for runtime and build time usages. Even for build time usage it will give more control over colors for devs.

I believe, nowadays, with a popularity of frameworks like angular, react and etc., flexibility and dynamic features in great demand.

Hope, bootstrap team will make steps toward CSS variables.

P.S. and very hope that we will not have to wait v5))))))

@herbalite
Copy link

herbalite commented Jun 3, 2018

Replacing lighten() and darken() in SASS could be done with using hsl() colors.

See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl()

And it works from IE8 onwards. https://msdn.microsoft.com/en-us/library/hh781508(v=vs.85).aspx#colors

@yordis
Copy link

yordis commented Jun 5, 2018

@andresgalante would be this part of v4 or actually v5 I am saying because of the label v4 that you added.

For this to be able to make it into production we need to drop support for IE, which I totally support, because few of us still care about IE shouldn't mean we need to be lock down in time until they want to convert or take business decisions (we still having marquee tags in the core browser cores, we will never get there if we wait for them).

@mdo
Copy link
Member

mdo commented Jul 9, 2018

No plans to rearchitect all of the codebase to do this. It'd be a significant investment and involve rewriting just about everything.

@mdo mdo closed this as completed Jul 9, 2018
@yordis
Copy link

yordis commented Jul 9, 2018

Like every single version of Boostrap that you break backwards compatibility and because you rearchitect everything.

Don’t take it personally but it is the truth.

So I don’t see how you could use that argument to be honest. Specially that you don’t need to rearchitect that much (progressive enhancement exists)

@mdo
Copy link
Member

mdo commented Jul 9, 2018

@yordis Last time I rewrote everything, it took me over three years to do it :). My hope for v5 was not to fundamentally change the project, but to do a less disruptive change. My point isn't that this cannot happen, but it would take a breaking change (aka, major) release to do it and a lot of time.

@tobi-or-not-tobi
Copy link
Author

tobi-or-not-tobi commented Jul 9, 2018

@mdo do I get it right that the vision is that CSS custom properties are not for Bootstrap?

The runtime configurability of CSS custom properties over build-time configurability are huge, imho. Webapps are going to change with smaller reusable webcomponents, using view encapsulation that cannot be controlled with a global css. Cloud native applications prefer configurability over extensibility.

Imagine we would change the primary color for an app with a large number of webcomponents. Without CSS custom properties, it means that we need to rebuild and redeploy all the webcomponents (assuming primary color is used everywhere) and clients can no longer use the cached components (which is so important . Moreover, the components are not reusable as the CSS is baken into the component.

Using CSS custom properties is going to make a much better story. Web components can be reused by multiple apps; CSS custom properties do pass the encapsulated shadow DOM by design; changing CSS custom properties do not require a new build, deployment and the components do not need to be evicted from caches.

@yordis
Copy link

yordis commented Jul 9, 2018

@mdo has been a long trip with Bootstrap and being grateful for your work, but I believe you are being stubborn on this one.

CSS Variables are 100% supported on green browsers and the global usage is quite enough for saying that we should start embracing the technology.

Keeping v5 without major updates shouldn’t be the goal, like I said Bootstrap has been breaking changes every major release and everyone is aware of that.

I do not believe that the rearchitecture has to be that bad, specially that must of the source code is well known and written so far.

Progressive enhancement always exists and we could slowly upgrade the framework to be 100% CSS variables with all the good practices in the future.

Sorry, but I can see any valid point for not doing it. I would take the initiative of written the spec of the changes and the roadmap and implement it if you want.

I don’t want to create another Bootstrap and having to deal with marketing the framework. I would love to see you change your mind.

Just put in perspective the implications of not doing this agains your thoughts right now.

@felschr
Copy link

felschr commented Sep 20, 2018

Isn't it possible for you to distribute a 2nd file, something like bootstrap.customproperties.css?
This way it doesn't have to brake any existing websites.

Just give us the option.

@andrew-mykhalchuk
Copy link

Or use something similar to this polyfill - https://github.com/jhildenbiddle/css-vars-ponyfill. I tested it in one of my recent projects and it worked really well. Possibly even load it only in IE 10/11 only to avoid additional JS in modern browsers.

@Hermanya
Copy link

Hermanya commented Dec 4, 2018

Inspired by this issue, I figured that it's possible to take the existing bootstrap CSS, parse all colors, figure out the color palette and replace all colors with variables. I wrote a blog post about it in a little more detail:
https://medium.com/@Hermanhasawish/how-i-repainted-bootstrap-without-sass-d789f41aa74b

@piernik
Copy link

piernik commented Jan 9, 2019

@Hermanya Do You have source scss files?

@Hermanya
Copy link

Hermanya commented Jan 9, 2019

@piernik as I replied on medium, I don't have a source scss file, because I'm not relying on scss to generate the color palette. With this css file from my blog post, you can set a bunch of css variables with color codes generated using a tool like https://hermanya.github.io/palette/ or https://palx.jxnblk.com

@ydmitry
Copy link
Contributor

ydmitry commented Sep 19, 2020

I'll need a POC to understand, I don't think that could work at all for now :/

Please take a look into a pull request #31708

With this strategy - no need to transform all SCSS variables to CSS variables, library users could just easily setup own variants by setting own CSS variables.
We can provide separate _variables-css.scss file with everything enabled as a CSS variables and expressed with CSS expressions, so users can opt-in CSS variables for e.g. font sizes (mentioned before):

// BS variable override
$font-size: var(--my-font-size);

// Create wrapper and variants
:root {
  --font-size: 16px;
}

@media screen (max-width: 320px) {
  --font-size: 13px;
}

Or for variable for specific component:

$toast-background-color: var(--toast-background-color);
$toast-color: var(--toast-color);

.toast {
  --toast-background-color: rgba($white, .85);
  --toast-color: $grey-100;
}

.my-toast-variant {
  --toast-background-color: red;
  --toast-color: white;
}

And it will allow to partially support IE11 for users who have to support it in their applciations. Yes, without variants, but only core part.

@ffoodd
Copy link
Member

ffoodd commented Sep 21, 2020

@ydmitry Thanks for the PR, that makes it clearer! Your point is to use var() in Sass variables and not directly in components, which is really interesting indeed.

Not sure it can be generalized but it'd allow to opt-in for custom properties with something like $enable-custom-properties (or $support-ie11?), as we do with shadows or radii.

Need to think deeper on this but I get your point now, thanks for your patience :)

@ydmitry
Copy link
Contributor

ydmitry commented Sep 26, 2020

Here we can support both specific values without increase of the default Bootstrap css size or enable CSS variables mode for applications those need to change colors runtime: #31753 (Alert). It removes darken usage from styles and moved to variables declaration to be managable. Docs updated too.

@ydmitry
Copy link
Contributor

ydmitry commented Sep 29, 2020

Need a feedback which approach is better for CSS variables in Bootstrap - #31789 or #31753 ?

@darkguy2008
Copy link

Need a feedback which approach is better for CSS variables in Bootstrap - #31789 or #31753 ?

While I'm not a BS developer (but a heavy user), those two PRs are a bit confusing to me, but I'll +1 your method a couple comments above ( #26596 (comment) ) as I feel it adds the flexibility we need. The media queries example was great, if it's intended to work as I'm thinking. Having the flexibility to do it per component or per property is awesome.

Just my 2 cents :)

@ydmitry
Copy link
Contributor

ydmitry commented Sep 30, 2020

@darkguy2008 thank you for feedback!
Well, these pull requests for not trivial case with alert variants:

  1. We have color expressions there, so it's not easy to make them depend on one CSS variable. So e.g. I change only $primary and it also changes primary alert automatically
  2. A variant for each of $theme-colors. So e.g. for night theme need to adapt all of the variants.
    Maybe thats why these PRs can look confusing :). The sense of these PRs is to make alert component more customizable so you can pass here a Sass value, Sass expression, CSS variable, expression from CSS variables or null.
    Maybe need to improve docs in PR to make it more clear

@felixfbecker
Copy link

Gave my feedback here: https://github.com/twbs/bootstrap/pull/30500/files#r497388035

@tabacitu
Copy link

Can I just say that I'm so so happy to see you guys have been making progress on this in the past few weeks - thanks a lot! And thank you @mdo for taking this into consideration again for v5, and trying things out.

Just my 2 cents - the biggest argument that I see thrown around against Bootstrap is that "Websites powered by Bootstrap all look the same". I believe making it dead-simple to change the color palette and sizes for Bootstrap-powered websites, using CSS variables instead of having to recompile everything, will be a game-changer. I believe in time it will render that argument dead. I don't see this as a minor feature, but as something HUGE. The way I see it, it'll make it easier for template developers to do their job. And easier for developers to not use a template at all. Really excited about this 🥳 🤞

@dennisreimann
Copy link

dennisreimann commented Oct 9, 2020

Chiming in here because I love the progress with this and I'm excited to see CSS variables becoming a first-class concept in Bootstrap 5. With this comment I'd like to describe our concrete use case, provide insight into how this can benefit users/devs as well as outline some needs regarding the CSS variables implementation.

Our approach up to now has been to fork Bootstrap v4 and implement this ourselves, using a technique described in this thread already: Programmatically replacing existing colors with our own custom properties.

Use Case

The main reason for this was to enable light/dark mode for our sites and apps. As stated by others already, this made it possible for us to provide one general bootstrap.css file and a separate files containing only the variable definitions.

In our case it would be possible to even merge these two files, but we have these separate as we are also using the variable file on sites without Bootstrap. Keeping them separate would also allow for one file per "theme" and loading/swapping in only the chosen theme, as other sites are doing it.

The fork and replace approach works and provides great flexibility, but having this as a native feature inside Bootstrap would be even greater.

Colors

The main hurdle has already been described in other comments: Providing the color variations, which are generated via SASS functions (e.g. lighten or darken). We've circumvented this by introducing separate custom properties.

Common

Depending on how far one wants to go there might be more colors per concept (concept being something like "primary", "warning" or "light" here), but we've come a good way with the main color plus three variations. Here's an example with our primary palette:

/* variables */
--btcpay-color-primary: var(--btcpay-brand-primary);
--btcpay-color-primary-accent: var(--btcpay-brand-tertiary);
--btcpay-color-primary-backdrop: #D2E5CF;
--btcpay-color-primary-text: var(--btcpay-color-neutral-900);

/* sample from usage in alert */
.alert-primary {
  color: var(--btcpay-color-primary-text, var(--btcpay-color-white));
  background-color: var(--btcpay-color-primary-backdrop);
  border-color: var(--btcpay-color-primary-backdrop); }
  .alert-primary hr {
    border-top-color: var(--btcpay-color-primary-accent); }

Component-level

Based on this one could go even further and provide component-level properties, which by default leverage the common ones. It's great to see Bootstrap taking this direction, as it allows for even more flexibilty.

A concrete use case for us would be to tweak e.g. the button colors just in dark mode, by setting them via --bs-button-primary-background (made that up) in dark mode and leaving the defaults as it is in light mode.

Right now, we are solving this via CSS overrides, luckily as these cases are rare.

Playground

We have a Bootstrap kitchensink page where you can see this in action.
Use the button in the top-right corner to toggle between light and dark mode.

light-dark

As you can see we e.g. inverted the light and dark color, depending on which mode you are in.

The power of this approach is being able to easily switch between these modes by just providing a different set of variables (either via separate files or like here via :root and :root[data-theme selectors). This frees us from generating a whole bootstrap.css per theme and saves the users bandwidth.

@ydmitry
Copy link
Contributor

ydmitry commented Nov 7, 2020

I think when add CSS variables need to have some list of features on which we are focusing:

  1. Better code organization
  2. Simplier variants generation
  3. Run time theme change

From the examples provided by Bootstrap teams I see 1st and partially 2nd goals. Am I right? Are there any plans to support more extended 2nd and 3rd features?

@septatrix
Copy link
Contributor

Now that css variable are exposed (https://getbootstrap.com/docs/5.0/customize/css-variables/#root-variables) I thought this would also mean that classes like btn-success would inherit that color. However it seems like that is not the case. Is this still planned for v5.x or rather put off to v6?

@felixfbecker
Copy link

I just saw that the Bootstrap 5 beta is released and with that no more features go into v5. I assume that means v5 will not have support for CSS vars and theming. That's very disappointing. There was progress on this issue, so I kind of assumed it would be a big part of v5.

@darkguy2008
Copy link

@felixfbecker well, it's still in Beta. I would bet that they will make it for the final version, that's what we all are hoping for anyways as it is/was one of the big news for V5...

@felixfbecker
Copy link

With our first beta release of Bootstrap 5, we’re calling it on new features and breaking changes. From here on out, we’re only fine-tuning features, bugs, and documentation on our way to a stable v5 release. Woohoo!

https://blog.getbootstrap.com/2020/12/07/bootstrap-5-beta-1/

@darkguy2008
Copy link

With our first beta release of Bootstrap 5, we’re calling it on new features and breaking changes. From here on out, we’re only fine-tuning features, bugs, and documentation on our way to a stable v5 release. Woohoo!

https://blog.getbootstrap.com/2020/12/07/bootstrap-5-beta-1/

Thanks for the link. Well, then it's a major disappointment indeed. It's sad to see that one of the biggest, most useful features (way more than RTL, popper.js v2 and tooltip positioning...) is still being left off for a next version. So much for listening to the community! :/

@septatrix
Copy link
Contributor

Not all hope is lost. While I agree that I will most likely not ship with v5.0 I can very well see this implemented as a backwards compatible feature in v5.1.

Styles which are currently implemented like: background-color: #00ff00 could definitiv be replaced by background-color: var(--bs-success). Supporting styles which currently use SCSS calculations could also still be used by introducing another layer of indirection.

@mdo
Copy link
Member

mdo commented Dec 10, 2020

Dropping #32424 as a first step to getting more CSS vars inside v5.

The goal was never to go full-in on CSS vars for v5. Up until a few months ago we had planned on supporting IE11, which doesn't allow CSS variables. Now that things are settling with the beta process, we can look for ways to iterate. #32424 is an example of what I think we should be looking to do in v5:

  • Increase the number of :root variables
  • Reassign some Sass variables to their CSS variable equivalents (with the CSS vars using the Sass vars behind the scenes)
  • Increasingly turn to CSS vars for customization

That's not a perfect solution or formula, but it's been the direction I've planned on going since we first dropped IE11. Hope that helps, and please let the feedback (continue to) rip!

@septatrix
Copy link
Contributor

That already sound fantastic. Good to get the stance of someone from the devteam towards this!
I just commented on #31538 (comment) about what possibilities I personally envision from these changes.

Keep up the great work. v5 already looks great and increasing the CSS var adoption throughout the next few minor releases would make it even better. Personally I hope you focus on colors regarding the usage of CSS vars as I think that is where people will be most excited for them.

@mdo
Copy link
Member

mdo commented Sep 6, 2021

This process has started with v5.1.0's release with new :root CSS level variables and updated text and background utilities.

Closing this out as we'll continue to iterate and add more CSS vars by component. For example: #34622, #34443, and #34600.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests