Replacing Bootstrap with Bulma

Why remove Bootstrap you ask, well while Bootstrap is a great CSS framework it is not the only CSS framework. For this exercise, I felt I would like to try out the CSS framework Bulma. Another reason you may want to remove Bootstrap is due to its reliance on JQuery. I plan to use VueJs as the main Javascript framework, thus Jquery becomes obsolete. I also believe it is a good exercise to practice changing up existing projects. In the industry, we are often editing existing codebases and don't have the luxury of starting from scratch.

Removing Bootstrap, in this case, is as easy as removing the link tag to the Bootstrap CDN in the header. And the link to the JQuery script in the Footer. Unfortunately, there is also a bit of tediousness involved. We will need to go through all the existing templates and replace the Bootstrap classes.

There will be many classes to change throughout the codebase. Headings, columns, fonts etc. In this article, I will go through editing the navigation and forms. For the rest, I recommend going through each class one by one and googling the Bulma alternative.

Creating a Bulma Navigation

Open base.html and delete the Bootstrap navigation bar. We will replace it with the below Bulma alternative:

<div class="has-background-light">
    <div class="container">
        <nav class="navbar has-background-light" role="navigation" aria-label="main navigation">
            <div class="navbar-brand">
                <a class="navbar-item" href="{% url 'home' %}">
                    <img src="path/to/your/logo.png" width="112" height="28" />
                </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-start">
                        <a class="navbar-item" href="{% url 'home' %}"> Home </a>
                        <a class="navbar-item" href="{% url 'about' %}"> About </a>
                    </div>
                    <div class="navbar-end">
                        <div class="navbar-item">
                            <div class="">
                            {% if request.user.is_authenticated %}
                                <a class="button is-primary" href="{% url 'users:detail' request.user.username %}"> <strong>My profile</strong> </a>
                                <a class="button is-light" href="{% url 'account_logout' %}"> Log out</a>
                            {% else %}
                                <a class="button is-primary" href="{% url 'account_signup' %}"> <strong>Sign up</strong> </a>
                                <a class="button is-light" href="{% url 'account_login' %}"> Log in</a>
                            {% endif %}
                            </div>
                        </div>
                    </div>
                </div>
            </nav>
      </div>
</div>

Easy right? Well if you resize the screen you will notice the lovely burger menu has no functionality. This is because Bulma does not come with a javascript solution out of the box. We have to implement our own. Open static/js/project.js and add the following code:

var navButton = document.querySelector("nav .navbar-brand a.navbar-burger.burger[role='button']");

navButton.addEventListener('click', function(e){
    var navMenu = document.getElementById(e.target.dataset.target);
    navMenu.classList.toggle('is-active');
});

First, we use a query selector to grab the burger menu button, and then add a click event listener to it. When the button gets clicked, we find the element by its ID and toggle the ‘is-active’ class.

Now the burger menu works as expected.

Adding Bulma classes to Forms

Cookicutterdjango comes installed with crispy forms by default. A Django app that integrates Bootstrap classes into Django forms. Seeing as we are aiming to do away with the Bootstrap JQuery combo lets remove crispy forms. Open config/settings/base.py and delete ‘crispy_forms’ from THIRD_PARTY_APPS.

Open requirements/base.txt and remove the following line

django-crispy-forms==1.7.2

Rebuild the Docker container for changes to take effect:

Docker-compos -f local.yml -f build

Now that crispy forms have been removed, go through each template and remove these lines. {% load crispy_forms_tags %} and {{ form|crispy }} from each one.

Now we are left with the standard browser form HTML which is a little ugly. To add Bulma styling to our forms we will need to create a template tag. Create a folder and name it templatetags, inside this folder be sure to create an __init__.py! Now in the same directory, create a file form_tags.py and enter the following:

from django import template

register = template.Library()

@register.filter(name='add_class')
def add_class(field, classname):
    existing_classes = field.field.widget.attrs.get('class', None)
    if existing_classes:
        if existingclasses.find(given_class) == -1:
            classes = existing_classes + ' ' + classname
        else:
            classes = existing_classes
    else:
        classes = classname
    return field.as_widget(attrs={'class': classes})

For our template tag to be usable in our templates we need to add it to our settings. In config/settings/base.py add our new template tag as per below:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            str(APPS_DIR.path('templates')),
            ],

        'OPTIONS': {
        'debug': DEBUG,
        'loaders': [
            'django.template.loaders.filesystem.Loader',
            'django.template.loaders.app_directories.Loader',
            ],
        'context_processors': [
            'django.template.context_processors.debug',
            ## Removed for brevity
            'django.contrib.messages.context_processors.messages',
            ],

        'libraries' : {
            'templatetags' : 'myappname.templatetags.form_tags'
            }
        },

Now we can put our template tag to use. At the top of the template file add the following:

{{ load templatetags }}

Now we can use the function in our forms to add a Bulma class to any field:

{% for field in form %}
    {{ field|add_class:"input" }}
{% endfor %}

We will need to work through the remaining forms and templates doing the same. Replacing any Bootstrap styling with the Bulma alternative. We now have our own personalised styling preferences configured for our Django application. In the next part, I will be adding Vuejs to the project.