PluginBench
Skill
Review
Audit score 70

ionic-design

cap-go/capgo-skills

Build beautiful native-looking mobile apps with Ionic Framework components and Capacitor.

What is ionic-design?

Ionic Framework provides 100+ mobile-optimized UI components with automatic iOS/Android platform styling, dark mode support, and accessibility built-in. Use this skill when building mobile apps with Ionic and React, Vue, or Angular, or when users need help with component usage, theming, and mobile UI patterns.

  • 100+ mobile-optimized UI components with automatic platform-specific styling
  • Page structure components (IonPage, IonHeader, IonToolbar, IonContent)
  • Form controls (IonInput, IonTextarea, IonSelect, IonToggle, IonCheckbox, IonRadio)
  • List components with sliding actions and avatars
  • Button variants with colors, sizes, fills, and icon support
  • Card components for content layout

How to install ionic-design

npx skills add https://github.com/cap-go/capgo-skills --skill ionic-design
Prerequisites
  • Node.js and npm installed
  • Familiarity with React, Vue, or Angular
  • Capacitor installed in your project
Claude Code
Cursor
Windsurf
Cline

How to use ionic-design

  1. 1.Install @ionic/react (or @ionic/vue/@ionic/angular) and required CSS files
  2. 2.Import setupIonicReact() and wrap your app in IonApp component
  3. 3.Import core Ionic CSS files in your main entry point
  4. 4.Use IonPage as the root component for each page
  5. 5.Compose pages using IonHeader, IonContent, and other layout components
  6. 6.Add form controls, lists, buttons, and cards as needed for your UI

Use cases

Good for
  • Building cross-platform mobile apps that look native on iOS and Android
  • Creating forms with floating labels and validation
  • Implementing swipeable list items with actions like delete or archive
  • Designing card-based layouts for content feeds or product displays
  • Building navigation with headers, toolbars, and back buttons
Who it's for
  • React developers building mobile apps
  • Vue developers using Ionic framework
  • Angular developers with Ionic
  • Mobile app developers using Capacitor

ionic-design FAQ

Do I need Capacitor to use Ionic?

No, Ionic works as a web framework, but Capacitor is needed to deploy as a native iOS/Android app.

How do I customize Ionic component colors?

Use the color prop on components (e.g., color="primary") or customize CSS variables in your theme/variables.css file.

Can I use Ionic with vanilla JavaScript?

Yes, Ionic supports vanilla JS, but the SKILL.md examples focus on React and Vue setup.

How does Ionic handle iOS vs Android styling?

Ionic automatically applies platform-specific styling—iOS gets the iOS look and feel, Android gets Material Design—without extra code.

What icons are available in Ionic?

Ionic uses Ionicons, a library of 1000+ open-source icons imported from the 'ionicons/icons' package.

Full instructions (SKILL.md)

Source of truth, from cap-go/capgo-skills.


name: ionic-design description: Guide to using Ionic Framework components for beautiful native-looking Capacitor apps. Covers component usage, theming, platform-specific styling, and best practices for mobile UI. Use this skill when users need help with Ionic components or mobile UI design.

Ionic Framework Design Guide

Build beautiful, native-looking mobile apps with Ionic Framework and Capacitor.

When to Use This Skill

  • User is using Ionic components
  • User wants native-looking UI
  • User asks about Ionic theming
  • User needs mobile UI patterns
  • User wants platform-specific styling

What is Ionic Framework?

Ionic provides:

  • 100+ mobile-optimized UI components
  • Automatic iOS/Android platform styling
  • Built-in dark mode support
  • Accessibility out of the box
  • Works with React, Vue, Angular, or vanilla JS

Getting Started

Installation

# For React
npx create-vite@latest my-app --template react-ts
cd my-app
npm install @ionic/react @ionic/react-router

# For Vue
npx create-vite@latest my-app --template vue-ts
cd my-app
npm install @ionic/vue @ionic/vue-router

# Add Capacitor
npm install @capacitor/core @capacitor/cli
npx cap init

Setup (React)

// main.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { IonApp, setupIonicReact } from '@ionic/react';
import App from './App';

/* Core CSS required for Ionic components to work properly */
import '@ionic/react/css/core.css';

/* Basic CSS for apps built with Ionic */
import '@ionic/react/css/normalize.css';
import '@ionic/react/css/structure.css';
import '@ionic/react/css/typography.css';

