jgthms / bulma

Modern CSS framework based on Flexbox
https://bulma.io
MIT License
48.88k stars 3.93k forks source link

Reduce 1.0.0 size (Remove, reduce CSS variables option) #3761

Open kasperkamperman opened 3 months ago

kasperkamperman commented 3 months ago

This is a question.

Overview of the problem

This is about the Bulma CSS framework I'm using Bulma version [1.0.0] I am sure this issue is not a duplicate

Description

I love the new Bulma and the accessible color styles. What I love less is the new file size. The default minified went from 207kb to 554kb.

This is mainly do addition from the css variables. This is nice of for color switching, but I feel it's not worth the trade-off for my current sites.

Is there away to compile without variables in Sass. I tried postprocessing with postcss, but that plugin hangs on the Bulma script. I posted an issue on that here: https://github.com/MadLittleMods/postcss-css-variables/issues/139

I'm curious to know if there are other ways.

I use other postcss plugins to prune unused variables and compress variables names (not nice for readibility, but regular site-users won't care. I also changed the prefix to one character (instead of bulma- in front of each variable).

Any suggestions or ideas are welcome.

jgthms commented 3 months ago

It's probably doable by not including any of the themes and creating your own.

It's a bit tricky because you'd need to know which CSS variables to include yourself.

Now that I think about it, creating a "lite" theme could be something of a solution.

kasperkamperman commented 3 months ago

That would be interesting. I feel there is too much overhead. I would only like to have css variables for the parts that actually are variable and change (I assume mostly color).

For example the new "gradient" colorpalettes take a lot of variable space (I commented out the digits in the sass file to exclude them), maybe they could be in a separate sass file, which can be excluded.

Anyway, super nice job.

kasperkamperman commented 3 months ago

I'm still figuring out how to remove css variables. I found a package to convert them to sass-variables (and then remove them).

However the order of variables gives a issue. For css you can refer to a variable that is declared later, but for sass not.

There are several cases where reference is made to "not declared" variables.

I don't know if that needs to be fixed (and it might be really difficult), just figuring out a way to make some major savings in file size.

kasperkamperman commented 3 months ago

Changed the title to better explain the context of my question.

E021ntox commented 3 months ago

Yes, 1.0 is terrible, I have rolled back to 0.9.4.

kasperkamperman commented 3 months ago

@E021ntox Well I could make major savings with postcss.

My postcss.config.js:

const purgecss = require('@fullhuman/postcss-purgecss')
const cssnano = require('cssnano')
const pruneVar = require('postcss-prune-var')
const varCompress = require('postcss-variable-compress')

module.exports = {
    plugins: [
        purgecss({
            // file paths to your contents to remove unused styles. 
            // content: ['js/my.js','*.php'],
            // other wise our aria-selected is removed (like with purgecss online)
            dynamicAttributes: ["aria-selected"]
        }),
        pruneVar(), // remove unused css variables
        varCompress(), // compress css variables
        cssnano({
            preset: 'default',
        }),
    ],
};

My final Bulma 1.0.0 CSS was 52kb, the 0.9.4 version was 46kb. Good compressed CSS variables might be even more efficient size wise.

I think color contrast is a major improvement in 1.0.0, I don't know if the theme is backwards compatible.

max-arnold commented 3 months ago

@kasperkamperman Could you please elaborate a bit on what is needed to make a custom Bulma build with your config? So far I've followed these steps and got the basic my-bulma-project.css build. What I need to add to the package.json to reproduce your setup?

Unfortunately I'm not familiar with the Node.js tooling ecosystem

kasperkamperman commented 3 months ago

Yes. I'm also not super familiar either, but I could make it happen.

You have to install more package install --save-dev (although I think the --save-dev doesn't really matter (I'm not really into npm).

My package.json looks like this:

{
  "devDependencies": {
    "@fullhuman/postcss-purgecss": "^6.0.0",
    "cssnano": "^6.1.2",
    "dependencies": "^0.0.1",
    "postcss-cli": "^11.0.0",
    "postcss-prune-var": "^1.1.2",
    "postcss-variable-compress": "^3.0.0",
  },
  "dependencies": {
    "sass": "^1.72.0"
  },
  "scripts": {
     "build-my-bulma": "sass --style=expanded --source-map my-bulma.scss css/bulma.css",
    "build-bulma": "sass --style=expanded --source-map bulma/bulma.scss css/bulma.css",
    "purge-bulma": "postcss css/bulma.css --no-map --output css/bulma.min.css",
  }
}

You see I add purge-bulma, to run the postcss part. Although you could run it from the command-line to I think with the postcss cli. For example I now use cat css/bulma.css css/page.css | npx postcss > css/style.css to first merge bulma with some other css file with my modifications, this is piped | into postcss. NPX is npm but then without installing (so I think I could use the command npm too.

Everytime you run postcss it runs what's in the postcss.config.js (which I shared above). You can comment out parts you don't use.

I use below too customize bulma (no dark-mode, custom colors). I removed the bulma-- prefix from the css variables, although if you use postcss-variables-compress that is not needed (since they get compressed names anyway).

For completeness below, my custom version to build bulma (my-bulma.scss). I removed dark-mode and new things as grid/skeleton. However if you use purge-css and you don't use it, all those classes will be removed anyway.

@charset "utf-8";

$cyan: hsl(198, 100%, 70%);
$blue: hsl(229, 86%, 36%);
$red: hsl(348, 86%, 49%); // used for danger

/* no dark mode, only light-theme for now */
@use "bulma/sass" with (
  $cssvars-prefix: "",
  $primary: $cyan,
  $link: $blue,
  $danger: $red
);

@forward "bulma/sass/utilities";
//@forward "themes";
@forward "bulma/sass/base";
@forward "bulma/sass/elements";
@forward "bulma/sass/form";
@forward "bulma/sass/components";
//@forward "bulma/sass/grid";
@forward "bulma/sass/layout";
//@forward "bulma/sass/base/skeleton";
@forward "bulma/sass/helpers";

@use "bulma/sass/themes/light";
@use "bulma/sass/themes/setup";

:root {
  @include light.light-theme;
  //@include setup.setup-theme;
}

Hope this helps.

nioc commented 2 months ago

Thanks @kasperkamperman. As I don't master the subtleties of SCSS, I have 2 questions about your example:

  1. I was wondering why you import the bulma/sass file when you're also importing the bulma/sass/utilities and bulma/sass/base files?
  2. Why @use some files and @forward others? Is there a difference in your use?
kasperkamperman commented 2 months ago

@nioc To be honest, I just worked from the Bulma examples and tried to make it work. I hardly get SCSS either (and I'm too "lazy" to dive into it) :-)

nioc commented 2 months ago

My understanding is:

As you @use "bulma/sass"(all lib), you should remove following @forward statements.

In bulma docs, it is said that we can load all lib (bulma/sass) or load only what we need.

But it does not matter, as you purge css 😄

nioc commented 2 months ago

Thanks for your help again @kasperkamperman and obviously @jgthms for this incredible work. In case anyone needs to reduce CSS in a Svelte app (Vite not Sveltekit), here is my postcss.config.cjs (just it and npm i -D @fullhuman/postcss-purgecss, no need for anything else, Vite does the magic):

/* eslint-disable @typescript-eslint/no-var-requires */
const purgecss = require('@fullhuman/postcss-purgecss')

module.exports = {
  plugins: [
    purgecss({
      content: [
        './index.html',
        './src/**/*.{svelte,js,ts}',
      ], // declaring source files
      safelist: {
        standard: [/$svelte-/], // required for inline component styles
      },
      variables: true, // remove unused CSS variables
    }),
  ],
}

I do not use postcss-prune-var because purgecss has an option that gives better results in my case:

Bulma version Bulma Themes Post CSS CSS assets size CSS variables count
0.9.4 No Nothing 214.41 kB
1.0.0 Dark & light 565.77 kB 987
1.0.0 Dark & light PurgeCSS + PruneVar 139.64 kB 561
1.0.0 Dark & light PurgeCSS with variables: true 107.30 kB 220

So 107 kB (60 kB gzipped), for such a rich CSS with 2 themes, that's impressive 🦸🏻

jgthms commented 2 months ago

Great find. Thanks @nioc. I'll see if I can include purgecss in the build process, without it impacting the final result and the customization options.

rednaks commented 1 month ago

Yes, 1.0 is terrible, I have rolled back to 0.9.4.

same, rolledback to 0.9.4 due to bundle size ...

muellerj commented 3 weeks ago

@nioc: How did you instruct purgecss to leave the themes alive? I have not managed to configure it in such a way, the theme-dark selectors seem to get purged regardless.

  system "purgecss",
    "--css", "_build/css/base.css",
    "--content", "_tmp/compiled.html.erb",
    "--safelist", "theme-dark",
    "--safelist", "fa-*",
    "--output", "_build/css"
nioc commented 3 weeks ago

Hello @muellerj I'm not behind my pc but I think themes are also purged, but it's what I wanted (keep only used variables and classes, and in my case I use both light and dark in a svelte app).

muellerj commented 3 weeks ago

Understood - thanks. If I find a configuration that keeps the themes, I'll post it here. My feeling is that the CLI interface for purgecss doesn't off the full power of the patterns (https://purgecss.com/safelisting.html) as the Javascript API.

muellerj commented 3 weeks ago

If anyone stumbles across this - I ended up using the inline CSS comments to guard the themes from being purged. In ruby, it looks something like this:

def add_purgecss_protection(file)
  return if has_purgecss_protection?(file)

  puts "Adding protection to #{file}"
  File.write(file, [
    "/*! purgecss start ignore */",
    *File.readlines(file).map(&:chomp),
    "/*! purgecss end ignore */",
  ].join("\n"))
end

def has_purgecss_protection?(file)
  lines = File.readlines(file)
  return false unless lines.first.chomp == "/*! purgecss start ignore */"
  return false unless lines.last.chomp  == "/*! purgecss end ignore */"

  true
end

# ...

  add_purgecss_protection("_src/bulma/sass/themes/_index.scss")  # <= This guards themes/_index.scss from being purged
  system "sass", "sass/base.scss", "_build/css/base.css"
  system "purgecss",
    "--css", "_build/css/tc-base.css",
    "--content", "_tmp/compiled.html.erb",
    "--output", "_build/css"
  system "csso", "_build/css/base.css",
    "--output", "_build/css/base.css"