Bulma: a Flexbox CSS framework

Bulma is a free, open source CSS framework based on Flexbox. It’s the new kid on the block in the field competing directly with the old man and the sea: Twitter Bootstrap.

There are vary ways to install it from CDN to install it as npm package: Install Bulma. There is even an interesting bulma-start package.

After trying the CDN easy way, I decided to install it using npm. Why? I can work offline, I can customize which components to add or exclude, I need to work with SASS npm package anyway.

Create the package.json file: npm init. There is an interactive questionaire to fill in. When prompted for an entry point, enter sass/styles.scss.

I created a new folder: mkdir bulma-template-html and inside I run:

npm install node-sass --save-dev
npm install bulma --save-dev

Create a sass folder in which you add a file called styles.scss:

@charset "utf-8";
@import "../node_modules/bulma/bulma.sass";

Now I create the basic index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My custom Bulma website</title>
    <link rel="stylesheet" href="css/mystyles.css">
  </head>
  <body>
     <h1 class="title">
        Bulma
      </h1>

      <p class="subtitle">
        Modern CSS framework based on <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox">Flexbox</a>
      </p>

      <div class="field">
        <div class="control">
          <input class="input" type="text" placeholder="Input">
        </div>
      </div>

      <div class="field">
        <p class="control">
          <span class="select">
            <select>
              <option>Select dropdown</option>
            </select>
          </span>
        </p>
      </div>

      <div class="buttons">
        <a class="button is-primary">Primary</a>
        <a class="button is-link">Link</a>
      </div>
  </body>
</html>

If you can see some color on page then you are set with Bulma!

Prepare working with SASS

To build a CSS file from a Sass file, we can use node scripts. In package.json, add the following:

{
  "name": "bulmaloo",
  "version": "1.0.0",
  "description": "A bulma powered template.",
  "main": "sass/styles.scss",
  "scripts": {
    "css-build": "node-sass --omit-source-map-url sass/styles.scss css/styles.css",
    "css-watch": "npm run css-build -- --watch",
    "start": "npm run css-watch"
  },
  "keywords": [
    "bulma",
    "template"
  ],
  "author": "Ovi Farcas",
  "license": "MIT",
  "devDependencies": {
    "bulma": "^0.9.1",
    "node-sass": "^5.0.0"
  }
}

Do a test:

$ npm run css-build

Rendering Complete, saving .css file...
Wrote CSS to /path/to/mybulma/css/styles.css

It will create the final css file.

To watch for changes, just launch the following command:

npm start

This command will run the ‘start’ script defined in your package.json file and thus having a watcher executing the conversion sass>css in real time. If still prefer to run manually use the npm run css-build as before.

Note: There are other ways to install bulma with sass gem install sass. There are other ways: Customize Install.

Build markup with Bulma

Now we can proceed building up a template. Create a folder assets/images to place your images there.

Now scrap the scaffolding of a page:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Flowera</title>
  <link rel="stylesheet" href="css/styles.css">
  <script defer src="https://use.fontawesome.com/releases/v5.14.0/js/all.js"></script>

</head>
<body>
<!--YOUR BULMA CODE HERE-->
</body>
</html>

I scrap the navigation with a burger menu for the small handhelds.

<nav class="navbar" role="navigation" aria-label="main navigation">
    <div class="navbar-brand">
      <a class="navbar-item" href="/">
        <img src="assets/images/watermark.png">
      </a>

      <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
        <span aria-hidden="true"></span>
        <span aria-hidden="true"></span>
        <span aria-hidden="true"></span>
      </a>
    </div>

    <div id="navbarBasicExample" class="navbar-menu">
      <div class="navbar-end has-text-weight-semibold">
        <a class="navbar-item">
          Home
        </a>

        <a class="navbar-item">
          About
        </a>

        <div class="navbar-item has-dropdown is-hoverable">
          <a class="navbar-link">
            Products
          </a>

          <div class="navbar-dropdown">
            <a class="navbar-item">
              BusinessWallTattoo
            </a>
            <a class="navbar-item">
              WallTattoo
            </a>
            <a class="navbar-item">
              BusinessTattoo
            </a>
            <hr class="navbar-divider">
            <a class="navbar-item">
              Customized request
            </a>
          </div>
        </div>

        <a class="navbar-item">
          Blog
        </a>

      </div>

    </div>

  </nav>

