Multiple Tailwind CSS Configs - laravel

I am building a project using Laravel/Inertia/Vue and I am using Tailwind CSS.
I want to have separate admin.css and client.css files using tailwindcss 3.2 ability to have multiple config files:
./styles/admin.css
#config "./tailwind.admin.config.js"
#tailwind base;
#tailwind components;
#tailwind utilities;
but the problem is that Vite will build just app.css for me not the admin one
vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
ssr: 'resources/js/ssr.js',
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
ssr: {
noExternal: ['#inertiajs/server'],
},
server: {
host: "localhost",
},
});
app.css is imported in app.js
I can not figure it out
Could you please help me?
I want to have separate admin.css and client.css files per each tailwindcss config file.

You can pass an array of input files to vite as follows:
input: ['resources/js/app.js','resources/css/admin.css','resources/css/client.css']
This should result in seperate output files in your build directory.
If you want to keep the css as javascript import you can create a second InertiaApp for the admin area:
Copy app.js and rename it like 'admin.js'
Change css import in admin.js to '/styles/admin.css'
Change your vite input to: input: ['resources/js/app.js','resources/js/admin.js']
Use a different blade layouts for the 'admin' area with reference to admin.js instead of app.js : #vite('resources/js/admin.js')

Thanks #dustin for your answer. Here are some more things:
I can split javascript application by defining multiple rootViews using inertia-laravel#0.3.2 in HandleInertiaRequests.php middleware:
public function rootView(Request $request)
{
if ($request->routeIs('admin.*')) {
return 'admin';
}
return 'app';
}
And have two different apps.
But do you think its a good approach to have two different apps?
I like the separation idea but is it the right way?
I Also have concerns about bundling and mixing in inertia and ssr, would it be a problem for that when you have two apps? I dont know anything about inertia's way of working
I was hoping there is some other method like creating a higher order component or something like that. I am very new to Vue world and I am still trying to learn.

Related

Laravel Vite configuration for separating assets (CSS and images)

I have one application where I want to separate my asset folders in Vite configuration.
This is how my current vite.config.js look like. I want something like
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
build: {
assetsDir: './images'
},
plugins: [
laravel({
input: [
'resources/css/app.css,'
'resources/scss/app.scss',
'resources/js/app.js',
],
refresh: true,
}),
],
});
I want to separate all images and CSS after the build. For instance
Build - Assets - images (all images will be going)
Build - Assets - Css (all CSS will be going)

Default Laravel + Vite configuration throws WebSocket connection to failed:

