Change navigation styles depending on current path in Gatsby.js - javascript

With require(), I can get the CSS switching from page A to page B ... but when returning to page A, it keeps the stylesheet from page B active, even though both pages require() their own specific stylesheet. The same happens when I require() stylesheets by comparing to the current URL (location.pathname). What is the easiest way to accomplish this in Gatsby?
What I've tried:
import React from 'react'
import Layout from '../components/Layout'
import Header from '../components/Home/Header'
import style from "../templates/module.main.css";
const IndexPage = () => {
return (
<Layout className={style}>
<Header />
</Layout>
)
}
export default IndexPage
import React from 'react'
import Layout from '../components/Layout'
import Header from '../components/Home/Header'
import style from "../templates/module.secondary.css";
const IndexPageTwo = () => {
return (
<Layout className={style}>
<Header />
</Layout>
)
}
export default IndexPageTwo

Use css module, it will link the style to the pages/components.
Example with scss:
styleA.module.scss:
.page {
background: red
}
PageA.tsx:
import style from "./styleA.module.scss";
export default () => <div className={style.page}>Test</div>
styleB.module.scss:
.page {
background: blue
}
PageB.tsx:
import style from "./PageB.module.scss";
export default () => <div className={style.page}>Test</div>

Okay, I think the best solution for me was using react helmet and targeting the class like that:
<Helmet bodyAttributes={{ class: 'pageTwo' }} />
and then
.pageTwo .navbar {
color: black;
}

Related

Style child component from parent component using styled-components

I have a parent component and a child component. I import child component into parent and use it there. Below is the child component.
import styled from "styled-components";
const HeaderContainer = styled.h1``;
const Header = () => <HeaderContainer>This is Header</HeaderContainer>;
export default Header;
As you can see it is a simple component all it does it render text.
The parent component is below.
import "./styles.css";
import Header from "./header";
import styled from "styled-components";
const Main = styled.div`
${Header} {
background-color: "red";
}
`;
export default function App() {
return (
<Main className="App">
<Header />
</Main>
);
}
I am importing Header and using it inside JSX. What I want is to style Header component from parent component. I tried below styles using styled-components but it does not work somehow.
const Main = styled.div`
${Header} {
background-color: "red";
}
`;
Here is the codesandbox.
https://codesandbox.io/s/dreamy-brown-w3bbs?file=/src/App.js:0-277
How can I make this work or if you have a better idea then please share.
You don't need any global class names or selecting DOM elements which are both obvious bad practices. Instead, just use "styled" on the child component inside its parent. So, e.g., in your case:
import Header from "./header";
import styled from "styled-components";
const StyledHeader = styled(Header)`
background-color: "red";
`;
export default function App() {
return (
<Main className="App">
<StyledHeader />
</Main>
);
}
Also it's important to notice that: Some CSS-in-JS tools will require you to manually add a className prop inside the child component's props in order for this to work. So, e.g., your Header component must become:
const Header = ({ className }) => <HeaderContainer className={className}>This is Header</HeaderContainer>;
You don't need to pass this prop yourself from the parent component, your CSS-in-JS tool will pass this className automatically.
Hopefully I'm following what you want to do but in your Main styles you could just target the h1 for the header?
const Main = styled.div`
h1 {
background-color: red;
}
`;
Or if you didn't want to target all H1's then add a className to your header, I've added a few ways to do this here: https://codesandbox.io/s/clever-stallman-bdwfk?file=/src/App.js
In your code, the part where you tried to style the header, it was wrong. Instead what you can and should try is:
inline styles:
import "./styles.css";
import Header from "./header";
import styled from "styled-components";
export default function App() {
return (
<Main className="App">
<Header style={{background-color: "red"}}/>
</Main>
);
}
External CSS
You can give a class name to your elements inside the components and style them in the parent file like the following
The css file, let's name it style.css
.header-heading {
font-size: 4rem;
color: "red";
}
The child component
const Header = function () {
return (
<h1 className="header-heading">This is the heading</h1>
);
}
export default Header;
Then you can import both of them in the parent file and you'll see that the child component is styled now.
The parent component
import React from "react";
import Header from "./Header";
import "./style.css";
const App = function () {
<>
<h1>Below is the h1 component rendered</h1>
<Header />
</>
}

How to theme components with Styled-Component and Material-UI

