@kennethsolomon/shipkit 3.2.0 → 3.4.0

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.
@@ -0,0 +1,236 @@
1
+ # Landing Page — SaaS Section Patterns
2
+
3
+ Structure and patterns for the mandatory MVP landing page.
4
+
5
+ ---
6
+
7
+ ## Required Sections (in order)
8
+
9
+ Every landing page must include ALL of these sections. Do not skip any.
10
+
11
+ ### 1. Navbar
12
+
13
+ ```
14
+ [Logo/Name] [Features] [Pricing] [Waitlist] [Join Waitlist →]
15
+ ```
16
+
17
+ - Sticky at top with backdrop blur.
18
+ - Logo or product name (text logo is fine — use display font).
19
+ - 2-4 nav links that scroll to sections (anchor links).
20
+ - CTA button on the right that scrolls to waitlist section.
21
+ - Mobile: collapse to hamburger.
22
+
23
+ ### 2. Hero Section
24
+
25
+ ```
26
+ ┌─────────────────────────────────────────────────┐
27
+ │ │
28
+ │ [small eyebrow badge or label] │
29
+ │ │
30
+ │ Big Bold Headline That Sells │
31
+ │ the Benefit, Not the Feature │
32
+ │ │
33
+ │ A subheadline that elaborates in 1-2 │
34
+ │ sentences. Specific, not vague. │
35
+ │ │
36
+ │ [Primary CTA Button] [Secondary Link] │
37
+ │ │
38
+ │ ┌─────────────────────────────────┐ │
39
+ │ │ Hero visual / app preview / │ │
40
+ │ │ illustration / gradient box │ │
41
+ │ └─────────────────────────────────┘ │
42
+ │ │
43
+ └─────────────────────────────────────────────────┘
44
+ ```
45
+
46
+ - **Headline**: Benefit-driven ("Ship faster" not "Project management tool"). 5-10 words max.
47
+ - **Subheadline**: Explain what it does and for whom. 1-2 sentences.
48
+ - **CTA**: Action verb + outcome ("Join the waitlist", "Get early access", "Start free").
49
+ - **Visual**: App screenshot mockup, abstract gradient, or SVG illustration. Never leave empty.
50
+ - **Eyebrow**: Optional small badge above headline ("Now in beta", "For developers", "AI-powered").
51
+
52
+ ### 3. Social Proof Bar
53
+
54
+ ```
55
+ Trusted by 500+ early adopters
56
+ [Logo] [Logo] [Logo] [Logo] [Logo]
57
+ ```
58
+
59
+ - Single line below hero.
60
+ - Use placeholder company names/logos (styled as gray text or simple SVG shapes).
61
+ - Alternatively: "Join 500+ people on the waitlist" with a count (fake but plausible).
62
+ - Keep subtle — muted colors, small text.
63
+
64
+ ### 4. Features Grid
65
+
66
+ ```
67
+ ┌──────────┐ ┌──────────┐ ┌──────────┐
68
+ │ 🎯 Icon │ │ ⚡ Icon │ │ 🔒 Icon │
69
+ │ Title │ │ Title │ │ Title │
70
+ │ 2-line │ │ 2-line │ │ 2-line │
71
+ │ desc │ │ desc │ │ desc │
72
+ └──────────┘ └──────────┘ └──────────┘
73
+ ```
74
+
75
+ - 3-6 features in a grid (3 columns desktop, 1 mobile).
76
+ - Each card: icon/emoji + title (3-5 words) + description (1-2 sentences).
77
+ - Icons: use emoji or simple SVG. Heroicons or Lucide if the stack supports it.
78
+ - Feature text must match the key features from Step 1.
79
+
80
+ ### 5. How It Works
81
+
82
+ ```
83
+ Step 1 Step 2 Step 3
84
+ ① ② ③
85
+ Sign up Connect your See results
86
+ and set up data source in minutes
87
+ your account in one click
88
+ ```
89
+
90
+ - 3 steps (rarely 4). Numbered or with icons.
91
+ - Each step: number/icon + title + 1-sentence description.
92
+ - Optional: connecting line or arrow between steps.
93
+ - Explains the user journey from signup to value.
94
+
95
+ ### 6. Pricing
96
+
97
+ ```
98
+ ┌──────────┐ ┌──────────────┐ ┌──────────┐
99
+ │ Free │ │ Pro ⭐ │ │Enterprise│
100
+ │ $0/mo │ │ $29/mo │ │ Custom │
101
+ │ │ │ │ │ │
102
+ │ • 3 feat│ │ • All free │ │ • All │
103
+ │ • Basic │ │ • 5 more │ │ • Custom│
104
+ │ │ │ • Priority │ │ • SLA │
105
+ │ [Start] │ │ [Get Pro] │ │ [Contact]│
106
+ └──────────┘ └──────────────┘ └──────────┘
107
+ ```
108
+
109
+ - 2-3 tiers. Middle tier highlighted (border, scale, badge).
110
+ - Prices should be fake but realistic for the product type.
111
+ - Each tier: name, price, feature list (5-7 items), CTA button.
112
+ - Free tier CTA → waitlist. Paid tier CTAs → waitlist (it's an MVP).
113
+ - All buttons route to the waitlist since nothing is real yet.
114
+
115
+ ### 7. Testimonials
116
+
117
+ ```
118
+ ┌──────────────────────────────────┐
119
+ │ "This changed how I work..." │
120
+ │ │
121
+ │ [Avatar] Jane Smith │
122
+ │ CTO, TechCo │
123
+ └──────────────────────────────────┘
124
+ ```
125
+
126
+ - 2-3 testimonial cards. Carousel or grid.
127
+ - Each: quote (1-3 sentences), name, role/company, avatar placeholder.
128
+ - Generate realistic-sounding quotes that align with the product's value prop.
129
+ - Avatars: use gradient circles with initials, or `ui-avatars.com` service.
130
+ - Mark clearly in code comments that these are placeholder testimonials.
131
+
132
+ ### 8. Waitlist / CTA Section
133
+
134
+ ```
135
+ ┌─────────────────────────────────────────────────┐
136
+ │ │
137
+ │ Ready to try {Product}? │
138
+ │ Join the waitlist for early access. │
139
+ │ │
140
+ │ [email@example.com ] [Join →] │
141
+ │ │
142
+ │ ✓ No spam. We'll only email you when │
143
+ │ we launch. │
144
+ │ │
145
+ └─────────────────────────────────────────────────┘
146
+ ```
147
+
148
+ - Prominent section near bottom (but before footer).
149
+ - Headline: compelling CTA ("Ready to X?", "Be the first to try").
150
+ - Email input + submit button on one line (desktop), stacked (mobile).
151
+ - Trust line below: "No spam" or "Join X others".
152
+ - States:
153
+ - **Default**: input + button enabled.
154
+ - **Loading**: button shows spinner, input disabled.
155
+ - **Success**: replace form with "You're on the list! We'll notify you at {email}."
156
+ - **Error**: show error message below input (invalid email, server error).
157
+
158
+ ### 9. Footer
159
+
160
+ ```
161
+ {Product Name} Features | Pricing | Waitlist
162
+ Built with ♥ © 2026 {Product}. All rights reserved.
163
+ ```
164
+
165
+ - Simple. Product name, nav links (repeat from navbar), copyright.
166
+ - Optional: social links (use # placeholders).
167
+ - Dark or muted background to separate from content.
168
+
169
+ ---
170
+
171
+ ## Waitlist Backend Patterns
172
+
173
+ ### Backend Stacks (Next.js, Nuxt, Laravel)
174
+
175
+ **API Route Pattern:**
176
+
177
+ ```
178
+ POST /api/waitlist
179
+ Body: { "email": "user@example.com" }
180
+
181
+ → Validate email format (regex or built-in validator)
182
+ → Read waitlist.json from disk (create if doesn't exist)
183
+ → Check for duplicate email
184
+ → Append { email, timestamp, source: "landing-page" }
185
+ → Write back to waitlist.json
186
+ → Return { success: true, message: "You're on the list!" }
187
+
188
+ Errors:
189
+ → Invalid email: 400 { success: false, message: "Please enter a valid email." }
190
+ → Duplicate: 200 { success: true, message: "You're already on the list!" }
191
+ → Server error: 500 { success: false, message: "Something went wrong. Try again." }
192
+ ```
193
+
194
+ **waitlist.json format:**
195
+ ```json
196
+ {
197
+ "entries": [
198
+ {
199
+ "email": "user@example.com",
200
+ "timestamp": "2026-03-18T10:30:00Z",
201
+ "source": "landing-page"
202
+ }
203
+ ]
204
+ }
205
+ ```
206
+
207
+ The waitlist.json file should be in a non-public location:
208
+ - Next.js: project root `./waitlist.json` (outside `public/`)
209
+ - Nuxt: project root `./waitlist.json`
210
+ - Laravel: `storage/app/waitlist.json`
211
+
212
+ ### Static Stacks (React + Vite)
213
+
214
+ **Formspree Pattern:**
215
+
216
+ ```html
217
+ <form action="https://formspree.io/f/{your-form-id}" method="POST">
218
+ <input type="email" name="email" required />
219
+ <button type="submit">Join Waitlist</button>
220
+ </form>
221
+ ```
222
+
223
+ - Handle submission via JavaScript fetch for better UX (show loading/success states).
224
+ - Add a code comment: `// Replace {your-form-id} with your Formspree form ID — create one free at formspree.io`
225
+ - Handle Formspree's response format for success/error states.
226
+
227
+ ---
228
+
229
+ ## Copywriting Guidelines
230
+
231
+ - **Headlines**: Lead with the benefit, not the feature. "Save 10 hours a week" > "Task management tool".
232
+ - **Subheadlines**: Be specific about who and what. "For freelancers who juggle too many clients" > "For everyone".
233
+ - **CTAs**: Action verb + outcome. "Get early access" > "Submit". "Join the waitlist" > "Sign up".
234
+ - **Feature descriptions**: Problem → solution format. "Stop losing track of invoices. Auto-track every payment in real time."
235
+ - **Tone**: Match the product. B2B SaaS = professional but warm. Dev tools = casual and direct. Consumer = friendly and energetic.
236
+ - **Never use**: "Revolutionize", "leverage", "synergy", "disrupt", "cutting-edge" (overused startup jargon).
@@ -0,0 +1,321 @@
1
+ # Laravel + Blade + Tailwind — Stack Reference
2
+
3
+ ## Scaffold
4
+
5
+ ```bash
6
+ composer create-project laravel/laravel {project-name}
7
+ cd {project-name}
8
+ npm install
9
+ ```
10
+
11
+ Laravel ships with Tailwind and Vite out of the box (Laravel 11+).
12
+
13
+ ## Directory Structure
14
+
15
+ ```
16
+ {project-name}/
17
+ ├── resources/
18
+ │ ├── views/
19
+ │ │ ├── layouts/
20
+ │ │ │ ├── landing.blade.php ← landing page layout
21
+ │ │ │ └── app.blade.php ← app layout (sidebar)
22
+ │ │ ├── components/
23
+ │ │ │ ├── landing/
24
+ │ │ │ │ ├── navbar.blade.php
25
+ │ │ │ │ ├── hero.blade.php
26
+ │ │ │ │ ├── features.blade.php
27
+ │ │ │ │ ├── how-it-works.blade.php
28
+ │ │ │ │ ├── pricing.blade.php
29
+ │ │ │ │ ├── testimonials.blade.php
30
+ │ │ │ │ ├── waitlist-form.blade.php
31
+ │ │ │ │ └── footer.blade.php
32
+ │ │ │ ├── app/
33
+ │ │ │ │ ├── sidebar.blade.php
34
+ │ │ │ │ └── {feature components}
35
+ │ │ │ └── ui/
36
+ │ │ │ ├── button.blade.php
37
+ │ │ │ ├── input.blade.php
38
+ │ │ │ ├── card.blade.php
39
+ │ │ │ └── modal.blade.php
40
+ │ │ ├── landing.blade.php ← landing page
41
+ │ │ ├── dashboard.blade.php ← dashboard
42
+ │ │ ├── {feature-1}.blade.php
43
+ │ │ ├── {feature-2}.blade.php
44
+ │ │ └── settings.blade.php
45
+ │ ├── css/
46
+ │ │ └── app.css ← Tailwind directives + custom vars
47
+ │ └── js/
48
+ │ └── app.js ← Vite entry + Alpine.js for interactivity
49
+ ├── routes/
50
+ │ └── web.php ← all routes
51
+ ├── app/
52
+ │ └── Http/
53
+ │ └── Controllers/
54
+ │ └── WaitlistController.php
55
+ ├── storage/
56
+ │ └── app/
57
+ │ └── waitlist.json ← email storage (auto-created)
58
+ ├── public/
59
+ │ └── {compiled assets}
60
+ ├── tailwind.config.js
61
+ ├── vite.config.js
62
+ └── package.json
63
+ ```
64
+
65
+ ## Routes
66
+
67
+ `routes/web.php`:
68
+
69
+ ```php
70
+ use App\Http\Controllers\WaitlistController;
71
+
72
+ // Landing page
73
+ Route::view('/', 'landing');
74
+
75
+ // App pages
76
+ Route::view('/dashboard', 'dashboard');
77
+ Route::view('/{feature-1}', '{feature-1}');
78
+ Route::view('/{feature-2}', '{feature-2}');
79
+ Route::view('/settings', 'settings');
80
+
81
+ // Waitlist API
82
+ Route::post('/api/waitlist', [WaitlistController::class, 'store']);
83
+ ```
84
+
85
+ ## Layouts
86
+
87
+ ### Landing Layout (`resources/views/layouts/landing.blade.php`)
88
+
89
+ ```blade
90
+ <!DOCTYPE html>
91
+ <html lang="en">
92
+ <head>
93
+ <meta charset="UTF-8">
94
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
95
+ <title>{{ $title ?? '{Product Name}' }}</title>
96
+ <link rel="preconnect" href="https://fonts.googleapis.com">
97
+ <link href="https://fonts.googleapis.com/css2?family={DisplayFont}:wght@400;600;700;800&family={BodyFont}:wght@400;500;600&display=swap" rel="stylesheet">
98
+ @vite(['resources/css/app.css', 'resources/js/app.js'])
99
+ </head>
100
+ <body class="bg-bg text-fg font-body antialiased">
101
+ {{ $slot }}
102
+ </body>
103
+ </html>
104
+ ```
105
+
106
+ ### App Layout (`resources/views/layouts/app.blade.php`)
107
+
108
+ ```blade
109
+ <!DOCTYPE html>
110
+ <html lang="en">
111
+ <head>
112
+ <meta charset="UTF-8">
113
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
114
+ <title>{{ $title ?? '{Product Name}' }}</title>
115
+ <link rel="preconnect" href="https://fonts.googleapis.com">
116
+ <link href="https://fonts.googleapis.com/css2?family={DisplayFont}:wght@400;600;700;800&family={BodyFont}:wght@400;500;600&display=swap" rel="stylesheet">
117
+ @vite(['resources/css/app.css', 'resources/js/app.js'])
118
+ </head>
119
+ <body class="bg-bg text-fg font-body antialiased">
120
+ <div class="flex min-h-screen">
121
+ <x-app.sidebar />
122
+ <main class="flex-1 p-6 lg:p-8">
123
+ {{ $slot }}
124
+ </main>
125
+ </div>
126
+ </body>
127
+ </html>
128
+ ```
129
+
130
+ ## Tailwind Config
131
+
132
+ `tailwind.config.js`:
133
+
134
+ ```js
135
+ export default {
136
+ content: [
137
+ './resources/**/*.blade.php',
138
+ './resources/**/*.js',
139
+ ],
140
+ theme: {
141
+ extend: {
142
+ colors: {
143
+ bg: 'var(--color-bg)',
144
+ fg: 'var(--color-fg)',
145
+ accent: 'var(--color-accent)',
146
+ muted: 'var(--color-muted)',
147
+ },
148
+ fontFamily: {
149
+ display: ['{DisplayFont}', 'serif'],
150
+ body: ['{BodyFont}', 'sans-serif'],
151
+ },
152
+ },
153
+ },
154
+ }
155
+ ```
156
+
157
+ CSS variables in `resources/css/app.css`:
158
+
159
+ ```css
160
+ @tailwind base;
161
+ @tailwind components;
162
+ @tailwind utilities;
163
+
164
+ :root {
165
+ --color-bg: #xxxxxx;
166
+ --color-fg: #xxxxxx;
167
+ --color-accent: #xxxxxx;
168
+ --color-muted: #xxxxxx;
169
+ }
170
+ ```
171
+
172
+ ## Interactivity with Alpine.js
173
+
174
+ Install Alpine.js for lightweight interactivity (modals, toasts, form states):
175
+
176
+ ```bash
177
+ npm install alpinejs
178
+ ```
179
+
180
+ `resources/js/app.js`:
181
+
182
+ ```js
183
+ import Alpine from 'alpinejs'
184
+ window.Alpine = Alpine
185
+ Alpine.start()
186
+ ```
187
+
188
+ ## Waitlist Controller
189
+
190
+ `app/Http/Controllers/WaitlistController.php`:
191
+
192
+ ```php
193
+ <?php
194
+
195
+ namespace App\Http\Controllers;
196
+
197
+ use Illuminate\Http\Request;
198
+ use Illuminate\Support\Facades\Storage;
199
+
200
+ class WaitlistController extends Controller
201
+ {
202
+ public function store(Request $request)
203
+ {
204
+ $request->validate([
205
+ 'email' => 'required|email',
206
+ ]);
207
+
208
+ $email = $request->input('email');
209
+ $path = 'waitlist.json';
210
+
211
+ // Read or create
212
+ $data = Storage::exists($path)
213
+ ? json_decode(Storage::get($path), true)
214
+ : ['entries' => []];
215
+
216
+ // Check duplicate
217
+ $exists = collect($data['entries'])->contains('email', $email);
218
+ if ($exists) {
219
+ return response()->json(['success' => true, 'message' => "You're already on the list!"]);
220
+ }
221
+
222
+ // Append
223
+ $data['entries'][] = [
224
+ 'email' => $email,
225
+ 'timestamp' => now()->toISOString(),
226
+ 'source' => 'landing-page',
227
+ ];
228
+
229
+ Storage::put($path, json_encode($data, JSON_PRETTY_PRINT));
230
+
231
+ return response()->json(['success' => true, 'message' => "You're on the list!"]);
232
+ }
233
+ }
234
+ ```
235
+
236
+ ## Blade Component Patterns
237
+
238
+ ### Anonymous Components (preferred for UI)
239
+
240
+ `resources/views/components/ui/button.blade.php`:
241
+
242
+ ```blade
243
+ @props(['variant' => 'primary', 'size' => 'md'])
244
+
245
+ @php
246
+ $classes = match($variant) {
247
+ 'primary' => 'bg-accent text-white hover:opacity-90',
248
+ 'secondary' => 'border border-accent text-accent hover:bg-accent/10',
249
+ 'ghost' => 'text-fg hover:bg-muted/20',
250
+ };
251
+ $sizes = match($size) {
252
+ 'sm' => 'px-4 py-2 text-sm',
253
+ 'md' => 'px-6 py-3 text-base',
254
+ 'lg' => 'px-8 py-4 text-lg',
255
+ };
256
+ @endphp
257
+
258
+ <button {{ $attributes->merge(['class' => "$classes $sizes rounded-xl font-medium transition-all duration-200"]) }}>
259
+ {{ $slot }}
260
+ </button>
261
+ ```
262
+
263
+ Usage: `<x-ui.button variant="primary">Join Waitlist</x-ui.button>`
264
+
265
+ ### Landing Page
266
+
267
+ `resources/views/landing.blade.php`:
268
+
269
+ ```blade
270
+ <x-layouts.landing>
271
+ <x-landing.navbar />
272
+ <x-landing.hero />
273
+ <x-landing.features />
274
+ <x-landing.how-it-works />
275
+ <x-landing.pricing />
276
+ <x-landing.testimonials />
277
+ <x-landing.waitlist-form />
278
+ <x-landing.footer />
279
+ </x-layouts.landing>
280
+ ```
281
+
282
+ ### Waitlist Form with Alpine.js
283
+
284
+ ```blade
285
+ <div x-data="{ email: '', status: 'idle', message: '' }">
286
+ <form @submit.prevent="
287
+ status = 'loading';
288
+ fetch('/api/waitlist', {
289
+ method: 'POST',
290
+ headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
291
+ body: JSON.stringify({ email })
292
+ })
293
+ .then(r => r.json())
294
+ .then(d => { status = 'success'; message = d.message; })
295
+ .catch(() => { status = 'error'; message = 'Something went wrong.'; })
296
+ ">
297
+ <template x-if="status !== 'success'">
298
+ <div class="flex gap-3">
299
+ <input x-model="email" type="email" placeholder="you@example.com" required
300
+ class="flex-1 px-4 py-3 rounded-lg border focus:ring-2 focus:ring-accent" />
301
+ <x-ui.button type="submit" x-bind:disabled="status === 'loading'">
302
+ <span x-show="status !== 'loading'">Join Waitlist</span>
303
+ <span x-show="status === 'loading'">Joining...</span>
304
+ </x-ui.button>
305
+ </div>
306
+ </template>
307
+ <p x-show="status === 'success'" x-text="message" class="text-green-600 font-medium"></p>
308
+ <p x-show="status === 'error'" x-text="message" class="text-red-500 text-sm mt-2"></p>
309
+ </form>
310
+ </div>
311
+ ```
312
+
313
+ ## Dev Server
314
+
315
+ ```bash
316
+ # Run both in separate terminals (or use Concurrently)
317
+ php artisan serve # http://localhost:8000
318
+ npm run dev # Vite dev server for assets
319
+ ```
320
+
321
+ Or use Laravel Herd if available (auto-serves at `{project-name}.test`).