So Laravel decided to innovate once again and fix what was not broken, so Mix is gone and now default asset bundling goes with Vite.
I'm following the absolute default in their documentation to a bunch of front-end bugs and finally only several remained:
I use Laragon with SSL.
I haven't configured anything additional and my vite.config.js looks like this:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
});
When I run npm run dev and visit the Laragon domain I get the following in the console:
client.ts:78 WebSocket connection to 'wss://127.0.0.1:5173/' failed.
client.ts:48 [vite] failed to connect to websocket.
your current setup:
(browser) 127.0.0.1:5173/ <--[HTTP]--> 127.0.0.1:5173/ (server)
(browser) 127.0.0.1:5173/ <--[WebSocket (failing)]--> 127.0.0.1:5173/ (server)
Check out your Vite / network configuration and https://vitejs.dev/config/server-options.html#server-hmr .
I guess I need to configure my actual domain somewhere? I tried doing that in a server object in the config, but it didn't help those errors.
PS: Now in my vue files I need to import including the .vue extension e.g. import Button from '#/Components/Button.vue' is there any way I can ommit the .vue like it was with Laravel Mix?
I haven't use laragon before, but if you have a custom domain, eg, like
http://cutom-domain.test, you need to tell vite to use the certificate like so;
In your vite.config.js, add a server key with the following configuration
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
import fs from 'fs';
import { homedir } from 'os';
import { resolve } from 'path';
// Ignore the protocol on the host, ie do not put "http"
const host = 'cutom-domain.test';
const viteServerConfig = host => {
let keyPath = resolve(homedir(), `.config/valet/Certificates/${host}.key`)
let certificatePath = resolve(homedir(), `.config/valet/Certificates/${host}.crt`)
if (!fs.existsSync(keyPath)) {
return {}
}
if (!fs.existsSync(certificatePath)) {
return {}
}
return {
hmr: {host},
host,
https: {
key: fs.readFileSync(keyPath),
cert: fs.readFileSync(certificatePath),
},
}
}
export default defineConfig({
server: viteServerConfig(host),
plugins: [
laravel({
input: 'resources/js/app.js',
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
});
Credit to this blogpost that explains more - Making Vite and Valet play nice together
I don't know if it's still relevant, but looking in the source code of laravel-vite-plugin I found a way to solve this problem in a very simple way, without even changing the vite.config.js file.
Put these two variables in the .env file and set them with full path to .key and .crt files on your system:
VITE_DEV_SERVER_KEY='C:/laragon/etc/ssl/laragon.key'
VITE_DEV_SERVER_CERT='C:/laragon/etc/ssl/laragon.crt'
Do not change anything on vite.config.js file. Here is my (fresh install of laravel + jetstream w/ inertia and --ssr):
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
ssr: 'resources/js/ssr.js',
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
});
And that's it. Run npm run dev and Vite will "magically" start a development server with https on.
VITE v4.0.4 ready in 1248 ms
➜ Local: https://laravel.test:5173/
➜ Network: https://192.168.1.2:5173/
➜ press h to show help
LARAVEL v9.48.0 plugin v0.7.3
➜ APP_URL: https://laravel.test/
Even though the configuration present in the official documentation also works, this way is much simpler, and the host, key and cert variables are not defined in the file, but they are dynamic reflecting the dev environment.
Hope this helps someone.
Here is the source where I found this, and you can also inspect in node_modules\laravel-vite-plugin\dist\index.js of your project.
When I do npm run build instead of regular npm run dev, the problem is gone. I guess, build mechanism is different for prod, so there is no WSS related errors in console.
So, in other words, perform a production Vite build and deploy it (if you are testing on a remote project).

Laravel vite config using separate tailwind.config files for admin and site

With Laravel Mix I'm generating two different css/js files for Admin and for the main site like this:
const mix = require('laravel-mix');
const tailwindcss = require('tailwindcss');
mix.js('resources/js/site/app.js', 'public/js')
.postCss('resources/css/site/app.css', 'public/css', [
require('autoprefixer'),
require('postcss-import'),
tailwindcss('./tailwind.site.config.js'),
])
.options({
processCssUrls: false,
}).version();
mix.js('resources/js/admin/app.js', 'public/_admin/js')
.postCss('resources/css/admin/app.css', 'public/_admin/css', [
require('autoprefixer'),
require('postcss-import'),
tailwindcss('./tailwind.admin.config.js'),
])
.options({
processCssUrls: false,
}).version();
How can I tell vite to do the same thing ??
Thanks!!
The default vite.config.js is
import { defineConfig } from 'vite';
import laravel, { refreshPaths } from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/app.css',
'resources/js/app.js',
],
refresh: [
...refreshPaths,
'app/Http/Livewire/**',
],
}),
],
});
Create two vite.config.js files, and also create two tailwind.config.js files for frontend & backend.
I have already posted this solution on github with demo laravel project.
https://github.com/pkfan/setup-laravel-vite-for-multiple-tailwind.config.js
I have upload a video about it.
watch this video on youtube
See https://laravel-vite.dev/guide/extra-topics/multiple-configurations.html.
You will need to add a configuration to config/vite.php, create a new vite.back-office.config.ts file, pass the configuration name to the #vite directive and run slightly different development and build commands.
The docs are TypeScript-focused, but the same technique will work for your JS/CSS assets.
Starting with Tailwind CSS v3.2, hacky workarounds are no longer needed.
You can now define the config file to use inside of your CSS files:
https://tailwindcss.com/blog/tailwindcss-v3-2#multiple-config-files-in-one-project-using-config

Vite does not build my #extend rules from bulma-scss

