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

_custom.scss alternatives #22891

Closed
silverwind opened this issue Jun 22, 2017 · 25 comments
Closed

_custom.scss alternatives #22891

silverwind opened this issue Jun 22, 2017 · 25 comments
Labels

Comments

@silverwind
Copy link

Now that _custom.scss has been remove in #22821, I wonder how builds with custom colors and the likes could be made. My build so far was

  1. Clone twbs/bootstrap to a temporary location
  2. Copy my _custom.scss into the scss directory
  3. Run npm build scripts
  4. Copy the compiled .css and .js files to my project

The PR mentions it was nuked "in favor of relying on imports from package manager directories" but I have a hard time following? Anyone care to explain?

@TheBuzzer67
Copy link

I always use my own css file loaded at the end to make customization, so it could be a _custom.css file suggested!?

@silverwind
Copy link
Author

silverwind commented Jun 25, 2017

I always use my own css file loaded at the end to make customization, so it could be a _custom.css file suggested!?

That's what I do too, but it results in duplicate/unneccesary rules from Bootstrap's style being left over in the resulting CSS. It'd be more efficient to override the variables before the scss is compiled.

Meanwhile, I've found a workaround to get the old behaviour of _custom.scss by prepending it to _variables.scss (appending doesn't work):

$ cat _custom.scss _variables.scss > _variables_new.scss
$ mv -f _variables_new.scss _variables.scss

@rmoorman
Copy link

