← All @molecule/* packages · App templates

@molecule/app-ui-tailwind

Provider bond · ui · App (browser) · v1.1.0 · Apache-2.0

Tailwind CSS UI components for molecule.dev

npm install @molecule/app-ui-tailwind

npm · 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

Reference

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.ts JSDoc, 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-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 {@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,
) => string

accordionTriggerBase

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,
) => string

alertContent

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, padding for fixed header/footer).

const appLayout: 'min-h-screen pt-[55px] 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,
) => string

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'

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,
) => string

badgeSquare

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,
) => string

buttonIconLeft

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,
) => string

cardContent

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,
) => string

classMap

Tailwind CSS UIClassMap implementation mapping abstract UI tokens to Tailwind class strings.

const classMap: UIClassMap

cn

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[]) => string

container

Container component classes.

const container: (
  props?:
    | ({ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' | '2xl' | undefined } & { class?: string })
    | undefined,
) => string

controlContainer

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 }) => string

dialogBody

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,
) => string

dialogDescription

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'

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'

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'

The dropdown item disabled.

const dropdownItemDisabled: 'opacity-50 cursor-not-allowed'

The dropdown item icon.

const dropdownItemIcon: 'mr-2 flex-shrink-0'

The dropdown item label.

const dropdownItemLabel: 'flex-1'

The dropdown item shortcut.

const dropdownItemShortcut: 'ml-auto text-xs text-foreground-tertiary'

The dropdown label.

const dropdownLabel: 'px-2 py-1.5 text-sm font-semibold'

The dropdown separator.

const dropdownSeparator: '-mx-1 my-1 h-px bg-border'

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,
) => string

flex1

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'

Footer link classes.

const footerLink: 'p-[5px] text-[12px] text-foreground-secondary hover:text-foreground no-underline'

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,
) => 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 {@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,
) => string

iconMd

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,
) => string

inputClearButton

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,
) => string

labelBlock

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 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,
) => string

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,
) => string

radioGroupLabel

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,
) => string

selectNative

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,
) => string

skeleton

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,
) => string

srOnly

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,
) => string

surfaceSecondary

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,
) => string

switchThumb

Switch thumb (the sliding circle) classes.

const switchThumb: (
  props?: ({ size?: 'sm' | 'md' | 'lg' | undefined } & { class?: string }) | undefined,
) => string

table

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,
) => string

tabsFitted

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,
) => 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

tabTriggerFitted

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,
) => string

textOnPrimaryContainer

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: 'w-[30px] h-[30px] 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,
) => string

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'

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.0.1

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.