---
name: tailwind-capacitor
description: Guide to using Tailwind CSS in Capacitor mobile apps. Covers mobile-first design, touch targets, safe areas, dark mode, and performance optimization. Use this skill when users want to style Capacitor apps with Tailwind.
---
# Tailwind CSS for Capacitor Apps
Build beautiful mobile apps with Tailwind CSS and Capacitor.
## When to Use This Skill
- User is using Tailwind in Capacitor app
- User asks about mobile styling
- User needs responsive mobile design
- User wants dark mode with Tailwind
- User needs safe area handling
## Getting Started
### Installation
```bash
bun add -D tailwindcss postcss autoprefixer
bunx tailwindcss init -p
```
### Configuration
```javascript
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx,vue,svelte}',
],
theme: {
extend: {
// Mobile-first spacing
spacing: {
'safe-top': 'env(safe-area-inset-top)',
'safe-bottom': 'env(safe-area-inset-bottom)',
'safe-left': 'env(safe-area-inset-left)',
'safe-right': 'env(safe-area-inset-right)',
},
// Minimum touch targets (44px)
minHeight: {
'touch': '44px',
},
minWidth: {
'touch': '44px',
},
},
},
plugins: [],
};
```
### Import Styles
```css
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Mobile-specific base styles */
@layer base {
html {
/* Prevent text size adjustment on orientation change */
-webkit-text-size-adjust: 100%;
/* Smooth scrolling */
scroll-behavior: smooth;
/* Prevent pull-to-refresh on overscroll */
overscroll-behavior: none;
}
body {
/* Prevent text selection on long press */
-webkit-user-select: none;
user-select: none;
/* Disable callout on long press */
-webkit-touch-callout: none;
/* Prevent elastic scrolling on iOS */
position: fixed;
width: 100%;
height: 100%;
overflow: hidden;
}
/* Enable text selection in inputs */
input, textarea {
-webkit-user-select: text;
user-select: text;
}
}
```
## Safe Area Handling
### Utility Classes
```javascript
// tailwind.config.js
theme: {
extend: {
padding: {
'safe': 'env(safe-area-inset-bottom)',
'safe-t': 'env(safe-area-inset-top)',
'safe-b': 'env(safe-area-inset-bottom)',
'safe-l': 'env(safe-area-inset-left)',
'safe-r': 'env(safe-area-inset-right)',
},
margin: {
'safe': 'env(safe-area-inset-bottom)',
'safe-t': 'env(safe-area-inset-top)',
'safe-b': 'env(safe-area-inset-bottom)',
},
},
},
```
### Usage in Components
```tsx
// Header with safe area
function Header() {
return (
);
}
// Footer with safe area
function Footer() {
return (
);
}
// Main content
function Main() {
return (
{/* Content */}
);
}
```
## Touch-Friendly Design
### Minimum Touch Targets
```tsx
// Apple HIG recommends 44x44pt minimum
function TouchableButton() {
return (
);
}
// Icon button with proper touch target
function IconButton() {
return (
);
}
```
### Touch Feedback
```css
/* Add to index.css */
@layer utilities {
.touch-feedback {
@apply transition-colors duration-75;
}
.touch-feedback:active {
@apply bg-black/5 dark:bg-white/5;
}
}
```
```tsx
```
### Disable Hover on Touch
```javascript
// tailwind.config.js
module.exports = {
future: {
hoverOnlyWhenSupported: true, // Disables hover on touch devices
},
};
```
Or use media query:
```css
@media (hover: hover) {
.hover-only:hover {
@apply bg-gray-100;
}
}
```
## Dark Mode
### System Dark Mode
```javascript
// tailwind.config.js
module.exports = {
darkMode: 'media', // or 'class' for manual control
};
```
### Manual Dark Mode
```javascript
// tailwind.config.js
module.exports = {
darkMode: 'class',
};
```
```typescript
// theme.ts
import { Preferences } from '@capacitor/preferences';
type Theme = 'light' | 'dark' | 'system';
async function setTheme(theme: Theme) {
await Preferences.set({ key: 'theme', value: theme });
if (theme === 'system') {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.classList.toggle('dark', prefersDark);
} else {
document.documentElement.classList.toggle('dark', theme === 'dark');
}
}
// Listen for system changes
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
const theme = await Preferences.get({ key: 'theme' });
if (theme.value === 'system') {
document.documentElement.classList.toggle('dark', e.matches);
}
});
```
### Dark Mode Components
```tsx
function Card() {
return (
);
}
```
## Mobile Patterns
### Pull to Refresh Container
```tsx
function PullToRefresh({ onRefresh, children }) {
return (
{children}
);
}
```
### Bottom Sheet
```tsx
function BottomSheet({ isOpen, onClose, children }) {
return (
<>
{/* Backdrop */}
{/* Sheet */}
{/* Handle */}
{children}
>
);
}
```
### Swipe Actions
```tsx
function SwipeableItem({ children, onDelete }) {
return (
{/* Background action */}
Delete
{/* Foreground content */}
{children}
);
}
```
### Fixed Header with Blur
```tsx
function BlurHeader() {
return (
);
}
```
## Performance Optimization
### Reduce Bundle Size
```javascript
// tailwind.config.js
module.exports = {
content: [/* ... */],
// Only include used utilities
safelist: [], // Add dynamic classes here if needed
};
```
### GPU Acceleration
```tsx
// Use transform for animations (GPU accelerated)
Animated Element
```
### Avoid Layout Thrashing
```tsx
// BAD: Causes reflow
// GOOD: Fixed dimensions
```
## Component Examples
### Mobile List Item
```tsx
function ListItem({ title, subtitle, image, onClick }) {
return (
);
}
```
### Mobile Button
```tsx
function MobileButton({ children, variant = 'primary', ...props }) {
const variants = {
primary: 'bg-blue-500 text-white active:bg-blue-600',
secondary: 'bg-gray-100 text-gray-900 active:bg-gray-200',
danger: 'bg-red-500 text-white active:bg-red-600',
};
return (
);
}
```
### Mobile Input
```tsx
function MobileInput({ label, error, ...props }) {
return (
);
}
```
## Resources
- Tailwind CSS Documentation: https://tailwindcss.com/docs
- Tailwind Mobile Patterns: https://tailwindui.com/
- CSS Safe Area Guide: https://webkit.org/blog/7929/designing-websites-for-iphone-x/