How to Style: Root Without !Important Using Proper Specificity

How to style :root without !important using proper specificity

You are using CSS variable so you can still rely on them like this:

window.customElements.define('game-toes', class extends HTMLElement {  constructor() {    super();    let shadowRoot = this.attachShadow({      mode: 'open'    });    shadowRoot.innerHTML = 'Toes';    shadowRoot.appendChild(document.querySelector('#Styles').content.cloneNode(true));  }});
:root {  --boardsize: 40vh;  --color1: green;  --color2: red;}
game-toes { width: var(--boardsize); height: var(--boardsize); border: 10px solid var(--playercolor,grey); color:var(--playercolor,#000); background: lightgrey; display: inline-block;}
<TEMPLATE id="Styles">  <STYLE>      :host {          display: inline-block;          font-size:2em;      }
:host([player="X"]) { --playercolor: var(--color1); }
:host([player="O"]) { --playercolor: var(--color2); } </STYLE></TEMPLATE><game-toes player="X"></game-toes><game-toes player="O"></game-toes><game-toes ></game-toes>

Overriding styles without !important

It depends. CSS stands for Cascading Style Sheets, and there's a specific order that styles are applied in, overwriting previous styles. Without going into too much detail:

  • If your rules have the same specificity, just load your stylesheet second and everything will work fine.
  • If your rules have higher specificity, the order won't matter.
  • If your rules have lower specificity, you'll need to modify them to match.

So, what's specificity? Basically, it's the sum of each selector in a rule. So this:

a {
background-color: black;
color: white;
}

Has less specificity than this:

body a {
color: orange;
}

ID selectors have higher specificity than class selectors, which have the same specificity as pseudo-class selectors, which have higher specificity than tag selectors. So if all your content is contained in a <div> with an id of content, you would be able to override a style that looks like this:

body a {
border: 0;
}

With:

#content a {
border: 1px solid black;
}

CSS !important does not override styles from external stylesheets

This has nothing to do with CSS specificity or !important. You have a rule in main.css that says:

#legend .label {
color: black;
}

The selector is targeting the .label elements directly and giving them a color, which prevents them from inheriting the color from the body or some other ancestor. An !important property applies only to the element that is targeted; it does not force other elements to inherit that property. In other words, a specified rule always takes precedence over an inherited rule, even if that inherited rule is !important.

See the spec:

User agents must first assign a specified value to each property based on the following mechanisms (in order of precedence):

  1. If the cascade results in a value, use it.
  2. Otherwise, if the property is inherited and the element is not the root of the document tree, use the computed value of the parent element.
  3. Otherwise use the property's initial value. The initial value of each property is indicated in the property's definition.

— http://www.w3.org/TR/CSS21/cascade.html#specified-value

Is it bad practice to use :root with asterisk (CSS universal selector) to override Bootstrap variables?

This is mentioned in official documentation. When you want to override any variable you can declare them in :root without all selector and without important. It is important to import/use your override CSS after you include Bootstrap - not before.

Example:

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<style>
:root {
--bs-danger-rgb: 0,0,250;
}
</style>
<div class="bg-danger">It's blue</div>

Is :root really useless?

