A modern service business website built with React Router v7 and deployed on Cloudflare Workers. Features a fully responsive design with dark mode support, progressive form enhancement, and comprehensive security measures.
- Framework: React Router v7 (migrated from Remix v2)
- Runtime: Cloudflare Workers (Web APIs, not Node.js)
- Styling: Tailwind CSS v4 with centralized style architecture
- Forms: Progressive enhancement with CSRF protection
- Email: Brevo API integration
- Deployment: Cloudflare Workers
- Type Safety: TypeScript with Cloudflare Workers types
- Service Pages: Computer Repair, Website Development, Cross-Platform Apps, Family Support
- Contact Form: CSRF-protected with Zod validation and HTML email templates
- Security: CSP headers, HSTS, CORS configuration via
public/_headers - Dark Mode: Comprehensive dark mode support across all components
- Responsive: Mobile-first design with smooth animations
- SEO: Meta tags and semantic HTML structure
- Progressive Enhancement: Forms work without JavaScript
- File-based routing via
@react-router/fs-routes - Routes configured in
app/routes.ts - Server functions (loaders/actions) for data fetching and mutations
- Centralized styles: Style objects in
app/styles/directory - Pattern: Import styles, use
className={styles.property} - Categories:
navigation,cards,form,footer,contactStyles - Custom animations:
draw-arrow,marquee, defined intailwind.config.ts
- CSRF Protection: Token generation and validation in
app/utils/csrf.server.ts - Validation: Zod schemas for server-side form validation
- Email: Brevo API with HTML templates in
app/utils/email.server.ts
- Access via
context.cloudflare.env.VARIABLE_NAMEin loaders/actions - Type-safe with extended
Envinterface inload-context.ts - Required:
CSRF_SECRET,BREVO_API_KEY,FROM_EMAIL,TO_EMAIL
node >= 22.12.0
pnpm >= 9.0.0# Install dependencies
pnpm install
# Generate Cloudflare types
pnpm run cf-typegen
# Start development server
pnpm run dev
# or
pnpm devThe dev server will start at http://localhost:5173
pnpm run dev # Start Vite dev server (or: pnpm dev)
pnpm run build # Build for production
pnpm run preview # Preview production build locally
pnpm run deploy # Deploy to Cloudflare Workers
pnpm run typecheck # Run TypeScript validation
pnpm run cf-typegen # Generate Cloudflare Workers typesapp/
βββ routes/ # Page routes
β βββ _index.tsx # Homepage
β βββ contact.tsx # Contact form with CSRF
β βββ *.tsx # Service pages
βββ components/ # React components
β βββ Header.tsx # Navigation with scroll detection
β βββ Footer.tsx # Footer with links
β βββ ContactForm.tsx # Reusable contact form
βββ styles/ # Centralized Tailwind styles
β βββ components.ts # Component-specific styles
β βββ layout.ts # Layout styles
β βββ sections.ts # Section styles
βββ utils/ # Server utilities
β βββ csrf.server.ts # CSRF token handling
β βββ email.server.ts # Brevo email integration
β βββ token.server.ts # Token utilities
βββ hooks/ # Custom React hooks
βββ root.tsx # Root layout
workers/
βββ app.ts # Cloudflare Workers entry point
public/
βββ _headers # Security headers configuration
βββ assets/ # Static assets (images, logos)
- CSP: Content Security Policy configured in
public/_headers - CSRF: Token-based protection on all forms
- HSTS: Strict-Transport-Security for HTTPS enforcement
- Secure Cookies: HttpOnly, Secure, SameSite=Strict
- Input Validation: Zod schemas on server-side
- Configure Wrangler: Update
wrangler.jsoncwith your route and settings - Environment Variables: Set secrets via Wrangler CLI:
wrangler secret put CSRF_SECRET wrangler secret put BREVO_API_KEY
- Deploy: Push to main branch (CI) or run manually:
pnpm run deploy
pnpm run build
pnpm run deployAll styles are organized in app/styles/ for consistency and maintainability:
components.ts: Navigation, cards, forms, footer, contact page styleslayout.ts: Layout containers, headers, sectionssections.ts: Hero, features, services, CTA, pricing components
import { contactStyles as styles } from "~/styles/components";
export default function Component() {
return <div className={styles.wrapper}>
<h1 className={styles.title}>Title</h1>
</div>;
}All interactive elements include proper focus states:
// β
Good: Full accessibility support
button: "... focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ..."
// Navigation links with keyboard support
navLink: "... focus-visible:ring-2 focus-visible:ring-offset-2 ..."
// Form inputs with visible focus
input: "... focus:ring-4 focus:ring-blue-500/20 focus-visible:outline-none ..."Implemented:
- β
focus-visiblestates on all interactive elements - β
focus-withinfor card keyboard navigation - β Ring offsets for better contrast
- β Disabled states with proper cursors
- β WCAG 2.1 AA compliant
Respects user's motion preferences with prefers-reduced-motion:
// β
Good: Motion-safe animations
card: "... hover:scale-105 motion-reduce:transition-none motion-reduce:hover:scale-100"
button: "... transition-all duration-300 motion-reduce:transition-none"Applied to:
- Hover scale effects
- Vertical translations
- All transform animations
- Smooth scrolling behaviors
Consistent pattern across all interactive elements:
// Button states: default β hover β active β disabled
button: "bg-blue-600 hover:bg-blue-700 active:bg-blue-800 disabled:opacity-50 disabled:cursor-not-allowed"
// Link states with proper transitions
link: "transition-colors duration-200 hover:text-blue-600"Includes:
- β Hover states (lighter/darker colors)
- β Active states (pressed appearance)
- β Focus-visible states (keyboard navigation)
- β Disabled states (visual + functional)
Standardized durations for consistency:
// Simple changes (colors, opacity): 200ms
"transition-colors duration-200"
// Complex animations (scale, transforms, shadows): 300ms
"transition-all duration-300"All styles include dark mode variants:
// β
Good: Comprehensive dark mode support
wrapper: "bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
border: "border-gray-200 dark:border-gray-700"
shadow: "shadow-lg dark:shadow-gray-900/50"Coverage:
- β All text colors
- β All background colors
- β All border colors
- β Shadow adaptations
- β Focus ring offsets
app/styles/
βββ components.ts # UI components (nav, cards, forms, footer)
βββ layout.ts # Page layouts and containers
βββ sections.ts # Page sections (hero, features, CTA)
When to use each:
- components.ts: Reusable UI components used across multiple pages
- layout.ts: Page structure and layout containers
- sections.ts: Specific page sections and templates
- Inline styles: Page-unique styles (e.g.,
_index.tsxhero badge)
import { type ActionFunctionArgs, type LoaderFunctionArgs } from "react-router";
import { createCsrfToken, validateCsrfToken } from "~/utils/csrf.server";
export async function loader({ request, context }: LoaderFunctionArgs) {
const { token, cookieHeader } = await createCsrfToken(
request,
context.cloudflare.env.CSRF_SECRET
);
return Response.json({ csrfToken: token }, {
headers: { "Set-Cookie": cookieHeader }
});
}
export async function action({ request, context }: ActionFunctionArgs) {
const formData = await request.formData();
await validateCsrfToken(
request,
formData.get("csrfToken"),
context.cloudflare.env.CSRF_SECRET
);
// Process form...
}- Runtime: Use Web APIs (
fetch,crypto,Request/Response), not Node.js APIs - Asset paths: Use
/assets/for public files - Environment variables: Access via
context.cloudflare.envin server code - TypeScript: Use
@remix-run/cloudflaretypes, not@types/node
This project is private and proprietary.
This is a private project. Contact the owner for contribution guidelines.