How do I import a 2nd CSS framework (prefixed) into my Vite-based Vue 3 project (1st framework being TailwindCSS) - css

So I have a default Laravel app working with Vue 3 and assets configured by Vite, TailwindCSS and TailwindUI installed. This all works fine.
I understand to have another CSS framework, I need to prefix it to avoid clashes. According to these instructions, I need to add the following line: (after installing via npm):
import PrimeVue from 'primevue/config'; //I have included this in app.js
as well as reference these styles:
primevue/resources/themes/saga-blue/theme.css
primevue/resources/primevue.min.css
primeicons/primeicons.css
How exactly do I reference these css files with a prefix so as to avoid clashes?
My postcss.config.js file currently looks like this:
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
My app.js looks like this:
import './bootstrap';
import '../css/app.css';
import { createApp, h } from 'vue';
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
/* added by me*/ import PrimeVue from 'primevue/config';
/* added by me*/ import InputMask from 'primevue/inputmask';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(ZiggyVue, Ziggy)
.use(PrimeVue)
/*added by me*/ .component('InputMask', InputMask)
.mount(el);
},
});
InertiaProgress.init({ color: '#4B5563' });

Related

Why my Vue3 Webcomponent has no css in its shadow dom

I have built a Vue3 web component and try to embed it in an other project.
This is my main.js for building the web component.
import {createApp, getCurrentInstance, h} from 'vue'
import App from './App.vue'
import {createPinia} from 'pinia'
import i18n from './services/i18n'
import {router} from '#/helpers'
import VueApexCharts from "vue3-apexcharts";
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap"
import './assets/main.css'
import './assets/sweetalert2.min.css'
import contextmenu from "v-contextmenu";
import "v-contextmenu/dist/themes/default.css";
import {defineCustomElement} from "vue";
const clickOutside = {
beforeMount: (el, binding) => {
el.clickOutsideEvent = event => {
// here I check that click was outside the el and his children
if (!(el == event.target || el.contains(event.target))) {
// and if it did, call method provided in attribute value
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted: el => {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
const pinia = createPinia()
export const createElementInstance = ({
component = null,
props = [],
sharedStoreInstance = false,
plugins = [],
renderOptions = {}
} = {}) => {
return defineCustomElement({
props: props,
setup() {
const app = createApp();
if (!sharedStoreInstance) {
const pinia = createPinia();
app.use(pinia);
}
app
.use(router)
.use(VueApexCharts)
.use(pinia)
.use(contextmenu)
.use(i18n)
.directive("click-outside", clickOutside)
const inst = getCurrentInstance();
Object.assign(inst.appContext, app._context);
Object.assign(inst.provides, app._context.provides);
},
render: () => h(component, renderOptions)
})
}
const config = {
component: App,
props: {title: String},
sharedStoreInstance: true,
renderOptions: {ref: 'component'}
}
customElements.define('ma-ansicht', createElementInstance(config, {
shadow: true
}));
I have 2 files to import in the other project
<script type="module" crossorigin src="/assets/index.3f46efca.js"></script>
<link rel="stylesheet" href="/assets/index.ac2fca12.css">
The shadow dom of the component doesnt have any css and the generated css file appears in the header of the skeleton application.
How can I build a Web Component which includes my css in the shadow dom?
I have tried different build options like npm run build and
vue-cli-service build --target lib --name ma-ansicht src/main.js
I have tried to embed the wc in different skeleton apps
I expect that my app as web component is fully functional and styled

How to add vue plugins to Vite?

when i was using webpack instead of vite i used to write this code in app.js
// for File uploads
import vueFilePond from "vue-filepond";
import "filepond/dist/filepond.min.css";
// image preview in file pond
import FilePondPluginImagePreview from "filepond-plugin-image-preview";
import "filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css";
import FilePondPluginFilePoster from "filepond-plugin-file-poster";
import "filepond-plugin-file-poster/dist/filepond-plugin-file-poster.css";
// file size validations
import FilePondPluginFileValidateSize from "filepond-plugin-file-validate-size";
import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type";
const FilePond = vueFilePond(
FilePondPluginImagePreview,
FilePondPluginFilePoster,
FilePondPluginFileValidateSize,
FilePondPluginFileValidateType
);
but now with Vite this is not working.
i tried adding
export default defineConfig({
plugins: [
laravel({
input: [
'resources/js/app.js',
'resources/css/app.css',
],
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
vueFilePond *****************************this************
],
resolve: {
alias: {
'$': 'jQuery',
},
},
});
this is not working. Please help!
So this worked for me!
// for File uploads
import vueFilePond from "vue-filepond";
import "filepond/dist/filepond.min.css";
// image preview in file pond
import FilePondPluginImagePreview from "filepond-plugin-image-preview";
import "filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css";
import FilePondPluginFilePoster from "filepond-plugin-file-poster";
import "filepond-plugin-file-poster/dist/filepond-plugin-file-poster.css";
// file size validations
import FilePondPluginFileValidateSize from "filepond-plugin-file-validate-size";
import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type";
const FilePond = vueFilePond(
FilePondPluginImagePreview,
FilePondPluginFilePoster,
FilePondPluginFileValidateSize,
FilePondPluginFileValidateType
);
Vue.use(AnyComponent) ; THis was not working in my case
So i tried adding component in below code. this worked for me.
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(ZiggyVue, Ziggy)
**.mixin({ components: { FilePond } })**
.mount(el);
},
});

#aws-amplify/ui-react how to customize UI if my project uses SASS?

I'm extending a Next.js (React) project that was built by someone else, which uses .scss files (SASS) for styling. This is the project in question https://github.com/codebushi/nextjs-starter-dimension
Now I'm adding an authentication flow to the project using #aws-amplify/ui-react. Everything works fine, but I want to customize the UI style. I've found in the documentation that I can do that through :root in globals.css, as so:
:root {
--amplify-primary-color: #ff6347;
--amplify-primary-tint: #ff7359;
--amplify-primary-shade: #e0573e;
}
Documentation here: https://docs.amplify.aws/ui/customization/theming/q/framework/react
I know pretty much nothing about SASS except the basics. How would I do the equivalent of setting those variables in :root?
Edit with more details
This is my next.config.js:
module.exports = {
webpack: (config, { dev }) => {
config.module.rules.push(
{
test: /\.scss$/,
use: ['raw-loader', 'sass-loader']
}
)
return config
}
}
This is my authentication page where the Amplify elements are defined:
import { useState, useEffect } from 'react'
import { AuthState, onAuthUIStateChange } from '#aws-amplify/ui-components';
import { AmplifyAuthenticator, AmplifyAuthContainer, AmplifySignUp, AmplifySignIn, AmplifySignOut } from '#aws-amplify/ui-react';
import Router from 'next/router'
const Auth = (props) => {
const [authState, setAuthState] = useState();
const [user, setUser] = useState();
useEffect(() => {
return onAuthUIStateChange((nextAuthState, authData) => {
setAuthState(nextAuthState);
setUser(authData);
//console.log(`authData: ${JSON.stringify(authData, null, 2)}`);
});
}, []);
if (authState === AuthState.SignedIn && user) {
Router.push('https://mywebsite')
return <p>Redirecting...</p>
}
return (
<AmplifyAuthContainer>
<AmplifyAuthenticator usernameAlias="email">
<AmplifySignUp
slot="sign-up"
usernameAlias="email"
formFields={[
{
type: "email",
label: "Email Address *",
placeholder: "Enter your email address",
inputProps: { required: true },
},
{
type: "password",
label: "Password *",
placeholder: "Enter your password",
inputProps: { required: true },
},
]}
/>
<AmplifySignIn slot="sign-in" usernameAlias="email" />
</AmplifyAuthenticator>
</AmplifyAuthContainer>
);
}
export default Auth;
As per the answer below from Sean W, I've already tried creating an _app.js with:
import '../styles/global.scss'
const App = ({ Component, pageProps }) => {
return <Component {...pageProps} />
}
export default App;
with global.scss:
:root {
--amplify-primary-color: #ff6347;
--amplify-primary-tint: #ff7359;
--amplify-primary-shade: #e0573e;
}
But the CSS variables don't seem to be replaced.
Include the styles in your pages. The easiest way is to create a new SCSS file and include it in a custom _app.js.
By default, Next supports SASS - you only need to install the sass npm package and likely do not need a custom next.config. This could be one of your problems.
file - global.scss -
:root{
--amplify-primary-color: #ff6347;
--amplify-primary-shade: #e0573e;
}
//scoped & redeclared to an element with id #__next
:root #__next {
--amplify-primary-color: #ff6347;
--amplify-primary-shade: #e0573e;
}
Amplify could also be setting setting styles after you have already defined them for the :root - if this is the case you will need to scope your custom styles to take precedence over the default Amplify CSS variables. To do this - redeclare them in a CSS rule that is more specific than :root like the example above - keeping order of precedence in mind
Amplify also lets you directly target components.
amplify-authenticator {
--amplify-primary-color: #ff6347;
background: var(--amplify-primary-color);
padding: 5px;
}
// scoped & redeclared
:root #__next amplify-sign-in{
--amplify-primary-color: #ff6347;
}
The amplify elements that can be targeted are
amplify-authenticator
amplify-sign-in
amplify-confirm-sign-in
amplify-sign-up
amplify-confirm-sign-up
amplify-forgot-password
amplify-require-new-password
amplify-verify-contact
amplify-totp-setup
import your file - pages/_app.js
import 'path/to/global.scss';
const App = ({ Component, pageProps }) => <Component {...pageProps} />;
export default App;
Global variables (the equivalent of :root variables) can be created simply with this line.
$color = #c0ff33 !important;
The content can be anything that is available in css and partially sass.
For large projects, creating a variables.scss file and including in it all these variable declarations can be truly helpful for changing multiple variables at once. To include this file, just do #import "./variables.scss" at the top of your file.
Hope this helps! Good luck in your sassy endeavors!

PurgeCSS does not remove unused CSS from NextJS project

I'm trying to remove unused css from my NextJS project using PurgeCSS. However, I'm having difficulty getting the most basic integration of PurgeCSS into my project to work.
I'm using this documentation: https://www.purgecss.com/guides/next.
My next.config file looks like this:
// next.config.js
const withCss = require('#zeit/next-css')
const withPurgeCss = require('next-purgecss')
module.exports = withCss(withPurgeCss())
Here is my component:
import React from 'react'
import App from 'next/app'
export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<>
<style jsx global>{`
.purgecss-test {
color: red;
}
`}</style>
<Component {...pageProps} />
</>
)
}
}
When I do a global search in my code base for 'purgecss-test', I only get the one result that's in the component above, so I am expecting that style to be removed during the build. However, when my app builds and I navigate to the page and inspect the source, I can still see it there:
<style amp-custom="">.purgecss-test{color:red;}
Try customizing PostCSS instead as per this page (ignore tailwindcss): https://nextjs.org/learn/basics/assets-metadata-css/styling-tips
A bit simplified, install #fullhuman/postcss-purgecss and postcss-preset-env, then create a postcss.config.js file in the top-level directory and enter this in it:
module.exports = {
plugins: [
[
'#fullhuman/postcss-purgecss',
{
content: [
'./pages/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}'
],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
}
],
'postcss-preset-env'
]
};

