Create a Base Template for SuperDjango UI

Warning

As of this writing (release 4.1.0), SuperDjango UI is reasonably stable and the requirements for the base template are reasonably set. However, this does not mean further changes will not occur.

The Need for Base

The UI system expects a base.html template. The base_template variable is added to the context and defaults to base.html. Both superdjango.ui.interfaces.ModelUI and superdjango.ui.interfaces.SiteUI provide a base_template attribute that may be used to override the setting.

Structure of the Theme

The theme/ directory contains the templates and files used to render the site, while the static/ directory contains only static files that are site-wide or used to override the theme. This helps keep the theme more generic and re-usable.

The suggested structure includes the following:

project_name
|-- source
|   |-- static
|   |   |-- css
|   |   |   `-- custom.css
|   |   |-- fonts
|   |   |-- images
|   |   `-- js
|   |       `-- custom.js
|   |-- theme
|   |   |-- includes
|   |   |   |-- breadcrumbs.html
|   |   |   |-- footer.html
|   |   |   |-- menu.html
|   |   |   |-- messages.html
|   |   |   |-- navbar.html
|   |   |   `-- submenu.html
|   |   |-- static
|   |   |   |-- css
|   |   |   |-- fonts
|   |   |   |-- images
|   |   |   `-- js
|   |   |-- 403.html
|   |   |-- 404.html
|   |   |-- 500.html
|   |   |-- 503.html
|   |   |-- base.html
|   |   |-- sidebar_left.html
|   |   |-- sidebar_right.html
|   |   `-- three_columns.html
`--

Base Template HTML

The Start of the Base Template

The beginning of the template looks something like this.

{% load html_tags %}
{% load i18n %}
{% load static %}
<!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, shrink-to-fit=no">
        <title>{% block title %}{% endblock %} - {{ SITE_TITLE }}</title>
        <link rel="shortcut icon" type="image/png" href="{% static "images/favicon.png" %}"/>
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/all.css">
        <link href="{% static "css/theme.css" %}" rel="stylesheet">
        <link href="{% static "css/custom.css" %}" rel="stylesheet">

        {# view css #}
        {% for css in view_css %}
            {{ css }}
        {% endfor %}

        {% block extra_css %}
        {% endblock %}

        {% block extra_head %}
        {% endblock %}
    </head>
    <body class={% block body_class %}{% endblock %}">

Some things of note:

  • We load html_tags, i18n, and static template tags.

  • FontAwesome is current the font used by the UI system. (Later releases may handle support of different icon libraries.)

  • The theme.css is loaded next, and then the custom.css to allow any overrides.

  • view_css is an iterable received from the view which allows the view to contribute CSS links or style blocks to the theme as needed.

  • extra_css allows templates to contribute CSS links or style blocks.

  • extra_head may be used by templates for any additional HTML to be included in the head tab.

  • Finally, the body_class block is added to the opening body tag to allow body-wide styling as needed.

Pre-Content Body of Base Template

Next, we add the HTML for everything that appears before the principle content of the page. This may vary greatly depending upon the design you are adapting, but the important bit is the blocks to include.

<div class="container">
    {% block navbar %}
        {% include "includes/navbar.html %}
    {% endblock %}

    {% block messages %}
        {% if messages %}
            <div class="row" id="messages">
                <div class="col">
                    {% for message in messages %}
                        {% html "alert" enable_close=True status=message.tags %}
                    {% endfor %}
                </div>
            </div>
        {% endif %}
    {% endblock %}

    {% block breadcrumbs %}
        {% if breadcrumbs %}
            <div class="row" id="breadcrumbs">
                <div class="col">
                    {% html "breadcrumbs" include_home=True home_url="/dashboard/" %}
                </div>
            </div>
        {% endif %}
    {% endblock %}

    {% block heading %}
        <div class="row" id="heading">
            <div class="col-8">
                {% block page_header %}{% endblock %}
            </div>
            <div class="col-4">
                {% block call_to_action %}{% endblock %}
            </div>
        </div>
    {% endblock %}

Some things of note:

  • Everything is wrapped in a block to allow overriding.

  • It’s good to supply element IDs to allow specific styling overrides if needed. It’s also nice when these IDs are the same as the blocks they wrap.

  • Each block is important to SuperDjango UI.

Defining the Base Layout

Next we come to the layout of the page.

{% block layout %}
    {% block main %}
        <div class="row">
            <div class="col-12" id="content">
                {% block content %}
                {% endblock %}
            </div>
        </div>
        <div class="row">
            <div class="col-12" id="sidebar">
                {% block sidebar %}
                {% endblock %}
            </div>
        </div>
    {% endblock %}
{% endblock %}

Some things of note:

  • Wrapping the main block in layout allows other templates to easily change the layout.

Closing Out The Base

The end of the base template adds a footer, closes the container, and includes JavaScript.

            <div id="footer">
                {% block footer %}
                    {% include "includes/footer.html" %}
                {% endblock %}
            </div>
        </div>{# container close #}

        {# core javascript and plugins #}
        <script src="{% static "vendor/jquery/jquery.min.js" %}"></script>
        <script src="{% static "vendor/bootstrap/js/bootstrap.bundle.min.js" %}"></script>
        <script src="{% static "vendor/jquery-easing/jquery.easing.min.js" %}"></script>

        {# globally enable bootstrap tooltips #}
        <script>
            $(function () {
                $('[data-toggle="tooltip"]').tooltip()
            })
        </script>

        {# https://docs.djangoproject.com/en/3.0/topics/i18n/translation/#module-django.views.i18n #}
        <script type="text/javascript" src="{% url 'sd-javascript-catalog' %}"></script>

        {# form #}
        {% if form %}
            {{ form.media }}
        {% endif %}

        {# view js #}
        {% for js in view_js %}
            {{ js }}
        {% endfor %}

        {# extra js #}
        {% block extra_js %}
        {% endblock %}

    </body>
</html>

Some things of note:

  • Footer is an include which may include legal info, menus, etc.

  • Form styles and scripts are included after all other scripts, but before view JS.

  • view_js is an iterable which allows views to contribute JavaScript to the rendered page.

  • extra_js allows templates to contribute JavaScript to the rendered page.

The Finished Base Template

The final template looks something like this:

{% load html_tags %}
{% load i18n %}
{% load static %}
<!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, shrink-to-fit=no">
        <title>{% block title %}{% endblock %} - {{ SITE_TITLE }}</title>
        <link rel="shortcut icon" type="image/png" href="{% static "images/favicon.png" %}"/>
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/all.css">
        <link href="{% static "css/theme.css" %}" rel="stylesheet">
        <link href="{% static "css/custom.css" %}" rel="stylesheet">

        {# view css #}
        {% for css in view_css %}
            {{ css }}
        {% endfor %}

        {% block extra_css %}
        {% endblock %}

        {% block extra_head %}
        {% endblock %}
    </head>
    <body class={% block body_class %}{% endblock %}">
        <div class="container">
            {% block navbar %}
                {% include "includes/navbar.html %}
            {% endblock %}

            {% block messages %}
                {% if messages %}
                    <div class="row" id="messages">
                        <div class="col">
                            {% for message in messages %}
                                {% html "alert" enable_close=True status=message.tags %}
                            {% endfor %}
                        </div>
                    </div>
                {% endif %}
            {% endblock %}

            {% block breadcrumbs %}
                {% if breadcrumbs %}
                    <div class="row" id="breadcrumbs">
                        <div class="col">
                            {% html "breadcrumbs" include_home=True home_url="/dashboard/" %}
                        </div>
                    </div>
                {% endif %}
            {% endblock %}

            {% block heading %}
                <div class="row" id="heading">
                    <div class="col-8">
                        {% block page_header %}{% endblock %}
                    </div>
                    <div class="col-4">
                        {% block call_to_action %}{% endblock %}
                    </div>
                </div>
            {% endblock %}

            {% block layout %}
                {% block main %}
                    <div class="row">
                        <div class="col-12" id="content">
                            {% block content %}
                            {% endblock %}
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-12" id="sidebar">
                            {% block sidebar %}
                            {% endblock %}
                        </div>
                    </div>
                {% endblock %}
            {% endblock %}

            <div id="footer">
                {% block footer %}
                    {% include "includes/footer.html" %}
                {% endblock %}
            </div>
        </div>{# container close #}

        {# core javascript and plugins #}
        <script src="{% static "vendor/jquery/jquery.min.js" %}"></script>
        <script src="{% static "vendor/bootstrap/js/bootstrap.bundle.min.js" %}"></script>
        <script src="{% static "vendor/jquery-easing/jquery.easing.min.js" %}"></script>

        {# globally enable bootstrap tooltips #}
        <script>
            $(function () {
                $('[data-toggle="tooltip"]').tooltip()
            })
        </script>

        {# https://docs.djangoproject.com/en/3.0/topics/i18n/translation/#module-django.views.i18n #}
        <script type="text/javascript" src="{% url 'sd-javascript-catalog' %}"></script>

        {# form #}
        {% if form %}
            {{ form.media }}
        {% endif %}

        {# view js #}
        {% for js in view_js %}
            {{ js }}
        {% endfor %}

        {# extra js #}
        {% block extra_js %}
        {% endblock %}

    </body>
</html>