twbs / bootstrap

The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.
https://getbootstrap.com
MIT License
169.96k stars 78.74k forks source link

Use custom properties (CSS variables) in bootstrap #26596

Closed tobi-or-not-tobi closed 3 years ago

tobi-or-not-tobi commented 6 years ago

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.

ajcerejeira commented 3 years ago

Just wanted to say that it would be really great if this was shipped in the next v5 release :crossed_fingers:

To give an example I worked on a Django project that used Bootstrap 4, but we wanted to customize the colours and fonts to match the company branding, so we ended up installing node.js and configuring webpack just to change some SASS variables. It brought a lot of complexity into the project stack just to change a few colours. Now if this goes forward we could simply edit the variables values directly in the CSS without needing all of those extra tools and build step.

ydmitry commented 3 years ago

Regarding some CSS vars in components, is this the direction folks are wanting? See #31685 and #30500.

I would like to see support both SCSS variables mode, e.g. for shorter bundles and projects already using SCSS, and CSS variable mode (for runtime customizations, creating new modifications of components on the fly, for web-components, dark-mode, contrast mode).

This can be achieved if in SCSS styles we use just SCSS variables, but we can set values of this variables to CSS variables:

// Variables (maybe separate file of variables with CSS vars mode
$color: var(--color);

// SCSS styles
.btn {
  color: $color;
}

Additional benefit: I can just remove this style by setting $color: null, which will decrease CSS size even more.

ffoodd commented 3 years ago

@ydmitry I'm not sure to understand. It doesn't seem possible this way, since the main benefit of custom properties is to define their values on a component basis, eg. --bs-table-striped-color on .table.

What would happen to the custom props declarations, with your suggestion? How'd you drop them, and replace them with their targetted properties to ensure the same end result?

Current styles for tables:

.table {
  --bs-table-bg: transparent;
  --bs-table-striped-color: #212529;
}

.table > :not(caption) > * > * {
  background-color: var(--bs-table-bg);
  background-image: linear-gradient(var(--bs-table-accent-bg),var(--bs-table-accent-bg));
}

.table-striped > tbody > tr:nth-of-type(2n+1) {
  --bs-table-accent-bg: var(--bs-table-striped-bg);
}

Should output something like this without custom props (skipping the transparent values, since they wouldn't be outputted, would they?):

.table-striped > tbody > tr:nth-of-type(2n+1) {
  background-image: linear-gradient(#212529, #212529);
}

And please note that even if this code block looks shorter, the overall tables with all variants would not.

I don't see any way to handle both cases, and don't see any point either.

FWIW, there's a way to nullify custom props too, as explained by @MartijnCuppens in a comment. If that's needed, we might interpolate all our custom props, maybe?

ydmitry commented 3 years ago

@ffoodd when we use:

.table-striped > tbody > tr:nth-of-type(2n+1) {
  --bs-table-accent-bg: var(--bs-table-striped-bg);
}

It's not possible to combine both SCSS and CSS. But this approach is about how to write styles. It will require to drop IE11 support without any backports. Currently in v5 support is dropped, but it is possible to find some polyfills.

ffoodd commented 3 years ago

I'm sorry I still don't get your point. What's your approach? How do you solve the issue I mention?

ydmitry commented 3 years ago

For this use case - only with writing additional properties and selectors

// CSS variables declaration:

.table {
  --bs-table-bg: transparent;
  --bs-table-striped-color: #212529;
  --bs-table-accent-bg: var(--bs-table-striped-color);
}

// SCSS to CSS variables mapper
$table-bg: var(--bs-table-bg) !default;
$table-bg-image: linear-gradient(var(--bs-table-accent-bg),var(--bs-table-accent-bg)) !default;
$table-accent-bg: var(--bs-table-striped-bg) !default;

// Pure SCSS variables for projects without CSS variables support
$table-bg: transparent !default;
$table-accent-bg: #212529 !default;
$table-bg-image: linear-gradient($table-accent-bg, $table-accent-bg) !default;

// SCSS component styles
.table > :not(caption) > * > * {
  background-color: $table-bg;
  background-image: $table-bg-image;
}

.table-striped > tbody > tr:nth-of-type(2n+1) > * {
  // we added a property here, in some cases we will need to add selectors too
  // in case if we have more styles those were using --bs-table-accent-bg variable
  background-image: $table-accent-bg;
}

So that we have only SCSS variables in SCSS components.

For the projects which doesn't support CSS variables we can include a file with only SCSS variables.

In some projects we can decide which of the variables make CSS and allow runtime swtiching of these properties with different selectors, e.g.:

// Projects SCSS
$table-accent-bg: var(--my-table-with-special-accent-bg);

// Will generate a variant with special accent color, this will be allowed in SCSS of the projects, but not in SCSS of the library
.my-table {
  --my-table-with-special-accent-bg: #F00;
}
ffoodd commented 3 years ago

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

ydmitry commented 3 years ago

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 https://github.com/twbs/bootstrap/pull/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 commented 3 years ago

@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 commented 3 years ago

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: https://github.com/twbs/bootstrap/pull/31753 (Alert). It removes darken usage from styles and moved to variables declaration to be managable. Docs updated too.

ydmitry commented 3 years ago

Need a feedback which approach is better for CSS variables in Bootstrap - https://github.com/twbs/bootstrap/pull/31789 or https://github.com/twbs/bootstrap/pull/31753 ?

darkguy2008 commented 3 years ago

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 ( https://github.com/twbs/bootstrap/issues/26596#issuecomment-695345684 ) 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 commented 3 years ago

@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 commented 3 years ago

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

tabacitu commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

@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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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:

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 commented 3 years ago

That already sound fantastic. Good to get the stance of someone from the devteam towards this! I just commented on https://github.com/twbs/bootstrap/issues/31538#issuecomment-742801115 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 commented 3 years ago

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.