For the dropdown this javascript will do the functionality and set the active element:

  <script>
  document.addEventListener('DOMContentLoaded', () => {
    // Get all "navbar-burger" elements
    const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
    // Check if there are any navbar burgers
    if ($navbarBurgers.length > 0) {
      // Add a click event on each of them
      $navbarBurgers.forEach( el => {
        el.addEventListener('click', () => {
          // Get the target from the "data-target" attribute
          const target = el.dataset.target;
          const $target = document.getElementById(target);
          // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
          el.classList.toggle('is-active');
          $target.classList.toggle('is-active');
        });
      });
    }
  });
</script>

Let’s build a hero with flexbox with 3 or more columns. The pattern is:

columns
  column
  column
  column
  ...

Here is the code:


<section class="hero">
 <div class="hero-body">
  <div class="container">
    <div class="columns">
      <div class="column">
        <figure class="image">
          <img src="assets/images/tuscan-valley.png" alt="Image">
        </figure>
      </div>
      <div class="column">
        <figure class="image">
          <img src="assets/images/tuscan-valley.png" alt="Image">
        </figure>
      </div>
      <div class="column">
        <figure class="image">
          <img src="assets/images/tuscan-valley.png" alt="Image">
        </figure>
      </div>
    </div>
  </div>
 </div>
</section>


The container has to purpose to coagulate the page on the center instead of overflowing over all page width.

  <section class="hero">
    <div class="hero-body">
      <div class="container">
        <div class="columns is-vcentered">
          <div class="column">
            <figure class="image">
              <img src="assets/images/myface.png">
            </figure>
          </div>
          <div class="column">
            <h1 class="title titled is-1 mb-6">
              Hello!.
            </h1>
            <h3 class="is-2 is-size-3 mb-6">
              This is a sample text.
            </h3>
            <div class="buttons">
              <button class="button is-warning is-large is-uppercase has-text-weight-semibold">How do we work</button>
            </div>
          </div>

        </div>
      </div>
    </div>
  </section>

Various labels are easy to remember and are extremely useful. They are either prefixed with is- or has- following by a very intuitive name. Worth mentioning is the fact tags h1…h6 are being reset, in term of default styling thus is-1 is necessary to style its markup to a big size text. mb-6 has to meaning of spacing middle bottom with size factor 6. Button labels set a customize shape. No css styling is needed.

The following piece of code is set in full width with text centered.

  <div class="container is-widescreen has-text-centered my-6 ">
  <h3 class="is-1"><strong>SO GLAD YOU’RE HERE!</strong> Pick an option below to get started:</h3>
  </div>
  <section class="hero">
    <div class="hero-body">
      <div class="container">
        <div class="columns is-1-mobile is-3-desktop">
          <!-- HERO SECONDARY 1ST COLUMN -->
          <div class="column">
            <div class="card">
              <div class="card-image">
                <figure class="image">
                  <img src="assets/images/map_bruxelles_mur.png" alt="Placeholder image">
                </figure>
              </div>
              <div class="card-content">
                <div class="content">
                  <h2 class="is-1">WallTattoo for Business</h2>

                  <p>Do you have a business place with walls? The art applied directly on the walls will make your place unforgettable.</p>
                  <a href="#" class="button is-warning is-medium is-uppercase has-text-weight-semibold">Find more</a>
                </div>
              </div>
            </div>
          </div>

          <!-- HERO SECONDARY 2ND COLUMN -->
          <div class="column">
            <div class="card">
              <div class="card-image">
                <figure class="image">
                  <img src="assets/images/map_bruxelles_mur.png" alt="Placeholder image">
                </figure>
              </div>
              <div class="card-content">
                <div class="content">
                  <h2 class="is-2">WallTattoo for Business</h2>

                  <p>Do you have a business place with walls? The art applied directly on the walls will make your place unforgettable.</p>
                  <a href="#" class="button is-warning is-medium is-uppercase has-text-weight-semibold">Find more</a>
                </div>
              </div>
            </div>
          </div>
          <!-- HERO SECONDARY 3RD COLUMN -->
          <div class="column">
            <div class="card">
              <div class="card-image">
                <figure class="image">
                  <img src="assets/images/map_bruxelles_mur.png" alt="Placeholder image">
                </figure>
              </div>
              <div class="card-content">
                <div class="content">
                  <h2 class="is-2">WallTattoo for Business</h2>

                  <p>Do you have a business place with walls? The art applied directly on the walls will make your place unforgettable.</p>
                  <a href="#" class="button is-warning is-medium is-uppercase has-text-weight-semibold">Find more</a>
                </div>
              </div>
            </div>
          </div>
        </div> <!-- columns-->
      </div>
    </div>
  </section>
  <section class="hero">
    <div class="hero-body">
      <div class="container">

