Build a Dashboard with Django and Bootstrap: Part 3

This is Part 3 of Building a Dashboard with Django and Bootstrap:

Introduction

If you follow the first part of this blog topic, you have a running Django dashboard.

But, the content ist still static. Lets review the current state:

Prepare our Django project

Right now, the whole content of our Django project is provided by the dashboard template

dashboard/template/site/base.html

Looking at our web site, you will see the different side menu items. So, intentionally, our web site should display different pages. And each page should provide the dynamic content.

The final goal of this part is to change our web app, so that each side item navigates us to a different page. For this, we have to take care about two things:

  • Navigation: how to we get to another page in our app
  • Project Structure: where to place the required components for each page

Basics of Navigation

Navigation usually is the process of getting from one page to another by clicking on a link.

So, we need to things:

  • the source page, containing the link
  • the destination page
  • the link, pointing to the destination page

Let’s take a look into the site template with the side menu:

The corresponding code in the side template is

<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo">
	<div class="bg-white py-2 collapse-inner rounded">
		<h6 class="collapse-header">Custom Components:</h6>
		<a class="collapse-item" href="buttons.html">Buttons</a>
		<a class="collapse-item" href="cards.html">Cards</a>
	</div>
</div>

This is the result:

Linking to a html page is not possible, because Django does not work with html pages. Navigation in Django works with urls (in urls.py) and views in (views.py).

We must replace the html link tag (<a href="buttons.html">) with an Django-conform code. Read here for more details and the basics.

The idea behind the navigation is

Define the needed links/buttons
Give each link a name“buttons”
Define, which view to call for this linkcomponents/buttons/views.py
Tell Django, how to insert this link in a html page<a href="{% url 'buttons' %}">Buttons</a>

With this in mind, we change our site template for the side navigation (e. g. for the components menu):

But, if you save the template and try to view the web page, you will see this error:

We missed to tell Django, what to do when the corresponding link for this name is requested. We have to tell Django to use the view defined in buttons/views.py to generate the resulting view/page.

So, change the global url mapping file dashboard/urls.py

import dashboard.apps.components.buttons.views as ButtonsViews
import dashboard.apps.components.cards.views as CardsViews

urlpatterns = [
    path('', include('dashboard.apps.urls')),

    path('buttons', 	ButtonsViews.IndexView.as_view(),   name='buttons'),
    path('cards',       CardsViews.IndexView.as_view(),     name='cards'),

    path('admin/', admin.site.urls),
]

dashboard/apps/components/buttons/views.py

from django.shortcuts import render
from django.views import generic

class IndexView(generic.TemplateView):
    template_name = 'buttons/base.html'

dashboard/apps/components/cards/views.py

from django.shortcuts import render
from django.views import generic

class IndexView(generic.TemplateView):
    template_name = 'cards/base.html'

dashboard/apps/components/cards/templates/cards/base.html

{% extends 'site/base.html' %}
{% load static %}

{% block content %}
<h1 style="text-align: center">CARDS</h1>
{% endblock content %}

dashboard/apps/components/cards/templates/buttons/base.html

{% extends 'site/base.html' %}
{% load static %}

{% block content %}
<h1 style="text-align: center">BUTTONS</h1>
{% endblock content %}

Save everything and view at the resulting page

What happens, from showing the page, clicking on the link until you see the final result:

  • Django create the side menu item Cards
  • with the corresponding link localhost:9000/cards
  • which will be the destination page, if you click on the link
  • and finally, Django creates the desired page (through view.py)

Namespaces and structure

Now, think about the names, e. g. buttons and cards in our Components.

Your project is getting bigger and you plan to add an additional page info with general information for both Components and Utilities menu.

So, you will have to additional files, for example

  • dashboard/apps/components/info/views.py
  • dashboard/apps/utilities/info/views.py

The corresponding url mapping (urls.py) could look like this:

