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):
- If the cascade results in a value, use it.
- 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.
- 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
:
:root
, being a pseudo-class, is more specific. If you require the lesser specificity of a type selector, it's entirely acceptable to usehtml
instead of:root
.: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:
TestButton
is defined after the code that uses it and therefore after themakeStyles
call that is defining styles intended to override styles inTestButton
. Since themakeStyles
call forgButton
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, anymakeStyles
calls at the top level of the imported file will be executed before anymakeStyles
calls in the file using the imported component.The
makeStyles
call forTestButton
is not being done at the top level. Instead it is being done inside theTestButton
function which means it will be executed whenTestButton
is rendered instead of whenTestButton
is imported. Calls tomakeStyles
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 frommakeStyles
(i.e.GrangeButtonStyles
in your example). SincemakeStyles
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
Horizontally Center Small Triangle at Bottom of Div and Resize Responsively and Proportionately
How to Automatically Add Browser Prefix to CSS3 Properties
How to Add Multiple Browser Specific Values into a CSS Style in React
CSS Background to Stretch to Window Bottom
Stop Firefox Rendering Inline Colours in Rgb() Form
How to Add or Change The Class Attribute of a Jsf Datatable (Primefaces)
CSS Divs Jumping When Border Added
CSS Selector Compare 2 Attributes
3D-Like Hover Effect on Button
Characters from Embedded Google Fonts Not Showing Up in Firefox 4 and Ie9
How to Add Linear-Gradient Color to Slider
Using Rems with a Pixel Fallback
CSS Animations End Together Even Though They Are Started at Different Times
CSS Drop Down - Sub Menu Color
Alternate Rows in One Column Only - CSS
Changing The Color of Active Navigation Bar
Bullet Point Background Color Attribute? CSS
Use CSS to Make Background-Image Above Background-Color in a List