Change body background with data-theme attribute on click with React - css

I'm trying to change the theme in an app using the data attribute and then changing the CSS variables according to the different data-theme values.
In the App component, I check if the user has a default theme set, and use that to set new theme on click
import "./styles.css";
import useLocalStorage from "use-local-storage";
export default function App() {
// Check user set theme mode...
const defaultDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
// Create theme mode state...
const [theme, setTheme] = useLocalStorage(
"theme",
defaultDark ? "dark" : "light"
);
// Handle on click from the theme switcher...
const clickHandler = () => {
const newTheme = theme === "light" ? "dark" : "light";
setTheme(newTheme);
};
return (
<div className="App" data-theme={theme}>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={() => clickHandler()}>Dark Mode</button>
</div>
);
}
In the styles.css I set the different variables to define the theme
/* Set the dark mode variables... */
[data-theme="dark"] {
--background: black;
--title-text: white;
--desc-text: grey;
}
/* Set the light mode variables... */
[data-theme="light"] {
--background: white;
--title-text: black;
--desc-text: grey;
}
/* Page Styles... */
body {
padding: 0;
margin: 0;
background: var(--background);
}
/*The rest of the CSS...*/
The rest of the elements work fine as they are wrapped in an element that has the data-theme attribute. However, the body is not wrapped with the data-theme attribute, so there is no change in the body background. In this example, I used .App but I would like to change the body instead. Is there a way to wrap the body in the data-theme attribute in React?
Here is the link to the full code in CodeSandbox
Full code on CodeSandbox

How about wrap App with div.body them pass data-theme into .body instead and make div.body cover body
<div className='body' data-theme={theme}>
<div className='App'></div>
</div>

Related

Is there a way to keep linked stylesheets localized to single components?

I have a component that relies on external stylesheets. I'm bringing the stylesheet into the component like this:
Child component
export default class Child extends Component {
render() {
return (
<div>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" />
...my code here...
</div>
);
}
}
But what's happening is this is forcing those styles onto the parent component as well.
Parent Component
export default class Parent extends Component {
render() {
return (
<div>
...code here...
<Child />
... more code here...
</div>
);
}
}
Is anyone aware of a way that I can keep that stylesheet link localized to just that child component so the styles aren't applied to the parent component as well?
Edit 2
Currently trying the shadow dom route, trying to pass down some children. Getting an error after the initial render saying Cannot read properties of undefined (reading 'children'). It does render the this.props.children initially...
import React, { Component } from 'react';
class MyComponent extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
${this.props.children}
`;
}
};
export default class Child extends Component {
render() {
return (
<div>
<script>
{!customElements.get("my-component") && customElements.define('my-component', MyComponent)}
</script>
<my-component>
<h1>Hello from shadow</h1>
</my-component>
<h1>Hello</h1>
</div>
);
}
}
You can try CSS Modules. Add :local(.className) to the class you want to use in your code which is in the font-awesome-min.css file. Then import the styles to your component. For example import styles from './font-awesome-min.css' then use the module in your code. The styles will only apply to specific element and won't affect other elements in the document. So let's say you have a class called .usericon in your css you do this in the css file.
CSS
:local(.usericon){
fill: red;
}
React Code
import styles from './font-awesome-min.css'
export default function Profile(){
return (
<i className={styles.usericon}>User Icon</i>
)
}
One way to truly isolate your CSS is with Web Components. Web Components are a browser API that allows defining custom elements with their own "shadow DOM". If a style is defined inside the shadow DOM, it is truly sandboxed with no styles going in or out. You can use whatever selectors you like:
class FancyBox extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
.fancy-box {
border: solid 3px darkblue;
background: dodgerblue;
padding: 10px;
color: white;
font: 16px sans-serif;
}
</style>
<div class="fancy-box">
<slot></slot>
</div>
`;
}
}
customElements.define('fancy-box', FancyBox);
.fancy-box {
border: dashed 3px darkred !important;
background: crimson !important;
padding: 10px !important;
color: white !important;
font: 16px sans-serif;
}
<fancy-box>Safe in my shadow DOM</fancy-box>
<div class="fancy-box">I am affected by outside stylesheets</div>
Note the use of <slot></slot>. This is a placeholder for child elements of the component.
If I wanted to use this custom element from React, it needs to be defined separately so it only runs once.
class FancyBox extends HTMLElement { /*...*/ };
customElements.define('fancy-box', FancyBox);
class ReactFancyBox extends React.Component {
constructor() {
super();
this.state = { value: 'hello world!' }
}
handleChange(e) {
this.setState({ value: e.currentTarget.value });
}
render() {
return (
<div>
<fancy-box>
<strong>{this.state.value}</strong>
</fancy-box>
<input value={this.state.value} onChange={e => this.handleChange(e)} />
</div>
);
}
};

