Advanced Integration Continued: Suggested CSS Setup

Now that you have your site compiling let's talk about how we might suggest setting up your CSS in this environment. Below we're going to show how to setup SASS but adapt to any precompiler.

This is only a suggested setup. There are a LOT of ways to setup your CSS. If you want to explore more take a look at the Vue CLI project or you can even just use good 'ol Laravel Mix to compile your styles and have two things running on your command line while working. Whatever makes you comfy is what matters. Things this structure did for us is:

  • Allowed us to load essential CSS and defer the bulk of the css as non-blocking which pleases the browser gods

  • Create multiple CSS files for different sites when using multi-tenancy in Devise and the sites differed in style.

  • Have a single compiler instead of running Vue CLI and Laravel Mix.

  • Handles hot reloading

  • Injects the essentials into the <head> so there is no blocking CSS

If that sounds interesting to you give the following a go or steal pieces of it to streamline your project.

Create a SASS folder in src

In src create a sass folder.

Create essentials.scss and global.scss

So, we're actually going to generate three css files. One is going to be loaded in the <head> of your HTML and the other two are going to be deferred and loaded at the bottom of the page. Why? Well, essentials is going to contain styles for everything (love this term 😑 ) "above the fold" while the meat of the CSS is going to be loaded at the bottom of the page to load everything up all snappy.

So, create src/sass/essentials.scss and src/sass/global.scss and maybe drop a test style into each.

Create styles folder and add two files inside it

Create a styles folder inside the root of your CLI project (ex: project-name/project-app/styles), add two javascript files and name them the same as your main SCSS files. In this example we're going to create essentials.js and global.js

Place the following inside each of the files and change the contents appropriately.

/project-app/styles/essentials.js
import '../sass/essentials.scss';

At this point you should have a structure like so:

/project-name
   ....
   /project-app

      /src
        /scss
          /essentials
            some-styles-we-want-in-the-head.scss
          /globals
            some-styles-we-are-ok-deferring.scss
          essentials.scss
          globals.scss
        /styles
          essentials.js
          globals.js
      buildpages.js (next step)
   ...

Create buildpages.js

To split out our styles we're going to need to do a little hacky bit here with Vue CLI. If anyone has any suggestions on a better way to do this we are all ears. In the root of the CLI project ( ex:project-name/project-app/) create buildpages.js and add the following to it:

/project-app/buildpages.js
module.exports = {
  app: {
    // Styles coming from style tags in your components 
    // if you wish to do that
    entry: 'src/main.js',
    template: 'public/index.html',
    filename: 'index.html',
    title: 'App',
  },
  global: {
    // Styles generated from globals.scss THROUGH globals.js
    entry: 'src/styles/globals.js',
    template: 'public/index.html',
    filename: 'index.html',
    title: 'App',
  },
  essentials: {
    // Styles generated from essentials.scss THROUGH essentials.js
    entry: 'src/styles/essentials.js',
    template: 'public/index.html',
    filename: 'index.html',
    title: 'Essential',
  },
};

Update your vue.config.js

Add the following into your vue.config.js at the top

const PagesObject = require('./buildpages.js');

Then add this somewhere in the main module exports object:

project-app/vue.config.js
module.exports = {


...

  indexPath: undefined,
  pages: PagesObject,
  chainWebpack: (config) => {
    config.plugins.delete('prefetch');
    // TODO: Remove this workaround once https://github.com/vuejs/vue-cli/issues/2463 is fixed
    // Remove preload plugins for multi-page build to prevent infinite recursion
    Object.keys(PagesObject).forEach((page) => {
      config.plugins.delete(`preload-${page}`);
      config.plugins.delete(`prefetch-${page}`);
    });
  },

  ...

}

Finally, update your main layout

Add the following to the head

resources/views/layouts/main.blade.php
@if(App::environment() == 'production' || App::environment() == 'staging' )
  <style><?php echo File::get( public_path( vuemix('/css/chunk-vendors.css', '/app') ) ) ?></style>
  <style><?php echo File::get( public_path( vuemix('/css/essentials.css', '/app') ) ) ?></style>
