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

Default to using the prefers-color-scheme in @media for automatically rendering the client's preferred colour scheme #37973

Closed
2 tasks done
Tracked by #37549
dan-giddins opened this issue Jan 29, 2023 · 12 comments

Comments

@dan-giddins
Copy link

Prerequisites

Proposal

I'm very happy that dark mode has been added to bootstrap, I think its been in desperate need of it for a long time now. However I am a bit disappointed in the way it has been implemented. I do not like how the 'default' way of using it is to hard code elements to be a particular colour scheme. I believe it would be better to, by default, look to prefers-color-scheme to automatically decide how every element on the page should be rendered, and to view all bootstrap components as being 'colour scheme agnostic', i.e. the same semantic design is present, regardless of the client's specific colour scheme preference.

I appreciate that it is possible through the use of JavaScript to create a toggler, but there is no guarantee that developers will do this.

Before v5.3 was released, I added my own rules to my site, using the following CSS, which hijacks some of Bootstrap's internal variables:

@media (prefers-color-scheme: dark) {
	:root {
		--bs-body-color: white;
		--bs-white-rgb: 32, 32, 32;
		--bs-light-rgb: 40, 40, 40;
		--bs-dark-rgb: 255, 255, 255;
		--bs-border-color: grey;
	}
}

This mostly worked well, causing my site to automatically render in the light mode when user is in light mode, and dark mode. However due to the way Bootstrap was (is still?) designed, I had to intervene in a few other areas as well (for the sake of @media (prefers-color-scheme: dark)):

	.form-control {
		background-color: rgb(var(--bs-white-rgb));
		border-color: var(--bs-border-color);
		color: var(--bs-body-color);
	}

		.form-control:focus {
			background-color: rgb(var(--bs-light-rgb));
			color: var(--bs-body-color);
		}

	.alert {
		background-color: var(--bs-alert-color);
		color: var(--bs-alert-bg);
	}

	.btn-close {
		filter: invert(1) grayscale(100%) brightness(200%);
	}

Motivation and context

I personally prefer dark theme. I find it much easier on the eyes to read, and I think a lot of people, especially developers agree with me and also have this preference. However, I also know someone who really struggles with dark mode, to the point that they cannot read the contents of any page with white text on a black background due to their astigmatism, and will simply cease to use that site, as the site is completely unusable to them. There are also many people who prefer light theme, either because they find it more comfortable to read, or it is just their personal preference.

I really despise having to use a site that is fixed to be light theme, in the same way that people who require/prefer a light theme despise having to or simply won't use a dark theme site. I see this as an accessibility issue that stretches all the way from a minor preference to absolutely critical for a sites usability.

If, by default, the easiest way that dark theme is available to a developer is through the use of data-bs-theme="dark", then we know full well that many developers will simply drop data-bs-theme="dark" at the html tag level, rendering the site fully dark theme, creating an uncomfortable or even unusable experience for those who prefer light theme. Conversely, if a developer is ignorant of the data-bs-theme system, or prefers light theme, then they will, by default, create a site that will be light theme, causing a problem for those of us who prefer dark theme.

My interpretation of the Bootstrap library philosophy is that usage of it should allow a site to be as usable and as accessible as possible to the highest number of users, with the least amount of developer input. Surely, the default theme of the page should be whatever the client prefers through the use of prefers-color-scheme? If the site is well made with a correct application of Bootstrap, then this would cause this site to automatically present a dark theme to a user, simply with the upgrading of the version of Bootstrap. Then if the client does have a reason to force a certain colour scheme, they then could explicitly override this.

Bootstrap already automatically looks at properties of the client for prefers-reduced-motion for the sake of accessibility. Why is prefers-color-scheme any less deserving of this status? While we are at it, prefers-contrast could also be given the same treatment. Image all the sites that would automatically become more accessible to more people.

@mdo
Copy link
Member

mdo commented Feb 15, 2023

