No description
Find a file
autocommit d9b4237654
Some checks failed
Publish / publish (push) Failing after 0s
deps-upgrade(root-core): ⬆️ Update root-core dependencies to latest stable versions for security patches and compatibility improvements
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-11 01:24:10 -07:00
.forgejo/workflows chore: initial package split from monorepo 2026-04-20 01:10:38 -07:00
src chore: initial package split from monorepo 2026-04-20 01:10:38 -07:00
.gitignore chore: add .gitignore, remove node_modules/dist/.turbo from tracking 2026-04-20 01:12:39 -07:00
eslint.config.js chore: initial package split from monorepo 2026-04-20 01:10:38 -07:00
package.json deps-upgrade(root-core): ⬆️ Update root-core dependencies to latest stable versions for security patches and compatibility improvements 2026-06-11 01:24:10 -07:00
README.md chore: initial package split from monorepo 2026-04-20 01:10:38 -07:00
tsconfig.json chore: initial package split from monorepo 2026-04-20 01:10:38 -07:00
tsup.config.ts chore: initial package split from monorepo 2026-04-20 01:10:38 -07:00

@lilith/ui-feedback

Feedback and overlay components for React applications. Includes modals, toasts, dropdowns, tooltips, tabs, popovers, skeleton loaders, and more with theme-aware styling and animations.

Features

  • Modal - Dialog overlays with customizable size and animations
  • Toast - Notification system with multiple types
  • Dropdown - Customizable dropdown menus
  • Tooltip - Hover tooltips with positioning
  • Popover - Click-triggered content panels
  • Tabs - Tab navigation with variants (standard and pill)
  • Skeleton - Loading placeholders
  • PromptDialog - Confirmation dialogs with context API
  • BaseDrawer - Slide-out drawer component
  • TranslatedText - Text with loading states for i18n
  • Focus Management - Hooks for body scroll lock, escape handling, and focus trapping

Installation

npm install @lilith/ui-feedback
# or
pnpm add @lilith/ui-feedback

Peer Dependencies

npm install react react-dom styled-components

Usage

Modal

Dialog overlay with header and body.

import { Modal, ModalActions } from '@lilith/ui-feedback';

const [isOpen, setIsOpen] = useState(false);

<Modal
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
  title="Confirm Action"
  maxWidth="500px"
>
  <p>Are you sure you want to proceed?</p>
  <ModalActions>
    <Button variant="secondary" onClick={() => setIsOpen(false)}>
      Cancel
    </Button>
    <Button variant="primary" onClick={handleConfirm}>
      Confirm
    </Button>
  </ModalActions>
</Modal>

Toast

Notification system with provider and hook.

import { ToastProvider, useToast } from '@lilith/ui-feedback';

// Wrap your app
<ToastProvider>
  <App />
</ToastProvider>

// Use in components
function MyComponent() {
  const { addToast } = useToast();

  const handleSave = () => {
    addToast({
      type: 'success',
      message: 'Changes saved successfully',
      duration: 3000,
    });
  };

  const handleError = () => {
    addToast({
      type: 'error',
      message: 'Failed to save changes',
      action: {
        label: 'Retry',
        onClick: () => retrySave(),
      },
    });
  };

  // Types: 'success', 'error', 'warning', 'info'
}

Dropdown

Customizable dropdown menu.

import { Dropdown, DropdownItem } from '@lilith/ui-feedback';

<Dropdown
  trigger={<Button>Actions</Button>}
  align="left"
>
  <DropdownItem onClick={() => handleEdit()}>Edit</DropdownItem>
  <DropdownItem onClick={() => handleDuplicate()}>Duplicate</DropdownItem>
  <DropdownItem onClick={() => handleDelete()} variant="danger">
    Delete
  </DropdownItem>
</Dropdown>

Tooltip

Hover-triggered informational tooltips.

import { Tooltip } from '@lilith/ui-feedback';

<Tooltip content="This action cannot be undone" position="top">
  <Button variant="danger">Delete</Button>
</Tooltip>

// With rich content
<Tooltip
  content={
    <div>
      <strong>Keyboard Shortcut</strong>
      <p>Press Cmd+S to save</p>
    </div>
  }
  position="right"
>
  <span>Save</span>
</Tooltip>

Popover

Click-triggered content panels.

import { Popover } from '@lilith/ui-feedback';

<Popover
  trigger={<Button>Show Details</Button>}
  content={
    <div>
      <h3>User Details</h3>
      <p>Name: John Doe</p>
      <p>Email: john@example.com</p>
    </div>
  }
  position="bottom"
/>

Tabs

Tab navigation with multiple variants.

import { Tabs } from '@lilith/ui-feedback';
import type { Tab } from '@lilith/ui-feedback';

const tabs: Tab[] = [
  { id: 'overview', label: 'Overview', content: <Overview /> },
  { id: 'settings', label: 'Settings', content: <Settings /> },
  { id: 'history', label: 'History', content: <History />, disabled: true },
];