/* Optional CSS utils */
import '@ionic/react/css/padding.css';
import '@ionic/react/css/float-elements.css';
import '@ionic/react/css/text-alignment.css';
import '@ionic/react/css/text-transformation.css';
import '@ionic/react/css/flex-utils.css';
import '@ionic/react/css/display.css';

/* Theme */
import './theme/variables.css';

setupIonicReact();

const root = createRoot(document.getElementById('root')!);
root.render(
  <IonApp>
    <App />
  </IonApp>
);

Core Components

Page Structure

import {
  IonPage,
  IonHeader,
  IonToolbar,
  IonTitle,
  IonContent,
  IonButtons,
  IonBackButton,
} from '@ionic/react';

function MyPage() {
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonButtons slot="start">
            <IonBackButton defaultHref="/home" />
          </IonButtons>
          <IonTitle>Page Title</IonTitle>
        </IonToolbar>
      </IonHeader>

      <IonContent fullscreen>
        {/* Large title for iOS */}
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">Page Title</IonTitle>
          </IonToolbar>
        </IonHeader>

        {/* Page content */}
        <div className="ion-padding">
          Your content here
        </div>
      </IonContent>
    </IonPage>
  );
}

Lists

import {
  IonList,
  IonItem,
  IonLabel,
  IonNote,
  IonAvatar,
  IonIcon,
  IonItemSliding,
  IonItemOptions,
  IonItemOption,
} from '@ionic/react';
import { chevronForward, trash, archive } from 'ionicons/icons';

function ContactList() {
  return (
    <IonList>
      {/* Simple item */}
      <IonItem>
        <IonLabel>Simple Item</IonLabel>
      </IonItem>

      {/* Item with detail */}
      <IonItem detail button>
        <IonLabel>
          <h2>Item Title</h2>
          <p>Item description text</p>
        </IonLabel>
        <IonNote slot="end">Note</IonNote>
      </IonItem>

      {/* Item with avatar */}
      <IonItem>
        <IonAvatar slot="start">
          <img src="/avatar.jpg" alt="" />
        </IonAvatar>
        <IonLabel>
          <h2>John Doe</h2>
          <p>john@example.com</p>
        </IonLabel>
      </IonItem>

      {/* Sliding item */}
      <IonItemSliding>
        <IonItem>
          <IonLabel>Swipe me</IonLabel>
        </IonItem>
        <IonItemOptions side="end">
          <IonItemOption color="danger">
            <IonIcon slot="icon-only" icon={trash} />
          </IonItemOption>
          <IonItemOption>
            <IonIcon slot="icon-only" icon={archive} />
          </IonItemOption>
        </IonItemOptions>
      </IonItemSliding>
    </IonList>
  );
}

Forms

import {
  IonInput,
  IonTextarea,
  IonSelect,
  IonSelectOption,
  IonToggle,
  IonCheckbox,
  IonRadioGroup,
  IonRadio,
  IonItem,
  IonLabel,
  IonButton,
} from '@ionic/react';

function MyForm() {
  return (
    <form>
      {/* Text input */}
      <IonItem>
        <IonInput
          label="Email"
          labelPlacement="floating"
          type="email"
          placeholder="Enter email"
        />
      </IonItem>

      {/* Password */}
      <IonItem>
        <IonInput
          label="Password"
          labelPlacement="floating"
          type="password"
        />
      </IonItem>

      {/* Textarea */}
      <IonItem>
        <IonTextarea
          label="Bio"
          labelPlacement="floating"
          rows={4}
          placeholder="Tell us about yourself"
        />
      </IonItem>

      {/* Select */}
      <IonItem>
        <IonSelect label="Country" placeholder="Select">
          <IonSelectOption value="us">United States</IonSelectOption>
          <IonSelectOption value="uk">United Kingdom</IonSelectOption>
          <IonSelectOption value="de">Germany</IonSelectOption>
        </IonSelect>
      </IonItem>

      {/* Toggle */}
      <IonItem>
        <IonToggle>Enable notifications</IonToggle>
      </IonItem>

      {/* Checkbox */}
      <IonItem>
        <IonCheckbox slot="start" />
        <IonLabel>I agree to terms</IonLabel>
      </IonItem>

      {/* Radio group */}
      <IonRadioGroup>
        <IonItem>
          <IonRadio value="small">Small</IonRadio>
        </IonItem>
        <IonItem>
          <IonRadio value="medium">Medium</IonRadio>
        </IonItem>
        <IonItem>
          <IonRadio value="large">Large</IonRadio>
        </IonItem>
      </IonRadioGroup>

      <IonButton expand="block" type="submit">
        Submit
      </IonButton>
    </form>
  );
}

