Friday, June 8, 2018

My Impressions of the Bulma CSS Framework

In my last blog post, I said I was going to refactor my vadacl demo Angular application using the Bulma CSS framework.  I recently finished that task.  Here are some screenshots of the new look:



Having now used Bulma in a project, here are some of my thoughts and some of the things I learned:

  • The ability to change core style settings using Sass variables is a convenient feature.  I was able to implement the color scheme for my app by simply changing three of the primary theme variables.  I also made two minor spacing and sizing adjustments.

    $primary: #694979;
    $info: #474e52;
    $danger: #d01e42;
    $gap: 16px;
    $help-size: 1.65ex;
    

  • I like the clarity of the class names Bulma uses. "has-text-black" and "has-background-primary" are pretty self-explanatory and easy to remember.  The grid layout is implemented with a "columns" container and "column" blocks, with size modifiers like "is-2" or "is-half".

  • Most CSS frameworks remove the natural top and bottom margins of the HTML <p> paragraph element, but Bulma also removes the style and margins of the header elements (<h1>, <h2>, etc.).  You can re-apply the expected styles by adding the "title" class and one of the "is-size-x" classes (where "x" is a number between 1 and 7).

  • Related to the previous bullet, Bulma provides a "content" class that, when applied to a container element, restores the normal HTML styles to elements like paragraphs and titles.  So if you want to display a block of several normal paragraphs, you can enclose them in a "content" container and not have to create and apply your own CSS class to those paragraphs.

  • I like the variety of the different container classes Bulma provides.  Initially I tried to lay out the main menu page using tiles but couldn't achieve the look that I wanted, and I ended up using "box" elements inside of "column" blocks.

  • Some of the layout implementations can end up being very deep in terms of nesting.  Unless I'm designing for mobile, I tend to favor horizontal form layouts with the label and form control on the same line.  Bulma allows for this (and in a way that lets the labels and inputs still stack vertically when the screen becomes narrow, which is great), but each "row" of the form ends up using a lot of elements.  In the forms in the demo, the element tree containing each input ends up being:

    div.field.is-horizontal -> div.field-body -> div.field -> div.control -> input 

    Not the prettiest HTML to look at.

  • I also ran into a problem unique to horizontal form layout where the label (to the left of the input) only reaches a certain width before is starts to wrap the label text to the next line (see https://github.com/jgthms/bulma/issues/1852).  There's no obvious workaround for it using the Bulma classes.  To get around it, I ended up creating some custom CSS styles that widen the label to certain widths and maintain the default spacing between the input "rows".

    .hz-label-150 div.field-label {
      min-width: 150px;
    }
    
    .hz-label-200 div.field-label {
      min-width: 200px;
    }
    
    .hz-label-250 div.field-label {
      min-width: 250px;
    }
    
    .hz-label-300 div.field-label {
      min-width: 300px;
    }
    
    .hz-label-150 div.field.is-horizontal,
    .hz-label-200 div.field.is-horizontal,
    .hz-label-250 div.field.is-horizontal,
    .hz-label-300 div.field.is-horizontal
    {
      margin-bottom: 0.75rem;
    }
    
    <form [formGroup]="profileForm" class="hz-label-200">...
    


Overall, I liked Bulma enough to consider using it for future projects.

Sunday, May 13, 2018

Implementing the Bulma CSS Framework Using Sass Compilation in an Angular CLI Project

I recently stumbled across the Bulma CSS framework. I'd never heard of it before, but it's been out now for over two years, and is generated considered as a pure CSS (no JavaScript) alternative to Bootstrap.

After looking at the documentation, I decided I wanted to try styling my Angular 5.x vadacl demo app with Bulma, and that I wanted to be able to take advantage of the fact that it could be customized via Sass (even though I've never worked with Sass before).

After some missteps and some research, I discovered that integrating Bulma with the Angular CLI is pretty easy.  Here's what I ended up doing:

  1. In my vadacl demo project, I ran the CLI command that updates the CLI configuration to use SCSS ("SCSS" is the latest syntax for Sass) files to generate the styles for the project:  "ng set defaults.styleExt scss".
    • When creating a new Angular CLI project, you can use the flag "--style=scss" on the "ng new" command to do the same thing.

  2. I then installed Bulma using npm: "npm install bulma --save-dev".

  3. Next, I created a "sass" folder in the assets folder of my project and created a "variables.scss" file.  In that file, I added the line:

    $primary: #694979;

    ...to change the color value of the $primary Sass variable used in Bulma.

  4. Then I created the "styles.scss" file in the src folder of my project, and in that file I added the following lines:

    @import "assets/sass/variables.scss";
    @import "../node_modules/bulma/bulma.sass";

    Initially it seemed counterintuitive that I would set my own values for the Bulma variables ahead of the import for Bulma, but Bulma is designed to accept those variable values if they've already been declared, and otherwise fall back to using the Bulma defaults.

  5. Then I opened the "angular-cli.json" file in my project and made sure the "styles" property array included "styles.scss".

That's all I needed to do.  I confirmed that everything worked by adding a few buttons to the home view of my app with Bulma styles:
<p>Bulma test</p>
<a class="button is-primary">Primary</a>
<a class="button is-link">Link</a>
<a class="button is-info">Info</a>
<a class="button is-success">Success</a>
<a class="button is-warning">Warning</a>
<a class="button is-danger">Danger</a>
...and I could see the effect of my change to the $primary color (which is normally a light sea green.



Resources:


Friday, March 30, 2018

Added a generateForm() Method to vadacl

Today I published a new version of my vadacl library that includes a new convenience method:  generateForm().  The generateForm() method accepts a data object and returns a FormGroup with FormControl instances generated from the properties and any vadacl-based validation configurations associated with the data object.

So instead of declaring the FormGroup and FormControl instances manually:

export class CruiseShipComponent extends Vadacl implements OnInit {
  cruiseShip: CruiseShip;
  shipForm: FormGroup;

  ngOnInit() {
    this.cruiseShip = new CruiseShip();
    this.shipForm = new FormGroup({
      'name': new FormControl( this.cruiseShip.name, this.applyRules( this.cruiseShip, 'name' ) ),
      'cruiseLine': new FormControl( this.cruiseShip.cruiseLine, this.applyRules( this.cruiseShip, 'cruiseLine' ) ),
      'yearBuilt': new FormControl( this.cruiseShip.yearBuilt, this.applyRules( this.cruiseShip, 'yearBuilt' ) )
    });
  }

...you can simply invoke generateForm() with the data object:

...
ngOnInit() {
  this.cruiseShip = new CruiseShip();
  this.shipForm = this.generateForm( this.cruiseShip );
}

generateForm() also accepts a second arguments:  a "mods" object that allows you to customize the FormGroup returned by the method.  The possible modification properties are:
  • exclude:  an array of data object properties you don't want to create a FormControl for.
  • only:  an array of the only data object properties you want to create FormControls for (overrides the "exclude" property if you mistakenly use both).
  • rename: an object literal for remapping a data object property name (key) to a different FormControl name (value).
  • validations: an object literal of additional validations to apply to particular data object properties.

An example of using the mods argument:
this.shipForm = this.generateForm( this.cruiseShip, {
  exclude: [ 'yearBuilt' ],
  rename: { cruiseLine: 'company' },
  validations: {
     name: { maxLength: { maxLength: 100, message: 'Ship name cannot be longer than 100 characters' } }
  }
});

The Angular application I maintain on GitHub to demonstrate vadacl has been updated with a demo that uses generateForm().