I have a theme option I capture via API call. I want this theme to correspond with css files (or combinations of them) I have created in the Vue 3 app (using vite).
These theme files I would like to 'layer' so I can have base.css, and style5.css which would append to that.
I'm also using tailwind.
main.pcss
/* tailwind base */
#import 'tailwindcss/base';
#import 'tailwindcss/components';
#import 'tailwindcss/utilities';
The current way I'm doing this (below) I don't like it for several reasons, but it was the only way I could get it working where the CSS would import properly, and would not cause a FOUC. I want the component and the css to load together.
So these are the things I'm hoping to achieve
No FOUC as the sheets load in the order I specify
They use global class styles (not scoped), compiled by tailwind with #apply, etc.
I would love to remove the component entirely and just import CSS files, to remove this layer of indirection, but when I tried that with yarn build the layout had none of css applied.
Should multiple separate css files I can combine, like base.css, style1.css style1.css, style2.css (if I want to make 1 tweak to style 1). As you see below I can't layer them, they just have to be copy pasted in full which is obviously not ideal
I want to make sure each 'theme' (group of css) is bundled and not all themes are bundled together (for file size reasons). This is why im using the async component
My layout component
<template>
<component v-if="theme" :is="themeComponent">
<slot />
</component>
</template>
<script>
import { computed, defineAsyncComponent } from 'vue';
import { useStore } from '#/store';
export default {
setup() {
const store = useStore();
const theme = computed(() => store.theme);
const themeComponent = defineAsyncComponent(() => {
if (theme.value === 'STYLE_1') return import('#/themes/Style1.vue');
if (theme.value === 'STYLE_2') return import('#/themes/Style2.vue');
return import('#/themes/StyleDefault.vue');
});
return {
theme,
themeComponent,
};
},
};
</script>
Here is my Style1.vue component:
<template>
<slot />
</template>
<style>
/* font */
#import url('https://fonts.googleapis.com/css2?family=Inter:wght#400;500;600;700&display=swap');
/* base */
body {
#apply text-slate-800 bg-white;
font-family: 'Inter', sans-serif;
}
.link {
#apply hover:underline;
}
.wrapper {}
.container {
#apply px-4 mx-auto max-w-[750px];
}
.header {
#apply bg-gray-50;
}
.content {
#apply pb-20 border-t-2 border-b-2 border-blue-300 ;
}
...
You can import your CSS file directly:
if (theme.value === 'STYLE_1') {
import('#/assets/styles/style1.scss')
}
But, since you import your CSS dynamically, there is no way to avoid FOUC. Because the CSS is loaded after JS and after the DOM loaded
Related
Solved - TLDR; Adding import '#fortawesome/fontawesome-svg-core/styles.css' to the _app.js / index.js file fixes the issue and FontAwesome works as intended. My issue was caused by npx-create-next-app including purgeCSS by default, which in turn stripped out the FontAwesome required styles.
I'm using FontAwesome in my Next app. I followed the React guide on the FA website and the icon SVG's are being output on the page. Problem is, none of the features work and they don't scale with font-size as they're meant to.
I don't want to hack it together by manually targeting the SVG's and adding size etc. as it's not ideal when it comes to responsiveness. i.e. it would be nice to have icons scale with accompanying text and the ability to add 'spinner', 'fixedWidth' etc.
Strangely, they have started working once or twice but then break again and I can't seem to reproduce.
// package.json
"dependencies": {
"#fortawesome/react-fontawesome": "^0.1.14",
"#fortawesome/fontawesome-svg-core": "^1.2.34",
"#fortawesome/pro-regular-svg-icons": "^5.15.2",
}
// _app.js
import { library } from '#fortawesome/fontawesome-svg-core'
import { faHeart } from '#fortawesome/pro-regular-svg-icons'
library.add( faHeart )
// header.js
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
export default function Header() {
return (
<header>
<FontAwesomeIcon icon={['far', 'heart']} spin />
</header>
)
}
// style.css
header {
font-size: 20px; (does nothing to the icon)
}
svg {
width: 20px; (works, but this shouldn't be required according to FA docs)
}
I've also tried individual use (importing icons into individual components, rather than utilising the library function) to the same effect. Like so:
// header.js
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faHeart } from '#fortawesome/pro-regular-svg-icons'
export default function Header() {
return (
<header>
<FontAwesomeIcon icon={faHeart} spin />
</header>
)
}
Fixed it. The issue was purgeCSS which was added to the project when using npx-create-next-app. purgeCSS was purging the required FontAwesome styles.
Explicitly importing the FontAwesome styles fixed the issue.
Specifically, I added import '#fortawesome/fontawesome-svg-core/styles.css' to _app.js.
According to the doc, The react-fontawesome component integrates well with Next.js but there is one caveat you need to solve. Since Next.js manages CSS differently
In your project entry, probably App.js
import { config } from '#fortawesome/fontawesome-svg-core'
import '#fortawesome/fontawesome-svg-core/styles.css'
config.autoAddCss = false
Next.js allows you to import CSS directly in .js files. It handles optimization and all the necessary Webpack configuration to make this work.
import '#fortawesome/fontawesome-svg-core/styles.css'
You change this configuration value to false so that the Font Awesome core SVG library will not try and insert elements into the of the page. Next.js blocks this from happening anyway so you might as well not even try.
config.autoAddCss = false
I use FontAwesomeIcon in my React apps like this and it works:
import { faHeart} from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
in the code:
<FontAwesomeIcon className="icon" icon={faHeart} />
and in css:
.icon{
color: ; / if you want to change color
font-size: 36px;
}
Essentially, all you need to do is:
import the icon:
import { yourIcon} from "#fortawesome/free-solid-svg-icons";
and use it:
<FontAwesomeIcon icon={yourIcon} />
You can add a classname to the icon and use that class to style it.
<FontAwesomeIcon icon={yourIcon} className="styled-icon" />
Here is a good video on adding font awesome icons to next.js: https://youtu.be/kaA2aX4X3NU
I am trying to style the MUI slider,so I decided to style it using the className prop. But the style applied to the main class does'nt get applied,while rest other styles like 'hover' state get applied. If I remove all the classes and just style it using SX prop,everything works fine. But I want to keep the styles seperate into an external css file.
Below is my code :
App.css
.container{
margin-left: 30%;
margin-top: 20%;
}
/* This does'nt get applied */
.slider {
color: #ff0000;
width: 300px;
}
.slider:hover {
color: #2e8b57;
}
.slider > .MuiSlider-thumb {
border-radius: 1px;
}
App.js
import "./App.css"
import * as React from 'react';
import Slider from '#mui/material/Slider';
export default function App() {
return (
<div className="container">
<Slider className="slider" defaultValue={30} />
</div>
);
}
The problem is with Material UI style injection order. The custom styles do apply, but Mui styles are injected before the custom style so they doesn't have effect in this case.
This guide explain how to change the injection order:
https://mui.com/guides/interoperability/#css-injection-order
I don't know if it is required, but I use css modules and material-ui. You can rename your css file to
App.module.css
then import like so
import styles from "./App.module.css"
you can then use it like
<Slider className={styles.slider} defaultValue={30} />
In Nextjs you can throw everything in styles.css to make it global, but I don't know if that is also for react as well.
I have a React component with the following files:
src/components/HomePage/index.js
src/components/HomePage/style.scss
The component is very simple:
import React from 'react';
import './style.scss';
const HomePage = () => {
return (
<div className="homepage">
<h1>Landing page</h1>
</div>
);
};
export default HomePage;
Within style.scss I am applying a style to all <h1> tags:
h1 {
color: #f3f3f3;
font-family: "Cambria";
font-weight: normal;
font-size: 2rem;
}
And it works as expected. However, I now see that the h1 style within styles.scss is being applied to every h1 on my site, even on pages that do not use this component.
I am using Gatsby, but it's a React app at heart. My understanding is that React's code-splitting feature would take care of this, that the code from style.scss would only be included in bundles for any page that uses my component.
It's the why that I am asking about. I have two easy fixes:
Wrap everything in style.scss in a .homepage wrapper
Use CSS modules and rename the file to style.module.scss. When I see people do that they always do `import style from './style.module.scss' - is there a way to have CSS modules without assigning it to an object like that?
Update: Coming back to this question after spending a lot of time with React and I think there's a gap in the market for React styling. CSS modules is syntactically poor in my opinion and having to manually wrap everything in a .home tag to localise it is manual work I don't want to do. Someone should really create a React plugin that automatically does this so that whenever I have a file called Home.js and I import Home.css that all the CSS is automatically restricted to Home.js without me having to do anything special.
If you want to localize CSS rules, then you would have to switch to modular stylesheets (works the same for sass stylesheets).
In your current structure, the component imports non-modular stylesheet and doesn't localize the changes with a unique identifier. Therfore added rules live in a global scope without a unique identifier that would localize them so that only selected components could understand them. That means that they are capable of easily overwriting the same-named rules which were previously established (import order matters here, because it would dictate how the bundler appends the output stylesheet).
So instead of holding component-related rules within ./style.scss file, rename it to ./index.module.scss and then you would utilize it within the component like so:
import React from 'react';
import styles from './index.module.scss';
const HomePage = () => {
return (
<div className={style.homepage}>
<h1 className={style.heading}>Landing page</h1>
</div>
);
};
export default HomePage;
and your stylesheet would look like:
.heading {
color: #f3f3f3;
font-family: "Cambria";
font-weight: normal;
font-size: 2rem;
}
disclaimer:
I've changed the styling convention from selecting elements by their tag, to selecting them by class, because targetting elements by tag is widely considered a bad practice [ref] , but if you want to maintain it, then you would have to provide a parent scope for such a rule (it already exists since the parent <div/> element has an assigned class.
In this case the implementation would look like:
import React from 'react';
import styles from './index.module.scss';
const HomePage = () => {
return (
<div className={style.homepage}>
<h1>Landing page</h1>
</div>
);
};
export default HomePage;
and styles:
.homepage {
h1 {
color: #f3f3f3;
font-family: "Cambria";
font-weight: normal;
font-size: 2rem;
}
}
You can either go with the easiest way that is mentioned below.
import React from 'react';
import './style.scss';
const HomePage = () => {
return (
<div className = "home">
<div className="homepage">
<h1>Landing page</h1>
</div>
</div>
);
};
export default HomePage;
You can wrap whole html inside one div of particular component name
CSS:
.home h1 {
color: #f3f3f3;
font-family: "Cambria";
font-weight: normal;
font-size: 2rem;
}
This is the easiest way. However this is my personal solution because I also face the same issues when I was beginner at react.
At the moment, my webpack is bundling my CSS styles like this:
//this css is going first, it's supposed to go last
.rbc-btn {
color: red;
}
//this css must be first
.myStyle {
color: green;
}
What I want is to bundle my CSS styles in a specific order, something like this:
.myStyle {
color: green;
}
.rbc-btn {
color: red;
}
How can I fix this with webpack?
Thanks! :)
There was a similar bug that was fixed with extract-text-webpack-plugin#3.0.0, ensure you're using the same version or newer.
If that doesn't help, a common mistake is to load a component first and then loading the CSS files. It has become a common pattern to make every component to import their own styles which can change the style order in webpack if your component is loaded first.
Considering you have index.js like this:
import MyApp from './myApp'
import './myStyle.css'
It means to Webpack that every style imported in './myApp' will be loaded first, so styles applied 'myStyle.css' will appear below other styles, thus overriding them.
The fix could potentially be just changing orders
import './myStyle.css' // parent component imports style first
import MyApp from './myApp' // imports your component along with any other styles
Adding on to Cezar Augusto's answer:
If you have module.css, the import order of the components whose module.css is being bundled together will impact the order!
So for me I needed #import for fonts in my css to be bundled on top first, so in my index.js file, I needed to import my module.css file with my #import first before importing my components whose module.css needed to be bundled later.
In a Polymer app I want to give the users the option to choose a certain theme from a set provided. So let's say that in a wrapper element I have a property called "theme" which holds a value like "dark", "light", etc. I would like to include a certain file with custom styles depending on that value.
So I have an element my-app-wrapper that includes some other ones if the user is not authenticated, or one called my-app for those that are. Now I tried to refactor it so that I have a new element called my-app-dark that extends my-app and just adds the import to the custom styles I need.
So in a file, let's say dark.html I have something like:
<custom-style>
<style is="custom-style">
html {
--custom-theme: {
--app-primary-color: #990AE3;
--app-secondary-color: var(--paper-deep-orange-500);
}
;
}
</style>
</custom-style>
And in my-app-wrapper I have something like this:
<template is="dom-if" if="[[_equals(theme, 'dark')]]" restamp>
<my-app-dark></my-app-dark>
</template>
<template is="dom-if" if="[[!_includes(themes, theme)]]" restamp>
<my-app></my-app>
</template>
The problem here is that in the wrapper element I need to import both my-app and my-app-dark. So even if I have that if statement and I use my-app the custom style imported by my-app-dark is still loaded and it applies its styles.
My only restriction is that I can't use lazy imports and load the file with Polymer.importHref, but even if I could, the import would happen after the CSS rules are parsed so it wouldn't work.
I experimented with theme changes with polymer 1.x using events and changing the theme programmatically.
In the main may-app.html file I set colors to variables in the host element:
<style include="shared-styles">
:host {
--some-color: black;
}
</style>
These css color values were used throughout the child elements.
When a change-theme event was called, I used this.customStyle['--some-color'] = 'white'; and Polymer.updateStyles(); to apply the color change.
_onChangeTheme() {
if (this.darkTheme) {
//change to light theme
this.customStyle['--some-color'] = 'white';
this.set('darkTheme', false);
} else {
//change to dark theme
this.customStyle['--some-color'] = 'black';
this.set('darkTheme', true);
}
Polymer.updateStyles();
}
In Polymer 2 it should probably look something like this:
_onChangeTheme() {
if (this.darkTheme) {
//change to light theme
this.updateStyles('--some-color','white');
this.set('darkTheme', false);
} else {
//change to dark theme
this.updateStyles('--some-color','black');
this.set('darkTheme', true);
}
}
The easiest way would be to just set a class on one of the top elements, and then use CSS.
<style>
.some-top-element.dark {
background-color: #000
color: #fff;
}
.some-top-element.light {
background-color: #fff;
color: #000;
}
</style>
<div class$="some-top-element [[theme]]">
Change the property theme to set a new CSS class. It's as simple as that. Do note the $ in the class property.