Buttons

import { IonButton, IonIcon } from '@ionic/react';
import { heart, share, download } from 'ionicons/icons';

function Buttons() {
  return (
    <>
      {/* Fill variants */}
      <IonButton>Solid</IonButton>
      <IonButton fill="outline">Outline</IonButton>
      <IonButton fill="clear">Clear</IonButton>

      {/* Colors */}
      <IonButton color="primary">Primary</IonButton>
      <IonButton color="secondary">Secondary</IonButton>
      <IonButton color="danger">Danger</IonButton>
      <IonButton color="success">Success</IonButton>

      {/* Sizes */}
      <IonButton size="small">Small</IonButton>
      <IonButton size="default">Default</IonButton>
      <IonButton size="large">Large</IonButton>

      {/* With icons */}
      <IonButton>
        <IonIcon slot="start" icon={heart} />
        Like
      </IonButton>

      {/* Icon only */}
      <IonButton>
        <IonIcon slot="icon-only" icon={share} />
      </IonButton>

      {/* Full width */}
      <IonButton expand="block">Block Button</IonButton>
      <IonButton expand="full">Full Width</IonButton>
    </>
  );
}

Cards

import {
  IonCard,
  IonCardHeader,
  IonCardTitle,
  IonCardSubtitle,
  IonCardContent,
  IonImg,
  IonButton,
} from '@ionic/react';

function Cards() {
  return (
    <IonCard>
      <IonImg src="/card-image.jpg" alt="" />
      <IonCardHeader>
        <IonCardSubtitle>Card Subtitle</IonCardSubtitle>
        <IonCardTitle>Card Title</IonCardTitle>
      </IonCardHeader>
      <IonCardContent>
        Card content goes here. This is a standard card with
        an image, title, subtitle, and content.
      </IonCardContent>
      <div className="ion-padding-horizontal ion-padding-bottom">
        <IonButton fill="clear">Action 1</IonButton>
        <IonButton fill="clear">Action 2</IonButton>
      </div>
    </IonCard>
  );
}

Modals and Sheets

import { IonModal, IonButton, IonContent, IonHeader, IonToolbar, IonTitle } from '@ionic/react';
import { useState, useRef } from 'react';

function ModalExample() {
  const [isOpen, setIsOpen] = useState(false);
  const modal = useRef<HTMLIonModalElement>(null);

  return (
    <>
      <IonButton onClick={() => setIsOpen(true)}>Open Modal</IonButton>

      {/* Full page modal */}
      <IonModal isOpen={isOpen} onDidDismiss={() => setIsOpen(false)}>
        <IonHeader>
          <IonToolbar>
            <IonTitle>Modal Title</IonTitle>
            <IonButton slot="end" onClick={() => setIsOpen(false)}>
              Close
            </IonButton>
          </IonToolbar>
        </IonHeader>
        <IonContent>
          <p>Modal content</p>
        </IonContent>
      </IonModal>

      {/* Bottom sheet */}
      <IonModal
        ref={modal}
        trigger="open-sheet"
        initialBreakpoint={0.5}
        breakpoints={[0, 0.25, 0.5, 0.75, 1]}
      >
        <IonContent>
          <div className="ion-padding">
            <h2>Sheet Content</h2>
            <p>Drag to resize</p>
          </div>
        </IonContent>
      </IonModal>
      <IonButton id="open-sheet">Open Sheet</IonButton>
    </>
  );
}

Navigation

Tab Navigation

import {
  IonTabs,
  IonTabBar,
  IonTabButton,
  IonIcon,
  IonLabel,
  IonRouterOutlet,
} from '@ionic/react';
import { Route, Redirect } from 'react-router-dom';
import { home, search, person } from 'ionicons/icons';

