@molecule/app-ui-tailwind
Provider bond · ui · App (browser) · v1.2.6 · Apache-2.0
Tailwind CSS UI components for molecule.dev
npm install @molecule/app-ui-tailwindnpm · Source on GitHub · Implements @molecule/app-ui
How it works
@molecule/app-ui-tailwind is a provider bond on the app (browser) side: it implements the ui core interface (@molecule/app-ui) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
// 1. Wire the ClassMap once at startup (bonds.ts):
import { setClassMap } from '@molecule/app-ui'
import { classMap } from '@molecule/app-ui-tailwind'
setClassMap(classMap)
// 2. In the app's Tailwind v4 CSS entry (e.g. src/index.css):
// @import 'tailwindcss';
// @import '@molecule/app-ui-tailwind/base.css'; <- theme tokens
// @source "../node_modules/@molecule/app-ui-tailwind/dist"; <- keep
// Tailwind's purge from stripping the classes this package emits
// (Scaffolded molecule apps already have both — this is only needed when
// integrating into an app not generated by mlcl.)
// 3. Resolve classes anywhere via the ClassMap — never hardcode Tailwind:
import { getClassMap } from '@molecule/app-ui'
const cm = getClassMap()
element.className = cm.button({ color: 'primary', size: 'md' })Works with: @molecule/app-styling, @molecule/app-ui
Live demos
Running apps built with Molecule that install this package — open the app or study the template.
Reference
Tailwind CSS UI components for molecule.dev.
Provides pre-built, accessible UI components using Tailwind CSS classes. These are framework-agnostic class strings that can be used with any rendering library (React, Vue, Svelte, etc.).
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
// 1. Wire the ClassMap once at startup (bonds.ts):
import { setClassMap } from '@molecule/app-ui'
import { classMap } from '@molecule/app-ui-tailwind'
setClassMap(classMap)
// 2. In the app's Tailwind v4 CSS entry (e.g. src/index.css):
// @import 'tailwindcss';
// @import '@molecule/app-ui-tailwind/base.css'; <- theme tokens
// @source "../node_modules/@molecule/app-ui-tailwind/dist"; <- keep
// Tailwind's purge from stripping the classes this package emits
// (Scaffolded molecule apps already have both — this is only needed when
// integrating into an app not generated by mlcl.)
// 3. Resolve classes anywhere via the ClassMap — never hardcode Tailwind:
import { getClassMap } from '@molecule/app-ui'
const cm = getClassMap()
element.className = cm.button({ color: 'primary', size: 'md' })
Type
provider
Installation
npm install @molecule/app-ui-tailwind @molecule/app-styling @molecule/app-ui
API
Interfaces
CVAConfig
Configuration for a class-variance-authority (cva) function: the variant
definitions, default selections, and compound variants used to resolve a
component's final class string from its props.
interface CVAConfig<T extends Record<string, Record<string, string>>> {
variants?: T
defaultVariants?: {
[K in keyof T]?: keyof T[K]
}
compoundVariants?: Array<
{
[K in keyof T]?: keyof T[K]
} & {
class: string
}
>
}
Types
ClassValue
Class name value types accepted by cn.
type ClassValue =
| string
| number
| boolean
| undefined
| null
| ClassValue[]
| Record<string, boolean | undefined | null>
Constants
classMap
Tailwind CSS UIClassMap implementation mapping abstract UI tokens to Tailwind class strings.
const classMap: UIClassMap
button
Button component classes.
const button: (
props?:
| ({
variant?:
| 'default'
| 'secondary'
| 'success'
| 'outline'
| 'ghost'
| 'link'
| 'warning'
| 'danger'
| undefined
size?: 'sm' | 'md' | 'lg' | 'icon' | undefined
} & { class?: string })
| undefined,
) => string
input
Input component classes.
const input: (
props?:
| ({
variant?: 'default' | 'error' | undefined
size?: 'sm' | 'md' | 'lg' | undefined
align?: 'left' | 'center' | undefined
} & { class?: string })
| undefined,
) => string
textarea
Textarea component classes.
const textarea: (
props?:
| ({ variant?: 'default' | 'error' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => string
select
Select component classes.
const select: (
props?:
| ({ variant?: 'default' | 'error' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => string
checkbox
Checkbox component classes.
const checkbox: (
props?: ({ variant?: 'default' | 'error' | undefined } & { class?: string }) | undefined,
) => string
radio
Radio component classes.
const radio: (
props?: ({ variant?: 'default' | 'error' | undefined } & { class?: string }) | undefined,
) => string
switchBase
Switch component classes.
The color axis maps 1:1 to the semantic ColorVariant scale (same
primary/secondary/success/warning/error/info tokens
progressColor already uses) — no lookup table needed, unlike button's
differently-named CVA variants.
const switchBase: (
props?:
| ({
color?: 'secondary' | 'success' | 'warning' | 'error' | 'primary' | 'info' | undefined
size?: 'sm' | 'md' | 'lg' | undefined
} & { class?: string })
| undefined,
) => string
switchThumb
Switch thumb (the sliding circle) classes.
const switchThumb: (
props?: ({ size?: 'sm' | 'md' | 'lg' | undefined } & { class?: string }) | undefined,
) => string
label
Label component classes.
const label: (
props?: ({ required?: 'true' | 'false' | undefined } & { class?: string }) | undefined,
) => string
formError
Form error message classes.
const formError: 'text-sm text-error mt-1'
formHint
Form hint message classes.
const formHint: 'text-sm text-foreground-secondary mt-1'
card
Card component classes.
const card: (
props?:
| ({ variant?: 'default' | 'outline' | 'ghost' | 'elevated' | undefined } & { class?: string })
| undefined,
) => string
cardHeader
The card header.
const cardHeader: 'flex flex-col space-y-1.5 p-6'
cardTitle
The card title.
const cardTitle: 'text-lg font-semibold leading-none tracking-tight'
cardDescription
The card description.
const cardDescription: 'text-sm text-foreground-secondary'
cardContent
The card content.
const cardContent: 'p-6 pt-0'
cardFooter
The card footer.
const cardFooter: 'flex items-center p-6 pt-0'
badge
Badge component classes with variant support.
const badge: (
props?:
| ({
variant?: 'default' | 'secondary' | 'success' | 'outline' | 'warning' | 'error' | undefined
} & { class?: string })
| undefined,
) => string
alert
Alert component classes with variant support (default, info, success, warning, error).
const alert: (
props?:
| ({ variant?: 'default' | 'success' | 'warning' | 'error' | 'info' | undefined } & {
class?: string
})
| undefined,
) => string
alertLeftAccent
Left-accent strip for Alert's variant="left-accent" — a 4px colored bar
on the leading edge, colored by the alert's STATUS (the base alert cva
keys its variants off status too, so the two compose 1:1). Previously the
variant was typed in the core contract but silently dropped by every
Alert implementation, so left-accent call sites rendered as plain alerts.
const alertLeftAccent: (
props?:
| ({ variant?: 'default' | 'success' | 'warning' | 'error' | 'info' | undefined } & {
class?: string
})
| undefined,
) => string
accentCard
A block with the accent bar down its leading edge (see ACCENT_BAR).
custom reads --mol-accent, set inline by the caller for data-driven colors.
const accentCard: (
props?:
| ({
tone?:
'default' | 'success' | 'warning' | 'error' | 'primary' | 'info' | 'custom' | undefined
} & { class?: string })
| undefined,
) => string
accentCardLabel
The small uppercase heading of an accent block, in the bar's color.
const accentCardLabel: (
props?:
| ({
tone?:
'default' | 'success' | 'warning' | 'error' | 'primary' | 'info' | 'custom' | undefined
} & { class?: string })
| undefined,
) => string
alertTitle
The alert title.
const alertTitle: 'mb-1 font-medium leading-none tracking-tight'
alertDescription
The alert description.
const alertDescription: 'text-sm [&_p]:leading-relaxed'
avatar
Avatar component classes.
const avatar: (
props?: ({ size?: 'sm' | 'md' | 'lg' | 'xl' | undefined } & { class?: string }) | undefined,
) => string
avatarImage
The avatar image.
const avatarImage: 'aspect-square h-full w-full object-cover'
avatarFallback
Avatar fallback container classes (centered, rounded, secondary background).
const avatarFallback: 'flex h-full w-full items-center justify-center rounded-full bg-surface-secondary text-foreground-secondary'
dialogOverlay
Modal/Dialog overlay classes — the TINT only. The backdrop blur lives on
dialogWrapper (the dialog's PARENT), never here: a backdrop-filter
on a same-z sibling relies on DOM paint order, and browser-extension DOM
injection (e.g. Bitwarden's autofill overlay) can trigger a Chromium
recomposite that momentarily sorts the filter layer above the dialog —
blurring the dialog itself. A parent's backdrop-filter can never affect its
children, by spec.
const dialogOverlay: 'fixed inset-0 z-[9998] bg-overlay pointer-events-none'
dialogContent
The dialog content.
const dialogContent: (
props?:
({ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' | undefined } & { class?: string }) | undefined,
) => string
dialogHeader
The dialog header.
const dialogHeader: 'flex-shrink-0 flex items-center justify-between px-6 py-2'
dialogFooter
Dialog footer classes.
const dialogFooter: 'flex-shrink-0 flex flex-col-reverse px-6 py-4 border-t border-border sm:flex-row sm:justify-end sm:space-x-2'
dialogTitle
The dialog title.
const dialogTitle: 'text-xl font-semibold leading-tight tracking-tight'
dialogDescription
The dialog description.
const dialogDescription: 'text-sm text-foreground-secondary'
dialogClose
Dialog close button classes.
const dialogClose: 'flex-shrink-0 ml-4 -mr-4 p-1.5 cursor-pointer text-foreground-secondary hover:text-foreground transition-colors focus:outline-none'
dialogCloseFloating
Floating close for title-less dialogs — overlays the top-right corner of
the dialog box (dialogContent is relative) instead of rendering a
header row that pushes the body down.
const dialogCloseFloating: 'absolute right-3 top-3 z-10 p-1.5 cursor-pointer text-foreground-secondary hover:text-foreground transition-colors focus:outline-none'
dialogWrapper
The dialog wrapper. Carries the backdrop blur (see dialogOverlay for
why it must be on the dialog's parent, not a sibling) and sits one z above
the tint overlay so the dialog's stacking never depends on DOM order.
const dialogWrapper: 'fixed inset-0 z-[9999] flex items-center justify-center px-4 py-10 backdrop-blur-[2px]'
dialogBody
The dialog body.
const dialogBody: 'flex-1 min-h-0 overflow-y-auto px-6 py-6'
actionSheet
Action sheet: bottom-anchored panel for pickers and action menus.
const actionSheet: 'fixed bottom-0 left-0 right-0 z-[9998] bg-surface rounded-t-2xl max-h-[50vh] overflow-hidden'
actionSheetHeader
Action sheet header with bottom border.
const actionSheetHeader: 'flex-shrink-0 px-4 py-3 border-b border-border'
dropdownContent
Dropdown menu content container classes.
const dropdownContent: 'z-[1000] min-w-[8rem] overflow-hidden rounded-md border bg-surface p-1 text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95'
dropdownItem
Dropdown menu item classes.
const dropdownItem: 'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-surface-secondary focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50'
dropdownSeparator
The dropdown separator.
const dropdownSeparator: '-mx-1 my-1 h-px bg-border'
dropdownLabel
The dropdown label.
const dropdownLabel: 'px-2 py-1.5 text-sm font-semibold'
table
Table root classes.
const table: 'w-full caption-bottom text-sm'
tableHeader
The table header.
const tableHeader: '[&_tr]:border-b'
tableBody
The table body.
const tableBody: '[&_tr:last-child]:border-0'
tableFooter
The table footer.
const tableFooter: 'border-t bg-surface-secondary font-medium [&>tr]:last:border-b-0'
tableRow
Table row classes.
const tableRow: 'border-b transition-colors hover:bg-surface-secondary data-[state=selected]:bg-surface-secondary'
tableHead
Table header cell classes.
const tableHead: 'h-12 px-4 text-left align-middle font-medium text-foreground-secondary [&:has([role=checkbox])]:pr-0'
tableCell
The table cell.
const tableCell: 'p-4 align-middle [&:has([role=checkbox])]:pr-0'
tableCaption
The table caption.
const tableCaption: 'mt-4 text-sm text-foreground-secondary'
tabsList
Tabs list (tablist container) classes.
variant controls shape/background (a full-width underline rail for
line; a filled/bordered box for enclosed; a soft or solid pill track
for *-rounded); size controls only the container height, so it never
conflicts with tabsTrigger's own size-driven padding.
enclosed+md (the defaults) reproduce the single style this used to be
hardcoded to, token-for-token.
const tabsList: (
props?:
| ({
variant?: 'line' | 'enclosed' | 'soft-rounded' | 'solid-rounded' | undefined
size?: 'sm' | 'md' | 'lg' | undefined
} & { class?: string })
| undefined,
) => string
tabsTrigger
Tabs trigger (individual tab button) classes.
variant controls the shape/color of the ACTIVE state via
data-[state=active]:* attribute selectors — the caller (see
@molecule/app-ui-react's Tabs) sets data-state="active"|"inactive" on
the element; nothing here reads a JS active flag. size controls
padding/font-size only, shared across every variant so it can never
conflict with a variant's own classes.
enclosed+md (the defaults) reproduce the single style this used to be
hardcoded to, token-for-token.
const tabsTrigger: (
props?:
| ({
variant?: 'line' | 'enclosed' | 'soft-rounded' | 'solid-rounded' | undefined
size?: 'sm' | 'md' | 'lg' | undefined
} & { class?: string })
| undefined,
) => string
tabsContent
Tabs content (panel) classes.
variant only adjusts the top spacing beneath the tablist — enclosed
(the default) reproduces the single style this used to be hardcoded to,
token-for-token.
const tabsContent: (
props?:
| ({ variant?: 'line' | 'enclosed' | 'soft-rounded' | 'solid-rounded' | undefined } & {
class?: string
})
| undefined,
) => string
tooltipContent
Tooltip content container classes.
const tooltipContent: 'z-[1500] max-w-[90vw] overflow-hidden whitespace-nowrap rounded-md border bg-surface px-3 py-1.5 text-sm text-foreground shadow-md animate-in fade-in-0 zoom-in-95'
progress
Progress component classes.
const progress: 'relative h-2 w-full overflow-hidden rounded-full bg-surface-secondary'
progressIndicator
The progress indicator.
const progressIndicator: 'h-full w-full flex-1 bg-primary transition-all'
skeleton
Skeleton component classes.
const skeleton: 'animate-pulse rounded-md bg-surface-secondary'
separator
Separator component classes with horizontal/vertical orientation.
const separator: (
props?:
({ orientation?: 'horizontal' | 'vertical' | undefined } & { class?: string }) | undefined,
) => string
spinner
Spinner component classes with size variants.
const spinner: (
props?: ({ size?: 'sm' | 'md' | 'lg' | 'xl' | undefined } & { class?: string }) | undefined,
) => string
toastViewport
Toast viewport container classes (positioned fixed, stacked).
const toastViewport: 'fixed top-0 z-[1600] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]'
toast
Tailwind toast notification variant classes (default, success, error, warning).
const toast: (
props?:
| ({ variant?: 'default' | 'success' | 'warning' | 'error' | 'info' | undefined } & {
class?: string
})
| undefined,
) => string
toastTitle
The toast title.
const toastTitle: 'text-sm font-semibold'
toastDescription
The toast description.
const toastDescription: 'text-sm opacity-90'
toastClose
Toast close button classes.
const toastClose: 'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100'
toastAction
Toast action button classes.
const toastAction: 'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-surface-secondary focus:outline-none focus:ring-2 disabled:pointer-events-none disabled:opacity-50'
accordion
Accordion component classes.
const accordion: 'w-full'
accordionItem
The accordion item.
const accordionItem: 'border-b'
accordionTrigger
The accordion trigger.
const accordionTrigger: (
props?: ({ variant?: 'default' | 'bordered' | undefined } & { class?: string }) | undefined,
) => string
accordionContent
Accordion content wrapper classes with collapse animation.
const accordionContent: 'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down'
accordionContentInner
The accordion content inner.
const accordionContentInner: 'pb-4 pt-0'
pagination
Pagination component classes.
const pagination: 'mx-auto flex w-full justify-center'
paginationContent
The pagination content.
const paginationContent: 'flex flex-row items-center gap-1'
paginationItem
The pagination item.
const paginationItem: (
props?:
| ({ variant?: 'default' | 'active' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => string
paginationLink
The pagination link.
const paginationLink: 'cursor-pointer'
paginationPrevious
The pagination previous.
const paginationPrevious: 'gap-1 pl-2.5'
paginationNext
The pagination next.
const paginationNext: 'gap-1 pr-2.5'
paginationEllipsis
The pagination ellipsis.
const paginationEllipsis: 'flex h-9 w-9 items-center justify-center'
footerBar
Footer bar classes (full-width). Position is determined by the consuming layout, not forced here.
const footerBar: 'w-full flex flex-wrap items-center justify-around p-[5px] bg-background text-[12px] text-foreground-secondary'
footerLink
Footer link classes.
const footerLink: 'p-[5px] text-[12px] text-foreground-secondary hover:text-foreground no-underline'
footerButton
Footer button classes.
const footerButton: 'inline-flex items-center gap-1 border-0 m-0 p-[5px] text-[12px] text-foreground-secondary hover:text-foreground bg-transparent cursor-pointer'
languageGrid
Language selector grid container classes.
const languageGrid: 'grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2 max-h-96 overflow-y-auto p-2'
languageOption
Language option (inactive) classes. On coarse pointers (touch) the row grows
to the 44px touch floor and centers its label with flex (text-center alone
cannot center flex items, hence justify-center); fine-pointer rendering is
unchanged.
const languageOption: 'px-3 py-2 rounded-md text-sm cursor-pointer text-center transition-colors hover:bg-surface-secondary pointer-coarse:min-h-[44px] pointer-coarse:flex pointer-coarse:items-center pointer-coarse:justify-center'
languageActive
Language option (active/selected) classes. Same coarse-pointer touch floor as languageOption.
const languageActive: 'px-3 py-2 rounded-md text-sm cursor-pointer text-center transition-colors bg-primary/15 text-primary font-medium pointer-coarse:min-h-[44px] pointer-coarse:flex pointer-coarse:items-center pointer-coarse:justify-center'
languagePickerTrigger
Language picker trigger button classes (globe icon + native locale name). Inherits surface/foreground tokens so it sits cleanly inside Settings cards, header chrome, or footers without forcing a specific background.
const languagePickerTrigger: 'inline-flex items-center gap-2 px-3 py-2 rounded-md text-sm font-medium bg-transparent text-foreground-secondary hover:text-foreground hover:bg-surface-secondary border border-border-secondary cursor-pointer transition-colors'
themeToggleButton
Theme toggle button classes.
const themeToggleButton: 'h-10 w-10 p-0 border-0 bg-transparent text-foreground-secondary hover:text-foreground cursor-pointer flex items-center justify-center'
oauthDivider
OAuth divider classes (horizontal line with text).
const oauthDivider: 'relative mt-[45px] mb-[15px] text-[15px] text-center uppercase text-foreground-secondary'
oauthDividerLine
OAuth divider horizontal line classes.
const oauthDividerLine: 'absolute top-1/2 left-0 right-0 w-full h-[1px] border-t border-[rgba(170,170,170,0.33)]'
oauthDividerText
The oauth divider text.
const oauthDividerText: 'relative px-[15px] bg-background'
oauthButtonGroup
OAuth provider button group container classes.
const oauthButtonGroup: 'flex flex-wrap gap-[15px] w-full max-w-[330px] mx-auto justify-center items-center'
oauthButton
OAuth provider button classes.
const oauthButton: 'group grow basis-[40px] flex flex-col justify-center items-center min-h-[60px] bg-surface border-0 rounded-[3px] cursor-pointer p-0 transition-all duration-250 hover:bg-surface-hover'
oauthButtonIcon
OAuth provider button icon classes.
const oauthButtonIcon: 'opacity-75 group-hover:opacity-100 text-foreground transition-opacity'
authFormError
Auth form error message classes.
const authFormError: 'text-error mt-[15px]'
forgotPasswordLink
Forgot password link classes.
const forgotPasswordLink: 'border-0 p-0 m-0 bg-transparent text-foreground-secondary text-[12px] cursor-pointer hover:text-primary'
iconXs
Icon extra-small size classes (12×12).
const iconXs: 'h-3 w-3'
iconSm
Tailwind classes for small icons (16x16).
const iconSm: 'h-4 w-4'
iconMd
Tailwind classes for medium icons (20x20).
const iconMd: 'h-5 w-5'
buttonIconLeft
Button left icon spacing classes.
const buttonIconLeft: 'shrink-0'
buttonIconRight
The button icon right.
const buttonIconRight: 'shrink-0'
buttonSpinner
The button spinner.
const buttonSpinner: 'shrink-0'
cardInteractive
Interactive card classes (hover shadow effect).
const cardInteractive: 'cursor-pointer hover:shadow-md transition-shadow'
alertIconWrapper
Alert icon wrapper classes.
const alertIconWrapper: 'flex-shrink-0'
alertContent
The alert content.
const alertContent: 'flex-1'
alertDismiss
The alert dismiss.
const alertDismiss: 'flex-shrink-0 ml-2 p-1 rounded hover:bg-black/5 transition-colors'
floatingInputWrapper
Floating input wrapper classes (relative container).
const floatingInputWrapper: 'relative inline-block align-middle w-full'
floatingInput
The floating input.
const floatingInput: 'peer'
floatingLabel
Floating label classes (absolute positioned, animated on focus).
const floatingLabel: 'absolute top-0 left-0 right-0 w-full p-[3px] text-[11px] leading-none whitespace-nowrap text-foreground pointer-events-none transition-all duration-250 peer-hover:bg-input-background peer-focus:text-primary peer-focus:[text-shadow:0_0_var(--color-primary)] peer-focus:bg-input-background'
inputWrapper
Input wrapper classes.
const inputWrapper: 'w-full'
inputInner
The input inner.
const inputInner: 'relative'
inputLeftElement
Input left addon/icon container classes.
const inputLeftElement: 'absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-foreground-secondary'
inputRightElement
The input right element.
const inputRightElement: 'absolute inset-y-0 right-0 flex items-center pr-3'
inputClearButton
The input clear button.
const inputClearButton: 'text-foreground-secondary hover:text-foreground'
inputPadLeft
The input pad left.
const inputPadLeft: 'pl-10'
inputPadRight
The input pad right.
const inputPadRight: 'pr-10'
selectNative
Native select appearance reset classes.
const selectNative: 'appearance-none'
labelBlock
Block-level label classes.
const labelBlock: 'mb-1.5 block'
formFieldset
The form fieldset.
const formFieldset: 'space-y-4'
formFieldsetContents
The form fieldset contents.
const formFieldsetContents: 'contents'
formField
The form field.
const formField: 'space-y-1.5'
formFieldWrapper
The form field wrapper.
const formFieldWrapper: 'flex flex-col'
controlLabel
Control (checkbox/switch/radio) label wrapper classes.
const controlLabel: 'flex items-center gap-2 cursor-pointer'
controlContainer
The control container.
const controlContainer: 'flex items-center'
controlText
The control text.
const controlText: 'text-sm'
controlDisabled
The control disabled.
const controlDisabled: 'opacity-50 cursor-not-allowed'
radioGroupLabel
Radio group label classes.
const radioGroupLabel: 'text-sm font-medium mb-2 block'
accordionChevron
Accordion chevron icon classes (rotates on open).
const accordionChevron: 'h-4 w-4 shrink-0 transition-transform duration-200'
accordionTriggerBase
The accordion trigger base.
const accordionTriggerBase: 'w-full text-left'
tabsFitted
Fitted (full-width) tabs container classes.
const tabsFitted: 'w-full'
tabTriggerFitted
The tab trigger fitted.
const tabTriggerFitted: 'flex-1'
tabTriggerIcon
The tab trigger icon.
const tabTriggerIcon: 'mr-2'
paginationInteractive
Pagination interactive item (cursor pointer).
const paginationInteractive: 'cursor-pointer'
progressWrapper
Progress bar wrapper classes.
const progressWrapper: 'w-full'
progressLabelContainer
The progress label container.
const progressLabelContainer: 'flex justify-between mb-1 text-sm'
progressLabelText
The progress label text.
const progressLabelText: 'text-foreground-secondary'
progressIndeterminate
The progress indeterminate.
const progressIndeterminate: 'animate-indeterminate-progress'
tableWrapper
Table wrapper classes (scrollable container).
const tableWrapper: 'relative w-full overflow-auto'
tableLoadingOverlay
Table loading overlay classes (semi-transparent centered spinner container).
const tableLoadingOverlay: 'absolute inset-0 bg-background/50 flex items-center justify-center z-10'
tableEmptyCell
The table empty cell.
const tableEmptyCell: 'py-10 text-center text-foreground-secondary'
tableSortWrapper
The table sort wrapper.
const tableSortWrapper: 'flex items-center gap-1'
tableSortIcon
The table sort icon.
const tableSortIcon: 'ml-1'
tableRowStriped
The table row striped.
const tableRowStriped: 'bg-surface-secondary/50'
tableRowHoverable
The table row hoverable.
const tableRowHoverable: 'hover:bg-surface-secondary'
tableRowClickable
The table row clickable.
const tableRowClickable: 'cursor-pointer'
tableHeadSortable
The table head sortable.
const tableHeadSortable: 'cursor-pointer select-none hover:bg-surface-secondary'
tableBordered
The table bordered.
const tableBordered: 'border'
toastIconWrapper
Toast icon wrapper classes.
const toastIconWrapper: 'flex-shrink-0'
toastContentWrapper
The toast content wrapper.
const toastContentWrapper: 'flex-1 gap-1'
dropdownTrigger
Dropdown trigger wrapper classes.
const dropdownTrigger: 'inline-block'
dropdownItemIcon
The dropdown item icon.
const dropdownItemIcon: 'mr-2 flex-shrink-0'
dropdownItemLabel
The dropdown item label.
const dropdownItemLabel: 'flex-1'
dropdownItemShortcut
The dropdown item shortcut.
const dropdownItemShortcut: 'ml-auto text-xs text-foreground-tertiary'
dropdownItemDisabled
The dropdown item disabled.
const dropdownItemDisabled: 'opacity-50 cursor-not-allowed'
tooltipTrigger
Tooltip trigger wrapper classes.
const tooltipTrigger: 'inline-block'
avatarInitials
Avatar initials text classes.
const avatarInitials: 'text-sm font-medium'
avatarFallbackIcon
The avatar fallback icon.
const avatarFallbackIcon: 'h-1/2 w-1/2'
avatarSquare
The avatar square.
const avatarSquare: 'rounded-md'
badgeSquare
Badge with square (rounded-md) corners.
const badgeSquare: 'rounded-md'
skeletonTextContainer
Skeleton text placeholder container (vertical spacing).
const skeletonTextContainer: 'space-y-2'
skeletonCircle
The skeleton circle.
const skeletonCircle: 'rounded-full'
skeletonWave
Skeleton wave animation classes (shimmer gradient effect).
const skeletonWave: 'animate-shimmer bg-gradient-to-r from-surface-secondary via-surface to-surface-secondary bg-[length:200%_100%]'
skeletonNone
The skeleton none.
const skeletonNone: 'animate-none'
displayBlock
Display block utility class.
const displayBlock: 'block'
displayInlineBlock
The display inline block.
const displayInlineBlock: 'inline-block'
displayContents
The display contents.
const displayContents: 'contents'
mxAuto
Horizontal auto-margin centering utility class.
const mxAuto: 'mx-auto'
cursorPointer
The cursor pointer.
const cursorPointer: 'cursor-pointer'
roundedFull
The rounded full.
const roundedFull: 'rounded-full'
textRight
Text right-alignment utility class.
const textRight: 'text-right'
logoText
Logo text classes (tracked, 26px).
const logoText: 'text-[26px] leading-[26px] tracking-[-1px]'
logoIcon
Tailwind class for the logo icon color.
const logoIcon: 'text-primary'
headerFixed
Header container classes (full-width). Position is determined by the consuming layout, not forced here.
const headerFixed: 'top-0 left-0 right-0 w-full'
headerInner
The header inner.
const headerInner: 'w-full max-w-[1280px] mx-auto h-10 p-[5px]'
authHeaderInner
Auth page header inner container classes.
const authHeaderInner: 'w-full h-10 p-[5px]'
authPageBody
The auth page body.
const authPageBody: 'flex flex-col flex-1 items-center justify-center px-4 pb-[30px]'
authFormWrapper
The auth form wrapper.
const authFormWrapper: 'w-max min-w-[330px] max-w-[90vw] py-[30px] mx-auto text-center'
authField
The auth field.
const authField: 'mb-[15px]'
authAfterSubmit
The auth after submit.
const authAfterSubmit: 'mt-[15px]'
authButtonRow
Auth button row (flex container).
const authButtonRow: 'flex'
authBackButton
The auth back button.
const authBackButton: 'w-[45px] mr-[10px]'
authSubmitButton
The auth submit button.
const authSubmitButton: 'grow whitespace-nowrap'
authLoginSignup
The auth login signup.
const authLoginSignup: 'grow whitespace-nowrap mr-[10px]'
authLoginSubmit
The auth login submit.
const authLoginSubmit: 'grow whitespace-nowrap'
authLoginSubmitFull
The auth login submit full.
const authLoginSubmitFull: 'w-full whitespace-nowrap'
authHidden
The auth hidden.
const authHidden: 'hidden'
authArrowIcon
The auth arrow icon.
const authArrowIcon: 'ml-[2.5px]'
appLayout
Main app layout classes (min-height, horizontal/bottom padding). No top
padding: the shell renders the header IN FLOW (AppHeader's fixed default
contributes no position utility — headerFixed is offsets only), so a
top pad cleared a header that never overlapped the content and every page
opened with a phantom 55px gap. Apps with a genuinely fixed/sticky header
add their own top padding.
const appLayout: 'min-h-screen px-[15px] pb-[55px]'
flex1
Flex grow utility class (flex-1).
const flex1: 'flex-1'
oauthProviderLabel
OAuth provider label (fallback when no icon).
const oauthProviderLabel: 'text-sm font-medium'
surfaceSecondary
Secondary surface background utility class.
const surfaceSecondary: 'bg-surface-secondary'
textPrimary
Primary text color utility class.
const textPrimary: 'text-primary'
bgPrimaryContainer
Primary-tinted container surface; pair with textOnPrimaryContainer.
const bgPrimaryContainer: 'bg-primary-container'
textOnPrimaryContainer
Readable text on bgPrimaryContainer.
const textOnPrimaryContainer: 'text-on-primary-container'
textSuccess
The text success.
const textSuccess: 'text-success'
textWarning
The text warning.
const textWarning: 'text-warning'
borderB
Bottom border with theme border color.
const borderB: 'border-b border-border'
borderT
Tailwind classes for a top border with the theme border color.
const borderT: 'border-t border-border'
borderR
Tailwind classes for a right border with the theme border color.
const borderR: 'border-r border-border'
borderL
Tailwind classes for a left border with the theme border color.
const borderL: 'border-l border-border'
borderAll
The border all.
const borderAll: 'border border-border'
borderBPrimary
The border b primary.
const borderBPrimary: 'border-b-2 border-b-primary'
bgErrorSubtle
Subtle error background utility class.
const bgErrorSubtle: 'bg-error-light'
bgBorder
Tailwind class for a background matching the theme border color.
const bgBorder: 'bg-border'
pageShell
PageShell root — flex row filling the screen.
Use this pattern (or an equivalent flex row on the page root) whenever a
persistent sidebar and <main> are siblings. A full-height sticky sidebar
in block flow (no flex row on the parent) still occupies vertical space,
so <main> with only ml-[…] starts below the viewport — content appears
under fixed headers / near the bottom. Prefer pageShell + pageShellSidebar,
or a fixed inset-y-0 sidebar plus horizontal offset on main.
const pageShell: 'flex h-screen overflow-hidden'
pageShellSidebar
PageShell sidebar — fixed-width left panel.
const pageShellSidebar: 'w-60 flex-shrink-0 border-r border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 overflow-y-auto transition-all duration-300'
pageShellSidebarCollapsed
PageShell sidebar collapsed modifier — narrow width.
const pageShellSidebarCollapsed: 'w-16'
pageShellTopbar
PageShell top bar — horizontal strip above content.
const pageShellTopbar: 'h-14 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 flex items-center px-4 gap-3'
pageShellContent
PageShell main content area.
const pageShellContent: 'flex-1 overflow-y-auto'
pageShellSidebarToggle
PageShell sidebar collapse/expand toggle button.
const pageShellSidebarToggle: 'p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors'
emptyState
EmptyState root — centered column layout.
const emptyState: 'flex flex-col items-center justify-center py-16 px-4 text-center'
emptyStateIcon
EmptyState icon wrapper.
const emptyStateIcon: 'mb-4 text-gray-400 dark:text-gray-500'
emptyStateTitle
EmptyState heading.
const emptyStateTitle: 'text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2'
emptyStateDescription
EmptyState description.
const emptyStateDescription: 'text-sm text-gray-500 dark:text-gray-400 mb-6 max-w-md'
emptyStateAction
EmptyState action slot.
const emptyStateAction: ''
pageHeader
PageHeader root container.
const pageHeader: 'mb-6'
pageHeaderTitle
PageHeader title.
const pageHeaderTitle: 'text-2xl font-bold text-gray-900 dark:text-gray-100'
pageHeaderDescription
PageHeader description.
const pageHeaderDescription: 'mt-1 text-sm text-gray-500 dark:text-gray-400'
pageHeaderActions
PageHeader action buttons container.
const pageHeaderActions: 'flex gap-2 mt-4 sm:mt-0'
pageHeaderBreadcrumbs
PageHeader breadcrumb trail.
const pageHeaderBreadcrumbs: 'flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mb-2'
pageHeaderBreadcrumbItem
PageHeader breadcrumb item.
const pageHeaderBreadcrumbItem: 'hover:text-gray-700 dark:hover:text-gray-200'
pageHeaderBreadcrumbSeparator
PageHeader breadcrumb separator.
const pageHeaderBreadcrumbSeparator: 'mx-1'
searchField
SearchBox field: a bordered bar; the ring appears while anything inside has focus.
const searchField: 'relative flex h-11 w-full items-center gap-2 rounded-xl border border-border bg-input-background px-3 text-foreground transition-colors focus-within:border-primary focus-within:shadow-[0_0_0_3px_rgba(64,112,224,0.18)]'
searchFieldIcon
SearchBox leading icon slot.
const searchFieldIcon: 'flex flex-none items-center text-foreground-secondary'
searchFieldInput
SearchBox input: borderless, fills the bar, left-aligned, visible placeholder.
const searchFieldInput: 'h-full min-w-0 flex-1 border-0 bg-transparent p-0 text-left text-[15px] leading-none text-foreground placeholder:text-foreground-secondary placeholder:opacity-100 focus:outline-none focus-visible:outline-none [&::-webkit-search-cancel-button]:hidden'
searchFieldActions
SearchBox trailing slot.
const searchFieldActions: 'flex flex-none items-center gap-1.5'
searchFieldClear
SearchBox icon-only clear button.
const searchFieldClear: 'flex h-7 w-7 items-center justify-center rounded-md text-foreground-secondary hover:bg-surface-secondary hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary'
searchStatusRow
SearchBox status row: chips left, count and tips right.
const searchStatusRow: 'mt-2 flex min-h-6 flex-wrap items-center gap-1.5 text-[13px] text-foreground-secondary'
searchTips
SearchBox syntax tips line.
const searchTips: 'mt-1 text-[13px] leading-relaxed text-foreground-secondary'
kbd
A key cap.
const kbd: 'inline-flex h-5 min-w-5 items-center justify-center rounded-md border border-border px-1.5 font-mono text-[11px] leading-none text-foreground-secondary'
filterChip
A removable filter chip.
const filterChip: 'inline-flex cursor-pointer items-center gap-1 rounded-full border border-primary/40 bg-primary/10 px-2.5 py-0.5 text-xs font-medium text-foreground transition-colors hover:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary'
filterChipNegated
Added to a filter chip that excludes its value.
const filterChipNegated: 'border-warning/50 bg-warning/10'
filterChipRemove
The × inside a filter chip.
const filterChipRemove: 'text-foreground-secondary'
resultList
SearchResults list container.
const resultList: 'm-0 mt-4 list-none divide-y divide-border overflow-hidden rounded-xl border border-border bg-surface p-0'
resultItem
One result row.
const resultItem: 'relative flex cursor-pointer items-start gap-3 px-4 py-3 transition-colors hover:bg-surface-secondary'
resultItemActive
The active result row.
const resultItemActive: 'bg-surface-secondary before:absolute before:bottom-2 before:left-0 before:top-2 before:w-0.5 before:rounded-full before:bg-primary'
resultBody
A result row's body: the link that fills the row.
const resultBody: 'block min-w-0 flex-1 no-underline'
resultTitle
A result row's title.
const resultTitle: 'font-semibold text-foreground'
resultTitleMono
Added to a result title that is code.
const resultTitleMono: 'font-mono text-[0.95em]'
resultMeta
A result row's meta line.
const resultMeta: 'text-xs text-foreground-secondary'
resultDescription
A result row's description.
const resultDescription: 'mt-0.5 line-clamp-2 text-[13.5px] leading-snug text-foreground-secondary'
resultMark
A matched term inside a result.
const resultMark: 'rounded-[3px] bg-primary/20 px-0.5 text-inherit'
resultAction
A result row's trailing action slot.
const resultAction: 'flex-none self-center'
resultEmpty
A results empty state.
const resultEmpty: 'py-10 text-center text-[13.5px] text-foreground-secondary'
container
Container component classes.
const container: (
props?:
| ({ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' | '2xl' | undefined } & { class?: string })
| undefined,
) => string
flex
Flex layout classes.
const flex: (
props?:
| ({
direction?: 'row' | 'row-reverse' | 'col' | 'col-reverse' | undefined
align?: 'center' | 'start' | 'end' | 'stretch' | 'baseline' | undefined
justify?: 'center' | 'start' | 'end' | 'between' | 'around' | 'evenly' | undefined
wrap?: 'wrap' | 'nowrap' | 'wrap-reverse' | undefined
gap?: 'sm' | 'md' | 'lg' | 'xl' | 'none' | 'xs' | undefined
} & { class?: string })
| undefined,
) => string
grid
Grid layout classes.
These cols values are the FIXED (non-responsive) column classes — a grid
that is cols-wide at every viewport width. The ClassMap grid resolver
(see classMap.ts) uses these only for the opt-out (responsive: false)
and single-column paths; by DEFAULT it emits the mobile-first ramp in
gridResponsiveCols instead.
const grid: (
props?:
| ({
cols?: 1 | 2 | 3 | 4 | 5 | 6 | 12 | undefined
gap?: 'sm' | 'md' | 'lg' | 'xl' | 'none' | 'xs' | undefined
} & { class?: string })
| undefined,
) => string
gridResponsiveCols
Mobile-first responsive column ramps for cm.grid({ cols }).
Each value is a COMPLETE, literal Tailwind class string (never built by
template interpolation) so Tailwind's scanner picks it up from this
package's compiled dist (see the @source note in index.ts), and
base.css safelists the same set. A multi-column grid starts at one
column on phones and steps up to the requested cols at sm/lg/xl,
so KPI / listing / card grids COLLAPSE on mobile instead of overflowing.
cols: 1 stays single-column. The ClassMap grid resolver uses this as
the DEFAULT for cols >= 2; pass responsive: false there for the fixed
grid-cols-N from the grid CVA above. Every emitted token is a
real Tailwind utility (grid-cols-1..6, grid-cols-12, and the
sm:/lg:/xl: variants of each).
const gridResponsiveCols: Record<number, string>
stack
Stack layout classes (vertical flexbox).
const stack: (
props?:
| ({
align?: 'center' | 'start' | 'end' | 'stretch' | undefined
gap?: 'sm' | 'md' | 'lg' | 'xl' | 'none' | 'xs' | undefined
} & { class?: string })
| undefined,
) => string
hstack
HStack layout classes (horizontal flexbox).
const hstack: (
props?:
| ({
justify?: 'center' | 'start' | 'end' | 'between' | undefined
gap?: 'sm' | 'md' | 'lg' | 'xl' | 'none' | 'xs' | undefined
} & { class?: string })
| undefined,
) => string
center
Center layout classes.
const center: 'flex items-center justify-center'
srOnly
Screen reader only classes.
const srOnly: 'sr-only'
notSrOnly
The not sr only.
const notSrOnly: 'not-sr-only'
cn
Merges class names, filtering out falsy values. Supports strings, numbers, conditional objects, and nested arrays.
When a class merger is registered via setClassMerger (e.g. the
Tailwind bond registers tailwind-merge), conflicting utilities such as two
gap-* classes are resolved by it; otherwise the joined string is returned
as-is.
const cn: (...classes: ClassValue[]) => string
classes— Class values to merge (strings, booleans, objects, arrays).
Returns: A single space-separated class string.
cva
Creates a class variance authority (CVA) function for component variants. Given a base class and variant configuration, returns a function that resolves the final class string based on selected variants.
const cva: <T extends Record<string, Record<string, string>>>(
base: string,
config?: CVAConfig<T>,
) => (props?: { [K in keyof T]?: keyof T[K] } & { class?: string }) => string
base— The base class that is always included.config— Variant definitions, defaults, and compound variants.
Returns: A function that accepts variant props and returns the resolved class string.
Core Interface
Implements @molecule/app-ui interface.
Injection Notes
Requirements
Peer dependencies:
@molecule/app-styling^1.0.1@molecule/app-ui^1.2.0
Runtime Dependencies
@molecule/app-styling@molecule/app-ui
Theming / where colors come from: the class strings here use semantic tokens
(bg-primary, text-primary, bg-surface, …), NOT literal palette classes
(bg-blue-600). Those tokens are defined in this package's base.css
@theme block, where the ~15 CORE colors read
--color-primary: var(--mol-color-primary, <default>) (from the
@molecule/app-theme bond) and every other token (hover, container, Material
on-*, surface tiers) is AUTO-DERIVED from the cores via color-mix() — so
changing one core recolors all its shades.
To recolor a scaffolded app: edit app/src/theme.css (the --color-*
variables, :root = light, [data-mol-mode='dark'] = dark) when it exists —
it overrides everything here. Only when there is no theme.css do the
--mol-color-* from the theme bond drive the palette. NEVER hardcode a literal
Tailwind color class in a component — it breaks theming and the safelist.