apple-liquid-glass-ui
v1.0.9
Published
A premium React UI library with Apple-inspired glass surfaces. Features true glassmorphism with translucent backgrounds, blur effects, and smooth animations.
Downloads
971
Maintainers
Readme
glass-ui
A premium React UI library with Apple-inspired glass surfaces. Features true glassmorphism with translucent backgrounds, blur effects, and smooth animations.
Perfect for creating modern, elegant UIs with authentic macOS/iOS feel.
🚀 View Live Demo | 📦 npm | 📖 GitHub
Features
- True Glassmorphism: Translucent backgrounds with backdrop blur and saturation filters
- Apple-Inspired Design: Authentic macOS/iOS aesthetic with refined typography and spacing
- Dual API: Use as React components or standalone CSS classnames
- Dark Mode Support: Seamless light/dark theme switching
- Mobile-First: Touch-friendly with responsive breakpoints
- 27+ Components: Complete UI toolkit from buttons to navigation
- TypeScript: Full type definitions included
- Tree-Shakeable: Import only what you need
- Zero Runtime: No CSS-in-JS overhead - just static CSS
⚡️ Performance
Apple Liquid Glass UI is built for speed. Unlike traditional CSS-in-JS libraries, we use zero runtime CSS for maximum performance.
Bundle Size
| Library | Full Bundle (gzipped) | vs Glass UI | |---------|----------------------|-------------| | Apple Liquid Glass UI | 24.1 KB | baseline | | Material-UI (MUI) | 95.0 KB | +293% | | Chakra UI | 85.0 KB | +252% | | Ant Design | 180.0 KB | +645% | | Mantine | 75.0 KB | +211% |
Key Metrics:
- 📦 24.1 KB total (gzipped) - JavaScript + CSS combined
- 🎯 6.6 KB JavaScript (gzipped)
- 🎨 10.9 KB CSS (gzipped)
- ⚡️ 72% smaller than Chakra UI
- 🚀 75% smaller than Material-UI
Zero Runtime CSS-in-JS
Traditional CSS-in-JS libraries (styled-components, emotion, etc.) have runtime overhead:
- ❌ Parse style objects on every render
- ❌ Generate class names dynamically
- ❌ Inject styles into the DOM
- ❌ Additional ~30KB bundle size
Apple Liquid Glass UI uses static CSS:
- ✅ Zero runtime overhead
- ✅ Styles loaded once, cached forever
- ✅ No style computation during renders
- ✅ Maximum performance
Tree-Shaking Support
Import only what you need. Each component adds minimal weight:
| Component | Size (with CSS) | |-----------|----------------| | Button | 3.9 KB | | Input | 4.7 KB | | Badge | 2.5 KB | | Avatar | 3.3 KB | | Card | 4.8 KB | | DatePicker | 14.5 KB |
Average component size: ~5.1 KB (uncompressed source)
Performance Benchmarks
Run the benchmark yourself:
npm run benchmark
# or open scripts/benchmark.html in your browserTypical results (1000 components):
- 30-40% faster initial mount vs CSS-in-JS
- 25-35% faster updates vs CSS-in-JS
- 0KB runtime vs ~30KB for styled-components
Why It Matters
For a typical application using 10-15 components:
- Glass UI: ~15-20 KB total
- MUI/Chakra: ~100 KB total
- You save: ~80 KB (4-5x smaller bundle)
Result: Faster page loads, better Core Web Vitals, improved user experience.
Installation
npm install apple-liquid-glass-uiQuick Start
Import the CSS tokens and utilities in your app entry point:
import 'apple-liquid-glass-ui/dist/index.css';Set the theme attribute on your HTML element:
<html data-gl-theme="light">
<!-- or data-gl-theme="dark" -->
</html>Add a colorful background to see the glass effect:
import { Button, Card } from 'apple-liquid-glass-ui';
function App() {
return (
<div
className="gl-base"
style={{
minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%)',
backgroundAttachment: 'fixed',
padding: '2rem'
}}
>
<Card padding="lg">
<h1>Welcome to apple-liquid-glass-ui</h1>
<Button variant="primary">Get Started</Button>
</Card>
</div>
);
}Dual API
apple-liquid-glass-ui supports both React components and classname utilities.
React Components
import { Button } from 'apple-liquid-glass-ui';
<Button variant="primary" size="md">
Click me
</Button>Classname Utilities
<button class="gl-btn gl-btn-primary gl-btn-md">
Click me
</button>Both approaches provide the same authentic Apple Glass styling with blur effects, translucency, and smooth animations.
Components
Button
import { Button } from 'glass-ui';
<Button variant="primary" size="md">Primary</Button>
<Button variant="subtle" size="md">Subtle</Button>
<Button variant="ghost" size="md">Ghost</Button>
// Icon button
<Button variant="primary" size="md" iconOnly>
×
</Button>Classname API:
<button class="gl-btn gl-btn-primary gl-btn-md">Primary</button>
<button class="gl-btn gl-btn-subtle gl-btn-md">Subtle</button>
<button class="gl-btn gl-btn-ghost gl-btn-md">Ghost</button>
<button class="gl-btn gl-btn-primary gl-btn-icon-md">×</button>Input
import { Input, Textarea, InputGroup } from 'glass-ui';
<Input size="md" placeholder="Enter text..." />
<Textarea size="md" placeholder="Enter text..." />
<InputGroup
label="Email"
hint="We'll never share your email"
error="Invalid email"
required
>
<Input type="email" error />
</InputGroup>Classname API:
<input class="gl-input gl-input-md" placeholder="Enter text..." />
<textarea class="gl-input gl-textarea gl-textarea-md" placeholder="Enter text..."></textarea>
<div class="gl-input-group">
<label class="gl-input-label">Email <span style="color: var(--gl-color-error)"> *</span></label>
<input class="gl-input gl-input-md gl-input-invalid" type="email" />
<span class="gl-input-error">Invalid email</span>
</div>Card
import { Card, CardHeader, CardBody, CardFooter } from 'glass-ui';
<Card variant="default" padding="md">
<CardHeader title="Card Title" description="Card description" />
<CardBody>Content goes here</CardBody>
<CardFooter>
<Button variant="ghost">Cancel</Button>
<Button variant="primary">Save</Button>
</CardFooter>
</Card>Classname API:
<div class="gl-card gl-card-md">
<div class="gl-card-header">
<div>
<h3 class="gl-card-header-title">Card Title</h3>
<p class="gl-card-header-description">Card description</p>
</div>
</div>
<div class="gl-card-body">Content goes here</div>
<div class="gl-card-footer">
<button class="gl-btn gl-btn-ghost gl-btn-md">Cancel</button>
<button class="gl-btn gl-btn-primary gl-btn-md">Save</button>
</div>
</div>Sheet (Modal)
import { Sheet } from 'glass-ui';
const [open, setOpen] = useState(false);
<Sheet open={open} onClose={() => setOpen(false)} size="md">
<Sheet.Header title="Sheet Title" onClose={() => setOpen(false)} />
<Sheet.Body>Content goes here</Sheet.Body>
<Sheet.Footer>
<Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
<Button variant="primary">Confirm</Button>
</Sheet.Footer>
</Sheet>Select & Dropdown
import { Select, Dropdown } from 'glass-ui';
// Native select
<Select size="md">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</Select>
// Custom dropdown
<Dropdown
options={[
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' },
]}
value={value}
onChange={setValue}
size="md"
/>Checkbox, Radio, Switch
import { Checkbox, Radio, Switch } from 'glass-ui';
<Checkbox label="Accept terms" size="md" />
<Radio name="option" label="Option 1" size="md" />
<Switch label="Enable notifications" size="md" />Classname API:
<label class="gl-checkbox-wrapper">
<input type="checkbox" class="gl-checkbox-input" />
<span class="gl-checkbox-box"></span>
<span class="gl-checkbox-label">Accept terms</span>
</label>
<label class="gl-radio-wrapper">
<input type="radio" class="gl-radio-input" name="option" />
<span class="gl-radio-circle"></span>
<span class="gl-radio-label">Option 1</span>
</label>
<label class="gl-switch-wrapper">
<input type="checkbox" class="gl-switch-input" />
<span class="gl-switch-track"></span>
<span class="gl-switch-label">Enable notifications</span>
</label>DatePicker
Glass-styled calendar date picker with animations:
import { DatePicker } from 'apple-liquid-glass-ui';
const [date, setDate] = useState<Date | undefined>();
// Basic date picker
<DatePicker
label="Select Date"
placeholder="Choose a date"
value={date}
onChange={setDate}
size="md"
/>
// With constraints
<DatePicker
label="Birth Date"
placeholder="MM/DD/YYYY"
value={birthDate}
onChange={setBirthDate}
maxDate={new Date()}
size="md"
/>
// Different sizes
<DatePicker label="Small" size="sm" />
<DatePicker label="Medium" size="md" />
<DatePicker label="Large" size="lg" />Props:
value: Selected date (Date object)onChange: Callback with selected datelabel: Optional label textplaceholder: Input placeholderminDate: Minimum selectable datemaxDate: Maximum selectable datedisabled: Disable pickersize:'sm' | 'md' | 'lg'
Features:
- Full calendar view with month/year navigation
- Today button for quick access
- Min/max date constraints
- Glass-styled dropdown with blur effects
- Smooth animations
- Dark mode support
Badge
import { Badge } from 'glass-ui';
<Badge variant="primary" size="md">New</Badge>
<Badge variant="success" size="md">Success</Badge>
<Badge variant="default" size="md" dot />Avatar
import { Avatar, AvatarGroup } from 'glass-ui';
<Avatar src="/path/to/image.jpg" size="md" />
<Avatar fallback="John Doe" size="md" status="online" />
<AvatarGroup>
<Avatar fallback="JD" size="md" />
<Avatar fallback="AS" size="md" />
<Avatar fallback="BK" size="md" />
</AvatarGroup>Typography
import { Heading, Text, Code, Link } from 'glass-ui';
<Heading level={1}>Main Heading</Heading>
<Text size="md" variant="secondary">Paragraph text</Text>
<Code>const foo = 'bar';</Code>
<Link href="#">Learn more</Link>Classname API:
<h1 class="gl-heading gl-h1">Main Heading</h1>
<p class="gl-text gl-text-md gl-text-secondary">Paragraph text</p>
<code class="gl-code">const foo = 'bar';</code>
<a href="#" class="gl-link">Learn more</a>Tooltip
import { Tooltip } from 'glass-ui';
<Tooltip content="Tooltip text" position="top">
<Button>Hover me</Button>
</Tooltip>Layout
import { Container, Stack, Grid, Flex, Divider, Spacer, Center } from 'apple-liquid-glass-ui';
<Container size="lg">
<Stack gap="lg">
<div>Item 1</div>
<div>Item 2</div>
</Stack>
</Container>
<Grid cols={3} gap="lg">
<div>Grid item 1</div>
<div>Grid item 2</div>
<div>Grid item 3</div>
</Grid>
// Flex - Flexible layouts with props
<Flex direction="row" justify="between" align="center" gap="md">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</Flex>
<Flex direction="column" align="start" gap="sm">
<Button variant="ghost">Action 1</Button>
<Button variant="ghost">Action 2</Button>
</Flex>
<Divider />
<Divider vertical />Flex Props:
direction:'row' | 'column' | 'row-reverse' | 'column-reverse'align:'start' | 'center' | 'end' | 'stretch' | 'baseline'justify:'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'wrap:'nowrap' | 'wrap' | 'wrap-reverse'gap:'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'inline: Display as inline-flex
Classname API:
<div class="gl-container gl-container-lg">
<div class="gl-stack gl-stack-gap-lg">
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
<div class="gl-grid gl-grid-cols-3 gl-grid-gap-lg">
<div>Grid item 1</div>
<div>Grid item 2</div>
<div>Grid item 3</div>
</div>
<!-- Flex with utility classes -->
<div class="d-flex justify-content-between align-items-center gap-3">
<div>Item 1</div>
<div>Item 2</div>
</div>
<hr class="gl-divider" />
<span class="gl-divider-vertical"></span>Alert
Display important messages with glass-styled alerts:
import { Alert } from 'apple-liquid-glass-ui';
<Alert
variant="success"
title="Success!"
message="Your changes have been saved."
closable
/>
<Alert
variant="error"
title="Error"
message="Something went wrong."
/>
<Alert
variant="warning"
title="Warning"
message="Please review before proceeding."
/>
<Alert variant="info">
Simple info alert without title
</Alert>Props:
variant:'default' | 'success' | 'error' | 'warning' | 'info'title: Optional alert titlemessage: Alert message contentclosable: Show close buttononClose: Callback when closed
Navigation
Apple-style navigation with glass blur effect:
import { GlassTopNav, GlassNavItem, GlassBottomNav } from 'apple-liquid-glass-ui';
// Top Navigation
<GlassTopNav>
<GlassTopNav.Left>
<div className="gl-logo">MyApp</div>
</GlassTopNav.Left>
<GlassTopNav.Center>
<GlassNavItem active onClick={() => setActive('home')}>
Home
</GlassNavItem>
<GlassNavItem onClick={() => setActive('about')}>
About
</GlassNavItem>
<GlassNavItem onClick={() => setActive('contact')}>
Contact
</GlassNavItem>
</GlassTopNav.Center>
<GlassTopNav.Right>
<Button variant="primary" size="sm">Sign In</Button>
</GlassTopNav.Right>
</GlassTopNav>
// Bottom Navigation (shows on mobile <768px)
<GlassBottomNav>
<GlassNavItem active icon="🏠" label="Home" />
<GlassNavItem icon="🔍" label="Search" />
<GlassNavItem icon="⚙️" label="Settings" />
</GlassBottomNav>Features:
- Sticky top navigation with blur background
- Mobile bottom navigation that appears on small screens
- Active state styling with accent color
- Compound component pattern for flexible layouts
Slider
Range input with glass styling and optional value display:
import { Slider } from 'apple-liquid-glass-ui';
const [volume, setVolume] = useState(50);
<Slider
label="Volume"
value={volume}
onChange={setVolume}
min={0}
max={100}
showValue
size="md"
/>
<Slider
label="Temperature"
value={temp}
onChange={setTemp}
min={-20}
max={40}
step={0.5}
showValue
unit="°C"
/>Props:
label: Optional label textvalue: Current valueonChange: Callback with new valuemin,max,step: Range configurationshowValue: Display current valueunit: Optional unit suffixsize:'sm' | 'md' | 'lg'
Tabs
Content organization with default or underline variants:
import { Tabs } from 'apple-liquid-glass-ui';
<Tabs
items={[
{ key: 'overview', label: 'Overview', content: <div>Overview content</div> },
{ key: 'details', label: 'Details', content: <div>Details content</div> },
{ key: 'settings', label: 'Settings', content: <div>Settings content</div> }
]}
defaultActiveKey="overview"
onChange={(key) => console.log('Tab changed:', key)}
/>
// Underline variant
<Tabs
variant="underline"
items={[...]}
/>Props:
items: Array of{ key, label, content }defaultActiveKey: Initial active tabonChange: Callback when tab changesvariant:'default' | 'underline'
Progress
Progress indicators with optional percentage display:
import { Progress } from 'apple-liquid-glass-ui';
// Determinate progress
<Progress value={45} showPercent size="md" />
// Indeterminate (loading)
<Progress indeterminate size="md" />
// With label
<Progress
value={75}
showPercent
label="Upload Progress"
size="lg"
/>
// Variants
<Progress value={60} variant="success" />
<Progress value={30} variant="error" />Props:
value: Progress value (0-100)indeterminate: Show loading animationshowPercent: Display percentage textlabel: Optional label above progress barvariant:'default' | 'success' | 'error' | 'warning'size:'sm' | 'md' | 'lg'
Spinner
Loading indicators with optional overlay mode:
import { Spinner } from 'apple-liquid-glass-ui';
// Basic spinner
<Spinner size="md" />
// With label
<Spinner label="Loading..." size="lg" />
// Full-page overlay
<Spinner overlay label="Processing..." size="lg" />
// Different sizes
<Spinner size="sm" />
<Spinner size="md" />
<Spinner size="lg" />Props:
size:'sm' | 'md' | 'lg'label: Optional loading textoverlay: Full-page overlay mode with glass background
Skeleton
Bootstrap-style loading placeholders with smooth animations:
import { Skeleton, SkeletonText, SkeletonAvatar, SkeletonCard, SkeletonTable } from 'apple-liquid-glass-ui';
// Basic skeleton
<Skeleton variant="text" width="100%" />
<Skeleton variant="rectangular" width={200} height={100} />
<Skeleton variant="circular" width={40} height={40} />
<Skeleton variant="rounded" width="100%" height={200} />
// Animation types
<Skeleton animation="wave" /> // Default shimmering effect
<Skeleton animation="pulse" /> // Opacity pulsing
<Skeleton animation={false} /> // No animation
// Text lines
<SkeletonText lines={3} />
<SkeletonText lines={5} width="80%" />
// Avatar placeholder
<SkeletonAvatar size="sm" />
<SkeletonAvatar size="md" />
<SkeletonAvatar size="lg" />
<SkeletonAvatar size="xl" />
// Card with avatar and text
<SkeletonCard avatar lines={3} />
<SkeletonCard avatar={false} lines={5} />
// Table placeholder
<SkeletonTable rows={5} columns={4} />
<SkeletonTable rows={10} columns={6} animation="pulse" />Skeleton Props:
variant:'text' | 'circular' | 'rectangular' | 'rounded'width: Width in pixels or percentageheight: Height in pixels or percentageanimation:'wave' | 'pulse' | false
SkeletonText Props:
lines: Number of lines (default: 3)width: Width for all lines except lastanimation: Animation type
SkeletonAvatar Props:
size:'sm' | 'md' | 'lg' | 'xl'animation: Animation type
SkeletonCard Props:
avatar: Show avatar (default: true)lines: Number of text lines (default: 3)animation: Animation type
SkeletonTable Props:
rows: Number of rows (default: 5)columns: Number of columns (default: 4)animation: Animation type
Menu
Context menus and dropdown menus with glass styling:
import { Menu } from 'apple-liquid-glass-ui';
<Menu
trigger={<Button>Actions</Button>}
position="bottom"
items={[
{ key: 'edit', label: 'Edit', icon: '✏️', shortcut: '⌘E', onClick: handleEdit },
{ key: 'duplicate', label: 'Duplicate', icon: '📋', onClick: handleDuplicate },
{ type: 'divider' },
{ type: 'label', label: 'Danger Zone' },
{ key: 'delete', label: 'Delete', icon: '🗑️', danger: true, onClick: handleDelete },
{ key: 'disabled', label: 'Disabled Action', disabled: true }
]}
/>Props:
trigger: Element to click to open menuposition:'top' | 'bottom' | 'left' | 'right'items: Array of menu items, dividers, or labels
MenuItem Type:
key: Unique identifierlabel: Display texticon: Optional icon elementshortcut: Optional keyboard shortcut displaydanger: Red text for destructive actionsdisabled: Disable interactiononClick: Callback when clicked
Table
Data tables with sorting, hover effects, and glass styling:
import { Table } from 'apple-liquid-glass-ui';
// Basic table
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Name</Table.Head>
<Table.Head>Email</Table.Head>
<Table.Head align="right">Status</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>Sarah Chen</Table.Cell>
<Table.Cell>[email protected]</Table.Cell>
<Table.Cell align="right">
<Badge variant="success">Active</Badge>
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
// Table with sorting
const [sortColumn, setSortColumn] = useState<string | null>(null);
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
<Table hover striped>
<Table.Header>
<Table.Row>
<Table.Head
sortable
sorted={sortColumn === 'name' ? sortDirection : null}
onSort={() => {
if (sortColumn === 'name') {
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
} else {
setSortColumn('name');
setSortDirection('asc');
}
}}
>
Name
</Table.Head>
<Table.Head>Project</Table.Head>
<Table.Head align="right">Progress</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{sortedData.map((row) => (
<Table.Row key={row.id}>
<Table.Cell>{row.name}</Table.Cell>
<Table.Cell>{row.project}</Table.Cell>
<Table.Cell align="right">{row.progress}%</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
// Compact table
<Table size="sm">
{/* ... */}
</Table>Props:
- Table:
hover,striped,size('sm' | 'md' | 'lg') - Table.Head:
sortable,sorted('asc' | 'desc' | null),onSort,align('left' | 'center' | 'right') - Table.Cell:
align('left' | 'center' | 'right')
Features:
- Glassmorphism styling with backdrop blur
- Sortable column headers with arrow indicators
- Hover effect on rows
- Striped rows for better readability
- Three size variants (sm, md, lg)
- Responsive design with horizontal scrolling
- Cell alignment options
Theming
Toggle between light and dark themes:
// Toggle theme
const theme = document.documentElement.getAttribute('data-gl-theme');
document.documentElement.setAttribute('data-gl-theme', theme === 'light' ? 'dark' : 'light');CSS Variables
All design tokens are available as CSS variables:
var(--gl-color-bg)
var(--gl-color-surface)
var(--gl-color-border)
var(--gl-color-accent)
var(--gl-radius-lg)
var(--gl-blur-surface)
var(--gl-shadow-soft)
var(--gl-space-md)
var(--gl-font-size-md)See tokens.css for the complete list.
License
MIT
