Wagtail DaisyUI Interface Editor
Create reusable DaisyUI themes through Wagtail, apply them to pages, and build navigation menus from the same block components.
Links
- Documentation
- Developer docs · CSS pipeline
- Emails · Context models · Error pages · Forms · Approval workflows · Data components · Notifications · django-allauth · Allauth pages
- Changelog
- Contributing
Supported versions
This package supports Wagtail 7.3 and up, and all compatible versions of Python and Django.
Installation
uv add wagtail-daisIE
poetry add wagtail-daisIE
pip install wagtail-daisIE
Install every optional feature with extras:
uv add "wagtail-daisIE[notifications,allauth,allauth_emails,blocks]"
# or: pip install "wagtail-daisIE[notifications,allauth,allauth_emails,blocks]"
1. Add to INSTALLED_APPS
The core apps are required; notifications, allauth_ui and allauth_emails
are opt-in and need their extras (see below).
# myproject/settings.py
INSTALLED_APPS = [
# Required.
"wagtail_daisIE",
"wagtail_daisIE.assets",
"wagtail_daisIE.menus",
"wagtail_daisIE.feeds",
"wagtail_daisIE.errors",
# Optional (each requires its extra).
"wagtail_daisIE.notifications",
"wagtail_daisIE.allauth_ui",
"wagtail_daisIE.allauth_emails",
# ...
"wagtail",
# ...
"wagtail.contrib.table_block",
# ...
"colorfield",
]
Extras: notifications (emails, audiences, campaigns), allauth
(django-allauth pages/emails) and blocks (draftail-text-utils).
allauth_emails pulls in both notifications and allauth.
2. Run migrations
python manage.py migrate
Blocks are serialised in migrations by a stable registry key rather than a
frozen block tree, which keeps migrations small and makemigrations fast.
Runtime behaviour (the editor, revisions, previews, fixtures) is unchanged. If
you add blocks to a page model, regenerate migrations as usual
(python manage.py makemigrations <your_apps>). See
docs/migrations.md for the key/rename contract, the
upgrade procedure and data guarantees.
3. Serve the DaisyUI stylesheet
The package ships a committed, precompiled Tailwind + daisyUI stylesheet; no Node.js or CLI is required in production.
# settings.py
MIDDLEWARE += [
# Covers arbitrary colour utilities (bg-[#0080ff] and friends).
"wagtail_daisIE.middleware.ArbitraryCSSMiddleware",
]
Add the render-blocking link to your base template's <head>:
{% load wagtail_daisIE_tags %}
{% daisyui_styles %}
Regenerate wagtail_daisIE/static/wagtail_daisIE/css/daisie.css after changing
any class choices:
just build-css # regenerates the safelist, then runs the Tailwind CLI
See docs/assets.md for the full pipeline and production notes.
Quick start
Create a theme
In the Wagtail admin, open Design → Themes and create a theme. Configure:
- Name — a unique identifier (used as the
data-themevalue). - Set as default / Set as default dark theme — fallbacks.
- Color scheme —
light,dark, ornormal. - Colors — primary, secondary, accent, neutral, base surfaces, semantic colors.
- Border radii, Sizes, Effects — DaisyUI design tokens.
- Background — solid, gradient, or image layers.
- Fonts — font families by role (
heading,body,subheading,code, or custom) with fallbacks, base font size and line height. - Font CDNs — stylesheet links for webfonts (e.g. Google Fonts).
Use the page mixin
StyledPageMixin adds a theme, per-page background layers, and a content body
StreamField to any Wagtail page:
from wagtail_daisIE.pages import StyledPageMixin
class MyPage(StyledPageMixin):
content_panels = (
StyledPageMixin.content_panels
+ [
# Add any custom panels here
]
)
This mixin adds:
page_theme— aForeignKey(DaisyUITheme)(defaults to the default theme).page_background— background layers that override the theme for this page.body— aStreamFieldof content blocks.get_daisyui_theme()— returnspage_themeor the default theme.- Context variables
daisyui_themeanddaisyui_page_background_css.
Render the theme in your templates
{% load wagtailcore_tags wagtail_daisIE_tags %}
<!DOCTYPE html>
<html{% if daisyui_theme %} data-theme="{{ daisyui_theme.name }}"{% endif %}>
<head>
{% daisyui_theme_full_css daisyui_theme %}
{% daisyui_theme_script %}
{% daisyui_icon_assets %}
</head>
<body{% if daisyui_page_background_css %} style="background: {{ daisyui_page_background_css }}"{% endif %}>
{% daisie_messages %}
{% block content %}{% endblock %}
</body>
</html>
The Tailwind/DaisyUI stylesheet is a committed static file; add
{% daisyui_styles %} to <head> (render-blocking, no FOUC).
{% daisyui_theme_full_css theme %} emits the
theme's color, radius, size, effect, background, font, and font-CDN CSS, and
{% daisyui_theme_script %} persists the visitor's theme choice.
{% daisie_messages %} renders action confirmations as DaisyUI alerts.
Menus
DaisyUIMenu is a reusable snippet rendered with the {% daisyui_menu %} tag.
Menus reuse the same blocks as page bodies, so content components behave
identically in both contexts.
{% load wagtail_daisIE_tags %}
{% daisyui_menu "Main navigation" %}
{% daisyui_menu "Footer" css_class="bg-base-200" %}
Each menu has:
- Layout —
navbar,footer,sidebar,horizontal, orvertical. - Branding — a logo and/or wordmark, optionally wrapped in one destination link (page, URL, document, email, or phone).
- Search — an optional search box with configurable URL, parameter, and placeholder.
- Theme — a
menu_theme(falls back to the default theme) plus an optional light/dark toggle. - Item defaults (
item_design) — default typography, background, spacing, size, border and box styles applied to every item. Each item's own settings are appended on top, so per-block design still wins. - Menu items — links, buttons, search boxes, inline cards, accordions, link lists, headers, text, and newsletters.
Blocks and design
Design primitives live in wagtail_daisIE.base_blocks and are composed into the
public blocks in wagtail_daisIE.blocks. Every themed block resolves a
block_css class string from its design settings, merging any inherited
block_css from its parent context. This is what lets menu-level item_design
defaults cascade into items without any menu-specific block code.
Any block with a typography group (a TypographyBlock) exposes a Font
family picker populated from the current theme's font-family roles. The stored
value is the role (or custom name) and renders as font-<role>.
Custom blocks should subclass DaisieStructBlock/DaisieStreamBlock (the
Themed* bases already do) so they are serialised compactly; see
docs/extending.md and docs/migrations.md.
Audience restrictions
Content blocks expose an audience field so editors can restrict content.
Audiences are declared in settings and evaluated at render time.
# mysite/settings/base.py
WAGTAIL_DAISIE_AUDIENCE_RULES = {
"adults": {"label": "Adults", "rule": "home.audience.is_adult"},
"verified": {"label": "Verified users", "rule": "home.audience.is_verified"},
}
# mysite/home/audience.py
def is_adult(request):
user = getattr(request, "user", None)
return bool(user and user.is_authenticated and getattr(user, "age", 0) >= 18)
Each rule is a dotted path to a callable invoked as rule(request) returning a
boolean. Selecting multiple audiences is a logical OR. If
WAGTAIL_DAISIE_AUDIENCE_RULES is empty, the audience field is hidden.
For campaigns (which have no request) a rule may also declare a queryset
returning a User queryset — see docs/notifications.md.
Context models and dynamic content
Expose project models so authors can reference them from any block:
WAGTAIL_DAISIE_CONTEXT_MODELS = {
"user": {"label": "Current user", "model": "users.User", "source": "request.user"},
"meeting": {
"label": "Meeting",
"model": "meetings.Meeting",
"source": "url",
"lookup_field": "slug",
"url_source": "get_absolute_url",
},
}
Then use {{ user.first_name }}, {{ meeting.url }} in text, rich text,
Image blocks (dynamic source) and link destinations. Pages can pin a
value to a specific instance or resolve it from the URL. Full guide:
docs/context-models.md.
Data components
Render project data and trigger actions with reusable components:
- Feeds — a snippet (Design → Feeds) that lists a context model with admin-designed item cards, typed filters (choice/multi, boolean, date, date range, price range, search), AJAX filtering and optional infinite scroll.
- Action — a struct of a themed button and an optional confirmation alert
that posts to a developer-defined action (
WAGTAIL_DAISIE_ACTIONS), e.g. Add to basket. Inside a card it can make the whole card clickable. - Calendar — a Cally date picker showing each day's events as designed cards.
WAGTAIL_DAISIE_ACTIONS = {
"basket.add": {
"label": "Add to basket",
"handler": "myapp.actions.add_to_basket",
},
}
# urls.py
(path("daisie/", include("wagtail_daisIE.dynamic.urls")),)
Full guide: docs/data-components.md.
Emails and notifications
Build responsive MJML emails from the same design primitives and edit them in
Wagtail, with {{ payload.* }} placeholders and an admin help panel listing
what's available:
- Emails: docs/emails.md
- Bridges, audiences and scheduled campaigns: docs/notifications.md
- django-allauth pages, forms and email overrides: docs/allauth.md
WAGTAIL_DAISIE_NOTIFICATION_BRIDGES = {
"booking_requested": {
"label": "Booking requested",
"template": "Booking requested",
"signal": "myapp.signals.booking_requested",
"sender": "myapp.models.MeetingRequest",
},
}
Form pages
DaisieFormPage renders DaisyUI forms, links each input to a model field,
styles inputs and labels individually, and can create a configured model
instance from a submission (with an optional approval flag, success/error
bodies and a success redirect). See docs/forms.md.
Error pages
ErrorPage snippets provide themed bodies for supported HTTP statuses. Wire
Django's handlers in the root URL configuration; audience-gated pages can also
render the 403 snippet directly. See
docs/error-pages.md.
Icons
Icons are stored as "<prefix>:<name>" (e.g. mdi:home) or as raw CSS classes
(e.g. fa-solid fa-home). Four providers ship with the package:
| Provider | Prefix example | Notes |
|---|---|---|
wagtail |
wagtail:home |
Built-in Wagtail admin icons, rendered inline. |
iconify |
mdi:home |
On-demand icons from Iconify (cached server-side). |
font |
fa6-solid:house |
Any webfont rendered via CSS classes. |
custom |
brand:mark |
A project-supplied IconifyJSON or name-to-SVG manifest. |
Icon sources are managed under Design → Icon Sources. Render an icon with
{% daisyui_icon value %} and include provider assets with
{% daisyui_icon_assets %} (or the icon_assets context processor).
from wagtail import hooks
@hooks.register("register_icon_providers")
def register_icon_providers(providers):
return providers + [MyIconProvider()]
Template tags
| Tag | Type | Output |
|---|---|---|
{% daisyui_theme_css theme %} |
Inclusion | Inline <style> with color/radius/size/effect variables |
{% daisyui_theme_inline_css theme %} |
Simple | Raw theme CSS string |
{% daisyui_theme_background_css theme %} |
Inclusion | Inline <style> for background layers |
{% daisyui_theme_background_inline_css theme %} |
Simple | Raw background CSS string |
{% daisyui_theme_font_css theme %} |
Inclusion | Inline <style> with --font-* variables |
{% daisyui_theme_font_cdns theme %} |
Inclusion | <link> tags for font CDNs |
{% daisyui_theme_full_css theme %} |
Inclusion | Font CDNs + colors + background + fonts |
{% daisyui_theme_full_inline_css theme %} |
Simple | Raw combined CSS string |
{% daisyui_theme_script %} |
Simple | Script tag that persists the theme choice |
{% daisie_messages %} |
Inclusion | Renders Django messages as DaisyUI alerts |
{% daisyui_menu "Name" %} |
Inclusion | Renders a DaisyUIMenu snippet |
{% daisyui_icon value %} |
Simple | Renders a stored icon value |
{% daisyui_email_icon value %} |
Simple | Rendering-safe data-URI icon image for email |
{% daisyui_icon_assets %} |
Inclusion | Provider scripts/styles for <head> |
{{ item|is_active:request }} |
Filter | Whether a menu item points at the current path |
Settings
Styling needs only {% daisyui_styles %} plus the default
ArbitraryCSSMiddleware (see Installation).
Everything else is optional:
# settings.py
WAGTAIL_DAISIE_ICONS = {
"iconify": {
"api": "https://api.iconify.design", # or a self-hosted API
"mode": "cached-svg", # or "component"
"collections": ["mdi", "fa6-solid", "lucide"],
"timeout": 3,
},
"cache_timeout": 604800,
}
WAGTAIL_DAISIE_AUDIENCE_RULES = {
"adults": {"label": "Adults", "rule": "home.audience.is_adult"},
}
WAGTAIL_DAISIE_CONTEXT_MODELS = {
"user": {"label": "Current user", "model": "users.User", "source": "request.user"},
}
WAGTAIL_DAISIE_NOTIFICATION_BRIDGES = {
"booking_requested": {
"label": "Booking requested",
"template": "Booking requested",
"signal": "myapp.signals.booking_requested",
"sender": "myapp.models.MeetingRequest",
},
}
# Requires the [allauth] extra.
WAGTAIL_DAISIE_ALLAUTH_UI = True
WAGTAIL_DAISIE_ACTIONS = {
"basket.add": {"label": "Add to basket", "handler": "myapp.actions.add"},
}
# Optional: self-hosted Cally for the calendar block.
WAGTAIL_DAISIE_CALLY_URL = "https://unpkg.com/cally"
Demo
The demo/ project is a DaisyUI-styled Wagtail site. Run it with just demo
(migrate, load data, collect static, runserver) or load only the data with
just load_initial_data.
The loader reads demo/fixtures/content.json and
demo/fixtures/media/original_images/, then seeds related snippets
programmatically, including themes, menus, 403/404/500 error pages, email
templates, notification audiences and feeds. It is idempotent (use --force to
recreate content). See
demo/fixtures/README.md for the fixture schema.
The demo wires the 403, 404 and 500 handlers. Since just demo enables
DEBUG=True, check the designed 404 and 500 responses with non-debug settings;
the audience-gated 403 renders directly in either mode.
The demo also showcases the notification, context-model, form and allauth features:
- Context and components page — context models plus feedback/data-input blocks.
- Members only page — audience-gated with a designed 403 error page.
- Suggest a bread — a
DaisieFormPagethat creates an unapprovedBreadSuggestionfor review; approving it runs an approval workflow that creates theBread(and its generated detail page). - Newsletter in the footer — posts to the Daisie subscribe endpoint and populates the Newsletter audience.
- Breads and basket — a
Model listof theBreadmodel with an Add to basket action button, a session basket list, and a Clear basket action (a cart parallel). - Bread calendar — the breads grouped by
added_onin a Cally calendar. - Blog feed — the blog index renders a filterable feed (tag/author/date) of live posts with AJAX pagination.
- Blog post published — a notification bridge emailing that audience.
- Account pages at
/accounts/— DaisyUI allauth pages and emails, with sample Account confirmation and Password reset email templates. - Admin user
admin/changeme.
Draftail rich text (draftail_text_utils)
The blocks extra integrates
draftail-text-utils so the
rich-text editor's colour palette, font family, font size and alignment are
driven by the active DaisyUI theme: colours via
wagtail_daisIE.utils.get_draftail_color_palette and fonts/URLs via the
demo.draftail_palette bridge.
Install the extra with uv sync --dev --extra blocks (the dev environment
already includes it).
Development
See CONTRIBUTING.md and docs/.
just install # Install Python and Node.js dependencies
just demo # Run the demo site
just test # Run tests
just lint # Run all linters
License
wagtail-daisIE is licensed under the BSD 3-Clause License.
Metadata
Release files for wagtail-daisIE 3.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wagtail_daisie-3.0.0.tar.gz | 330.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wagtail_daisie-3.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 845.1 kB
Release files / wagtail_daisie-3.0.0.tar.gz
| Download URL | wagtail_daisie-3.0.0.tar.gz |
|---|---|
| Size | 330.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b4aa72164cca198b8be80372735ffb2d483cd8e606f2a598cb135645e2cc08ff
|
|
BLAKE2b-256 checksum How to use checksums |
7562cdc5172a3d2c5d0e19438282ee9724ed0ad808bad8d370e2d116ee88be5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency logRelease files / wagtail_daisie-3.0.0-py3-none-any.whl
| Download URL | wagtail_daisie-3.0.0-py3-none-any.whl |
|---|---|
| Size | 514.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8e94a463ff93d54deb0b4e6d712453762da2c8279e6d8c36d5b1b10efa52b146
|
|
BLAKE2b-256 checksum How to use checksums |
aca240ef9b7523ce12c6883fe4af932bca79179cd8a5a96943f25452028fb234
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency log