From 3f00dd6b28dc41c89fe048ea2b4353b3cb875f4f Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Mon, 25 May 2026 15:12:32 -0400 Subject: [PATCH] feat: add landing page Features, How It Works, and CTA sections - HowItWorksSection: 3-step staggered timeline with gradient circles - FeaturesGridSection: 6-card responsive grid (DarkWatch, VoicePrint, SpamShield, HomeTitle, RemoveBrokers, Family Plans) - ForUsersSection: Split panel for Individuals and Families with checkmark lists - WhyShieldAISection: 3 value prop cards (Proactive, AI-Powered, Privacy First) - CTABannerSection: Final CTA with Create Account and Sign In buttons - Updated routes/index.tsx with clip-path polygon transitions between sections - Added 49 unit tests for all new sections --- .../components/landing/CTABannerSection.tsx | 41 ++ .../landing/FeaturesGridSection.tsx | 213 ++++++++++ .../components/landing/ForUsersSection.tsx | 151 +++++++ .../components/landing/HowItWorksSection.tsx | 158 ++++++++ .../components/landing/WhyShieldAISection.tsx | 185 +++++++++ web/src/components/landing/index.ts | 7 +- web/src/components/landing/sections.test.tsx | 379 ++++++++++++++++++ web/src/routes/index.tsx | 59 ++- 8 files changed, 1186 insertions(+), 7 deletions(-) create mode 100644 web/src/components/landing/CTABannerSection.tsx create mode 100644 web/src/components/landing/FeaturesGridSection.tsx create mode 100644 web/src/components/landing/ForUsersSection.tsx create mode 100644 web/src/components/landing/HowItWorksSection.tsx create mode 100644 web/src/components/landing/WhyShieldAISection.tsx create mode 100644 web/src/components/landing/sections.test.tsx diff --git a/web/src/components/landing/CTABannerSection.tsx b/web/src/components/landing/CTABannerSection.tsx new file mode 100644 index 0000000..c5e79a2 --- /dev/null +++ b/web/src/components/landing/CTABannerSection.tsx @@ -0,0 +1,41 @@ +import { A } from "@solidjs/router"; +import { cn } from "~/lib/utils"; +import { Button } from "~/components/ui"; +import PageContainer from "~/components/layout/PageContainer"; + +interface CTABannerSectionProps { + class?: string; +} + +export default function CTABannerSection(props: CTABannerSectionProps) { + return ( +
+ +
+

+ Ready to protect your identity? +

+

+ Join thousands of users who trust ShieldAI to keep their digital + identity safe from emerging threats. +

+ +
+
+
+ ); +} diff --git a/web/src/components/landing/FeaturesGridSection.tsx b/web/src/components/landing/FeaturesGridSection.tsx new file mode 100644 index 0000000..da47bca --- /dev/null +++ b/web/src/components/landing/FeaturesGridSection.tsx @@ -0,0 +1,213 @@ +import { For } from "solid-js"; +import type { JSX } from "solid-js"; +import { cn } from "~/lib/utils"; +import Card from "~/components/ui/Card"; +import PageContainer from "~/components/layout/PageContainer"; + +interface Feature { + title: string; + description: string; + icon: () => JSX.Element; +} + +function DarkWatchIcon() { + return ( + + + + ); +} + +function VoicePrintIcon() { + return ( + + + + ); +} + +function SpamShieldIcon() { + return ( + + + + ); +} + +function HomeTitleIcon() { + return ( + + + + ); +} + +function RemoveBrokersIcon() { + return ( + + + + ); +} + +function FamilyPlansIcon() { + return ( + + + + ); +} + +const features: Feature[] = [ + { + title: "DarkWatch", + description: + "Continuous dark web monitoring to detect your exposed credentials and personal data.", + icon: DarkWatchIcon, + }, + { + title: "VoicePrint", + description: + "AI-powered voice clone detection to protect against deepfake voice scams.", + icon: VoicePrintIcon, + }, + { + title: "SpamShield", + description: + "Intelligent spam and scam call blocking that learns your patterns over time.", + icon: SpamShieldIcon, + }, + { + title: "HomeTitle", + description: + "Property fraud alerts that notify you of unauthorized changes to your home records.", + icon: HomeTitleIcon, + }, + { + title: "RemoveBrokers", + description: + "Automatic data broker removal to minimize your personal data footprint online.", + icon: RemoveBrokersIcon, + }, + { + title: "Family Plans", + description: + "Protect your whole household with shared monitoring, alerts, and management tools.", + icon: FamilyPlansIcon, + }, +]; + +interface FeatureCardProps { + feature: Feature; +} + +function FeatureCard(props: FeatureCardProps) { + const Icon = props.feature.icon; + return ( + +
+
+ +
+
+

+ {props.feature.title} +

+

+ {props.feature.description} +

+
+
+
+ ); +} + +interface FeaturesGridSectionProps { + class?: string; +} + +export default function FeaturesGridSection(props: FeaturesGridSectionProps) { + return ( +
+ +
+

+ Platform Features +

+

+ Comprehensive protection powered by AI and real-time monitoring +

+
+ +
+ + {(feature) => } + +
+
+
+ ); +} diff --git a/web/src/components/landing/ForUsersSection.tsx b/web/src/components/landing/ForUsersSection.tsx new file mode 100644 index 0000000..426eae7 --- /dev/null +++ b/web/src/components/landing/ForUsersSection.tsx @@ -0,0 +1,151 @@ +import { For } from "solid-js"; +import type { JSX } from "solid-js"; +import { cn } from "~/lib/utils"; +import Card from "~/components/ui/Card"; +import PageContainer from "~/components/layout/PageContainer"; + +function CheckIcon() { + return ( + + + + ); +} + +function IndividualIcon() { + return ( + + + + + ); +} + +function FamilyIcon() { + return ( + + + + + + ); +} + +interface PanelProps { + title: string; + description: string; + items: string[]; + icon: () => JSX.Element; +} + +function Panel(props: PanelProps) { + const Icon = props.icon; + return ( + +
+
+ +
+

+ {props.title} +

+

+ {props.description} +

+
    + + {(item) => ( +
  • + + + {item} + +
  • + )} +
    +
+
+
+ ); +} + +interface ForUsersSectionProps { + class?: string; +} + +export default function ForUsersSection(props: ForUsersSectionProps) { + return ( +
+ +
+

+ For Everyone +

+

+ Whether you're protecting yourself or your whole family +

+
+ +
+ + +
+
+
+ ); +} diff --git a/web/src/components/landing/HowItWorksSection.tsx b/web/src/components/landing/HowItWorksSection.tsx new file mode 100644 index 0000000..f1f0d8c --- /dev/null +++ b/web/src/components/landing/HowItWorksSection.tsx @@ -0,0 +1,158 @@ +import { For } from "solid-js"; +import type { JSX } from "solid-js"; +import { cn } from "~/lib/utils"; +import PageContainer from "~/components/layout/PageContainer"; + +interface Step { + number: number; + title: string; + description: string; + icon: () => JSX.Element; +} + +function EnrollIcon() { + return ( + + + + ); +} + +function MonitorIcon() { + return ( + + + + ); +} + +function AlertIcon() { + return ( + + + + ); +} + +const steps: Step[] = [ + { + number: 1, + title: "Enroll Your Identity", + description: + "Sign up and add your emails, phone numbers, and family members to create your protection profile.", + icon: EnrollIcon, + }, + { + number: 2, + title: "We Monitor 24/7", + description: + "Our system runs continuous dark web scans, voiceprint detection, and spam filtering to catch threats early.", + icon: MonitorIcon, + }, + { + number: 3, + title: "Get Instant Alerts", + description: + "Receive real-time notifications the moment a threat is detected, with clear guidance on what to do next.", + icon: AlertIcon, + }, +]; + +interface StepItemProps { + step: Step; + index: number; +} + +function StepItem(props: StepItemProps) { + const isEven = props.index % 2 === 0; + const Icon = props.step.icon; + + return ( +
+
+
+ +
+
+ +
+
+ + Step {props.step.number} + +
+

+ {props.step.title} +

+

+ {props.step.description} +

+
+
+ ); +} + +interface HowItWorksSectionProps { + class?: string; +} + +export default function HowItWorksSection(props: HowItWorksSectionProps) { + return ( +
+ +
+

+ How It Works +

+

+ Three simple steps to full identity protection +

+
+ +
+ + {(step, index) => } + +
+
+
+ ); +} diff --git a/web/src/components/landing/WhyShieldAISection.tsx b/web/src/components/landing/WhyShieldAISection.tsx new file mode 100644 index 0000000..dcd9edd --- /dev/null +++ b/web/src/components/landing/WhyShieldAISection.tsx @@ -0,0 +1,185 @@ +import { For } from "solid-js"; +import type { JSX } from "solid-js"; +import { cn } from "~/lib/utils"; +import Card from "~/components/ui/Card"; +import PageContainer from "~/components/layout/PageContainer"; + +function CheckIcon() { + return ( + + + + ); +} + +function ProactiveIcon() { + return ( + + + + ); +} + +function AIIcon() { + return ( + + + + ); +} + +function PrivacyIcon() { + return ( + + + + ); +} + +interface ValueProp { + title: string; + description: string; + items: string[]; + icon: () => JSX.Element; +} + +const valueProps: ValueProp[] = [ + { + title: "Proactive, Not Reactive", + description: + "We detect threats before they cause damage, so you can act early.", + icon: ProactiveIcon, + items: [ + "Real-time dark web scanning", + "Pre-breach alerts and warnings", + "Automated threat response", + ], + }, + { + title: "AI-Powered Detection", + description: + "Machine learning models trained on real scam data to catch the latest threats.", + icon: AIIcon, + items: [ + "Deepfake voice identification", + "Pattern-based scam detection", + "Continuous model improvement", + ], + }, + { + title: "Privacy First", + description: + "Your data stays encrypted and private. We never sell your information.", + icon: PrivacyIcon, + items: [ + "End-to-end encrypted data", + "GDPR and CCPA compliant", + "Zero data selling policy", + ], + }, +]; + +interface ValueCardProps { + prop: ValueProp; +} + +function ValueCard(props: ValueCardProps) { + const Icon = props.prop.icon; + return ( + +
+
+ +
+

+ {props.prop.title} +

+

+ {props.prop.description} +

+
    + + {(item) => ( +
  • + + + {item} + +
  • + )} +
    +
+
+
+ ); +} + +interface WhyShieldAISectionProps { + class?: string; +} + +export default function WhyShieldAISection(props: WhyShieldAISectionProps) { + return ( +
+ +
+

+ Why ShieldAI +

+

+ Built on cutting-edge technology with your privacy at the core +

+
+ +
+ + {(prop) => } + +
+
+
+ ); +} diff --git a/web/src/components/landing/index.ts b/web/src/components/landing/index.ts index af54582..11e2cb1 100644 --- a/web/src/components/landing/index.ts +++ b/web/src/components/landing/index.ts @@ -1,2 +1,7 @@ -export { default as ColorWaveBackground } from "./ColorWaveBackground"; +export { ColorWaveBackground } from "./ColorWaveBackground"; export { default as HeroSection } from "./HeroSection"; +export { default as HowItWorksSection } from "./HowItWorksSection"; +export { default as FeaturesGridSection } from "./FeaturesGridSection"; +export { default as ForUsersSection } from "./ForUsersSection"; +export { default as WhyShieldAISection } from "./WhyShieldAISection"; +export { default as CTABannerSection } from "./CTABannerSection"; diff --git a/web/src/components/landing/sections.test.tsx b/web/src/components/landing/sections.test.tsx new file mode 100644 index 0000000..92892e3 --- /dev/null +++ b/web/src/components/landing/sections.test.tsx @@ -0,0 +1,379 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { render } from "solid-js/web"; +import type { JSX } from "solid-js"; + +vi.mock("@solidjs/router", () => ({ + A: (props: { href?: string; children?: JSX.Element }) => { + const href = props.href || "#"; + return ( + + {props.children} + + ); + }, +})); + +import HowItWorksSection from "./HowItWorksSection"; +import FeaturesGridSection from "./FeaturesGridSection"; +import ForUsersSection from "./ForUsersSection"; +import WhyShieldAISection from "./WhyShieldAISection"; +import CTABannerSection from "./CTABannerSection"; + +function mount(comp: () => JSX.Element): HTMLDivElement { + const container = document.createElement("div"); + document.body.appendChild(container); + render(() => comp(), container); + return container; +} + +beforeEach(() => { + document.body.innerHTML = ""; +}); + +afterEach(() => { + document.body.innerHTML = ""; +}); + +describe("HowItWorksSection", () => { + it("renders the section heading", () => { + mount(() => ); + expect(document.body.textContent).toContain("How It Works"); + }); + + it("renders the section subheading", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Three simple steps to full identity protection", + ); + }); + + it("renders all 3 steps", () => { + mount(() => ); + expect(document.body.textContent).toContain("Enroll Your Identity"); + expect(document.body.textContent).toContain("We Monitor 24/7"); + expect(document.body.textContent).toContain("Get Instant Alerts"); + }); + + it("renders step descriptions", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Sign up and add your emails", + ); + expect(document.body.textContent).toContain("dark web scans"); + expect(document.body.textContent).toContain("real-time notifications"); + }); + + it("renders 3 numbered circles with gradient-primary", () => { + mount(() => ); + const circles = document.querySelectorAll(".gradient-primary"); + expect(circles.length).toBe(3); + }); + + it("has the anchor ID for smooth scrolling", () => { + mount(() => ); + const section = document.querySelector('#how-it-works'); + expect(section).toBeTruthy(); + }); + + it("applies custom class prop", () => { + mount(() => ); + const section = document.querySelector("section.custom-how"); + expect(section).toBeTruthy(); + }); + + it("wraps content in PageContainer", () => { + mount(() => ); + const container = document.querySelector(".max-w-7xl"); + expect(container).toBeTruthy(); + }); +}); + +describe("FeaturesGridSection", () => { + it("renders the section heading", () => { + mount(() => ); + expect(document.body.textContent).toContain("Platform Features"); + }); + + it("renders the section subheading", () => { + mount(() => ); + expect(document.body.textContent).toContain("Comprehensive protection"); + }); + + it("renders all 6 feature cards", () => { + mount(() => ); + expect(document.body.textContent).toContain("DarkWatch"); + expect(document.body.textContent).toContain("VoicePrint"); + expect(document.body.textContent).toContain("SpamShield"); + expect(document.body.textContent).toContain("HomeTitle"); + expect(document.body.textContent).toContain("RemoveBrokers"); + expect(document.body.textContent).toContain("Family Plans"); + }); + + it("renders 6 Card components", () => { + mount(() => ); + const cards = document.querySelectorAll(".gradient-card"); + expect(cards.length).toBe(6); + }); + + it("renders feature descriptions", () => { + mount(() => ); + expect(document.body.textContent).toContain("dark web monitoring"); + expect(document.body.textContent).toContain("voice clone detection"); + expect(document.body.textContent).toContain("scam call blocking"); + }); + + it("has the anchor ID for smooth scrolling", () => { + mount(() => ); + const section = document.querySelector('#features'); + expect(section).toBeTruthy(); + }); + + it("applies custom class prop", () => { + mount(() => ); + const section = document.querySelector("section.custom-features"); + expect(section).toBeTruthy(); + }); + + it("uses responsive grid layout", () => { + mount(() => ); + const grid = document.querySelector(".grid-cols-1"); + expect(grid).toBeTruthy(); + expect(grid!.className).toContain("md:grid-cols-2"); + expect(grid!.className).toContain("lg:grid-cols-3"); + }); +}); + +describe("ForUsersSection", () => { + it("renders the section heading", () => { + mount(() => ); + expect(document.body.textContent).toContain("For Everyone"); + }); + + it("renders the section subheading", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Whether you're protecting yourself", + ); + }); + + it("renders both panels", () => { + mount(() => ); + expect(document.body.textContent).toContain("For Individuals"); + expect(document.body.textContent).toContain("For Families"); + }); + + it("renders individual panel description", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Personal identity protection", + ); + }); + + it("renders family panel description", () => { + mount(() => ); + expect(document.body.textContent).toContain("Group management tools"); + }); + + it("renders bullet items for individuals", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Monitor personal email and phone numbers", + ); + expect(document.body.textContent).toContain( + "Dark web credential scanning", + ); + }); + + it("renders bullet items for families", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Add unlimited family members", + ); + expect(document.body.textContent).toContain("Shared alert dashboard"); + }); + + it("renders checkmark icons", () => { + mount(() => ); + const checkmarks = document.querySelectorAll( + 'svg path[fill="var(--color-success)"]', + ); + expect(checkmarks.length).toBeGreaterThan(0); + }); + + it("renders 2 Card components for panels", () => { + mount(() => ); + const cards = document.querySelectorAll(".gradient-card"); + expect(cards.length).toBe(2); + }); + + it("has the anchor ID for smooth scrolling", () => { + mount(() => ); + const section = document.querySelector('#for-users'); + expect(section).toBeTruthy(); + }); + + it("applies custom class prop", () => { + mount(() => ); + const section = document.querySelector("section.custom-users"); + expect(section).toBeTruthy(); + }); + + it("uses two-column grid on desktop", () => { + mount(() => ); + const grid = document.querySelector(".grid-cols-1"); + expect(grid).toBeTruthy(); + expect(grid!.className).toContain("md:grid-cols-2"); + }); +}); + +describe("WhyShieldAISection", () => { + it("renders the section heading", () => { + mount(() => ); + expect(document.body.textContent).toContain("Why ShieldAI"); + }); + + it("renders the section subheading", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Built on cutting-edge technology", + ); + }); + + it("renders all 3 value prop cards", () => { + mount(() => ); + expect(document.body.textContent).toContain("Proactive, Not Reactive"); + expect(document.body.textContent).toContain("AI-Powered Detection"); + expect(document.body.textContent).toContain("Privacy First"); + }); + + it("renders value prop descriptions", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "detect threats before they cause damage", + ); + expect(document.body.textContent).toContain( + "Machine learning models trained", + ); + expect(document.body.textContent).toContain("encrypted and private"); + }); + + it("renders bullet items for each card", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Real-time dark web scanning", + ); + expect(document.body.textContent).toContain( + "Deepfake voice identification", + ); + expect(document.body.textContent).toContain("End-to-end encrypted data"); + }); + + it("renders 3 Card components", () => { + mount(() => ); + const cards = document.querySelectorAll(".gradient-card"); + expect(cards.length).toBe(3); + }); + + it("has the anchor ID for smooth scrolling", () => { + mount(() => ); + const section = document.querySelector('#why-shieldai'); + expect(section).toBeTruthy(); + }); + + it("applies custom class prop", () => { + mount(() => ); + const section = document.querySelector("section.custom-why"); + expect(section).toBeTruthy(); + }); + + it("uses three-column grid on desktop", () => { + mount(() => ); + const grid = document.querySelector(".grid-cols-1"); + expect(grid).toBeTruthy(); + expect(grid!.className).toContain("md:grid-cols-3"); + }); +}); + +describe("CTABannerSection", () => { + it("renders the CTA headline", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Ready to protect your identity?", + ); + }); + + it("renders the CTA subtext", () => { + mount(() => ); + expect(document.body.textContent).toContain( + "Join thousands of users", + ); + }); + + it("renders Create Account button", () => { + mount(() => ); + expect(document.body.textContent).toContain("Create Account"); + const primaryBtn = document.querySelector("button.gradient-primary"); + expect(primaryBtn).toBeTruthy(); + }); + + it("renders Sign In button", () => { + mount(() => ); + expect(document.body.textContent).toContain("Sign In"); + }); + + it("has Create Account link to /signup", () => { + mount(() => ); + const links = document.querySelectorAll("a"); + const signupLink = Array.from(links).find( + (a) => a.getAttribute("href") === "/signup", + ); + expect(signupLink).toBeTruthy(); + expect(signupLink!.textContent).toContain("Create Account"); + }); + + it("has Sign In link to /login", () => { + mount(() => ); + const links = document.querySelectorAll("a"); + const loginLink = Array.from(links).find( + (a) => a.getAttribute("href") === "/login", + ); + expect(loginLink).toBeTruthy(); + expect(loginLink!.textContent).toContain("Sign In"); + }); + + it("renders 2 buttons", () => { + mount(() => ); + const buttons = document.querySelectorAll("button"); + expect(buttons.length).toBe(2); + }); + + it("has the anchor ID for smooth scrolling", () => { + mount(() => ); + const section = document.querySelector('#cta'); + expect(section).toBeTruthy(); + }); + + it("applies custom class prop", () => { + mount(() => ); + const section = document.querySelector("section.custom-cta"); + expect(section).toBeTruthy(); + }); + + it("uses centered text layout", () => { + mount(() => ); + const inner = document.querySelector(".text-center"); + expect(inner).toBeTruthy(); + }); + + it("wraps content in PageContainer", () => { + mount(() => ); + const container = document.querySelector(".max-w-7xl"); + expect(container).toBeTruthy(); + }); + + it("uses gradient card for CTA banner", () => { + mount(() => ); + const card = document.querySelector(".gradient-card"); + expect(card).toBeTruthy(); + }); +}); diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx index d626d51..571cf22 100644 --- a/web/src/routes/index.tsx +++ b/web/src/routes/index.tsx @@ -1,15 +1,62 @@ import { Title } from "@solidjs/meta"; -import { ColorWaveBackground, HeroSection } from "~/components/landing"; +import { + ColorWaveBackground, + HeroSection, + HowItWorksSection, + FeaturesGridSection, + ForUsersSection, + WhyShieldAISection, + CTABannerSection, +} from "~/components/landing"; + +const cut = "clamp(16px, 2.5vw, 40px)"; export default function Home() { return ( -
+
ShieldAI — AI-Powered Identity Protection -
- + +
+ +
+ +
-
- + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
);