For the vast majority of authors working with HTML, there are only two practical differences between :root and html:

  1. :root, being a pseudo-class, is more specific. If you require the lesser specificity of a type selector, it's entirely acceptable to use html instead of :root.
  2. :root can be used to hide rules from Internet Explorer 8 and older (a use case that perhaps isn't prevalent in 2017 but was fairly common half a decade ago).

Other than that, the :root pseudo-class is indeed designed primarily for the use case of matching the root element in an arbitrary document without having to know the type of element, which means XML-based languages and even other, non-XML-based types of documents. Remember that as of level 3, the Selectors module is designed for a variety of uses with a variety of document languages, and not just styling HTML elements with CSS (which is why it's now simply called "Selectors" and not "CSS Selectors", despite it being a CSS module).

Additionally, for host languages where the root element doesn't have its own type (e.g. in a language where element may be both the root element, and nested), :root is necessary for distinguishing the root element from nested elements (though alternatives exist, like :not(:nth-child(n)) with equal specificity in Selectors 3, or :not(* > *) with zero specificity in Selectors 4).

CSS precendence in shadow DOM style

This behaviour is defined in the CSS Scoping Module Level 1 Draft §3.3:

When comparing two declarations that have different tree contexts, then for normal rules the declaration earlier in the shadow-including tree order [the first, global rule] wins, and for important rules the declaration coming later in the shadow-including tree [the second, ::slotted(*) rule] order wins.

Note: This is the opposite of how scoped styles work.

In other worlds:

Styles that applied before distribution continue to apply after distribution.

Overwrite angular material css -- Why does !important work

The MDN Web Docs has a great explanation of CSS specificity and the role of !important in overriding the cascade. Keep in mind that id="" is more specific than class="".

https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity

From the docs:

Some rules of thumb:

Always look for a way to use specificity before even considering !important

Only use !important on page-specific CSS that overrides foreign CSS

Never use !important on site-wide CSS.

Instead of using !important, consider:

Make better use of the CSS cascade, Use more specific rules.

Indicate one or more elements before the element you're selecting,
the rule becomes more specific and gets higher priority.

IMHO your use of !important here is valid as you are overriding "external" css.

As for styling Angular Material Components, when you set up your project you are using either a pre-built or custom theme. If you have lots of customizations to make, a custom theme is the way to go.

Angular Material Theming

Material UI Overriding styles with increased specificity

You could use global overrides to change the default margin of the AccordionSummary. However, this will affect all AccordionSummary components in your application.

The better approach (the one you are already using) is to wrap the component and alter its classes. If you look into the source of the AccordionSummary, you will find that the expanded property is an empty block. The margin gets set by the referencing selector in the content property:

    content: {
display: 'flex',
flexGrow: 1,
transition: theme.transitions.create(['margin'], transition),
margin: '12px 0',
'&$expanded': {
margin: '20px 0',
},
},

If you add the same reference in your custom styles, the priority becomes higher and you won't need !important. You will have to add the expanded className to your custom styles though:

import React from 'react';
import makeStyles from '@material-ui/core/styles/makeStyles'
import Accordion from '@material-ui/core/Accordion';
import AccordionDetails from '@material-ui/core/AccordionDetails';
import AccordionSummary from '@material-ui/core/AccordionSummary';

const useStyles = makeStyles(() => ({
expanded: {},
content: {
'&$expanded': {
marginBottom: 0,
},
},
}));

const MyAccordion = ({ summary, details }) => {
const classes = useStyles();

return (
<Accordion>
<AccordionSummary classes={{ content: classes.content, expanded: classes.expanded }}>
{summary}
</AccordionSummary>
<AccordionDetails>
{details}
</AccordionDetails>
</Accordion>
)
};

export default MyAccordion;

I cannot overide a style from materialize

Your CSS was incorrect defined, that's the reason why you don't see your style applied.

With your HTML markup:

<li key={elements.index}>  <div className="row">    <div className="col red s4">f</div>    <div className="col blue s8">f</div>  </div>  <div className="row">    <div className="col red s4 lvl-2">indented</div>    <div className="col green s8">f</div>  </div></li>

Styles being overwritten by Material-UI style

From https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity:

When multiple declarations have equal specificity, the last declaration found in the CSS is applied to the element.

So in your case where you are defining CSS classes in your custom component (e.g. TestButton) and in the code that uses that component, the specificity is determined by the order in which those CSS classes appear within the <head> element. This order is determined by an index that is set when makeStyles is called, so classes defined by later calls to makeStyles will appear later in the <head> element and thus have greater specificity.

There are two issues then in your example:

  1. TestButton is defined after the code that uses it and therefore after the makeStyles call that is defining styles intended to override styles in TestButton. Since the makeStyles call for gButton occurs first, the corresponding CSS class will be first in the <head> element. In real-world usage though, TestButton (your custom component) would be defined in a separate file and be imported. Since imports have to be at the top, any makeStyles calls at the top level of the imported file will be executed before any makeStyles calls in the file using the imported component.

  2. The makeStyles call for TestButton is not being done at the top level. Instead it is being done inside the TestButton function which means it will be executed when TestButton is rendered instead of when TestButton is imported. Calls to makeStyles should always be at the top level rather than nested within a component function. One other minor issue is the name of the variable returned from makeStyles (i.e. GrangeButtonStyles in your example). Since makeStyles returns a custom hook, you should always have a name that starts with "use" (e.g. useGrangeButtonStyles). This will ensure that the eslint rules for hooks recognize it as a hook and warn you of any hook misuse.

Related answers and references:

  • Material UI v4 makeStyles exported from a single file doesn't retain the styles on refresh
  • Internal implementation of "makeStyles" in React Material-UI?
  • https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity


Related Topics



Leave a reply



Submit