@johngarcia9110/invariant
v0.1.5
Published
A reusable React component library with Tailwind CSS
Maintainers
Readme
@johngarcia9110/invariant
A reusable React component library built with TypeScript and Tailwind CSS.
Installation
npm install @johngarcia9110/invariantPeer Dependencies: React 18+ is required. This library uses Tailwind CSS v4.
Usage
CSS is automatically injected when you import components - no additional setup required.
import {
Button,
Card,
CardHeader,
CardBody,
Input,
} from '@johngarcia9110/invariant';
function App() {
return (
<Card>
<CardHeader>
<h2>Sign In</h2>
</CardHeader>
<CardBody>
<Input label="Email" type="email" placeholder="Enter your email" />
<Input label="Password" type="password" />
<Button variant="primary" size="md">
Sign In
</Button>
</CardBody>
</Card>
);
}Components
Button
<Button variant="primary" size="md">Click me</Button>
<Button variant="secondary" size="sm">Secondary</Button>
<Button variant="outline" size="lg">Outline</Button>
<Button isLoading>Submitting</Button>
<Button isLoading loadingText="Saving...">Save</Button>Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| variant | 'primary' | 'secondary' | 'outline' | 'primary' | Button style variant |
| size | 'sm' | 'md' | 'lg' | 'md' | Button size |
| isLoading | boolean | false | Shows loading spinner and disables button |
| loadingText | string | 'Loading...' | Text displayed while loading |
Plus all standard button HTML attributes.
Card
<Card>
<CardHeader>Header content</CardHeader>
<CardBody>Body content</CardBody>
<CardFooter>Footer content</CardFooter>
</Card>Input
<Input
label="Username"
placeholder="Enter username"
helperText="Must be at least 3 characters"
/>
<Input label="Email" error="Invalid email address" />
<Input label="Email" success="Email is available" />
<Input size="lg" leadingIcon={<SearchIcon />} placeholder="Search..." />
<Input prefix="$" suffix="USD" placeholder="0.00" />
<Input isLoading placeholder="Loading..." />Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| label | string | - | Label text above input |
| error | string | - | Error message (red styling) |
| success | string | - | Success message (green styling) |
| helperText | string | - | Helper text below input |
| size | 'sm' | 'md' | 'lg' | 'md' | Input size |
| leadingIcon | ReactNode | - | Icon at start of input |
| trailingIcon | ReactNode | - | Icon at end of input |
| prefix | ReactNode | - | Prefix content (e.g., "$") |
| suffix | ReactNode | - | Suffix content (e.g., "USD") |
| isLoading | boolean | false | Shows loading spinner |
Plus all standard input HTML attributes.
Utilities
cn()
A utility function for merging Tailwind classes:
import { cn } from '@johngarcia9110/invariant'
<div className={cn('p-4', isActive && 'bg-blue-500', className)}>Theme
ThemeProvider
Wrap your app with ThemeProvider to enable theme management:
import { ThemeProvider } from '@johngarcia9110/invariant';
function App() {
return (
<ThemeProvider defaultMode="light">
<YourApp />
</ThemeProvider>
);
}Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| mode | 'light' | 'dark' | 'system' | - | Controlled theme mode |
| defaultMode | 'light' | 'dark' | 'light' | Default mode (uncontrolled) |
| theme | ThemeConfig | - | Light mode color overrides |
| darkTheme | ThemeConfig | - | Dark mode color overrides |
| storageKey | string | 'invariant-theme-mode' | localStorage key for persistence |
| disableDarkClass | boolean | false | Disable .dark class on <html> |
useTheme Hook
Access and control the theme from any component:
import { useTheme, Button } from '@johngarcia9110/invariant';
function ThemeToggle() {
const { mode, setMode, toggleMode } = useTheme();
return (
<div>
<p>Current theme: {mode}</p>
<Button onClick={toggleMode}>Toggle Theme</Button>
<Button onClick={() => setMode('system')}>Use System</Button>
</div>
);
}Returns:
| Property | Type | Description |
|----------|------|-------------|
| mode | 'light' | 'dark' | Current effective theme |
| configuredMode | 'light' | 'dark' | 'system' | User's configured preference |
| setMode | (mode) => void | Set theme to light, dark, or system |
| toggleMode | () => void | Toggle between light and dark |
| colors | ThemeColors | Current resolved color values |
Custom Colors
Override theme colors using the theme and darkTheme props:
<ThemeProvider
theme={{
colors: {
primary: 'oklch(0.6 0.2 250)',
secondary: 'oklch(0.7 0.15 180)',
}
}}
darkTheme={{
colors: {
primary: 'oklch(0.7 0.2 250)',
}
}}
>
<App />
</ThemeProvider>Available color tokens: background, foreground, primary, primaryForeground, secondary, secondaryForeground, destructive, muted, mutedForeground, accent, accentForeground, border, input, ring, card, cardForeground, popover, popoverForeground.
CSS Variables
You can also override colors directly in CSS:
:root {
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
}Dark Mode
The ThemeProvider automatically manages dark mode by:
- Adding/removing the
.darkclass on<html> - Persisting user preference to localStorage
- Detecting system preference when
mode="system"
TypeScript
Types are included and exported from the package:
import type {
// Component props
ButtonProps,
CardProps,
InputProps,
// Theme types
ThemeProviderProps,
ThemeConfig,
ThemeColors,
ThemeMode,
ThemeContextValue,
} from '@johngarcia9110/invariant';License
MIT
