Zodiac Compatibility #
A private astrology compatibility website for sharing your zodiac signs and compatibility report with friends — no accounts, no tracking, no data storage.
Deployed at: zodiac.psingletary.com
How it works #
- You share the link with someone (in person or via SMS).
- They enter their birthday (and optionally their name) and instantly see:
- Their Western zodiac sign (Sun sign + element, modality, ruling planet)
- Their Chinese zodiac sign (animal + element + heavenly stem)
- A compatibility report comparing them to a fixed admin profile
- They tap "Text this chart to [admin]" which opens their own phone's SMS app — no server-side SMS, no Twilio cost. The visitor sends the message themselves.
Privacy #
- No birthdays are stored anywhere. The report is computed client-side.
- Share tokens are encoded directly into the URL (base64url). No server storage, no database. Tokens expire client-side after 48 hours.
- No analytics. No cookies with PII.
- The admin's birthday lives only in env vars — never in the client bundle.
Tech Stack #
- Framework: Next.js 16 (App Router) + TypeScript
- Styling: Tailwind CSS v4
- Architecture: Fully static — zero server routes, zero API endpoints. Deployable to any static host (Tangled, wisp, Vercel, S3, etc.)
- Astrology: Pure TypeScript with baked-in Lunar New Year lookup table (no external library needed; 1900-2100 coverage)
- Share tokens: Client-side encode/decode (base64url, embedded expiry). No server storage, no database.
- Testing: Vitest (62 tests)
Project Structure #
src/
├── app/
│ ├── r/page.tsx # Static page — decodes report from ?t= query param
│ ├── layout.tsx # Root layout with metadata
│ ├── page.tsx # Landing page with birthday form
│ └── globals.css # Tailwind + base styles
├── components/
│ ├── BirthdayForm.tsx # Form with validation + computation
│ ├── CompatibilityReport.tsx # Full report display + share section
│ └── SmsLink.tsx # sms: URI builder + copy fallback
├── lib/
│ ├── astrology/
│ │ ├── western.ts # Western zodiac computation
│ │ ├── chinese.ts # Chinese zodiac (Lunar New Year table)
│ │ ├── index.ts # Barrel export
│ │ └── __tests__/ # 47 tests
│ ├── compatibility/
│ │ ├── data.ts # All sign profiles + compatibility ratings
│ │ ├── engine.ts # Report builder
│ │ └── __tests__/ # 8 tests
│ ├── config/
│ │ └── admin.ts # Admin profile from env vars
│ └── share/
│ ├── tokens.ts # Client-side token encode/decode
│ └── __tests__/ # 7 tests
└── .hermes/plans/ # Implementation plan
Getting Started #
Prerequisites #
- Node.js 20+
- npm
Local Development #
# Install dependencies
npm install
# Copy and configure environment
cp .env.example .env.local
# Edit .env.local with your phone number and nickname:
# ADMIN_PHONE="+1XXXXXXXXXX"
# ADMIN_NICKNAME="YourName"
# Run dev server
npm run dev
# Opens at http://localhost:3000
Run Tests #
npm test # Single run (62 tests)
npm run test:watch # Watch mode
Build #
npm run build # Production build
npm start # Start production server
Environment Variables #
| Variable | Required | Default | Description |
|---|---|---|---|
ADMIN_PHONE |
Yes | — | E.164 phone number for SMS sharing |
ADMIN_NICKNAME |
No | "me" |
Display name for the share button |
SHARE_TOKEN_TTL_SECONDS |
No | 172800 (48h) |
How long share links stay valid |
Admin Profile #
The admin (the person receiving the SMS) is Fire Dragon Virgo (Yang Fire /
Bing). The admin's actual birthday is configured via ADMIN_BIRTHDAY env var
(if present) and is never exposed to the client bundle.
To change the admin's astrology profile, edit the compatibility data in
src/lib/compatibility/data.ts. The current profile is:
- Western: Virgo (Earth, Mutable, Mercury)
- Chinese: Fire Dragon (Yang Fire, 丙 / Bing)
- Lucky numbers: 1, 6, 7
- Lucky colors: Gold, Silver, Gray
SMS Sharing #
The sharing mechanism uses the sms: URI scheme, not a server-side SMS
provider:
- Visitor views their report on the site
- They tap "Text this chart to [admin]" which generates a share link
- On mobile, the
sms:URI opens their native SMS app pre-filled with the link - On desktop, a "Copy link" button + admin phone number is shown instead
The sms: URI uses & separator for iOS and ? for Android (auto-detected
via user agent).
Deployment #
Fully static. Deploy to any static host. See DEPLOY.md for detailed instructions (Tangled, wisp, Vercel, Netlify, S3, etc.).
License #
Private project — all rights reserved.