Cutting Bulma's CSS Bundle by 90% with Modular Sass Imports
Bulma's modular Sass lets you import only needed components (grid, button, card) cutting minified CSS from ~180 KB to ~15 KB. See a Vite example, breakpoint customization, and trade-offs.
24 Dec 2025, 01:11 UTC

The problem: shipping 180 KB of CSS you don't use
Most teams add Bulma via the prebuilt CDN link or @import \"bulma\" in their Sass entry point. That pulls in the entire framework — grid, navbar, card, form, table, helpers, and ~30 other components — even if the page only uses a button and a card. The minified output weighs ~180 KB before compression. For a server-rendered app or static site where every kilobyte affects LCP, that's waste you can measure.
Bulma's Sass source is organized as ~35 discrete .scss files under bulma/sass/. Each component (grid, button, card, navbar, etc.) lives in its own file, and the main bulma.sass entry point simply @imports them all. Because there is zero JavaScript in the framework, the only payload is the CSS you actually compile. Selective imports let you pay for exactly what you use.
How modular imports work
Instead of importing the monolith, you import individual component files from node_modules/bulma/sass/. A minimal build that only needs the grid system, buttons, and cards looks like this:
// styles/main.scss\n@import \"bulma/sass/utilities/initial-variables\"; // must come first\n@import \"bulma/sass/utilities/functions\";\n@import \"bulma/sass/utilities/derived-variables.sass\";\n@import \"bulma/sass/grid\";\n@import \"bulma/sass/button\";\n@import \"bulma/sass/card\";\nThe three utility imports are required — they set up Bulma's Sass variables, functions, and derived values that every component depends on. After that, each component import adds only its own rules. In a Vite + Sass project, this compiles to roughly 15 KB minified (about 3 KB gzipped) versus 180 KB for the full framework.
Worked example: Vite project with selective Bulma
Run these commands in a fresh project directory. You need Node 18+ and npm 9+.
# 1. Scaffold a minimal Vite + vanilla project\nnpm create vite@latest bulma-demo -- --template vanilla\ncd bulma-demo\n\n# 2. Install Bulma and Sass\nnpm i bulma sass\n\n# 3. Replace src/style.css with a Sass entry point\ncat > src/main.scss << 'EOF'\n// Required utilities (order matters)\n@import \"bulma/sass/utilities/initial-variables\";\n@import \"bulma/sass/utilities/functions\";\n@import \"bulma/sass/utilities/derived-variables.sass\";\n\n// Only the components you actually use\n@import \"bulma/sass/grid\";\n@import \"bulma/sass/button\";\n@import \"bulma/sass/card\";\nEOF\n\n# 4. Update main.js to import the Sass file\nsed -i \"s|import './style.css'|import './main.scss'|\" src/main.js\n\n# 5. Build and inspect output\nnpm run build\nls -lh dist/assets/*.css\nWhere to run: Terminal at project root. Permissions: Standard user write access to project directory. Expected check: The compiled CSS file in dist/assets/ should be ~15 KB (minified) or ~3 KB gzipped. Risk: If you forget the three utility imports, Sass will error on undefined variables.
Customizing breakpoints before import
Responsive modifiers like is-hidden-mobile and is-flex-tablet are generated from a single $breakpoints Sass map. To change them, create a _custom-variables.sass and import it before the utilities:
// _custom-variables.sass\n$breakpoints: (\n \"mobile\": 0,\n \"tablet\": 768px,\n \"desktop\": 1024px,\n \"widescreen\": 1216px,\n \"fullhd\": 1408px\n)\n\n// main.scss\n@import \"custom-variables\"; // must precede utilities\n@import \"bulma/sass/utilities/initial-variables\";\n@import \"bulma/sass/utilities/functions\";\n@import \"bulma/sass/utilities/derived-variables.sass\";\n// ... component imports\nOverriding $breakpoints after the utility imports has no effect because the responsive classes are generated at import time.
Trade-offs and limitations
- Build pipeline required. Plain HTML + CDN cannot use selective imports; you must compile Sass (Vite, Webpack, esbuild, or Dart Sass CLI).
- Implicit dependencies.
navbarusesburger;dropdownusesnavbar-item. Importingnavbaralone may miss required sub-components. Checknode_modules/bulma/sass/for each component's own@importstatements. - Utility classes persist. Classes like
has-text-centeredoris-pulled-rightremain in the output unless you run PurgeCSS (or Vite's built-in CSS code-split) against your templates. - CSS custom properties need fallbacks. Since v0.9.0 Bulma defines colors and spacing as CSS variables for runtime theming. For IE11 support you must provide Sass fallbacks; otherwise the compiled CSS will contain
var(--bulma-primary)with no fallback value. - Version 1.0 (2024) changed import paths. If upgrading from 0.9.x, consult the migration guide — some component file names and locations moved.
Verify the savings in your repo
- Add a temporary script to
package.json:\"size\": \"npm run build && gzip-size dist/assets/*.css\"(installgzip-size-clifirst). - Run
npm run sizewith the full@import \"bulma\"— note the gzipped size. - Switch to selective imports as shown above, rebuild, and compare.
If the gzipped size drops from ~20 KB to ~3 KB, the modular build is working. Pair it with PurgeCSS for production to strip any remaining unused utility classes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.