The closed view tree.
A page is composed of exactly four nested levels — Page > Section > Block
Leaf— plusOverlay, which lives only inPage.overlays. No constructor accepts a child of its own level or above, so "a card inside a card", "a section inside a block", "six sections" and "two primary buttons" are all unrepresentable rather than merely discouraged.
Every XConfig is a record of Maybe <Group> values taken straight from
Daisy.Schema.<Component> plus, where the component declares one,
modifiers : List <Component>.Modifier. There is no String class field
anywhere and no Raw Html escape hatch, so Daisy.Render is the only module
that can emit a class.
import Daisy.Tree exposing (..)
savePage : Page Msg
savePage =
Page
{ shell = Plain
, sections = Sections1 (Stack defaultStackConfig [ Prose [ Text "Hello" ] ])
, cta = cta "Save" Save
, overlays = []
, theme = Light
, dock = Nothing
, fab = Nothing
}One to five sections. There is no constructor for zero or six, so the page content budget is a type error rather than a runtime check.
The page frame.
Plain is a single column of sections. Dashboard renders as a daisyUI
drawer that is open from lg: up, with the sidebar menu in drawer-side
and the navbar on top of drawer-content.
The page's single primary call to action. Rendered as btn btn-primary.
Every theme daisyUI 5 ships. Daisy.Render.page writes the chosen one to
data-theme on the page root.
The data-theme value for a Theme.
themeToString Caramellatte --> "caramellatte"A top-level band of the page.
Navbar holds leaves in three named part lists; the others hold blocks.
hero has no class groups; overlay decides whether the renderer emits the
hero-overlay part.
A hero with no overlay.
Configuration of a Grid section.
A four-column grid.
How many columns a Grid section has. Spacing is fixed by
Daisy.Render.tokens; only the column count is authorable.
Configuration of a Stack section. This is a fixed-gap vertical band, not
the daisyUI stack component — that one is Stacked.
A stack whose blocks sit at the start edge.
The default is deliberately not AlignStretch, even though stretching is
what most bands want: under Shell.Plain the renderer appends Page.cta to
the last section's container, so a stretched last Stack would stretch the
page's single primary button across the whole page. Ask for AlignStretch
where you want it instead.
Cross-axis alignment of a Stack section.
AlignStretch (items-stretch) is the one that lets a Table, Alert or
Card fill the band; the other three shrink every block to its content width.
A self-contained content container inside a section. A block never contains another block.
Groups of the daisyUI collapse component, plus the radio group name that
turns a set of collapses into an accordion.
An accordion whose items share the group name "accordion".
One collapse inside an Accordion.
Groups of the daisyUI alert component.
An alert with no colour, style or direction class.
Groups of the daisyUI card component plus the aura and hover-3d
decoration properties.
A plain card.
The card-* parts. A part record makes card-body outside a card
unrepresentable.
A card with nothing in it.
What a card-body may hold: leaves, plus the four block shapes a
dashboard card is actually made of.
There is deliberately no CardCard: a card can never contain a card, which is
what tools/should-not-compile/Reject/CardInCard.elm and
Reject/CardInCardBody.elm pin down. Every constructor here takes exactly the
arguments its Block counterpart takes, and Daisy.Render renders it through
the same helper, so a chart in a card and a bare chart are the same markup.
Groups of the daisyUI carousel component.
A carousel with no direction or snap class.
One carousel-item.
Where a carousel snaps its items. daisyUI declares these under modifier
but they are mutually exclusive, so the tree offers one field and the renderer
expands it into a single modifier class.
One chat message. The colour applies to the chat-bubble part, which is
where daisyUI puts it.
Groups of the daisyUI collapse component.
A closed collapse with no arrow or plus icon.
The collapse-title and collapse-content parts.
The diff-item-1 and diff-item-2 parts. diff-resizer is emitted by the
renderer.
A fieldset inside a Form block. fieldset-legend cannot exist without
its fieldset.
One labelled, optionally validated control.
label and validator-hint are held here rather than as leaves, so a stray
legend, label or hint is unrepresentable and the hint is always the sibling
immediately after its input.
Where a field's label sits: before the control, after it, or floating over
it (floating-label).
A labelled field with no validation.
field "Email" (Input defaultInputConfig)One list-row.
One cell of a list-row.
list-col-grow and list-col-wrap mark a single cell, not the whole list, so
they are flags here rather than modifiers on the block. A cell with neither
flag is rendered as its bare leaf.
A plain cell that neither grows nor wraps.
listCell (Text "Cy Ganderton")Groups of the daisyUI menu component. menu-active, menu-disabled and
menu-focus belong on an item, so they are flags on MenuItem,
not entries here.
A vertical menu at the default size.
One menu entry. title = True renders it as the menu-title part; a
non-empty submenu renders the menu-dropdown parts.
href becomes the anchor's href. A menu item without one is a bare <a>,
which is not focusable and carries no link role, so navigation items should
always set it; onClick may still be set alongside (a Browser.application
turns the click into an onUrlRequest on its own).
A badge shown at the end of a menu item.
A menu referenced from somewhere other than Block.Menu, namely
Shell.Dashboard.sidebar and the dropdown property.
The mockup-browser-toolbar part plus the framed content.
The framed content. mockup-phone-camera and mockup-phone-display are
emitted by the renderer.
A window title bar plus the framed content.
One line of a MockupCode block.
Groups of the daisyUI pagination component, which reuses join.
A pagination bar with no direction class.
The pages of a pagination bar and the index of the active one.
Groups of the daisyUI stack component.
A stack with no alignment class.
Where a daisyUI stack aligns its overlapping children. Declared under
modifier but mutually exclusive; see snap on CarouselConfig.
Groups of the daisyUI stat component. The container class is stats.
A stats container with no direction class.
How a stats container lays its tiles out.
Fixed is the schema direction group: one class, or none. Responsive is
daisyUI's own stats-vertical lg:stats-horizontal idiom — a column on a phone,
a row from lg up. It is a separate constructor rather than a flag beside
direction so there is no combination where one silently wins over the other.
One tile: the stat, stat-figure, stat-title, stat-value,
stat-desc and stat-actions parts.
A titled tile with a value and nothing else.
Groups of the daisyUI steps component. The colour classes are step-* and
belong on a Step, not here.
Steps with no direction class.
One step, optionally coloured and with a step-icon.
Groups of the daisyUI table component.
A table with no size or zebra class.
One table row. header = True renders its cells as <th> inside <thead>.
Groups of the daisyUI tab component. tab-active and tab-disabled
belong on a Tab, not here.
Tabs with no style, size or placement class.
One tab and the panel it owns.
Groups of the daisyUI timeline component.
A timeline with no direction class.
The container-level modifiers of the daisyUI timeline component.
This deliberately omits timeline-box, the way ButtonColor
omits btn-primary: daisyUI puts that class on one side of one item, so it
is startBox / endBox on TimelineItem and cannot be
listed here.
Every TimelineModifier value.
Widen a TimelineModifier to the generated schema
type.
The timeline-start, timeline-middle and timeline-end parts of one
entry.
startBox / endBox put timeline-box on that side of this item, which is
where daisyUI puts it.
A terminal control or piece of content. A leaf holds only data, never another node.
The src of an image. Leaf.Image is not a daisyUI component; it exists
because card, carousel, diff, stack, avatar and hover-gallery all
need one.
The rank of a Leaf.Heading.
Heading is not a daisyUI component and emits no daisyUI class: it renders a
bare <h1> / <h2> / <h3>, which Tailwind typography styles when the
heading sits inside a Block.Prose. It exists because a page otherwise has no
document outline at all — Leaf.Text is a text node and Prose is a <div>.
Groups of the daisyUI avatar component plus the mask, dropdown,
indicator and tooltip properties.
An avatar with no status dot or mask.
One member of an avatar-group.
Groups of the daisyUI badge component.
A badge with no colour, style or size class.
Groups of the daisyUI button component, with the tree's narrowed colour
type, plus the tooltip, dropdown, indicator and aura properties and the
click handler.
A plain button.
Button defaultButtonConfig "Save"The colour of a Button leaf.
This deliberately omits Primary: the only btn-primary on a page is
Page.cta, which uses the schema type directly.
Every ButtonColor value.
Widen a ButtonColor to the generated schema type.
Everything the calendar picker needs that is not its own state.
The leaf is a typed front for the pure-Elm package alexbruf/elm-cally, a
port of the Cally web component that renders the same markup and the same
part attributes in the light DOM — which is exactly what daisyUI's
calendar.css styles through its cally class. No ports, no custom element
registration, no shadow DOM.
Daisy.Render builds elm-cally's own Config record from this one; it is
never exposed raw, the same way Daisy.Chart hides terezka/elm-charts.
The update flow. A calendar is the second stateful leaf after
ThemeSelect, and the only one whose state the application must
store, because a date picker remembers which day holds the roving tabindex
and which month is on screen:
CalendarState in your Model, built once with
Daisy.Render.initCalendarRange (or initCalendarDate /
initCalendarMulti).toMsg a constructor of your own Msg that carries a
CalendarMsg, and forward it in update to
Daisy.Render.updateCalendar, storing the state it returns and running
the Cmd it returns (that command carries the picker's own
Browser.Dom.focus call, so keyboard navigation needs it).onChange fires with a CalendarValue whenever the
selection changes; store that too if you want to show it.Leaf.Calendar config state from the model on every view. The
leaf is data, so nothing is retained between renders.id prefixes every DOM id the picker renders (day buttons, the two paging
buttons), so it must be unique on the page and stable across renders.
A calendar in EnGB showing one month.
Unlike every other defaultXConfig this is a function, not a value: a picker
cannot exist without an id, a today and somewhere to send its messages,
and elm-cally's own defaultConfig takes the same four.
The two locales alexbruf/elm-cally bundles. Elm has no Intl, so a
locale is a table of formatting functions rather than a language tag, and the
package ships exactly these two; a closed type keeps an unbuildable one from
being asked for.
How many month grids the picker shows, and therefore how far its previous and next buttons page.
This is a closed pair rather than an Int for the usual reason: months = 13
is not a calendar anyone means, and the renderer would have to decide what to
do with it. TwoMonths puts the two grids side by side from sm up and in one
column below it: elm-cally's [part~="months"] is a bare <div> and daisyUI's
calendar.css has no months rule, so the layout comes from Daisy.Render's
own token table (see Daisy.Render.calendarMonths).
What the picker selects, and everything it remembers for itself.
The three constructors are the three pickers elm-cally provides, and each one
carries that picker's Value and Model as opaque data — the same way
Leaf.ThemeSelect carries the current Theme. Build one with
Daisy.Render.initCalendarDate / initCalendarRange / initCalendarMulti
and hand it back to Daisy.Render.updateCalendar; nothing else can construct
the Model inside, so the picker's invariants stay elm-cally's business.
The selection CalendarConfig.onChange reports, one constructor per
CalendarState constructor. A range is always sorted.
One of the picker's own messages, on its way back to
Daisy.Render.updateCalendar. The payloads are elm-cally's opaque Msg
types, so an application can route one but cannot invent one.
Write the selection CalendarConfig.onChange reported back into the
state, so the next render draws it.
Daisy.Render.updateCalendar advances everything the picker remembers for
itself (the focused day and the page on screen); the value is the
application's, which is why it comes back as its own message and goes back in
through this function. A value of a different kind than the state is a no-op:
one Leaf.Calendar only ever produces one kind.
Groups of the daisyUI checkbox component.
An unchecked checkbox with no colour or size class.
Groups of the daisyUI divider component.
A divider with no colour, direction or placement class.
Groups of the daisyUI file-input component.
A file input with no colour, style or size class.
A filter: a radio group styled as buttons, plus an optional reset
control. daisyUI's filter declares no class groups, so this is all data.
How a filter's reset control is drawn.
daisyUI has two idioms and the class set differs between them. ResetPart
carries the filter-reset part, which is what a filter outside a <form>
needs. ResetButton is the plain btn a filter inside a real <form> uses
(daisyUI's own examples make it btn-square), where the browser's form reset
does the work and no part class is involved.
The mask and hover-3d properties of an image, plus its alt text.
A plain image with empty alt text.
Groups of the daisyUI input component, plus the HTML validation
constraints daisyUI's validator / validator-hint pair needs.
inputType, required, pattern, minLength and maxLength are rendered as
the matching HTML attributes. Without at least one of them a Field marked
validate = True can never become :user-invalid, so its hint would never
show.
pattern is a regular expression (the HTML pattern attribute), never a class.
An empty, unconstrained text input with no colour, style or size class.
What an input accepts. Rendered as the type attribute, which is half
of what makes daisyUI's validator class do anything: the styling hangs off
:user-invalid, and only a real constraint can make a control invalid.
Groups of the daisyUI join component.
A join with no direction class.
What a join may group. A closed list, so Leaf stays non-recursive.
Groups of the daisyUI kbd component.
A kbd with no size class.
Groups of the daisyUI link component.
A link to "#" with no colour or style class.
Groups of the daisyUI loading component.
A loading indicator with no style or size class.
Groups of the daisyUI otp component.
A one-time-code input with no colour or size class.
How many boxes an otp has.
Groups of the daisyUI progress component.
A progress bar with no colour class.
The value of a progress bar. Nothing renders the indeterminate state.
The value and centre label of a radial-progress. Its size and thickness
are CSS variables, not classes, so there is no config.
Groups of the daisyUI radio component.
A radio with no colour or size class.
The group name of a radio and whether it is selected.
Groups of the daisyUI range component.
A range with no colour, size or direction class.
The bounds and value of a range.
Groups of the daisyUI rating component.
shape is the mask-* clip-path each radio wears. Nothing keeps the
renderer's default (mask-star-2), which is what daisyUI's own rating examples
use most.
A rating of default-shaped stars with no size class.
The container-level modifiers of the daisyUI rating component.
This omits rating-hidden the way TimelineModifier
omits timeline-box: daisyUI puts it on the rating's first, blank radio, so
it is clearable on RatingData.
RatingHalf also drives the item masks: with it the renderer alternates
mask-half-1 / mask-half-2 across the radios, which is the only way daisyUI's
half-star rating works.
Every RatingModifier value.
Widen a RatingModifier to the generated schema type.
The group name, number of stars and current value of a rating.
clearable = True puts daisyUI's blank rating-hidden radio first, so the
rating can be set back to "no stars".
Groups of the daisyUI select component.
A select with no colour, style or size class.
The options of a select and which one is chosen.
Groups of the daisyUI skeleton component.
A block skeleton.
Groups of the daisyUI status component.
A status dot with no colour or size class.
Groups of the daisyUI swap component.
A swap with no rotate or flip class.
The swap-on, swap-off and swap-indeterminate faces.
Groups of the daisyUI textarea component.
An empty, optional textarea with no colour, style or size class.
A theme switcher. theme-controller declares no class groups, so this is
all data. Page.theme supplies the initial data-theme; this control changes
it.
How a theme-controller is presented. daisyUI's docs examples (toggle,
checkbox, radio group, select, swap, dropdown) are presentations of one
control, not separate components.
ThemeAsDropdown is the only compact one: every other presentation renders one
sibling control per theme, which is 35 controls wide with allThemes.
It renders daisyUI's documented "Using a dropdown" markup — a btn trigger and
a dropdown-content list of theme-controller radios.
Groups of the daisyUI toggle component.
A toggle with no colour or size class.
Whether a toggle is on.
These are never nodes. They are fields on a leaf's config, so the renderer always owns the wrapper element and the anchor can never go missing.
A tooltip attached to one leaf.
Groups of the daisyUI tooltip component.
A tooltip with no colour or placement class.
A dropdown attached to one leaf. Its content is always a menu, so the
dropdown-content part can never appear without an anchor.
Groups of the daisyUI dropdown component.
A dropdown with no placement class.
An indicator attached to one leaf.
Groups of the daisyUI indicator component.
An indicator with no placement class.
What sits in the indicator-item.
Groups of the daisyUI mask component. A clip-path shape applied to one
existing element.
A mask with no shape class.
Groups of the daisyUI aura component. A decorative border light around one
existing element.
An aura with no style or size class.
The only three components that may float over the page. They live in
Page.overlays and nowhere else, and the renderer draws them after every
section in one fixed wrapper, in the order drawer, modal, toast.
Groups of the daisyUI modal component. actions is rendered into the
modal-action part, so a modal's buttons cannot drift out of it.
onClose is fired when the browser closes the dialog: pressing Escape, or
the native close event. The renderer draws the modal with daisyUI's
recommended dialog method (<dialog class="modal">), which is what makes
Escape and the focus trap work at all; the application still owns
visibility through SModal.Open, and onClose is how it learns to drop it.
A closed, untitled modal with the id "daisy-modal" that reports nothing
when it closes.
Groups of the daisyUI drawer component. The variant group
(is-drawer-open: / is-drawer-close:) is a pair of selector prefixes, not
classes an element can carry, so it has no field here.
A closed drawer with the id "daisy-drawer".
Groups of the daisyUI toast component.
A toast in daisyUI's default corner.
A viewport-fixed bottom navigation bar. It is a field on Page rather than
a section, because in a section it would overlap the content it sits on.
Groups of the daisyUI dock component. Its single modifier (dock-active)
belongs on an item, so it is a flag on DockItem.
A dock with no size class.
One dock button. Its label becomes the dock-label part.
A viewport-fixed floating action button, with the fab-main-action and
fab-close parts.
main is the trigger the user sees when the fab is closed. mainAction is the
separate button daisyUI marks fab-main-action, which stays in place once the
speed dial is open; the part class goes on that leaf itself, which is where
every daisyUI example puts it.
A fab with no flower class.
A whole page.
sectionsis a fixed-arity type, so a page has between one and five sections.ctais mandatory and is the page's onlybtn-primary; the shell decides where it goes.overlaysis the only place a modal, drawer or toast can appear.dockandfabare viewport-fixed chrome rendered in the same layer.