I have two components called GroceryItem and BasketItem. Since these components share quite a few styles and I didn't want to write the same properties in different stylesheets, I set up a stylesheet called _item-layout.scss that has all the common styles and then imported this in both BasketItem.scss and GroceryItem.scss.
The core difference between these components is that the BasketItem icon is meant to switch from white to red when the enclosing div is hovered upon, whereas the GroceryItem icon is meant to switch to green. To enable this functionality, I set the hover property to change colour in the BasketItem and the GroceryItem stylesheets.
The thing is, both components change icon colour to red when hovered on. This is especially confusing for me because in my GroceryItem.js file, I've only imported GroceryItem.scss. So how can it match a selector that isn't even present in the stylesheet that has been imported?
item-layout.scss:
.root {
background-color: rgba(230, 230, 230, 1);
margin: 10px 0px;
user-select: none; //standard syntax
-webkit-user-select: none; //webkit (safari, chrome) browsers
-moz-user-select: none; //mozilla browser
-khtml-user-select: none; //webkit (konqueror) browser
-ms-user-select: none; //IE10+
.p {
font-weight: 500;
}
}
.icon {
margin-right: 10px;
color: whitesmoke;
}
.row-content {
display: flex;
padding: 15px;
}
GroceryItem.scss:
#import "./../../styles/_item-layout";
.root:hover .icon {
color: green;
}
BasketItem.scss:
#import "./../../styles/item-layout";
.root:hover .icon {
color: red;
}
GroceryItem.js:
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPlusSquare } from "#fortawesome/free-solid-svg-icons";
import "./GroceryItem.scss";
import React from "react";
function GroceryItem(props) {
return (
<div className="root" onClick={props.onClick} >
<div className="row-content">
<FontAwesomeIcon icon={faPlusSquare} className="icon"/>
<p>{props.title}</p>
</div>
</div>
)
}
export default GroceryItem;
I think the problem is that you have hover styles for the root class and you're using this class in both components.
If you import BasketItem.js or GroceryItem.js, their respective scss files will also be imported. Each of these scss files sets the color of elements with class root on hover to either red or green. So whichever js file is imported last will indirectly decide the root class hover color.
Since you say both components have a red hover color BasketItem.js is probably imported after GroceryItem.js based on the code in your question. If you switch the import order around you will probably see both components turn green.
The simple solution here is to just use separate classes for GroceryItem and BasketItem so there is no collision / involuntary overwriting of styles.
Both declarations of icon styles are in the same scope so the last loaded scss file overwrites the first one.
You could add an additional classname in both components and use them as a kind of color flag. Lets say in first component you add the css classname green next to your icon classname and in the second one add red next to your icon classname. So you have:
... className="icon green" ... and
... className="icon red"
In you main scss file you do
.root {
.icon {
&:hover {
&.green {
color: green;
}
&.red {
color: red;
}
}
}
}
Of course add the other styles you have set for your root class
Related
I have code like this:
<theme-component theme="dark">
<my-component></my-component>
</theme-component>
Is it possible for my-component to know it is within theme-component with a theme of dark? Or do I also need to pass theme=dark manually into my-component?
my-component has styles defined like this:
static darkStyle = css`
:host {
--my-bkg: #535353;
--my-items: #474747;
--my-gutter: #4a4a4a;
--my-border: #474747;
--my-item-border: #535353; /* on dark themes only this is the same color as the background instead of the footer border */
--my-divider: #636363;
}
`;
static lightStyle = css`
:host {
--my-bkg: #b8b8b8;
--my-items: #c7c7c7;
--my-gutter: #ababab;
--my-border: #adadad;
--my-item-border: #adadad; /* on light themes only this is the same color as the footer border */
--my-divider: #9e9e9e;
}
`;
static styles = [this.darkStyle, sheet];
I would love to have some kind of ternary to switch between this.darkStyle or this.lightStyle. Or is there some kind of CSS I can write that is like
static styles = [css`
theme-component[theme=dark] :root {
--my-bkg: #535353;
}
`, sheet];
I see documentation about SuperElement.styles, but it's not clear to me how to use that when I am relying on variables based on an attribute here. I'm not trying to share a style as much as use the attribute to determine some new variables.
Edit after clarifications:
A component could technically find out the attribute on a direct parent component by imperatively doing the following:
this.parentElement.getAttribute('theme').
I am not sure how that could be done in CSS. Below this edit I've outlined a common approach to solving the issue of theming using CSS custom-properties which have the default behavior of inheriting through the shadow DOM. Also see video by Lit team: "How to style your Lit elements" for more context.
Original answer:
I think what you're looking for is the :host() CSS pseudo-class function.
Your setup is correct where the theme-component is providing CSS custom properties that inherit to all children. my-component does not need any changes.
The only change from your example that should be needed is:
static darkStyle = css`
:host([theme="dark"]) {
--my-bkg: #535353;
--my-items: #474747;
--my-gutter: #4a4a4a;
--my-border: #474747;
--my-item-border: #535353;
--my-divider: #636363;
}
`;
This will select and apply the dark theme CSS custom properties when the theme-component has the theme attribute set to value "dark". These custom properties will then inherit into the children.
Runnable sample:
<script type="module">
import {html, css, LitElement} from "https://cdn.jsdelivr.net/gh/lit/dist#2/core/lit-core.min.js";
class ThemeEl extends LitElement {
static styles = css`
:host {
--my-bkg: green;
display: block;
border: 2px dotted var(--my-bkg);
}
:host([theme="dark"]) {
--my-bkg: gray;
}
`;
render() { return html`<slot></slot>`; }
}
class ChildEl extends LitElement {
static styles = css`
:host {
display: block;
background-color: var(--my-bkg);
}
`;
render() { return html`<p>Child El</p>`; }
}
customElements.define('theme-el', ThemeEl);
customElements.define('child-el', ChildEl);
</script>
<theme-el>
<p>Default Theme</p>
<child-el></child-el>
</theme-el>
<theme-el theme="dark">
<p>Dark Theme</p>
<child-el></child-el>
</theme-el>
This technique can also be used such that a custom element can change its own host styling. By setting attributes on itself and using :host().
If the child component also needs to know what theme is set from JavaScript, that could also be communicated via a CSS custom property and queried with window.getComputedStyle(this).getPropertyValue(<custom property>).
How do I add a underline in text-decoration when hovering the text?
Seems this is correct but it still doesn't work
CLICK HERE
<span
style={{
color: "red",
cursor: "pointer",
"& :hover": {
textDecoration: "underline"
}
}}
>
Click this.
</span>
The style property doesn't support selectors.
You need to move your logic into a <style> element or a <link>ed stylesheet.
There are plenty of React-friendly libraries for generating them on-the-fly for you. Styled Components is a popular tool for this which supports the SCSS syntax you are (almost — you have a rogue space after the &) using.
import { styled } from 'styled-components';
const MySpan = styled.span`
color: red;
cursor: pointer;
&:hover {
text-decoration: underline;
}
`;
and then
<MySpan>Click this.</MySpan>
However, span elements are not designed to be interactive. They are not announced as clickable by screen readers and you can't tab to them if you aren't using a mouse. This is a major accessibility barrier. If you want something for the user to click on, use a link (if you are linking somewhere) or a button (otherwise).
According to here:
There has been plenty of valid points made that react inline style is not a good idea. No support for CSS selectors like “:hover”, “:active” “:focus”, “:before” and “:after”, media queries, nor for SCSS syntax, to name a few.
Just use a css file and implement it there with class:hover
try this, I Tried it and it works.
App.js:
import "./styles.css";
export default function App() {
return (
<div className="App">
<span
style={{
color: "red",
cursor: "pointer"
}}
>
Click this.
</span>
</div>
);
}
style.css:
.App {
font-family: sans-serif;
text-align: center;
}
span:hover {
text-decoration: underline !important;
color: "red";
}
I want to be able to not have to use !important and instead simply resolve by just using more specific selectors. Take this element for example:
<div>
<p className={`${headerStyles.headerOuter} ${bodyStyles.something} ${otherStyles.another}`}>Test</p>
</div>
It uses three classes each defined in separate css modular files:
import headerStyles from ‘…’
import bodyStyles from ‘…’
import otherStyles from ‘…’
Let’s say that headerStyles.module.scss contains:
.headerOuter {
color: blue;
}
bodyStyles.module.scss contains:
div .something {
color: red;
}
And otherStyles.module.scss contains:
.another {
color: green;
}
The p will have red text since bodyStyles is more specific.
But I want to be able to do this in headerStyles.module.scss:
.headerOuter {
&.another {
color: blue;
}
}
// or .headerOuter.another
So that headerOuter and another can work together to be higher in specificity than bodyStyles to force the element to apply blue text. But the problem is that headerStyles and otherStyles don’t seem to be able to recognise each other.
How can this be fixed?
I’ve made a similar demo here, where the text should be black but it’s not: https://codesandbox.io/s/css-modules-react-forked-mxtt6 - see another.module.scss and the text should be black
Thank you
From the codepen
The color: black selector is:
._src_another_module__another._src_another_module__something
While the actual element's classes are:
_src_test_module__test
_src_sassy_module__something
_src_another_module__another
The second element's class contains "sassy", it is different from the selector, that's why it doesn't match.
You can check it with the DevTools. The blue and red styles are shown as overwritten, the green has more specificity, but the black one doesn't even apply for the element as shown in the picture below.
Edit
I think there is lack of information about the actual tool behavior or just
a misunderstanding. The way it builds the name is _src + _<file_name> + _<selector_name>.
That being said:
/* The final style from "another.module.scss. */
._src_another_module__something {
color: red;
}
._src_another_module__bling {
background: #eee;
font-family: sans-serif;
text-align: center;
}
._src_another_module__bling button {
background: red;
}
._src_another_module__another {
color: blue;
}
._src_another_module__another._src_another_module__something {
color: black;
}
Notice the #import './sassy.module.scss' has nothign to do with the black stuff, it just duplicates the style with a different naming.
<!-- The final element with it's classes. -->
<p class="_src_test_module__test _src_sassy_module__something _src_another_module__another">
test
</p>
Note: all this code comes from the codepen.
Text isn't black because you are including the selector something from the import of sassy.module.scss with the ${style.something}, therefore the class will be named as _src_sassy_module__something (which is the red one).
If not yet, I encourage you to check the results with the DevTools often.
<p className={`${testStyles.test} ${styles.something} ${anotherStyles.another}`}>test</p>
The reason it is not working is that the latest calssname which is another is being called and it dosent effect what you do with the previous classes that are added to the element which here is something. In the scss file another.modules.scss you are importing the sassy.module.scss, this updates the style on the class something but dosent effect the style on the latest class another.
`#import './sassy.module.scss';
.another {
color: blue;
&.something {
color: black; // should be black
}
}
I'm trying to update Radio Button colors of an ant design radio mentioned in this link in my REACT App.
I tried using the suggestion in this post to update the colors.
But my colors are not updating. I am not sure what I'm doing incorrectly here.
Also, Its a REACT project and my package.json has a dependency for "antd": "^4.5.0"
and the import for antd.css exists in app.tsx like this
// Global styles
import "antd/dist/antd.css";
This is a code I have in one of the component files of the project.
File name: MyRadio.tsx
import { Radio } from "antd";
import styles from "./mystyles.module.scss";
return (
<Radio.Group
className={styles.toggle}
>
<Radio id="RDC" value="C">
C
</Radio>
<Radio id="RDI" value="I">
I
</Radio>
</Radio.Group>
);
Here is how mystyles.module.scss looks like:
.toggle {
width: 244px;
background: #ffffff;
border: 1px solid #d9d9d9;
box-sizing: border-box;
display: flex;
align-items: center;
float: left;
}
/* Followed suggestion from other post but didnt see colors updating, when I un-comment the following code
.ant-radio-checked .ant-radio-inner {
border-color: red !important ;
}
.ant-radio-checked .ant-radio-inner:after {
background-color: red;
}
.ant-radio:hover .ant-radio-inner {
border-color: red;
}
*/
Update 1
You need to override the below classes. It should work. Use !important only if your css is overridden by the existing classes.
You need to import "antd/dist/antd.css"; to get these in the console and then override the css
https://codesandbox.io/s/nameless-dream-4ojr4
.ant-radio-inner:after {
background: red !important;
}
.ant-radio-checked .ant-radio-inner,
.ant-radio:hover .ant-radio-inner,
.ant-radio-wrapper:hover,
.ant-radio-input:focus .ant-radio-inner {
border-color: red !important;
}
Edit - Included the codesandbox link and the hover css
I can not write comment (too low reputation), so i will write an "answer". You can try to change theme of ant-design https://ant.design/docs/react/customize-theme (This default blue color is primary one). However it requires some changes and new color will be applied globally, so its not too good for existing big projects.
We are using a class on the html-element to determine whether the user is in dark or light mode of the app.
<html class="dark-mode"></html>
This class is added using Renderer2 in a service that detects the selected setting of the user. This works fine so far.
Now, we have to adjust all the components to look good in the dark mode as well. But the problem is Angular's ViewEncapsulation.
What we thought would come in handy, is to simply update the SCSS of the component:
.headline {
color: black;
.dark-mode & {
color: white;
}
}
Or in plain CSS:
.headline {
color: black;
}
.dark-mode .headline {
color: white;
}
Now this doesn't work, because the class .dark-mode seems to be encapsulated, as expected.
How can we solve this problem?
:host-context provides access to css classes above the :host. By using :host-context you are able to check if any of the parents of the host have a specific css class and apply styling:
:host-context(.dark-mode) h2 {
color: white;
}
Documentation: https://angular.io/guide/component-styles#host-context