@molecule/app-ui-tailwind
v1.2.1
Published
Tailwind CSS UI components for molecule.dev
Readme
@molecule/app-ui-tailwind
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.
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.).
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-uiAPI
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 {@link cn}.
type ClassValue =
| string
| number
| boolean
| undefined
| null
| ClassValue[]
| Record<string, boolean | undefined | null>Constants
accordion
Accordion component classes.
const accordion: 'w-full'accordionChevron
Accordion chevron icon classes (rotates on open).
const accordionChevron: 'h-4 w-4 shrink-0 transition-transform duration-200'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'accordionItem
The accordion item.
const accordionItem: 'border-b'accordionTrigger
The accordion trigger.
const accordionTrigger: (
props?: ({ variant?: 'default' | 'bordered' | undefined } & { class?: string }) | undefined,
) => stringaccordionTriggerBase
The accordion trigger base.
const accordionTriggerBase: 'w-full text-left'actionSheet
Action sheet: bottom-anchored panel for pickers and action menus.
const actionSheet: 'fixed bottom-0 left-0 right-0 z-[1300] 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'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,
) => stringalertContent
The alert content.
const alertContent: 'flex-1'alertDescription
The alert description.
const alertDescription: 'text-sm [&_p]:leading-relaxed'alertDismiss
The alert dismiss.
const alertDismiss: 'flex-shrink-0 ml-2 p-1 rounded hover:bg-black/5 transition-colors'alertIconWrapper
Alert icon wrapper classes.
const alertIconWrapper: 'flex-shrink-0'alertTitle
The alert title.
const alertTitle: 'mb-1 font-medium leading-none tracking-tight'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]'authAfterSubmit
The auth after submit.
const authAfterSubmit: 'mt-[15px]'authArrowIcon
The auth arrow icon.
const authArrowIcon: 'ml-[2.5px]'authBackButton
The auth back button.
const authBackButton: 'w-[45px] mr-[10px]'authButtonRow
Auth button row (flex container).
const authButtonRow: 'flex'authField
The auth field.
const authField: 'mb-[15px]'authFormError
Auth form error message classes.
const authFormError: 'text-error mt-[15px]'authFormWrapper
The auth form wrapper.
const authFormWrapper: 'w-max min-w-[330px] max-w-[90vw] py-[30px] mx-auto text-center'authHeaderInner
Auth page header inner container classes.
const authHeaderInner: 'w-full h-10 p-[5px]'authHidden
The auth hidden.
const authHidden: 'hidden'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'authPageBody
The auth page body.
const authPageBody: 'flex flex-col flex-1 items-center justify-center px-4 pb-[30px]'authSubmitButton
The auth submit button.
const authSubmitButton: 'grow whitespace-nowrap'avatar
Avatar component classes.
const avatar: (
props?: ({ size?: 'sm' | 'md' | 'lg' | 'xl' | undefined } & { class?: string }) | undefined,
) => stringavatarFallback
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'avatarFallbackIcon
The avatar fallback icon.
const avatarFallbackIcon: 'h-1/2 w-1/2'avatarImage
The avatar image.
const avatarImage: 'aspect-square h-full w-full object-cover'avatarInitials
Avatar initials text classes.
const avatarInitials: 'text-sm font-medium'avatarSquare
The avatar square.
const avatarSquare: 'rounded-md'badge
Badge component classes with variant support.
const badge: (
props?:
| ({
variant?: 'default' | 'secondary' | 'success' | 'outline' | 'warning' | 'error' | undefined
} & { class?: string })
| undefined,
) => stringbadgeSquare
Badge with square (rounded-md) corners.
const badgeSquare: 'rounded-md'bgBorder
Tailwind class for a background matching the theme border color.
const bgBorder: 'bg-border'bgErrorSubtle
Subtle error background utility class.
const bgErrorSubtle: 'bg-error-light'bgPrimaryContainer
Primary-tinted container surface; pair with {@link textOnPrimaryContainer}.
const bgPrimaryContainer: 'bg-primary-container'borderAll
The border all.
const borderAll: 'border border-border'borderB
Bottom border with theme border color.
const borderB: 'border-b border-border'borderBPrimary
The border b primary.
const borderBPrimary: 'border-b-2 border-b-primary'borderR
Tailwind classes for a right border with the theme border color.
const borderR: 'border-r border-border'borderT
Tailwind classes for a top border with the theme border color.
const borderT: 'border-t border-border'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,
) => stringbuttonIconLeft
Button left icon spacing classes.
const buttonIconLeft: 'mr-2'buttonIconRight
The button icon right.
const buttonIconRight: 'ml-2'buttonSpinner
The button spinner.
const buttonSpinner: 'mr-2'card
Card component classes.
const card: (
props?:
| ({ variant?: 'default' | 'outline' | 'ghost' | 'elevated' | undefined } & { class?: string })
| undefined,
) => stringcardContent
The card content.
const cardContent: 'p-6 pt-0'cardDescription
The card description.
const cardDescription: 'text-sm text-foreground-secondary'cardFooter
The card footer.
const cardFooter: 'flex items-center p-6 pt-0'cardHeader
The card header.
const cardHeader: 'flex flex-col space-y-1.5 p-6'cardInteractive
Interactive card classes (hover shadow effect).
const cardInteractive: 'cursor-pointer hover:shadow-md transition-shadow'cardTitle
The card title.
const cardTitle: 'text-lg font-semibold leading-none tracking-tight'center
Center layout classes.
const center: 'flex items-center justify-center'checkbox
Checkbox component classes.
const checkbox: (
props?: ({ variant?: 'default' | 'error' | undefined } & { class?: string }) | undefined,
) => stringclassMap
Tailwind CSS UIClassMap implementation mapping abstract UI tokens to Tailwind class strings.
const classMap: UIClassMapcn
Merges class names, filtering out falsy values. Supports strings, numbers, conditional objects, and nested arrays.
When a class merger is registered via {@link 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[]) => stringcontainer
Container component classes.
const container: (
props?:
| ({ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' | '2xl' | undefined } & { class?: string })
| undefined,
) => stringcontrolContainer
The control container.
const controlContainer: 'flex items-center'controlDisabled
The control disabled.
const controlDisabled: 'opacity-50 cursor-not-allowed'controlLabel
Control (checkbox/switch/radio) label wrapper classes.
const controlLabel: 'flex items-center gap-2 cursor-pointer'controlText
The control text.
const controlText: 'text-sm'cursorPointer
The cursor pointer.
const cursorPointer: 'cursor-pointer'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 }) => stringdialogBody
The dialog body.
const dialogBody: 'flex-1 min-h-0 overflow-y-auto px-6 py-6'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'dialogContent
The dialog content.
const dialogContent: (
props?:
({ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' | undefined } & { class?: string }) | undefined,
) => stringdialogDescription
The dialog description.
const dialogDescription: 'text-sm text-foreground-secondary'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'dialogHeader
The dialog header.
const dialogHeader: 'flex-shrink-0 flex items-center justify-between px-6 py-2'dialogOverlay
Modal/Dialog overlay classes — the TINT only. The backdrop blur lives on {@link 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-[1300] bg-overlay pointer-events-none'dialogTitle
The dialog title.
const dialogTitle: 'text-xl font-semibold leading-tight tracking-tight'dialogWrapper
The dialog wrapper. Carries the backdrop blur (see {@link 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-[1301] flex items-center justify-center px-4 py-10 backdrop-blur-[2px]'displayBlock
Display block utility class.
const displayBlock: 'block'displayContents
The display contents.
const displayContents: 'contents'displayInlineBlock
The display inline block.
const displayInlineBlock: 'inline-block'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'dropdownItemDisabled
The dropdown item disabled.
const dropdownItemDisabled: 'opacity-50 cursor-not-allowed'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'dropdownLabel
The dropdown label.
const dropdownLabel: 'px-2 py-1.5 text-sm font-semibold'dropdownSeparator
The dropdown separator.
const dropdownSeparator: '-mx-1 my-1 h-px bg-border'dropdownTrigger
Dropdown trigger wrapper classes.
const dropdownTrigger: 'inline-block'emptyState
EmptyState root — centered column layout.
const emptyState: 'flex flex-col items-center justify-center py-16 px-4 text-center'emptyStateAction
EmptyState action slot.
const emptyStateAction: ''emptyStateDescription
EmptyState description.
const emptyStateDescription: 'text-sm text-gray-500 dark:text-gray-400 mb-6 max-w-md'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'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,
) => stringflex1
Flex grow utility class (flex-1).
const flex1: 'flex-1'floatingInput
The floating input.
const floatingInput: 'peer'floatingInputWrapper
Floating input wrapper classes (relative container).
const floatingInputWrapper: 'relative inline-block align-middle w-full'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'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'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'footerLink
Footer link classes.
const footerLink: 'p-[5px] text-[12px] text-foreground-secondary hover:text-foreground no-underline'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'formError
Form error message classes.
const formError: 'text-sm text-error mt-1'formField
The form field.
const formField: 'space-y-1.5'formFieldset
The form fieldset.
const formFieldset: 'space-y-4'formFieldsetContents
The form fieldset contents.
const formFieldsetContents: 'contents'formFieldWrapper
The form field wrapper.
const formFieldWrapper: 'flex flex-col'formHint
Form hint message classes.
const formHint: 'text-sm text-foreground-secondary mt-1'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
{@link 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,
) => stringgridResponsiveCols
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 {@link 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>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]'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,
) => stringiconMd
Tailwind classes for medium icons (20x20).
const iconMd: 'h-5 w-5'iconSm
Tailwind classes for small icons (16x16).
const iconSm: 'h-4 w-4'iconXs
Icon extra-small size classes (12×12).
const iconXs: 'h-3 w-3'input
Input component classes.
const input: (
props?:
| ({
variant?: 'default' | 'error' | undefined
size?: 'sm' | 'md' | 'lg' | undefined
align?: 'left' | 'center' | undefined
} & { class?: string })
| undefined,
) => stringinputClearButton
The input clear button.
const inputClearButton: 'text-foreground-secondary hover:text-foreground'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'inputPadLeft
The input pad left.
const inputPadLeft: 'pl-10'inputPadRight
The input pad right.
const inputPadRight: 'pr-10'inputRightElement
The input right element.
const inputRightElement: 'absolute inset-y-0 right-0 flex items-center pr-3'inputWrapper
Input wrapper classes.
const inputWrapper: 'w-full'label
Label component classes.
const label: (
props?: ({ required?: 'true' | 'false' | undefined } & { class?: string }) | undefined,
) => stringlabelBlock
Block-level label classes.
const labelBlock: 'mb-1.5 block'languageActive
Language option (active/selected) classes. Same coarse-pointer touch floor as {@link 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'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'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'logoIcon
Tailwind class for the logo icon color.
const logoIcon: 'text-primary'logoText
Logo text classes (tracked, 26px).
const logoText: 'text-[26px] leading-[26px] tracking-[-1px]'mxAuto
Horizontal auto-margin centering utility class.
const mxAuto: 'mx-auto'notSrOnly
The not sr only.
const notSrOnly: 'not-sr-only'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'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'oauthButtonIcon
OAuth provider button icon classes.
const oauthButtonIcon: 'opacity-75 group-hover:opacity-100 text-foreground transition-opacity'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'oauthProviderLabel
OAuth provider label (fallback when no icon).
const oauthProviderLabel: 'text-sm font-medium'pageHeader
PageHeader root container.
const pageHeader: 'mb-6'pageHeaderActions
PageHeader action buttons container.
const pageHeaderActions: 'flex gap-2 mt-4 sm:mt-0'pageHeaderBreadcrumbItem
PageHeader breadcrumb item.
const pageHeaderBreadcrumbItem: 'hover:text-gray-700 dark:hover:text-gray-200'pageHeaderBreadcrumbs
PageHeader breadcrumb trail.
const pageHeaderBreadcrumbs: 'flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mb-2'pageHeaderBreadcrumbSeparator
PageHeader breadcrumb separator.
const pageHeaderBreadcrumbSeparator: 'mx-1'pageHeaderDescription
PageHeader description.
const pageHeaderDescription: 'mt-1 text-sm text-gray-500 dark:text-gray-400'pageHeaderTitle
PageHeader title.
const pageHeaderTitle: 'text-2xl font-bold text-gray-900 dark:text-gray-100'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'pageShellContent
PageShell main content area.
const pageShellContent: 'flex-1 overflow-y-auto'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'pageShellSidebarToggle
PageShell sidebar collapse/expand toggle button.
const pageShellSidebarToggle: 'p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors'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'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'paginationEllipsis
The pagination ellipsis.
const paginationEllipsis: 'flex h-9 w-9 items-center justify-center'paginationInteractive
Pagination interactive item (cursor pointer).
const paginationInteractive: 'cursor-pointer'paginationItem
The pagination item.
const paginationItem: (
props?:
| ({ variant?: 'default' | 'active' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => stringpaginationLink
The pagination link.
const paginationLink: 'cursor-pointer'paginationNext
The pagination next.
const paginationNext: 'gap-1 pr-2.5'paginationPrevious
The pagination previous.
const paginationPrevious: 'gap-1 pl-2.5'progress
Progress component classes.
const progress: 'relative h-2 w-full overflow-hidden rounded-full bg-surface-secondary'progressIndeterminate
The progress indeterminate.
const progressIndeterminate: 'animate-indeterminate-progress'progressIndicator
The progress indicator.
const progressIndicator: 'h-full w-full flex-1 bg-primary transition-all'progressLabelContainer
The progress label container.
const progressLabelContainer: 'flex justify-between mb-1 text-sm'progressLabelText
The progress label text.
const progressLabelText: 'text-foreground-secondary'progressWrapper
Progress bar wrapper classes.
const progressWrapper: 'w-full'radio
Radio component classes.
const radio: (
props?: ({ variant?: 'default' | 'error' | undefined } & { class?: string }) | undefined,
) => stringradioGroupLabel
Radio group label classes.
const radioGroupLabel: 'text-sm font-medium mb-2 block'roundedFull
The rounded full.
const roundedFull: 'rounded-full'select
Select component classes.
const select: (
props?:
| ({ variant?: 'default' | 'error' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => stringselectNative
Native select appearance reset classes.
const selectNative: 'appearance-none'separator
Separator component classes with horizontal/vertical orientation.
const separator: (
props?:
({ orientation?: 'horizontal' | 'vertical' | undefined } & { class?: string }) | undefined,
) => stringskeleton
Skeleton component classes.
const skeleton: 'animate-pulse rounded-md bg-surface-secondary'skeletonCircle
The skeleton circle.
const skeletonCircle: 'rounded-full'skeletonNone
The skeleton none.
const skeletonNone: 'animate-none'skeletonTextContainer
Skeleton text placeholder container (vertical spacing).
const skeletonTextContainer: 'space-y-2'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%]'spinner
Spinner component classes with size variants.
const spinner: (
props?: ({ size?: 'sm' | 'md' | 'lg' | 'xl' | undefined } & { class?: string }) | undefined,
) => stringsrOnly
Screen reader only classes.
const srOnly: 'sr-only'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,
) => stringsurfaceSecondary
Secondary surface background utility class.
const surfaceSecondary: 'bg-surface-secondary'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,
) => stringswitchThumb
Switch thumb (the sliding circle) classes.
const switchThumb: (
props?: ({ size?: 'sm' | 'md' | 'lg' | undefined } & { class?: string }) | undefined,
) => stringtable
Table root classes.
const table: 'w-full caption-bottom text-sm'tableBody
The table body.
const tableBody: '[&_tr:last-child]:border-0'tableBordered
The table bordered.
const tableBordered: 'border'tableCaption
The table caption.
const tableCaption: 'mt-4 text-sm text-foreground-secondary'tableCell
The table cell.
const tableCell: 'p-4 align-middle [&:has([role=checkbox])]:pr-0'tableEmptyCell
The table empty cell.
const tableEmptyCell: 'py-10 text-center text-foreground-secondary'tableFooter
The table footer.
const tableFooter: 'border-t bg-surface-secondary font-medium [&>tr]:last:border-b-0'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'tableHeader
The table header.
const tableHeader: '[&_tr]:border-b'tableHeadSortable
The table head sortable.
const tableHeadSortable: 'cursor-pointer select-none hover:bg-surface-secondary'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'tableRow
Table row classes.
const tableRow: 'border-b transition-colors hover:bg-surface-secondary data-[state=selected]:bg-surface-secondary'tableRowClickable
The table row clickable.
const tableRowClickable: 'cursor-pointer'tableRowHoverable
The table row hoverable.
const tableRowHoverable: 'hover:bg-surface-secondary'tableRowStriped
The table row striped.
const tableRowStriped: 'bg-surface-secondary/50'tableSortIcon
The table sort icon.
const tableSortIcon: 'ml-1'tableSortWrapper
The table sort wrapper.
const tableSortWrapper: 'flex items-center gap-1'tableWrapper
Table wrapper classes (scrollable container).
const tableWrapper: 'relative w-full overflow-auto'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,
) => stringtabsFitted
Fitted (full-width) tabs container classes.
const tabsFitted: 'w-full'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,
) => stringtabsTrigger
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,
) => stringtabTriggerFitted
The tab trigger fitted.
const tabTriggerFitted: 'flex-1'tabTriggerIcon
The tab trigger icon.
const tabTriggerIcon: 'mr-2'textarea
Textarea component classes.
const textarea: (
props?:
| ({ variant?: 'default' | 'error' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => stringtextOnPrimaryContainer
Readable text on {@link bgPrimaryContainer}.
const textOnPrimaryContainer: 'text-on-primary-container'textPrimary
Primary text color utility class.
const textPrimary: 'text-primary'textRight
Text right-alignment utility class.
const textRight: 'text-right'textSuccess
The text success.
const textSuccess: 'text-success'textWarning
The text warning.
const textWarning: 'text-warning'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'toast
Tailwind toast notification variant classes (default, success, error, warning).
const toast: (
props?:
| ({ variant?: 'default' | 'success' | 'warning' | 'error' | 'info' | undefined } & {
class?: string
})
| undefined,
) => stringtoastAction
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'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'toastContentWrapper
The toast content wrapper.
const toastContentWrapper: 'flex-1 gap-1'toastDescription
The toast description.
const toastDescription: 'text-sm opacity-90'toastIconWrapper
Toast icon wrapper classes.
const toastIconWrapper: 'flex-shrink-0'toastTitle
The toast title.
const toastTitle: 'text-sm font-semibold'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]'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'tooltipTrigger
Tooltip trigger wrapper classes.
const tooltipTrigger: 'inline-block'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.