import dashboard.apps.components.info.views as ComponentInfoViews
import dashboard.apps.utilities.info.views as UtilitiesInfoViews

urlpatterns = [
    path('', include('dashboard.apps.urls')),

    path('info', ComponentInfoViews.IndexView.as_view(), name='info'),
    path('info', UtilitiesInfoViews.IndexView.as_view(), name='info'),

Two pages with the same name (info) in different views.py!

Of course, we could choose different names and link (component_info, utilities_info), but this not a good programming style.

We will choose a more modern way of programming

  • Spread the responsibility over separate, independent modules
  • Name these modules with different names

What does this mean? We will have

  • a separate module frontend, only responsible for the start page (frontend)
  • a separate module components, responsible for all components
  • a separate module utilities, responsible for all utilities
  • a separate module pages, responsible for all pages

Resulting folder structure and file content

File dashbard/urls.py

urlpatterns = [
    path('', include('dashboard.apps.urls')),
    path('admin/',	admin.site.urls),
]

File dashbard/apps/urls.py

from django.urls import path
from django.urls.conf import include

from dashboard.apps.frontend.views import IndexView

app_name = 'app'

urlpatterns = [
    path('', IndexView.as_view(), name='index'),
    path('pages/', include('dashboard.apps.pages.urls')),
    path('components/', include('dashboard.apps.components.urls')),
    path('utilities/', include('dashboard.apps.utilities.urls')),
]

File dashbard/apps/components/urls.py

from django.urls import path

import dashboard.apps.components.buttons.views as ButtonsViews
import dashboard.apps.components.cards.views as CardsViews

app_name = 'components'

urlpatterns = [
    path('', ButtonsViews.IndexView.as_view(), name='index'),
    path('buttons/', ButtonsViews.IndexView.as_view(), name='buttons'),
    path('cards/', CardsViews.IndexView.as_view(), name='cards'),
]

File dashbard/apps/utilities/urls.py

from django.urls import path

import dashboard.apps.utilities.colors.views as ColorsViews
import dashboard.apps.utilities.borders.views as BordersViews
import dashboard.apps.utilities.animations.views as AnimationsViews
import dashboard.apps.utilities.others.views as OthersViews

app_name = 'utilities'

urlpatterns = [
    path('', BordersViews.IndexView.as_view(), name='index'),
    path('animations/', AnimationsViews.IndexView.as_view(), name='animations'),
    path('borders/', BordersViews.IndexView.as_view(), name='borders'),
    path('colors/', ColorsViews.IndexView.as_view(), name='colors'),
    path('others/', OthersViews.IndexView.as_view(), name='others'),
]

File dashbard/apps/pages/urls.py

from django.urls import path

import dashboard.apps.pages.blank.views as BlankViews
import dashboard.apps.pages.login.views as LoginViews
import dashboard.apps.pages.pagenotfound.views as PageNotFoundViews
import dashboard.apps.pages.password.views as PasswordViews
import dashboard.apps.pages.register.views as RegisterViews
import dashboard.apps.pages.charts.views as ChartsViews
import dashboard.apps.pages.tables.views as TablesViews

app_name = 'pages'

urlpatterns = [
    path('', ChartsViews.IndexView.as_view(), name='index'),
    path('blank', BlankViews.IndexView.as_view(), name='blank'),
    path('charts', ChartsViews.IndexView.as_view(), name='charts'),
    path('login', LoginViews.IndexView.as_view(), name='login'),
    path('pagenotfound', PageNotFoundViews.IndexView.as_view(), name='pagenotfound'),
    path('password', PasswordViews.IndexView.as_view(), name='password'),
    path('register', RegisterViews.IndexView.as_view(), name='register'),
    path('tables', TablesViews.IndexView.as_view(), name='tables'),
]

Let’s finally check the namespace structure:

$ find . -name urls.py
./dashboard/urls.py
./dashboard/apps/utilities/urls.py
./dashboard/apps/components/urls.py
./dashboard/apps/urls.py
./dashboard/apps/pages/urls.py

We create three levels for our namespaces:

Djane URL FileNamespace
./dashboard/urls.py
./dashboard/apps/urls.pyapp
./dashboard/apps/utilities/urls.py
./dashboard/apps/components/urls.py
./dashboard/apps/pages/urls.py
app:utilities
app:components
app:pages

These namespaces must be used in the template files, for example:

<a href="{% url 'app:components:buttons' %}" class="collapse-item" >Buttons</a>
<a href="{% url 'app:components:cards' %}" class="collapse-item" >Cards</a>
<a class="collapse-item" href="{% url 'app:utilities:colors' %}">Colors</a>
<a class="collapse-item" href="{% url 'app:utilities:borders' %}">Borders</a>
<a class="collapse-item" href="{% url 'app:utilities:animations' %}">Animations</a>
<a class="collapse-item" href="{% url 'app:utilities:others' %}">Other</a>

Install the Django Extensions for additional commands:

pip install django-extensions

Add Django Extensions to the INSTALLED_APPS

INSTALLED_APPS = [
    ...

    'django_extensions'
]

Show URLs and Namespaces (only for out apps, admin urls are removed)

python3 manage.py show_urls

Preparing required components and pages

In summary, these are the steps to create the desired folder structure:

mkdir -p dashboard/apps/components/buttons/templates/buttons
mkdir -p dashboard/apps/components/cards/templates/cards
mkdir -p dashboard/apps/pages/blank/templates/blank
mkdir -p dashboard/apps/pages/charts/templates/charts
mkdir -p dashboard/apps/pages/login/templates/login
mkdir -p dashboard/apps/pages/pagenotfound/templates/pagenotfound
mkdir -p dashboard/apps/pages/password/templates/password
mkdir -p dashboard/apps/pages/register/templates/register
mkdir -p dashboard/apps/pages/tables/templates/tables
mkdir -p dashboard/apps/utilities/animations/templates/animations
mkdir -p dashboard/apps/utilities/borders/templates/borders
mkdir -p dashboard/apps/utilities/colors/templates/colors
mkdir -p dashboard/apps/utilities/others/templates/others
python3 manage.py startapp buttons dashboard/apps/components/buttons
python3 manage.py startapp cards dashboard/apps/components/cards
python3 manage.py startapp blank dashboard/apps/pages/blank
python3 manage.py startapp charts dashboard/apps/pages/charts
python3 manage.py startapp login dashboard/apps/pages/login
python3 manage.py startapp pagenotfound dashboard/apps/pages/pagenotfound
python3 manage.py startapp password dashboard/apps/pages/password
python3 manage.py startapp register dashboard/apps/pages/register
python3 manage.py startapp tables dashboard/apps/pages/tables
python3 manage.py startapp animations dashboard/apps/utilities/animations
python3 manage.py startapp borders dashboard/apps/utilities/borders
python3 manage.py startapp colors dashboard/apps/utilities/colors
python3 manage.py startapp others dashboard/apps/utilities/others
echo "{% extends 'site/base.html' %}

{% block    content %}
{% endblock content %}"> base.html
cp base.html dashboard/apps/components/buttons/templates/buttons
cp base.html dashboard/apps/components/cards/templates/cards
cp base.html dashboard/apps/pages/blank/templates/blank
cp base.html dashboard/apps/pages/charts/templates/charts
cp base.html dashboard/apps/pages/login/templates/login
cp base.html dashboard/apps/pages/pagenotfound/templates/pagenotfound
cp base.html dashboard/apps/pages/password/templates/password
cp base.html dashboard/apps/pages/register/templates/register
cp base.html dashboard/apps/pages/tables/templates/tables
cp base.html dashboard/apps/utilities/animations/templates/animations
cp base.html dashboard/apps/utilities/borders/templates/borders
cp base.html dashboard/apps/utilities/colors/templates/colors
cp base.html dashboard/apps/utilities/others/templates/others
rm base.html

Each of the folders has the same structure, for example the buttons component. We will delete some unnecessary files

Replacing Projects with dynamic data

Replacing the static parts with dynamic content could be achieved by the following approach:

  • Identify the dynamic parts
  • Move these parts from the site template into the view template base.html of the component
  • Modify frontend view.py to generate dynamic content from data

The steps are the same for all components (all items of the side menu).

Find the

Identify dynamic parts in template

Create templates for side menu pages

For every side menu item, their is a corresponding html file in the install folder of the sb-admin-2 template:

Remember the environment variable we create in part 1 for the start of our project

DASHBOARD_ROOT=$(pwd)
cd $DASHBOARD_ROOT
find dashboard/apps install/sb-admin-2 -name *.html

Each template file base.html has a corresponding html file unter sb-admin-2. Look at the following table to find the mapping:

apps/components/buttons/templates/buttons/base.htmlsb-admin-2/buttons.html
apps/components/cards/templates/cards/base.htmlsb-admin-2/cards.html
apps/pages/blank/templates/blank/base.htmlsb-admin-2/blank.html
apps/pages/charts/templates/charts/base.htmlsb-admin-2/charts.html
apps/pages/login/templates/login/base.htmlsb-admin-2/login.html
apps/pages/pagenotfound/templates/pagenotfound/base.htmlsb-admin-2/404.html
apps/pages/password/templates/password/base.htmlsb-admin-2/forgot-password.html
apps/pages/register/templates/register/base.htmlsb-admin-2/register.html
apps/pages/register/templates/tables/base.htmlsb-admin-2/tables.html
apps/utilities/animations/templates/animations/base.htmlsb-admin-2/utilities-animation.html
apps/utilities/borders/templates/borders/base.htmlsb-admin-2/utilities-border.html
apps/utilities/colors/templates/colors/base.htmlsb-admin-2/utilities-color.html
apps/utilities/others/templates/others/base.htmlsb-admin-2/utilities-html

Each template base.html should have the following content:

{% extends 'site/base.html' %}

{% block    content %}
{% endblock content %}

And each corresponding view.py file should have the following content, only the template_name should be different (the name of the template base.html file)

from django.views import generic

class IndexView(generic.TemplateView):
    template_name = 'buttons/base.html'

So, for each template file, we have to

  • locate the corresponding html file from the install folder (see table above)
  • copy the content between these tags to the template file:
        <!-- Begin Page Content -->
        <div class="container-fluid">
        ....
        </div>
        <!-- /.container-fluid -->

Build a Dashboard with Django and Bootstrap: Part 2

This is Part 2 of Building a Dashboard with Django and Bootstrap:

Introduction

If you follow the first part of this blog topic, you have a running Django dashboard.

But, unfortunately, the content is still static. Let’s review the current state:

Perfect. We are done with the basic setup.

Still, some work to do, because our dashboard is only a static dashboard. All content is programmed in the dashboard template file dashboard/templates/site/sb-admin-2/base.html

For example, look at the cards with the earnings at the top:

To achieve a more dynamic content, we need to move the desired parts of the dashboard from the template file to the frontend view file.

We will do this by following these steps:

  • Identify the dynamic parts
  • Move these parts from the template into for frontend view template index.html
  • Modify frontend view.py to generate dynamic content from data

Identify dynamic parts

How to find the parts, which are dynamic.

One way is to ask:

  • Which parts should be on every page (unchanged) and
  • What should change on every page

You mostly get the same answers by the question:

  • What are the main components of a web page (including navigation and content)

For answer the first question, take a look at the current page and “name” the areas:

So, these “names” are also the answer for the 3. Question:

  • sidemenu
  • top bar
  • content

And maybe you find additional “names”

  • header
  • footer
  • top menu
  • left and right sidebar

Find identified parts in template

Next step is, to find the identified parts in our dashboard template

dashboard/templates/site/sb-admin-2/base.html

This is an easy step, because the developer of the SB Admin 2 template documented their template well:

Looking into the code of the template, you will find comment lines describing the content:

  • <!-- Sidebar -->
  • <!-- Topbar -->
  • <!-- Top Search -->
  • <!-- Top Navbar -->
  • <!-- Begin Page Content-->

So, it is obvious what do to next:

  • get the dynamic part (lines under)<!-- Begin Page Content-->
    the green box in the following image
  • move it to the frontend template
  • place some information in the dashboard template, that the real content should be displayed here
    the blue curly braces in the following image

This is the way, the template system of django works:

Let’s explain this with a simple example: the page title

We declare a title (which should be considered as the default title). And in the frontend page, we declare the title for this page (the frontend page).

To achieve this, we have to tell our template system the following:

Now, we do the same with the content:

Looking at our resulting page, nothing changes. This is the desired result, but how could we be sure, that we really change the structure?

Well, let’s make a test and try to include a different content in the dashboard template.

Change the lines, where we include the content into this:

{% block content_missing %}
MISSING CONTENT
{% endblock content_missing %}

Did you notice the other name of the content: content_missing?

Change the template, save the file and have a look at the result:

Change content back, so your template is working again:

{% block content %}
MISSING CONTENT
{% endblock content %}

The final step in Part 3 will be replacing all static content of the dashboard with dynamic content.

Build a Dashboard with Django and Bootstrap: Part 1

This is Part 1 of Building a Dashboard with Django and Bootstrap:

Introduction

Building a complete web app isn’t always an easy task. Designing and Implementing on both sides (backend and front-end) requires mostly a lot of knowledge. So, why don’t using tools or framework, which helps and makes our life easier. Django is one of these frameworks:

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development

So, let’s get started.

Create project

For subsequent steps, we will remember our starting directory

DASHBOARD_ROOT=$(pwd)
echo $DASHBOARD_ROOT
... here you will see your current folder...

First step is to create our main Django project

django-admin startproject dashboard
cd dashboard
python manage.py migrate
python manage.py runserver 8080
python manage.py startapp app_base

View current project in browser

Create first apps

mkdir -p dashboard/apps/core
python manage.py  startapp Core dashboard/apps/core

mkdir -p dashboard/apps/frontend
python manage.py createapp Frontend dashboard/apps/frontend

Add new apps to project

Modify dashboard/settings.py

INSTALLED_APPS = [
   ...

    'dashboard.apps.core',
    'dashboard.apps.frontend',
]

Modify dashboard/urls.py

from django.urls import path

import dashboard.apps.frontend.views as views

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('admin/', admin.site.urls),
]

Create view

Modify view in apps/frontend/views.py

from django.shortcuts import render
from django.views import generic


class IndexView(generic.TemplateView):
    """
    IndexView:
    """
    module = 'indexView'
    template_name = 'frontend/index.html'

Create template for frontend View

Create template file dashboard/apps/frontend/templates/frontend/index.html

<h1>
Let's get started
</h1>

View current project in browser

Current folder Structure

So far, we have the following folder structure

Prepare for administrate your project

Create admin user

python manage.py createsuperuser
Username (leave blank to use 'user'): admin
Email address: admin@localhost
Password: 
Password (again): 
Superuser created successfully.

Add Bootstrap support

Download requires files for Bootstrap, jQuery ad PopperJS

mkdir -p install/bootstrap
wget  -q https://github.com/twbs/bootstrap/archive/v4.4.1.zip -O install/bootstrap/4.4.1.zip
wget  -q https://github.com/twbs/bootstrap/releases/download/v4.4.1/bootstrap-4.4.1-dist.zip -O install/bootstrap/4.4.1-dist.zip
rm -rf   dashboard/static/vendor/bootstrap/bootstrap-4.4.1-dist
unzip -q install/bootstrap/4.4.1-dist.zip -d       dashboard/static/vendor/bootstrap/
rm -rf   dashboard/static/vendor/bootstrap/4.4.1
mv       dashboard/static/vendor/bootstrap/bootstrap-4.4.1-dist dashboard/static/vendor/bootstrap/4.4.1
mkdir -p dashboard/static/vendor/jquery/3.4.1/js
wget -q https://code.jquery.com/jquery-3.4.1.min.js      -O dashboard/static/vendor/jquery/3.4.1/js/jquery.min.js
wget -q https://code.jquery.com/jquery-3.4.1.slim.min.js -O dashboard/static/vendor/jquery/3.4.1/js/jquery.slim.min.js
make install-popperjs
mkdir -p dashboard/static/vendor/popper/2.0.6/js
wget  -q https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js          -O dashboard/static/vendor/popper/2.0.6/js/popper.min.js
wget  -q https://unpkg.com/@popperjs/core@2.0.6/dist/umd/popper.min.js.map  -O dashboard/static/vendor/popper/2.0.6/js/popper.min.js.map

