Skip to main content

Wagtail DaisyUI Interface Editor

CI — lint & tests

Create reusable DaisyUI themes through Wagtail, apply them to pages, and build navigation menus from the same block components.

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",
    # ...
    "django_htmx",  # htmx integration (feeds/action forms/newsletter)
    # ...
    "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 += [
    # Lets views detect htmx requests (feeds, action forms, newsletter).
    "django_htmx.middleware.HtmxMiddleware",
    # Covers arbitrary colour utilities (bg-[#0080ff] and friends).
    "wagtail_daisIE.middleware.ArbitraryCSSMiddleware",
]

The htmx script itself is opt-out: {% daisie_htmx %} (default on) renders it, and WAGTAIL_DAISIE_HTMX = False disables the integration. See docs/htmx.md.

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-theme value).
  • Set as default / Set as default dark theme — fallbacks.
  • Color scheme — light, dark, or normal.
  • 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 — a ForeignKey(DaisyUITheme) (defaults to the default theme).
  • page_background — background layers that override the theme for this page.
  • body — a StreamField of content blocks.
  • get_daisyui_theme() — returns page_theme or the default theme.
  • Context variables daisyui_theme and daisyui_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 %}
        {% daisie_htmx %}
        {% 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, or vertical.
  • 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.

Reusable blocks

A Reusable block (Design → Reusable blocks) is a saved, admin-designed section of page content. Import it anywhere page content is allowed — pages, forms, menus and feed item cards — with a Reusable block block. It is a live reference: editing the saved entry updates every place that imports it. Entries cannot contain other entries, so references never recurse. Emails have their own Reusable content entries (Emails → Reusable content) holding MJML components. See docs/reusable-blocks.md.

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. Item variants let different instances use alternate designs chosen by a condition (basic operators, or a named rule in WAGTAIL_DAISIE_FEED_CONDITIONS).
  • 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.
  • Action form — one <form> of data-input blocks that posts to a developer-defined action, with an inline/reload/navigate behaviour. Ideal for several independent mini-forms on one page (e.g. a tabbed settings page).
  • 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:

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_htmx %} Simple Script tag for htmx (when WAGTAIL_DAISIE_HTMX is on)
{% 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"},
}

# htmx integration (default on; requires django_htmx + HtmxMiddleware).
WAGTAIL_DAISIE_HTMX = True
WAGTAIL_DAISIE_HTMX_VERSION = 2

# Optional: self-hosted Cally for the calendar block.
WAGTAIL_DAISIE_CALLY_URL = "https://unpkg.com/cally@0.9.2"

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 DaisieFormPage that creates an unapproved BreadSuggestion for review; approving it runs an approval workflow that creates the Bread (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 list of the Bread model 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_on in 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.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wagtail-daisIE 3.4.0
File Size Uploaded
wagtail_daisie-3.4.0.tar.gz 370.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wagtail-daisIE 3.4.0
File Interpreter ABI Platform
wagtail_daisie-3.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 919.4 kB

Release files / wagtail_daisie-3.4.0.tar.gz

Download URL wagtail_daisie-3.4.0.tar.gz
Size 370.1 kB
Tags Source
SHA-256 checksum
How to use checksums
80553a6918564133cef1173ce497e2826764aa05d23808dbe8375740e2c6b068
BLAKE2b-256 checksum
How to use checksums
40d2a61e0b5a5e31f8ccc8f6b4d43c424df2accfa0b9b6e7cef664a1d44de449
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 13, 2026.

Transparency log

Release files / wagtail_daisie-3.4.0-py3-none-any.whl

Download URL wagtail_daisie-3.4.0-py3-none-any.whl
Size 549.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc2e4332e2194d57c88fbd9ea01cb49b1f8eaed7639a092d59723f9e60d8dca4
BLAKE2b-256 checksum
How to use checksums
3cabc7369dd017c4546136074b26e9a380494ec4d92880b526009a448d5b8309
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 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

3.4.0 This release

2 release files

3.3.0

2 release files

3.2.0

2 release files

3.1.1

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.1.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page