I've tried to follow exactly what's documented here:
How to theme components with styled-components and Material-UI?
import React from "react";
import { withTheme } from "#material-ui/core/styles";
import styled from "styled-components";
const StyledDiv = withTheme(styled.div`
background: ${props => props.theme.palette.primary.main};
color: ${props => props.theme.palette.primary.contrastText};
`);
export default function App() {
return (
<StyledDiv>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</StyledDiv>
);
}
But instead of using a "standard" HTML component like above I tried against a Material-UI Button with no luck. Can someone please help me with what I am doing wrong?
This is what I am trying:
styles.ts
import styled from 'styled-components';
import { withTheme } from "#material-ui/core/styles";
import { Button } from '#material-ui/core';
export const StyledInnerSignInButton = withTheme(styled(Button)`
margin: ${props => props.theme.spacing(3, 0, 2)};
`)
index.tsx
import { StyledInnerSignInButton } from './styles';
[...]
<StyledInnerSignInButton
type="submit"
fullWidth
variant="contained"
color="primary"
>
Sign In
</StyledInnerSignInButton>
I'm stuck here and still a React newbie. Any help is greatly appreciated!
I managed to resolve my issue. It was due to specificity. There are two ways to mitigate that, one is by wrapping the new component style with &&, for example:
export const StyledInnerSignInButton = withTheme(styled(Button)`
&& { margin: ${props => props.theme.spacing(3, 0, 2)}; }
`)
Or by manipulating the CSS Injection Order, as documented here
In your "master" index.tsx file, you will set up your code this way:
import React from 'react';
import { render } from 'react-dom';
import { StylesProvider } from '#material-ui/core/styles';
import Home from './pages/Home';
render(
<React.StrictMode>
<StylesProvider injectFirst>
{/* component name */}
<Home />
</StylesProvider>
</React.StrictMode>,
document.getElementById('root')
)
I hope that helps someone with the same problem as me.

How to detect if another component is present in the document?

I have a site built with React Static that has a Header component that is always present. Depending on if the current page has a hero component or not, the Header should be either light or dark.
The Header is rendered outside of the routes and the useEffect is triggered before the children is rendered. This is probably because of the routing.
This is the current code:
// App.js
import React, { useState, useEffect } from 'react'
import { Root, Routes } from 'react-static'
export default () => {
const [useDarkTheme, setUseDarkTheme] = useState(false);
useEffect(() => {
if (typeof document !== "undefined") {
const heroPresent = document.querySelectorAll(".o-hero").length > 0;
console.log("The hero is present: " + heroPresent);
setUseDarkTheme(!heroPresent);
}
})
return (
<Root>
<React.Suspense fallback={ <em>Loading...</em> }>
<Header useDarkTheme={ useDarkTheme } />
<Routes default />
</React.Suspense>
</Root>
);
}
What will be rendered at <Routes default /> is the static pages configured in React Static's static.config.js.
Below is an example of the Hero component:
// Hero.js
import React from "react";
export default () => {
console.log("This is the Hero rendering. If this exist, the Header should be dark.");
return (
<div className="o-hero">
<p>Hero!</p>
</div>
);
}
When I run the application and look at the logs this is what I get:
The hero is present: false
This is the Hero rendering. If this exist, the Header should be dark.
How could I somehow detect the presence of the Hero from the Header although the Hero is in a router and the Header is not? This feels like quite a common use case, but I could not find any info on the interwebs.
Thanks in advance!
So I ended up using useContext to provide all children with a getter and a setter for the Header's theme (dark or light). The solution is very much inspired from this answer. The solution looks like this:
// App.js
import React, { useState, useContext } from 'react'
import { Root, Routes } from 'react-static'
import { HeaderThemeContext } from "./context";
export default () => {
const { theme } = useContext(HeaderThemeContext);
const [headerTheme, setHeaderTheme] = useState(theme);
return (
<Root>
<React.Suspense fallback={ <em>Loading...</em> }>
<HeaderThemeContext.Provider value={ { theme: headerTheme, setTheme: setHeaderTheme } }>
<Header theme={ headerTheme } />
<Routes default />
</HeaderThemeContext.Provider>
</React.Suspense>
</Root>
);
}
// Hero.js
import React from "react";
import { headerThemes, setHeaderTheme } from "./context";
export default () => {
setHeaderTheme(headerThemes.DARK);
console.log("This is the Hero rendering. If this exist, the Header should be dark.");
return (
<div className="o-hero">
<p>Hero!</p>
</div>
);
}
// context.js
import React, { createContext, useContext } from "react";
export const headerThemes = {
LIGHT: "light",
DARK: "dark",
};
export const HeaderThemeContext = createContext({
theme: headerThemes.LIGHT,
setTheme: () => {}
});
// This is a hook and can only be used in a functional component with access to the HeaderThemeContext.
export const setHeaderTheme = theme => useContext(HeaderThemeContext).setTheme(theme);
This gives global access to set and get the header theme, which might not be optional, but it works for now and I think it's fine. Please let me know if there is a better way of doing this.

