Skip to content

fix(daisy): ship both tags in daisyUI's documented CDN recipe - #58

Merged
fsecada01 merged 1 commit into
masterfrom
fix/component-framework-ui-56-daisy-cdn-utilities
Aug 1, 2026
Merged

fix(daisy): ship both tags in daisyUI's documented CDN recipe#58
fsecada01 merged 1 commit into
masterfrom
fix/component-framework-ui-56-daisy-cdn-utilities

Conversation

@fsecada01

Copy link
Copy Markdown
Owner

Summary

Fixes #56.

cf_ui_head(theme="daisy") emitted only daisyUI's full.min.css link — the component layer only, since daisyUI is a Tailwind plugin.

The shipped daisy templates depend heavily on Tailwind utilities (flex, w-full, gap-4, items-center, text-lg, …) that bundle does not contain.

A consumer following the quickstart with CF_UI_THEME = "daisy" got styled buttons and cards sitting in a completely inert layout, with no error anywhere.

daisyUI's own CDN documentation (https://v4.daisyui.com/docs/cdn/) prescribes two tags, in order: the stylesheet link, then <script src="https://cdn.tailwindcss.com"></script> (Tailwind's Play CDN). cf-ui was shipping only the first.

Acceptance criteria (issue #56), item by item

  • "A consumer setting CF_UI_THEME = "daisy" with no Tailwind build either gets working styling or gets an unmissable, actionable signal — never a silently half-styled page."
    Chose Option 1 from the issue (a bundle that includes utilities), via CF_UI_DAISY_CDN = "play" (default): cf_ui_head now emits the daisyUI stylesheet and the Tailwind Play CDN script, so a default install gets working layout. The emitted HTML also carries an explanatory comment, so the CDN mode is a visible, greppable signal in view-source even for a consumer who never opens the docs.

  • "A test pins whichever behaviour is chosen, so a future CDN version bump cannot quietly reintroduce the gap."
    tests/unit/test_asset_tags.py gained: play-mode emits the script; the other four themes emit no <script> from cf_ui_head at all; off-mode emits neither daisy tag but still emits cf_ui_axes.css / [x-cloak]; play-mode carries the explanatory comment; Jinja-macro/Django-tag parity for the script tag in both modes; an invalid CF_UI_DAISY_CDN raises ImproperlyConfigured at startup naming the valid values; and a named regression tripwire (test_regression_daisy_stylesheet_alone_is_not_a_complete_head_issue_56) that fails if a future refactor drops the script while keeping the stylesheet.

  • "docs/daisyui.md and the quickstart state the daisy CDN caveat explicitly."
    docs/daisyui.md gained a new section ("The CDN path needs two tags, not one") covering the two-tag recipe, CF_UI_DAISY_CDN, when to use each value, and the upstream "Play CDN is for development purposes only, and is not intended for production" label. docs/installation.md's DaisyUI warning callout and docs/getting-started.md (§1 theme table intro and §3 Assets) no longer imply all five themes are CDN-uniform.

  • "The real-Tailwind-build path is unchanged and still documented as the production answer."
    CF_UI_DAISY_CDN = "off" is exactly that path: cf_ui_head emits neither daisy tag, so a real Tailwind build supplies both layers itself. Nothing about the Tailwind content-glob guidance changed.

Why "play" is the default, not "off"

The one outcome the issue forbids is a silently half-styled page. A default of "off" does not avoid that failure, it just relocates it: anyone who sets CF_UI_THEME = "daisy", follows the quickstart, and has not yet read docs/daisyui.md gets the same unstyled layout with the same absence of an error.

"play" gives that same consumer a working page immediately, plus an unmissable, greppable signal in view-source (the HTML comment) pointing at the real answer.

A consumer running a real Tailwind build is exactly the consumer who has read the docs and can flip CF_UI_DAISY_CDN to "off" deliberately.

Design notes

  • CF_UI_DAISY_CDN is resolved by a new themes.resolve_daisy_cdn, right next to resolve_theme, using the same ThemeError and message idiom (unknown daisy CDN mode {value!r} — valid values are: play, off).
  • Django's CfUiConfig.ready() validates it at startup with the same f"cf-ui: {exc}. Check {SETTING} in settings." shape already used for CF_UI_THEME / CF_UI_COMPOSITION.
  • The Jinja cf_ui_head macro gains a matching daisy_cdn="play" keyword argument. Vendor order is preserved: comment, then stylesheet <link>, then Play CDN <script>.
  • The other four themes (bulma, bootstrap, foundation, fomantic) are untouched — the branch only affects the theme == "daisy" path.

The E2E workaround, deleted as proof

tests/integration/jinja_app/main.py used to hand-build its <head> from a _THEME_CSS URL table plus a _THEME_EXTRA_HEAD["daisy"] entry that injected the exact Tailwind Play CDN script cf_ui_head was supposed to emit but didn't.

That meant the E2E daisy tier was exercising a workaround, never the shipped tag.

Both dicts are deleted. The gallery route's <head> is now built by calling the real cf_ui_head Jinja macro from cf_ui/assets.jinja, so the E2E daisy suite (tests/e2e/test_daisy.py) now proves the shipped tag directly. Ran green after the change (pytest tests/e2e --browser chromium -k daisy → 44 passed, 5 skipped; full E2E suite → 230 passed, 33 skipped).

Finding, not forced: there was no django-cotton-side equivalent to delete. The cotton E2E gallery templates (tests/integration/cotton_app/templates/cotton_gallery/*.html) never call cf_ui_head and never load a stylesheet or script at all — they render bare components and the cotton daisy E2E tests only assert markup/classes, never computed layout. That tier was never exposed to this bug and needed no change.

Gate results

python -m pytest tests/unit tests/integration -q   → 2158 passed, 13 skipped
ruff check src tests                                → All checks passed
ruff format --check src tests                       → 77 files already formatted
mkdocs build --strict                               → Documentation built, no warnings
prek run --all-files                                → all hooks passed
pytest tests/e2e --browser chromium -q               → 230 passed, 33 skipped

assets.jinja (src/cf_ui/templates/cf_ui/assets.jinja) is confirmed out of djLint's scope: .pre-commit-config.yaml scopes the djLint hooks to templates/cotton/** and templates/jinja/** only, and prek run --all-files left it untouched.

Test plan

  • tests/unit/test_asset_tags.py — new/extended tests confirmed red before implementation, green after
  • pytest tests/unit tests/integration -q
  • ruff check / ruff format --check
  • mkdocs build --strict
  • prek run --all-files
  • pytest tests/e2e --browser chromium -q (full suite + daisy-only)

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

https://claude.ai/code/session_01NhqNRBg83czKfr8L6FF5xf

cf_ui_head(theme="daisy") emitted only the daisyUI stylesheet link.
daisyUI is a Tailwind plugin, so that bundle is the component layer
only (.btn{, .card{) with none of the utility classes (.flex{,
.w-full{, .gap-4{, .items-center{) the shipped daisy templates depend
on for layout. A consumer following the quickstart got styled buttons
and cards sitting in a completely inert layout, with no error and
nothing in the docs at the point of failure.

daisyUI's own CDN documentation (https://v4.daisyui.com/docs/cdn/)
pairs the stylesheet with Tailwind's Play CDN script, in that order.
cf-ui was shipping only the first tag; the E2E integration harness
(tests/integration/jinja_app/main.py) was hand-patching the second one
in via a _THEME_EXTRA_HEAD dict, which meant that tier never actually
exercised the shipped cf_ui_head tag.

Adds CF_UI_DAISY_CDN ("play" default, "off"), resolved by
themes.resolve_daisy_cdn and validated at Django startup the same way
CF_UI_THEME and CF_UI_COMPOSITION already are. "play" emits an
explanatory HTML comment, the daisyUI stylesheet, then the Tailwind
Play CDN script, in the vendor's order. "off" emits neither tag, for a
consumer running a real Tailwind build. The other four themes are
byte-identical to before. Jinja's cf_ui_head macro gains the matching
daisy_cdn="play" keyword argument.

"play" is the default rather than "off" because the failure this
fixes is a silently half-styled page, and defaulting to "off" just
relocates that same silent failure onto anyone who has not yet read
docs/daisyui.md. "play" instead gives that consumer a working page
plus a greppable, in-view-source signal pointing at the real answer.

Deletes the jinja_app integration harness's _THEME_EXTRA_HEAD
hand-patch and routes its gallery <head> through the real
cf_ui_head Jinja macro instead of a hand-maintained CDN URL table, so
the E2E daisy suite now proves the shipped tag rather than a
workaround for its absence. No django-cotton equivalent existed to
delete: the cotton E2E gallery templates never call cf_ui_head or load
any stylesheet at all, so that tier was never exposed to this bug.

Extends the Django-tag/Jinja-macro parity test in
tests/unit/test_asset_tags.py to cover the new script tag in both CDN
modes, and adds a regression tripwire pinning that the daisy
stylesheet alone is not sufficient.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NhqNRBg83czKfr8L6FF5xf
@fsecada01 fsecada01 self-assigned this Aug 1, 2026
@fsecada01 fsecada01 added the bug Something isn't working label Aug 1, 2026
@fsecada01
fsecada01 merged commit dc7f291 into master Aug 1, 2026
7 checks passed
@fsecada01
fsecada01 deleted the fix/component-framework-ui-56-daisy-cdn-utilities branch August 1, 2026 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

daisy CDN default ships components without Tailwind utilities — silently half-styled pages

1 participant