Create site templates in dashboard/templates/site

Add templates path to settings

File dashboard/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            BASE_DIR + '/dashboard/templates',
        ],

Add static path to settings

File dashboard/settings.py

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, APP_NAME + '/static')
]

Modify frontend view template

File dashboard/apps/frontend/templates/index.html

{% extends 'site/base.html' %}
{% load static %} {% block title %}Dashboard with Django and Bootstrap{% endblock title %}
{% block nav-style %}mkt-nav{% endblock nav-style %}

{% block content %}
<div class="jumbotron">
	<div class="container"></div>
		FRONTEND
	<div class="container full"></div>
</div>
{% endblock content %}

View current status of project

Add dashboard from an existing template

For a first start, we will use this sb-admin-2 dashboard template from here:

Download required files

Bootstrap templates consist of at least 3 different types of files

  • main index.html page, responsible for collection all elements and set up your page
  • CSS files defining the style of your page
  • JavaScript files, containing needed frameworks and code

So, first start by downloading the sample template from here. Be sure, you start in our project root folder:

cd $DASHBOARD_ROOT
git clone https://github.com/BlackrockDigital/startbootstrap-sb-admin-2 install/sb-admin-2

Next, find out, what we need for our template in addition to the file index.html

cd install/sb-admin-2
grep -E "<(link|script)" index.html 
  <link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
  <link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
  <link href="css/sb-admin-2.min.css" rel="stylesheet">
  <script src="vendor/jquery/jquery.min.js"></script>
  <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
  <script src="vendor/jquery-easing/jquery.easing.min.js"></script>
  <script src="js/sb-admin-2.min.js"></script>
  <script src="vendor/chart.js/Chart.min.js"></script>
  <script src="js/demo/chart-area-demo.js"></script>
  <script src="js/demo/chart-pie-demo.js"></script>

