記事一覧

Flower Power

Why daisyUI Is Great for Modern Projects

2026年8月5日

#Artificial Intelligence#Software Engineering#daisyui#Web Development#ai agents
Flower Power

Before writing this article, I audited every repository where my teams and I have shipped daisyUI. The count came to eight projects across five organizations: two marketing sites, a WordPress theme, three internal tools, a client marketing site, and a dense B2B compliance platform. Together they hold roughly 138,000 lines of UI code and 7,762 uses of daisyUI classes. Every number in this article was measured from those repos with git and grep, and I will flag the places where my own data undercuts my pitch.

I also put questions to Pouya Saadeghi, the benevolent creator of daisyUI, while writing this. It's really cool to be able to talk with the founder of an open-source project with 41,000 GitHub stars — one of the most-used component libraries in the Tailwind world — and he was generous with his time and direct with his answers. They show up throughout, including one place where he corrected a premise I had wrong.

What daisyUI is, and the two kinds of component library

daisyUI is a plugin for Tailwind CSS. Tailwind gives you low-level utility classes — padding, color, border radius — and you compose every component from scratch out of them. daisyUI adds a layer of named components on top: btn, card, badge, modal, with semantic modifiers like btn-primary and btn-sm. Color and typography come from a theme you define once, in one file, as design tokens. A button is btn btn-primary everywhere, and what "primary" means lives in exactly one place.

Underneath the components sits a real design system, and its color model is worth understanding because it is where most of the leverage lives. Raw Tailwind colors are constants: bg-green-500 is that green, forever, on every theme. daisyUI replaces them with about twenty semantic roles. The brand colors are primary, secondary, and accent; neutral covers the unsaturated parts of the UI; info, success, warning, and error handle status. Surfaces get a three-layer scale — base-100 is the page background, base-200 one shade deeper, base-300 deeper still — which is how you express elevation and grouping without inventing any new colors. And every role is paired with a -content counterpart (primary-content, base-content) that guarantees readable foreground on that background: btn-primary sets the background, border, and text color together, so contrast is the system's job rather than each author's.

Semantic color roles are not a daisyUI invention — they are a well-known design-token practice, and that is part of their value: the pattern is proven and widely understood. What the markup encodes is intent, never a literal color, and that is what makes whole-theme switching trivial. Because every role is a CSS variable, a theme is just a short list of variable values, and swapping the active theme reskins the entire application with zero markup changes: light to dark, a high-contrast variant, even a compressed high-density theme for power users. That is also why dark mode on my projects is configuration rather than code, why a theme fits in a screenful of CSS, and why muted text is an opacity modifier (text-base-content/50) instead of yet another color decision.

It helps to be precise about what kind of thing daisyUI is architecturally, too, because "component library" covers two different designs.

A JavaScript component library — MUI, Chakra, Ant Design, and the React components you get from shadcn/ui — ships components as code in a specific framework. A button is <Button variant="primary">: a React (or Vue, or Svelte) component carrying its own markup, behavior, and often state. You import it, your bundle includes it, and it works only inside that framework. The copy-paste variant of this model vendors the component source into your repo, where your team owns and edits it from then on.

A CSS component library, which is what daisyUI is, ships no JavaScript at all. A component is a class name and the styles behind it. btn btn-primary works on a <button> in React, in Vue, in a PHP template, in a static HTML page — anywhere there is markup. There is no runtime to bundle, no framework coupling, and nothing vendored into your repo to maintain.

I argued the other side of this with Pouya, a year and a half ago. I had written the PR that added daisyUI support to RJSF, the React JSON Schema Form library, and in February 2025 I asked him why he didn't ship official React components — I thought a higher-level abstraction would launch the project even further. He pushed back on the premise: "I don't think <Button color=\"primary\"> has any better DX than <button class=\"btn btn-primary\">" — and "Most daisyUI components have short, friendly class names anyway." The cost of JS components, he said, is one people don't see: "When we have a Button.jsx and it includes all the possible variants for a button, Tailwind CSS generates styles for all of them in production."

I kept pressing — surely the benefit is coupling logic and markup in one reusable place? His answer drew a line I have used ever since: "Of course JS components are needed, but not for a single HTML tag. For example I wouldn't put a <button> tag in a <Button> component just to render the same <button> tag again. That's not functionality. Most component libraries who do this, are just adding a color or other visual details but not a functionality. Of course when it's about functionality, it needs to be a component. I would put 2 inputs and a button and a checkbox in a component and call it <Login/>. That's the best way."

He didn't fully convince me at the time; I thought my form-driven use case needed the React abstraction. Eight projects later, my own repos have settled the argument in his favor. Styling lives in class names; components exist where there is actual behavior to encapsulate. Both things stay small.

And for the cases where you do need real JS behavior — dropdowns with keyboard navigation, accessible dialogs, comboboxes — there is a sanctioned answer that keeps the separation intact: pair daisyUI with a headless component library. Pouya wrote this up himself: Headless UI "gives you functionality without design decisions," daisyUI "provides design decisions without functionality," and, in his words, "That's why it is suggested to use them together." A Menu from Headless UI supplies the behavior and accessibility; btn and menu classes supply the look; the theme still owns every color. Behavior and styling never get tangled into a single component you have to maintain.

The class-name architecture is also why one vocabulary spans my whole portfolio. Those repos include React 18, React 19, and a WordPress theme written in PHP. Adding daisyUI to a WordPress project is easy — we did it: Tailwind and the daisyUI plugin compile into the theme's stylesheet, and every PHP template gets the same vocabulary. In that theme, 1,439 daisyUI class usages sit across 168 templates with identical grammar to the React apps. For an agent, a class vocabulary is a far smaller thing to learn and emit than a framework component's API surface, and it cannot drift the way vendored component source can once every team edits its own copy. The copy-paste model has real strengths. I have avoided it, and the rest of this article is the reason.

Why a constrained vocabulary suits a language model

A model writing raw Tailwind has to make dozens of small decisions per component: which padding scale, which of several equivalent color utilities, which border treatment. There are a hundred defensible ways to build a button, so two agents working the same codebase will build two different buttons, and both will pass review because each is locally fine.

daisyUI collapses that decision space. The button is btn. The primary button is btn btn-primary. When the vocabulary is small and semantic, the model has fewer ways to be inconsistent, and every emission reinforces the same convention.

Pouya described the same mechanism from the library side: "If we are using 40 class name for a button, and 42 class names for another button, AI can't understand what's the design system about and what are the rules." And he pointed out that the goal predates AI: "The goal for daisyUI was to speed up the development speed by writing less class names. It turns out to be even more important when we use AI because less class names means less token consumption, faster generation and more design consistency."

Fewer class names per component means fewer tokens per generation, which means faster and cheaper output. A smaller vocabulary also means more repeatable output, which is the property that matters when agents write most of the code.

There is a second layer, which I wrote about a couple of weeks ago in the context of model weights: a model can only reliably produce patterns that are well represented in its training data. daisyUI's class names are heavily documented in public, and many of them descend from conventions old enough to be everywhere. I had assumed models could simply guess the names. Pouya corrected that: "For AI models, I think it's more about training than guessing. A class name like btn-primary that you mentioned, also exists in older libraries like Bootstrap. However that's not the case for all class names. So we need to give proper resources (skills or MCP server) to the LLM, to make sure they have a source of truth." A component added tomorrow is in nobody's weights yet, and his conclusion was blunt: "Resources must be provided to AI."

He is right, and the distinction matters in practice. The established vocabulary rides in the weights for free. Anything newer or custom needs a source of truth handed to the agent — a skill, an MCP server, or the codebase itself demonstrating the approved pattern. He practices this, too: the daisyUI repository now ships an official skill directory for AI agents, a maintained source of truth for exactly the parts a model cannot guess. The framework earns you a large head start, not a complete exemption from context.

One precondition hides under all the mechanics, and grep cannot measure it. Constraint amplifies whatever taste is encoded in the system: if the defaults were ugly, a disciplined vocabulary would make every screen uniformly ugly, at agent speed. daisyUI works because Pouya is a designer first. The spacing, the radii, the proportions, the contrast pairs are beautiful out of the box, and every btn btn-primary an agent emits inherits that taste for free. None of this would work if his designs were ugly.

What the repositories show

The strongest evidence in my repos is not speed. It is the absence of drift.

The B2B platform is the biggest project: 479 UI files, about 102,000 lines, built over five months by two humans and a fleet of coding agents — 1,378 of its 1,710 commits, 81 percent, carry an AI co-author trailer. I sampled btn btn-primary call sites across it, from different areas, written months apart, by different authors. Every variant is the canonical class string plus size or width modifiers. No bespoke buttons. In the same 102,000 lines there are zero hardcoded color values in the UI files; every color routes through the theme.

Style-correction work is nearly absent across the whole set. Out of roughly 2,900 commits in seven repos, 17 are UI style or class fixes. The medical practice website has zero. The two small internal tools have zero. The platform, with 191 UI commits, has six.

One commit message from the platform captures why. It reads: "conform Risk Class filter to the approved DaisyUI select pattern." A reviewer — human or agent — could name the correct implementation because there is exactly one approved way to build a select. The design system functions as a review standard, not just a stylesheet. That is what a design vocabulary buys you at review time: deviation becomes visible, and the fix is a noun, not a debate.

Velocity shows up too, though I hold it more loosely because greenfield speed has many parents. The core of a client marketing site went from empty repo to substantially complete in three days — 31 of its 50 total commits land on February 4 through 6. An internal RAG tool's entire working UI, nine files, shipped in two commits on a single day. Those numbers are consistent with agents that already know the vocabulary and a theme that already answers every color question.

And Pouya's economic framing predicts exactly this behavior from the agent's side: "If you were an LLM and a library provides everything you want for a specific job, logically it wouldn't make sense for you to recreate it from scratch everytime." The agents did not recreate buttons. There was nothing to gain by doing so.

One theme file per brand

The medical practice website's entire visual identity is a 141-line CSS file. When a client wants a brand adjustment, the diff is a handful of design tokens, and everything that uses the theme moves together.

The client with both a marketing site and a SaaS platform shows what that costs in practice. Their marketing site launched on a warm amber theme named bold-industrial. Three days into the platform build, they wanted the product to carry its own identity, and the change — to a teal theme named product-teal — shipped the same day, inside the commit that replaced 48 placeholder pages with real UI. What happened next is the part I keep showing clients: nothing. That theme block has not been modified since March 10. Five months, seventeen hundred commits, two humans and a fleet of agents — and the platform's entire visual identity sat still in one file while everything around it churned.

I put the unification question to Pouya, and he allowed that one shared theme "might work for some products" but sided with splitting when the audiences differ: "Maybe it's not always a good idea to force the exact theme for both. For example I would assume a marketing site needs bigger buttons, more spacious layout, more animations, graphics, etc but an admin dashboard would be better with smaller buttons, a dense UI, and no decorative visuals. The audience are totally different."

What carries across projects in both cases is the vocabulary, the component grammar, and the theming mechanism. An agent that learned btn btn-primary on the marketing site is equally fluent in the platform, whether primary resolves to the same color there or a different one. When the client wants one look everywhere, a single theme file delivers it; when they want two, each is still just a screenful of tokens.

A related pattern from the same repos: the marketing sites do escape the system for decorative work — I counted 19 and 18 arbitrary color values on the two of them — while the dense application has none. Marketing pages want texture and moments of flair; an admin screen wants uniformity. The constraint holds hardest exactly where consistency matters most, which is the right shape for the trade.

Extension has a disciplined path too. The client marketing site defines a custom card-industrial class — written once in the stylesheet, used 84 times across the site. The brand got its distinctive card without forking the system or scattering one-off styles, and by Pouya's earlier point, that custom name is precisely the kind of thing you then feed to the agent as a resource, because no model will guess it.

The sameness question

The obvious objection to all of this: if every agent reaches for the same vocabulary, does everything end up looking the same?

Pouya's answer was more direct than mine would have been. AI-generated UI lacks creativity, he argues, because it is "trained on existing UIs. So it gives you a remix of whatever was posted on Dribbble last year." The escape is not the framework — it is what you feed the model: "If we are all using the same model, which is trained on the same data, at least we must make sure our input is unique enough, the resources we feed the agent and our creative direction is detailed enough to give us a result different than others. All websites looking similar is not a cost any brand is willing to pay. The one website that looks different from all the competitors will get the attention."

The sameness comes from the model, not the component library. Identical prompts to identical models produce identical design regardless of what CSS framework renders it. The differentiation has to come from your input — the theme tokens, the creative direction, the custom vocabulary like card-industrial. He expects the trend cycle to accelerate and creativity to become the scarce value: "I think web design would become similar to fast fashion. Trends would have a shorter life span and the creativity would be the value."

That division of labor seems right to me. Let the library standardize the ninety percent of UI that should be boring — forms, tables, buttons, navigation — and spend your creative budget, deliberately, in the theme and the few places where the brand actually differentiates.

What I would tell a team adopting agents for front-end work

Three things, all grounded in the data above.

First, pick a constrained, well-published vocabulary and be strict about it. The constraint is what makes agent output repeatable, and the public documentation is what makes the vocabulary already live in the weights. My repos show what strictness buys: 81 percent agent-authored code with no button drift and near-zero style-fix commits.

Second, treat anything custom as a resource you owe the agent. New components, custom classes, local conventions — none of it is guessable. Write it down where the agent can read it, and let the codebase demonstrate the approved patterns. The "approved DaisyUI select pattern" commit is what it looks like when that works.

Third, unify the grammar, not necessarily the palette. Share the vocabulary and the theming mechanism across projects, and let each audience get the theme it needs. The agent's fluency transfers; the amber and the teal do not have to.

Fair caveats: all of these repos adopted daisyUI at day one, so this data says nothing about retrofit cost on an existing codebase. And co-author trailers undercount agent involvement, so 81 percent is a floor, not an estimate. But eight projects in, the pattern is consistent enough that I no longer consider it luck. A small semantic vocabulary, one theme file, and agents that already speak the language — that combination has quietly become the fastest, most consistent way I know to ship a front end.

I want to close with plain appreciation. daisyUI has been an incredible accelerator for my projects — marketing sites, client platforms, internal tools, a WordPress theme — and every one of them moved faster and stayed cleaner because of decisions Pouya made years before AI agents existed. He built a design system clear enough that machines now speak it natively, he maintains it in the open, he gives it away for free, and he took real time to answer my questions thoughtfully for this article. That combination of craft and generosity is what open source looks like at its best. If you build for the web — and especially if you are pointing agents at a front end — go try daisyUI, and if it earns a place in your stack the way it has in mine, star the repo and support the project. Thank you, Pouya.


Jason Vertrees is the founder of Heavy Chain Engineering, which helps lower middle-market vertical SaaS companies and PE firms turn scattered AI usage into measurable delivery leverage — 85% faster feature velocity, six-to-eight-week projects shipped in days. If you want help building an AI-native engineering organization, book an AI Delivery Assessment.