@marcoschwartz/lite-ui
v0.25.4
Published
A lightweight UI component library built with Tailwind CSS
Downloads
3,362
Maintainers
Readme
Lite UI
A lightweight, modern UI component library built with React, TypeScript, and Tailwind CSS v4.
✨ Features
- 🎨 21 Production-Ready Components - From basic inputs to complex data tables
- 🌙 Dark Mode Support - Built-in dark mode with system preference detection
- 🎭 Theming System - Multiple themes with easy customization
- 📦 Tree-Shakeable - Only bundle what you use (ESM & CJS)
- ⚡ TypeScript First - Full type safety and IntelliSense support
- 🚀 Server Component Compatible - Works with Next.js App Router and RSC
- 🎯 Zero Runtime CSS - Styles compiled with Tailwind CSS v4
- ♿ Accessible - ARIA-compliant components
📦 Installation
npm install @marcoschwartz/lite-uiSetup for Next.js (Required)
⚠️ Important: You must include
ColorSchemeScriptin your layout to prevent dark mode flashing (FOUC).
// app/layout.tsx
import { ThemeProvider, ColorSchemeScript } from '@marcoschwartz/lite-ui';
import '@marcoschwartz/lite-ui/styles.css';
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<ColorSchemeScript defaultColorMode="system" />
</head>
<body>
<ThemeProvider defaultTheme="default" defaultColorMode="system">
{children}
</ThemeProvider>
</body>
</html>
);
}Key points:
ColorSchemeScriptmust be in<head>- it prevents flash by setting colors before React hydratessuppressHydrationWarningon<html>is required to avoid React warningsdefaultColorModeshould match betweenColorSchemeScriptandThemeProvider
Setup for Vite/Other React Apps
// main.tsx
import '@marcoschwartz/lite-ui/styles.css';
import { ThemeProvider } from '@marcoschwartz/lite-ui';
ReactDOM.createRoot(document.getElementById('root')!).render(
<ThemeProvider defaultTheme="default" defaultColorMode="system">
<App />
</ThemeProvider>
);For Vite apps, add the theme script to your index.html:
<head>
<script>
(function(){try{var d=document.documentElement,c=localStorage.getItem('lite-ui-color-mode')||'system',r=c;if(c==='system'||!c)r=matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';d.setAttribute('data-color-mode',r);d.style.colorScheme=r;if(r==='dark'){d.classList.add('dark');d.style.backgroundColor='hsl(0,0%,3.9%)';d.style.color='hsl(0,0%,98%)'}else{d.classList.remove('dark');d.style.backgroundColor='hsl(0,0%,100%)';d.style.color='hsl(0,0%,3.9%)'}}catch(e){}})();
</script>
</head>Start using components:
import { Button, Card, TextInput } from '@marcoschwartz/lite-ui';
export default function Page() {
return (
<Card>
<h1>Welcome to Lite UI</h1>
<TextInput placeholder="Enter your name" />
<Button variant="primary">Submit</Button>
</Card>
);
}🎨 Components
Form Controls
- Button - Versatile button with 6 variants and 4 sizes
- TextInput - Text input with validation and error states
- Select - Dropdown select with custom styling
- Checkbox - Checkbox with label and error support
- Toggle - Switch/toggle component with multiple sizes
- DatePicker - Date selection input
- TimePicker - Time selection input
- DateTimePicker - Combined date and time picker
Layout & Navigation
- AppShell - Complete app layout with header, navbar, aside, and footer
- Navbar - Responsive navigation bar
- Sidebar - Collapsible sidebar with mobile support
- SidebarNav - Collapsible navigation with icon-only mode and tooltips
- Drawer - Slide-out drawer panel
- Modal - Accessible modal dialog
- Tabs - Tabbed navigation component
Feedback & Overlays
- Alert - Alert messages with 4 variants
- Spinner - Loading spinner with 3 sizes
- Badge - Status badges with 6 variants
Data Display
- Card - Content container with optional header/footer
- Table - Data table with sorting and selection
- Pagination - Page navigation component
Utilities
- ActionMenu - Dropdown action menu
- Icons - Set of common UI icons
📖 View Full Component Documentation
🌙 Dark Mode
Lite UI includes built-in dark mode support with three color modes.
Preventing Flash (FOUC)
To prevent the flash of wrong colors on page load, you must use ColorSchemeScript:
// app/layout.tsx
import { ColorSchemeScript, ThemeProvider } from '@marcoschwartz/lite-ui';
<html lang="en" suppressHydrationWarning>
<head>
<ColorSchemeScript defaultColorMode="system" />
</head>
<body>
<ThemeProvider defaultColorMode="system">
{children}
</ThemeProvider>
</body>
</html>Toggling Dark Mode
import { useTheme } from '@marcoschwartz/lite-ui';
function ThemeToggle() {
const { colorMode, setColorMode } = useTheme();
return (
<select value={colorMode} onChange={(e) => setColorMode(e.target.value)}>
<option value="system">System</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
);
}⬆️ Upgrading from v0.24.7 or earlier
If you're upgrading from an earlier version, update your layout.tsx:
- import { ThemeProvider, themeScript } from '@marcoschwartz/lite-ui';
+ import { ThemeProvider, ColorSchemeScript } from '@marcoschwartz/lite-ui';
<html lang="en" suppressHydrationWarning>
<head>
- <script
- dangerouslySetInnerHTML={{ __html: themeScript }}
- suppressHydrationWarning
- />
+ <ColorSchemeScript defaultColorMode="system" />
</head>The new ColorSchemeScript component:
- Sets CSS custom properties inline (prevents flash even if CSS loads slowly)
- Cleaner API than the old
themeScriptstring - Supports
nonceprop for Content Security Policy
🎭 Theming
Switch between built-in themes or create your own:
import { useTheme } from '@marcoschwartz/lite-ui';
function ThemeSwitcher() {
const { themeName, setTheme } = useTheme();
return (
<div>
<button onClick={() => setTheme('default')}>Default Theme</button>
<button onClick={() => setTheme('minimalistic')}>Minimalistic Theme</button>
</div>
);
}Available Themes
- Default - Vibrant colors, shadows, and smooth animations
- Minimalistic - Flat design, subtle colors, minimal effects
🚀 Usage Examples
Basic Form
"use client";
import { useState } from 'react';
import { Card, TextInput, Button, Alert } from '@marcoschwartz/lite-ui';
export function ContactForm() {
const [email, setEmail] = useState('');
const [submitted, setSubmitted] = useState(false);
return (
<Card>
<h2>Contact Us</h2>
{submitted && (
<Alert variant="success">Message sent successfully!</Alert>
)}
<TextInput
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="[email protected]"
/>
<Button
variant="primary"
onClick={() => setSubmitted(true)}
>
Send Message
</Button>
</Card>
);
}Data Table
import { Table } from '@marcoschwartz/lite-ui';
const columns = [
{ key: 'name', label: 'Name', sortable: true },
{ key: 'email', label: 'Email', sortable: true },
{ key: 'status', label: 'Status' },
];
const data = [
{ id: 1, name: 'John Doe', email: '[email protected]', status: 'Active' },
{ id: 2, name: 'Jane Smith', email: '[email protected]', status: 'Inactive' },
];
export function UserTable() {
return (
<Table
columns={columns}
data={data}
sortable
selectable
/>
);
}Modal Dialog
"use client";
import { useState } from 'react';
import { Modal, Button } from '@marcoschwartz/lite-ui';
export function ConfirmDialog() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button onClick={() => setIsOpen(true)}>Open Dialog</Button>
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Confirm Action"
>
<p>Are you sure you want to proceed?</p>
<div className="flex gap-2 mt-4">
<Button variant="danger" onClick={() => setIsOpen(false)}>
Confirm
</Button>
<Button variant="secondary" onClick={() => setIsOpen(false)}>
Cancel
</Button>
</div>
</Modal>
</>
);
}Collapsible Sidebar Navigation
"use client";
import { useState } from 'react';
import {
AppShell,
SidebarNav,
SidebarNavItem,
SidebarNavSection,
SidebarNavDivider,
SidebarNavCollapseToggle,
HomeIcon,
UserIcon,
SettingsIcon,
} from '@marcoschwartz/lite-ui';
export function DashboardLayout({ children }) {
const [activeItem, setActiveItem] = useState('dashboard');
const [navbarWidth, setNavbarWidth] = useState<number | undefined>();
return (
<AppShell
title="My App"
navbar={{
content: (
<SidebarNav
collapsible
onCollapseChange={(_, width) => setNavbarWidth(width)}
>
<SidebarNavSection grow>
<SidebarNavItem
icon={<HomeIcon size="sm" />}
label="Dashboard"
active={activeItem === 'dashboard'}
onClick={() => setActiveItem('dashboard')}
/>
<SidebarNavItem
icon={<UserIcon size="sm" />}
label="Users"
active={activeItem === 'users'}
onClick={() => setActiveItem('users')}
/>
<SidebarNavItem
icon={<SettingsIcon size="sm" />}
label="Settings"
active={activeItem === 'settings'}
onClick={() => setActiveItem('settings')}
/>
</SidebarNavSection>
<SidebarNavDivider />
<SidebarNavCollapseToggle />
</SidebarNav>
),
width: navbarWidth, // Dynamic width from SidebarNav
}}
>
{children}
</AppShell>
);
}SidebarNav Features:
- Collapsible mode - Toggle between full and icon-only view
- Tooltips - Show labels on hover when collapsed
- Persistent state - Collapse state saved to localStorage
- Smooth transitions - Animated width changes
- Mantine-compatible API - Use
leftSectionas alias foricon
🛠️ Development
Running the Demo
# Clone the repository
git clone https://github.com/marcoschwartz/lite-ui.git
cd lite-ui-js
# Install dependencies
npm install
# Run library + demo app in watch mode
npm run dev:allThis will:
- Start the library build in watch mode (auto-rebuilds on changes)
- Start the Next.js demo app on http://localhost:3000
- Hot-reload changes from the library in the demo app
Building
# Build library
npm run build
# Build CSS only
npm run build:css📂 Project Structure
lite-ui-js/
├── src/ # Library source code
│ ├── components/ # React components
│ ├── theme/ # Theme system
│ ├── icons/ # Icon components
│ └── index.ts # Main exports
├── demo/ # Demo Next.js app
│ └── app/
│ ├── components/ # Component examples
│ └── page.tsx # Home page
├── dist/ # Built library (generated)
└── package.json🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
MIT © Marco Schwartz
🔗 Links
💡 Technology Stack
- React 19 - UI framework
- TypeScript 5 - Type safety
- Tailwind CSS v4 - Utility-first styling
- tsup - TypeScript bundler
- Next.js 15 - Demo app framework
