Website Architecture
Technical analysis of the current A-Grade Services app structure and risks.
Website Architecture
apps/web is a Next.js App Router application styled with Tailwind v4 and animated with Framer Motion. The repo is now a Bun workspace with the website in apps/web, docs in apps/docs, and framework-neutral shared code in packages/shared.
Route Map
| Route | File | Render model | Main notes |
|---|---|---|---|
/ | apps/web/app/page.tsx | Server component with client carousel | Sticky video hero, carousel, service/process/metric sections |
/about | apps/web/app/about/page.tsx | Async server component | Calls getPexelsImages, renders stats/story/values |
/services | apps/web/app/services/page.tsx | Async server component | Calls getPexelsImages, renders six service cards |
/team | apps/web/app/team/page.tsx | Async server component | Calls getPexelsImages, renders placeholder team cards |
/contact | apps/web/app/contact/page.tsx | Async server component | Calls getPexelsImages, renders contact details and presentational form |
Shared Layout
apps/web/app/layout.tsx renders:
NavBar- route content
Footer
The layout also loads Google Roboto through next/font/google and sets global metadata.
Navigation System
components/layout/NavBar.tsx is a client component. It owns:
- The fixed top nav.
- Overlay menu open/close state.
- Body scroll locking while the overlay is open.
- Reduced-motion-aware overlay/nav animation timing.
components/layout/MiddleNav.tsx is also a client component. It uses Framer Motion useScroll, useSpring, and useTransform to collapse the middle route links over the first 180px of scroll. Hydrated localhost screenshots confirm this behavior works on desktop.
Current technical risk: NavBar hard-codes the shell to w-[850px] max-w-[850px], so mobile viewports overflow. The overlay menu uses a horizontal justify-between content row, which also clips the contact/social column on mobile.
Homepage Mechanics
The homepage uses a sticky hero section followed by a content wrapper with matching negative margin and top padding:
- Hero:
sticky top-0, full viewport height. - Content wrapper:
-mt-[100vh] pt-[100vh]. - First content section: full-height project carousel.
This creates the effect of the white content scrolling over the fixed video hero. Preserve this mechanic unless the design direction intentionally changes.
The hero has a technical layering issue: the black before: overlay is z-0, while the video is z-10. The overlay is therefore behind the video, not between the video and text. Current footage is dark enough in many frames, but the code does not guarantee text contrast.
Carousel
components/home/ProjectsShowcase.tsx uses Embla with autoplay:
loop: truealign: "center"- delay:
1500ms stopOnMouseEnter: true
The carousel has a polished hover label driven by motion values. It does not yet expose visible controls, pagination, slide captions, case-study links, or a reduced-motion alternative for autoplay.
Media And Dynamic Images
Homepage project and service images are local files under apps/web/public/projects.
About, Services, Team, and Contact use getPexelsImages from apps/web/lib/pexels.ts. If PEXELS_API_KEY is missing, the helper returns a fixed fallback array of remote Pexels URLs and logs a warning.
Risks:
- The fallback images are generic and sometimes off-topic.
- Services asks for six images, but the fallback array has five; the sixth service repeats the first image through
images[index] || images[0]. - Team requests portraits but can receive architecture/nature imagery.
- Query params are interpolated into a URL string instead of built with
URLSearchParams.
For launch, critical pages should prefer curated local assets or route-specific fallback sets.
Contact Form
The Contact page currently renders a form with inputs and a submit button, but it does not submit anywhere.
Missing pieces:
actionor server action/API route.- Input
nameattributes. - Visible labels.
- Validation.
- Pending/success/error states.
- Spam protection.
This is the highest-risk functional gap because it affects lead capture.
Accessibility Notes
Positive:
- The menu button has
aria-expanded,aria-controls, andaria-label. - The overlay uses
role="dialog"andaria-modal="true". - The close button has an
aria-label.
Needs work:
- Overlay focus is not trapped.
- Escape-to-close behavior is not implemented.
- Form controls rely on placeholders instead of labels.
- Social/link-like footer content includes non-functional destinations.
- Mobile nav overflow blocks access to controls.
Performance Notes
The local hero video is 8.5M, which is reasonable for an early prototype but should still be checked on mobile networks. The strongest performance risk is less about file size and more about remote image dependencies on secondary pages.
Recommended next steps:
- Use local optimized launch imagery for the first public version.
- Add poster imagery for the hero video.
- Confirm
next/imageremote behavior in the deployment target. - Respect reduced motion for carousel autoplay.
Test Targets
Before launch, add coverage or manual checks for:
- Desktop and mobile nav visibility.
- Overlay open/close, focus movement, and Escape close.
- Contact form validation and submit states.
- Pexels fallback behavior with no API key.
- Homepage scroll states at hero, carousel, services, CTA, and footer.
- Build/typecheck for web, docs, and shared packages.