// Standard tabs
<Tabs
  tabs={tabs}
  activeTab={activeTab}
  onChange={setActiveTab}
/>

// Pill tabs (for filtering, segmented controls)
import { PillTabs } from '@lilith/ui-feedback';

<PillTabs
  tabs={[
    { id: 'all', label: 'All' },
    { id: 'active', label: 'Active' },
    { id: 'archived', label: 'Archived' },
  ]}
  activeTab={filter}
  onChange={setFilter}
/>

Skeleton

Loading placeholders.

import {
  Skeleton,
  TextSkeleton,
  AvatarSkeleton,
  CardSkeleton,
} from '@lilith/ui-feedback';

// Basic skeleton
<Skeleton width={200} height={20} />

// Text lines
<TextSkeleton lines={3} />

// Avatar placeholder
<AvatarSkeleton size={48} />

// Card placeholder
<CardSkeleton />

PromptDialog

Confirmation dialogs with context API.

import { PromptDialogProvider, usePromptDialog } from '@lilith/ui-feedback';

// Wrap your app
<PromptDialogProvider>
  <App />
</PromptDialogProvider>

// Use in components
function DeleteButton() {
  const { prompt } = usePromptDialog();

  const handleDelete = async () => {
    const confirmed = await prompt({
      title: 'Delete Item',
      message: 'Are you sure you want to delete this item?',
      confirmLabel: 'Delete',
      cancelLabel: 'Cancel',
      variant: 'danger',
    });

    if (confirmed) {
      await deleteItem();
    }
  };

  return <Button onClick={handleDelete}>Delete</Button>;
}

BaseDrawer

Slide-out drawer component.

import { BaseDrawer } from '@lilith/ui-feedback';

<BaseDrawer
  isOpen={isDrawerOpen}
  onClose={() => setIsDrawerOpen(false)}
  position="right"
  width="400px"
  title="Settings"
>
  <SettingsForm />
</BaseDrawer>

Skeleton Loading Patterns

import {
  ImageWithSkeleton,
  SuspenseWithSkeleton,
  PageSuspense,
} from '@lilith/ui-feedback';

// Image with loading skeleton
<ImageWithSkeleton
  src="/user/avatar.jpg"
  alt="User avatar"
  width={100}
  height={100}
/>

// Suspense wrapper with skeleton fallback
<SuspenseWithSkeleton skeleton={<CardSkeleton />}>
  <LazyLoadedCard />
</SuspenseWithSkeleton>

// Page-level suspense
<PageSuspense>
  <LazyLoadedPage />
</PageSuspense>

Focus Management Hooks

import {
  useBodyScrollLock,
  useModalEscape,
  useModalFocusTrap,
} from '@lilith/ui-feedback';

function CustomModal({ isOpen, onClose, children }) {
  const modalRef = useRef(null);

  // Lock body scroll when open
  useBodyScrollLock(isOpen);

  // Close on Escape key
  useModalEscape(onClose, isOpen);

  // Trap focus within modal
  useModalFocusTrap(modalRef, isOpen);

  return (
    <div ref={modalRef} role="dialog">
      {children}
    </div>
  );
}

API Reference

Modal

Prop Type Default Description
isOpen boolean required Visibility state
onClose () => void required Close handler
title string required Modal title
children ReactNode required Modal content
maxWidth string '600px' Maximum width
maxHeight string '90vh' Maximum height

Toast (via useToast)

interface Toast {
  type: 'success' | 'error' | 'warning' | 'info';
  message: string;
  duration?: number;
  action?: {
    label: string;
    onClick: () => void;
  };
}

Tooltip

Prop Type Default Description
content ReactNode required Tooltip content
position 'top' | 'bottom' | 'left' | 'right' 'top' Position
children ReactNode required Trigger element
delay number 200 Show delay in ms

Tabs

Prop Type Default Description
tabs Tab[] required Tab definitions
activeTab string required Active tab ID
onChange (id: string) => void required Tab change handler

BaseDrawer

Prop Type Default Description
isOpen boolean required Visibility state
onClose () => void required Close handler
position 'left' | 'right' 'right' Slide direction
width string '400px' Drawer width
title string - Header title
children ReactNode required Drawer content

Types

interface Tab {
  id: string;
  label: string;
  content?: ReactNode;
  disabled?: boolean;
}

interface PromptOptions {
  title: string;
  message: string;
  confirmLabel?: string;
  cancelLabel?: string;
  variant?: 'default' | 'danger';
}

interface SkeletonProps {
  width?: number | string;
  height?: number | string;
  borderRadius?: string;
  animation?: 'pulse' | 'wave';
}

Dependencies

  • @lilith/ui-primitives - Base components
  • @lilith/ui-theme - Theme tokens
  • framer-motion - Animations
  • lucide-react - Icons

License

MIT