Increase size of Twemoji in REACT

My REACT component's code is like this
import React from 'react';
import { Twemoji } from 'react-emoji-render';
import emoji.css;
const emoji = () => {
return ( <Twemoji className="Twemoji" text=":+1:"/> );
}
export default emoji;
My css file (emoji.css) has the following code
.Twemoji {
width: 20em;
height: 20em;
}
but the size of the emoji doesn't change.
if I inspect the element and modify the inline style in the page html that works
Please can you help me understand how I can increase the emoji size via CSS
Twemoji Component does not take a prop className (see here), instead you will have to use the options prop in order to pass a custom css classname
const options = { className: "Twemoji" };
const emoji = () => {
return ( <Twemoji text=":+1:" options={options} /> );
}
EDIT:
you would also have to add !important to width and height in the css class to take precedence over the element style (see css precedence)
.Twemoji {
width: 4em !important;
height: 4em !important;
}

Mock hover state in Storybook?

I have a hover style in my component:
const style = css`
color: blue;
&:hover {
color: red;
}
`;
Can you mock this in Storybook so that it can be shown without having to manually hover over the component?
Since in Storybook you need to show the component hover appearance, rather than correctly simulate the hover related stuff,
the first option is to add a css class with the same style as :hover :
// scss
.component {
&:hover, &.__hover {
color: red;
}
}
// JSX (story)
const ComponentWithHover = () => <Component className="component __hover">Component with hover</Component>
the second one is to use this addon.

Print MaterialUI Dialog fullscreen

I am new to React and Material-UI and I want to print my current dialog.
The problem is that I cannot find a way to maximize my Dialog for priting (set to fullScreen) without doing it in the Browser, too. So I basically want a smaller Dialog in my Browser and for the Dialog the maximal size.
Here is my basic code in TSX:
import React, { Component} from 'react';
import { Button, Dialog } from '#material/core';
export default class MUITester extends Component {
render(){
return (
<Dialog fullScreen={false}>
<div>
<Button onClick={() => window.print()}>
PRINT
</Button>
</div>
</Dialog>
);
}
And the corresponding css file:
#media print {
.print {
fullScreen=true;
color: blue;
}
}
Can I solve it using css? Or do I have to use React/Material-UI?
I solved it! Change the classes of Dialog:
<Dialog classes={{paperFullScreen: "prePrint printDialog"}} fullScreen>
Here my css:
.prePrint {
height: auto !important;
max-width: 600px !important;
}
/*Print Dialog*/
#media print {
.printDialog {
max-width: 100% !important;
}
}
You can set the width of your dialog like this:
<Dialog fullWidth={true} maxWidth='md'>
<div>
<Button onClick={() => window.print()}>
PRINT
</Button>
</div>
</Dialog>
As given in the Documentation
For printing div, which is inside dialog, use below code, and add css also
import React, { Component} from 'react';
import { Button, Dialog } from '#material/core';
export default class MUITester extends Component {
render(){
return (
<Dialog classes={{paperFullScreen: "prePrint"}} fullScreen>
<div id="DialogPrint">
some text some text , some paragraph and so on
</div>
<div >
<Button onClick={() => window.print()}>
PRINT
</Button>
</div>
</Dialog>
);
}
}
add below code in css
.prePrint {
height: auto !important;
max-width: 600px !important;
}
/*Print Dialog*/
#media print {
body * {
visibility: hidden;
}
#DialogPrint,
#DialogPrint * {
visibility: visible;
}
#DialogPrint {
position: absolute;
left: 0;
top: 0;
}
}
I was looking for a full-screen Dialog on mobile and a simple dialog for desktop and the below example resolved my issue, please take a look if helps.
import useMediaQuery from '#mui/material/useMediaQuery';
function MyComponent() {
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('md'));
return <Dialog fullScreen={fullScreen} />;
}
You can check demo on MUI official documentation.
You just need to add fullScreen flag to modal component in order to achieve full screen.
Like below
<Dialog fullScreen open={open} onClose={handleClose} TransitionComponent={Transition}>
And if you don't want to use fullScreen, simply remove that fullScreen flag and don't need to use CSS here.

How to change the style of a Ant-Design 'Select' component?

