create-aron-app 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/package.json +5 -2
  2. package/templates/_base/.cursor/agents/skills/clerk/SKILL.md +89 -0
  3. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/SKILL.md +142 -0
  4. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/api-specs-context.sh +30 -0
  5. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/execute-request.sh +88 -0
  6. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-endpoint-detail.sh +165 -0
  7. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-tag-endpoints.sh +208 -0
  8. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-tags.js +14 -0
  9. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/SKILL.md +157 -0
  10. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-in.md +224 -0
  11. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-up.md +190 -0
  12. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-in.md +314 -0
  13. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-up.md +259 -0
  14. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/show-component.md +125 -0
  15. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/SKILL.md +94 -0
  16. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/api-routes.md +50 -0
  17. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/caching-auth.md +56 -0
  18. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/middleware-strategies.md +68 -0
  19. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/server-actions.md +56 -0
  20. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/server-vs-client.md +104 -0
  21. package/templates/_base/.cursor/agents/skills/clerk/clerk-webhooks/SKILL.md +131 -0
  22. package/templates/_base/.cursor/agents/skills/shadcn/SKILL.md +241 -0
  23. package/templates/_base/.cursor/agents/skills/shadcn/agents/openai.yml +5 -0
  24. package/templates/_base/.cursor/agents/skills/shadcn/assets/shadcn-small.png +0 -0
  25. package/templates/_base/.cursor/agents/skills/shadcn/assets/shadcn.png +0 -0
  26. package/templates/_base/.cursor/agents/skills/shadcn/cli.md +257 -0
  27. package/templates/_base/.cursor/agents/skills/shadcn/customization.md +202 -0
  28. package/templates/_base/.cursor/agents/skills/shadcn/evals/evals.json +47 -0
  29. package/templates/_base/.cursor/agents/skills/shadcn/mcp.md +94 -0
  30. package/templates/_base/.cursor/agents/skills/shadcn/rules/base-vs-radix.md +306 -0
  31. package/templates/_base/.cursor/agents/skills/shadcn/rules/composition.md +195 -0
  32. package/templates/_base/.cursor/agents/skills/shadcn/rules/forms.md +192 -0
  33. package/templates/_base/.cursor/agents/skills/shadcn/rules/icons.md +101 -0
  34. package/templates/_base/.cursor/agents/skills/shadcn/rules/styling.md +162 -0
  35. package/templates/_base/.cursor/commands/builder.md +0 -0
  36. package/templates/_base/.cursor/commands/pr.md +7 -0
  37. package/templates/_base/.cursor/rules/api_architecture.mdc +268 -0
  38. package/templates/_base/.cursor/rules/coding_standards.mdc +64 -0
  39. package/templates/_base/.cursor/rules/convex_rules.mdc +675 -0
  40. package/templates/_base/.cursor/rules/frontend_rules.mdc +268 -0
  41. package/templates/_base/.env.convex.example +3 -0
  42. package/templates/_base/.github/workflows/ci.yml +29 -0
  43. package/templates/_base/.nvmrc +1 -0
  44. package/templates/_base/.vscode/settings.json +9 -0
  45. package/templates/_base/apps/api/auth.config.ts +18 -0
  46. package/templates/_base/apps/api/functions.ts +99 -0
  47. package/templates/_base/apps/api/project.json +22 -0
  48. package/templates/_base/apps/api/schema.ts +11 -0
  49. package/templates/_base/apps/api/todos/crud.ts +81 -0
  50. package/templates/_base/apps/api/todos/schema.ts +11 -0
  51. package/templates/_base/apps/api/todos/types.ts +22 -0
  52. package/templates/_base/apps/api/tsconfig.json +23 -0
  53. package/templates/_base/apps/api/types.ts +16 -0
  54. package/templates/_base/biome.json +114 -0
  55. package/templates/_base/convex.json +4 -0
  56. package/templates/_base/emails/project.json +16 -0
  57. package/templates/_base/emails/tsconfig.json +5 -0
  58. package/templates/_base/emails/welcome_email.tsx +53 -0
  59. package/templates/_base/nx.json +29 -0
  60. package/templates/_base/package.json +73 -0
  61. package/templates/_base/scripts/sync_convex_env.ts +63 -0
  62. package/templates/_base/shared/assets/image.d.ts +4 -0
  63. package/templates/_base/shared/assets/src/styles/global.css +73 -0
  64. package/templates/_base/shared/assets/tsconfig.json +5 -0
  65. package/templates/_base/shared/ui/src/base/alert_dialog.tsx +139 -0
  66. package/templates/_base/shared/ui/src/base/badge.tsx +33 -0
  67. package/templates/_base/shared/ui/src/base/basic_data_table.tsx +61 -0
  68. package/templates/_base/shared/ui/src/base/button.tsx +69 -0
  69. package/templates/_base/shared/ui/src/base/button_group.tsx +82 -0
  70. package/templates/_base/shared/ui/src/base/card.tsx +79 -0
  71. package/templates/_base/shared/ui/src/base/checkbox.tsx +26 -0
  72. package/templates/_base/shared/ui/src/base/command.tsx +165 -0
  73. package/templates/_base/shared/ui/src/base/dialog.tsx +129 -0
  74. package/templates/_base/shared/ui/src/base/dropdown_menu.tsx +232 -0
  75. package/templates/_base/shared/ui/src/base/form.tsx +161 -0
  76. package/templates/_base/shared/ui/src/base/input.tsx +129 -0
  77. package/templates/_base/shared/ui/src/base/label.tsx +19 -0
  78. package/templates/_base/shared/ui/src/base/popover.tsx +46 -0
  79. package/templates/_base/shared/ui/src/base/radio_group.tsx +49 -0
  80. package/templates/_base/shared/ui/src/base/resizable.tsx +55 -0
  81. package/templates/_base/shared/ui/src/base/scroll_area.tsx +44 -0
  82. package/templates/_base/shared/ui/src/base/select.tsx +151 -0
  83. package/templates/_base/shared/ui/src/base/separator.tsx +32 -0
  84. package/templates/_base/shared/ui/src/base/sheet.tsx +130 -0
  85. package/templates/_base/shared/ui/src/base/side_bar.tsx +688 -0
  86. package/templates/_base/shared/ui/src/base/skeleton.tsx +7 -0
  87. package/templates/_base/shared/ui/src/base/spinner.tsx +20 -0
  88. package/templates/_base/shared/ui/src/base/switch.tsx +27 -0
  89. package/templates/_base/shared/ui/src/base/table.tsx +91 -0
  90. package/templates/_base/shared/ui/src/base/text_area.tsx +21 -0
  91. package/templates/_base/shared/ui/src/base/tooltip.tsx +31 -0
  92. package/templates/_base/shared/ui/src/base/utils.ts +17 -0
  93. package/templates/_base/shared/ui/src/hooks/use_keyboard_press.tsx +48 -0
  94. package/templates/_base/shared/ui/src/hooks/use_keyboard_release.tsx +48 -0
  95. package/templates/_base/shared/ui/src/hooks/use_mobile.tsx +25 -0
  96. package/templates/_base/shared/ui/src/hooks/use_mouse_click.tsx +44 -0
  97. package/templates/_base/shared/ui/src/hooks/use_mouse_location.tsx +55 -0
  98. package/templates/_base/shared/ui/src/hooks/use_outside_click.tsx +29 -0
  99. package/templates/_base/shared/ui/src/hooks/use_query_params.tsx +33 -0
  100. package/templates/_base/shared/ui/tsconfig.json +8 -0
  101. package/templates/_base/shared/utils/src/convex.ts +3 -0
  102. package/templates/_base/shared/utils/src/time.ts +12 -0
  103. package/templates/_base/shared/utils/tsconfig.json +5 -0
  104. package/templates/_base/skills-lock.json +35 -0
  105. package/templates/_base/tsconfig.base.json +34 -0
  106. package/templates/nextjs/.env.example +8 -0
  107. package/templates/nextjs/index.d.ts +6 -0
  108. package/templates/nextjs/next-env.d.ts +5 -0
  109. package/templates/nextjs/next.config.js +22 -0
  110. package/templates/nextjs/postcss.config.js +17 -0
  111. package/templates/nextjs/project.json +22 -0
  112. package/templates/nextjs/src/app/(auth)/layout.tsx +21 -0
  113. package/templates/nextjs/src/app/(auth)/not-allowed/page.tsx +22 -0
  114. package/templates/nextjs/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +15 -0
  115. package/templates/nextjs/src/app/(dashboard)/layout.tsx +27 -0
  116. package/templates/nextjs/src/app/(dashboard)/page.tsx +5 -0
  117. package/templates/nextjs/src/app/(dashboard)/todos/[id]/page.tsx +23 -0
  118. package/templates/nextjs/src/app/(dashboard)/todos/page.tsx +16 -0
  119. package/templates/nextjs/src/app/app.css +3 -0
  120. package/templates/nextjs/src/app/layout.tsx +26 -0
  121. package/templates/nextjs/src/convex.ts +11 -0
  122. package/templates/nextjs/src/middleware.ts +18 -0
  123. package/templates/nextjs/src/providers/convex_provider.tsx +44 -0
  124. package/templates/nextjs/src/surfaces/home_surface.tsx +22 -0
  125. package/templates/nextjs/src/surfaces/todos/all_todos_surface.tsx +97 -0
  126. package/templates/nextjs/src/surfaces/todos/create_todo_sheet.tsx +107 -0
  127. package/templates/nextjs/src/surfaces/todos/single_todo_surface.tsx +90 -0
  128. package/templates/nextjs/src/ui/sidebar/nav_link.tsx +36 -0
  129. package/templates/nextjs/src/ui/sidebar/sidebar.tsx +125 -0
  130. package/templates/nextjs/src/utils/font.ts +9 -0
  131. package/templates/nextjs/tsconfig.json +42 -0
  132. package/templates/react-router/.env.example +8 -0
  133. package/templates/react-router/postcss.config.js +15 -0
  134. package/templates/react-router/project.json +23 -0
  135. package/templates/react-router/public/favicon.ico +0 -0
  136. package/templates/react-router/react-router.config.ts +9 -0
  137. package/templates/react-router/src/app.css +3 -0
  138. package/templates/react-router/src/components/error_boundary.tsx +33 -0
  139. package/templates/react-router/src/layouts/sidebar/sidebar_aside/sidebar_aside.tsx +76 -0
  140. package/templates/react-router/src/layouts/sidebar/sidebar_aside/user_menu.tsx +36 -0
  141. package/templates/react-router/src/layouts/sidebar/sidebar_layout.tsx +22 -0
  142. package/templates/react-router/src/providers/api_auth_provider.tsx +38 -0
  143. package/templates/react-router/src/root.tsx +37 -0
  144. package/templates/react-router/src/routes/auth/layout.tsx +13 -0
  145. package/templates/react-router/src/routes/auth/sign-in.tsx +13 -0
  146. package/templates/react-router/src/routes/index.tsx +9 -0
  147. package/templates/react-router/src/routes/layout.tsx +26 -0
  148. package/templates/react-router/src/routes/todos/[id].tsx +22 -0
  149. package/templates/react-router/src/routes/todos/index.tsx +13 -0
  150. package/templates/react-router/src/routes.ts +12 -0
  151. package/templates/react-router/src/surfaces/home_surface.tsx +20 -0
  152. package/templates/react-router/src/surfaces/todos/all_todos_surface.tsx +87 -0
  153. package/templates/react-router/src/surfaces/todos/create_todo_sheet.tsx +102 -0
  154. package/templates/react-router/src/surfaces/todos/single_todo_surface.tsx +81 -0
  155. package/templates/react-router/tsconfig.json +20 -0
  156. package/templates/react-router/vite.config.ts +40 -0
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env bash
2
+ # extract-tag-endpoints.sh
3
+ #
4
+ # Extracts all endpoints for a given tag from an OpenAPI YAML spec (stdin),
5
+ # along with any $ref'd schemas/components.
6
+ #
7
+ # Usage:
8
+ # curl -s <spec-url> | bash extract-tag-endpoints.sh "Billing"
9
+
10
+ set -euo pipefail
11
+
12
+ TAG="${1:?Usage: extract-tag-endpoints.sh <tag-name>}"
13
+ TMPDIR_WORK=$(mktemp -d)
14
+ trap 'rm -rf "$TMPDIR_WORK"' EXIT
15
+
16
+ SPEC="$TMPDIR_WORK/spec.yml"
17
+ cat > "$SPEC"
18
+
19
+ # 1. Find all path+method blocks that have a matching tag
20
+ # Strategy: find line numbers of path entries (lines starting with " /"),
21
+ # then for each method block under that path, check if it contains the tag.
22
+
23
+ node - "$TAG" "$SPEC" <<'SCRIPT'
24
+ const fs = require("fs");
25
+ const tag = process.argv[2];
26
+ const specFile = process.argv[3];
27
+ const lines = fs.readFileSync(specFile, "utf8").split("\n");
28
+
29
+ const tagLower = tag.toLowerCase();
30
+
31
+ // Phase 1: Find all path definitions and their method blocks
32
+ // Paths start at indent 2 with " /"
33
+ // Methods start at indent 4 with " get:", " post:", etc.
34
+ const methods = ["get", "post", "put", "patch", "delete", "options", "head"];
35
+ const endpoints = [];
36
+ const refs = new Set();
37
+
38
+ let currentPath = null;
39
+ let currentMethod = null;
40
+ let blockStart = -1;
41
+ let blockLines = [];
42
+ let inPaths = false;
43
+ let inComponents = false;
44
+
45
+ // First pass: locate the "paths:" and "components:" top-level keys
46
+ let pathsStart = -1;
47
+ let pathsEnd = -1;
48
+ let componentsStart = -1;
49
+
50
+ for (let i = 0; i < lines.length; i++) {
51
+ const line = lines[i];
52
+ if (/^paths:\s*$/.test(line)) {
53
+ pathsStart = i;
54
+ } else if (pathsStart >= 0 && pathsEnd < 0 && /^\S/.test(line) && i > pathsStart) {
55
+ pathsEnd = i;
56
+ }
57
+ if (/^components:\s*$/.test(line)) {
58
+ componentsStart = i;
59
+ }
60
+ }
61
+ if (pathsEnd < 0) pathsEnd = lines.length;
62
+
63
+ // Second pass: extract endpoints matching the tag
64
+ function flushBlock() {
65
+ if (!currentPath || !currentMethod || blockLines.length === 0) return;
66
+
67
+ // Check if this block has the target tag
68
+ let inTags = false;
69
+ let hasTag = false;
70
+ const blockRefs = [];
71
+
72
+ for (const bl of blockLines) {
73
+ const trimmed = bl.trim();
74
+
75
+ // Detect tags section
76
+ if (/^tags:\s*$/.test(trimmed)) {
77
+ inTags = true;
78
+ continue;
79
+ }
80
+ if (inTags) {
81
+ if (/^- /.test(trimmed)) {
82
+ const tagVal = trimmed.replace(/^- /, "").trim().replace(/^['"]|['"]$/g, "");
83
+ if (tagVal.toLowerCase() === tagLower) hasTag = true;
84
+ } else {
85
+ inTags = false;
86
+ }
87
+ }
88
+
89
+ // Collect $ref values
90
+ const refMatch = bl.match(/\$ref:\s*['"]?(#\/[^'"}\s]+)['"]?/);
91
+ if (refMatch) blockRefs.push(refMatch[1]);
92
+ }
93
+
94
+ if (hasTag) {
95
+ // Extract summary, operationId, description
96
+ let summary = "";
97
+ let operationId = "";
98
+ let description = "";
99
+ let params = [];
100
+ let inDesc = false;
101
+ let inParams = false;
102
+
103
+ for (const bl of blockLines) {
104
+ const trimmed = bl.trim();
105
+ const indent = bl.length - bl.trimStart().length;
106
+
107
+ // Only capture operation-level keys (indent 6 = direct children of the method block)
108
+ if (indent === 6) {
109
+ const sumMatch = trimmed.match(/^summary:\s*(.+)/);
110
+ if (sumMatch) summary = sumMatch[1].replace(/^['"]|['"]$/g, "");
111
+
112
+ const opMatch = trimmed.match(/^operationId:\s*(.+)/);
113
+ if (opMatch) operationId = opMatch[1].replace(/^['"]|['"]$/g, "");
114
+
115
+ const descMatch = trimmed.match(/^description:\s*(.+)/);
116
+ if (descMatch && !inDesc) {
117
+ const val = descMatch[1].trim();
118
+ if (val === "|-" || val === "|" || val === ">-" || val === ">") {
119
+ inDesc = true;
120
+ } else {
121
+ description = val.replace(/^['"]|['"]$/g, "");
122
+ }
123
+ continue;
124
+ }
125
+ }
126
+
127
+ if (inDesc) {
128
+ // Continuation lines of description — grab first non-empty line
129
+ if (!description && trimmed.length > 0) {
130
+ description = trimmed;
131
+ }
132
+ // Stop when we hit the next operation-level key
133
+ if (indent === 6 && trimmed.length > 0 && !/^description:/.test(trimmed)) {
134
+ inDesc = false;
135
+ }
136
+ }
137
+ }
138
+
139
+ endpoints.push({
140
+ method: currentMethod.toUpperCase(),
141
+ path: currentPath,
142
+ operationId,
143
+ summary,
144
+ description,
145
+ refs: blockRefs,
146
+ });
147
+
148
+ for (const r of blockRefs) refs.add(r);
149
+ }
150
+ }
151
+
152
+ for (let i = pathsStart + 1; i < pathsEnd; i++) {
153
+ const line = lines[i];
154
+
155
+ // Path line: exactly 2 spaces + /
156
+ if (/^ {2}\/\S/.test(line)) {
157
+ flushBlock();
158
+ currentPath = line.trim().replace(/:$/, "");
159
+ currentMethod = null;
160
+ blockLines = [];
161
+ continue;
162
+ }
163
+
164
+ // Method line: exactly 4 spaces + method name
165
+ const methodMatch = line.match(/^ {4}(\w+):\s*$/);
166
+ if (methodMatch && methods.includes(methodMatch[1])) {
167
+ flushBlock();
168
+ currentMethod = methodMatch[1];
169
+ blockLines = [];
170
+ continue;
171
+ }
172
+
173
+ if (currentMethod) {
174
+ blockLines.push(line);
175
+ }
176
+ }
177
+ flushBlock();
178
+
179
+ // Output endpoints
180
+ if (endpoints.length === 0) {
181
+ console.error(`No endpoints found for tag: "${tag}"`);
182
+ process.exit(1);
183
+ }
184
+
185
+ console.log(`## Endpoints for "${tag}" (${endpoints.length} total)\n`);
186
+ for (const ep of endpoints) {
187
+ console.log(`### \`${ep.method}\` \`${ep.path}\``);
188
+ if (ep.operationId) console.log(`- **operationId**: \`${ep.operationId}\``);
189
+ if (ep.summary) console.log(`- **summary**: ${ep.summary}`);
190
+ if (ep.description && ep.description !== ep.summary)
191
+ console.log(`- **description**: ${ep.description}`);
192
+ if (ep.refs.length > 0) {
193
+ console.log(`- **refs**: ${ep.refs.map(r => "\`" + r.split("/").pop() + "\`").join(", ")}`);
194
+ }
195
+ console.log();
196
+ }
197
+
198
+ // Output unique refs list
199
+ if (refs.size > 0) {
200
+ console.log(`## Referenced Components (${refs.size} unique)\n`);
201
+ const sorted = [...refs].sort();
202
+ for (const r of sorted) {
203
+ const name = r.split("/").pop();
204
+ const category = r.split("/").slice(0, -1).join("/").replace("#/", "");
205
+ console.log(`- \`${name}\` (${category})`);
206
+ }
207
+ }
208
+ SCRIPT
@@ -0,0 +1,14 @@
1
+ let input = "";
2
+ process.stdin.on("data", d => input += d);
3
+ process.stdin.on("end", () => {
4
+ const lines = input.replace(/\r/g, "").split("\n");
5
+ let inTags = false;
6
+ for (const line of lines) {
7
+ if (line === "tags:") { inTags = true; continue; }
8
+ if (inTags && line.length > 0 && line[0] !== " ") break;
9
+ if (inTags) {
10
+ const m = line.match(/^\s{2}- name:\s*(.+)/);
11
+ if (m) console.log(m[1]);
12
+ }
13
+ }
14
+ });
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: clerk-custom-ui
3
+ description: Custom authentication flows and component appearance - hooks (useSignIn, useSignUp), themes, colors, fonts, CSS. Use for custom sign-in/sign-up flows, appearance styling, visual customization, branding.
4
+ allowed-tools: WebFetch
5
+ license: MIT
6
+ metadata:
7
+ author: clerk
8
+ version: "2.1.0"
9
+ ---
10
+
11
+ # Custom UI
12
+
13
+ > **Prerequisite**: Ensure `ClerkProvider` wraps your app. See `setup/`.
14
+ >
15
+ > **Version**: Check `package.json` for the SDK version — see `clerk` skill for the version table. This determines which custom flow references to use below.
16
+
17
+ This skill covers two areas:
18
+ 1. **Custom authentication flows** — build your own sign-in/sign-up UI with hooks
19
+ 2. **Appearance customization** — theme, style, and brand Clerk's pre-built components
20
+
21
+ ## Custom Flow References
22
+
23
+ | Task | Core 2 | Current |
24
+ |------|--------|---------|
25
+ | Custom sign-in (useSignIn) | `core-2/custom-sign-in.md` | `core-3/custom-sign-in.md` |
26
+ | Custom sign-up (useSignUp) | `core-2/custom-sign-up.md` | `core-3/custom-sign-up.md` |
27
+ | `<Show>` component | *(use `<SignedIn>`, `<SignedOut>`, `<Protect>`)* | `core-3/show-component.md` |
28
+
29
+ ---
30
+
31
+ ## Appearance Customization
32
+
33
+ Appearance customization applies to both Core 2 and the current SDK.
34
+
35
+ ### Component Customization Options
36
+
37
+ | Task | Documentation |
38
+ |------|---------------|
39
+ | Appearance prop overview | https://clerk.com/docs/nextjs/guides/customizing-clerk/appearance-prop/overview |
40
+ | Options (structure, logo, buttons) | https://clerk.com/docs/nextjs/guides/customizing-clerk/appearance-prop/layout |
41
+ | Themes (pre-built dark/light) | https://clerk.com/docs/nextjs/guides/customizing-clerk/appearance-prop/themes |
42
+ | Variables (colors, fonts, spacing) | https://clerk.com/docs/nextjs/guides/customizing-clerk/appearance-prop/variables |
43
+ | CAPTCHA configuration | https://clerk.com/docs/nextjs/guides/customizing-clerk/appearance-prop/captcha |
44
+ | Bring your own CSS | https://clerk.com/docs/nextjs/guides/customizing-clerk/appearance-prop/bring-your-own-css |
45
+
46
+ ### Appearance Pattern
47
+
48
+ ```typescript
49
+ <SignIn
50
+ appearance={{
51
+ variables: {
52
+ colorPrimary: '#0000ff',
53
+ borderRadius: '0.5rem',
54
+ },
55
+ options: {
56
+ logoImageUrl: '/logo.png',
57
+ socialButtonsVariant: 'iconButton',
58
+ },
59
+ }}
60
+ />
61
+ ```
62
+
63
+ > **Core 2 ONLY (skip if current SDK):** The `options` property was named `layout`. Use `layout: { logoImageUrl: '...', socialButtonsVariant: '...' }` instead of `options`.
64
+
65
+ ### variables (colors, typography, borders)
66
+
67
+ | Property | Description |
68
+ |----------|-------------|
69
+ | `colorPrimary` | Primary color throughout |
70
+ | `colorBackground` | Background color |
71
+ | `borderRadius` | Border radius (default: `0.375rem`) |
72
+
73
+ **Opacity change:** `colorRing` and `colorModalBackdrop` now render at full opacity. Use explicit `rgba()` values if you need transparency.
74
+
75
+ > **Core 2 ONLY (skip if current SDK):** `colorRing` and `colorModalBackdrop` rendered at 15% opacity by default.
76
+
77
+ ### options (structure, logo, social buttons)
78
+
79
+ | Property | Description |
80
+ |----------|-------------|
81
+ | `logoImageUrl` | URL to custom logo |
82
+ | `socialButtonsVariant` | `'blockButton'` \| `'iconButton'` \| `'auto'` |
83
+ | `socialButtonsPlacement` | `'top'` \| `'bottom'` |
84
+ | `showOptionalFields` | Show optional fields (default: `false`) |
85
+
86
+ > **Core 2 ONLY (skip if current SDK):** This property is called `layout`, not `options`. Also, `showOptionalFields` defaulted to `true`.
87
+
88
+ ### Themes
89
+
90
+ Install themes from `@clerk/ui`:
91
+
92
+ ```bash
93
+ npm install @clerk/ui
94
+ ```
95
+
96
+ ```typescript
97
+ import { dark } from '@clerk/ui/themes'
98
+
99
+ <ClerkProvider appearance={{ theme: dark }} />
100
+ ```
101
+
102
+ > **Core 2 ONLY (skip if current SDK):** Themes are imported from `@clerk/themes` (install `@clerk/themes` instead of `@clerk/ui`). Example: `import { dark } from '@clerk/themes'`.
103
+
104
+ **Theme stacking** — pass an array where the last theme takes precedence:
105
+
106
+ ```typescript
107
+ import { dark, neobrutalism } from '@clerk/ui/themes'
108
+
109
+ <ClerkProvider appearance={{ theme: [dark, neobrutalism] }} />
110
+ ```
111
+
112
+ **CSS `color-scheme` support** — the default theme respects the CSS `color-scheme` property for automatic light/dark mode toggling.
113
+
114
+ **Available themes:** `dark`, `neobrutalism`, `shadcn`, `simple`
115
+
116
+ #### shadcn Theme (HIGH PRIORITY)
117
+
118
+ > **IMPORTANT:** If the project has `components.json` (shadcn/ui installed), ALWAYS apply the shadcn theme as the FIRST customization step. This ensures Clerk components visually match the existing shadcn design system. Skip this only if the user explicitly requests a different theme.
119
+
120
+ **If the project uses shadcn/ui** (check for `components.json` in the project root), **always use the shadcn theme**:
121
+
122
+ ```typescript
123
+ import { shadcn } from '@clerk/ui/themes'
124
+
125
+ <ClerkProvider appearance={{ theme: shadcn }} />
126
+ ```
127
+
128
+ Also import shadcn CSS in your global styles:
129
+ ```css
130
+ @import 'tailwindcss';
131
+ @import '@clerk/ui/themes/shadcn.css';
132
+ ```
133
+
134
+ > **Core 2 ONLY (skip if current SDK):** Import from `@clerk/themes` and `@clerk/themes/shadcn.css`:
135
+ > ```typescript
136
+ > import { shadcn } from '@clerk/themes'
137
+ > ```
138
+ > ```css
139
+ > @import '@clerk/themes/shadcn.css';
140
+ > ```
141
+
142
+ ## Workflow
143
+
144
+ 1. Identify customization needs (custom flow or appearance)
145
+ 2. For custom flows: check SDK version → read appropriate `core-2/` or `core-3/` reference
146
+ 3. For appearance: WebFetch the appropriate documentation from table above
147
+ 4. Apply appearance prop to your Clerk components or build custom flow with hooks
148
+
149
+ ## Common Pitfalls
150
+
151
+ | Issue | Solution |
152
+ |-------|----------|
153
+ | Colors not applying | Use `colorPrimary` not `primaryColor` |
154
+ | Logo not showing | Put `logoImageUrl` inside `options: {}` (or `layout: {}` in Core 2) |
155
+ | Social buttons wrong | Add `socialButtonsVariant: 'iconButton'` in `options` (or `layout` in Core 2) |
156
+ | Styling not working | Use appearance prop, not direct CSS (unless with bring-your-own-css) |
157
+ | Hook returns different shape | Check SDK version — Core 2 and current have completely different `useSignIn`/`useSignUp` APIs |
@@ -0,0 +1,224 @@
1
+ # Custom Sign-In Flow (Core 2)
2
+
3
+ > This document covers the **older SDK** (`@clerk/nextjs` v5–v6, `@clerk/clerk-react` v5–v6, `@clerk/clerk-expo` v1–v2). For the current SDK, see `core-3/custom-sign-in.md`.
4
+
5
+ Build a custom sign-in experience using the `useSignIn()` hook.
6
+
7
+ ## Hook API
8
+
9
+ ```typescript
10
+ import { useSignIn } from '@clerk/nextjs' // or @clerk/clerk-react, @clerk/clerk-expo
11
+
12
+ const { signIn, isLoaded, setActive } = useSignIn()
13
+ ```
14
+
15
+ | Property | Type | Description |
16
+ |----------|------|-------------|
17
+ | `signIn` | `SignIn` | Sign-in object with methods |
18
+ | `isLoaded` | `boolean` | Whether the hook has loaded |
19
+ | `setActive` | `(params) => Promise` | Sets the active session |
20
+
21
+ ## Sign-In Flow
22
+
23
+ ### 1. Create Sign-In
24
+
25
+ ```typescript
26
+ const result = await signIn.create({
27
+ identifier: 'user@example.com',
28
+ password: 'securePassword123',
29
+ })
30
+ ```
31
+
32
+ ### 2. First Factor Verification
33
+
34
+ If additional verification is needed (email code, phone code):
35
+
36
+ ```typescript
37
+ // Prepare first factor
38
+ await signIn.prepareFirstFactor({
39
+ strategy: 'email_code', // or 'phone_code'
40
+ })
41
+
42
+ // Attempt first factor
43
+ const result = await signIn.attemptFirstFactor({
44
+ strategy: 'email_code',
45
+ code: '123456',
46
+ })
47
+ ```
48
+
49
+ ### 3. Second Factor (MFA)
50
+
51
+ If the sign-in requires MFA:
52
+
53
+ ```typescript
54
+ // Prepare second factor
55
+ await signIn.prepareSecondFactor({
56
+ strategy: 'email_code', // or 'phone_code'
57
+ })
58
+
59
+ // Attempt second factor
60
+ const result = await signIn.attemptSecondFactor({
61
+ strategy: 'totp', // or 'email_code', 'phone_code', 'backup_code'
62
+ code: '123456',
63
+ })
64
+ ```
65
+
66
+ ### 4. Finalize
67
+
68
+ Set the active session after successful authentication:
69
+
70
+ ```typescript
71
+ await setActive({ session: signIn.createdSessionId })
72
+ ```
73
+
74
+ ### Password Reset
75
+
76
+ ```typescript
77
+ // 1. Start reset flow
78
+ await signIn.create({ strategy: 'reset_password_email_code', identifier: 'user@example.com' })
79
+
80
+ // or prepare after initial create:
81
+ await signIn.prepareFirstFactor({ strategy: 'reset_password_email_code' })
82
+
83
+ // 2. Verify reset code
84
+ await signIn.attemptFirstFactor({ strategy: 'reset_password_email_code', code: '123456' })
85
+
86
+ // 3. Set new password
87
+ await signIn.resetPassword({ password: 'newSecurePassword123' })
88
+ ```
89
+
90
+ ### SSO (OAuth)
91
+
92
+ ```typescript
93
+ await signIn.authenticateWithRedirect({
94
+ strategy: 'oauth_google', // or 'oauth_github', etc.
95
+ redirectUrl: '/sso-callback',
96
+ redirectUrlComplete: '/',
97
+ })
98
+ ```
99
+
100
+ ## Error Handling
101
+
102
+ Use try/catch with `isClerkAPIResponseError()`:
103
+
104
+ ```typescript
105
+ import { isClerkAPIResponseError } from '@clerk/nextjs/errors'
106
+
107
+ try {
108
+ await signIn.create({ identifier, password })
109
+ } catch (err) {
110
+ if (isClerkAPIResponseError(err)) {
111
+ err.errors.forEach((e) => {
112
+ console.log(e.code) // e.g. 'form_identifier_not_found'
113
+ console.log(e.message) // Human-readable message
114
+ console.log(e.longMessage) // Detailed message
115
+ })
116
+ }
117
+ }
118
+ ```
119
+
120
+ ## Complete Example: Email/Password with MFA
121
+
122
+ ```tsx
123
+ 'use client'
124
+ import { useState } from 'react'
125
+ import { useSignIn } from '@clerk/nextjs'
126
+ import { isClerkAPIResponseError } from '@clerk/nextjs/errors'
127
+ import { useRouter } from 'next/navigation'
128
+
129
+ export default function SignInPage() {
130
+ const { signIn, isLoaded, setActive } = useSignIn()
131
+ const router = useRouter()
132
+
133
+ const [identifier, setIdentifier] = useState('')
134
+ const [password, setPassword] = useState('')
135
+ const [mfaCode, setMfaCode] = useState('')
136
+ const [step, setStep] = useState<'credentials' | 'mfa'>('credentials')
137
+ const [error, setError] = useState('')
138
+
139
+ if (!isLoaded) return <div>Loading...</div>
140
+
141
+ async function handleSignIn(e: React.FormEvent) {
142
+ e.preventDefault()
143
+ setError('')
144
+
145
+ try {
146
+ const result = await signIn.create({ identifier, password })
147
+
148
+ if (result.status === 'needs_second_factor') {
149
+ setStep('mfa')
150
+ return
151
+ }
152
+
153
+ if (result.status === 'complete') {
154
+ await setActive({ session: result.createdSessionId })
155
+ router.push('/')
156
+ }
157
+ } catch (err) {
158
+ if (isClerkAPIResponseError(err)) {
159
+ setError(err.errors[0]?.message || 'Sign in failed')
160
+ }
161
+ }
162
+ }
163
+
164
+ async function handleMFA(e: React.FormEvent) {
165
+ e.preventDefault()
166
+ setError('')
167
+
168
+ try {
169
+ const result = await signIn.attemptSecondFactor({
170
+ strategy: 'totp',
171
+ code: mfaCode,
172
+ })
173
+
174
+ if (result.status === 'complete') {
175
+ await setActive({ session: result.createdSessionId })
176
+ router.push('/')
177
+ }
178
+ } catch (err) {
179
+ if (isClerkAPIResponseError(err)) {
180
+ setError(err.errors[0]?.message || 'Verification failed')
181
+ }
182
+ }
183
+ }
184
+
185
+ if (step === 'mfa') {
186
+ return (
187
+ <form onSubmit={handleMFA}>
188
+ <input
189
+ type="text"
190
+ value={mfaCode}
191
+ onChange={(e) => setMfaCode(e.target.value)}
192
+ placeholder="Enter MFA code"
193
+ />
194
+ {error && <p>{error}</p>}
195
+ <button type="submit">Verify</button>
196
+ </form>
197
+ )
198
+ }
199
+
200
+ return (
201
+ <form onSubmit={handleSignIn}>
202
+ <input
203
+ type="email"
204
+ value={identifier}
205
+ onChange={(e) => setIdentifier(e.target.value)}
206
+ placeholder="Email"
207
+ />
208
+ <input
209
+ type="password"
210
+ value={password}
211
+ onChange={(e) => setPassword(e.target.value)}
212
+ placeholder="Password"
213
+ />
214
+ {error && <p>{error}</p>}
215
+ <button type="submit">Sign In</button>
216
+ </form>
217
+ )
218
+ }
219
+ ```
220
+
221
+ ## Docs
222
+
223
+ - [Custom sign-in flow](https://clerk.com/docs/custom-flows/overview)
224
+ - [useSignIn() reference](https://clerk.com/docs/references/react/use-sign-in)