Ionic Design

cap-go/capgo-skills/skills/ionic-design

作者 cap-go2cfb277a1488無授權條款72 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫今天更新

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.

AI 產生的概覽

用於打造 Ionic Framework 行動介面的參考指南,涵蓋元件、主題與平台樣式。

功能
提供以 Capacitor 為基礎的行動應用之 Ionic Framework 元件參考指南。內容包含 React 與 Vue 的安裝與匯入方式,頁面、清單、表單、按鈕、卡片、模態視窗與導覽的範例程式碼,以及主題變數、深色模式和平台專屬樣式。也列出效能、無障礙與安全區域的實務建議。
適用情境
當使用者使用 Ionic 元件、想要接近原生外觀的行動介面,或詢問 Ionic 主題、行動介面模式與平台專屬樣式時使用。
執行需求
不含指令碼,僅為說明與程式碼範例。依範例操作需要 JavaScript 專案環境,包括 Node.js 與 npm,並安裝 React 或 Vue 對應的 Ionic 套件以及 Capacitor 套件。

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

bash
# For Reactnpx create-vite@latest my-app --template react-tscd my-appnpm install @ionic/react @ionic/react-router
# For Vuenpx create-vite@latest my-app --template vue-tscd my-appnpm install @ionic/vue @ionic/vue-router
# Add Capacitornpm install @capacitor/core @capacitor/clinpx cap init

Setup (React)

typescript
// main.tsximport 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

tsx
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

tsx
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>[email protected]</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

tsx
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

tsx
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

tsx
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

tsx
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

tsx
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

tsx
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

css
/* 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

css
/* 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

typescript
import { isPlatform } from '@ionic/react';
// Check platformif (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

tsx
import { isPlatform, IonIcon } from '@ionic/react';import { chevronBack, arrowBack } from 'ionicons/icons';
function BackButton() {  return (    <IonIcon      icon={isPlatform('ios') ? chevronBack : arrowBack}    />  );}

Best Practices

Performance

tsx
// Use IonVirtualScroll for long listsimport { 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

tsx
// 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

tsx
// 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

來源與署名

來源:cap-go/capgo-skills位於skills/ionic-design提交2cfb277

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架