I'm trying to use use-local-storage to achieve a theme changer in React.
App component:
import './App.css';
import React from 'react';
import { Navbar, SearchBar, Header, Main, Chart, Map } from './components';
import { Routes, Route, BrowserRouter } from 'react-router-dom';
import useLocalStorage from 'use-local-storage';
function App() {
// a function that toggles between darkmode and lightmode in css
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const [theme, setTheme] = useLocalStorage('theme', defaultDark ? 'dark' : 'light');
const switchTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
}
console.log(theme)
return (
<BrowserRouter>
<div className='App' data-theme={theme} >
<Header />
<SearchBar />
<Navbar switchTheme={switchTheme} />
<Routes>
<Route path="/" element={<Main />} />
<Route path="/map" element={<Map />} />
<Route path="/chart" element={<Chart />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Navbar component:
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faMapLocationDot, faChartLine, faHouseUser } from '#fortawesome/free-solid-svg-icons'
import React from 'react'
import { Link } from 'react-router-dom'
const Navbar = ({switchTheme}) => {
return (
<nav className='nav'>
<button onClick={switchTheme}>Toggle</button>
<Link to='/'>
<FontAwesomeIcon icon={faHouseUser} size='4x' color='blue' />
<br></br>
Home
</Link>
<Link to='/map'>
<FontAwesomeIcon icon={faMapLocationDot} size='4x' />
<br></br>
Map</Link>
<Link to='/chart'>
<FontAwesomeIcon icon={faChartLine} size='4x' color='red' />
<br></br>
Chart</Link>
</nav>
)
}
export default Navbar
CSS:
*, *::after, *::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/****************** VARIABLES ******************/
:root {
--background-color:coral;
}
[data-theme="light"] {
--background-color:red;
}
[data-theme="dark"] {
--background-color:yellow;
}
body {
background-color:var(--background-color);
font-family: 'Roboto', sans-serif;
font-size: 16px;
color: #333;
line-height: 1.5;
margin: 2vmin;
}
.App {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/********************** SearchBar **********************/
form {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-width: 500px;
margin: 0 auto;
}
form > svg {
margin-left: -20px;
}
input {
font-size:inherit;
border-radius: 1vmin;
border: .5px solid #ccc;
padding: .5rem;
}
input:focus {
border-color: #333;
}
nav {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
background-color: yellow;
border-bottom: 1px solid #eaeaea;
width: 10vw;
height: 50vh;
border: 3px dotted purple;
align-self: flex-start;
}
a {
text-decoration: none;
}
/* a:active {
/* do sth with selected Link
} */
I am getting the correct values from console.log(theme) in App.js but I can't change the background colour of the whole app.
Any ideas to solve this issue ?
You are having a cascading issue, as you are setting your theme on body, and trying to change it later through the App component. Add the data-theme on the body itself or on html, which comes before, not on something that comes after.
Adding this useEffect in App.js just before your return would work:
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
Find the full example as well as a CodeSandbox below:
import './App.css';
import React, {useEffect} from 'react';
import { Navbar, SearchBar, Header, Main, Chart, Map } from './components';
import { Routes, Route, BrowserRouter } from 'react-router-dom';
import useLocalStorage from 'use-local-storage';
function App() {
// a function that toggles between darkmode and lightmode in css
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const [theme, setTheme] = useLocalStorage('theme', defaultDark ? 'dark' : 'light');
const switchTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
}
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
return (
<BrowserRouter>
<div className='App'>
<Header />
<SearchBar />
<Navbar switchTheme={switchTheme} />
<Routes>
<Route path="/" element={<Main />} />
<Route path="/map" element={<Map />} />
<Route path="/chart" element={<Chart />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Related
NavStyles.js
import styled from 'styled-components';
export const Nav = styled.navwidth: 100%; ;
export const NavMenuMobile = styled.ul`
height: 80px;
.navbar_list_class {
font-size: 2rem;
background-color: red;
}
${props => props.navbar_list_props && `
font-size: 2rem;
background-color: gray;
`}
`;
Navbar.js
import React from 'react'
import {Nav, NavMenuMobile} from "./NavStyles";
const Navbar = () => {
return (
<Nav>
{/* work no problem */}
<NavMenuMobile navbar_list_props>Nav Bar props</NavMenuMobile>
{/* not work How to use..? */}
<NavMenuMobile className="navbar_list_class">Nav Bar class</NavMenuMobile>
</Nav>
)
}
export default Navbar
<Nav>
<NavMenuMobile className={navbar_list_props}>Nav Bar props</NavMenuMobile>
</Nav>
Try This
Looks like you are setting styles for the children within NavMenuMobile with the class "navbar_list_class".
Should work with &.navbar_list_class
export const NavMenuMobile = styled.ul`
height: 80px;
&.navbar_list_class {
font-size: 2rem;
background-color: red;
}
`;
I have been trying to read the value of scrollY in my reactapp. But I found that it is being messed by overflow.
Here is the code that I used to read the scrollY:
import React from "react";
import{useEffect, useState} from "react"
export default function Test() {
const [offset, setOffset] = useState(0);
const setScroll = () => {
setOffset(window.scrollY);
};
useEffect(() => {
window.addEventListener("scroll", setScroll);
return () => {
window.removeEventListener("scroll", setScroll);
};
}, []);
return (
<div>
<div style={{color:"red"}}>{offset}</div>
</div>
);
}
Here is my app.jsx >>>
import React, { Component } from 'react';
import { Route } from 'react-router';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';
import abc2 from './img/abc2.jpg'
import './custom.css'
import Resume from './components/resumePage/Resume';
import Test from './components/Test.jsx';
var backGStyle={
backgroundImage:`url(${abc2})`,
minHeight: "100vh",
backgroundSize:'cover',
color:'red'
}
export default class App extends Component {
static displayName = App.name;
render () {
return (
<div className="app" style={backGStyle}>
<div className="mask">
<Layout>
<Route exact path='/' component={Home} />
<Route path='/counter' component={Counter} />
<Route path='/resume' component={Resume} />
<Route path='/test' component={Test} />
</Layout>
</div>
</div>
);
}
}
and my custom.css >>
a {
color: #0366d6;
}
code {
color: #E01A76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.backgroundBody{
position: relative;
}
.app .mask{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
color:#fff;
background-color:rgba(0, 0, 0, 0.6);
overflow:auto;
}
.abc{
color: aqua;
}
And if I remove the overflow: auto from custome.css .app .mask, I am being able to get the value of scrollY but my background image will be messed up, there will be white space in the background of half of the content like this >>>>
try element.scrollTop since your using divs as masks. ScrollY is only for the scroll position of the window (HTML element). Your question is a little vague, if your trying to align you background images use background-position and background-size. also set your overflow to clip.
https://developer.mozilla.org/en-US/docs/Web/CSS/background-position
I have a question about styling an anchor component when it is on the active page.
Here is my code:
import Link from 'next/link';
import styled from 'styled-components';
const NavStyle = styled.nav`
display: flex;
justify-content: space-between;
.nav-link a {
text-decoration: none;
color: white;
padding: 10px;
background-color: #FFCF00;
}
`;
export default function Nav() {
return (
<NavStyle>
<div className="nav-link">
<Link href="/" passHref>
<a>HOME</a>
</Link>
<Link href="/pricing">
<a>PRICING</a>
</Link>
<Link href="/terms">
<a>TERMS</a>
</Link>
<Link href="/login">
<a>LOGIN</a>
</Link>
</div>
<Link href="/">
<a>LOGO</a>
</Link>
</NavStyle>
)
}
What I want is, when the I click on the link and move to another page, the active link (that's matched with the URL) would have a green background. I have tried this, but it doesn't make any change:
const NavStyle = styled.nav`
display: flex;
justify-content: space-between;
.nav-link a {
text-decoration: none;
color: white;
padding: 10px;
background-color: #FFCF00;
&[aria-current] {
background-color: green;
}
}
`;
Next.js won't add aria-current to your active link; however, you can create a custom Link component that checks if the current pathname is the same as the href prop.
import React from "react";
import Link from "next/link";
import { useRouter } from "next/router";
const NavLink = ({ children, href }) => {
const child = React.Children.only(children);
const router = useRouter();
return (
<Link href={href}>
{React.cloneElement(child, {
"aria-current": router.pathname === href ? "page" : null
})}
</Link>
);
};
export default NavLink;
Then, you can use this component instead of the default Link whenever you want to add aria-current to the active link:
const NavStyle = styled.nav`
display: flex;
justify-content: space-between;
a {
background-color: #353637;
color: #fff;
padding: 1rem;
text-decoration: none;
&[aria-current] {
background-color: #faf9f4;
color: #353637;
}
}
`;
export default function Nav() {
return (
<NavStyle>
<div className="nav-link">
<NavLink href="/">
<a>HOME</a>
</NavLink>
<NavLink href="/pricing">
<a>PRICING</a>
</NavLink>
<NavLink href="/terms">
<a>TERMS</a>
</NavLink>
<NavLink href="/login">
<a>LOGIN</a>
</NavLink>
</div>
<Link href="/">
<a>LOGO</a>
</Link>
</NavStyle>
);
}
The best answer to this question that I have found is at https://amanexplains.com/using-aria-current-for-nav-links/
Note that the first answer to this question says to use pathname, but the link I provided describes why that won't work in some cases (slugged paths, for instance). Instead, use asPath.
Note also that this currently (NextJS 13) only works for client-side components as far as I can tell.
I have a side-drawer nav component that opens when a hamburger icon is clicked (mobile view).
When the component is open, it shows nav links. The issue is that the side-drawer stays open even when a link is a clicked and a new page loads. What is the best way to close the side nav when a navlink gets clicked? Edit: added componentDidUpdate to app.js, side drawer still won't close
//side-drawer.jsx
import React from 'react'
import './side-drawer.css'
import { Link } from 'react-router-dom';
const SideDrawer = props => {
let drawerClasses = 'side-drawer';
if (props.show){
drawerClasses = 'side-drawer open';
}
return(
<nav className={drawerClasses}>
<div className="side-nav">
<Link className="side-items" to="/about">ABOUT</Link>
<Link className="side-items" to="/contact">CONTACT US</Link>
</div>
</nav>
);
};
export default SideDrawer;
//app.js
class App extends React.Component {
state = {
sideDrawerOpen: false
};
drawerToggleClickHandler = () => {
this.setState((prevState) => {
return {sideDrawerOpen: !prevState.sideDrawerOpen};
});
};
componentDidUpdate(prevProps) {
const { location } = this.props;
if (location !== prevProps.location && this.state.sideDrawerOpen) {
this.setState({ sideDrawerOpen: false });
}
}
render(){
return (
<div style={{height: '100%'}}>
<Header drawerClickHandler={this.drawerToggleClickHandler}/>
<SideDrawer show={this.state.sideDrawerOpen}/>
<Switch>
<Route exact path='/' component={Homepage}/>
<Route exact path='/about' component={About}/>
<Route exact path='/contact' component={Contact}/>
</Switch>
<Footer/>
</div>
);
}
}
//.side-drawer.css
.side-drawer {
height: 100%;
background: linen;
box-shadow: 1px 0px 7px rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
left: 0;
width: 70%;
max-width: 400px;
z-index: 200;
transform: translateX(-100%);
transition: transform 0.3s ease-out;
}
.side-drawer.open{
transform: translateX(0);
}
.side-nav{
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.side-items {
margin: 0.5rem auto;
}
.side-items:hover{
text-decoration-line: overline underline;
color: #99bbff;
}
#media (min-width: 769px){
.side-drawer-open{
display: none;
}
}
Well if you're conditionally applying classnames to the SideDrawer component you can do this instead...
<nav className={props.show ? 'side-drawer open' : 'side-drawer'}>
Or you could conditionally show the component in the App component by...
return (
<div style={{height: '100%'}}>
<Header drawerClickHandler={this.drawerToggleClickHandler}/>
{
this.state.sideDrawerOpen && <SideDrawer />
}
<Switch>
<Route exact path='/' component={Homepage}/>
<Route exact path='/about' component={About}/>
<Route exact path='/contact' component={Contact}/>
</Switch>
<Footer/>
</div>
);
Here is the complete solution..
You need a specific handler for closing the sideDrawer and some css changes.
sideDrawer.js
import React from "react";
import style from "./side-drawer.module.css";
import { Link } from "react-router-dom";
const SideDrawer = (props) => {
let drawerClasses = [style.sideDrawer, style.Close];
if (props.show) {
drawerClasses = [style.sideDrawer, style.Open];
}
return (
<div className={drawerClasses.join(" ")}>
<div className={style.sideNav}>
<Link
to="/about"
// className={style.Items}
onClick={props.closed}
style={{ textDecoration: "none", color: "black" }}
>
ABOUT
</Link>
<Link
to="/contact"
// className={style.Items}
onClick={props.closed}
style={{ textDecoration: "none", color: "black" }}
>
CONTACT US
</Link>
</div>
</div>
);
};
export default SideDrawer;
App.js
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import SideDrawer from "./sideDrawer";
import { Homepage, About, Contact } from "./Components";
import Header from "./Header";
class App extends Component {
state = {
sideDrawerOpen: false
};
drawerToggleClickHandler = () => {
this.setState((prevState) => {
return { sideDrawerOpen: !prevState.sideDrawerOpen };
});
};
drawerClosedHandler = () => {
this.setState({ sideDrawerOpen: false });
};
render() {
return (
<div style={{ height: "100%" }}>
<Header drawerClickHandler={this.drawerToggleClickHandler} />
<SideDrawer
show={this.state.sideDrawerOpen}
closed={this.drawerClosedHandler}
/>
<Switch>
<Route exact path="/" component={Homepage} />
<Route exact path="/about" component={About} />
<Route exact path="/contact" component={Contact} />
</Switch>
{/* <footer /> */}
</div>
);
}
}
export default App;
side-drawer.module.css
module.css because of using join function in sideDrawer.js
.sideDrawer {
position: fixed;
width: 700px;
max-width: 80%;
height: 100%;
left: 0;
top: 0;
z-index: 600;
background-color: white;
box-sizing: border-box;
transition: transform 0.3s ease-out;
background-color: yellow;
}
#media (min-width: 900px) {
.sideDrawer {
display: none;
}
}
.sideNav a{
border: 1px solid black;
margin: 200px 30px ;
}
.Open {
transform: translateX(0);
}
.Close {
transform: translateX(-100%);
}
full source code
I have a React application. I have decided to add a layout using Ant Design. And i followed below example.
https://2x.ant.design/components/layout/#components-layout-demo-custom-trigger
This is my component code. (Full.js)
import React, { Component, Fragment } from 'react'
import { Layout, Menu } from 'antd'
import Icon from '#ant-design/icons'
import './Full.css'
import ReactDOM from 'react-dom'
import DailyData from '../DailyData'
import Container from 'react-bootstrap/Container'
import Toggle from './Toggle'
const { Header, Footer, Sider, Content } = Layout;
class Full extends React.Component {
state = {
collapsed: false,
};
toggle = () => {
this.setState({
collapsed: !this.state.collapsed,
});
}
render() {
return (
<Layout style={{ height: "100vh" }}>
<Sider
trigger={null}
collapsible
collapsed={this.state.collapsed}
>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1">
<Icon type="user" />
<span>nav 1</span>
</Menu.Item>
<Menu.Item key="2">
<Icon type="video-camera" />
<span>nav 2</span>
</Menu.Item>
<Menu.Item key="3">
<Icon type="upload" />
<span>nav 3</span>
</Menu.Item>
</Menu>
</Sider>
<Layout>
<Header style={{ background: '#fff', padding: 0 }}>
<Icon
className="trigger"
type={this.state.collapsed ? 'menu-unfold' : 'menu-fold'}
onClick={this.toggle}
/>
</Header>
<Content style={{ margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280 }}>
Content
</Content>
</Layout>
</Layout>
);
}
}
export default Full;
I'm using this component as below in App.js file.
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import Header from './components/layout/Header.js'
import DailyData from './components/DailyData.js'
import Container from 'react-bootstrap/Container'
import Toggle from './components/layout/Toggle.js'
import Full from './components/layout/Full'
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="App">
<Full />
</div >
);
}
}
export default App;
My Full.css file as below.
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 16px;
cursor: pointer;
transition: color .3s;
}
#components-layout-demo-custom-trigger .trigger:hover {
color: #108ee9;
}
#components-layout-demo-custom-trigger .logo {
height: 32px;
background: #333;
border-radius: 6px;
margin: 16px;
}
But the output will display like this.
Well, I also used Ant design Sider and I am using version 4.4.1. Not sure what you want to acheive, so I am sharing the whole code with you.
import React from 'react';
import { Route, Switch, NavLink, Redirect, withRouter } from 'react-router-dom'
import {RouteComponentProps} from "react-router";
import { Layout, Menu } from 'antd';
import {
DesktopOutlined,
PieChartOutlined,
UserOutlined,
SettingOutlined
} from '#ant-design/icons';
import Shipper from '../shipper/shipper'
const { Header, Content, Footer, Sider } = Layout;
const { SubMenu } = Menu;
// Type whatever you expect in 'this.props.match.params.*'
type PathParamsType = {
param1: string,
}
// Your component own properties
type PropsType = RouteComponentProps<PathParamsType> & {
}
class Sidebar extends React.Component<PropsType> {
state = {
collapsed: false,
};
onCollapse = (collapsed: any) => {
this.setState({ collapsed });
};
render() {
let { pathname } = this.props.location;
return (
<Layout style={{ minHeight: '100vh' }}>
<Header className="header">
<div className="logo" />
<Menu mode="horizontal" theme="dark" className="flex-setting">
<SubMenu icon={<SettingOutlined />} title="Settings">
<Menu.ItemGroup title="Basic Settings">
<Menu.Item key="setting:2">
<a href={"/accounts/logout/"} target="_self">
Signout
</a>
</Menu.Item>
</Menu.ItemGroup>
</SubMenu>
</Menu>
</Header>
<Layout>
<Sider collapsible collapsed={this.state.collapsed} onCollapse={this.onCollapse}>
{/* <div className="logo" /> */}
<Menu theme="dark"
defaultSelectedKeys={['/shipper']}
selectedKeys={[pathname]}
mode="inline">
<Menu.Item key="/shipper" icon={<PieChartOutlined />}>
<NavLink to="/shipper">
<span>Shipper</span>
</NavLink>
</Menu.Item>
</SubMenu>
</Menu>
</Sider>
<Layout className="site-layout">
{/* <Header className="site-layout-background" style={{ padding: 0 }} /> */}
<Content style={{ margin: "16px 16px", background: "#fff" }}>
<div style={{ padding: 24, background: "#fff", minHeight: 360 }}>
<Switch>
<Route exact path="/" render={() => (
<Redirect to="/shipper"/>
)}/>
<Route path="/shipper">
<Shipper />
</Route>
</Switch>
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>Copyrights © 2020 All Rights Reseverd by Company.</Footer>
</Layout>
</Layout>
</Layout>
);
}
}
export default withRouter(Sidebar);
My css is following:
.header {
display: inherit;
}
.logo {
height: 32px;
width: 35px;
margin: 16px;
background-image: url("main-logo.jpg");
}
.flex-setting {
display: flex;
flex-direction: row-reverse;
width: 100%;
}
Alot of things not necessary for you, so remove that part. Once you follow this, you will achieve something link this:
Remove " #components-layout-demo-custom-trigger" from css file and it will work.
I am sharing my css file.
App.css
#import "~antd/dist/antd.css";
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
.trigger:hover {
color: #1890ff;
}
.logo {
height: 32px;
background: rgba(255, 255, 255, 0.2);
margin: 16px;
}
.site-layout .site-layout-background {
background: #fff;
}
Because the example has trigger class ID # component-layout-demo-custom-trigger in front of it but the copied code also has # component-layout-demo-custom-trigger removed
enter image description here