Installation

Installing Mod in a Django App

How to install Mod for use in a Django app.

To use Mod with Django, you will want to decide how you want Mod's files loaded in your app. There are two options:

Global

This will load Mod's CSS and JavaScript globally in your app for all pages. If you're not an advanced Django developer, this is the recommended approach.

Global CSS and ESM JavaScript

This will load Mod's CSS globally and set up your JavaScript to load via ESM. This is recommended for advanced Django developers building a production-level app that want to reduce page size.

Using Global CSS and JavaScript

To start installing Mod in your app, first, you'll want to copy the required Mod files into your app. Use the table below to know where files should go:

Paths for Plus Users

If you've purchased a copy of Mod Plus, you will want to use the plus file variants (e.g., mod-light-plus.min.css) below.

Type File in mod(-plus).zip Target path in app What is it?
File mod-light(-plus).min.css static/css/mod-light(-plus).min.css The light-themed version of Mod.
File mod-dark(-plus).min.css static/css/mod-dark(-plus).min.css The dark-themed version of Mod.
File mod(-plus).iife.min.js static/js/mod(-plus).iife.min.js The IIFE version of the JavaScript for Mod's interactive components. Use this if you want to load Mod's JavaScript globally in your app.
File fonts/lucide.woff2 static/fonts/lucide.woff2 The icon font used by Mod, Lucide.
File fonts/mod-brand-icons.woff2 static/fonts/mod-brand-icons.woff2 The icon font for brand logos used by Mod.

Important: the above paths assume that you have a publicly-loaded static folder at static/fonts. Ensure that your urls.py includes a pattern for this so that Mod's icon fonts load properly:

urls.py

from django.urls import re_path
from django.conf import settings

urlpatterns = [
    ...other url patterns...
    re_path(r'^fonts/(?P<path>.*)#39;, serve, {'document_root': settings.BASE_DIR / '<app_name>/static/fonts'}),
]

The above will ensure that the static/fonts folder is properly routed to from your application's root (e.g., http://localhost:8000/fonts/lucide.woff2).

Once you have the files copied over to your app (and if need be, urls.py updated), next, you'll want to update your settings.py file (relative to your project) to include an additional context processor to your TEMPLATES array's OPTIONS object for the theme cookie that you'll use to handle theme toggling (see below):

<project>/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],  # Empty since we're using app templates
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                '<app_name>.context_processors.theme',
            ],
        },
    },
]

Above, you'll want to replace <app_name> with the name of your Django app.

Next, you'll need to add the context processor for the theme cookie to your app:

<app_name>/context_processors.py

def theme(request):
    return {'theme': request.COOKIES.get('theme', 'light')}

With that in place, the last thing to update is your main HTML file at templates/base.html:

templates/base.html

{% load static %}
<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8">
      <title>{% block title %}My Site{% endblock %}</title>

      <!-- Start Mod Includes -->
      <link rel="preconnect" href="https://fonts.googleapis.com">
      <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
      <link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap" rel="stylesheet">

      <link rel="stylesheet" href="{% static 'css/mod-'|add:theme|add:'(-plus).min.css' %}">
      <script src="{% static 'js/mod-plus.iife.min.js' %}"></script>
      <!-- End Mod Includes -->
  </head>
  <body data-mod-theme="{{ theme }}">
      {% block content %}
      {% endblock %}
  </body>
</html>

Above, you'll want to be mindful of placement. Make note of the <!-- Start Mod Includes --> and <!-- End Mod Includes --> comments. All of the required includes for the files we copied over earlier are included here. This also includes the Google Fonts CDN links for Mod's font, Inter (optional and can be replaced with static assets in the app).

For theme switching, we pull in the theme variable that we set up earlier via our context processor and first, using the |add template filter, dynamically switch between our different Mod theme CSS files (we do this by using the filter inside of the URL for our CSS file). Remember, the theme variable stores either the current value of the theme cookie, or, the default light.

On our HTML's <body> tag, we're adding the data attribute data-mod-theme and passing it a reference to the theme variable we defined above.

data-mod-theme is required

Styles in Mod are theme-scoped using the data-mod-theme attribute on your <body> tag. If this is omitted, Mod's components will appear broken/unstyled.

With this, Mod should be loaded globally in your app. For pages with components that require JavaScript, calls to the specific component's JavaScript can be loaded directly in your template's HTML:

templates/test.html

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

{% block title %}Test Page{% endblock %}

{% block content %}
<div class="mod-command-palette">
  <!-- HTML for command palette component -->
</div>

<script>
  document.addEventListener('DOMContentLoaded', () => {
    if (window.mod) {
      window.mod.command_palette();
    }
  });
</script>
{% endblock %}

Above, we add a <script> tag to our template and inside, add an event listener for the DOMContentLoaded event. In that listener's callback, we check to see if the global window.mod value is defined, and if it is, call to the JavaScript method to enable interaction for the component(s) on our page (here, we're using the Mod Plus Command Palette component).

Using Global CSS and ESM JavaScript

To start installing Mod in your app, first, you'll want to copy the required Mod files into your app. Use the table below to know where files should go:

Paths for Plus Users

If you've purchased a copy of Mod Plus, you will want to use the plus file variants (e.g., mod-light-plus.min.css) below.

Type File in mod(-plus).zip Target path in app What is it?
File mod-light(-plus).min.css static/css/mod-light(-plus).min.css The light-themed version of Mod.
File mod-dark(-plus).min.css static/css/mod-dark(-plus).min.css The dark-themed version of Mod.
File mod(-plus).esm.min.js static/js/mod(-plus).esm.min.js The ESM version of the JavaScript for Mod's interactive components. Use this if you want to load Mod's JavaScript incrementally in your app.
File fonts/lucide.woff2 static/fonts/lucide.woff2 The icon font used by Mod, Lucide.
File fonts/mod-brand-icons.woff2 static/fonts/mod-brand-icons.woff2 The icon font for brand logos used by Mod.

Important: the above paths assume that you have a publicly-loaded static folder at static/fonts. Ensure that your urls.py includes a pattern for this so that Mod's icon fonts load properly:

urls.py

from django.urls import re_path
from django.conf import settings

urlpatterns = [
    ...other url patterns...
    re_path(r'^fonts/(?P<path>.*)#39;, serve, {'document_root': settings.BASE_DIR / '<app_name>/static/fonts'}),
]

The above will ensure that the static/fonts folder is properly routed to from your application's root (e.g., http://localhost:8000/fonts/lucide.woff2).

Once you have the files copied over to your app (and if need be, urls.py updated), next, you'll want to update your settings.py file (relative to your project) to include an additional context processor to your TEMPLATES array's OPTIONS object for the theme cookie that you'll use to handle theme toggling (see below):

<project>/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],  # Empty since we're using app templates
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                '<app_name>.context_processors.theme',
            ],
        },
    },
]

Above, you'll want to replace <app_name> with the name of your Django app.

Next, you'll need to add the context processor for the theme cookie to your app:

<app_name>/context_processors.py

def theme(request):
    return {'theme': request.COOKIES.get('theme', 'light')}

With that in place, the last thing to update is your main HTML file at templates/base.html:

templates/base.html

{% load static %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>{% block title %}My Site{% endblock %}</title>

    <!-- Start Mod Includes -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap" rel="stylesheet">

    <link rel="stylesheet" href="{% static 'css/mod-'|add:theme|add:'(-plus).min.css' %}">
    <script type="module" src="{% static 'js/mod-plus.esm.min.js' %}"></script>
    <!-- End Mod Includes -->
</head>
<body data-mod-theme="{{ theme }}">
    {% block content %}
    {% endblock %}
</body>
</html>

Above, you'll want to be mindful of placement. Make note of the <!-- Start Mod Includes --> and <!-- End Mod Includes --> comments. All of the required includes for the files we copied over earlier are included here. This also includes the Google Fonts CDN links for Mod's font, Inter (optional and can be replaced with static assets in the app).

To load Mod's JavaScript, we add the type attribute to our <script> tag and set it to module so that the browser knows we're loading an ESM module.

For theme switching, we pull in the theme variable that we set up earlier via our context processor and first, using the |add template filter, dynamically switch between our different Mod theme CSS files (we do this by using the filter inside of the URL for our CSS file). Remember, the theme variable stores either the current value of the theme cookie, or, the default light.

On our HTML's <body> tag, we're adding the data attribute data-mod-theme and passing it a reference to the theme variable we defined above.

data-mod-theme is required

Styles in Mod are theme-scoped using the data-mod-theme attribute on your <body> tag. If this is omitted, Mod's components will appear broken/unstyled.

With this, Mod should be loaded globally in your app. For pages with components that require JavaScript, calls to the specific component's JavaScript can be loaded directly in your template's HTML:

templates/test.html

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

{% block title %}Test Page{% endblock %}

{% block content %}
<div class="mod-command-palette">
  <!-- HTML for command palette component -->
</div>

<script type="module" src="{% static 'js/test.js' %}"></script>
{% endblock %}

Above, we add a <script> tag to our template, importing another JavaScript file where our template's JavaScript will be called:

static/js/test.js

import { command_palette } from '/static/js/mod(-plus).esm.min.js';

command_palette();

Above, we import the JavaScript method for the Mod Plus command palette component and call it to enable the component's interactivity.