name: sap-fiori-ios
description: SAP Fiori for iOS Design Skill — Converts Figma designs from the Fiori iOS UI Kit into SwiftUI prototypes with animations and interactions. Use this skill when a designer provides a Figma node ID or component name and wants working SwiftUI code, when asking which Fiori iOS component to use, when mapping Figma variants to SwiftUI, or when adding animations and state transitions to iOS prototypes. Triggers on: Fiori iOS, SwiftUI, iOS prototype, Figma to iOS, FioriButton, FioriTheme, animation, interaction, SAP 72, preferredFioriColor, ObjectItem, FioriSwiftUI.
SAP Fiori for iOS — Design Skill
Figma UI Kit
File key: 1450853598524410675
Community URL: https://www.figma.com/community/file/1450853598524410675/sap-fiori-for-ios-ui-kit
How to read a component from Figma
get_figma_data(fileKey: "1450853598524410675", nodeId: "<nodeId>", depth: 3)
Known Page Node IDs (depth=1 from file root)
| Page | Contents |
|---|
| Cover | File cover |
| 🎨 Foundations | Colors, typography, spacing, elevation |
| 🔘 Buttons | FioriButton all variants |
| 🗂 Cards | Card, KPI Card, Timeline Card |
| 📋 Lists | ObjectItem, SectionHeader, KeyValueItem |
| 🗺 Navigation | TabBar, NavigationBar, SideBar |
| 📝 Forms | TextFieldFormView, NoteFormView, SwitchFormView |
| 📊 Charts | All chart types |
| 🔔 Notifications | Banners, toasts, badges |
| 📅 Pickers | DateTimePicker, DurationPicker |
| 📈 KPI | KPIItem, KPIProgressItem, KPIHeader |
Core Rule: NEVER hardcode hex values
Always use Fiori color tokens. The API is:
Color.preferredColor(.primaryLabel)
.foregroundStyle(Color.preferredColor(.tintColor))
Color.preferredColor(.tintColor, background: .darkConstant)
See references/foundations/colors.md and references/colors.md for the full token table.
Figma Variant → SwiftUI Mapping
FioriButton
| Figma Variant Property | Figma Value | SwiftUI |
|---|
| Type | Primary | .fioriPrimaryButtonStyle |
| Type | Secondary | .fioriSecondaryButtonStyle |
| Type | Tertiary | .fioriTertiaryButtonStyle |
| Type | Destructive | .fioriDestructiveButtonStyle |
| Type | Menu | .fioriMenuButtonStyle |
| State | Default | (no modifier) |
| State | Pressed | .disabled(false) + tap gesture |
| State | Disabled | .disabled(true) |
FioriButton { _ in Text("Continue") }
.fioriButtonStyle(.fioriPrimaryButtonStyle)
FioriButton { _ in Text("Cancel") }
.fioriButtonStyle(.fioriSecondaryButtonStyle)
FioriButton { _ in Text("Submit") }
.fioriButtonStyle(.fioriPrimaryButtonStyle)
.frame(maxWidth: .infinity)
Typography
| Figma Text Style | SwiftUI |
|---|
| extraLargeTitle | .fiori(forTextStyle: .extraLargeTitle) |
| extraLargeTitle2 | .fiori(forTextStyle: .extraLargeTitle2) |
| largeTitle | .fiori(forTextStyle: .largeTitle) |
| title1 | .fiori(forTextStyle: .title1) |
| title2 | .fiori(forTextStyle: .title2) |
| title3 | .fiori(forTextStyle: .title3) |
| headline | .fiori(forTextStyle: .headline) |
| body | .fiori(forTextStyle: .body) |
| callout | .fiori(forTextStyle: .callout) |
| subheadline | .fiori(forTextStyle: .subheadline) |
| footnote | .fiori(forTextStyle: .footnote) |
| caption1 | .fiori(forTextStyle: .caption1) |
| caption2 | .fiori(forTextStyle: .caption2) |
| KPI | .fiori(forTextStyle: .KPI) |
| largeKPI | .fiori(forTextStyle: .largeKPI) |
Text("Hello")
.font(.fiori(forTextStyle: .headline))
Text("42")
.font(.fiori(fixedSize: 36, weight: .light))
Navigation
| Figma Component | SwiftUI Equivalent |
|---|
| TabBar | TabView + .tabItem {} |
| NavigationBar | NavigationStack + .navigationTitle() + .toolbar {} |
| SideBar | NavigationSplitView with SidebarListStyle |
| BackButton | Automatic in NavigationStack |
Cards
| Figma Variant | SwiftUI |
|---|
| Card / Default | CardView {} or custom VStack in card style |
| Card / Highlighted | Add .overlay(RoundedRectangle.stroke(Color.preferredFioriColor(forStyle: .tintColor))) |
| KPI Card | _KPIItem or custom card with .fiori(forTextStyle: .KPI) |
List / ObjectItem
| Figma Property | SwiftUI |
|---|
| ObjectItem / 1-line | ObjectItem(title: Text(""), footnote: nil) |
| ObjectItem / 2-line | ObjectItem(title: Text(""), subtitle: Text("")) |
| ObjectItem / 3-line | ObjectItem(title: Text(""), subtitle: Text(""), footnote: Text("")) |
| ObjectItem / with icon | Add detailImage: Image(...) |
Forms
| Figma Variant | SwiftUI Component |
|---|
| TextField | TextFieldFormView(title: Text(""), text: $value) |
| NoteField | NoteFormView(title: Text(""), text: $value) |
| KeyValue | KeyValueFormView(keyText: Text(""), value: Text("")) |
| Switch | SwitchFormView(title: Text(""), isOn: $value) |
| DateTimePicker | _DateTimePicker(...) |
Animation Specs
| Interaction | Animation |
|---|
| Button tap press | .scaleEffect(0.97) with .spring(response: 0.2, dampingFraction: 0.8) |
| View appear | .opacity(0→1) + .offset(y: 8→0) with .easeOut(duration: 0.25) |
| Sheet present | System default .sheet() — do not override |
| List row tap | .opacity feedback with duration: 0.15 |
| Navigation push | System default — do not override |
| Toggle switch | System default .toggle() — let SwiftUI handle |
| Loading/skeleton | .opacity(0.3...1.0) repeating with .easeInOut(duration: 0.9) |
| Error shake | .offset(x: -6...6) sequence, 3 cycles, duration: 0.07 each |
struct FioriButtonPress: ViewModifier {
@State private var isPressed = false
func body(content: Content) -> some View {
content
.scaleEffect(isPressed ? 0.97 : 1.0)
.animation(.spring(response: 0.2, dampingFraction: 0.8), value: isPressed)
.simultaneousGesture(DragGesture(minimumDistance: 0)
.onChanged { _ in isPressed = true }
.onEnded { _ in isPressed = false })
}
}
FioriTheme Setup
import FioriThemeManager
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
ThemeManager.shared.setPaletteVersion(.v8)
Font.registerFioriFonts()
return true
}
ThemeManager.shared.setHexColor("FF6000", for: .tintColor, variant: .light)
ThemeManager.shared.reset()
Quick Start SwiftUI Template
import SwiftUI
import FioriSwiftUICore
import FioriThemeManager
struct FioriScreenTemplate: View {
var body: some View {
NavigationStack {
List {
ObjectItem {
Text("Invoice #1234")
} subtitle: {
Text("SAP SE")
} footnote: {
Text("Due: 30 Jan 2026")
} detailImage: {
Image(systemName: "doc.text")
}
}
.navigationTitle("My Items")
.toolbar {
ToolbarItem(placement: .primaryAction) {
FioriButton { _ in Image(systemName: "plus") }
.fioriButtonStyle(FioriPrimaryButtonStyle())
}
}
}
}
}
File Directory
All content lives under references/. When reading any file, always use the full path from the skill root as listed below. Never guess a path — look it up here first.
Foundations — references/foundations/
Components — references/components/
Patterns — references/patterns/
Page Types — references/page-types/
Joule — references/joule/
In-App AI Design — references/in-app-ai-design/
Platform Extensions
Resources & Discovery