function TabsLayout() {
  return (
    <IonTabs>
      <IonRouterOutlet>
        <Route exact path="/tabs/home" component={HomePage} />
        <Route exact path="/tabs/search" component={SearchPage} />
        <Route exact path="/tabs/profile" component={ProfilePage} />
        <Route exact path="/tabs">
          <Redirect to="/tabs/home" />
        </Route>
      </IonRouterOutlet>

      <IonTabBar slot="bottom">
        <IonTabButton tab="home" href="/tabs/home">
          <IonIcon icon={home} />
          <IonLabel>Home</IonLabel>
        </IonTabButton>
        <IonTabButton tab="search" href="/tabs/search">
          <IonIcon icon={search} />
          <IonLabel>Search</IonLabel>
        </IonTabButton>
        <IonTabButton tab="profile" href="/tabs/profile">
          <IonIcon icon={person} />
          <IonLabel>Profile</IonLabel>
        </IonTabButton>
      </IonTabBar>
    </IonTabs>
  );
}

Stack Navigation

import { IonReactRouter } from '@ionic/react-router';
import { IonRouterOutlet } from '@ionic/react';
import { Route } from 'react-router-dom';

function App() {
  return (
    <IonReactRouter>
      <IonRouterOutlet>
        <Route exact path="/" component={Home} />
        <Route exact path="/detail/:id" component={Detail} />
      </IonRouterOutlet>
    </IonReactRouter>
  );
}

Theming

Theme Variables

/* theme/variables.css */
:root {
  /* Primary */
  --ion-color-primary: #3880ff;
  --ion-color-primary-rgb: 56, 128, 255;
  --ion-color-primary-contrast: #ffffff;
  --ion-color-primary-shade: #3171e0;
  --ion-color-primary-tint: #4c8dff;

  /* Secondary */
  --ion-color-secondary: #3dc2ff;

  /* Custom colors */
  --ion-color-brand: #ff6b35;
  --ion-color-brand-rgb: 255, 107, 53;
  --ion-color-brand-contrast: #ffffff;
  --ion-color-brand-shade: #e05e2f;
  --ion-color-brand-tint: #ff7a49;
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --ion-background-color: #121212;
    --ion-text-color: #ffffff;
    --ion-color-step-50: #1e1e1e;
    --ion-color-step-100: #2a2a2a;
  }
}

/* iOS specific */
.ios {
  --ion-toolbar-background: #f8f8f8;
}

/* Android specific */
.md {
  --ion-toolbar-background: #ffffff;
}

Custom Component Styling

/* Global styles */
ion-content {
  --background: var(--ion-background-color);
}

ion-card {
  --background: #ffffff;
  border-radius: 16px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}

/* Platform-specific */
.ios ion-toolbar {
  --border-width: 0;
}

.md ion-toolbar {
  --border-width: 0 0 1px 0;
}

Platform-Specific Code

Detect Platform

import { isPlatform } from '@ionic/react';

// Check platform
if (isPlatform('ios')) {
  // iOS-specific code
}

if (isPlatform('android')) {
  // Android-specific code
}

if (isPlatform('hybrid')) {
  // Running in native app
}

if (isPlatform('mobileweb')) {
  // Running in mobile browser
}

Conditional Rendering

import { isPlatform, IonIcon } from '@ionic/react';
import { chevronBack, arrowBack } from 'ionicons/icons';

function BackButton() {
  return (
    <IonIcon
      icon={isPlatform('ios') ? chevronBack : arrowBack}
    />
  );
}

Best Practices

Performance

// Use IonVirtualScroll for long lists
import { IonVirtualScroll } from '@ionic/react';

<IonVirtualScroll
  items={items}
  renderItem={(item) => (
    <IonItem key={item.id}>
      <IonLabel>{item.name}</IonLabel>
    </IonItem>
  )}
/>

// Lazy load images
<IonImg src={url} />  // Automatically lazy loads

Accessibility

// Always provide labels
<IonButton aria-label="Delete item">
  <IonIcon slot="icon-only" icon={trash} />
</IonButton>

// Use semantic elements
<IonItem button role="link">
  <IonLabel>Clickable item</IonLabel>
</IonItem>

Safe Area

// Content respects safe areas by default
<IonContent>
  {/* Auto padding for notch/home indicator */}
</IonContent>

// Custom safe area handling
<div style={{ paddingTop: 'env(safe-area-inset-top)' }}>
  Custom header
</div>

Resources