I am trying to move my old static HTML project into Next.js, but the extra
<div id="__next">
is blocking some of class in my css stylesheet(eg. body > section).
I tried to remove the extra div in inspect mode and it works. Just wondering how to remove it from the rendering? Thanks a bunch.
For class based react (can also use in constructor, componentWillMount will be deprecated)
componentWillMount() {
let tar = document.getElementById('__next');
tar.parentNode.innerHTML = tar.children[0].innerHTML;
}
For react hook (Functional component)
useEffect(()=>{
let tar = document.getElementById('__next');
tar.parentNode.innerHTML = tar.children[0].innerHTML;
})
Related
I am looking for a solution that will allow some styling options in a CMS that can dynamically change the classes of specific components in my NextJS app. My code looks like this:
pages/index.js:
...
import client from "../lib/client";
const Home = ({ headerConfig }) => {
return (
<>
<Header headerConfig={headerConfig} />
...
</>
);
};
export const getServerSideProps = async () => {
const headerConfig = await client.getDocument("headerConfig");
return {
props: { headerConfig },
};
};
export default Home;
components/Header.jsx:
const Header = ({ headerConfig }) => {
return (
<nav className={`relative ... ${headerConfig.bgColour}`}>
...
</nav>
);
}
export default Header
However, the styling does not apply and the background colour remains unchanged although the class does seem to be injected into the class attribute on the browser.
I know my current method is incorrect but I am clueless as to how to fix this. Could someone help point me in the right direction?
I assume that you are using tailwind. If so, you cannot inject classnames into an html element. This is because tailwind only includes classes that are explicitly declared somewhere within your code (it will find classes within any string in your project depending on the configuration). You can get around this problem by adding classes to the safelist array in your tailwind.config.js file. You can also safelist classes with regex to allow all variants of certain utilities.
However, safelisting only works if there are a specific set of classes that could potentially be injected. One option, which will be guaranteed to work but NOT RECOMMENDED, is to add a <link> in your html to the tailwind cdn. However this will include every single tailwind class in your css bundle, making it MUCH larger and your website slower.
Another solution is to use inline styles which are calculated with javascript depending on the classes you need to inject. If you are dealing with only simple parts of tailwind (like padding, margin, or other sizing units), this may be a good approach. For example a class like p-4 would get converted to padding: 1rem in your inline styles.
Depending on the needs of your application, one of these three approaches is probably the way to go. Hope this helps!
I am using Angular, my goal is to be able to use a string declared in typescript inside a CSS file. I am trying to set the background image of a navbar component. Later on, the background image path will be received from a database service, that's why I need it to be in the typescript file. I read something about using [ngStyle], but the img will not be updated, I just need the paths to be received from a database. Should I still try to use it? And how? I am a bit lost.
My typescript file has something like:
// ...
export class NavbarComponent{
background_url='../../../assets/img/background.png';
constructor() { }
// ...
And in my CSS file i want to do something like:
nav{
background-image: background_url;
}
However, this isn't working for me.
How could I better approach this? Thanks
I think it's not possible to access the ts file variables from the CSS file, but you can get elements from DOM and set style to that from the ts file.
an example:
document.getElementById('element').style.backgroundImage = background_url;
also if you are using frameworks like angular, you can use #ViewChild to get elements from DOM and style them by using the renderer2 library like this:
export class NavbarComponent {
#ViewChild('element') element: ElementRef;
background_url='../../../assets/img/background.png';
constructor(private renderer: Renderer2) {}
setStyle() {
this.renderer.setStyle(
this.element.nativeElement,
'background-image',
this.background_url
);
}
}
and then call the setStyle function where ever you want.
more from renderer2: https://angular.io/api/core/Renderer2
I have an application where I'm using Material UI and its theme provider (using JSS).
I'm now incorporating fullcalendar-react, which isn't really a fully fledged React library - it's just a thin React component wrapper around the original fullcalendar code.
That is to say, that I don't have access to things like render props to control how it styles its elements.
It does however, give you access to the DOM elements directly, via a callback that is called when it renders them (eg. the eventRender method).
Here's a basic demo sandbox.
Now what I'm wanting to do is make Full Calendar components (eg, the buttons) share the same look and feel as the rest of my application.
One way to do this, is that I could manually override all of the styles by looking at the class names it's using and implementing the style accordingly.
Or - I could implement a Bootstrap theme - as suggested in their documentation.
But the problem with either of these solutions, is that that:
It would be a lot of work
I would have synchronisation problems, if I made changes to my MUI theme and forgot to update the calendar theme they would look different.
What I would like to do is either:
Magically convert the MUI theme to a Bootstrap theme.
Or create a mapping between MUI class names and the calendar class names, something like:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary
I wouldn't mind having to massage the selectors etc to make it work (ie. For example - MUI Buttons have two internal spans, whereas Full Calendar have just one). It's mostly about when I change the theme - don't want to have to change it in two places.
Using something like Sass with its #extend syntax would is what I have in mind. I could create the full-calendar CSS with Sass easily enough - but how would Sass get access to the MuiTheme?
Perhaps I could take the opposite approach - tell MUI 'Hey these class names here should be styled like these MUI classes'.
Any concrete suggestions on how I would solve this?
Here is my suggestion (obviously, it's not straight forward). Take the styles from the MUI theme and generate style tag based on it using react-helmet. To do it event nicely, I created a "wrapper" component that do the map. I implemented only the primary rule but it can be extended to all the others.
This way, any change you will do in the theme will affect the mapped selectors too.
import React from "react";
import { Helmet } from "react-helmet";
export function MuiAdapter({ theme }) {
if (!theme.palette) {
return <></>;
}
return (
<Helmet>
<style type="text/css">{`
.fc-button-primary {
background: ${theme.palette.primary.main}
}
/* more styles go here */
`}</style>
</Helmet>
);
}
And the use of the adapter
<MuiAdapter theme={theme} />
Working demo: https://codesandbox.io/s/reverent-mccarthy-3o856
You could create a mapping between MUI class names and the calendar class names by going through ref's. It's possible that this is not what some would call "best practice"...but it's a solution :). Note that I updated your component from a functional component to a class component, but you could accomplish this with hooks in a functional component.
Add refs
Add a ref to the MUI element you want to set as a reference, in your case the Button.
<Button
color="primary"
variant="contained"
ref={x => {
this.primaryBtn = x;
}}
>
And a ref to a wrapping div around the component you want to map to. You can't add it directly to the component since that wouldn't give us access to children.
<div
ref={x => {
this.fullCal = x;
}}
>
<FullCalendar
...
/>
</div>
Map classes
From componentDidMount() add whatever logic you need to target the correct DOM node (for your case, I added logic for type and matchingClass). Then run that logic on all FullCalendar DOM nodes and replace the classList on any that match.
componentDidMount() {
this.updatePrimaryBtns();
}
updatePrimaryBtns = () => {
const children = Array.from(this.fullCal.children);
// Options
const type = "BUTTON";
const matchingClass = "fc-button-primary";
this.mapClassToElem(children, type, matchingClass);
};
mapClassToElem = (arr, type, matchingClass) => {
arr.forEach(elem => {
const { tagName, classList } = elem;
// Check for match
if (tagName === type && Array.from(classList).includes(matchingClass)) {
elem.classList = this.primaryBtn.classList.value;
}
// Run on any children
const next = elem.children;
if (next.length > 0) {
this.mapClassToElem(Array.from(next), type, matchingClass);
}
});
};
This is maybe a little heavy handed, but it meets your future proof requirement for when you updated update Material UI. It would also allow you to alter the classList as you pass it to an element, which has obvious benefits.
Caveats
If the 'mapped-to' component (FullCalendar) updated classes on the elements you target (like if it added .is-selected to a current button) or adds new buttons after mounting then you'd have to figure out a way to track the relevant changes and rerun the logic.
I should also mention that (obviously) altering classes might have unintended consequences like a breaking UI and you'll have to figure out how to fix them.
Here's the working sandbox: https://codesandbox.io/s/determined-frog-3loyf
After loading a css file like this:
const themes = ['dark-theme.css', 'light-theme.css'];
async function loadcss(file) {
return await import(file);
}
loadcss(themes[0]).then(console.log)
The console output is an empty object for me and a new annonymous < style> tag sits in the < head> of my index.html. So far so good, but what if I (in this example) want to change the theme to light-theme.css. That would merge both themes as dark-theme.css is already loaded.
Is there a way to remove the < style> tag from the DOM?
To furthermore specify my question, the provided example shows an abstracted behaviour and I am only interested in removing the dynamically loaded css from the DOM.
I don't know vue.js but here is simple example in React hope it helps somehow :) perhaps some ideas at least :)
class TodoApp extends React.Component {
static themes = {
dark: 'dark-theme.css',
light: 'light-theme.css',
};
render() {
return ReactDOM.createPortal(
(<link rel="stylesheet" href={TodoApp.themes.dark} type="text/css"></link>),
document.head
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
http://jsfiddle.net/3y4hw2ox/
Thanks to OZZIE, I questioned my methodology and found, that importing the css files, like my question shows (through ES6 import, or require.context(...)), is not usefull, as we can't identify it, we dont get access to the <style> element, leaving us with no entry to the DOM and no way to manipulate it.
Instead we will link the css files manually in the <head>, as we know their name and path.
const themes = ['dark-theme.css', 'light-theme.css'];
const head = document.body.parentElement.firstElementChild;
const link = document.createElement('link');
link.setAttribute('href', process.env.BASE_URL + themes[0]);
link.setAttribute('id', themes[0]); // set id so we can remove it later
head.appendChild(link);
I have two routes on my React app: /a and /b.
For /a, I want the body css tag to have a background-color: red;.
For /b, I want the body css tag to have a background-color: blue;.
Both components a and b live in different .JSX files, and both import their own main.scss file which defines their own respective body background-color.
However, since the entire app is compiled into the body tag, there seems to be a conflict, and only one of the body tags is respected for both routes.
<body>
<script src="bundle.js" type="text/javascript"></script>
</body>
The reason I want it on the body tag and not just a container div is that I want the background-color to be visible when I scroll outside the bounds of the page (the bounce effect on Mac and iOS).
What's the proper way to do this?
That's happening because when you import your styles in your component without CSS Modules, the styles are global so your body style is defined two times (you can see all the styles in the <head> tag).
You can fix that by setting the background color in your component componentDidMount() method.
Example
componentDidMount(){
document.body.style.backgroundColor = "red"// Set the style
document.body.className="body-component-a" // Or set the class
}
or, if youre using functional components you can do the same with useEffect...
useEffect(() => {
document.body.className = 'class-name';
}, []);
I agree with what QoP said but, as an add on to that, you should also make sure to use componentWillUnmount to set it back to whatever it normally is outside that component.
for example:
if normally for the whole application text-align is left but for one component you want it to be center, but after the component it needs to return to being left, you will do the following:
componentDidMount() {
document.body.style.textAlign = "center"
}
componentWillUnmount(){
document.body.style.textAlign = "left"
}
Add this code
componentDidMount(){
document.body.style.backgroundColor = "white"
}
Hope to help.