For v5, it'll stay as-is with the data attributes being the first class method for adding new color modes. Right now, prefers-color-scheme only supports light and dark values. We wanted to implement color modes that extend beyond light and dark, allowing you to build and ship more complex and custom series of "themes", color schemes, or color modes. For example, you could have light, dark, dimmed dark, high contrast, and high contrast dark.

Beyond that, shipping a whole new behavior to anyone who updates in a minor release feels like an overreach. The vast majority of websites built on Bootstrap likely don't have dark mode, and surprising them with it doesn't feel right.

Lastly, it bears mentioning that while the data attributes are static by default, our JS color mode picker that we use in our own docs defaults to automatically changing the color mode to match the OS color mode. This gives developers and users flexibility. Developers can pick what modes to include, while users can use their OS defaults or select an override as their new default. This is the case in Bootstrap's docs, and the solution we recommend others take as well.

Hope that helps explain things more.

@mdo mdo closed this as not planned Won't fix, can't repro, duplicate, stale Feb 15, 2023
@dan-giddins
Copy link
Author

@mdo

our JS color mode picker that we use in our own docs defaults to automatically changing the color mode to match the OS color mode.

I use .NET Blazor, so my projects are free of JS, but I could do the same thing in C#. However, I feel that its better to use inbuilt CSS styling functionality instead of using something dynamic, where possible.

I also wonder how popular the ability for a user to be able to change the theme of a site manually on the fly actually is. I wouldn't be surprised if most users don't just stick with/select the same theme that their OS/Browser is currently running anyway.

The ability to have different colour themes is an interesting one, and it will be interesting to see how you progress with that, and what the uptake of this is like.

For now though, I think for the sites I build, I will continue to just use my custom (prefers-color-scheme: dark) rules for simplicity. It would be nice though to only need to override root variables, not have to manually override rules like:

.form-control {
	background-color: rgb(var(--bs-white-rgb));
	border-color: var(--bs-border-color);
	color: var(--bs-body-color);
}

I think when I looked at these properties, they are getting their colours from hard coded values. Is there any reason why they aren't instead drawing from variables? I think I may raise a PR to improve this, unless you can think of reason why I shouldn't...

@mdo
Copy link
Member

mdo commented Feb 15, 2023

I think when I looked at these properties, they are getting their colours from hard coded values. Is there any reason why they aren't instead drawing from variables? I think I may raise a PR to improve this, unless you can think of reason why I shouldn't...

Not sure I follow you there, please open that PR if possible if you see an opportunity for impromement :).

@EccentricVamp
Copy link

EccentricVamp commented Feb 15, 2023

@mdo

Shipping a whole new behavior to anyone who updates in a minor release feels like an overreach.

I agree. Maybe this entire "color modes" feature should wait until version 6?

The vast majority of websites built on Bootstrap likely don't have dark mode, and surprising them with it doesn't feel right.

This statement seems to conflate the lack of a feature with the lack of desire or preparedness.

Our JS color mode picker that we use in our own docs defaults to automatically changing the color mode to match the OS color mode. This gives developers and users flexibility. Developers can pick what modes to include, while users can use their OS defaults or select an override as their new default

prefers-color-scheme and prefers-contrast also offers flexibility to developers and users. Developers can choose which schemes and contrast their site supports via meta attributes or CSS. Users can choose via their browser (or OS) defaults. Bootstrap's "color modes" seem to reinvent the wheel in this regard.

My company wants to offer dark and light color schemes on our websites using Bootstrap, but the current implementation only seems to give us two options:

  • Write a custom "color mode" picker from scratch in JavaScript and maintain it for the foreseeable future
  • Recompile Bootstrap's Sass from scratch, and then recompile it again every time an update comes out.

Most of my company's websites are written in C# using ASP.NET Core, like dan-giddins. So we don't have much experience with JavaScript or Sass

@dan-giddins
Copy link
Author

dan-giddins commented Feb 19, 2023