How can I add style to the body element with JSS?

I'm building an application using react-jss to style my components and wanted to know if it is possible to add styles to top-level elements (like html or body).
To illustrate, I have this simple NotFound component that I'm styling with react-jss. The style works fine, but the problem is the body elements has a default margin that I wanted to remove.
NotFound.js
import React from 'react';
import injectSheet from 'react-jss';
const styles = {
notFound: {
fontFamily: 'Roboto',
backgroundColor: 'blue',
color: 'white'
}
}
function NotFound({ classes }) {
return (
<div className={classes.notFound}>
NOT FOUND
</div>
)
}
export default injectSheet(styles)(NotFound);
Does anyone know if its possible to remove this margin using css-in-js? (I wanted to avoid css)
You can use the syntax introduced by jss-plugin-global
'#global': {
body: {...}
}
Also recommend creating a separate component for this and wrap your component with it. Otherwise your specific component becomes less reusable.
Just to elaborate Oleg's response, that's what I did:
1.Create my own JssProvider. Note: Had to also add jss-plugin-camel-case otherwise it wouldn't parse my properties from camelCase to lisp-case:
import App from './App';
import { create } from 'jss';
import { JssProvider } from 'react-jss';
import globalPlugin from 'jss-global';
import camelCase from 'jss-plugin-camel-case';
const jss = create();
jss.use(globalPlugin(), camelCase());
render(
<JssProvider jss={jss}>
<Router>
<App />
</Router>
</JssProvider>
document.getElementById("app")
);
2.Added my global property to my top level component
import React from "react";
import Main from "./common/ui/components/Main";
import NotFound from "./components/NotFound";
import injectSheet from 'react-jss';
const style = {
'#global': {
body: {
margin: 0
}
}
};
const App = () => {
return (
<Main>
<Switch>
<Route component={NotFound}/>
</Switch>
</Main>
);
};
export default injectSheet(style)(App);
And that was it! Worked like a charm.
EDIT: After some more research I found that I don't need step 1. Just adding the #global style to my App component did the job. I guess this jss-global plugin must be default in react-jss. (someone correct me if I'm wrong)

React Css Themr not working

My target is to have one main theme for the app and load custom vendor styles if one is set.
I have been following the tutorial of react-css-themr and I can't get it to work. The minimalistic example I could come up with is this:
my module:
import {render} from 'react-dom'
import React from 'react';
import {Item} from './components/presentational/Item';
import {ThemeProvider} from 'react-css-themr';
import style from './theme/ItemDefault.scss';
const contextTheme = {
Item: require('./theme/ItemVendor.scss'),
};
const About = () => {
return (
<ThemeProvider theme={contextTheme}>
<Item theme={style} className={style.red}/>
</ThemeProvider>
)
};
ItemDefault.scss:
.button{
color:deeppink;
}
ItemVendor.scss:
.button{
color:orangered;
}
That doesn't seem to give my any classes or any styling. Any ideas please?
The way I was wiring components was incorrect. The way to do this is as follows:
In the root component you need to have your theme provider and theme attached to it. This theme will override any child component theming.
import {render} from 'react-dom'
import React from 'react';
import {ThemeProvider} from 'react-css-themr';
import inlineCss from './page.scss';
import {Item} from './components/Item';
const contextTheme = {
Item: require('./theme/ItemVendor.scss'),
};
render((
<ThemeProvider theme={contextTheme}>
<Item />
</ThemeProvider>
), document.getElementById('app'));
The component itself will have it's default theming and then will be wrapped with themr API to overwrite the it's default settings.
import {render} from 'react-dom'
import React from 'react';
import { themr } from 'react-css-themr';
import defaultTheme from './Item.scss';
const DefaultItem = ({theme}) => {
return (
<div className={theme.button} >
Example item
</div>
)
};
export const Item = themr('Item', defaultTheme)(DefaultItem);
I put together a github repo showing how to use this:
https://github.com/adamgajzlerowicz/react-css-themr

Categories