@silverwind I think that we are supposed to use bootstrap through npm (or the like) and then include/compile the sass files using the facilities the bundler in question gives us.
See the example over here. Even though bootstrap is inlined into the repository (which is not exactly the same as going through npm and node_modules) in this example, you can see over here how it is imported and how variables are adjusted to fit the theme (didn't build the code in there myself yet though).
Would that be a solution for you (sans inlining bootstrap into your repo that is)?

@silverwind
Copy link
Author

I haven't thought about importing it because most of my projects are pure CSS, so I can only use the compiled CSS for those. But yes, that would work for SCSS projects, which I coincidentally also have a few.

So the gist is that CSS and preprocessors other than Sass are not supported for customization? Isn't that a bit opinionated?

@boardfish
Copy link

I've just decided on directly editing _variables.scss and removing the !default tag from those whose values are changed. I guess a non-destructive reset action could be handy, and it'd be as simple as downloading _variables.scss from this repository and renaming the existing one to old_variables.scss, then recompiling. grunt resetstyle, maybe?

@TheHokieCoder
Copy link

@rmoorman The two examples you provided don't work hardly at all anymore because of recent changes to the framework. As an example, say you want to change only the primary theme color. You'd want to create a custom SASS file that overrides the default value for the primary theme color and then imports the Bootstrap SASS file. However, the primary brand color is now defined as:

$theme-colors: (
  primary: $blue,
  secondary: $gray-600,
  success: $green,
  info: $cyan,
  warning: $yellow,
  danger: $red,
  light: $gray-100,
  dark: $gray-800
) !default;

As far as I know you can't overwrite just the primary color by doing something like:

$theme-colors: (
  primary: fuchsia;
);

This would result in the other keys (e.g. warning, success) not being defined. If you copied the entire map and overwrite the primary key, you'd get errors because the variables such as $blue and $gray-600 haven't been declared yet. To mitigate that, you might copy over those variables into your custom SASS file. But in my opinion, that's a lot of unnecessary overhead having so many variables declared that are unchanged from the source.

Instead, what I am doing (and is the best I can come up with so far), is to copy the contents of bootstrap.scss to a SASS file in my project and inject imports of my own custom SASS files where needed to overwrite the Bootstrap defaults. Example:

@import "<path to Bootstrap SCSS directory>/functions";
@import "<path to Bootstrap SCSS directory>/variables";
/* Import customized Bootstrap variables */
@import "custom-bootstrap-variables";
@import "<path to Bootstrap SCSS directory>/mixins";
...

I think that is probably the best that can be done in order to leave the Bootstrap distribution unmodified but still allow you to customize the various aspects of the framework. Thoughts/suggestions?

@supergithubo
Copy link

I assume bootstrap is expecting overrides BEFORE _variables.scss because !default is put on every variable inside _variables.scss.

The !default works like this:

$var: #000;
$var: #FFF !default;
//Assign #FFF to $var if $var is null or unassigned
//Since $var is previously assigned, then #000 still $var's value

Marking a variable with !default will make the previous variable assignment important. It's like a reverse of !important. In my opinion, Sass !default is confusing.

Because of this, I setup all custom variables on BEFORE/TOP of bootstrap and setup custom styles/mixin overrides on AFTER/BOTTOM of bootstrap:

@import "custom-variables";
@import "bootstrap";
@import "custom-styles"

(Note: custom-variables.scss is a copy of _variables.scss)

You can also assign further custom variables on top (useful for white-labeling):

@import "client-brand-colors"; //variables w/wo !default (highest priority)
@import "app-variables";//variables w/ !default (higher priority)
@import "bootstrap";
@import "app-styles";
@import "client-styles";

@lvmajor
Copy link

lvmajor commented Jul 19, 2017

I do the same as @supergithubo and it works like a charm. (I tend not to save my custom-variables.scss in the same folder as the pulled/copied bootstrap folder).
This way, it's also easier to pull new versions of the framework without "nuking" the previous customizations (which could happen if you let the package manager update your dependencies automagically, which would wipe the previously existing "_custom.scss" file anyway :) )

@TheHokieCoder
Copy link

@supergithubo

Marking a variable with !default will make the previous variable assignment important.

Be careful making a statement like that. It will make any previous assignment more important than the assignment using !default, but it won't be the same as the previous assignment being !important.

I assume bootstrap is expecting overrides BEFORE _variables.scss because !default is put on every variable inside _variables.scss.

I wouldn't say "expecting", but rather "supports". The !default keyword really only applies to previously defined variables. Consider the 3 examples below:

// #1
$var: #CCC;
$var: #FFF;

// #2
$var: $FFF;
$var: $CCC !default;

// #3
$var: #CCC !default;
$var: #FFF;

Example 1 is a plain example where the second declaration would override the first. Example 2 is from how Bootstrap used to support custom overrides by defining their values as defaults that only get set if no previous value was declared. Example 3 is how my setup works. Example 3 is effectively equivalent to example 1 since the !default flag bears no effect on the second declaration. Therefore all 3 examples end in the same result. $var will be #FFF when the SASS compiler does its thing.

So, while your suggestion/point does work for overriding Bootstrap's default values, it still has a major issue (in my opinion):
Your custom-variables.scss is a copy of _variables.scss. If you only want to override a few variables, why make a copy of ~900 lines of code just to change a few? Also, as Bootstrap evolves and they make changes, you'll have to manually merge changes from _variables.scss to your custom-variables.scss. Otherwise you might be still declaring variables that are no longer used.

In my previous comment I made a point about changing only the primary theme color, which using your method would result in having to copy the declarations for $theme-colors map, $colors map, all of the individual colors variables, $grays map, and all of the individual gray color variables. By my count that is 58 lines of variable declarations that need to be copied just to change one color value. Using my method, however, I would only have to redefine the $theme-colors map, or 10 lines of code.

I DID, however, find a way where Bootstrap could provide its default map values and a developer could override specific keys in a file BEFORE the _variables.scss (like supergithubo proposes):

// In a custom variables file...
$theme-colors: (
  primary: red
);


// In Bootstrap's _variables.scss file...
$theme-colors: () !default;
$theme-colors: map-merge((
  primary: $blue,
  secondary: $gray-600,
  success: $green,
  info: $cyan,
  warning: $yellow,
  danger: $red,
  light: $gray-100,
  dark: $gray-800
), $theme-colors);

The first line in the Bootstrap file, $theme-colors: () !default; is in case the developer does not declare (to override) the $theme-colors map. In that case, it provides a default, empty map to merge in the next line.

The second line declares Bootstrap's default map as the first input to map-merge which is then overridden by values in the $theme-colors map variable. So in the example above, 3 lines allowed me to override the primary key in the map without harming the defaults for the other keys.

If this change could be made to all maps in _variables.scss, then you could import your own custom Bootstrap variables file into your project without unnecessary duplication of variables, then import the completely untouched Bootstrap source tree, then import whatever else you need.

@TheHokieCoder
Copy link

@os1r1s110

(I tend not to save my custom-variables.scss in the same folder as the pulled/copied bootstrap folder)

Agreed. The whole point of migrating away from _custom_variables.scss was so that developers stopped modifying files within the Bootstrap source tree (which is typically delivered via package repository). But that left an undocumented hole in how to achieve the same customizations once provided by that file. Hopefully my suggestion above can make that much simpler since they have transitioned to using maps more instead of individual variables.

@fluke
Copy link

fluke commented Jul 20, 2017

@hokiecoder Do you think you could submit a PR to implement your map merge method by default? That's the behavior I expected. I am currently using the rubygem so I can't override the variables.scss file directly.

@supergithubo
Copy link

supergithubo commented Jul 21, 2017

@hokiecoder

I used to set it up like yours but I ran into some problems with variable references inside _variables.scss.
Considering your setup:

@import "variables";
@import "customs";
@import "mixins";

How did this worked for you:

// _variables.scss

$white: #fff !default;
$input-bg: $white !default;

// _custom-variables.scss
$white: #f0f0f0;

// _forms.scss
.form-control {
  background-color: $input-bg;
}

In this example (defined in bootstrap), I want to change all whites from #fff to #f0f0f0 so I override it inside _custom-variables.scss but the output of .form-control is:

.form-control {
  background-color: #fff;
}

Since $white is being used/referenced into $input-bg inside _variables.scss itself, I also need to REDEFINE it inside _custom-variables.scss and all references of white (~33 references).

//_custom-variables.scss
$white: #f0f0f0;
$input-bg: $white;
<other 33 references of $white>
$custom-control-indicator-checked-color: $white;
$custom-control-indicator-active-color: $white;
. . .

It seems your method only works on mixins directly using the overriden variable but not on variables already declared dependent to the defaults so you need to redefine them (Imagine doing this too with other references. Also a major issue)

Did I miss something on your setup?

@lvmajor
Copy link

lvmajor commented Jul 21, 2017

@supergithubo couldn't you use your method (custom variables BEFORE variables) without copying the whole variables file?

That's what I do and it seems to work really well (maybe I didn't have the complex use cases others had with mixins, etc...)

