Django application to add the Bulma CSS framework and its extensions

  • By Leon SandΓΈy
  • Last update: Dec 6, 2022
  • Comments: 16

django-simple-bulma

django-simple-bulma is a Django application that makes Bulma and Bulma-Extensions available to use in your Django project with as little setup as possible. The goal of this project is to make it as easy as possible to use Bulma with Django.

This project currently uses Bulma v0.9.2, and is automatically updated with every new release. If a new version has come out with features you'd like to make use of, please create an issue, and we will be happy to make a release to update it.

Installation

To get django-simple-bulma, up and running for your Django project, follow these simple steps:

  • Install it from PyPI with pip install django-simple-bulma (or add it to your Pipfile)

  • In your Django projects settings.py file:

    • Add django_simple_bulma to your INSTALLED_APPS
      INSTALLED_APPS = [
        # ...
        'django_simple_bulma',
        # ...
      ]
    • Add django_simple_bulma.finders.SimpleBulmaFinder to your STATICFILES_FINDERS. This normally holds two default handlers that you will probably want to keep, so unless you have any other custom Finders, it should look like this:
      STATICFILES_FINDERS = [
        # First add the two default Finders, since this will overwrite the default.
        'django.contrib.staticfiles.finders.FileSystemFinder',
        'django.contrib.staticfiles.finders.AppDirectoriesFinder',
      
        # Now add our custom SimpleBulma one.
        'django_simple_bulma.finders.SimpleBulmaFinder',
      ]
  • Run python manage.py collectstatic command in order to build Bulma and move it to your staticfiles folder. Please note that you will need to use this command every time you make a change to the configuration, as this is the only way to rebuild the Bulma css file. If you are not using collectstatic , read up on it and start using it.

    This app works fine with Whitenoise, which is a great way to serve static files without needing to mess with your webserver.

django-simple-bulma should now be working! In order to import it into your template, first load the app with {% load django_simple_bulma %}, and then use the {% bulma %} template tag. If you're planning on using icons, you might also want to import FontAwesome by using {% font_awesome %}.

<head>
<!-- ... -->
{% load django_simple_bulma %}
{% bulma %}
{% font_awesome %}
<!-- ... -->
</head>
  • You're all set! Any Bulma classes you apply should now be working!

Customization

Bulma looks nice by default, but most users will want to customize its look and feel. For this, we've provided a super simple way to change the Bulma variables and to choose which Bulma extensions you want to load into your project.

In order to do this, we'll simply create a dictionary inside your settings.py called BULMA_SETTINGS, and configure it there. Here's an example of what that looks like:

# Custom settings for django-simple-bulma
BULMA_SETTINGS = {
  "extensions": [
    "bulma-collapsible",
    "bulma-calendar",
  ],
  "variables": {
    "primary": "#000000",
    "size-1": "6rem",
  },
  "alt_variables": {
    "primary": "#fff",
    "scheme-main": "#000",
  },
  "output_style": "compressed",
  "fontawesome_token": "e761a01be3",
}

You may here define any variable found on the Bulma variables page, and you may use any valid SASS or CSS as the value. For example, hsl(217, 71%, 53%) would be a valid value for a color variable, as would #ffff00. Please note that any syntactically incorrect values may prevent Bulma from building correctly, so be careful what you add here unless you know exactly what you're doing.

Multiple themes

If you want multiple different configurations of variables, then you should define them as separate themes. Define a new theme by providing a key that matches the regex \w+_variables (e.g. alt_variables or dark_variables), unique stylesheets will then be generated using the variables at that key.

To use these stylesheets in a template, pass the theme name to the {% bulma %} tag either as a string {% bulma 'alt' %} or as a template variable {% bulma theme %}.

Extensions

If the extensions key is not found, it will default to not loading any extensions. If you want all extensions, simply set it to the string "all".

We currently support these extensions:

If an extension you want to use is missing, feel free to create an issue and we will be happy to add it. Alternatively, add it yourself and create a pull request ( see this pr for some context on how to go about doing that).

CSS style

