claude-skill-app-onboarding-questionnaire
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseApp Onboarding Questionnaire
应用新手引导问卷
Skill by ara.so — Daily 2026 Skills collection.
A Claude Code skill that analyses your existing app codebase and generates a complete, high-converting questionnaire-style onboarding flow — including all copy, screen designs, and production-ready code — modelled on proven patterns from top subscription apps.
本技能由ara.so开发 — 属于2026年度技能合集。
这是一款Claude Code技能,可分析你现有的应用代码库,参考顶级订阅应用的成熟转化模式,生成完整的高转化率问卷式新手引导流程,包含所有文案、页面设计以及可直接投产的代码。
What It Does
功能说明
When you run in your project, the skill:
/app-onboarding-questionnaire- Analyses your codebase — reads your app's source, manifest/plist files, and existing screens to understand your app's purpose, target users, and required permissions
- Defines the user transformation — constructs a before/after narrative that drives the onboarding story
- Designs a screen-by-screen blueprint — using a 14-screen psychological conversion framework
- Drafts all copy — headlines, questions, answer options, CTAs, testimonials, social proof
- Builds the screens — in your app's native framework (SwiftUI, React Native, Flutter, Jetpack Compose, etc.)
当你在项目中运行 命令时,该技能会执行以下操作:
/app-onboarding-questionnaire- 分析你的代码库 — 读取应用源码、manifest/plist配置文件以及现有页面,理解应用的定位、目标用户和所需权限
- 定义用户价值转变路径 — 搭建「使用前/使用后」的叙事框架,支撑新手引导的故事逻辑
- 设计逐页的实现蓝图 — 采用14屏心理学转化框架
- 撰写所有文案 — 包含标题、问题、选项、CTA按钮、用户评价、社交证明等内容
- 生成页面代码 — 支持你应用所用的原生框架(SwiftUI、React Native、Flutter、Jetpack Compose等)
Installation
安装
Option 1: Global skills directory
方案1:全局技能目录安装
bash
cd ~/.claude/skills
git clone https://github.com/adamlyttleapps/claude-skill-app-onboarding-questionnaire.git app-onboarding-questionnairebash
cd ~/.claude/skills
git clone https://github.com/adamlyttleapps/claude-skill-app-onboarding-questionnaire.git app-onboarding-questionnaireOption 2: Project-level dependency
方案2:项目级依赖安装
Add to your project's :
.claude/settings.jsonjson
{
"skills": [
"github:adamlyttleapps/claude-skill-app-onboarding-questionnaire"
]
}添加到项目的 文件中:
.claude/settings.jsonjson
{
"skills": [
"github:adamlyttleapps/claude-skill-app-onboarding-questionnaire"
]
}Usage
使用方法
Navigate to your app project directory and run:
/app-onboarding-questionnaireThe skill is interactive — it asks clarifying questions and builds incrementally. Progress is saved to Claude Code's memory system so you can resume across sessions.
进入你的应用项目目录,运行以下命令:
/app-onboarding-questionnaire该技能为交互式工具,会询问澄清问题并逐步构建引导流程。进度会保存到Claude Code的内存系统中,你可以跨会话恢复工作。
The 14-Screen Framework
14屏转化框架
| # | Screen | Conversion Purpose |
|---|---|---|
| 1 | Welcome | Hook — show the end state, create desire |
| 2 | Goal Question | "What are you trying to achieve?" — psychological investment |
| 3 | Pain Points | "What prevents you?" — builds empathy |
| 4 | Social Proof | Persona-matched testimonials |
| 5 | Tinder Cards | Swipe agree/disagree on pain statements |
| 6 | Personalised Solution | Mirror pains back with app solution stats |
| 7 | Comparison Table | Life with vs without the app (optional) |
| 8 | Preferences | Functional personalisation for the demo |
| 9 | Permission Priming | Benefit-framed pre-sell before system dialogs |
| 10 | Processing Moment | "Building X just for you..." anticipation builder |
| 11 | App Demo | User actually uses the core app mechanic |
| 12 | Value Delivery | Tangible output + share/viral moment |
| 13 | Account Gate | Optional sign-in to save what they created |
| 14 | Paywall | Hard paywall with trial, social proof, pricing |
Not every app needs every screen — the skill adapts based on your app's complexity and type.
| # | 页面名称 | 转化目标 |
|---|---|---|
| 1 | 欢迎页 | 吸引用户 — 展示最终价值,激发使用意愿 |
| 2 | 目标问卷 | 「你想要实现什么目标?」 — 提升用户心理投入度 |
| 3 | 痛点调研 | 「什么问题阻碍了你?」 — 建立情感共鸣 |
| 4 | 社交证明 | 匹配用户画像的真实评价 |
| 5 | Tinder滑动卡片 | 让用户滑动选择同意/不同意痛点描述 |
| 6 | 个性化解决方案 | 结合应用解决方案的数据回应用户痛点 |
| 7 | 对比表格 | 使用应用vs不使用应用的生活对比(可选) |
| 8 | 偏好设置 | 为演示环节做功能个性化配置 |
| 9 | 权限预申请 | 在系统弹窗出现前,基于价值说明提前铺垫权限申请 |
| 10 | 处理等待页 | 「正在为你专属定制X...」 提升用户期待感 |
| 11 | 应用演示 | 让用户实际体验应用的核心功能 |
| 12 | 价值传递 | 产出可感知的结果 + 分享/传播节点 |
| 13 | 账号门槛 | 可选的登录环节,用于保存用户生成的内容 |
| 14 | 付费墙 | 硬付费墙,包含试用、社交证明、定价信息 |
不是所有应用都需要全部页面,该技能会根据你的应用复杂度和类型自适应调整。
Key Differentiators
核心优势
App Demo Screen
应用演示页面
Instead of a tour, users do something — pick recipes, complete an exercise, categorise a transaction — and receive a tangible result. This is Screen 11 and is the highest-impact screen for conversion.
不同于普通的功能导览,用户会实际完成操作 — 挑选菜谱、完成练习、分类交易等,并获得可感知的结果。这是第11页,也是对转化率影响最高的页面。
Permission Priming (Screen 9)
权限预申请(第9页)
The skill auto-detects required permissions from your codebase:
- iOS: reads for
Info.plist,NSCameraUsageDescription, etc.NSLocationWhenInUseUsageDescription - Android: reads for
AndroidManifest.xmlentriesuses-permission - React Native / Flutter: checks both
For each permission found, it generates a benefit-framed priming screen shown before the system dialog. This converts at 70–80%+ vs ~40% for cold prompts.
该技能会自动从你的代码库中检测所需权限:
- iOS:读取中的
Info.plist、NSCameraUsageDescription等配置NSLocationWhenInUseUsageDescription - Android:读取中的
AndroidManifest.xml条目uses-permission - React Native / Flutter:同时检查上述两个位置
针对每一个检测到的权限,它会生成一个基于价值说明的预申请页面,在系统弹窗之前展示。这种模式的授权转化率可达70-80%以上,而冷弹窗的转化率仅约40%。
Viral / Share Moment (Screen 12)
传播/分享节点(第12页)
The demo output is designed to be shareable — a meal plan, a workout, a savings projection. This is where organic growth originates.
演示环节的产出被设计为可分享的内容 — 饮食计划、健身方案、储蓄预测等,这是自然增长的来源。
Code Examples
代码示例
SwiftUI — Goal Question Screen
SwiftUI — 目标问卷页面
swift
// Generated by /app-onboarding-questionnaire
import SwiftUI
struct GoalQuestionView: View {
@EnvironmentObject var onboardingState: OnboardingState
let goals = [
OnboardingOption(id: "lose_weight", emoji: "⚖️", title: "Lose weight", subtitle: "Reach a healthier body"),
OnboardingOption(id: "build_muscle", emoji: "💪", title: "Build muscle", subtitle: "Get stronger and leaner"),
OnboardingOption(id: "eat_healthier", emoji: "🥗", title: "Eat healthier", subtitle: "Improve my nutrition"),
OnboardingOption(id: "save_time", emoji: "⏱️", title: "Save time cooking", subtitle: "Quick, easy meals")
]
var body: some View {
VStack(spacing: 24) {
OnboardingHeader(
title: "What's your main goal?",
subtitle: "We'll personalise everything around this"
)
VStack(spacing: 12) {
ForEach(goals) { goal in
OnboardingOptionRow(
option: goal,
isSelected: onboardingState.selectedGoal == goal.id
) {
onboardingState.selectedGoal = goal.id
}
}
}
Spacer()
PrimaryButton(title: "Continue", isEnabled: onboardingState.selectedGoal != nil) {
onboardingState.advance()
}
}
.padding()
}
}swift
// Generated by /app-onboarding-questionnaire
import SwiftUI
struct GoalQuestionView: View {
@EnvironmentObject var onboardingState: OnboardingState
let goals = [
OnboardingOption(id: "lose_weight", emoji: "⚖️", title: "Lose weight", subtitle: "Reach a healthier body"),
OnboardingOption(id: "build_muscle", emoji: "💪", title: "Build muscle", subtitle: "Get stronger and leaner"),
OnboardingOption(id: "eat_healthier", emoji: "🥗", title: "Eat healthier", subtitle: "Improve my nutrition"),
OnboardingOption(id: "save_time", emoji: "⏱️", title: "Save time cooking", subtitle: "Quick, easy meals")
]
var body: some View {
VStack(spacing: 24) {
OnboardingHeader(
title: "What's your main goal?",
subtitle: "We'll personalise everything around this"
)
VStack(spacing: 12) {
ForEach(goals) { goal in
OnboardingOptionRow(
option: goal,
isSelected: onboardingState.selectedGoal == goal.id
) {
onboardingState.selectedGoal = goal.id
}
}
}
Spacer()
PrimaryButton(title: "Continue", isEnabled: onboardingState.selectedGoal != nil) {
onboardingState.advance()
}
}
.padding()
}
}React Native — Tinder Swipe Cards Screen
React Native — Tinder滑动卡片页面
tsx
// Generated by /app-onboarding-questionnaire
import React, { useState } from 'react';
import { View, Text, StyleSheet, Animated, PanResponder } from 'react-native';
import { useOnboarding } from '../context/OnboardingContext';
const PAIN_STATEMENTS = [
"I don't know what to cook each week",
"I end up wasting food I've bought",
"Healthy eating feels too complicated",
"I spend too long deciding what to make",
];
export function TinderCardsScreen() {
const { addAgreedPain, advance } = useOnboarding();
const [currentIndex, setCurrentIndex] = useState(0);
const position = new Animated.ValueXY();
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (_, gesture) => {
position.setValue({ x: gesture.dx, y: gesture.dy });
},
onPanResponderRelease: (_, gesture) => {
if (gesture.dx > 120) {
swipe('agree');
} else if (gesture.dx < -120) {
swipe('disagree');
} else {
Animated.spring(position, { toValue: { x: 0, y: 0 }, useNativeDriver: true }).start();
}
},
});
const swipe = (direction: 'agree' | 'disagree') => {
if (direction === 'agree') {
addAgreedPain(PAIN_STATEMENTS[currentIndex]);
}
Animated.timing(position, {
toValue: { x: direction === 'agree' ? 500 : -500, y: 0 },
duration: 250,
useNativeDriver: true,
}).start(() => {
position.setValue({ x: 0, y: 0 });
if (currentIndex + 1 >= PAIN_STATEMENTS.length) {
advance();
} else {
setCurrentIndex(i => i + 1);
}
});
};
return (
<View style={styles.container}>
<Text style={styles.title}>Do these sound familiar?</Text>
<Text style={styles.subtitle}>Swipe right if yes, left if no</Text>
<Animated.View
style={[styles.card, { transform: position.getTranslateTransform() }]}
{...panResponder.panHandlers}
>
<Text style={styles.cardText}>{PAIN_STATEMENTS[currentIndex]}</Text>
</Animated.View>
</View>
);
}tsx
// Generated by /app-onboarding-questionnaire
import React, { useState } from 'react';
import { View, Text, StyleSheet, Animated, PanResponder } from 'react-native';
import { useOnboarding } from '../context/OnboardingContext';
const PAIN_STATEMENTS = [
"I don't know what to cook each week",
"I end up wasting food I've bought",
"Healthy eating feels too complicated",
"I spend too long deciding what to make",
];
export function TinderCardsScreen() {
const { addAgreedPain, advance } = useOnboarding();
const [currentIndex, setCurrentIndex] = useState(0);
const position = new Animated.ValueXY();
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (_, gesture) => {
position.setValue({ x: gesture.dx, y: gesture.dy });
},
onPanResponderRelease: (_, gesture) => {
if (gesture.dx > 120) {
swipe('agree');
} else if (gesture.dx < -120) {
swipe('disagree');
} else {
Animated.spring(position, { toValue: { x: 0, y: 0 }, useNativeDriver: true }).start();
}
},
});
const swipe = (direction: 'agree' | 'disagree') => {
if (direction === 'agree') {
addAgreedPain(PAIN_STATEMENTS[currentIndex]);
}
Animated.timing(position, {
toValue: { x: direction === 'agree' ? 500 : -500, y: 0 },
duration: 250,
useNativeDriver: true,
}).start(() => {
position.setValue({ x: 0, y: 0 });
if (currentIndex + 1 >= PAIN_STATEMENTS.length) {
advance();
} else {
setCurrentIndex(i => i + 1);
}
});
};
return (
<View style={styles.container}>
<Text style={styles.title}>Do these sound familiar?</Text>
<Text style={styles.subtitle}>Swipe right if yes, left if no</Text>
<Animated.View
style={[styles.card, { transform: position.getTranslateTransform() }]}
{...panResponder.panHandlers}
>
<Text style={styles.cardText}>{PAIN_STATEMENTS[currentIndex]}</Text>
</Animated.View>
</View>
);
}Flutter — Processing / Loading Screen
Flutter — 处理/加载页面
dart
// Generated by /app-onboarding-questionnaire
import 'package:flutter/material.dart';
class ProcessingScreen extends StatefulWidget {
final VoidCallback onComplete;
const ProcessingScreen({required this.onComplete, super.key});
State<ProcessingScreen> createState() => _ProcessingScreenState();
}
class _ProcessingScreenState extends State<ProcessingScreen>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
int _stepIndex = 0;
final List<String> _steps = [
'Analysing your goals...',
'Matching your preferences...',
'Crafting your personal plan...',
'Almost ready!',
];
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 4))
..addListener(() {
final newIndex = (_controller.value * _steps.length).floor().clamp(0, _steps.length - 1);
if (newIndex != _stepIndex) {
setState(() => _stepIndex = newIndex);
}
})
..forward().whenComplete(widget.onComplete);
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(value: _controller.value),
const SizedBox(height: 32),
AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
child: Text(
_steps[_stepIndex],
key: ValueKey(_stepIndex),
style: Theme.of(context).textTheme.titleMedium,
),
),
],
),
),
);
}
}dart
// Generated by /app-onboarding-questionnaire
import 'package:flutter/material.dart';
class ProcessingScreen extends StatefulWidget {
final VoidCallback onComplete;
const ProcessingScreen({required this.onComplete, super.key});
State<ProcessingScreen> createState() => _ProcessingScreenState;
}
class _ProcessingScreenState extends State<ProcessingScreen>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
int _stepIndex = 0;
final List<String> _steps = [
'Analysing your goals...',
'Matching your preferences...',
'Crafting your personal plan...',
'Almost ready!',
];
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 4))
..addListener(() {
final newIndex = (_controller.value * _steps.length).floor().clamp(0, _steps.length - 1);
if (newIndex != _stepIndex) {
setState(() => _stepIndex = newIndex);
}
})
..forward().whenComplete(widget.onComplete);
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(value: _controller.value),
const SizedBox(height: 32),
AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
child: Text(
_steps[_stepIndex],
key: ValueKey(_stepIndex),
style: Theme.of(context).textTheme.titleMedium,
),
),
],
),
),
);
}
}SwiftUI — Permission Priming Screen (auto-generated from Info.plist)
SwiftUI — 权限预申请页面(从Info.plist自动生成)
swift
// Generated from detected NSCameraUsageDescription in Info.plist
struct CameraPermissionPrimingView: View {
@EnvironmentObject var onboardingState: OnboardingState
var body: some View {
VStack(spacing: 32) {
Image(systemName: "camera.fill")
.font(.system(size: 64))
.foregroundColor(.accentColor)
VStack(spacing: 12) {
Text("Scan ingredients instantly")
.font(.title2.bold())
Text("Point your camera at any ingredient or barcode and we'll find matching recipes in seconds — no typing needed.")
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
}
VStack(spacing: 8) {
Label("Identify 10,000+ ingredients", systemImage: "checkmark.circle.fill")
Label("Scan barcodes for nutrition info", systemImage: "checkmark.circle.fill")
Label("Works offline for pantry items", systemImage: "checkmark.circle.fill")
}
.foregroundColor(.primary)
Spacer()
PrimaryButton(title: "Enable Camera Access") {
// System prompt shown AFTER this priming screen
onboardingState.requestCameraPermission()
}
Button("Not now") {
onboardingState.skipPermission(.camera)
}
.foregroundColor(.secondary)
}
.padding(32)
}
}swift
// Generated from detected NSCameraUsageDescription in Info.plist
struct CameraPermissionPrimingView: View {
@EnvironmentObject var onboardingState: OnboardingState
var body: some View {
VStack(spacing: 32) {
Image(systemName: "camera.fill")
.font(.system(size: 64))
.foregroundColor(.accentColor)
VStack(spacing: 12) {
Text("Scan ingredients instantly")
.font(.title2.bold())
Text("Point your camera at any ingredient or barcode and we'll find matching recipes in seconds — no typing needed.")
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
}
VStack(spacing: 8) {
Label("Identify 10,000+ ingredients", systemImage: "checkmark.circle.fill")
Label("Scan barcodes for nutrition info", systemImage: "checkmark.circle.fill")
Label("Works offline for pantry items", systemImage: "checkmark.circle.fill")
}
.foregroundColor(.primary)
Spacer()
PrimaryButton(title: "Enable Camera Access") {
// System prompt shown AFTER this priming screen
onboardingState.requestCameraPermission()
}
Button("Not now") {
onboardingState.skipPermission(.camera)
}
.foregroundColor(.secondary)
}
.padding(32)
}
}Onboarding State Management Pattern
新手引导状态管理模式
The skill generates a central state object to track progress across all screens:
swift
// SwiftUI example
class OnboardingState: ObservableObject {
@Published var currentScreen: OnboardingScreen = .welcome
@Published var selectedGoal: String?
@Published var agreedPains: [String] = []
@Published var preferences: UserPreferences = .default
@Published var demoResult: DemoOutput?
// Persisted to UserDefaults so onboarding survives app restarts
func advance() {
let next = currentScreen.next(given: self)
withAnimation { currentScreen = next }
save()
}
func save() {
// Skill generates serialisation code appropriate to your stack
}
}该技能会生成一个中心化的状态对象,用于跟踪所有页面的进度:
swift
// SwiftUI example
class OnboardingState: ObservableObject {
@Published var currentScreen: OnboardingScreen = .welcome
@Published var selectedGoal: String?
@Published var agreedPains: [String] = []
@Published var preferences: UserPreferences = .default
@Published var demoResult: DemoOutput?
// Persisted to UserDefaults so onboarding survives app restarts
func advance() {
let next = currentScreen.next(given: self)
withAnimation { currentScreen = next }
save()
}
func save() {
// Skill generates serialisation code appropriate to your stack
}
}Configuration Options
配置选项
When you run , the skill asks about:
/app-onboarding-questionnaire| Option | Description |
|---|---|
| App type | Subscription, freemium, one-time purchase |
| Core loop | The single thing users do in your app |
| Target audience | Who the app is for (used for copy tone) |
| Paywall timing | Whether to show paywall before or after account creation |
| Screens to skip | Comparison table, account gate, etc. |
| Brand colours | Used in generated SwiftUI/CSS/Flutter theme code |
当你运行 时,该技能会询问以下配置项:
/app-onboarding-questionnaire| 配置项 | 说明 |
|---|---|
| 应用类型 | 订阅制、免费增值、一次性付费 |
| 核心使用流程 | 用户在应用中的核心操作 |
| 目标受众 | 应用的目标用户群体(用于调整文案风格) |
| 付费墙时机 | 在账号创建之前还是之后展示付费墙 |
| 要跳过的页面 | 对比表格、账号门槛等 |
| 品牌配色 | 用于生成SwiftUI/CSS/Flutter主题代码 |
Resuming a Session
会话恢复
Progress is saved to Claude Code's memory. To resume:
/app-onboarding-questionnaire resumeTo restart from a specific screen:
/app-onboarding-questionnaire --from=paywall进度会保存到Claude Code的内存中,恢复会话请运行:
/app-onboarding-questionnaire resume要从指定页面重新开始,请运行:
/app-onboarding-questionnaire --from=paywallTroubleshooting
问题排查
Skill doesn't detect my permissions correctly
- iOS: ensure is at the project root or
Info.plist<AppName>/Info.plist - Android: ensure is at
AndroidManifest.xmlapp/src/main/AndroidManifest.xml - React Native: the skill checks both locations automatically
Generated code uses wrong framework
- The skill infers your framework from file extensions (,
.swift,.tsx,.dart).kt - If detection fails, specify explicitly:
/app-onboarding-questionnaire --framework=swiftui
Paywall screen doesn't match my payment provider
- The skill generates a UI shell; wire up your payment provider (RevenueCat, StoreKit 2, stripe-react-native) separately
- RevenueCat is the recommended integration — the skill generates compatible purchase call sites
Want fewer screens for a simpler app
- The skill asks about complexity during setup
- You can also specify:
/app-onboarding-questionnaire --screens=welcome,goal,processing,paywall
技能无法正确检测我的权限
- iOS:请确保位于项目根目录或
Info.plist路径下<AppName>/Info.plist - Android:请确保位于
AndroidManifest.xml路径下app/src/main/AndroidManifest.xml - React Native:技能会自动检查上述两个位置
生成的代码使用了错误的框架
- 技能会根据文件扩展名(、
.swift、.tsx、.dart)推断你使用的框架.kt - 如果检测失败,可以显式指定:
/app-onboarding-questionnaire --framework=swiftui
付费墙页面与你的支付服务商不匹配
- 技能会生成UI外壳,你需要单独接入支付服务商(RevenueCat、StoreKit 2、stripe-react-native等)
- 推荐使用RevenueCat集成,技能会生成兼容的购买调用代码
希望为简单应用减少页面数量
- 技能会在初始化时询问应用复杂度
- 你也可以显式指定:
/app-onboarding-questionnaire --screens=welcome,goal,processing,paywall
Reference
参考来源
The framework is based on analysis of the Mob recipe app's 19-screen onboarding flow, widely regarded as one of the highest-converting onboarding experiences on the App Store, combined with patterns from Noom, Headspace, and Duolingo.
该框架基于对Mob菜谱应用19屏新手引导流程的分析(该流程被普遍认为是App Store上转化率最高的新手引导体验之一),同时结合了Noom、Headspace和Duolingo的成熟模式。