I should add that I don't only have the "custom variables" file outside of the bootstrap imported folder, I also have my custom "boostrap-build.scss" which replaces the ones published with the distribution. This way I'm completely safe if my package manager updates bootstrap without me notifying it (except for variables/mixins that will have been removed, but that's my job to keep them synced at least for this).

@supergithubo
Copy link

@os1r1s110 - Oh see. Thanks for pointing out. Say for example I want to change primary theme color from blue to green:

$green: #28a745;
$theme-colors: (
  primary: $green
);

The problem is you need to redeclare $green with the exact value from _variables.scss (problem of unnecessary variable @hokiecoder is pointing out).

@lvmajor
Copy link

lvmajor commented Jul 22, 2017

True, there are pros and cons to any solution hehe :P

But in my opinion, the drawbacks of copying the "general variables" as the colors, etc.. is not that bad to prevent me from doing it. So I will usually do so, copy all the variables that I need in my "custom-variables.scss" for it to work correctly and live with the fact that I might have a couple of duplicate lines (not in the final build though, only in build process, so not much of an issue in the end).

As I said earlier though, my use case is pretty basic, I don't do anything really funky except modifying the colors, the number of grid breakpoints, some modifications here and there. But I don't really modify mixins etc... so that might be why I don't see much trouble in the solution I'm using right now, but I completely understand your point and I guess it will be everyone's choice to proceed as they see fits best.

As for @hokiecoder 's idea, I find it pretty interesting, but I have no power whatsoever in bootstrap so we'll let the maintainers decide if they find it interesting enough to implement in the main distribution :)

@TheHokieCoder
Copy link

@supergithubo Thanks for pointing that out! I hadn't tested with a variable that had been re-used within the _variables.scss file. So my method, as you said, would not work for that. So it seems to be that the only solution that might work 100% is to use your ordering (custom file before _variables.scss) along with the changes to the default maps within _variables.scss so that they can easily be customized beforehand. There is an issue (above) that referenced this issue that will hopefully get attention from the maintainers about how the new variable maps create some big headaches for customizing Bootstrap now (even though the issue is closed). In the meantime, I'll try to create time to make a PR for my fix for all maps within _variables.scss.

@mdo
Copy link
Member

mdo commented Aug 9, 2017

PR opened for @hokiecoder's suggestion at #23260. Please let me know if I've missed anything or need to take something else into consideration. I'm thinking no matter what this will wait for beta 2 as I don't see it as any kind of breaking change, just a bit of a weird one for some folks. 😅

@silverwind
Copy link
Author

This new color override is working pretty well for me, so I guess all that's left here to do it documentation. The options page currently states that _custom.scss is included which is no longer true. I'd suggest that users who want to integrate their customizations in a automated build process use something like this:

$ cat _custom.scss _variables.scss > _variables_new.scss
$ mv -f _variables_new.scss _variables.scss
// start bootstrap's build process

@mother10
Copy link

First of all, i am a beginner in using bootstrap, and having difficulties in understanding the explanation on:
https://getbootstrap.com/docs/4.0/getting-started/theming/

