luaniverse
v4.2.26
Published
Lua Design System - A React component library for Lua applications
Readme
Luaniverse
Lua Design System - A React component library for Lua applications
Installation
npm install @lua-global/luaniverse
# or
yarn add @lua-global/luaniverse
# or
pnpm add @lua-global/luaniverseUsage
Importing Components
import { Button, IconButton, Badge, Textarea } from '@lua-global/luaniverse';
import { UserIcon, Trash, PDF, PNG, Chat, Clock, Minus, Plus, Tag } from '@lua-global/luaniverse';Styling
Import the CSS file in your app's entry point:
import '@lua-global/luaniverse/styles';Available Exports:
@lua-ai-global/luaniverse- Components and utilities@lua-ai-global/luaniverse/styles- CSS styles and design tokens@lua-ai-global/luaniverse/tailwind- Tailwind preset for utility classes
💡 Quick Setup: See INTEGRATION.md for step-by-step consumer app setup instructions.
Tailwind CSS Configuration
⚠️ IMPORTANT: Luaniverse requires BOTH the Tailwind preset AND the styles to work correctly. Missing either will cause components to lose their backgrounds and styling.
Step 1: Add the Tailwind Preset
Update your tailwind.config.js to use the Luaniverse preset:
// tailwind.config.js
import { luaniversePreset } from '@lua-ai-global/luaniverse/tailwind';
export default {
presets: [luaniversePreset], // 🔥 Required for component styling
content: [
// Your content paths
'./src/**/*.{js,jsx,ts,tsx}',
'./app/**/*.{js,jsx,ts,tsx}',
// 🔥 Required: Include luaniverse components for class detection
'./node_modules/@lua-ai-global/luaniverse/**/*.{js,jsx,ts,tsx}',
],
// Your additional config...
};Or with CommonJS:
// tailwind.config.js
const { luaniversePreset } = require('@lua-ai-global/luaniverse/tailwind');
module.exports = {
presets: [luaniversePreset],
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'./node_modules/@lua-ai-global/luaniverse/**/*.{js,jsx,ts,tsx}',
],
};Step 2: Import the Styles
In your main CSS file, import the Luaniverse styles:
/* app.css or globals.css */
@import '@lua-ai-global/luaniverse/styles'; /* 🔥 Required for design tokens */
@import 'tailwindcss';Why Both Are Required
- Preset: Enables utility classes like
bg-popover,text-card-foreground - Styles: Provides CSS variables (
--primary,--popover) and typography system
❌ Without preset: Components lose backgrounds (transparent dropdowns)
❌ Without styles: Components lose design tokens and typography
✅ With both: Components work perfectly!
Dark Mode Support
Luaniverse comes with built-in dark mode support. The library uses Tailwind's class-based dark mode strategy.
Enabling Dark Mode
To enable dark mode in your application:
- Add the dark class to your HTML:
<html class="dark">
<!-- Your app content -->
</html>- Or toggle it programmatically:
// Toggle dark mode
document.documentElement.classList.toggle('dark');
// Enable dark mode
document.documentElement.classList.add('dark');
// Disable dark mode
document.documentElement.classList.remove('dark');- With React state management:
function App() {
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
document.documentElement.classList.toggle('dark', darkMode);
}, [darkMode]);
return (
<div className={darkMode ? 'dark' : ''}>
<Button onClick={() => setDarkMode(!darkMode)}>
Toggle Dark Mode
</Button>
{/* Your app content */}
</div>
);
}Dark Mode Features
- Automatic Color Adaptation - All components automatically adapt their colors for dark mode
- Maintained Accessibility - Focus indicators and contrast ratios remain WCAG compliant in dark mode
- Brand Consistency - Primary and destructive colors maintain brand identity across themes
- Smooth Transitions - Built-in CSS transitions for theme switching
Troubleshooting
Missing Component Backgrounds
Problem: Dropdown menus, cards, or other components appear transparent or unstyled.
Cause: Missing Tailwind preset or incomplete setup.
Solution: Ensure you have both parts of the setup:
- ✅ Preset added to
tailwind.config.js - ✅ Styles imported in your CSS
- ✅ Content paths include
./node_modules/@lua-ai-global/luaniverse/**/*.{js,jsx,ts,tsx}
// ✅ Correct setup
import { luaniversePreset } from '@lua-ai-global/luaniverse/tailwind';
export default {
presets: [luaniversePreset], // Enables bg-popover, bg-card utilities
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'./node_modules/@lua-ai-global/luaniverse/**/*.{js,jsx,ts,tsx}', // Required!
],
};/* ✅ Required in your CSS */
@import '@lua-ai-global/luaniverse/styles';
@import 'tailwindcss';Missing Typography Utilities
Problem: Custom typography classes like text-h1, link-primary don't work.
Solution: Make sure you've imported the styles (they contain custom @utility definitions).
Build Errors
Problem: Build fails with Tailwind-related errors.
Solution: Check that your content paths are correct and that you're using a compatible Tailwind version (v3.3+ or v4+).
Components
Button
A versatile button component with multiple variants, sizes, and states.
import { Button } from '@lua-global/luaniverse';
function App() {
return (
<div>
<Button>Default Button</Button>
<Button variant="primary">Primary Button</Button>
<Button variant="outline">Outline Button</Button>
<Button variant="destructive">Destructive Button</Button>
<Button size="large">Large Button</Button>
<Button loading>Loading Button</Button>
</div>
);
}Button Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| variant | 'default' \| 'primary' \| 'outline-solid' \| 'tertiary' \| 'secondary' \| 'muted' \| 'destructive' | 'default' | The visual style variant |
| size | 'small' \| 'default' \| 'large' | 'default' | The size of the button |
| loading | boolean | false | Shows loading spinner when true |
| disabled | boolean | false | Disables the button |
| asChild | boolean | false | Render as a child element |
| startAdornment | React.ReactNode | - | Icon or element before the text |
| endAdornment | React.ReactNode | - | Icon or element after the text |
| aria-label | string | - | Accessible label for the button |
| aria-describedby | string | - | Additional description for complex actions |
Button Variants
- default: Black background with white text (adapts to white background in dark mode)
- primary: Blue background with white text
- outline: White background with blue border and text
- tertiary: White background with gray border (adapts to dark gray in dark mode)
- secondary: Gray background using CSS variables
- muted: Transparent background with hover states
- destructive: Red background with white text
Button Examples
// With Luaniverse icons
import { UserIcon, Camera } from '@lua-global/luaniverse';
<Button startAdornment={<UserIcon size={16} />}>
Profile
</Button>
<Button endAdornment={<Camera size={16} />}>
Take Photo
</Button>
// Loading state
<Button loading>
Processing...
</Button>
// As a link
<Button asChild>
<a href="/dashboard">Go to Dashboard</a>
</Button>IconButton
A button component designed specifically for icons with proper accessibility features and sizing.
import { IconButton } from '@lua-global/luaniverse';
import { UserIcon, Trash, XIcon } from '@lua-global/luaniverse';
function App() {
return (
<div>
<IconButton aria-label="User profile">
<UserIcon size={20} />
</IconButton>
<IconButton variant="destructive" aria-label="Delete item">
<Trash size={20} />
</IconButton>
<IconButton variant="muted" aria-label="Close dialog">
<XIcon size={20} />
</IconButton>
</div>
);
}IconButton Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| variant | 'default' \| 'primary' \| 'outline-solid' \| 'tertiary' \| 'secondary' \| 'muted' \| 'destructive' | 'default' | The visual style variant |
| size | 'small' \| 'default' \| 'large' | 'default' | The size of the button |
| loading | boolean | false | Shows loading spinner when true |
| disabled | boolean | false | Disables the button |
| asChild | boolean | false | Render as a child element |
| aria-label | string | Required | Accessible label describing the action |
| aria-describedby | string | - | Additional description for complex actions |
IconButton Sizes
- small: 28px × 28px button with 16px recommended icon size
- default: 40px × 40px button with 20px recommended icon size
- large: 48px × 48px button with 24px recommended icon size
IconButton Examples
import { IconButton } from '@lua-global/luaniverse';
import { UserIcon, Trash, Camera, ArrowLeft } from '@lua-global/luaniverse';
// Navigation
<IconButton variant="muted" aria-label="Go back">
<ArrowLeft size={20} />
</IconButton>
// Actions
<IconButton variant="primary" aria-label="Take photo">
<Camera size={20} />
</IconButton>
// Destructive actions
<IconButton variant="destructive" aria-label="Delete permanently">
<Trash size={20} />
</IconButton>
// Loading state
<IconButton loading aria-label="Saving changes">
<UserIcon size={20} />
</IconButton>
// Different sizes
<IconButton size="small" aria-label="Small action">
<UserIcon size={16} />
</IconButton>Badge
A flexible badge component for displaying status, counts, and labels with multiple variants and color options.
import { Badge } from '@lua-global/luaniverse';
function App() {
return (
<div>
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="success">Success</Badge>
<Badge variant="warning">Warning</Badge>
</div>
);
}Badge Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| variant | 'default' \| 'secondary' \| 'destructive' \| 'outline-solid' \| 'success' \| 'warning' | 'default' | The visual style variant |
| asChild | boolean | false | Render as a child element |
| aria-label | string | - | Accessible label for the badge |
Badge Variants
- default: Primary color background with white text
- secondary: Gray background using CSS variables
- destructive: Red background for errors and warnings
- outline: Transparent background with border
- success: Green background for positive status
- warning: Yellow background for cautionary information
Badge Examples
// Status badges
<Badge variant="success">Active</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="warning">Pending</Badge>
// Count badges
<Badge>3</Badge>
<Badge variant="secondary">New</Badge>
// As a link
<Badge asChild>
<a href="/notifications">5 unread</a>
</Badge>Textarea
A multi-line text input component with built-in label, description, error states, and accessibility features.
import { Textarea } from '@lua-global/luaniverse';
function App() {
return (
<div>
<Textarea
label="Message"
placeholder="Enter your message..."
/>
<Textarea
label="Feedback"
description="Please provide detailed feedback"
error="This field is required"
/>
</div>
);
}Textarea Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| label | string | - | Label text displayed above the textarea |
| description | string | - | Helper text displayed below the textarea |
| error | string | - | Error message displayed below the textarea |
| success | string | - | Success message displayed below the textarea |
| resize | 'none' \| 'vertical' \| 'horizontal' \| 'both' | 'vertical' | Resize behavior |
| disabled | boolean | false | Disables the textarea |
| aria-describedby | string | - | Additional description references |
| aria-invalid | boolean | - | Indicates validation state |
Textarea Examples
// Basic textarea
<Textarea
label="Comments"
placeholder="Add your comments..."
/>
// With description
<Textarea
label="Bio"
description="Tell us about yourself (max 500 characters)"
placeholder="Write your bio..."
/>
// Error state
<Textarea
label="Required Field"
error="This field is required"
placeholder="Please enter text..."
/>
// Success state
<Textarea
label="Feedback"
success="Thank you for your feedback!"
value="Great product!"
/>
// Disabled state
<Textarea
label="Read Only"
disabled
value="This content cannot be edited"
/>
// Custom resize behavior
<Textarea
label="Notes"
resize="both"
placeholder="Resize me in any direction..."
/>Icons
Luaniverse includes a comprehensive collection of icons for Lua applications.
Standard Icons
Standard icons inherit their parent's text color by default, making them work properly with different backgrounds and themes.
import {
ArrowLeft, ArrowUp, Camera, Chat, Clock, ImageIcon, Logo, MapPin,
Microphone, Minus, Paperclip, PaperPlane, PaperPlaneTilt, Plus,
RecordIcon, SignOut, Tag, Trash, UserIcon, VideoCamera, XIcon
} from '@lua-global/luaniverse';
function App() {
return (
<div>
<UserIcon size={24} />
<Camera size={24} color="#3B82F6" />
<Trash size={24} aria-label="Delete item" />
<XIcon size={24} title="Close dialog" />
</div>
);
}Available Standard Icons
- ArrowLeft - Left-pointing arrow for navigation
- ArrowUp - Up-pointing arrow for navigation
- Camera - Camera icon for photo actions
- Chat - Chat/message icon for communication
- Clock - Clock icon for time and scheduling
- ImageIcon - Generic image icon
- Logo - Lua logo
- MapPin - Location/map pin icon
- Microphone - Microphone for audio recording
- Minus - Minus/remove icon for actions
- Paperclip - Attachment icon
- PaperPlane - Send/paper plane icon
- PaperPlaneTilt - Tilted paper plane variant
- Plus - Plus/add icon for actions
- RecordIcon - Recording indicator
- SignOut - Sign out/logout icon
- Tag - Tag/label icon for categorization
- Trash - Delete/trash icon
- UserIcon - User profile icon
- VideoCamera - Video camera icon
- XIcon - Close/cancel icon
File Icons
File icons maintain their recognizable brand colors for easy file type identification.
import {
PDF, PNG, JPG, MP4, MP3, DOC, XLS, PPT, Zip, CSS, JS, JSON,
HTML, SVG, AI, PSD, Fig, Sketch
} from '@lua-global/luaniverse';
function FileList() {
return (
<div>
<PDF aria-label="PDF document" />
<PNG aria-label="PNG image" />
<JS aria-label="JavaScript file" />
<DOC aria-label="Word document" />
</div>
);
}Available File Icons
Images: PDF, PNG, JPG, SVG, AI, PSD, Fig, Sketch
Documents: DOC, XLS, PPT
Media: MP4, MP3
Code: CSS, JS, JSON, HTML
Archives: Zip
Icon Props
All icons accept these common props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| size | number | 24 | The size of the icon in pixels |
| color | string | "currentColor" | The color of the icon (standard icons only) |
| aria-label | string | - | Accessible label for the icon |
| aria-describedby | string | - | Additional description |
| aria-hidden | boolean | - | Hide from screen readers (for decorative icons) |
| title | string | - | Tooltip text that appears on hover |
Icon Accessibility
Icons are designed with accessibility in mind and include default aria-labels based on their names:
// Automatic aria-label (aria-label="User")
<UserIcon size={24} />
// Custom aria-label overrides the default
<UserIcon size={24} aria-label="User profile" />
// Icons with tooltips
<Trash size={24} title="Delete this item permanently" />
// Decorative icons in buttons (button provides the label)
<button aria-label="Delete item">
<Trash size={20} aria-hidden={true} />
</button>
// Default aria-labels for common icons:
<XIcon /> {/* aria-label="Close" */}
<ArrowLeft /> {/* aria-label="Arrow left" */}
<PaperPlane /> {/* aria-label="Send" */}
<MagnifyingGlass /> {/* aria-label="Search" */}
<Plus /> {/* aria-label="Plus" */}
<Minus /> {/* aria-label="Minus" */}Default Aria-Labels
Standard icons automatically get meaningful aria-labels:
- XIcon → "Close"
- ArrowLeft → "Arrow left"
- PaperPlane → "Send"
- MagnifyingGlass → "Search"
- UserIcon → "User"
- Plus/Minus → "Plus"/"Minus"
- And many more based on component names
This improves accessibility out of the box while still allowing customization.
Development
Prerequisites
- Node.js >= 18
- pnpm >= 8
Setup
# Install dependencies
pnpm install
# Start development
pnpm dev
# Build library
pnpm build
# Run Storybook
pnpm storybookBuilding
The library is built with tsup for optimal bundling:
- ESM:
dist/index.mjs - CJS:
dist/index.js - Types:
dist/index.d.ts - Styles:
dist/styles.css
Tree Shaking
The library is fully tree-shakable. Import only what you need:
// ✅ Tree-shakable - only Button and UserIcon are bundled
import { Button, UserIcon } from '@lua-global/luaniverse';
// ❌ Imports everything
import * as Luaniverse from '@lua-global/luaniverse';Accessibility
Luaniverse components are built with accessibility as a priority:
- WCAG AA Compliance - All components meet accessibility standards
- Keyboard Navigation - Full keyboard support with proper focus management
- Screen Reader Support - Proper ARIA labels and descriptions
- High Contrast - Works with high contrast modes and themes
- Development Warnings - Console warnings for missing accessibility attributes in development
Accessibility Best Practices
// ✅ Good - Descriptive labels
<IconButton aria-label="Delete this message">
<Trash size={20} />
</IconButton>
// ✅ Good - Additional context
<IconButton
aria-label="Delete"
aria-describedby="delete-description"
>
<Trash size={20} />
</IconButton>
// ❌ Bad - No accessible label
<IconButton>
<Trash size={20} />
</IconButton>Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT License - see the LICENSE file for details.
Support
For questions and support, please open an issue on GitHub.
