- Fix homepage heading hierarchy (h2 -> h1) - Add page-specific metadata to services and about pages - Fix outline-none on form inputs for keyboard accessibility - Delete sync conflict files - Add group class back to bento cards for hover animations - Use raw CSS for glassmorphism to fix Tailwind opacity errors - Add TODO.md with production checklist - Add semantic CSS classes (.section-container, .glass-card) - Update Tailwind config for opacity modifier support
3.8 KiB
Such Software - Production Checklist
🔴 Before Production Deployment
1. Cloudflare Turnstile Configuration
The contact form uses Cloudflare Turnstile for bot protection. You need to set up the secret key for production:
- Go to Cloudflare Dashboard → Turnstile
- Create a new site or use existing widget
- Copy the Site Key (public) and Secret Key (private)
- Set environment variables:
# In your production environment (Vercel, etc.)
NEXT_PUBLIC_TURNSTILE_SITE_KEY=your_site_key_here
TURNSTILE_SECRET_KEY=your_secret_key_here
⚠️ Warning: The current implementation returns true if no secret key is set. This is fine for development but must be configured in production.
See: apps/web/src/lib/turnstile.ts
🟡 Medium Priority Improvements
2. Extract Shared Header Component
The header is duplicated across three pages:
apps/web/src/app/page.tsxapps/web/src/app/services/page.tsxapps/web/src/app/about/page.tsx
Create a shared component at apps/web/src/components/layout/header.tsx.
3. Add prefers-reduced-motion Support
The NebulaField and DynamicBackground components run animations without checking user preferences.
File: packages/ui/src/procedural/nebula-field.tsx
Add:
const prefersReducedMotion = typeof window !== 'undefined'
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
: false;
if (prefersReducedMotion) {
return <div className="absolute inset-0 bg-gradient-to-b from-slate-100 to-slate-200 dark:from-slate-900 dark:to-slate-800" />;
}
4. Add Skip-to-Content Link
For keyboard accessibility, add a skip link in apps/web/src/app/layout.tsx:
<a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-primary focus:text-primary-foreground focus:rounded-lg">
Skip to main content
</a>
5. Add aria-hidden to Decorative Icons
In apps/web/src/app/about/page.tsx, add aria-hidden="true" to decorative icons:
<FaMapMarkerAlt aria-hidden="true" />
<FaPhone aria-hidden="true" />
<FaBuilding aria-hidden="true" />
6. Type Server Action Properly
In apps/web/src/actions/submit-contact.ts, the prevState parameter uses any. Update to:
export async function submitContactForm(
prevState: { success: boolean; message: string; errors?: Record<string, string> },
formData: FormData
): Promise<{ success: boolean; message: string; errors?: Record<string, string> }>
7. Rate Limiting
Consider adding rate limiting to the contact form submission to prevent abuse. Options: Upstash Ratelimit, Vercel Edge Config, or Cloudflare Workers.
🟢 Nice-to-Have
8. Add Structured Data (JSON-LD)
Add Organization schema to layout.tsx for better SEO:
<script type="application/ld+json">
{JSON.stringify({
"@context": "https://schema.org",
"@type": "Organization",
"name": "Such Software LLC",
"url": "https://such.software",
"address": {
"@type": "PostalAddress",
"streetAddress": "110 E State St, Suite 300",
"addressLocality": "Kennett Square",
"addressRegion": "PA",
"postalCode": "19348",
"addressCountry": "US"
}
})}
</script>
9. Add Canonical URLs
Add canonical meta tags to prevent duplicate content issues.
10. Remove Console Logs
Remove console.log statements before production:
apps/web/src/components/contact/turnstile-widget.tsx
✅ Recently Completed
- Deleted sync conflict files
- Fixed homepage
<h1>heading hierarchy - Added page metadata to services and about pages
- Fixed
outline-noneaccessibility issue on form inputs - Established semantic CSS architecture (
.section-container,.glass-card)