ios-style-ui
v1.0.0
Published
Beautiful iOS-style UI components for web applications
Maintainers
Readme
📱 iOS Style UI
Beautiful iOS-style UI components for web applications
Bring the elegant Apple iOS design system to your web projects with zero dependencies.
Demo · Report Bug · [Request Feature]https://github.com/liladhiee-web/ios-style-ui/issues)
✨ Features
- 🎨 Authentic iOS Design - Pixel-perfect iOS 17+ components
- 🌙 Dark Mode Support - Automatic light/dark theme switching
- 📱 Mobile First - Optimized for touch interactions
- 🪶 Lightweight - Zero dependencies, under 30KB minified
- 🔧 Easy to Use - Simple API with sensible defaults
- ♿ Accessible - Built with accessibility in mind
- 📦 Tree Shakeable - Import only what you need
- 🎭 Haptic Feedback - Native vibration support on mobile
📦 Installation
NPM
npm install ios-style-uiYarn
yarn add ios-style-uipnpm
pnpm add ios-style-uiCDN
<!-- Latest version -->
<script src="https://unpkg.com/ios-style-ui"></script>
<!-- Specific version -->
<script src="https://unpkg.com/[email protected]"></script>🚀 Quick Start
Browser (Script Tag)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/ios-style-ui"></script>
</head>
<body>
<div id="app"></div>
<script>
// Initialize the library
iOSUI.init();
// Create a button
iOSUI.createButton({
text: 'Hello World',
variant: 'primary',
onClick: () => iOSUI.Toast.success('Button clicked!')
}).mount('#app');
</script>
</body>
</html>ES Modules
import iOSUI from 'ios-style-ui';
// Initialize
iOSUI.init();
// Create components
const button = new iOSUI.Button({
text: 'Click Me',
onClick: () => console.log('Clicked!')
});
button.mount(document.getElementById('app'));CommonJS
const iOSUI = require('ios-style-ui');
iOSUI.init();
// Use components...📚 Components
Table of Contents
| Category | Components | |----------|------------| | Buttons & Controls | Button, Switch, Segmented Control, Stepper | | Form Inputs | Input, SearchBar, Checkbox | | Layout | Card, List | | Feedback | Alert, Action Sheet, Toast, Loading | | Overlays | Modal, Sheet | | Navigation | NavBar, TabBar | | Display | Avatar, Badge, Progress, Spinner |
🔘 Button
iOS-style buttons with multiple variants and states.
const button = iOSUI.createButton({
text: 'Click Me',
variant: 'primary',
size: 'medium',
rounded: false,
block: false,
icon: null,
iconPosition: 'left',
disabled: false,
loading: false,
ripple: true,
onClick: (event) => {}
});
button.mount('#container');Button Variants
// Primary (default)
iOSUI.createButton({ text: 'Primary', variant: 'primary' });
// Secondary
iOSUI.createButton({ text: 'Secondary', variant: 'secondary' });
// Destructive (red)
iOSUI.createButton({ text: 'Delete', variant: 'destructive' });
// Outline
iOSUI.createButton({ text: 'Outline', variant: 'outline' });
// Text only
iOSUI.createButton({ text: 'Text', variant: 'text' });Button Sizes
iOSUI.createButton({ text: 'Small', size: 'small' });
iOSUI.createButton({ text: 'Medium', size: 'medium' });
iOSUI.createButton({ text: 'Large', size: 'large' });Button with Icon
iOSUI.createButton({
text: 'Download',
icon: '⬇️',
iconPosition: 'left' // or 'right'
});
// Icon only button
iOSUI.createButton({
icon: '❤️',
variant: 'secondary'
});Button States
const btn = iOSUI.createButton({ text: 'Submit' });
// Set loading state
btn.setLoading(true);
// Set disabled state
btn.setDisabled(true);
// Update text
btn.setText('New Text');Full Width Button
iOSUI.createButton({
text: 'Continue',
block: true,
rounded: true
});🔀 Switch
iOS-style toggle switch.
const toggle = iOSUI.createSwitch({
checked: false,
disabled: false,
color: '#34C759', // Custom color when on
onChange: (isChecked) => {
console.log('Switch is now:', isChecked);
}
});
toggle.mount('#container');Switch Methods
const toggle = iOSUI.createSwitch({ checked: false });
// Toggle the switch
toggle.toggle();
// Set specific state
toggle.setChecked(true);
// Get current state
const isOn = toggle.isChecked();📊 Segmented Control
iOS-style segmented control for switching between views.
const segments = iOSUI.createSegmentedControl({
items: ['Day', 'Week', 'Month', 'Year'],
selectedIndex: 0,
onChange: (index, item) => {
console.log(`Selected: ${item} at index ${index}`);
}
});
segments.mount('#container');Segmented Control Methods
// Change selection programmatically
segments.setSelectedIndex(2);➕➖ Stepper
Number stepper with increment/decrement controls.
const stepper = iOSUI.createStepper({
value: 1,
min: 0,
max: 10,
step: 1,
showValue: true,
onChange: (value) => {
console.log('New value:', value);
}
});
stepper.mount('#container');Stepper Methods
// Get current value
const value = stepper.getValue();
// Set value
stepper.setValue(5);
// Increment/Decrement
stepper.increment();
stepper.decrement();📝 Input
iOS-style text input fields.
const input = iOSUI.createInput({
type: 'text', // text, email, password, number, tel, url
label: 'Email Address',
placeholder: 'Enter your email',
value: '',
helper: 'We will never share your email',
error: null,
disabled: false,
textarea: false,
rows: 4, // for textarea
onChange: (value) => {},
onBlur: (value) => {}
});
input.mount('#container');Input Types
// Text input
iOSUI.createInput({
type: 'text',
label: 'Name',
placeholder: 'Enter your name'
});
// Password input
iOSUI.createInput({
type: 'password',
label: 'Password',
placeholder: '••••••••'
});
// Email input
iOSUI.createInput({
type: 'email',
label: 'Email',
placeholder: '[email protected]'
});
// Textarea
iOSUI.createInput({
textarea: true,
label: 'Message',
placeholder: 'Write your message...',
rows: 6
});Input Validation
const emailInput = iOSUI.createInput({
type: 'email',
label: 'Email',
onBlur: (value) => {
if (!value.includes('@')) {
emailInput.setError('Please enter a valid email');
} else {
emailInput.setError(null);
}
}
});Input Methods
// Get value
const value = input.getValue();
// Set value
input.setValue('[email protected]');
// Set error
input.setError('This field is required');
// Clear error
input.setError(null);🔍 SearchBar
iOS-style search bar with clear button and cancel option.
const searchBar = iOSUI.createSearchBar({
placeholder: 'Search',
value: '',
showCancel: true,
onSearch: (query) => {
console.log('Searching for:', query);
},
onChange: (value) => {
console.log('Input changed:', value);
},
onFocus: () => {
console.log('Search focused');
},
onBlur: () => {
console.log('Search blurred');
}
});
searchBar.mount('#container');SearchBar Methods
// Get current value
const query = searchBar.getValue();
// Set value
searchBar.setValue('iPhone');
// Focus the input
searchBar.focus();☑️ Checkbox
iOS-style checkbox and radio buttons.
// Checkbox
const checkbox = iOSUI.createCheckbox({
checked: false,
label: 'I agree to the terms',
disabled: false,
onChange: (isChecked) => {
console.log('Checked:', isChecked);
}
});
// Radio button
const radio = iOSUI.createCheckbox({
radio: true,
name: 'options',
value: 'option1',
label: 'Option 1',
onChange: (checked, value) => {
console.log('Selected:', value);
}
});Radio Button Group
const container = document.getElementById('radio-group');
['Small', 'Medium', 'Large'].forEach((size, index) => {
iOSUI.createCheckbox({
radio: true,
name: 'size',
value: size.toLowerCase(),
label: size,
checked: index === 1, // Medium selected by default
onChange: (checked, value) => {
if (checked) console.log('Selected size:', value);
}
}).mount(container);
});Checkbox Methods
// Check if checked
const isChecked = checkbox.isChecked();
// Set checked state
checkbox.setChecked(true);🃏 Card
iOS-style content cards.
const card = iOSUI.createCard({
image: 'https://example.com/image.jpg',
title: 'Card Title',
subtitle: 'Card subtitle',
content: '<p>Card content goes here...</p>',
footer: 'Footer content',
inset: true, // Add horizontal margin
onClick: () => {
console.log('Card clicked');
}
});
card.mount('#container');Card Examples
// Simple card
iOSUI.createCard({
title: 'Welcome',
content: 'Thanks for using our app!'
});
// Card with image
iOSUI.createCard({
image: '/hero.jpg',
title: 'Featured Article',
subtitle: 'Published today',
content: 'Lorem ipsum dolor sit amet...'
});
// Card with custom footer
const footerButton = iOSUI.createButton({
text: 'Learn More',
variant: 'text'
});
iOSUI.createCard({
title: 'Special Offer',
content: 'Get 50% off your first order!',
footer: footerButton.element
});📋 List
iOS-style list/table view with various accessories.
const list = iOSUI.createList({
header: 'SETTINGS',
inset: true,
items: [
{
title: 'Notifications',
subtitle: 'Manage alerts',
icon: '🔔',
iconColor: '#FF3B30',
accessory: 'switch',
checked: true,
onChange: (isOn) => console.log('Notifications:', isOn)
},
{
title: 'Sound',
icon: '🔊',
iconColor: '#007AFF',
value: 'Default',
accessory: 'chevron',
onClick: (item) => console.log('Sound clicked')
},
{
title: 'Privacy',
icon: '🔒',
iconColor: '#34C759',
accessory: 'chevron',
onClick: (item) => openPrivacySettings()
}
]
});
list.mount('#container');List Item Options
| Property | Type | Description |
|----------|------|-------------|
| title | string | Main text |
| subtitle | string | Secondary text below title |
| icon | string | Emoji or HTML for icon |
| iconColor | string | Background color for icon |
| value | string | Right-aligned value text |
| accessory | string | 'chevron' or 'switch' |
| checked | boolean | Initial switch state |
| onChange | function | Callback for switch toggle |
| onClick | function | Callback for row tap |
Add Item Dynamically
list.addItem({
title: 'New Item',
accessory: 'chevron',
onClick: () => {}
});⚠️ Alert
iOS-style alert dialogs.
// Basic alert
await iOSUI.Alert.show({
title: 'Alert Title',
message: 'This is the alert message.',
buttons: [
{ text: 'OK', style: 'default' }
]
});
// Alert with multiple buttons
const result = await iOSUI.Alert.show({
title: 'Save Changes?',
message: 'Do you want to save your changes before leaving?',
buttons: [
{ text: 'Discard', style: 'destructive' },
{ text: 'Cancel', style: 'cancel' },
{ text: 'Save', style: 'default' }
]
});
console.log('Clicked button index:', result.buttonIndex);
console.log('Clicked button text:', result.buttonText);Confirm Dialog
const confirmed = await iOSUI.Alert.confirm(
'Delete Item?',
'This action cannot be undone.'
);
if (confirmed) {
// User clicked OK
deleteItem();
}Prompt Dialog
const name = await iOSUI.Alert.prompt(
'What\'s your name?',
'Please enter your full name.',
'John Doe' // placeholder
);
if (name !== null) {
// User entered a value and clicked OK
console.log('Hello,', name);
}Alert with Input
const result = await iOSUI.Alert.show({
title: 'Enter Password',
message: 'Please enter your password to continue.',
input: {
type: 'password',
placeholder: 'Password'
},
buttons: [
{ text: 'Cancel', style: 'cancel' },
{ text: 'Submit', style: 'default' }
]
});
if (result.buttonIndex === 1) {
console.log('Password:', result.value);
}📄 Action Sheet
iOS-style action sheet that slides up from the bottom.
const result = await iOSUI.ActionSheet.show({
title: 'Share Photo',
message: 'Choose how you want to share this photo',
buttons: [
{ text: 'Share to Instagram' },
{ text: 'Share to Twitter' },
{ text: 'Copy Link' },
{ text: 'Delete', style: 'destructive' }
],
cancelText: 'Cancel'
});
console.log('Selected:', result.buttonIndex, result.buttonText);Action Sheet without Title
const result = await iOSUI.ActionSheet.show({
buttons: [
{ text: 'Take Photo' },
{ text: 'Choose from Library' },
{ text: 'Browse Files' }
]
});🍞 Toast
Non-intrusive toast notifications.
// Basic toast
iOSUI.Toast.show({
message: 'This is a toast message',
duration: 3000, // milliseconds
type: 'default', // default, success, error, warning
icon: null // optional custom icon
});
// Convenience methods
iOSUI.Toast.success('Saved successfully!');
iOSUI.Toast.error('Something went wrong');
iOSUI.Toast.warning('Please check your input');
// Custom duration
iOSUI.Toast.success('Quick toast', 1500);
iOSUI.Toast.error('Important error', 5000);Toast with Custom Icon
iOSUI.Toast.show({
message: 'Message sent',
icon: '✈️',
duration: 2000
});⏳ Loading
Full-screen loading overlay.
// Show loading
iOSUI.Loading.show('Loading...');
// Perform async operation
await fetchData();
// Hide loading
iOSUI.Loading.hide();Loading with Custom Text
iOSUI.Loading.show('Uploading file...');
// or
iOSUI.Loading.show('Please wait');
// or with no text
iOSUI.Loading.show();Async/Await Pattern
async function saveData() {
iOSUI.Loading.show('Saving...');
try {
await api.save(data);
iOSUI.Loading.hide();
iOSUI.Toast.success('Saved!');
} catch (error) {
iOSUI.Loading.hide();
iOSUI.Toast.error('Failed to save');
}
}🪟 Modal
iOS-style modal dialogs.
const modal = iOSUI.Modal.show({
title: 'Settings',
content: '<div>Modal content here...</div>',
closable: true,
fullscreen: false,
onClose: () => {
console.log('Modal closed');
}
});
// Close programmatically
modal.close();Modal with Component Content
// Create content
const container = document.createElement('div');
iOSUI.createInput({
label: 'Name',
placeholder: 'Enter name'
}).mount(container);
iOSUI.createInput({
label: 'Email',
placeholder: 'Enter email'
}).mount(container);
// Show modal
const modal = iOSUI.Modal.show({
title: 'Edit Profile',
content: container,
closable: true
});Fullscreen Modal
iOSUI.Modal.show({
title: 'Full Screen',
content: '<p>This takes the whole screen</p>',
fullscreen: true
});📜 Sheet
Bottom sheet that slides up from the bottom.
const sheet = iOSUI.Sheet.show({
content: '<div>Sheet content here...</div>',
height: '50vh', // or 'auto', '300px', etc.
onClose: () => {
console.log('Sheet closed');
}
});
// Close programmatically
sheet.close();Sheet with Dynamic Content
const content = document.createElement('div');
content.innerHTML = `
<h3>Select an Option</h3>
<p>Choose from the options below:</p>
`;
const list = iOSUI.createList({
items: [
{ title: 'Option 1', onClick: () => sheet.close() },
{ title: 'Option 2', onClick: () => sheet.close() },
{ title: 'Option 3', onClick: () => sheet.close() }
]
});
list.mount(content);
const sheet = iOSUI.Sheet.show({ content });🧭 NavBar
iOS-style navigation bar.
const navbar = iOSUI.createNavBar({
title: 'Page Title',
largeTitle: false,
transparent: false,
leftButton: {
text: '← Back',
icon: null,
onClick: () => history.back()
},
rightButton: {
text: 'Edit',
onClick: () => enableEditMode()
}
});
navbar.mount('#nav-container');Large Title NavBar
iOSUI.createNavBar({
title: 'Settings',
largeTitle: true,
rightButton: {
text: 'Done',
onClick: () => save()
}
});Transparent NavBar
iOSUI.createNavBar({
title: 'Photos',
transparent: true
});Update Title
const nav = iOSUI.createNavBar({ title: 'Loading...' });
// Later...
nav.setTitle('Page Loaded');📍 TabBar
iOS-style bottom tab bar.
const tabbar = iOSUI.createTabBar({
items: [
{ icon: '🏠', label: 'Home' },
{ icon: '🔍', label: 'Search' },
{ icon: '➕', label: 'Add' },
{ icon: '❤️', label: 'Favorites', badge: '3' },
{ icon: '👤', label: 'Profile' }
],
selectedIndex: 0,
onChange: (index, item) => {
console.log(`Tab ${item.label} selected`);
showPage(index);
}
});
tabbar.mount('#app');TabBar Methods
// Change selected tab
tabbar.setSelectedIndex(2);
// Update badge
tabbar.setBadge(3, '5'); // Set badge on 4th tab
tabbar.setBadge(3, null); // Remove badge
tabbar.setBadge(3, '99+'); // Text badge👤 Avatar
User avatars with image or initials.
// Avatar with image
iOSUI.createAvatar({
src: 'https://example.com/photo.jpg',
name: 'John Doe',
size: 'medium' // small, medium, large
});
// Avatar with initials
iOSUI.createAvatar({
name: 'John Doe',
size: 'large',
color: '#007AFF' // optional, auto-generated if not set
});Avatar Sizes
iOSUI.createAvatar({ name: 'JD', size: 'small' }); // 28px
iOSUI.createAvatar({ name: 'JD', size: 'medium' }); // 40px
iOSUI.createAvatar({ name: 'JD', size: 'large' }); // 64px🔴 Badge
Notification badges.
// Text badge
iOSUI.createBadge({
text: '5',
variant: 'primary' // primary, danger, success, warning
});
// Dot badge
iOSUI.createBadge({
dot: true,
variant: 'danger'
});Badge Variants
iOSUI.createBadge({ text: 'New', variant: 'primary' }); // Blue
iOSUI.createBadge({ text: '3', variant: 'danger' }); // Red
iOSUI.createBadge({ text: 'Done', variant: 'success' }); // Green
iOSUI.createBadge({ text: '!', variant: 'warning' }); // OrangeUpdate Badge
const badge = iOSUI.createBadge({ text: '0' });
badge.setText('5');📊 Progress
Progress bars with determinate and indeterminate states.
// Determinate progress
const progress = iOSUI.createProgress({
value: 50,
max: 100,
color: '#007AFF' // optional custom color
});
progress.mount('#container');
// Update progress
progress.setValue(75);Indeterminate Progress
iOSUI.createProgress({
indeterminate: true
});🔄 Spinner
Loading spinners.
// Default spinner
iOSUI.createSpinner();
// Large spinner
iOSUI.createSpinner({ size: 'large' });
// Custom color
iOSUI.createSpinner({ color: '#FF3B30' });🎨 Theming
Set Theme Mode
// Light mode
iOSUI.setTheme('light');
// Dark mode
iOSUI.setTheme('dark');
// Auto (follows system preference)
iOSUI.setTheme('auto');Access Colors
// Get current theme colors
const colors = iOSUI.Utils.getColors();
console.log(colors.primary); // #007AFF or #0A84FF
console.log(colors.background); // #F2F2F7 or #000000Available Colors
| Color | Light Mode | Dark Mode | |-------|------------|-----------| | primary | #007AFF | #0A84FF | | secondary | #5856D6 | #5E5CE6 | | success | #34C759 | #30D158 | | warning | #FF9500 | #FF9F0A | | danger | #FF3B30 | #FF453A | | background | #F2F2F7 | #000000 | | surface | #FFFFFF | #1C1C1E | | text | #000000 | #FFFFFF | | textSecondary | #8E8E93 | #8E8E93 | | separator | #C6C6C8 | #38383A |
Custom CSS Variables
You can override the default colors using CSS variables:
:root {
--ios-primary: #FF6B6B;
--ios-success: #51CF66;
--ios-danger: #FF6B6B;
}⚙️ Configuration
Global Configuration
// Access configuration
iOSUI.config.animationDuration = 300; // Animation duration in ms
iOSUI.config.hapticFeedback = true; // Enable/disable haptic feedback
iOSUI.config.theme = 'auto'; // 'light', 'dark', 'auto'Disable Haptic Feedback
iOSUI.config.hapticFeedback = false;🛠️ Utility Functions
Generate Unique ID
const id = iOSUI.Utils.generateId();
// Returns: 'ios-ui-a1b2c3d4e'Check Dark Mode
const isDark = iOSUI.Utils.isDarkMode();Create Element
const div = iOSUI.Utils.createElement('div', {
className: 'my-class',
textContent: 'Hello',
onClick: () => console.log('clicked')
});Haptic Feedback
iOSUI.Utils.haptic('light'); // Light tap
iOSUI.Utils.haptic('medium'); // Medium tap
iOSUI.Utils.haptic('heavy'); // Heavy tap
iOSUI.Utils.haptic('success'); // Success pattern
iOSUI.Utils.haptic('warning'); // Warning pattern
iOSUI.Utils.haptic('error'); // Error patternDebounce
const debouncedSearch = iOSUI.Utils.debounce((query) => {
console.log('Searching:', query);
}, 300);
input.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});📱 Mobile Optimization
Viewport Meta Tag
Always include the viewport meta tag for proper mobile rendering:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">Safe Area Support
iOS Style UI automatically handles safe areas on notched devices (iPhone X and later). The NavBar and TabBar respect env(safe-area-inset-*) values.
Touch Optimization
All components have:
-webkit-tap-highlight-color: transparentto remove tap highlights- Proper touch target sizes (minimum 44px)
- Touch-friendly hit areas
🌐 Browser Support
| Browser | Version | |---------|---------| | Chrome | 60+ | | Firefox | 55+ | | Safari | 12+ | | Edge | 79+ | | iOS Safari | 12+ | | Chrome for Android | 60+ |
📄 TypeScript Support
iOS Style UI includes TypeScript definitions. Import types like this:
import iOSUI, { ButtonOptions, SwitchOptions } from 'ios-style-ui';
const buttonOptions: ButtonOptions = {
text: 'Click Me',
variant: 'primary',
onClick: () => {}
};
const button = iOSUI.createButton(buttonOptions);🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Development Setup
# Clone the repository
git clone https://github.com/liladhiee-web/ios-style-ui.git
# Navigate to the directory
cd ios-style-ui
# Install dependencies (if any)
npm install
# Start development
npm run dev📃 License
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2024 Your Name
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.🙏 Acknowledgments
- Inspired by Apple's Human Interface Guidelines
- Icons from various emoji sets
📞 Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
Made with ❤️ by lilAdhi
⭐ Star this repo if you find it useful!
