Is it possible to add a classname to a CSS variable or is there some other way to set it up so that I don't have to manipulate each individual variable directly via javascript? I'd like to keep all my styles in CSS and simply turn on relevant classes with JS. For example, If something like this was possible in CSS:
:root.white{ --bgcol:#FFF; --col:#000; }
:root.black{ --bgcol:#000; --col:#FFF; }
Then I could then just toggle the .black or .white class from javascript to trigger all vars to change. What's the best approach for this type of setup?
That's frankly the best (as in most idiomatic) approach — the use of class names, if not altogether separate stylesheets (as has been tradition for many, many years), to theme entire layouts via custom properties. It's the most "fundamentally CSS" approach with JavaScript merely being the glue that makes the theme switching work. You really can't do much better than that.
For those unaware what :root means and wondering where exactly the class names are being applied, it's the html element (the parent of body). So there is nothing special going on here — you're simply switching class names on the html element. It just happens that global custom properties are conventionally defined for the document root element since it's at the top level of the inheritance chain.
If you have any theme-agnostic custom properties, as well as style properties (i.e. not custom properties) that apply to the root element, keep them in their own unqualified :root rule, separate from your themed custom properties, so they won't be affected by theme switching. Here's an example:
const root = document.documentElement;
// Default theme - should assign declaratively in markup, not JS
// For a classless default theme, move its custom properties to unqualified :root
// Again, keep it separate from the other :root rule that contains non-theme props
// Remember, the cascade is your friend, not the enemy
root.classList.add('white');
document.querySelector('button').addEventListener('click', function() {
root.classList.toggle('white');
root.classList.toggle('black');
});
:root {
--spacing: 1rem;
color: var(--col);
background-color: var(--bgcol);
}
:root.white {
--bgcol: #FFF;
--col: #000;
}
:root.black {
--bgcol: #000;
--col: #FFF;
}
p {
margin: var(--spacing);
border: thin dashed;
padding: var(--spacing);
}
<button>Switch themes</button>
<p>Hello world!
Using :root selector is identical to using html, except its specifity is higher, thus there is no issues in using this approach.
For example:
:root {
--bg: red;
}
:root.blue {
--bg: blue;
}
// ...
div {
background: var(--bg);
}
Later, you should just change html's class and variables will change.
You can see an example in this fiddle.
Related
I'm working on implementing different themes across my website. I've refactored my main stylesheet to feature variables, and have three other "theme" stylesheets that define those variables at the :root level. The final HTML then links to two stylesheets: The main stylesheet and one of the three theme stylesheets, which are switched on button click by changing the link's href attribute. Somehow, only some of the variables are working.
/* dark.css (theme stylesheet only containing the custom properties) */
/* Only --background works */
:root {
--background: #000000;
--seperator: rgba(1,1,1,0.12);
--text: #FFFFFF;
--block-border: #404040;
--block-shadow: #000000;
--block-background: #151017;
}
/* All properties work except for --comment */
code {
--keyword: #F72057;
--type: #FF9519;
--call: #FF5700;
--property: #FF5700;
--number: #F72057;
--string: #F72057;
--comment: #FFFFFF;
--dot-access: #FF5700;
--preprocessing: #646485;
}
When I then go into the inspector, everything seems to be alright. I can see the proper inheritance, and I can even click on the variable where it's used and see the intended color.
Some examples of how the variables are used:
/* styles.css (main stylesheet) */
body {
...
background: var(--background);
color: var(--text);
}
pre code .comment {
color: var(--comment);
opacity: 0.4;
}
Other approaches
I've tried several other approaches, all of which led to the same result (only some variables working).
Instead of linking to another stylesheet, directly change the variables with javascript on the HTML tag
Instead of linking to multiple stylesheets, having three separate main stylesheets
Changing a custom attribute in the HTML tag and defining all variables like [theme="dark"] {...} in one main stylesheet
As #Pushkin and #Temany Afif have pointed out, there were strange characters all over my code. A quick project-wide find and replace solved the problem.
This questions is similar to some other on StackOverflow, but I couldn't find any answer describing applicable to my situation and non-deprecated method (and I'm starting thinking that maybe there is no any good solution for that situation).
Let's say we have some main.css file which includes common styles for buttons, lists, links and so on. So it's just some standard .css file which contains common styles that we want to reuse across the application. And we want to apply the same styles to Web Components with Shadow DOM.
There are a few ways, that I know about, to accomplish that:
Using one of deprecated approaches: ::shadow, >>>, /deep/ selectors. But those selectors are deprecated by now, so I guess it's not good approach to move forward with.
Using css variables. This approach is good for customization purposes, if we need to set a few properties. But it's too complex if we want to migrate 10-20 common styles from main.css file.
Using #import statement or "link" tags inside of Shadow DOM. It will work, but it will duplicate all styles for every component. If we have 10 web components we will end up with 10 duplicates of exactly the same styles. It doesn't sound like good enough solution too. Especially if we have a lot of common styles, sounds like it can be bad solution from performance point of view.
Don't use Shadow DOM at all and use global styles :) But it's not solution for current problem.
I also checked how the same problem resolved in Angular Framework (I checked version 5 of Angular). When I set encapsulation behavior to Native, it's just actually duplicating styles (like in #3 described above), what I think isn't the best way (but maybe the best currently existing way).
So, does anyone know if there is any other way to solve this problem without described above drawbacks? It just sounds like current drawbacks of Shadow DOM bring even more problems than it tries to solve.
There's no real drawback with solution 3:
Whether you apply a CSS style to n elements in a main document, or to 1 element in n Shadow DOM, the style will be duplicated to the whole n elements anyways.
If you import a document n times in n Shadow DOM, il will be actually be loaded only one time and reused through the browser cache.
After that, it will depend on the browser implementation of Shadow DOM and CSS styles, and you should see a performance degradation only the thousands of Shadow DOM.
2019 update for Chrome 73+ and Opera 60+
Now you can directly instanciate a CSSStyleSheet object and assign it to different Shadow DOMs.
This way the HTML won't be duplicated.
var css = new CSSStyleSheet()
css.replaceSync( "#import url( main.css )" )
host.shadowRoot.adoptedStyleSheets = [css]
host2.shadowRoot.adoptedStyleSheets = [css]
You can also apply it to the global document:
document.adoptedStyleSheets = [css]
The other advantage is that an update on the stylesheet will be applied to all Shadow DOMs (and document) that adopted it.
css.replaceSync( '.color { color: red }' )
I managed to do it using javascript modules but I doubt it's the cleanest solution.
You can create a GlobalStyles.js file that will contain the css styling that is common throughout various components. Changing the language mode on your editor to 'html' will provide syntax highlighting for the css.
const GlobalStyles = {
main: `
<style>
body {
overflow: hidden;
margin: 0;
font-family: 'Poppins';
}
h3 {
font-size: 39px;
}
</style>
`,
button: `
<style>
button {
display: block;
cursor: pointer;
outline: none;
font-family: 'Poppins Medium';
line-height: 17px;
padding: 9px 13px;
font-size: 15px;
background-color: #9f28d8;
color: white;
border: 2px solid;
border-radius: 5px;
border-color: #9f28d8;
width: max-content;
}
</style>
`
}
export default GlobalStyles;
Afterwards, you can import it into another js file that contains the code to the shadow dom of your custom element.
import GlobalStyles from './GlobalStyles.js';
const template = document.createElement('template');
template.innerHTML = `
${GlobalStyles.button}
<style>
ul {
font-family: Helvetica, Arial, sans-serif;
font-size: 13px;
width: 20em;
list-style-type: none;
}
</style>
<ul></ul>
<button>Click me</button>
`;
export class CustomList extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(document.importNode(template.content, true));
}
}
The drawback with this approach is it only works if you are working purely with js files.
We're providing a pre-populated WordPress site for multiple members to use on their servers. The theme has a selector where they can customize their heading colours and fonts. Each member will have different colours based on their branding.
The site also includes a knowledgebase plugin and it uses its own colours which can be customized within its interface. Instead, we'd like to be able to set the knowedgebase colours/properties based on the theme's colour palette. (Ultimately, we'd like the member to only have to change the colors for the theme in one place rather than customize every single plugin.)
So, if H1 is globally set to blue, we'd like to be able to tell the knowledgebase's element (.kb-header) to be the same colour as H1.
Is this at all doable via CSS or Javascript or something?
Many thanks!
You can make an element to inherit the properties of a parent element. For example:
p { color: red; }
a { color: inherit; }
<p>Paragraph with link</p>
But definitely this won't work for what you're asking if your .kb-header is not a child of your h1.
Instead you could use different approaches to get the desired result. For example with custom properties (a.k.a CSS variables)
:root {
--user-color: red;
}
h1 {
color: var(--user-color);
}
.kb-header {
color: var(--user-color);
}
<h1>Title</h1>
<header class="kb-header">This is the header</header>
This way you could, for example, output your :root selector defining all your custom properties in your <head> tag using PHP. And your CSS would be totally independent from it.
If you want to inherit (Object Oriented Programing like) if the html tags doesn't have any relationship, you can use SCSS, for example SASS, it's a text pre processor that generates the CSS with a CSS-like coding with the addition that you can use variables and much more.
For more details visit SASS lang webpage i think this can help you a lot for mostly all your projects.
If you don't want to use SASS, you can even use javascript (with or without libraries) to reach this job, as plain CSS is not capable to conditionally apply styles between non-related tags.
I've written a miniature version of what you're looking for. Hope this helps :P
<html>
<style>
h1 {
background-color: blue;
}
</style>
<body>
<h1>Hey!</h1>
<div class="kb-header">I'm a div :)</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var bgColor = $("h1").css("background-color");
$(".kb-header").css({"background-color": bgColor});
});
</script>
<body>
</html>
In CSS you can combine selectors with a comma and have a single block of rules for both:
.a, .b { property: value; }.
And still style one or another selector with other declarations (example below).
When selectors are unrelated, it causes maintenance problems but if it's generated by a theme generator, that should be OK (you don't want to style those given components but have a clear documented list of what has to be styled with a set of colors).
h1,
.kb-header {
color: darkred;
}
h1 {
font-size: 2rem;
}
#media (min-width: 961px) {
.kb-header {
border: 1px solid tomato;
}
<h1>Title</h1>
<p>Unstyled paragraph</p>
<header class="kb-header">This is the header</header>
You can also add a utility class to all elements that are to be styled with a border, a background color and a color:
/* Theme 1
Primary color: #0055D0
Secondary color: #080;
*/
.c-primary {
color: #0055D0;
}
.bd-secondary {
border: 1px solid #080;
}
/* Common styles */
h1 {
font-size: 2rem;
}
.kb-header {
padding: 1rem;
}
<h1 class="c-primary">Title</h1>
<header class="kb-header bd-secondary c-primary">This is the header</header>
Other answers talking about a Sass environment (or other preprocessors like PostCSS and LESS) and CSS "Variables" (if you don't have to support old browsers like IE11 and Edge ~15 + Saf (iOS and OS X) 9.2 https://caniuse.com/#search=custom%20prop are also fine solutions to your problem IMHO.
I am implementing a dark mode on my site, and trying to do it in the cleanest way possible (no boiler plate code).
So I want to make .darkmode class in CSS, define styles with it, and when the user enables darkmode, javascript simply adds the darkmode class to the <body>.
How could I do something like this with CSS?
.darkmode {
.content{
background-color: black;
}
input{
background-color: black;
}
}
So my questions is, how can I make CSS change different elements on the page when adding this class to the <body>?
The code that you posted would be valid SCSS/LESS. But in plain css you can simply do that by using
.darkmode .content { /* CSS */ }
.darkmode input { /* CSS */ }
So yes, you always have to specify the .darkmode in front of every selector.
Let's suppose you have a selector, like
.mydiv .myanchor
You can override/add attributes using
body.darkmode .mydiv .myanchor
is much more specific and therefore the rules will override the default rules.
To achieve that in normal CSS you would have to use the CSS child selector;
body.darkmode .content {
/* Put styles here */
}
body.darkmode input {
/* Put styles here */
}
Basically the logic there says: "get the body element with the class darkmode and find it's child .content/input"
With CSS selectors, having two element selectors seporated by a space finds all of the second elements inside the first elements; div p would find all of the <p> tags inside all <div> tags.
I'd like to apply a CSS to some linkbuttons on page load but one of them <a id="lb1">logoff</a> must keep its style, no hover nor other event must change its style.
The linkbuttons have no class and the css applied to all of them is done to tags, this way:
a
{
//style
}
a:hover
{
// style
}
Is it possible?
No, you can't.
You can use more specific selectors (or even inline CSS with the style attribute) so that they are less likely to be overridden accidentally.
You can use the (eugh) sledgehammer of !important so they will only be overridden by another !important rule.
There is no way to prevent them being overridden though.
Please please please please please avoid using !important whenever possible. You will run into SO many annoying problems and issues from using this. I consider it a very lazy hack.
What you want to do is append a class to the link that you don't want overwritten. Classes are given a higher priority than general selectors (such a, p, b). So if you append this class to the link, the CSS will override the default CSS you have set for a.
CSS:
a {
color: red;
}
a:hover {
color: blue;
}
.derp:hover { /*you can add everything you want to preserve here, essentially make it the same as the link css. you can also change it to #lbl:hover, although there's no good reason to be using an ID as a CSS selector*/
color: red;
}
HTML:
this will turn blue on hover
<a class="derp" href="#">this will stay red on hover</a>
Here's a fiddle to show you. The second link has a class appended that preserves the original style: http://jsfiddle.net/p6QWq/
Why not add a class to all the link buttons you want to change, and not add it to the one you don't want to change.
Then you can call:
$(".myClass").css("backgound-color", "blue");
This would change the background color for every element with a class of myClass to a blue background.
Or you could add a whole new class to the link buttons that have a class of myClass:
$(".myClass").addClass("myExtraClass");
This would then make the class attribute of your link button class="myclass myExtraClass"
Seeing your code posted makes it a little more clear on what you want to do. Try this:
a {
text-decoration: none;
color: orange;
}
a:hover {
text-decoration: underline;
color: blue;
}
This would apply a default style to all <a> elements. Now you could overwrite this default style by providing a specific style for the anchor with the id you gave above:
#lb1 {
color: black;
text-decoration: none;
}
#lb1:hover {
color: black;
text-decoration: none;
}
I mocked this up in a quick and dirty jsFiddle. See if this gives you the desired result. IDs take precedence over classes and default element styling. So if you have one that you want to keep the same, apply and ID and style the particular element accordingly. This would also help you by preventing you from having to apply a class to several elements. It's less coding to apply one ID than to apply twelve classes. (Just an exaggerated example. I don't know how many links you have.)
Hope this helps.
css is cascading by definition, so any style you apply to a tags will apply to this specific one, except if you overwrite it.
You'll have to either assign a class to all the other buttons or overwrite all the default properties for this specific button.
Also, do not forget the pseudo-classes :visited and :active.
You should use !important in your css like :
a {
/* style */
background: #FFF !important;
}
a:hover {
/* style */
background: #FFF !important;
}
You could always overwrite your css by simply creating another stylesheet and place it at the END of your stylesheet links in the head of your html.
<head>
<link rel="stylesheet" href="location/location/first_stylesheet.css">
<link rel="stylesheet" href="location/location/revised_stylesheet.css">
</head>
This is not the most productive method of overwriting your css however; one would be well advised to eliminate the necessity for this separate stylesheet by simply appending elements with a class attribute. The class attr will allow you to modify basic html elements, tags and overlay a final layer to "rule them all". Enjoy!