That’s a lot of additional files.

To keep the file structure consistent, these files should be stored under the dash/board/static folder (same as the bootstrap files before).

To achieve this, there are two possibilities:

  • Create desired folder and copy each of the source files to the destination folder
  • Copy the complete static folder from the github repository for this post.

We use the second option to keep the focus on creating our dashboard.

So, first, clone the repository:

cd $DASHBOARD_ROOT/install
git clone https://github.com/rgoestenmeier/Dashboard_Django-Bootstrap

Then, copy the requred files

cd $DASHBOARD_ROOT
cp -R install/Dashboard_Django-Bootstrap/dashboard/static dashboard
cp -R install/Dashboard_Django-Bootstrap/dashboard/templates dashboard

Having everything needed for the dashboard template, the next step is to modify the front-end template

File dashboard/apps/frontend/templates/frontend/index.html

{% extends 'site/sb-admin-2/base.html' %}
{% load static %} {% block title %}Dashboard with Django and Bootstrap{% endblock title %}

{% block content %}
{% endblock content %}

View current project in browser

Perfect. We are done with the basic setup.

Still some work to do, because our dashboard is only a static dashboard. All content is programmed in the dashboard template file dashboard/templates/site/sb-admin-2/base.html

For example, look at the cards with the earnings at the top:

To achieve a more dynamic content, we need to move the desired parts of the dashboard from the template file to the front-end view file.

This will be described in the next step: Part 2: Prepare for dynamic content

Bootstrap | Coobook

Layout and Positioning

From here.

Now that Bootstrap 4 is flexbox by default, vertical alignment gets a little easier. In general, there are 3 different approaches to vertical alignment…

  1. Auto-margins
  2. Flexbox utilities
  3. Display utilities along with the Vertical Align utilities.

At first, the “Vertical Align” utilities would seem an obvious choice, but these only work with inline and table display elements. Consider the following vertical alignment options and scenarios.

In general, there are 2 types of vertical alignment scenarios you’ll encounter…

  1. vertical centering within a parent container.
  2. or, vertical centering relative to adjacent elements.

1. Vertical Center Using Auto Margins

One way to vertically center is to use my-auto. This will center the element within it’s flexbox container (The Bootstrap 4 .row is display:flex). For example, h-100 makes the row full height, and my-auto will vertically center the col-sm-12 column.

<div class="row h-100">
   <div class="col-sm-12 my-auto">
     <div class="card card-block w-25">Card</div>
   </div>
</div>

my-auto represents margins on the vertical y-axis, and is equivalent to:

margin-top: auto;
margin-bottom: auto;

Demo — Vertical Center Using Auto Margins

2. Vertical Center with Flexbox

Since the Bootstrap 4 .row class is now display:flex you can simply use the new align-self-center flexbox utility on any column to vertically center it:

<div class=”row”>
   <div class=”col-6 align-self-center”>
      <div class=”card card-block”>
      Center
      </div>
   </div>
   <div class=”col-6">
      <div class=”card card-inverse card-danger”>
      Taller
      </div>
   </div>
</div>

or, use align-items-center on the entire .row to vertically center align allcol-* (columns) in the row…

<div class=”row align-items-center”>
  <div class=”col-6”>
     <div class=”card card-block”>
     Center
     </div>
  </div>
  <div class=”col-6”>
     <div class=”card card-inverse card-danger”>
     Taller
     </div>
  </div>
</div>

Demo — Vertical Center Different Height Adjacent Columns

3. Vertical Center Using Display Utils

Bootstrap 4 has now has display utils that are used a wrapper for the CSS display propery such asdisplay:blockdisplay:inlinedisplay:table-cell, display:none, etc.. These can be used with the vertical alignment utilsto align inline, inline-block or table cell elements.

<div class="row h-50">
  <div class="col-sm-12 h-100 d-table">
    <div class="card card-block d-table-cell align-middle">
    I am groot
    </div>
  </div>
</div>

Demo — Vertical Center Using Display Utils

More Vertical Center Examples

Bootstrap 4 Vertical Center in Equal Height Cards
Bootstrap 4 Vertical Centering with Cards

Build a Jekyll Template based on Bootstrap 4

TL;DR

Combine two amazing open source tools: Jekyll and Bootstrap. The final template is here.

Bootstrap Template and Jekyll: two powerful tools

Start Point

While i want to learn about and work with bootstrap, i decided to build a Jekyll Template, so that i can build a dynamic website.

Asking Google for first inspiration leads me to this wonderful Blog entry:

Choose a Bootstrap Template

Quite nice. So I decided to use one of the free templates from Start Bootstrap: Modern Business

When i downloaded the template from Github and examine the content, i find out, that for each component (Pricing, Service, Contact), there is a corresponding HTML-file with all the content and all the formatting code:

  • about.html
  • blog-home-1.html
  • blog-home-2.html
  • blog-post.html
  • contact.html
  • faq.html
  • full-width.html
  • index.html
  • portfolio-1-col.html
  • portfolio-2-col.html
  • portfolio-3-col.html
  • portfolio-4-col.html
  • portfolio-item.html
  • pricing.html
  • services.html
  • sidebar.html

The Plan

My plan was to separate the presentation layer (what you will see) from the business layer (what creates the content for the presentation layer).

To achieve this with Jekyll, i convert the Bootstrap pages to Jekyll include pages. The final result should look like this:

The frontpage for the component

The jekyll include file with the component

---
layout: page
title: Services
---
<div class="container">
    <h1 class="mt-4 mb-3">{{ page.title }}</h1>
    {% include component/services.html %}
</div>
{% assign images = site.url |  append:  '/' | append: site.baseurl | append: '/assets/img/services' %}

<h2>Services: {{ site.services.title }}</h2>

<!-- Image Header -->
<img class="img-fluid rounded mb-4" src="{{ images }}/header.jpg" alt="">

<div class="row">
    {% for item in site.services %}
        <div class="col-lg-4 mb-4">
            <div class="card h-100">
                <h4 class="card-header">{{ item.title }}</h4>
                <div class="card-body">
                    <p class="card-text">{{ item.text | markdownify }}</p>
                </div>
                <div class="card-footer">
                    <a href="#" class="btn btn-primary">Learn More</a>
                </div>
            </div>
        </div>
    {% endfor %}
</div>

Next step was to convert every Bootstrap Template Page to a Jekyll Include File

Slider image

About Page

Slider image

FAQ Page

Slider image

Portfolio Page with 1 Column

Slider image

Portfolio Page with 2 Column

Slider image

Services Page

Slider image
Slider image

Pricing Page

The main challenge in separating the presentation from the business layer was: where to place the data to be displayed?

Depending on the type of the component, i choose three different solutions:

  1. Place the data in the corresponding include file of the component
  2. Place the date in the page, which calls the corresponding include file of the component
  3. Place the data in a Jekyll collection file

Data in corresponding include file of the component

I used this approach for components, which are used only once on the website and have a mostly static content, e.g. the FAQ Page

The component page

The frontend page

Date in the page, which calls the corresponding include file of the component

I used this approach for components, which are used more than once on the website, e.g. a Blog Post

The component page

The frontend page

Data in a Jekyll collection file

I used this approach for components, which are used only once on the website, but needs more configuration information, e.g. the Services- or Portfolio Page.

This step needs an additional configuration task: create the Jekyll Collections.

Jekyll collections are a great way to group related content like members of a team or talks at a conference.

To use a Collection you first need to define it in your _config.yml.

#
collections_dir: collections # folder, where collections files are stored
collections:
  services:
    title: "Services"
    output: true # store output files for each item under the collections folder

Then, you have to create the collection files, for each item in your collection one file:

These files look like this:

---
img: 1.jpg
title: Development
subtitle: 
footer: 
text: Lorem ipsum dolor sit amet, consectetur adipisicing elit. Possimus aut mollitia eum ipsum fugiat odio officiis odit.
---

And the data of this files can be accessed in the Jekyll include file with this code fragment:

  • all items of the collection: site.services, {% for item in site.services %}
  • data of each item: {{ item.title }}, {{ item.text | markdownify }}
{% for item in site.services %}
    <div class="col-lg-4 mb-4">
        <div class="card h-100">
            <h4 class="card-header">{{ item.title }}</h4>
            <div class="card-body">
                <p class="card-text">{{ item.text | markdownify }}</p>
            </div>
            <div class="card-footer">
                <a href="#" class="btn btn-primary">Learn More</a>
            </div>
        </div>
    </div>
{% endfor %}

The final result

Bootstrap Template and Jekyll: two powerful tools