The output_style parameter determines the style of the resulting CSS file. It can be any of "nested" (default) , "expanded", "compact", and "compressed". It is recommended to use "compressed" in production as to reduce the final file size.

FontAwesome

The optional fontawesome_token parameter allows you to specify your personal FontAwesome kit, which is necessary for FontAwesome v6 and up. This should be set to the identifier part of your FontAwesome kit script src parameter. For example, if your FontAwesome kit looks like this:

<script src="https://kit.fontawesome.com/e761a01be3.js" crossorigin="anonymous"></script>

Then your fontawesome_token should be e761a01be3.

This is used by the {% font_awesome %} template tag to set up FontAwesome for you. If you don't specify a fontawesome_token, the template tag will still work, but will then use an older version of FontAwesome (v5.14.0) .

Additional scripts

For your convenience, we also give you the option to add other quality of life improvements to your Bulma app. You may want to add these as well if they sound useful to you.

  • bulma-fileupload will handle displaying the filename in your file upload inputs.
  • bulma-navbar-burger will hook up your navbar-burgers and navbar-menus automatically, to provide a toggle for mobile users. We use a slightly updated version of the example from Bulma's documentation - simply add a data-target attribute to your navbar-burger that refers to the id of the navbar-menu that should be expanded and collapsed by the button.
  • bulma-notifications will allow you to close notifications by clicking on the X button.
  • bulma-dropdown will open/close dropdowns using the is-active class. It mimics how the dropdowns function on the documentation page.
  • bulma-modal will handle opening and closing modals. Just assign the modal-button class to a <button>, and make sure it has a data-target attribute that matches the id of the modal that you want to open. See the example code from Bulma's documentation for modal element code.

Compiling custom SCSS

If you're writing custom SCSS for your application, django-simple-bulma provides a mechanism for compiling it for you. This is provided mainly because django-simple-bulma may cause conflicts and issues with other tools to compile SCSS for you.

To use this feature, please specify the custom_css key when defining your BULMA_SETTINGS. This should be a list of strings, containing relative paths to .scss files to be compiled.

BULMA_SETTINGS = {
  "custom_scss": [
    "css/base/base.scss",                  # This is okay
    "my_app/static/css/base/base.scss",    # This also is okay
    "C:\Users\MainDawg\my_app\static\..."  # Don't do this, though.
  ],
}

The default Django behavior when collecting static files is to keep the containing file structure for them when they're copied over to the final staticfiles directory. We do the same thing, so all directories and subdirectories will still be intact in your staticfiles folder after they've been collected.

Here's the strategy the finder uses:

  • If your path contains static/, assume that the base path ends there and use the rest of the path as a relative path to the resource.
  • Use whatever Finders you have enabled in your settings.py to search for the file using that relative path.
  • If the path is found using one of these Finders, compile it to css and collect it.
  • Otherwise, raise a ValueException asking you to double-check the filepath.

Troubleshooting

  • If you have the module sass installed, please note that it is incompatible with this project. There is a namespace conflict between sass and libsass which will make django-simple-bulma crash when you attempt to do a collectstatic. To solve this, just uninstall sass and use libsass instead.

If you run into any other problems with this app, please create an issue, and I'll will be happy to help you with it. You can also find me on Discord as lemon#0001 - either at #django-simple-bulma in the lemonsaurus discord or at https://discord.gg/python.

Github

https://github.com/python-discord/django-simple-bulma

