twbs / bootstrap

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

Use HSL as default for color #36089

Closed Eseperio closed 2 years ago

Eseperio commented 2 years ago

Prerequisites

Proposal

HSL is the acronym of Hue, Saturation and Lightness, which means that without the need of scss we can easily darken or adjust hue to any color. See the example below:

darken($primary,10%)

can be replaced with:

background-color: hsl(var(--bs-primary-h), var(--bs-primary-s), calc(var(--bs-primary-l) - 10%))

See this codepen

This will allow the full color customization via css variables, which now is not possible. I.e; buttons require darken method and everytime you want to create a new theme, must compile scss. The same happens to adjust-hue, which can be replaced with HSL and a calc() in the hue variable.

Motivation and context

This change will allow live preview of the color changes within the browser. Will improve theme development as not compilation is need. Will allow a better implementation with future css4 features, like color mixins, etc...

Eseperio commented 2 years ago

Already found this pen from Una Kravets which already ported the color-contrast function. The codepen is part of this article where all common scss methods are ported to pure css. https://una.im/css-color-theming/

mdo commented 2 years ago

This is something we could evaluate for v6, but no sooner. Labelling accordingly and closing out for now. No promises one way or another either :).

MatthewKennedy commented 2 years ago

I've been waiting on this forever, get those colors out of SASS ASAP.

Eseperio commented 2 years ago

For those interested in how to use pure css with current BS5:

You have to do your own version of bootstrap. It is a little bit complex because there are many variables in BS5 using complex transformations that are not replicable in pure css (yet).

First: build our custom root.scss to register all scss variables. It uses native sass:color to extract each component for each color, and then registers it like 4 pure css variables. One for each component and one with HSL definition from the former ones. I also built my own namespace, for which i use a function called ps-prefix to automatically prefix all my css variables.

@use "sass:color";

:root {
  --black: black;
  --white: white;
  // Threshold used for color-contrast method, which determines the forecolor from the background
  --contrast-threshold: 65%;
  --color-contrast-dark: var(--black);
  --color-contrast-light: var(--white);

  /*********************************************
Break palettes into Hue, Stauration and lightness
 +++++++++++++++++++++++++++++++++++++++++++++*/

  @each $color, $value in $colors {
    #{ps-prefix($color + '-h')}: #{color.hue($value)};
    #{ps-prefix($color + '-s')}: #{color.saturation($value)};
    #{ps-prefix($color + '-l')}: #{color.lightness($value)};
  }
  @each $color, $value in $theme-colors {
    #{ps-prefix($color + '-h')}: #{color.hue($value)};
    #{ps-prefix($color + '-s')}: #{color.saturation($value)};
    #{ps-prefix($color + '-l')}: #{color.lightness($value)};
  }

  @each $color, $value in $grays {
    $color: 'gray-'+$color;
    #{ps-prefix($color + '-h')}: #{color.hue($value)};
    #{ps-prefix($color + '-s')}: #{color.saturation($value)};
    #{ps-prefix($color + '-l')}: #{color.lightness($value)};
  }

  /*********************************************
Create HSL versions
 +++++++++++++++++++++++++++++++++++++++++++++*/

  @each $color, $value in $colors {
    //--#{$variable-prefix}#{$color}-hsl: hsl(var(--#{$variable-prefix}#{$color}-h), var(--#{$variable-prefix}#{$color}-s), var(--#{$variable-prefix}#{$color}-l));
    #{ps-prefix($color + '-hsl')}: ps-getColorAsHsl($color);
  }

  @each $color, $value in $theme-colors {
    #{ps-prefix($color + '-hsl')}: ps-getColorAsHsl($color);
  }

  @each $color, $value in $grays {
    #{ps-prefix('gray-'+$color + '-hsl')}: ps-getColorAsHsl('gray-'+$color);
  }

  /*********************************************
  Create default BS variables with dependency on HSL and replicate them in my namespace
   +++++++++++++++++++++++++++++++++++++++++++++*/

  @each $color, $value in $colors {
    --#{$variable-prefix}#{$color}: #{ps-getVar($color + '-hsl')};
    #{ps-prefix($color)}: #{ps-getVar($color + '-hsl')};
  }

  @each $color, $value in $theme-colors {
    --#{$variable-prefix}#{$color}: #{ps-getVar($color + '-hsl')};
    #{ps-prefix($color)}: #{ps-getVar($color + '-hsl')};
  }

  @each $color, $value in $grays {
    --#{$variable-prefix}#{$color}: #{ps-getVar('gray-'+$color + '-hsl')};
    #{ps-prefix('gray-' + $color)}: #{ps-getVar('gray-'+$color + '-hsl')};
  }

}

These are the mixins i use in SASS to build all variables with pure css.


/**
Converts a variable into a css variable with a default value.
 */
@function ps-getVarWithDefault($name,$defaultValues) {
  @return var(ps-prefix($name), $defaultValues)
}

@function ps-prefix($name) {
  @return --#{$proshop-prefix}#{$name}
}

@function ps-getVar($simpleName){
  @return var(ps-prefix($simpleName))
}

And here are all the color utilities i´ve already made

/**
This method must be used only in combination with pure css variables.
pure css variables. They work with HSL values.
 */

@function ps-darken($name, $percent) {
  @return hsl(ps-getVar(#{$name}-h), ps-getVar(#{$name}-s), calc(#{ps-getVar($name+'-l')} - #{$percent}))
}

@function ps-lighten($name, $percent) {
  @return hsl(ps-getVar(#{$name}-h), ps-getVar(#{$name}-s), calc(#{ps-getVar($name+'-l')} + #{$percent}))
}

@function ps-color-contrast($bgName) {
  @return hsl(0deg, 0%, calc((#{ps-getVar($bgName + '-l')} - var(--contrast-threshold)) * -100))
}

@function ps-getHue($name) {
  @return ps-getVar($name + '-h');
}

@function ps-getSaturation($name) {
  @return ps-getVar($name + '-s');
}

@function ps-getLightness($name) {
  @return ps-getVar($name + '-l');
}

@function ps-getColorAsHsl($name) {
  @return hsl(ps-getHue($name), ps-getSaturation($name), ps-getLightness($name));
}

@function ps-alpha($name, $value:1) {
  @return hsla(ps-getHue($name), ps-getSaturation($name), ps-getLightness($name), $value)
}

Conclusions:

I´m working with this in a complex project and no issues at all. As you might notice, i´ve created a method called ps-getVarWithDefault, which helps me defining optionals variables and rely on bootstrap default value. I.e;

  max-width:  ps-getVarWithDefault('container-width',#{map-get($container-max-widths,'xxl')}) ;

With that, i can let a user change the width of the containers directly in their browser without the need of compiling. The same happens for $primary colors. The user can set it to its custom values. The only thing you have to do to override them is write a <style> tag and set your variables names and new values under :root.

I know it can be a really hard time implementing this approach for the bootstrap team, but i think it opens the door for online bootstrap designers which can share configuration between projects since no compilation is needed.

Anyway, thanks for keeping this great framework that rules 80% of internet alive.

MatthewKennedy commented 2 years ago

Hi @Eseperio ,

Do you have a demo project with this setup I can take a look at?

Eseperio commented 2 years ago

No. It is a private project. You must try at your own risk.