Generate RTL CSS file in create-react-app and switch between them based on change in state

I'm using create-react-app for a multi-language project.
I want to use some library like "cssJanus" or "rtlcss" to convert the Sass generated CSS file into a separate file and then use that newly generated file when I switch to another language.
Here's how my index.js looks like ...
import React from "react";
import ReactDOM from "react-dom";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter as Router } from "react-router-dom";
import { Provider } from "react-redux";
import App from "./App";
import { configureStore } from "./store/configureStore";
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>,
document.getElementById("root")
);
serviceWorker.unregister();
And here's how my "App.js" looks like ...
import React, { Component } from "react";
import "./App.scss";
import { Route, Switch } from "react-router-dom";
import SignIn from "./features/signin/SignIn";
class App extends Component {
render() {
return (
<>
<Switch>
<Route path="/" exact component={SignIn} />
</Switch>
</>
);
}
}
export default App;
As you can see I'm using "./App.scss" file that simply have a bunch of #import statements to another ".scss" files in the "./src/css/" directory ...
/* autoprefixer grid: on */
#import "css/reset";
#import "css/variables";
#import "css/global";
I need your advice on how to do that. How to convert the generated CSS from App.scss to RTL into their own .css file and switch between them and the original generated CSS based on a change in the global state.
I searched a lot for something like this but with no luck.
Or if you have a better approach I'm all ears.
Here is a simple solution that requires ejecting and adding a lightweight webpack-rtl-plugin.
After running
npx create-react-app react-rtl
cd react-rtl
yarn eject
yarn add -D webpack-rtl-plugin #babel/plugin-transform-react-jsx-source
Go to config/webpack.config.js and make some tweaks:
// import the plugin
const WebpackRTLPlugin = require('webpack-rtl-plugin')
// ...
module: { ... }
plugins: [
// ...,
// use the plugin
new WebpackRTLPlugin({ diffOnly: true })
].filter(Boolean),
// ...
On this stage, if you run yarn build and look up build/static/css folder, you should hopefully see additional .rtl.css file that contains your rtl styles.
Then we need to tell webpack to use MiniCssExtractPlugin.loader for development as well so it will serve styles through link tags instead of inline styles:
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && { loader: MiniCssExtractPlugin.loader }, // <-- use this
// isEnvDevelopment && require.resolve('style-loader'), <-- instead of this
and don't forget the plugin, lol:
module: { ... }
plugins: [
// ...,
// isEnvProduction && <-- comment this out
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
// ...
].filter(Boolean),
And from here you can finally grab your default stylesheet href and use to insert rtl styles. Here's how you could implement it:
class RtlCssBundleService {
constructor() {
this.rtlApplied = false
this.rtlStyles = [];
this.ltrStyles = Array.from(
document.querySelectorAll('link[rel="stylesheet"]')
)
}
insert = () => {
if (this.rtlApplied) { return }
this.rtlApplied = true
if (this.rtlStyles.length) {
return this.rtlStyles.forEach(style => {
document.body.appendChild(style)
})
}
this.rtlStyles = this.ltrStyles.map(styleSheet => {
const link = document.createElement("link")
link.href = styleSheet.href.replace(/\.css$/, '.rtl.css')
link.rel = "stylesheet"
document.body.appendChild(link)
return link
})
}
detach = () => {
this.rtlApplied = false
this.rtlStyles.forEach(style => {
document.body.removeChild(style)
})
}
toggle = () => {
return this.rtlApplied
? this.detach()
: this.insert()
}
}
const rtlStyles = new RtlCssBundleService()
export default rtlStyles
Then use this from any of your components.
So anyway, I'm sure I've missed something and maybe that is a terrible approach, but it seems to work and here is the demo
If you use flexbox and css grid they have RTL support built in. Then use CSS Logical Properties for margin, padding, border, etc. If that is not enough, then you can use [dir="rtl"] .your-class as a fallback.
Now you don't have two separate css files to maintain.
Here is a cross browser margin-right example.
-webkit-margin-end: 25px;
margin-inline-end: 25px;
#supports (not (-webkit-margin-end: 0)) and (not (margin-inline-end: 0)) {
margin-right: 25px;
}
You could wrap that up into a mixin for easier use across your app.
Looking around there is a library called react-with-direction from airbnb that provides a DirectionProvider - component you could wrap your components in based on the language. Hope that helps.

Resources