Comments(16)

  • 1

    Intermittent "circular import" when starting Django app

    Hi,

    Thanks for a nice Python/Django package. It works fine, but sometimes I get this error on startup with this Django app installed (as described in the installation details):

    pipenv run ./manage.py runserver
    Watching for file changes with StatReloader
    2021-04-20 13:37:09,008 INFO [django.utils.autoreload] Watching for file changes with StatReloader
    Performing system checks...
    
    Traceback (most recent call last):
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/utils.py", line 66, in __getitem__
        return self._engines[alias]
    KeyError: 'django'
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/backends/django.py", line 121, in get_package_libraries
        module = import_module(entry[1])
      File "/usr/lib/python3.9/importlib/__init__.py", line 127, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
      File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 790, in exec_module
      File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django_simple_bulma/templatetags/django_simple_bulma.py", line 11, in <module>
        from ..utils import (
    ImportError: cannot import name 'fontawesome_token' from partially initialized module 'django_simple_bulma.utils' (most likely due to a circular import) (/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django_simple_bulma/utils.py)
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "/home/per/git/centraln/./manage.py", line 22, in <module>
        main()
      File "/home/per/git/centraln/./manage.py", line 18, in main
        execute_from_command_line(sys.argv)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
        utility.execute()
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/core/management/__init__.py", line 413, in execute
        self.fetch_command(subcommand).run_from_argv(self.argv)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/core/management/base.py", line 354, in run_from_argv
        self.execute(*args, **cmd_options)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 61, in execute
        super().execute(*args, **options)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/core/management/base.py", line 398, in execute
        output = self.handle(*args, **options)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 96, in handle
        self.run(**options)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 103, in run
        autoreload.run_with_reloader(self.inner_run, **options)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/utils/autoreload.py", line 637, in run_with_reloader
        start_django(reloader, main_func, *args, **kwargs)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/utils/autoreload.py", line 622, in start_django
        reloader.run(django_main_thread)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/utils/autoreload.py", line 327, in run
        autoreload_started.send(sender=self)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/dispatch/dispatcher.py", line 180, in send
        return [
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/dispatch/dispatcher.py", line 181, in <listcomp>
        (receiver, receiver(signal=self, sender=sender, **named))
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/autoreload.py", line 41, in watch_for_template_changes
        for directory in get_template_directories():
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/autoreload.py", line 14, in get_template_directories
        for backend in engines.all():
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/utils.py", line 90, in all
        return [self[alias] for alias in self]
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/utils.py", line 90, in <listcomp>
        return [self[alias] for alias in self]
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/utils.py", line 81, in __getitem__
        engine = engine_cls(params)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/backends/django.py", line 25, in __init__
        options['libraries'] = self.get_templatetag_libraries(libraries)
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/backends/django.py", line 43, in get_templatetag_libraries
        libraries = get_installed_libraries()
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/backends/django.py", line 108, in get_installed_libraries
        for name in get_package_libraries(pkg):
      File "/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django/template/backends/django.py", line 123, in get_package_libraries
        raise InvalidTemplateLibrary(
    django.template.library.InvalidTemplateLibrary: Invalid template library specified. ImportError raised when trying to load 'django_simple_bulma.templatetags.django_simple_bulma': cannot import name 'fontawesome_token' from partially initialized module 'django_simple_bulma.utils' (most likely due to a circular import) (/home/per/.virtualenvs/centraln-czG2gyNf/lib/python3.9/site-packages/django_simple_bulma/utils.py)
    make: *** [Makefile:24: serve] Error 1
    

    Note that this only happens sometimes, which is both puzzling and annoying. It can also happen when making a change to any .py file in the project, i.e. when the apps are reloaded. Any ideas? Thanks in advance.

  • 2

    Latest release seems to be overriding the Bulma default is-link color

    Noticed that django-simple-bulma has changed the default "is-link" modifier color. The default used to be light blue, but with version 2.4.0, it has changed to a light purple (which is what I believe you use on your Python Dischord site). Haven't dug into the code yet, but I'm guessing maybe your site's css scheme might have been shipped.

  • 3

    Upgrade to Django 3.*

    Looks like a least one change is needed to be make to django-simple-bulma work with for Django 3.*

    If I get a few free cycles I'll look at working up a fix, and see if there are any other changes that need to be made.

  • 4

    Full path in js files

    I have following problems with JS files loaded into template.

    Your loader uses full path which in my case means that path contains site-packages and all that stuff
    /lib/python3.6/site-packages/django_simple_bulma/js/bulma-tagsinput.js

    Loading css works fine because it's done like this css = static("css/bulma.css") instead of using full path.

    By modifying following code to use filename.name to construct path seems to work in my case.

    @register.simple_tag
    def bulma():
        """
        Build and return all the HTML required to
        import bulma and the javascript for all
        active extensions.
        """
        # Build the html to include the stylesheet
        css = static("css/bulma.css")
        html = [f'<link rel="stylesheet" href="{css}">']
    
        # Build html to include all the js files required.
        for filename in js_folder.iterdir():
            js_file = static(f"js/{filename.name}")
            extension_name = filename.stem
    
            if extension_name in extensions or extensions == "_all":
                html.append(f'{" " * 8}<script type="text/javascript" src="{js_file}"></script>')
    
        return mark_safe("\n".join(html))  # noqa
    

    Now to my actual question. Is there something wrong on my end or is there actually small bug in code?

    Thank you very much for your work with this and if there's anything I can do to help further just say a word.

  • 5

    Submodule bulma instead of keeping a copy.

    Right now, we've got copies of specific versions of Bulma and Bulma-Extensions in this repo, which is license permissable but adds maintenance in that we have to manually update these files to in order to "update" to a new version.

    Perhaps we could include bulma and bulma-extensions as submodules to ensure these stayed up to date, and that we did not need to manage these files ourselves?

  • 6

    Add extension submodules

    Further work on #42 Closes #7

    I didn't include some extensions because they were extremely out of date or simple not worth the time. Most of the extensions are made by the same guy, so their directory structure all match up, but others have wildly differing setups. I tried my best to have it figure out what files are relevant, meaning adding new extensions should only be a matter of running:

    git submodule add <repo-link>
    git mv ./[extension_name]/ ./django_simple_bulma/extensions/[extension_name]
    

    Since i removed the old extensions, this is definitely not backwards compatible, because of this I suggest that when this branch is merged we release it as v2 or at least v1.4. Also, the way I've been testing is with one big file with all the extensions, which is only a visual verification. I also haven't tested the custom variables some of the extensions use.

  • 7

    Add pep8-naming and more pre-commit hooks

    Relevant Issues

    python-discord/organisation#138 python-discord/organisation#153 Closes https://github.com/python-discord/django-simple-bulma/issues/40

    Description

    New hooks were added for pre-commit and they will run in CI too. pep8-naming was added as a flake8 plugin to ensure names comply with PEP 8.

    Hooks added

    A couple of these hooks automatically apply fixes. However, they still report failure and leave any changes they make uncommitted. Therefore, the user has to commit the automatic fixes.

    • check-merge-conflict - Check for files that contain merge conflict strings.
    • check-yaml - Attempts to load all yaml files to verify syntax.
    • end-of-file-fixer - Makes sure files end in a newline and only a newline.
    • mixed-line-ending - Replaces mixed line endings with LF.
    • trailing-whitespace - Trims trailing whitespace.
    • python-check-blanket-noqa - Enforce that noqa annotations always occur with specific codes

    Reasoning

    • Dev dependencies were updated and re-pinned because why not? They were getting old enough. I'm just following along with the other repos.
    • The pre-commit environment is cached to speed up builds.
    • To reduce redundancy and overhead in maintain dependencies, flake8 is now invoked through the current active Python environment rather than being installed in pre-commit's isolated environment. This means the hook now assumes the user already has flake8 installed i.e. via pip install -e .[dev].

    Additional Details

    The pre-commit venv is cached. There should only be a cache miss if the .pre-commit-config.yaml file changes or if the location of the Python interpreter changes (more realistically, if the Python version changes). Maybe Azure wipes the cache sometimes too, but in that case pre-commit will simply re-create the environment.

  • 8

    Github Release action for v2.2.0 failed

    Looks like your Github action is using the depreciated legacy method. I believe if you update to a more recent gh action (like pypa/[email protected]) it should work.

  • 9

    Missing files in v2.0.0 release files

    Updating to v2.0.0, and it looks like the submodules you're using now aren't included in the tarball. Since I get the following error when attempting to run django.

    FileNotFoundError: [Errno 2] No such file or directory: '/home/bpepple/.local/share/virtualenvs/metron-UZX73b7p/lib/python3.6/site-packages/django_simple_bulma/bulma/sass'

    Pulled the tarball down from the release page, and verified the bulma folder is empty.

  • 10

    New release: 1.2.0

    For 1.2.0, we should add support for Django 2.2, Django 3.0 and Python 3.8.

    • We've already merged #38, which should allow us to support Django 3.0.
    • This app probably already supports Python 3.8, but this needs to be tested.

    So here's what needs to be done before we release the 1.2.0:

    • [x] Test django-simple-bulma inside a Django 3.0 app running Python 3.8, and verify that it still works as expected.
    • [ ] Make a pull request to the setup.py file and add Django 2.2, Django 3.0, Python 3.8 to the list of classifiers.
    • [ ] Also in the same pull request in setup.py, bump the version of django-simple-bulma to 1.2.0

    Once this has been done, we can do a release to PyPI.

  • 11

    CompileError() with Powershell

    Hi,

    I'm getting the following error with Powershell. This is likely because of platform insensitive paths in the finder code.

    File "C:\Users\<username>\envs\djenv\lib\site-packages\sass.py", line 709, in compile
        raise CompileError(v)
           jango_simple_bulma/bulma.sass.ort not found or unreadable: C:Users<username>nvs
            on line 6 of stdin
    >> @import "C:\Users\<username>\envs\djenv\Lib\site-packages\django_simple_bulma/bulm
    

    Best, Oguzhan

  • 12

    Trouble running "python manage.py collectstatic" in Python 3.7.5 Django app

    After successfully installing django-simple-bulma via pip I run collectstatic per instructions and get the following error:

    python manage.py collectstatic
    ...
      File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
      File "/Users/mruser/.pyenv/versions/scorekeeper/lib/python3.7/site-packages/django_simple_bulma/finders.py", line 166
        if absolute_path := finder.find(relative_path):
                          ^
    SyntaxError: invalid syntax
    

    I believe Python 3.7.5 does not like the := operator (apparently added in 3.8).

  • 13

    `_get_custom_css` results in `sass.CompileError` when `custom_scss` path is not found by finders

    I have set Bulma settings to

    BULMA_SETTINGS = {
        "custom_scss": [
            "cant/find/this.scss"
        ]
    }
    

    I expect to see ValueError: Unable to locate the SCSS file .... However I get an unhelpful sass.CompileError instead:

    sass.CompileError: Error: File to import not found or unreadable: [].
    

    It looks like this package has an helpful error that should be show, however

    https://github.com/lemonsaurus/django-simple-bulma/blob/3e3eac809cb8f04ce2494a406ee375ea0dce63ae/django_simple_bulma/finders.py#L170

    when this step is run, the value is an empty list [], so the comparison to None is evaluated as false.

    Suggestion: Change expression to if not absolute_path (I can submit a PR with that if desired).

  • 14

    Bulma Calendar widget shows incorrect value

    Using the Bulma Calendar widget and notice that it's showing the incorrect date on a form.

    Below screenshot shows the values it should be: Screenshot from 2022-03-05 15-39-13

    When updating the object the Bulma-Calendar widget shows the following: Screenshot from 2022-03-05 15-39-25

    It appears to be showing a day earlier than the value of the input: Screenshot from 2022-03-05 15-44-46

    Did a quick look at upstreams bugs and didn't see any for this bug, but when I get some free time I'll give a more thorough search.

  • 15

    New to django, how do i add the bulma calendar to my website?

    Hi there, I'm new to django and ive followed your steps on installing the app and the extensions. Just one question: How do i add the calendar to my website? I'm completely lost and I cant find anything online to help me. Thanks!

  • 16

    Using multiple themes

    Currently, I attempting to use this project's multiple themes functionality to create a dark and light mode.

    While trying to do this am I facing issues with sending duplicate .js files, as the tag has been used twice. With this .js being served twice, it is creating two event handlers for the navbar burger menu on the page and is causing it to stop working.

    Django template:

    {% bulma %}
    {% bulma 'dark' %}
    

    Firefox network tab when loading the page: image

    I am unsure how Django extensions work, but having a way to ensure that only one .js file is served would allow me to implement the functionality I want.


    This functionality for switching themes could be implemented into this package also, as it would be a nice to have for other users of django-simple-bulma