Suppose I want to change the standard white background color of the Select component to green.
My try...
<Select
style={{ backgroundColor: 'green' }}>
// Options...
</Select>
...didn't do it.
Can someone point me in the right direction?
[EDIT]
I ended up using the suggested approach from Jesper We.
Overwriting the color for all selections...
.ant-select-selection {
background-color: transparent;
}
...then I could style the Select components individually.
<Select> renders a whole set of <div>s, you need to take a look at the resulting HTML element tree to understand what you are doing. You can't do it through the style attribute, you need to do it in CSS.
The proper place to attach a background color is
.ant-select-selection {
background-color: green;
}
This will make all your selects green. Give them individual classNames if you want different colors for different selects.
For my form with Select element a have some code in render:
const stateTasksOptions =
this.tasksStore.filters.init.state.map(item =>
<Select.Option key={item.id} value={item.id} title={<span className={`${item.id}Label`}>{item.title}</span>}>
<span className={`${item.id}Label`}>{item.title}</span> - <span class="normal-text">{item.help}</span>
</Select.Option>
)
return (
....
<Select
mode="multiple"
value={this.tasksStore.filters.selected.state.map(d => d)}
onChange={this.handleTasksStatus}
optionLabelProp="title"
>
{stateTasksOptions}
</Select>
....
)
And some css for colorizing.
Result:
Try dropdownStyle instead of style.
<Select
dropdownStyle={{ backgroundColor: 'green' }}>
// Options...
</Select>
dropdownStyle is one of select props.
reference: antd select
From their official docs https://pro.ant.design/docs/style
Override the component style
Because of the special needs of the project, we often meet the need to cover the component style, here is a simple example.
Antd Select In multi-select state, the default will show all the select items, here we add a limit height for display scroll bar when the content beyond this height.
// TestPage.ts
import { Select } from 'antd';
import styles from './TestPage.less';
const Option = Select.Option;
const children = [];
for (let i = 10; i < 36; i++) {
children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>);
}
ReactDOM.render(
<Select
mode="multiple"
style={{ width: 300 }}
placeholder="Please select"
className={styles.customSelect}
>
{children}
</Select>,
mountNode,
);
/* TestPage.less */
.customSelect {
:global {
.ant-select-selection {
max-height: 51px;
overflow: auto;
}
}
}
Two points need to be noted:
The imported antd component class name is not translated by CSS Modules, so the overridden class name .ant-select-selection must be put in :global.
Because of the previous note, the override is global. To avoid affecting other Select components, the setting needs to be wrapped by an extra classname to add range restriction
with all the above answers you cant change the styles of tags conditionally but with below approach you can.
You can do a hack and change the styles as you like of tags of select dropdown.
You can use dropdownRender of select which takes 2 arguments
menuNode
props
use props children property to reach to each tag and change the styles and you can conditionally change the styles as you like.
for reference below is the example link for code sandbox
Select Tags Styles Sanbox
May not be an efficient way to do it but you can use this for now to meet your business requirement.
Thanks
Somebody stated the selector to be
.ant-select-selection {...
However it should be selector as follows:
.ant-select-selector {
background-color: green;
}
They implemented this feature with v4 of ant design:
https://github.com/ant-design/ant-design/pull/21064
But beware before blindly upgrading from v3 -> v4 - a lot has changed:
https://github.com/ant-design/ant-design/issues/20661
menuItemSelectedIcon={(props) => {
return (mode == "multiple" ?
<Tooltip title="Check to confirm the apps alongwith the vendor">
<input type="checkbox" checked={props.isSelected}
style={{
margin: 5
}}
/>
</Tooltip>
: null)
}}
Lastly I was working on ant dropdown and it did not get style as I wanted and I did not find a good solution for that.
Then I decided to share my css solution for those who are in my situation:
.license-plate-letters {
overflow-y: hidden !important;
min-width: 240px !important;
.rc-virtual-list-holder>div {
height: auto !important;
}
.rc-virtual-list-holder-inner {
display: grid !important;
grid-template-columns: repeat(5, 1fr) !important;
flex-direction: row !important;
flex-wrap: wrap !important;
.ant-select-item-option {
padding: 0.5rem 12px !important;
&:hover {
background-color: #452380d2 !important;
color: white !important;
}
}
}
}
<Select
virtual={false}
popupClassName="license-plate-letters">
<Select.Option key={sth} Title="title">title</Select.Option>
</Select>
In angular, you can override the style with ng-deep
::ng-deep .ant-select-selector {
background-color: red;
}

Resources