@EccentricVamp for now, try adding this on to the end of your custom site.css file for now to go with my 'auto dark theme' approach:

@media (prefers-color-scheme: dark) {
	:root {
		/*custom dark theme rgb values*/
		--dark-1: 32, 32, 32;
		--dark-2: 48, 48, 48;
		--dark-3: 64, 64, 64;
		--light-1: 240, 240, 240;
		/*bs overrides*/
		--bs-body-color: rgb(var(--light-1));
		--bs-white-rgb: var(--dark-1);
		--bs-light-rgb: var(--dark-2);
		--bs-dark-rgb: var(--light-1);
		--bs-border-color: rgb(var(--dark-3));
		--bs-body-bg: rgb(var(--dark-1));
	}

	/*swap colours*/
	.alert {
		background-color: var(--bs-alert-color);
		color: var(--bs-alert-bg);
	}

	/*invert*/
	.btn-close {
		filter: invert(1) grayscale(100%) brightness(200%);
	}
}

Basically, you code the whole site for light mode, and then just add in this media query for dark mode users. The advantage of doing things this way is that you are using native CSS media queries to set the site theme, so that the site is the 'right' theme for the user before it is even rendered to the page. But the disadvantage of this is that it may be tricky to give the user the ability to manually toggle the site theme.

This code is working for me however, and it looks great imo (although you may need to add more bootstrap variable overrides and custom class rules as you add in more components to your project).

Maybe it might be worth setting up a new repo just for maintaining this 'auto dark mode' code for everyone to contribute to...

@mdo

Not sure I follow you there

I had another look, and I realise now that the way bootstrap is currently built is actually fine for me as is for this; I just wasn't making proper use of all of the variables. I have also done a little refactor to pull out the RGA values, as these seem to be fundamental in the bootstrap rules for allowing for transparency, and this also allows me to define my dark mode 'override' colours in one place. I did have to keep the 'swap' rules for the alert component, but this feels reasonable to me. The .btn-close element is a funny one, as it is constructed from an SVG as a background, so I have had to use a filter on it. I could replace this with an icon for my site, but I think I will stick with the recommended approach, now that I have figured out how to modify it for dark mode.

@fxzxmic
Copy link

fxzxmic commented Jan 16, 2024

@mdo I agree that there is a need to provide more color mode options for websites in the existing way, but it cannot be ignored that most websites only require light and dark color modes. Don't need any JS to adapt the rising popular light and dark modes for websites and automatically switch between them. I think there should be many people who like this, even if not the majority.

@fxzxmic
Copy link

fxzxmic commented Jan 16, 2024

The vast majority of websites built on Bootstrap likely don't have dark mode, and surprising them with it doesn't feel right.

I don't agree with this statement. They don't have a dark mode just because bootstrap doesn't have a very simple way to add a dark mode. If you just need to update the CSS file to add a dark mode to these websites, then there will be no website that doesn't have a dark mode using bootstrap, not just the "majority".

@aykevl
Copy link

aykevl commented Mar 10, 2024

A backwards compatible change would be something like this:

<body data-bs-theme="auto">

This would tell Bootstrap to use whatever can be obtained from CSS. That would be dark and light at the moment, but could be extended in the future if browsers start supporting other color schemes like dimmed dark.

Right now I've worked around this issue using a bit of JavaScript:

function updateColorScheme() {
  document.body.setAttribute('data-bs-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
}
updateColorScheme();
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateColorScheme);

I'd much prefer a CSS only solution but since what I'm building requires JavaScript anyway this is a good enough stopgap.

@aykevl
Copy link

aykevl commented Mar 10, 2024

Actually I found something that doesn't require JavaScript. When you inspect the <body data-bs-theme="dark"> element, you'll find something like this (Bootstrap 5.3.3):