If this comment should be separate or belongs to another thread, just move it.

As i understand it is like this: in custom.scss i could write:

`// Your variable overrides
$mycolor: #f4f9f1;
$body-bg: $mycolor;
$body-color: $grey-900;

// Bootstrap and its default variables
@import "node_modules/bootstrap/scss/bootstrap";`

First:
this does not compile because $grey-900 does not excist yet.
I can solve that by declaring $grey-900 inside my own overrides. But that means i have to check what constant Bootstrap uses for grey-900, each time bootstrap is updated.
Would it be worth the trouble to split up _variables.scss, just to contain things like:

body-bg: $white !default; body-color: $gray-900 !default;' etc

and a file _constants.scss that would contain things like:

$white: #fff !default; $black: #000 !default; etc

Then it would be possible to do:

`// Bootstraps constants first
@import "node_modules/bootstrap/scss/constants";

// Your variable overrides
$mycolor: #f4f9f1;
$body-bg: mycolor;
$body-color: $grey-900;

// Bootstrap and its default variables
@import "node_modules/bootstrap/scss/bootstrap";`

Second:

The bootstrap theming url states you have to create your own "custom.scss".
But in fact the resulting custom.css contains the WHOLE themed bootstrap. It is not just a small file with only the things you want to change. Wouldnt it sound more logical to call it "themed-bootstrap.scss" instead of "custom.scss" in that theming article? That way it is more clear i think, that it will contain the whole bootstrap. And also by using it in your index.html it shows you are using bootstrap and not just some personal custom.css file.

@mdo
Copy link
Member

mdo commented Nov 14, 2017

@mother10 $grey-900 doesn't exist yet because it doesn't exist at all—we spell it gray. There are no plans to split our variables file.

Your custom.scss file should only include the parts of Bootstrap you import. If you need the entire project, you'll get a themed version of the entire project. If you only need a few parts, you'll get a few themed parts. In most cases, your file will be named something else and shouldn't include all of Bootstrap, which would make a name change moot.

@mother10
Copy link

@mdo Thanks for the quick answer.
And sorry :) i retyped my example, in my original it was indeed $gray-900, but the result was as i described.
So i guess if i want to use it the way i wrote, i have to define $gray-900 in my own overrides, with the value used in _variables.scss?

@steven-pribilinskiy
Copy link

steven-pribilinskiy commented Nov 16, 2017

As I see it now, after many years of working with the v3 LESS implementation (where it wasn't even an issue), in order to reference an existing variable from _variables.scss, the _bootstrap.scss must be copied and pasted into your project's directory, where your _custom-variables.scss can be imported right after _variables.scss:

// _my-bootstrap.scss
/*
 * Bootstrap v4.0.0-beta.2
 */
@import '~bootstrap/scss/functions';
@import '~bootstrap/scss/variables';
@import 'custom-variables';

@import '~bootstrap/scss/mixins';
@import '~bootstrap/scss/root';
@import '~bootstrap/scss/print';
...

The problem here is that after every update you'll have to compare the _my-bootstrap.scss file with the _bootstrap.scss and apply the differences because optional components could be added/renamed/deprecated/removed.

LESS has a notion of lazy variable evaluation which was weird back in the day but then you kinda used to it and use this approach everywhere and now it makes it hard to port LESS stylesheets to SCSS.

I'm kinda OK with the new approach. We are heading towards PostCSS (remember that old 2015 tweet from @mdo). And that's great!

But for now I have a proposal to split the _bootstrap.scss file
#24802
Now you'll be able to easily override the variables:

@import "~bootstrap/scss/bootstrap-required";
@import "custom-variables";
@import "~bootstrap/scss/bootstrap-optional";

@mother10
Copy link

@pribilinskiy :) :) Happy user overhere.
Because i dont know much of bootstrap yet i dont know if that might solve my problem, but it sounds like it. I expected already there might be much more needed in that first "required" part, as you call it, thats why i said "worth the trouble".
I'll try to follow from the links here and "twbs / bootstrap" what will change. That will help me get better knowledge of bootstrap.
Thanks for the thumbs up. :) :)

@mother10
Copy link

I might be wrong but sofar i dont see _variables.scss being split. So how does that solve the gray_900 problem i described?
@andresgalante i do hope someone can tell me if not splitting that variables file will solve my problem.
And not just me, i think more users might want this.

@steven-pribilinskiy
Copy link

@mother10 there's a PR #24802 that splits that file.
For now the _bootstrap.scss must be copied and pasted into your project as described in my former comment above

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

No branches or pull requests