<div class="columns is-1-mobile is-2-desktop is-vcentered">
  <div class="column">
    <div class="box">
      <article class="media">
        <div class="media-left">
          <figure class="image">
            <img src="assets/images/louise-cat.png" alt="Image">
          </figure>
        </div>
        <div class="media-content">
          <div class="content">
    <a href="#" class="button is-warning is-medium is-uppercase has-text-weight-semibold">shop</a>
          </div>
        </div>
      </article>
    </div>
  </div>
  <div class="column">
    <div class="box">
      <article class="media">
        <div class="media-left">
          <figure class="image">
            <img src="assets/images/covrig.png" alt="Image">
          </figure>
        </div>
        <div class="media-content">
          <div class="content">
    <a href="#" class="button is-warning is-medium is-uppercase has-text-weight-semibold">portfolio</a>
          </div>
        </div>
      </article>
    </div>
  </div>
</div>

</div></div></div>
</section>

The footer has set levels which are small horizontal pouches. I used the fontawesome to have ready shaped social network icons.

  <footer class="footer">
    <div class="content has-text-centered">
      <nav class="level">
        <!-- Left side -->

        <div class="level-left">
          <div class="level-item">
            Copyright 2020 &nbsp;  <img src="assets/images/watermark.png">
          </div>
          <div class="level-item">
            <a href="#">Terms of Use</a>
          </div>
          <div class="level-item">
              <a href="#">Privacy Policy</a>
          </div>
        </div>

        <!-- Right side -->
        <div class="level-right">
          <p class="level-item"><a href="#"><span class="icon">
          	<i class="fab fa-circle fa-2x fa-instagram"></i></span></a></p>
          <p class="level-item"><a href="#"><span class="icon"><i class="fab fa-circle fa-2x fa-linkedin"></i></span></a></p>
          <p class="level-item"><a href="#"><span class="icon"><i class="fab fa-circle fa-2x fa-pinterest"></i></span></a></p>
          <p class="level-item"><a href="#"><span class="icon"><i class="fab fa-circle fa-2x fa-youtube"></i></span></a></p>
          <p class="level-item"><a href="#"><span class="icon"><i class="fab fa-circle fa-2x fa-twitter"></i></span></a></p>

        </div>
      </nav>
    </div>
  </footer>

APPENDIX Alterntively, get started with bulma-start package

bulma-start is a tiny npm package that includes the npm dependencies you need to build your own website with Bulma. npm install bulma-start or yarn add bulma-start.

Now, that you prepared the groundwork for your project, set up Bulma and run the watchers:

npm install
npm start

Use npm run to show all available commands: Lifecycle scripts included in bulma-start:

available via `npm run-script`:
  css-build
    node-sass _sass/main.scss css/main.css
  css-deploy
    npm run css-build && npm run css-postcss
  css-postcss
    postcss --use autoprefixer --output css/main.css css/main.css
  css-watch
    npm run css-build -- --watch
  deploy
    npm run css-deploy && npm run js-build
  js-build
    babel _javascript --out-dir lib
  js-watch
    npm run js-build -- --watch