@else
  <link rel="stylesheet" href="{{vuemix('/css/chunk-vendors.css', '/app')}}">
  <link rel="stylesheet" href="{{vuemix('/css/essentials.css', '/app')}}">
@endif

Add the following to just before </body>

    <noscript id="deferred-styles">
      <link rel="stylesheet" href="{{vuemix('/css/global.css', '/app')}}">
    </noscript>

    <script>
      var loadDeferredStyles = function() {
        var addStylesNode = document.getElementById("deferred-styles");
        var replacement = document.createElement("div");
        replacement.innerHTML = addStylesNode.textContent;
        document.body.appendChild(replacement)
        addStylesNode.parentElement.removeChild(addStylesNode);
      };
      var raf = requestAnimationFrame || mozRequestAnimationFrame ||
          webkitRequestAnimationFrame || msRequestAnimationFrame;
      if (raf) raf(function() { window.setTimeout(loadDeferredStyles, 0); });
      else window.addEventListener('load', loadDeferredStyles);
    </script>

Congrats! You should be setup!

You should be setup and ready for destruction! Try building and see if the three files are created. If not do let us know in the issues and we can update this documentation.

Using Tailwind

Devise uses TailwindCSS under the hood for its own CSS. If you haven't used it before we couldn't recommend it enough. We have prefixed Devise styles with dvs to prevent conflicts. Since no Devise styles are loaded unless you are logged in you shouldn't really rely on those. To get your own tailwind up and running add the following to your SCSS files listed above:

@tailwind preflight;

@tailwind components;

// Examples
@import "essentials/globals";
@import "essentials/navigation";
// End examples

@tailwind utilities;

Remove PostCSS config from package.json

If your package.json file includes the following be sure to remove it.

  "postcss": {
    "plugins": {
      "autoprefixer": {}
    }
  },

Create a postcss.config.js in your Vue CLI app

Add autoprefixer to your dev dependencies and in your Vue CLI app add or edit postcss.config.js to contain:

const tailwind = require('tailwindcss');
const autoprefixer = require('autoprefixer');

module.exports = {
  plugins: [tailwind('./tailwind.js'), autoprefixer()],
};

Build and test

Try building your app from the Vue CLI UI. You should see your CSS in `` Now you an use Tailwind like so on your slices and components:

<div class="p-8 bg-pink font-bold font-sans text-xl">
    Look! Extra large bolded sans font with padding and a pink background
</div>

Purging the fat

When using something like tailwind you may want to cut any styles that are not being used. Add @fullhuman/postcss-purgecss , and glob-all to your dev-dependencies and edit postcss.config.js in the root of your Vue CLI project and add the following contents:

/project-app/postcss.config.js
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable global-require */
const purgecss = require('@fullhuman/postcss-purgecss');
const glob = require('glob-all');
const path = require('path');

module.exports = {
  plugins: [
    require('tailwindcss')('./tailwind.js'),
    require('autoprefixer')(),
    process.env.NODE_ENV === 'production' ? purgecss({
      content: [
        './src/**/*.vue',
        './../resources/**/*.blade.php',
      ],
      extractors: [
        {
          extractor: class {
            static extract(content) {
              return content.match(/[a-zA-Z0-9-:_/]+/g) || [];
            }
          },
          extensions: ['vue', 'html', 'php'],
        },
      ],

      // Example of when you have a package dependency that may have styles
      // that you don't want to cut out. Purge will scan these files and prevent
      // them from being purged.
      paths: glob.sync([
        path.join(__dirname, 'node_modules/tiny-slider/**/*.js'),
        path.join(__dirname, 'node_modules/tiny-slider/**/*.css'),
      ]),

      // Leave mobile, tablet, desktop, largeDesktop, and ultraWideDesktop as
      // Those are Devise breakpoints. In this example "/tns-*/" is just an
      // example for tinyslider and can be removed
      whitelistPatterns: [
        /mobile/,
        /tablet/,
        /desktop/,
        /largeDesktop/,
        /ultraWideDesktop/,
        /tns-*/,
      ],
    }) : '',
  ],
};

Last updated