I'm having a build issue with the scss at-rule "extend" using Vite to build out a Vue3 component library using the bulma-scss NPM package.
Using Bluma buttons for example, I would like to import the bulma-scss button.scss file into my tag in my .vue file (or into the button.scss file and then import that into the script tag) like so:
<template/>
<script/>
...
<style lang="scss">
#import 'bulma-scss/elements/button';
</style>
When running $ vite build I get this error from Vite:
File: /Users/my-user/sites/component-library/node_modules/bulma-scss/elements/_button.scss
Error: The target selector was not found.
Use "#extend %control !optional" to avoid this error.
It is specifically this line in the bulma-scss package that it doesn't like (in this example) https://github.com/j1mc/bulma-scss/blob/master/elements/_button.scss#L71
it looks like I can get around this by adding the following preprocessor option to my storybook's vite configs:
css: {
preprocessorOptions: {
scss: {
additionalData: `#import "bulma-scss";`
}
},
},
But that would include the entirety of bulma-scss right? Ideally I would only import the things I need from bulma.
And here is my entire vite config file (sans the css preprocessorOptions)
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
import {resolve} from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: resolve(__dirname, 'src/index.js'),
name: 'lmsComponentLibrary',
},
rollupOptions: {
// make sure to externalize deps that shouldn't be bundled
// into your library
external: ['vue'],
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
vue: 'Vue',
},
},
},
},
resolve: {
alias: {
'bulma-scss/': require('path').join(__dirname, 'node_modules/bulma-scss/'),
}
},
});
I think the problem comes from the bulma-scss package itself. In the file that you are importing there is no %control placeholder. Even its import ../utilities/controls and ../utilities/mixins don't have that placeholder neither. So the error is expected here.
The %control is defined in bulma-scss/utilities/extends so you can fix the problem by importing that file. BUT, it will lead to another problem because in the _extends.scss there are some variables that are not defined. So you need to import all the file that contains these variables.
Luckily, the package has a file containing all the utility variables. So you just need to import it.
#import 'bulma-scss/utilities/all'; <-- Add this line
#import 'bulma-scss/elements/button';

Disable asset handling in Vite

I have a PHP project (WordPress theme) with Vite and PostCSS to bundle my JS and CSS files.
The output directory is build and everything worked, but as soon as I import fonts or images in my CSS, Vite copies them into the build folder and changes the paths in the source.
File structure:
styles
|- tailwind.css
|- fonts
|- fa-brands-400.eot
|- fa-brands-400.woff
|- fa-brands-400.woff2
|- fa-brands-400.svg
|- fa-brands-400.ttf
js
|- index.js
vite.config.js
...
In my tailwind.css, I'm importing the font:
#font-face{
font-family:"Font Awesome 5 Brands";
font-style:normal;
font-weight:400;
font-display:block;
src:url(../styles/fonts/font_awesome/fa-brands-400.eot);
src:url(../styles/fonts/font_awesome/fa-brands-400.eot?#iefix) format("embedded-opentype"),url( ../styles/fonts/font_awesome/fa-brands-400.woff2) format("woff2"),url(../styles/fonts/font_awesome/fa-brands-400.woff) format("woff"),url(../styles/fonts/font_awesome/fa-brands-400.ttf) format("truetype"),url(../styles/fonts/font_awesome/fa-brands-400 .svg#fontawesome) format("svg")
}
The problem, Vite copied the imported font files to my build folder and my font import now looks like this (in build/tailwind.css:
#font-face{
font-family:"Font Awesome 5 Brands";
font-style:normal;
font-weight:400;
font-display:block;
src:url(/fa-brands-400.eot);
src:url(/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(/fa-brands-400.woff2) format("woff2"),url(/fa-brands-400.woff) format("woff"),url(/fa-brands-400.ttf) format("truetype"),url(/fa-brands-400.svg#fontawesome) format("svg")
}
Is there a way to disable this? I just want Vite to bundle my JS and CSS, but don't include my assets.
My vite.config.js looks like this:
import postcssImport from "postcss-import"
import tailwindcssNesting from "tailwindcss/nesting"
import tailwindcss from "tailwindcss"
import autoprefixer from "autoprefixer"
import postcssScss from "postcss-scss"
import { defineConfig } from "vite"
export default defineConfig({
build: {
outDir: "build",
cssCodeSplit: true,
emptyOutDir: true,
minify: false,
assetsDir: "",
rollupOptions: {
input: {
index: "js/index.js",
tailwind: "styles/tailwind.css",
},
output: {
entryFileNames: "[name].js",
assetFileNames: "[name].[ext]",
},
},
},
css: {
postcss: {
syntax: postcssScss,
plugins: [postcssImport, tailwindcssNesting, tailwindcss, autoprefixer],
},
},
clearScreen: true,
publicDir: false,
})
I am facing a similar issue with a library build. I want to have an image relative to my css file, but default it is placed at the root and the reference in the css file is also to the root (just like in your problem). I did not find a perfect solution, but I was able to place the image in the same folder as the css file, where the css file also references the image in the same folder. I used the rollup config option output.assetFileNames for this. You can pass your own function and in that function you can add the complete path to the folder where you want to add the asset.
assetFileNames: (assetInfo: PreRenderedAsset): string => {
if (assetInfo.type === 'asset') {
return 'styles/fonts/[name][extname]';
}
else {
return '[name][extname]';
}
},
This will place the fonts in the build/styles/font folder. The references in the css will also be in this folder.
There is one caveat: The references will begin with '/', so they will be from the root of the domain. I have not found a solution for this.

Resources