@kennethsolomon/shipkit 3.13.2 → 3.15.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.
- package/README.md +7 -6
- package/commands/sk/brainstorm.md +13 -0
- package/commands/sk/execute-plan.md +1 -0
- package/commands/sk/security-check.md +4 -0
- package/commands/sk/website.md +93 -0
- package/commands/sk/write-plan.md +38 -0
- package/package.json +1 -1
- package/skills/sk:autopilot/SKILL.md +0 -1
- package/skills/sk:fast-track/SKILL.md +0 -1
- package/skills/sk:gates/SKILL.md +4 -1
- package/skills/sk:retro/SKILL.md +0 -1
- package/skills/sk:reverse-doc/SKILL.md +0 -1
- package/skills/sk:review/SKILL.md +24 -6
- package/skills/sk:scope-check/SKILL.md +0 -1
- package/skills/sk:setup-claude/templates/commands/brainstorm.md.template +13 -0
- package/skills/sk:setup-claude/templates/commands/execute-plan.md.template +1 -0
- package/skills/sk:setup-claude/templates/commands/security-check.md.template +3 -0
- package/skills/sk:setup-claude/templates/commands/write-plan.md.template +37 -0
- package/skills/sk:start/SKILL.md +0 -1
- package/skills/sk:team/SKILL.md +0 -1
- package/skills/sk:website/SKILL.md +471 -0
- package/skills/sk:website/references/art-direction.md +210 -0
- package/skills/sk:website/references/brief-template.md +121 -0
- package/skills/sk:website/references/content-seo.md +143 -0
- package/skills/sk:website/references/handoff-template.md +261 -0
- package/skills/sk:website/references/launch-checklist.md +99 -0
- package/skills/sk:website/references/niche/accountant.md +75 -0
- package/skills/sk:website/references/niche/agency.md +75 -0
- package/skills/sk:website/references/niche/cafe.md +79 -0
- package/skills/sk:website/references/niche/dentist.md +78 -0
- package/skills/sk:website/references/niche/ecommerce.md +76 -0
- package/skills/sk:website/references/niche/gym.md +75 -0
- package/skills/sk:website/references/niche/home-services.md +76 -0
- package/skills/sk:website/references/niche/law-firm.md +75 -0
- package/skills/sk:website/references/niche/local-business.md +78 -0
- package/skills/sk:website/references/niche/med-spa.md +78 -0
- package/skills/sk:website/references/niche/portfolio.md +77 -0
- package/skills/sk:website/references/niche/real-estate.md +72 -0
- package/skills/sk:website/references/niche/restaurant.md +80 -0
- package/skills/sk:website/references/niche/saas.md +80 -0
- package/skills/sk:website/references/niche/wedding.md +80 -0
- package/skills/sk:website/references/stacks/laravel.md +425 -0
- package/skills/sk:website/references/stacks/nextjs.md +345 -0
- package/skills/sk:website/references/stacks/nuxt.md +374 -0
- package/skills/sk:website/references/whatsapp-cta.md +160 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# WhatsApp CTA Implementation Guide
|
|
2
|
+
|
|
3
|
+
Inject a floating WhatsApp button for local businesses in PH/SEA. WhatsApp is the primary contact channel for local businesses across Southeast Asia — not email.
|
|
4
|
+
|
|
5
|
+
## When to inject
|
|
6
|
+
|
|
7
|
+
See the SEA Location Detection table in SKILL.md. Default to injecting for any local business when location is unknown.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Component — Next.js + Tailwind
|
|
12
|
+
|
|
13
|
+
Create `components/whatsapp-button.tsx`:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
'use client'
|
|
17
|
+
|
|
18
|
+
interface WhatsAppButtonProps {
|
|
19
|
+
phone: string // E.164 format without +: e.g., "639171234567"
|
|
20
|
+
message?: string // Pre-filled message (URL-encoded)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function WhatsAppButton({ phone, message }: WhatsAppButtonProps) {
|
|
24
|
+
const url = message
|
|
25
|
+
? `https://wa.me/${phone}?text=${encodeURIComponent(message)}`
|
|
26
|
+
: `https://wa.me/${phone}`
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<a
|
|
30
|
+
href={url}
|
|
31
|
+
target="_blank"
|
|
32
|
+
rel="noopener noreferrer"
|
|
33
|
+
aria-label="Chat on WhatsApp"
|
|
34
|
+
className="fixed bottom-6 right-6 z-50 flex h-14 w-14 items-center justify-center rounded-full bg-[#25D366] shadow-lg transition-transform hover:scale-110 focus:outline-none focus:ring-2 focus:ring-[#25D366] focus:ring-offset-2"
|
|
35
|
+
>
|
|
36
|
+
{/* WhatsApp SVG icon */}
|
|
37
|
+
<svg
|
|
38
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
fill="white"
|
|
41
|
+
className="h-7 w-7"
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
>
|
|
44
|
+
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
|
|
45
|
+
</svg>
|
|
46
|
+
</a>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Usage in layout
|
|
54
|
+
|
|
55
|
+
Add to `app/layout.tsx` (or the root layout):
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { WhatsAppButton } from '@/components/whatsapp-button'
|
|
59
|
+
|
|
60
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
61
|
+
return (
|
|
62
|
+
<html lang="en">
|
|
63
|
+
<body>
|
|
64
|
+
{children}
|
|
65
|
+
<WhatsAppButton
|
|
66
|
+
phone="639171234567"
|
|
67
|
+
message="Hi! I found you on your website."
|
|
68
|
+
/>
|
|
69
|
+
</body>
|
|
70
|
+
</html>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Phone number format
|
|
78
|
+
|
|
79
|
+
WhatsApp uses E.164 format **without the `+`**:
|
|
80
|
+
- Philippines: `63` + 10-digit mobile number → `639171234567`
|
|
81
|
+
- Singapore: `65` + 8-digit number → `6591234567`
|
|
82
|
+
- Malaysia: `60` + mobile number → `60121234567`
|
|
83
|
+
- Indonesia: `62` + mobile number → `628111234567`
|
|
84
|
+
|
|
85
|
+
If phone number is unknown, use `+[PHONE]` as placeholder in the config and document it clearly in `HANDOFF.md`:
|
|
86
|
+
```tsx
|
|
87
|
+
<WhatsAppButton phone="[REPLACE_WITH_PHONE_WITHOUT_PLUS]" />
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Pre-filled message by business type
|
|
93
|
+
|
|
94
|
+
Set a context-aware default message so the client knows where the inquiry came from:
|
|
95
|
+
|
|
96
|
+
| Business type | Pre-filled message |
|
|
97
|
+
|---|---|
|
|
98
|
+
| Cafe / restaurant | "Hi! I found you on your website. I'd like to inquire about a reservation." |
|
|
99
|
+
| Service business | "Hi! I found you on your website. I'd like to get a quote." |
|
|
100
|
+
| Medical / dental | "Hi! I found you on your website. I'd like to book an appointment." |
|
|
101
|
+
| Fitness / gym | "Hi! I found you on your website. I'd like to know more about your classes." |
|
|
102
|
+
| Generic | "Hi! I found you on your website." |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Messenger alternative (Philippines-heavy markets)
|
|
107
|
+
|
|
108
|
+
For Philippine businesses where Facebook Messenger is more common than WhatsApp:
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
// Messenger variant — use when brief mentions Facebook or when user prefers Messenger
|
|
112
|
+
'use client'
|
|
113
|
+
|
|
114
|
+
export function MessengerButton({ pageId }: { pageId: string }) {
|
|
115
|
+
return (
|
|
116
|
+
<a
|
|
117
|
+
href={`https://m.me/${pageId}`}
|
|
118
|
+
target="_blank"
|
|
119
|
+
rel="noopener noreferrer"
|
|
120
|
+
aria-label="Message us on Messenger"
|
|
121
|
+
className="fixed bottom-6 right-6 z-50 flex h-14 w-14 items-center justify-center rounded-full bg-[#0084FF] shadow-lg transition-transform hover:scale-110 focus:outline-none focus:ring-2 focus:ring-[#0084FF] focus:ring-offset-2"
|
|
122
|
+
>
|
|
123
|
+
<svg
|
|
124
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
125
|
+
viewBox="0 0 24 24"
|
|
126
|
+
fill="white"
|
|
127
|
+
className="h-7 w-7"
|
|
128
|
+
aria-hidden="true"
|
|
129
|
+
>
|
|
130
|
+
<path d="M12 0C5.373 0 0 4.974 0 11.111c0 3.498 1.744 6.614 4.469 8.654V24l4.088-2.242c1.092.3 2.246.464 3.443.464 6.627 0 12-4.975 12-11.111S18.627 0 12 0zm1.191 14.963l-3.055-3.26-5.963 3.26L10.732 8l3.131 3.26L19.752 8l-6.561 6.963z"/>
|
|
131
|
+
</svg>
|
|
132
|
+
</a>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Both WhatsApp + Messenger (stacked)
|
|
140
|
+
|
|
141
|
+
If the client wants both options:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// Stack them vertically with a gap
|
|
145
|
+
<div className="fixed bottom-6 right-6 z-50 flex flex-col gap-3">
|
|
146
|
+
<MessengerButton pageId="your.page.name" />
|
|
147
|
+
<WhatsAppButton phone="639171234567" />
|
|
148
|
+
</div>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Adjust `bottom-6` / `right-6` to match the site's spacing.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Accessibility notes
|
|
156
|
+
|
|
157
|
+
- Always include `aria-label` on the button
|
|
158
|
+
- The SVG should have `aria-hidden="true"` (label is on the link)
|
|
159
|
+
- Ensure the button passes 3:1 contrast against the page background
|
|
160
|
+
- Fixed position — verify it doesn't cover critical page content on mobile (375px)
|