[data-bs-theme="dark"] {
    color-scheme: dark;
    --bs-body-color: #dee2e6;
    --bs-body-color-rgb: 222,226,230;
    --bs-body-bg: #212529;
    --bs-body-bg-rgb: 33,37,41;
    --bs-emphasis-color: #fff;
    --bs-emphasis-color-rgb: 255,255,255;
    --bs-secondary-color: rgba(222, 226, 230, 0.75);
    --bs-secondary-color-rgb: 222,226,230;
    --bs-secondary-bg: #343a40;
    --bs-secondary-bg-rgb: 52,58,64;
    --bs-tertiary-color: rgba(222, 226, 230, 0.5);
    --bs-tertiary-color-rgb: 222,226,230;
    --bs-tertiary-bg: #2b3035;
    --bs-tertiary-bg-rgb: 43,48,53;
    --bs-primary-text-emphasis: #6ea8fe;
    --bs-secondary-text-emphasis: #a7acb1;
    --bs-success-text-emphasis: #75b798;
    --bs-info-text-emphasis: #6edff6;
    --bs-warning-text-emphasis: #ffda6a;
    --bs-danger-text-emphasis: #ea868f;
    --bs-light-text-emphasis: #f8f9fa;
    --bs-dark-text-emphasis: #dee2e6;
    --bs-primary-bg-subtle: #031633;
    --bs-secondary-bg-subtle: #161719;
    --bs-success-bg-subtle: #051b11;
    --bs-info-bg-subtle: #032830;
    --bs-warning-bg-subtle: #332701;
    --bs-danger-bg-subtle: #2c0b0e;
    --bs-light-bg-subtle: #343a40;
    --bs-dark-bg-subtle: #1a1d20;
    --bs-primary-border-subtle: #084298;
    --bs-secondary-border-subtle: #41464b;
    --bs-success-border-subtle: #0f5132;
    --bs-info-border-subtle: #087990;
    --bs-warning-border-subtle: #997404;
    --bs-danger-border-subtle: #842029;
    --bs-light-border-subtle: #495057;
    --bs-dark-border-subtle: #343a40;
    --bs-heading-color: inherit;
    --bs-link-color: #6ea8fe;
    --bs-link-hover-color: #8bb9fe;
    --bs-link-color-rgb: 110,168,254;
    --bs-link-hover-color-rgb: 139,185,254;
    --bs-code-color: #e685b5;
    --bs-highlight-color: #dee2e6;
    --bs-highlight-bg: #664d03;
    --bs-border-color: #495057;
    --bs-border-color-translucent: rgba(255, 255, 255, 0.15);
    --bs-form-valid-color: #75b798;
    --bs-form-valid-border-color: #75b798;
    --bs-form-invalid-color: #ea868f;
    --bs-form-invalid-border-color: #ea868f;
}

Just copy-paste this in your media query block and you'll have pure CSS dark mode!
Something like this:

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --bs-body-color: #dee2e6;
    --bs-body-color-rgb: 222,226,230;
    /* ...etc */
  }
  .btn-close {
    filter: var(--bs-btn-close-white-filter);
  }
}

It'll probably need updating with a new Bootstrap version, but as long as the Bootstrap version is fixed this seems to work well.

Update: added .btn-close for modal dialogs.

@fxzxmic
Copy link

fxzxmic commented Mar 10, 2024

Actually I found something much better.

We know these, and the purpose of this issue is that you don't have to manually modify CSS files yourself.

@aykevl
Copy link

aykevl commented Mar 10, 2024

We know these

Well I didn't, and maybe someone else finding this issue via Google will find this helpful. I updated my comment to make this more clear.

(And yes I agree this really should be fixed in Bootstrap directly, instead of using workarounds like the ones I posted above).

@Fuseteam
Copy link

for those finding this issue via Google, you can now enable dark-mode via media query by setting $color-mode-type: media-query

https://getbootstrap.com/docs/5.3/customize/color-modes/#building-with-sass

@twbs twbs locked and limited conversation to collaborators Nov 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants