Cotton UI needs:
Install via pip:
pip install django-cotton-ui
This will also install django-cotton as a dependency.
Add both django_cotton and django_cotton_ui to your INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"django_cotton",
"django_cotton_ui",
# ...
]
Order matters: django_cotton must come before django_cotton_ui.
Link the kit's precompiled stylesheet and the Alpine bundle in your base template, then brand it by overriding the design tokens right in the <head>:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Cotton UI: precompiled components + design tokens.
Shipped inside the installed package, served by Django's staticfiles app. -->
<link rel="stylesheet" href="{% static 'django_cotton_ui/cotton-ui.css' %}">
<!-- Brand it: override the kit's tokens. Place this after the stylesheet link. -->
<style>
/* default / light mode */
:root {
--color-accent: var(--color-teal-500);
--color-accent-content: var(--color-teal-600);
}
/* dark mode */
.dark {
--color-accent: var(--color-teal-400);
--color-accent-content: var(--color-teal-300);
}
</style>
</head>
<body>
{{ slot }}
<!-- Cotton UI bundle first: it registers components on Alpine's alpine:init event -->
<script defer src="{% static 'django_cotton_ui/cotton-ui.min.js' %}"></script>
<!-- Alpine.js plugins, then Alpine core (deferred scripts run in order) -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/focus@3.x.x/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</body>
</html>
Both UI kit assets serve from the installed package. With django.contrib.staticfiles enabled, {% static %} resolves them in development; run collectstatic for production. Nothing to copy or build.
Some components need Alpine.js plugins, loaded before Alpine core:
@alpinejs/collapse - expand/collapse in the accordion, collapse, navbar mobile menu and navlist groups@alpinejs/focus - focus trapping and keyboard navigation in dialogs, drawers, dropdowns, menus, select, popover and the calendarRunning your own Tailwind? You can't just @source the installed package (it lives in a gitignored .venv, which Tailwind skips). Instead, run the bundled command to write a file your build can scan:
python manage.py cotton_ui_sources
This writes cotton-ui.sources.css next to your project: the kit's design tokens, dark variant and the full class list as @source inline(...). @import it into your app.css and your own build generates the kit's utilities, correctly, no preflight clash, dark mode included:
@import "tailwindcss";
/* kit tokens + dark variant + class list, from `manage.py cotton_ui_sources`.
Your build emits the kit's utilities (it resolves through .gitignore). */
@import "./cotton-ui.sources.css";
/* your own @source rules for your own templates (standard Tailwind, nothing kit-specific) */
@source "./your_app/templates/**/*.html";
/* brand it: override kit tokens with :root / .dark, NOT @theme
(@theme loses to the kit's baked-in value; use @theme only for your own new tokens) */
:root { --color-accent: var(--color-teal-500); --color-accent-content: var(--color-teal-600); }
.dark { --color-accent: var(--color-teal-400); --color-accent-content: var(--color-teal-300); }
The class list only changes when you upgrade the kit. Rather than remember to re-run it, wire it into your build as a pre-step, the command is fast and deterministic, so running it every build keeps the list in sync automatically:
{
"scripts": {
"prebuild": "python manage.py cotton_ui_sources",
"build": "tailwindcss -i app.css -o static/app.css --minify"
}
}
No node build? Chain it the same way in a Makefile or CI step: python manage.py cotton_ui_sources && ...build.... With django-tailwind-cli or django-tailwind, run cotton_ui_sources just before their build command. You don't <link> the kit's CSS on this path, your build already contains everything.
A neutral zinc accent ships by default. Brand it by overriding the accent tokens, in the inline <style> from the base template above (no build) or your app.css after the kit @import (custom build), with a .dark block for dark mode. The Theming guide covers the full token set, accent presets and gray palettes.
Drop a couple of components into a template. They should render styled, with the primary button in your accent colour:
<c-ui.button variant="primary">Primary Button</c-ui.button>
<c-ui.button variant="default">Default Button</c-ui.button>
{% cotton ui.button variant="primary" %}Primary Button{% endcotton %}
{% cotton ui.button variant="default" %}Default Button{% endcotton %}