← All @molecule/* packages · App templates
@molecule/app-ui-tailwindProvider bond · ui · App (browser) · v1.1.0 · Apache-2.0
Tailwind CSS UI components for molecule.dev
npm install @molecule/app-ui-tailwindnpm · Source on GitHub · Implements @molecule/app-ui
@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
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.).
// 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' })
provider
npm install @molecule/app-ui-tailwind @molecule/app-styling @molecule/app-ui
CVAConfigConfiguration 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
}
>
}
ClassValueClass name value types accepted by {@link cn}.
type ClassValue =
| string
| number
| boolean
| undefined
| null
| ClassValue[]
| Record<string, boolean | undefined | null>
accordionAccordion component classes.
const accordion: 'w-full'
accordionChevronAccordion chevron icon classes (rotates on open).
const accordionChevron: 'h-4 w-4 shrink-0 transition-transform duration-200'
accordionContentAccordion 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'
accordionContentInnerThe accordion content inner.
const accordionContentInner: 'pb-4 pt-0'
accordionItemThe accordion item.
const accordionItem: 'border-b'
accordionTriggerThe accordion trigger.
const accordionTrigger: (
props?: ({ variant?: 'default' | 'bordered' | undefined } & { class?: string }) | undefined,
) => string
accordionTriggerBaseThe accordion trigger base.
const accordionTriggerBase: 'w-full text-left'
actionSheetAction 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'
actionSheetHeaderAction sheet header with bottom border.
const actionSheetHeader: 'flex-shrink-0 px-4 py-3 border-b border-border'
alertAlert component classes with variant support (default, info, success, warning, error).
const alert: (
props?:
| ({ variant?: 'default' | 'success' | 'warning' | 'error' | 'info' | undefined } & {
class?: string
})
| undefined,
) => string
alertContentThe alert content.
const alertContent: 'flex-1'
alertDescriptionThe alert description.
const alertDescription: 'text-sm [&_p]:leading-relaxed'
alertDismissThe alert dismiss.
const alertDismiss: 'flex-shrink-0 ml-2 p-1 rounded hover:bg-black/5 transition-colors'
alertIconWrapperAlert icon wrapper classes.
const alertIconWrapper: 'flex-shrink-0'
alertTitleThe alert title.
const alertTitle: 'mb-1 font-medium leading-none tracking-tight'
appLayoutMain app layout classes (min-height, padding for fixed header/footer).
const appLayout: 'min-h-screen pt-[55px] px-[15px] pb-[55px]'
authAfterSubmitThe auth after submit.
const authAfterSubmit: 'mt-[15px]'
authArrowIconThe auth arrow icon.
const authArrowIcon: 'ml-[2.5px]'
authBackButtonThe auth back button.
const authBackButton: 'w-[45px] mr-[10px]'
authButtonRowAuth button row (flex container).
const authButtonRow: 'flex'
authFieldThe auth field.
const authField: 'mb-[15px]'
authFormErrorAuth form error message classes.
const authFormError: 'text-error mt-[15px]'
authFormWrapperThe auth form wrapper.
const authFormWrapper: 'w-max min-w-[330px] max-w-[90vw] py-[30px] mx-auto text-center'
authHeaderInnerAuth page header inner container classes.
const authHeaderInner: 'w-full h-10 p-[5px]'
authHiddenThe auth hidden.
const authHidden: 'hidden'
authLoginSignupThe auth login signup.
const authLoginSignup: 'grow whitespace-nowrap mr-[10px]'
authLoginSubmitThe auth login submit.
const authLoginSubmit: 'grow whitespace-nowrap'
authLoginSubmitFullThe auth login submit full.
const authLoginSubmitFull: 'w-full whitespace-nowrap'
authPageBodyThe auth page body.
const authPageBody: 'flex flex-col flex-1 items-center justify-center px-4 pb-[30px]'
authSubmitButtonThe auth submit button.
const authSubmitButton: 'grow whitespace-nowrap'
avatarAvatar component classes.
const avatar: (
props?: ({ size?: 'sm' | 'md' | 'lg' | 'xl' | undefined } & { class?: string }) | undefined,
) => string
avatarFallbackAvatar 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'
avatarFallbackIconThe avatar fallback icon.
const avatarFallbackIcon: 'h-1/2 w-1/2'
avatarImageThe avatar image.
const avatarImage: 'aspect-square h-full w-full object-cover'
avatarInitialsAvatar initials text classes.
const avatarInitials: 'text-sm font-medium'
avatarSquareThe avatar square.
const avatarSquare: 'rounded-md'
badgeBadge component classes with variant support.
const badge: (
props?:
| ({
variant?: 'default' | 'secondary' | 'success' | 'outline' | 'warning' | 'error' | undefined
} & { class?: string })
| undefined,
) => string
badgeSquareBadge with square (rounded-md) corners.
const badgeSquare: 'rounded-md'
bgBorderTailwind class for a background matching the theme border color.
const bgBorder: 'bg-border'
bgErrorSubtleSubtle error background utility class.
const bgErrorSubtle: 'bg-error-light'
bgPrimaryContainerPrimary-tinted container surface; pair with {@link textOnPrimaryContainer}.
const bgPrimaryContainer: 'bg-primary-container'
borderAllThe border all.
const borderAll: 'border border-border'
borderBBottom border with theme border color.
const borderB: 'border-b border-border'
borderBPrimaryThe border b primary.
const borderBPrimary: 'border-b-2 border-b-primary'
borderRTailwind classes for a right border with the theme border color.
const borderR: 'border-r border-border'
borderTTailwind classes for a top border with the theme border color.
const borderT: 'border-t border-border'
buttonButton 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
buttonIconLeftButton left icon spacing classes.
const buttonIconLeft: 'mr-2'
buttonIconRightThe button icon right.
const buttonIconRight: 'ml-2'
buttonSpinnerThe button spinner.
const buttonSpinner: 'mr-2'
cardCard component classes.
const card: (
props?:
| ({ variant?: 'default' | 'outline' | 'ghost' | 'elevated' | undefined } & { class?: string })
| undefined,
) => string
cardContentThe card content.
const cardContent: 'p-6 pt-0'
cardDescriptionThe card description.
const cardDescription: 'text-sm text-foreground-secondary'
cardFooterThe card footer.
const cardFooter: 'flex items-center p-6 pt-0'
cardHeaderThe card header.
const cardHeader: 'flex flex-col space-y-1.5 p-6'
cardInteractiveInteractive card classes (hover shadow effect).
const cardInteractive: 'cursor-pointer hover:shadow-md transition-shadow'
cardTitleThe card title.
const cardTitle: 'text-lg font-semibold leading-none tracking-tight'
centerCenter layout classes.
const center: 'flex items-center justify-center'
checkboxCheckbox component classes.
const checkbox: (
props?: ({ variant?: 'default' | 'error' | undefined } & { class?: string }) | undefined,
) => string
classMapTailwind CSS UIClassMap implementation mapping abstract UI tokens to Tailwind class strings.
const classMap: UIClassMap
cnMerges 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[]) => string
containerContainer component classes.
const container: (
props?:
| ({ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' | '2xl' | undefined } & { class?: string })
| undefined,
) => string
controlContainerThe control container.
const controlContainer: 'flex items-center'
controlDisabledThe control disabled.
const controlDisabled: 'opacity-50 cursor-not-allowed'
controlLabelControl (checkbox/switch/radio) label wrapper classes.
const controlLabel: 'flex items-center gap-2 cursor-pointer'
controlTextThe control text.
const controlText: 'text-sm'
cursorPointerThe cursor pointer.
const cursorPointer: 'cursor-pointer'
cvaCreates 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
dialogBodyThe dialog body.
const dialogBody: 'flex-1 min-h-0 overflow-y-auto px-6 py-6'
dialogCloseDialog 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'
dialogCloseFloatingFloating 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'
dialogContentThe dialog content.
const dialogContent: (
props?:
({ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' | undefined } & { class?: string }) | undefined,
) => string
dialogDescriptionThe dialog description.
const dialogDescription: 'text-sm text-foreground-secondary'
dialogFooterDialog 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'
dialogHeaderThe dialog header.
const dialogHeader: 'flex-shrink-0 flex items-center justify-between px-6 py-2'
dialogOverlayModal/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'
dialogTitleThe dialog title.
const dialogTitle: 'text-xl font-semibold leading-tight tracking-tight'
dialogWrapperThe 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]'
displayBlockDisplay block utility class.
const displayBlock: 'block'
displayContentsThe display contents.
const displayContents: 'contents'
displayInlineBlockThe display inline block.
const displayInlineBlock: 'inline-block'
dropdownContentDropdown 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'
dropdownItemDropdown 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'
dropdownItemDisabledThe dropdown item disabled.
const dropdownItemDisabled: 'opacity-50 cursor-not-allowed'
dropdownItemIconThe dropdown item icon.
const dropdownItemIcon: 'mr-2 flex-shrink-0'
dropdownItemLabelThe dropdown item label.
const dropdownItemLabel: 'flex-1'
dropdownItemShortcutThe dropdown item shortcut.
const dropdownItemShortcut: 'ml-auto text-xs text-foreground-tertiary'
dropdownLabelThe dropdown label.
const dropdownLabel: 'px-2 py-1.5 text-sm font-semibold'
dropdownSeparatorThe dropdown separator.
const dropdownSeparator: '-mx-1 my-1 h-px bg-border'
dropdownTriggerDropdown trigger wrapper classes.
const dropdownTrigger: 'inline-block'
emptyStateEmptyState root — centered column layout.
const emptyState: 'flex flex-col items-center justify-center py-16 px-4 text-center'
emptyStateActionEmptyState action slot.
const emptyStateAction: ''
emptyStateDescriptionEmptyState description.
const emptyStateDescription: 'text-sm text-gray-500 dark:text-gray-400 mb-6 max-w-md'
emptyStateIconEmptyState icon wrapper.
const emptyStateIcon: 'mb-4 text-gray-400 dark:text-gray-500'
emptyStateTitleEmptyState heading.
const emptyStateTitle: 'text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2'
flexFlex 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
flex1Flex grow utility class (flex-1).
const flex1: 'flex-1'
floatingInputThe floating input.
const floatingInput: 'peer'
floatingInputWrapperFloating input wrapper classes (relative container).
const floatingInputWrapper: 'relative inline-block align-middle w-full'
floatingLabelFloating 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'
footerBarFooter 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'
footerButtonFooter 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'
footerLinkFooter link classes.
const footerLink: 'p-[5px] text-[12px] text-foreground-secondary hover:text-foreground no-underline'
forgotPasswordLinkForgot password link classes.
const forgotPasswordLink: 'border-0 p-0 m-0 bg-transparent text-foreground-secondary text-[12px] cursor-pointer hover:text-primary'
formErrorForm error message classes.
const formError: 'text-sm text-error mt-1'
formFieldThe form field.
const formField: 'space-y-1.5'
formFieldsetThe form fieldset.
const formFieldset: 'space-y-4'
formFieldsetContentsThe form fieldset contents.
const formFieldsetContents: 'contents'
formFieldWrapperThe form field wrapper.
const formFieldWrapper: 'flex flex-col'
formHintForm hint message classes.
const formHint: 'text-sm text-foreground-secondary mt-1'
gridGrid 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,
) => string
gridResponsiveColsMobile-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>
headerFixedHeader container classes (full-width). Position is determined by the consuming layout, not forced here.
const headerFixed: 'top-0 left-0 right-0 w-full'
headerInnerThe header inner.
const headerInner: 'w-full max-w-[1280px] mx-auto h-10 p-[5px]'
hstackHStack 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
iconMdTailwind classes for medium icons (20x20).
const iconMd: 'h-5 w-5'
iconSmTailwind classes for small icons (16x16).
const iconSm: 'h-4 w-4'
iconXsIcon extra-small size classes (12×12).
const iconXs: 'h-3 w-3'
inputInput component classes.
const input: (
props?:
| ({
variant?: 'default' | 'error' | undefined
size?: 'sm' | 'md' | 'lg' | undefined
align?: 'left' | 'center' | undefined
} & { class?: string })
| undefined,
) => string
inputClearButtonThe input clear button.
const inputClearButton: 'text-foreground-secondary hover:text-foreground'
inputInnerThe input inner.
const inputInner: 'relative'
inputLeftElementInput left addon/icon container classes.
const inputLeftElement: 'absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-foreground-secondary'
inputPadLeftThe input pad left.
const inputPadLeft: 'pl-10'
inputPadRightThe input pad right.
const inputPadRight: 'pr-10'
inputRightElementThe input right element.
const inputRightElement: 'absolute inset-y-0 right-0 flex items-center pr-3'
inputWrapperInput wrapper classes.
const inputWrapper: 'w-full'
labelLabel component classes.
const label: (
props?: ({ required?: 'true' | 'false' | undefined } & { class?: string }) | undefined,
) => string
labelBlockBlock-level label classes.
const labelBlock: 'mb-1.5 block'
languageActiveLanguage 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'
languageGridLanguage 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'
languageOptionLanguage 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'
languagePickerTriggerLanguage 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'
logoIconTailwind class for the logo icon color.
const logoIcon: 'text-primary'
logoTextLogo text classes (tracked, 26px).
const logoText: 'text-[26px] leading-[26px] tracking-[-1px]'
mxAutoHorizontal auto-margin centering utility class.
const mxAuto: 'mx-auto'
notSrOnlyThe not sr only.
const notSrOnly: 'not-sr-only'
oauthButtonOAuth 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'
oauthButtonGroupOAuth provider button group container classes.
const oauthButtonGroup: 'flex flex-wrap gap-[15px] w-full max-w-[330px] mx-auto justify-center items-center'
oauthButtonIconOAuth provider button icon classes.
const oauthButtonIcon: 'opacity-75 group-hover:opacity-100 text-foreground transition-opacity'
oauthDividerOAuth divider classes (horizontal line with text).
const oauthDivider: 'relative mt-[45px] mb-[15px] text-[15px] text-center uppercase text-foreground-secondary'
oauthDividerLineOAuth 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)]'
oauthDividerTextThe oauth divider text.
const oauthDividerText: 'relative px-[15px] bg-background'
oauthProviderLabelOAuth provider label (fallback when no icon).
const oauthProviderLabel: 'text-sm font-medium'
pageHeaderPageHeader root container.
const pageHeader: 'mb-6'
pageHeaderActionsPageHeader action buttons container.
const pageHeaderActions: 'flex gap-2 mt-4 sm:mt-0'
pageHeaderBreadcrumbItemPageHeader breadcrumb item.
const pageHeaderBreadcrumbItem: 'hover:text-gray-700 dark:hover:text-gray-200'
pageHeaderBreadcrumbsPageHeader breadcrumb trail.
const pageHeaderBreadcrumbs: 'flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mb-2'
pageHeaderBreadcrumbSeparatorPageHeader breadcrumb separator.
const pageHeaderBreadcrumbSeparator: 'mx-1'
pageHeaderDescriptionPageHeader description.
const pageHeaderDescription: 'mt-1 text-sm text-gray-500 dark:text-gray-400'
pageHeaderTitlePageHeader title.
const pageHeaderTitle: 'text-2xl font-bold text-gray-900 dark:text-gray-100'
pageShellPageShell 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'
pageShellContentPageShell main content area.
const pageShellContent: 'flex-1 overflow-y-auto'
pageShellSidebarPageShell 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'
pageShellSidebarCollapsedPageShell sidebar collapsed modifier — narrow width.
const pageShellSidebarCollapsed: 'w-16'
pageShellSidebarTogglePageShell sidebar collapse/expand toggle button.
const pageShellSidebarToggle: 'p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors'
pageShellTopbarPageShell 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'
paginationPagination component classes.
const pagination: 'mx-auto flex w-full justify-center'
paginationContentThe pagination content.
const paginationContent: 'flex flex-row items-center gap-1'
paginationEllipsisThe pagination ellipsis.
const paginationEllipsis: 'flex h-9 w-9 items-center justify-center'
paginationInteractivePagination interactive item (cursor pointer).
const paginationInteractive: 'cursor-pointer'
paginationItemThe pagination item.
const paginationItem: (
props?:
| ({ variant?: 'default' | 'active' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => string
paginationLinkThe pagination link.
const paginationLink: 'cursor-pointer'
paginationNextThe pagination next.
const paginationNext: 'gap-1 pr-2.5'
paginationPreviousThe pagination previous.
const paginationPrevious: 'gap-1 pl-2.5'
progressProgress component classes.
const progress: 'relative h-2 w-full overflow-hidden rounded-full bg-surface-secondary'
progressIndeterminateThe progress indeterminate.
const progressIndeterminate: 'animate-indeterminate-progress'
progressIndicatorThe progress indicator.
const progressIndicator: 'h-full w-full flex-1 bg-primary transition-all'
progressLabelContainerThe progress label container.
const progressLabelContainer: 'flex justify-between mb-1 text-sm'
progressLabelTextThe progress label text.
const progressLabelText: 'text-foreground-secondary'
progressWrapperProgress bar wrapper classes.
const progressWrapper: 'w-full'
radioRadio component classes.
const radio: (
props?: ({ variant?: 'default' | 'error' | undefined } & { class?: string }) | undefined,
) => string
radioGroupLabelRadio group label classes.
const radioGroupLabel: 'text-sm font-medium mb-2 block'
roundedFullThe rounded full.
const roundedFull: 'rounded-full'
selectSelect component classes.
const select: (
props?:
| ({ variant?: 'default' | 'error' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => string
selectNativeNative select appearance reset classes.
const selectNative: 'appearance-none'
separatorSeparator component classes with horizontal/vertical orientation.
const separator: (
props?:
({ orientation?: 'horizontal' | 'vertical' | undefined } & { class?: string }) | undefined,
) => string
skeletonSkeleton component classes.
const skeleton: 'animate-pulse rounded-md bg-surface-secondary'
skeletonCircleThe skeleton circle.
const skeletonCircle: 'rounded-full'
skeletonNoneThe skeleton none.
const skeletonNone: 'animate-none'
skeletonTextContainerSkeleton text placeholder container (vertical spacing).
const skeletonTextContainer: 'space-y-2'
skeletonWaveSkeleton 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%]'
spinnerSpinner component classes with size variants.
const spinner: (
props?: ({ size?: 'sm' | 'md' | 'lg' | 'xl' | undefined } & { class?: string }) | undefined,
) => string
srOnlyScreen reader only classes.
const srOnly: 'sr-only'
stackStack 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
surfaceSecondarySecondary surface background utility class.
const surfaceSecondary: 'bg-surface-secondary'
switchBaseSwitch 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
switchThumbSwitch thumb (the sliding circle) classes.
const switchThumb: (
props?: ({ size?: 'sm' | 'md' | 'lg' | undefined } & { class?: string }) | undefined,
) => string
tableTable root classes.
const table: 'w-full caption-bottom text-sm'
tableBodyThe table body.
const tableBody: '[&_tr:last-child]:border-0'
tableBorderedThe table bordered.
const tableBordered: 'border'
tableCaptionThe table caption.
const tableCaption: 'mt-4 text-sm text-foreground-secondary'
tableCellThe table cell.
const tableCell: 'p-4 align-middle [&:has([role=checkbox])]:pr-0'
tableEmptyCellThe table empty cell.
const tableEmptyCell: 'py-10 text-center text-foreground-secondary'
tableFooterThe table footer.
const tableFooter: 'border-t bg-surface-secondary font-medium [&>tr]:last:border-b-0'
tableHeadTable header cell classes.
const tableHead: 'h-12 px-4 text-left align-middle font-medium text-foreground-secondary [&:has([role=checkbox])]:pr-0'
tableHeaderThe table header.
const tableHeader: '[&_tr]:border-b'
tableHeadSortableThe table head sortable.
const tableHeadSortable: 'cursor-pointer select-none hover:bg-surface-secondary'
tableLoadingOverlayTable loading overlay classes (semi-transparent centered spinner container).
const tableLoadingOverlay: 'absolute inset-0 bg-background/50 flex items-center justify-center z-10'
tableRowTable row classes.
const tableRow: 'border-b transition-colors hover:bg-surface-secondary data-[state=selected]:bg-surface-secondary'
tableRowClickableThe table row clickable.
const tableRowClickable: 'cursor-pointer'
tableRowHoverableThe table row hoverable.
const tableRowHoverable: 'hover:bg-surface-secondary'
tableRowStripedThe table row striped.
const tableRowStriped: 'bg-surface-secondary/50'
tableSortIconThe table sort icon.
const tableSortIcon: 'ml-1'
tableSortWrapperThe table sort wrapper.
const tableSortWrapper: 'flex items-center gap-1'
tableWrapperTable wrapper classes (scrollable container).
const tableWrapper: 'relative w-full overflow-auto'
tabsContentTabs 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
tabsFittedFitted (full-width) tabs container classes.
const tabsFitted: 'w-full'
tabsListTabs 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
tabsTriggerTabs 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
tabTriggerFittedThe tab trigger fitted.
const tabTriggerFitted: 'flex-1'
tabTriggerIconThe tab trigger icon.
const tabTriggerIcon: 'mr-2'
textareaTextarea component classes.
const textarea: (
props?:
| ({ variant?: 'default' | 'error' | undefined; size?: 'sm' | 'md' | 'lg' | undefined } & {
class?: string
})
| undefined,
) => string
textOnPrimaryContainerReadable text on {@link bgPrimaryContainer}.
const textOnPrimaryContainer: 'text-on-primary-container'
textPrimaryPrimary text color utility class.
const textPrimary: 'text-primary'
textRightText right-alignment utility class.
const textRight: 'text-right'
textSuccessThe text success.
const textSuccess: 'text-success'
textWarningThe text warning.
const textWarning: 'text-warning'
themeToggleButtonTheme toggle button classes.
const themeToggleButton: 'w-[30px] h-[30px] p-0 border-0 bg-transparent text-foreground-secondary hover:text-foreground cursor-pointer flex items-center justify-center'
toastTailwind toast notification variant classes (default, success, error, warning).
const toast: (
props?:
| ({ variant?: 'default' | 'success' | 'warning' | 'error' | 'info' | undefined } & {
class?: string
})
| undefined,
) => string
toastActionToast 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'
toastCloseToast 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'
toastContentWrapperThe toast content wrapper.
const toastContentWrapper: 'flex-1 gap-1'
toastDescriptionThe toast description.
const toastDescription: 'text-sm opacity-90'
toastIconWrapperToast icon wrapper classes.
const toastIconWrapper: 'flex-shrink-0'
toastTitleThe toast title.
const toastTitle: 'text-sm font-semibold'
toastViewportToast 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]'
tooltipContentTooltip 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'
tooltipTriggerTooltip trigger wrapper classes.
const tooltipTrigger: 'inline-block'
Implements @molecule/app-ui interface.
Peer dependencies:
@molecule/app-styling ^1.0.1@molecule/app-ui ^1.0.1@molecule/app-styling@molecule/app-uiTheming / 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.