I have the following css which is loaded into my project:
// Default theme (light mode)
:root {
/* Typography */
--col-body-text: #0b0c0c;
--col-body-text-light: #505a5f;
}
// Dark mode theme
:root.dark {
/* Typography */
--col-body-text: #c5c5c5;
--col-body-text-light: #f8f8f8;
}
In my actual app this works as expected, however, in storybook, it ignores the dark mode variables.
I have updated my preview.js file to add '.dark' to the `HTML element when dark mode is selected - which works as expected - indeed all of the other dark mode specific code in the components works fine. It's only those variables that are being ignored.
Is there an issue with using :root in storybook that I'm not aware of or something?
if it helps, here is the code that adds the class to the HTML element:
// get an instance to the communication channel for the manager and preview
const channel = addons.getChannel()
// switch body class for story along with interface theme
channel.on('DARK_MODE', isDark => {
if (isDark) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
})
If you place such stylesheet in the HTML page (e.g. in the <head>), the :root selector refers to <html> (https://developer.mozilla.org/en-US/docs/Web/CSS/:root). If you want to use the :root selector with a class, you need to set the class on the <html> element (rather than on <body> suggested in another answer), so document.documentElement.classList.add('dark') is correct.
There is a playground which uses the Storybook syntax and features where I created a working example for you: https://webcomponents.dev/edit/p8TI3583HotsFNWjBMd8/src/index.stories.js
Not sure if your Storybook is configured in another manner, maybe your stylesheet is not really added or gets overwritten somewhere later. Please also verify if you use the CSS Custom Properties (aka CSS vars) correctly, I hope the working demo helps there too.
try
CSS -> body.dark {
instead of :root.dark {
and
channel.on('DARK_MODE', isDark => document.body.classList.toggle('dark', isDark))
Related
I'm using a template from another developer made in SCSS and vue.js to develop online courses. All colors are using the SCSS variable $primary and $secondary. I was thinking of inserting a color-blind option that would cycle through complementary color for the Normal, Deuteranopia and Tritanopia spectrums.
I'm not finish with the design yet, just a prototype, but it'd be a circles in a dropdown that would update the primary color of the entire page on click.
I'm not exactly sure how to write it as i'm not too familiar with vue and scss, but it would change the variable of $primary: " "; by another variable from the choice selected. For exemple:
$(".changePrimaryColor").click(function(){
$primary == $theme-blue;
});
I thought of also incrementing the variable of $primary by 1 until 2 or 3, but not sure.
Any lead would help, thank you!
// Theme colors
$theme-green: #c4d600;
$theme-blue: #1a397c;
$theme-red:#9b1717;
$primary: $theme-green;
$secondary: $theme-red;
$success: #288515;
$danger: #a61616;
$warning: #efa92c;
$theme-colors: (
"primary": $primary,
"secondary": $secondary,
"success": $danger,
"danger": $success,
"warning": $warning,
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/sass.js/0.9.2/sass.min.js"></script>
Unfortunately scss can't do this (but it would be awesome). This is because scss compiles to normal css. So your scss variables do not exist anymore when the clients loads the page.
Fortunately you can use css properties / variables instead, which do re-render!
https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
This means that something like changing colours is pretty simply!
const button = document.querySelector('button')
function changeColor() {
document.body.style.setProperty("--bg-color", 'DodgerBlue');
}
button.addEventListener('click', changeColor)
:root {
--bg-color: tomato;
}
body {
background: var(--bg-color);
}
<button>Change color</button>
I am developing a library of react components and asked an important question. How to correctly change styles (css) when changing the application theme.
Appeal to those who already have experience in this and who can advise their approach or solution.
Now I'll tell you how it works for me:
I have my own themeProvider wrapped in a Context with the ability to change the theme.
When the theme changes in the provider, the changeCssVariables method is called.
export const changeCssVariables = (theme) => {
const root = document.querySelector(':root');
// root.style.setProperty('--default-color', 'orange');
const cssVariables = [
'color',
'background',
'color-uibutton',
'background-uibutton',
'box-shadow-uibutton',
'color-disabled-uibutton',
'background-disabled-uibutton',
'background-uiradiobutton',
'color-uiinputext',
'color-notes-uiinputext',
'color-subsection',
'background-subsection',
'box-shadow-subsection',
'alt-subsection',
'on-subsection',
'off-subsection',
];
cssVariables.forEach(element => {
root.style.setProperty(
`--default-${element}`,
`var(--theme-${theme}-${element})`
);
})
}
What happens in general: it has a global CSS with variables default, light and dark themes. When changing the theme, a method is called that changes the default variable to the variable of the selected theme.
Example css:
--default-color: var(--theme-light-color);
/* Themes */
--theme-light-color: #000;
--theme-dark-color: #fff;
I don't like that you have to pull in all the css and change it this way. What is the solution?
Thanks for the help!
Found a solution in using styled-components package
Created my wrapper as ThemeProvider (Context)
I have a shadow component in stenciljs like
#Component({
tag: "digital-verification",
styleUrl: "digital-verification.scss",
shadow: true,
})
and I have some css variable like
:host {
--app-primary-color: #488aff;
--app-border-radius: 10px;
--app-error-color: #EE320C;
}
Till now everything is working perfect and no issue.
Now I want to set this variables in the code like
document.querySelector(':root').style.setProperty('--app-primary-color', '#ffffff');
I tried
document.querySelector(':host').style
and
document.querySelector(':root').style
and
document.querySelector(':root').shadowRoot.style
and
document.querySelector(':root').root.style
and
#Element() el;
...
this.el.shadowRoot.querySelector(":host") // this is null
but I get compile error that Element doesn't have style
Wondering how should I see css variables in the code.
update
I did below code, and I can see value is set in console, but application color doesn't change
document.documentElement.style.setProperty("--app-primary-color", "#ff0000");
console.log(document.documentElement.style.getPropertyValue("--app-primary-color"));
In case somebody else has same issue
I did the below steps and issue is resolved
1- I deleted the below section
:host {
--app-primary-color: #488aff;
--app-border-radius: 10px;
--app-error-color: #EE320C;
}
2- defined a variable like below
$primary-color : var(--app-primary-color, #488aff);
3- in code set the css variable like below
document.documentElement.style.setProperty("--app-primary-color", "#ffff00" , "important");
Updated solution based on #Danny '365CSI' Engelman comment
1- You can delete the below section if you want to variable be applied to all children or keep it if you want it to be applied only on this component
:host {
--app-primary-color: #488aff;
--app-border-radius: 10px;
--app-error-color: #EE320C;
}
2- define #Element() el: HTMLElement; in your component
3- in code set the css variable like below
this.el.style.setProperty("--app-primary-color", "#ffff00");
I want to change the CSS property based on if a user has clicked on dark mode or light. I can get the a subscription going to capture the value every time the modes are switch but i want to be able to change certain property's in CSS when the different modes are clicked.
I'm using Angular 9
Any idea on how I can do this?
You can use BehaviourSubject RXJS to implement this feature. By default you can set to false and use light mode when ever User clicks the switch using next() method change the behaviour subject to true then dark etc. you have to subscribe to BehaviourSubject variable on init and handle the response.
You can have the reference at BehaviourSubject.
I personally implemented this dark mode feature in my project here is the link telivic.com the approach I have used in that site is as followed.Hope this works for you.
addDark(){
document.getElementById('nav').classList.add('dark');
document.getElementById('box').style.backgroundColor = "#35363A";
document.getElementsByTagName("BODY")[0].classList.add('dark');
document.getElementById('modal').style.backgroundColor ="bisque";
if(this.service.flag){
document.getElementById('opt').classList.add('dark');
}
document.getElementById('gen').style.color="white";
}
removeDark(){
document.getElementById('nav').classList.remove('dark');
document.getElementById('box').style.backgroundColor = "#fff";
document.getElementsByTagName("BODY")[0].classList.remove('dark');
document.getElementById('modal').style.backgroundColor ="white";
if(this.service.flag){
document.getElementById('opt').classList.remove('dark');
}
document.getElementById('gen').style.color="black";
}
/*Binded my switch to this function I have used service so that user dark/light mode preference can be processed in other pages also.*/
darkMode(){
this.dark = !this.dark;
this.userService.setMode();
if(this.dark){
this.addDark();
}else{
this.removeDark();
}
}
You can use styled-components. One example would be:
import styled from 'styled-components'
const ComponentName = styled.div`
color: ${props => props.mode === `dark` ? `white` : `black`};
`
And then in your render, do this:
<ComponentName mode={mode} />
You can add a variable in ts file,
modeType: string;
This will change as 'dark' or 'light' depending on user's selection.
In your html to change the css property use ngClass.
Or use
<div class="{{modeType == 'dark' ? 'dark-property': 'light-property'}}">
In your css file,
.dark-property{
add your "dark" css styles
}
.light-property{
add your "light" css styles
}
I am searching a way to styling shadow DOM from the outside. For example, I would like to set the color of all text in all 'span.special' elements as RED. Including 'span.special' elements from shadow DOM. How I can do this?
Previously there were ::shadow pseudo-element and /deep/ combinator aka >>> for this purpose. So I could write something like
span.special, *::shadow span.special {
color: red
}
But now ::shadow, /deep/ and >>> are deprecated. So, what do we have as a replacement of them?
I did try many methods, including those described here. Since I'm using an external Web Component lib, I don't have access to modify these components. So, the only solution that worked for me was using JS querySelector, like this:
document.querySelector("the-element.with-shadow-dom")
.shadowRoot.querySelector(".some-selector").setAttribute("style", "color: black");
Not the best solution, not suitable for large stylings, but does work for little enchancements.
#John this was tested with Chrome 83.0.4103.116 (still going to test in Safari) and I did for Ionic (v5) ion-toast component. Here is the (almost) real code I used:
import { toastController } from '#ionic/core';
let toastOpts = {
message: "Some message goes here.",
cssClass: "toast-with-vertical-buttons",
buttons: [
{
text: "Button 1",
side: 'end'
},
{
text: "Button2",
side: 'end'
},
{
icon: "close",
side: "start"
}
]
}
toastController.create(toastOpts).then(async p => {
let toast = await p.present(); // this renders ion-toast component and returns HTMLIonToastElement
toast.shadowRoot.querySelector('div.toast-button-group-end').setAttribute("style", "flex-direction: column");
});
There is still no easy way to pierce through the shadow root, but here are 3 ways you can go about it. Just keep in mind that you will need to make changes inside the web component.
Using variables v1 - You will need to pass the property and consume the variable inside the web component.
Using variables v2 - You will need to consume the variable inside the web component.
Using ::part() - You will need to add a part attribute to the element you want to style in the web component. (Note: this pseudo element is well supported but is still in experimental mode, so make sure you're aware of that before using it in production).
Run code sample below for details.
const elA = document.querySelector('custom-container-a');
const shadowRootA = elA.attachShadow({mode:'open'});
shadowRootA.innerHTML = '<style>:host([border]) {display:block;border: var(--custom-border);}</style>'+
'<p>Shadow content A</p>'
const elB = document.querySelector('custom-container-b');
const shadowRootB = elB.attachShadow({mode:'open'});
shadowRootB.innerHTML = '<style>p {display:block;color: var(--custom-color, blue);}</style>'+
'<p>Shadow content B</p>'
const elC = document.querySelector('custom-container-c');
const shadowRootC = elC.attachShadow({mode:'open'});
shadowRootC.innerHTML = '<p part="paragraph">Shadow content C</p>'
/* Normal way of styling */
p {
color: orange;
}
/* Using variables version 1 */
custom-container-a {
--custom-border: 3px solid gold;
}
/* Using variables version 2 */
custom-container-b {
--custom-color: green;
}
/* Using ::part() */
custom-container-c::part(paragraph) {
color: magenta;
}
<p>Light content</p>
<custom-container-a border></custom-container-a>
<custom-container-b></custom-container-b>
<custom-container-c></custom-container-c>
You could use #import css as explained in this answer to another question on SO.
Include the rule inside the style element in the shadow tree.
<style>
#import url( '/css/external-styles.css' )
</style>
Note that the >>> combinator is still part of the CSS Scoping Module Draft.
Well, #import is not a solution if you are working with library web component that you can't change ...
Finally I found several ways to do it:
1) Cascading. Styles of Shadow DOM's host element affect Shadow DOM elements also. Not an option if you need to style a particular element of the Shadow DOM, not every.
2) Custom properties https://www.polymer-project.org/1.0/docs/devguide/styling
If an author of the web component provided such.
3) In Polymer, the have Custom Mixins also https://www.polymer-project.org/1.0/docs/devguide/styling
4) #import, but only for not-library components
So, there are several possibilities, but all of them are limited. No powerful enough way to outside styling as ::shadow were.