feat: implement homepage structure spec 216 (#254)
Some checks failed
Main Confidence / confidence (push) Failing after 45s
Some checks failed
Main Confidence / confidence (push) Failing after 45s
## Summary Implements Spec 216 for the public website homepage in `apps/website`. This reworks the homepage into the required narrative flow: - hero with one dominant CTA, one secondary CTA, product-near visual, and bounded trust subclaims - outcome framing section - grouped capability model section - explicit trust block before the final CTA - dated progress teaser backed by changelog entries - final CTA transition to contact It also adds the full spec-kit artifact set for `specs/216-homepage-structure` and updates the smoke suite to prove section order, CTA hierarchy, onward route reachability, and mobile readability. ## Validation - `corepack pnpm build:website` - `cd apps/website && corepack pnpm exec playwright test` ## Notes - Branch: `216-homepage-structure` - Commit: `097f8e70` - Remote branch has been pushed and is ready for review. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #254
This commit is contained in:
parent
1fec9c6f9d
commit
40039337d8
4
.github/agents/copilot-instructions.md
vendored
4
.github/agents/copilot-instructions.md
vendored
@ -216,6 +216,8 @@ ## Active Technologies
|
|||||||
- PostgreSQL via existing `baseline_snapshots`, `evidence_snapshots`, `evidence_snapshot_items`, `tenant_reviews`, `review_packs`, and `operation_runs` tables; no schema change planned (214-governance-outcome-compression)
|
- PostgreSQL via existing `baseline_snapshots`, `evidence_snapshots`, `evidence_snapshot_items`, `tenant_reviews`, `review_packs`, and `operation_runs` tables; no schema change planned (214-governance-outcome-compression)
|
||||||
- Astro 6.0.0 templates + TypeScript 5.9 strict + Astro 6, Tailwind CSS v4 via `@tailwindcss/vite`, Astro content collections, local Astro layout/primitive/content helpers, Playwright smoke tests (215-website-core-pages)
|
- Astro 6.0.0 templates + TypeScript 5.9 strict + Astro 6, Tailwind CSS v4 via `@tailwindcss/vite`, Astro content collections, local Astro layout/primitive/content helpers, Playwright smoke tests (215-website-core-pages)
|
||||||
- Static filesystem pages, content modules, and Astro content collections under `apps/website/src` and `apps/website/public`; no database (215-website-core-pages)
|
- Static filesystem pages, content modules, and Astro content collections under `apps/website/src` and `apps/website/public`; no database (215-website-core-pages)
|
||||||
|
- Astro 6.0.0 templates + TypeScript 5.9.x + Astro 6, Tailwind CSS v4, local Astro layout/section primitives, Astro content collections, Playwright browser smoke tests (216-homepage-structure)
|
||||||
|
- Static filesystem content, Astro content collections, and assets under `apps/website/src` and `apps/website/public`; no database (216-homepage-structure)
|
||||||
|
|
||||||
- PHP 8.4.15 (feat/005-bulk-operations)
|
- PHP 8.4.15 (feat/005-bulk-operations)
|
||||||
|
|
||||||
@ -250,10 +252,10 @@ ## Code Style
|
|||||||
PHP 8.4.15: Follow standard conventions
|
PHP 8.4.15: Follow standard conventions
|
||||||
|
|
||||||
## Recent Changes
|
## Recent Changes
|
||||||
|
- 216-homepage-structure: Added Astro 6.0.0 templates + TypeScript 5.9.x + Astro 6, Tailwind CSS v4, local Astro layout/section primitives, Astro content collections, Playwright browser smoke tests
|
||||||
- 215-website-core-pages: Added Astro 6.0.0 templates + TypeScript 5.9 strict + Astro 6, Tailwind CSS v4 via `@tailwindcss/vite`, Astro content collections, local Astro layout/primitive/content helpers, Playwright smoke tests
|
- 215-website-core-pages: Added Astro 6.0.0 templates + TypeScript 5.9 strict + Astro 6, Tailwind CSS v4 via `@tailwindcss/vite`, Astro content collections, local Astro layout/primitive/content helpers, Playwright smoke tests
|
||||||
- 214-governance-outcome-compression: Added PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + Filament v5, Livewire v4, Pest v4, Laravel Sail, `ArtifactTruthPresenter`, `ArtifactTruthEnvelope`, `OperatorExplanationBuilder`, `BaselineSnapshotPresenter`, `BadgeCatalog`, `BadgeRenderer`, existing governance Filament resources/pages, and current Enterprise Detail builders
|
- 214-governance-outcome-compression: Added PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + Filament v5, Livewire v4, Pest v4, Laravel Sail, `ArtifactTruthPresenter`, `ArtifactTruthEnvelope`, `OperatorExplanationBuilder`, `BaselineSnapshotPresenter`, `BadgeCatalog`, `BadgeRenderer`, existing governance Filament resources/pages, and current Enterprise Detail builders
|
||||||
- 213-website-foundation-v0: Added Astro 6.0.0 templates + TypeScript 5.x (explicit setup in `apps/website`) + Astro 6, Tailwind CSS v4, custom Astro component primitives (shadcn-inspired), lightweight Playwright browser smoke tests
|
- 213-website-foundation-v0: Added Astro 6.0.0 templates + TypeScript 5.x (explicit setup in `apps/website`) + Astro 6, Tailwind CSS v4, custom Astro component primitives (shadcn-inspired), lightweight Playwright browser smoke tests
|
||||||
- 214-website-visual-foundation: Added Astro 6.0.0 templates + TypeScript 5.9 strict + Astro 6, Tailwind CSS v4 via `@tailwindcss/vite`, Astro content collections, local Astro component primitives, Playwright browser smoke tests
|
- 214-website-visual-foundation: Added Astro 6.0.0 templates + TypeScript 5.9 strict + Astro 6, Tailwind CSS v4 via `@tailwindcss/vite`, Astro content collections, local Astro component primitives, Playwright browser smoke tests
|
||||||
- 201-enforcement-review-guardrails: Added Markdown governance artifacts, JSON Schema plus logical OpenAPI planning contracts, and Bash-backed SpecKit scripts inside a PHP 8.4.15 / Laravel 12 / Filament v5 / Livewire v4 repository + `.specify/memory/constitution.md`, `.specify/templates/spec-template.md`, `.specify/templates/plan-template.md`, `.specify/templates/tasks-template.md`, `.specify/templates/checklist-template.md`, `.specify/README.md`, `docs/ui/operator-ux-surface-standards.md`, and Specs 196 through 200
|
|
||||||
<!-- MANUAL ADDITIONS START -->
|
<!-- MANUAL ADDITIONS START -->
|
||||||
<!-- MANUAL ADDITIONS END -->
|
<!-- MANUAL ADDITIONS END -->
|
||||||
|
|||||||
81
apps/website/public/images/hero-product-visual.svg
Normal file
81
apps/website/public/images/hero-product-visual.svg
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="500" viewBox="0 0 800 500">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#f8fafc;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#e2e8f0;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="header" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
|
<stop offset="0%" style="stop-color:#2f6fb7;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#8eaed1;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect width="800" height="500" rx="16" fill="url(#bg)" stroke="#e2e8f0" stroke-width="2"/>
|
||||||
|
<!-- Header bar -->
|
||||||
|
<rect x="0" y="0" width="800" height="48" rx="16" fill="url(#header)"/>
|
||||||
|
<rect x="0" y="16" width="800" height="32" fill="url(#header)"/>
|
||||||
|
<text x="24" y="30" font-family="system-ui" font-size="14" fill="white" font-weight="600">TenantAtlas</text>
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<rect x="0" y="48" width="180" height="452" fill="#f1f5f9"/>
|
||||||
|
<rect x="16" y="68" width="148" height="32" rx="8" fill="#2f6fb7" opacity="0.12"/>
|
||||||
|
<text x="28" y="89" font-family="system-ui" font-size="12" fill="#2f6fb7" font-weight="600">Inventory</text>
|
||||||
|
<rect x="16" y="112" width="148" height="28" rx="8" fill="transparent"/>
|
||||||
|
<text x="28" y="130" font-family="system-ui" font-size="12" fill="#64748b">Backup History</text>
|
||||||
|
<rect x="16" y="148" width="148" height="28" rx="8" fill="transparent"/>
|
||||||
|
<text x="28" y="166" font-family="system-ui" font-size="12" fill="#64748b">Restore</text>
|
||||||
|
<rect x="16" y="184" width="148" height="28" rx="8" fill="transparent"/>
|
||||||
|
<text x="28" y="202" font-family="system-ui" font-size="12" fill="#64748b">Drift Detection</text>
|
||||||
|
<rect x="16" y="220" width="148" height="28" rx="8" fill="transparent"/>
|
||||||
|
<text x="28" y="238" font-family="system-ui" font-size="12" fill="#64748b">Governance</text>
|
||||||
|
<!-- Main content area -->
|
||||||
|
<rect x="196" y="64" width="588" height="420" rx="12" fill="white" stroke="#e2e8f0"/>
|
||||||
|
<!-- Stats row -->
|
||||||
|
<rect x="212" y="80" width="130" height="64" rx="8" fill="#f8fafc" stroke="#e2e8f0"/>
|
||||||
|
<text x="228" y="104" font-family="system-ui" font-size="22" fill="#1e293b" font-weight="700">247</text>
|
||||||
|
<text x="228" y="130" font-family="system-ui" font-size="11" fill="#64748b">Policies tracked</text>
|
||||||
|
<rect x="358" y="80" width="130" height="64" rx="8" fill="#f8fafc" stroke="#e2e8f0"/>
|
||||||
|
<text x="374" y="104" font-family="system-ui" font-size="22" fill="#16a34a" font-weight="700">98.4%</text>
|
||||||
|
<text x="374" y="130" font-family="system-ui" font-size="11" fill="#64748b">Compliance rate</text>
|
||||||
|
<rect x="504" y="80" width="130" height="64" rx="8" fill="#f8fafc" stroke="#e2e8f0"/>
|
||||||
|
<text x="520" y="104" font-family="system-ui" font-size="22" fill="#2f6fb7" font-weight="700">12</text>
|
||||||
|
<text x="520" y="130" font-family="system-ui" font-size="11" fill="#64748b">Versions stored</text>
|
||||||
|
<rect x="650" y="80" width="118" height="64" rx="8" fill="#f8fafc" stroke="#e2e8f0"/>
|
||||||
|
<text x="666" y="104" font-family="system-ui" font-size="22" fill="#f59e0b" font-weight="700">3</text>
|
||||||
|
<text x="666" y="130" font-family="system-ui" font-size="11" fill="#64748b">Drift alerts</text>
|
||||||
|
<!-- Table header -->
|
||||||
|
<rect x="212" y="160" width="556" height="36" rx="0" fill="#f8fafc"/>
|
||||||
|
<text x="228" y="183" font-family="system-ui" font-size="11" fill="#64748b" font-weight="600">POLICY NAME</text>
|
||||||
|
<text x="440" y="183" font-family="system-ui" font-size="11" fill="#64748b" font-weight="600">TYPE</text>
|
||||||
|
<text x="560" y="183" font-family="system-ui" font-size="11" fill="#64748b" font-weight="600">STATUS</text>
|
||||||
|
<text x="670" y="183" font-family="system-ui" font-size="11" fill="#64748b" font-weight="600">LAST BACKUP</text>
|
||||||
|
<!-- Table rows -->
|
||||||
|
<line x1="212" y1="196" x2="768" y2="196" stroke="#e2e8f0"/>
|
||||||
|
<text x="228" y="220" font-family="system-ui" font-size="12" fill="#1e293b">Windows Compliance Baseline</text>
|
||||||
|
<text x="440" y="220" font-family="system-ui" font-size="12" fill="#64748b">Compliance</text>
|
||||||
|
<rect x="560" y="208" width="56" height="20" rx="10" fill="#dcfce7"/>
|
||||||
|
<text x="572" y="222" font-family="system-ui" font-size="10" fill="#16a34a" font-weight="500">Synced</text>
|
||||||
|
<text x="670" y="220" font-family="system-ui" font-size="12" fill="#64748b">2 min ago</text>
|
||||||
|
<line x1="212" y1="236" x2="768" y2="236" stroke="#f1f5f9"/>
|
||||||
|
<text x="228" y="260" font-family="system-ui" font-size="12" fill="#1e293b">BitLocker Encryption Policy</text>
|
||||||
|
<text x="440" y="260" font-family="system-ui" font-size="12" fill="#64748b">Config</text>
|
||||||
|
<rect x="560" y="248" width="56" height="20" rx="10" fill="#dcfce7"/>
|
||||||
|
<text x="572" y="262" font-family="system-ui" font-size="10" fill="#16a34a" font-weight="500">Synced</text>
|
||||||
|
<text x="670" y="260" font-family="system-ui" font-size="12" fill="#64748b">15 min ago</text>
|
||||||
|
<line x1="212" y1="276" x2="768" y2="276" stroke="#f1f5f9"/>
|
||||||
|
<text x="228" y="300" font-family="system-ui" font-size="12" fill="#1e293b">Conditional Access – MFA</text>
|
||||||
|
<text x="440" y="300" font-family="system-ui" font-size="12" fill="#64748b">Access</text>
|
||||||
|
<rect x="560" y="288" width="48" height="20" rx="10" fill="#fef3c7"/>
|
||||||
|
<text x="569" y="302" font-family="system-ui" font-size="10" fill="#d97706" font-weight="500">Drift</text>
|
||||||
|
<text x="670" y="300" font-family="system-ui" font-size="12" fill="#64748b">1 hr ago</text>
|
||||||
|
<line x1="212" y1="316" x2="768" y2="316" stroke="#f1f5f9"/>
|
||||||
|
<text x="228" y="340" font-family="system-ui" font-size="12" fill="#1e293b">Autopilot Deployment Profile</text>
|
||||||
|
<text x="440" y="340" font-family="system-ui" font-size="12" fill="#64748b">Enrollment</text>
|
||||||
|
<rect x="560" y="328" width="56" height="20" rx="10" fill="#dcfce7"/>
|
||||||
|
<text x="572" y="342" font-family="system-ui" font-size="10" fill="#16a34a" font-weight="500">Synced</text>
|
||||||
|
<text x="670" y="340" font-family="system-ui" font-size="12" fill="#64748b">30 min ago</text>
|
||||||
|
<line x1="212" y1="356" x2="768" y2="356" stroke="#f1f5f9"/>
|
||||||
|
<text x="228" y="380" font-family="system-ui" font-size="12" fill="#1e293b">App Protection – iOS Managed</text>
|
||||||
|
<text x="440" y="380" font-family="system-ui" font-size="12" fill="#64748b">Protection</text>
|
||||||
|
<rect x="560" y="368" width="56" height="20" rx="10" fill="#dcfce7"/>
|
||||||
|
<text x="572" y="382" font-family="system-ui" font-size="10" fill="#16a34a" font-weight="500">Synced</text>
|
||||||
|
<text x="670" y="380" font-family="system-ui" font-size="12" fill="#64748b">45 min ago</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.4 KiB |
@ -3,9 +3,10 @@ interface Props {
|
|||||||
as?: keyof HTMLElementTagNameMap;
|
as?: keyof HTMLElementTagNameMap;
|
||||||
class?: string;
|
class?: string;
|
||||||
variant?: 'accent' | 'default' | 'subtle';
|
variant?: 'accent' | 'default' | 'subtle';
|
||||||
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { as = 'article', class: className = '', variant = 'default' } = Astro.props;
|
const { as = 'article', class: className = '', variant = 'default', ...rest } = Astro.props;
|
||||||
|
|
||||||
const variantClasses = {
|
const variantClasses = {
|
||||||
default: 'surface-card',
|
default: 'surface-card',
|
||||||
@ -19,6 +20,7 @@ const Tag = as;
|
|||||||
<Tag
|
<Tag
|
||||||
class:list={['rounded-[1.65rem] p-6 sm:p-7', variantClasses[variant], className]}
|
class:list={['rounded-[1.65rem] p-6 sm:p-7', variantClasses[variant], className]}
|
||||||
data-surface={variant}
|
data-surface={variant}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ interface Props {
|
|||||||
id?: string;
|
id?: string;
|
||||||
layer?: '1' | '2' | '3';
|
layer?: '1' | '2' | '3';
|
||||||
tone?: 'default' | 'emphasis' | 'muted';
|
tone?: 'default' | 'emphasis' | 'muted';
|
||||||
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -15,6 +16,7 @@ const {
|
|||||||
id,
|
id,
|
||||||
layer = '2',
|
layer = '2',
|
||||||
tone = 'default',
|
tone = 'default',
|
||||||
|
...rest
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
const Tag = as;
|
const Tag = as;
|
||||||
const densityClasses = {
|
const densityClasses = {
|
||||||
@ -37,6 +39,7 @@ const toneClasses = {
|
|||||||
toneClasses[tone],
|
toneClasses[tone],
|
||||||
className,
|
className,
|
||||||
]}
|
]}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|||||||
59
apps/website/src/components/sections/CapabilityGrid.astro
Normal file
59
apps/website/src/components/sections/CapabilityGrid.astro
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
import Badge from '@/components/primitives/Badge.astro';
|
||||||
|
import Card from '@/components/primitives/Card.astro';
|
||||||
|
import Container from '@/components/primitives/Container.astro';
|
||||||
|
import Grid from '@/components/primitives/Grid.astro';
|
||||||
|
import Headline from '@/components/content/Headline.astro';
|
||||||
|
import Lead from '@/components/content/Lead.astro';
|
||||||
|
import Section from '@/components/primitives/Section.astro';
|
||||||
|
import SectionHeader from '@/components/primitives/SectionHeader.astro';
|
||||||
|
import type { CapabilityClusterContent } from '@/types/site';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
description?: string;
|
||||||
|
eyebrow?: string;
|
||||||
|
items: CapabilityClusterContent[];
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { description, eyebrow, items, title } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Section layer="2" data-section="capability">
|
||||||
|
<Container width="wide">
|
||||||
|
<div class="space-y-8">
|
||||||
|
<SectionHeader eyebrow={eyebrow} title={title} description={description} />
|
||||||
|
<Grid cols="2" gap="lg">
|
||||||
|
{items.map((cluster) => (
|
||||||
|
<Card class="h-full">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-start justify-between gap-3">
|
||||||
|
<Headline as="h3" size="card">
|
||||||
|
{cluster.title}
|
||||||
|
</Headline>
|
||||||
|
{cluster.meta && (
|
||||||
|
<Badge tone="signal">{cluster.meta}</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Lead size="body">
|
||||||
|
{cluster.description}
|
||||||
|
</Lead>
|
||||||
|
<ul class="flex flex-wrap gap-2 p-0">
|
||||||
|
{cluster.capabilities.map((cap) => (
|
||||||
|
<li class="list-none rounded-full border border-[color:var(--color-line)] bg-white/60 px-3 py-1.5 text-sm text-[var(--color-ink-800)]">
|
||||||
|
{cap}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
{cluster.href && (
|
||||||
|
<a class="text-link mt-2 inline-block text-sm font-semibold" href={cluster.href}>
|
||||||
|
Learn more →
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
40
apps/website/src/components/sections/OutcomeSection.astro
Normal file
40
apps/website/src/components/sections/OutcomeSection.astro
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
import Card from '@/components/primitives/Card.astro';
|
||||||
|
import Container from '@/components/primitives/Container.astro';
|
||||||
|
import Grid from '@/components/primitives/Grid.astro';
|
||||||
|
import Headline from '@/components/content/Headline.astro';
|
||||||
|
import Lead from '@/components/content/Lead.astro';
|
||||||
|
import Section from '@/components/primitives/Section.astro';
|
||||||
|
import SectionHeader from '@/components/primitives/SectionHeader.astro';
|
||||||
|
import type { OutcomeSectionContent } from '@/types/site';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
content: OutcomeSectionContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { content } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Section layer="2" data-section="outcome">
|
||||||
|
<Container width="wide">
|
||||||
|
<div class="space-y-8">
|
||||||
|
<SectionHeader
|
||||||
|
eyebrow="Why it matters"
|
||||||
|
title={content.title}
|
||||||
|
description={content.description}
|
||||||
|
/>
|
||||||
|
<Grid cols="3">
|
||||||
|
{content.outcomes.map((outcome) => (
|
||||||
|
<Card class="h-full">
|
||||||
|
<Headline as="h3" size="card">
|
||||||
|
{outcome.title}
|
||||||
|
</Headline>
|
||||||
|
<Lead class="mt-3" size="body">
|
||||||
|
{outcome.description}
|
||||||
|
</Lead>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
@ -42,7 +42,18 @@ const { calloutDescription, calloutTitle, hero, metrics = [] } = Astro.props;
|
|||||||
)}
|
)}
|
||||||
</Cluster>
|
</Cluster>
|
||||||
)}
|
)}
|
||||||
{hero.highlights && hero.highlights.length > 0 && (
|
{hero.trustSubclaims && hero.trustSubclaims.length > 0 && (
|
||||||
|
<ul class="grid gap-3 p-0 sm:grid-cols-3">
|
||||||
|
{
|
||||||
|
hero.trustSubclaims.map((claim) => (
|
||||||
|
<li class="list-none rounded-[1.1rem] border border-[color:var(--color-line)] bg-white/70 px-4 py-3 text-sm font-medium text-[var(--color-ink-800)]">
|
||||||
|
{claim}
|
||||||
|
</li>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
{hero.highlights && hero.highlights.length > 0 && !hero.trustSubclaims?.length && (
|
||||||
<ul class="grid gap-3 p-0 sm:grid-cols-3">
|
<ul class="grid gap-3 p-0 sm:grid-cols-3">
|
||||||
{
|
{
|
||||||
hero.highlights.map((highlight) => (
|
hero.highlights.map((highlight) => (
|
||||||
@ -57,7 +68,18 @@ const { calloutDescription, calloutTitle, hero, metrics = [] } = Astro.props;
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div class="grid gap-5">
|
<div class="grid gap-5">
|
||||||
{(calloutTitle || calloutDescription) && (
|
{hero.productVisual && (
|
||||||
|
<Card variant="accent" class="motion-rise overflow-hidden" data-hero-visual>
|
||||||
|
<img
|
||||||
|
src={hero.productVisual.src}
|
||||||
|
alt={hero.productVisual.alt}
|
||||||
|
class="w-full rounded-[var(--radius-lg)] object-cover"
|
||||||
|
loading="eager"
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!hero.productVisual && (calloutTitle || calloutDescription) && (
|
||||||
<Card variant="accent" class="motion-rise">
|
<Card variant="accent" class="motion-rise">
|
||||||
<p class="m-0 text-sm font-semibold uppercase tracking-[0.15em] text-[var(--color-brand)]">
|
<p class="m-0 text-sm font-semibold uppercase tracking-[0.15em] text-[var(--color-brand)]">
|
||||||
Trust-first launch surface
|
Trust-first launch surface
|
||||||
|
|||||||
53
apps/website/src/components/sections/ProgressTeaser.astro
Normal file
53
apps/website/src/components/sections/ProgressTeaser.astro
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
import Badge from '@/components/primitives/Badge.astro';
|
||||||
|
import Card from '@/components/primitives/Card.astro';
|
||||||
|
import Container from '@/components/primitives/Container.astro';
|
||||||
|
import Headline from '@/components/content/Headline.astro';
|
||||||
|
import Lead from '@/components/content/Lead.astro';
|
||||||
|
import PrimaryCTA from '@/components/content/PrimaryCTA.astro';
|
||||||
|
import Section from '@/components/primitives/Section.astro';
|
||||||
|
import SectionHeader from '@/components/primitives/SectionHeader.astro';
|
||||||
|
import type { ProgressTeaserContent } from '@/types/site';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
content: ProgressTeaserContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { content } = Astro.props;
|
||||||
|
|
||||||
|
function formatDate(date: Date): string {
|
||||||
|
return date.toLocaleDateString('en-US', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
<Section layer="2" data-section="progress">
|
||||||
|
<Container width="wide">
|
||||||
|
<div class="space-y-8">
|
||||||
|
<SectionHeader
|
||||||
|
eyebrow="Visible progress"
|
||||||
|
title={content.title}
|
||||||
|
description={content.description}
|
||||||
|
/>
|
||||||
|
<div class="space-y-4">
|
||||||
|
{content.entries.map((entry) => (
|
||||||
|
<Card class="flex flex-col gap-3 sm:flex-row sm:items-start sm:gap-6">
|
||||||
|
<Badge tone="signal">{formatDate(entry.date)}</Badge>
|
||||||
|
<div>
|
||||||
|
<Headline as="h3" size="card">
|
||||||
|
{entry.title}
|
||||||
|
</Headline>
|
||||||
|
<Lead class="mt-2" size="body">
|
||||||
|
{entry.description}
|
||||||
|
</Lead>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<PrimaryCTA cta={content.cta} />
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
@ -1,10 +1,12 @@
|
|||||||
import type {
|
import type {
|
||||||
CalloutContent,
|
CapabilityClusterContent,
|
||||||
FeatureItemContent,
|
CtaLink,
|
||||||
HeroContent,
|
HeroContent,
|
||||||
IntegrationEntry,
|
IntegrationEntry,
|
||||||
MetricItem,
|
OutcomeSectionContent,
|
||||||
PageSeo,
|
PageSeo,
|
||||||
|
ProgressTeaserContent,
|
||||||
|
TrustSignalGroupContent,
|
||||||
} from '@/types/site';
|
} from '@/types/site';
|
||||||
|
|
||||||
export const homeSeo: PageSeo = {
|
export const homeSeo: PageSeo = {
|
||||||
@ -15,131 +17,155 @@ export const homeSeo: PageSeo = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const homeHero: HeroContent = {
|
export const homeHero: HeroContent = {
|
||||||
eyebrow: 'Core public route set',
|
eyebrow: 'Governance of record',
|
||||||
title: 'TenantAtlas keeps Microsoft tenant change history, restore posture, and review context inside one operating record.',
|
title: 'TenantAtlas keeps Microsoft tenant change history, restore posture, and review context inside one operating record.',
|
||||||
description:
|
description:
|
||||||
'TenantAtlas gives MSP and enterprise teams a calmer way to understand what changed, what drifted, what can be restored, and what needs review without turning governance into a loose collection of disconnected screens.',
|
'MSP and enterprise teams use TenantAtlas to understand what changed, what drifted, what can be restored, and what needs review — without turning governance into disconnected screens.',
|
||||||
primaryCta: {
|
primaryCta: {
|
||||||
href: '/product',
|
href: '/contact',
|
||||||
label: 'See the product model',
|
label: 'Request a working session',
|
||||||
},
|
},
|
||||||
secondaryCta: {
|
secondaryCta: {
|
||||||
href: '/trust',
|
href: '/product',
|
||||||
label: 'Review the trust posture',
|
label: 'See the product model',
|
||||||
variant: 'secondary',
|
variant: 'secondary',
|
||||||
},
|
},
|
||||||
highlights: [
|
productVisual: {
|
||||||
'Product, trust, and changelog each do one explicit job in the buyer journey.',
|
src: '/images/hero-product-visual.svg',
|
||||||
'The public site stays static, readable, and separate from platform runtime concerns.',
|
alt: 'TenantAtlas governance dashboard showing tenant change history and restore posture',
|
||||||
'One clear contact path remains visible without pricing, docs, or placeholder routes taking over.',
|
},
|
||||||
|
trustSubclaims: [
|
||||||
|
'Tenant-isolated by design',
|
||||||
|
'Immutable change history',
|
||||||
|
'Bounded public claims',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const homeMetrics: MetricItem[] = [
|
export const homeOutcome: OutcomeSectionContent = {
|
||||||
|
title: 'Calmer operations start with visible governance.',
|
||||||
|
description:
|
||||||
|
'TenantAtlas replaces fragmented spreadsheets and manual audit trails with one connected record of tenant change, restore safety, and review context.',
|
||||||
|
audienceBias: 'MSP and enterprise operations teams',
|
||||||
|
outcomes: [
|
||||||
{
|
{
|
||||||
value: '1',
|
title: 'Understand what changed and why it matters.',
|
||||||
label: 'Primary next step',
|
description:
|
||||||
description: 'Contact stays singular and obvious while product, trust, and changelog remain nearby.',
|
'Immutable version history gives every tenant configuration a traceable timeline, so drift and unexpected changes surface before they become incidents.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: '4',
|
title: 'Restore with confidence, not guesswork.',
|
||||||
label: 'Core buyer questions',
|
description:
|
||||||
description: 'What is it, why it matters, why trust it, and what to do next each map to a named route.',
|
'Preview-first restore flows validate scope and impact before execution, turning risky rollbacks into reviewable operations.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: '0',
|
title: 'Reduce the operational risk of governance gaps.',
|
||||||
label: 'Runtime coupling',
|
description:
|
||||||
description: 'The website stays independent from platform auth, session, and API behavior.',
|
'Connected findings, evidence, and review workflows keep audit context in one place instead of scattered across tools and memory.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const homeCapabilities: CapabilityClusterContent[] = [
|
||||||
|
{
|
||||||
|
title: 'Backup & Version History',
|
||||||
|
description: 'Immutable snapshots of tenant configuration state with full version lineage.',
|
||||||
|
capabilities: ['Automated backup', 'Immutable snapshots', 'Version comparison', 'Change timeline'],
|
||||||
|
href: '/product',
|
||||||
|
meta: 'Core safety net',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Restore & Recovery',
|
||||||
|
description: 'Preview-first restore with selective scope and explicit confirmation.',
|
||||||
|
capabilities: ['Dry-run preview', 'Selective restore', 'Rollback safety', 'Conflict detection'],
|
||||||
|
href: '/product',
|
||||||
|
meta: 'Safer change operations',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Inventory & Drift Visibility',
|
||||||
|
description: 'Connected view of what exists, what drifted, and what needs attention.',
|
||||||
|
capabilities: ['Policy inventory', 'Drift detection', 'Assignment visibility', 'Cross-tenant comparison'],
|
||||||
|
href: '/product',
|
||||||
|
meta: 'Operational clarity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Governance & Evidence',
|
||||||
|
description: 'Audit-ready evidence, findings, and review workflows for tenant operations.',
|
||||||
|
capabilities: ['Audit trails', 'Evidence linkage', 'Exception tracking', 'Review workflows'],
|
||||||
|
href: '/product',
|
||||||
|
meta: 'Accountability and review',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const homePillars: FeatureItemContent[] = [
|
export const homeTrustSignals: TrustSignalGroupContent = {
|
||||||
{
|
title: 'Trust is a first-read concern, not a footnote.',
|
||||||
eyebrow: 'Product truth',
|
|
||||||
title: 'Show the connected governance model before any audience-specific detours.',
|
|
||||||
description:
|
description:
|
||||||
'The first pass should explain how inventory, immutable history, restore safety, findings, evidence, and reviews fit together before it asks a buyer to infer the model alone.',
|
'Tenant isolation, access boundaries, and operating discipline are product rules, not marketing language. Every public claim routes back to one bounded trust surface.',
|
||||||
meta: 'Product before route sprawl',
|
supportRoute: '/trust',
|
||||||
|
signals: [
|
||||||
|
{
|
||||||
|
title: 'Tenant isolation is a product boundary.',
|
||||||
|
description:
|
||||||
|
'Tenant-scoped data, access, and workflow boundaries are enforced as deliberate product rules.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eyebrow: 'Trust visibility',
|
title: 'Access stays bounded and purpose-specific.',
|
||||||
title: 'Make the credibility surface obvious while public claims stay bounded.',
|
|
||||||
description:
|
description:
|
||||||
'Trust belongs in top-level navigation because tenant isolation, access handling, and operating discipline are first-read questions for this category.',
|
'Microsoft tenant-facing access follows least-privilege scoping tied to the governance operations that require it.',
|
||||||
meta: 'Trust is top-level visible',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eyebrow: 'Visible progress',
|
title: 'Restore operations require preview and confirmation.',
|
||||||
title: 'Publish dated progress instead of implying motion through vague copy.',
|
|
||||||
description:
|
description:
|
||||||
'A changelog surface gives returning visitors one concrete route for current product movement without forcing a blog or resources hub into the first navigation layer.',
|
'High-risk changes go through validation, selective scope, and explicit confirmation instead of one-click execution.',
|
||||||
meta: 'Real changelog, no placeholder hub',
|
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
eyebrow: 'Clear action path',
|
};
|
||||||
title: 'Keep contact visible without turning the site into a demo funnel.',
|
|
||||||
description:
|
|
||||||
'The initial IA leads to one working-session route so serious buyers can move forward without guessing whether contact, demo, or sales are different flows.',
|
|
||||||
meta: 'One clear conversion route',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eyebrow: 'Outcome explanation',
|
|
||||||
title: 'Explain who the product helps without forcing a dedicated Solutions hub into the header.',
|
|
||||||
description:
|
|
||||||
'Home and Product should carry the buyer-outcome explanation well enough that retained supporting pages stay secondary instead of becoming required orientation routes.',
|
|
||||||
meta: 'Outcome clarity without route inflation',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eyebrow: 'Static-first delivery',
|
|
||||||
title: 'Preserve the website as a static public track with no platform coupling.',
|
|
||||||
description:
|
|
||||||
'The IA work stays local to the Astro website so trust, product, and legal surfaces remain reviewable without adding auth, API, or admin runtime obligations.',
|
|
||||||
meta: 'Website-only contract',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const homeProofBlocks: CalloutContent[] = [
|
export const homeProgressTeaser: ProgressTeaserContent = {
|
||||||
{
|
title: 'Product movement you can verify.',
|
||||||
eyebrow: 'First read',
|
|
||||||
title: 'A serious buyer should understand the product and the route model in one pass.',
|
|
||||||
description:
|
description:
|
||||||
'The site needs a stable answer to what the product is, how trust is handled, and how to continue the evaluation before optional surfaces earn any prominence.',
|
'Real progress shows up as dated changelog entries, not vague promises. Check the changelog for the latest updates.',
|
||||||
tone: 'accent',
|
entries: [],
|
||||||
|
cta: {
|
||||||
|
href: '/changelog',
|
||||||
|
label: 'Read the full changelog',
|
||||||
},
|
},
|
||||||
{
|
};
|
||||||
eyebrow: 'Trust boundary',
|
|
||||||
title: 'Trust claims should live on one explicit surface instead of leaking across marketing copy.',
|
export const homeCtaSection = {
|
||||||
|
eyebrow: 'Next step',
|
||||||
|
title: 'Move from first read into a working session.',
|
||||||
description:
|
description:
|
||||||
'Claims about isolation, operating discipline, access handling, or hosting must route back to a dedicated trust page that supports and bounds them clearly.',
|
'Once you understand the product model, the trust posture, and the progress record, the next step is a conversation about your governance reality.',
|
||||||
},
|
primary: {
|
||||||
{
|
href: '/contact',
|
||||||
eyebrow: 'No placeholder prestige pages',
|
label: 'Request a working session',
|
||||||
title: 'Leave docs, pricing, and resources out of the spotlight until they are real.',
|
} as CtaLink,
|
||||||
description:
|
secondary: {
|
||||||
'The public story stays more credible when optional hubs remain unpublished and visible progress comes from the changelog instead of speculative route growth.',
|
href: '/product',
|
||||||
tone: 'subtle',
|
label: 'See the product model',
|
||||||
},
|
variant: 'secondary',
|
||||||
];
|
} as CtaLink,
|
||||||
|
};
|
||||||
|
|
||||||
export const homeEcosystem: IntegrationEntry[] = [
|
export const homeEcosystem: IntegrationEntry[] = [
|
||||||
{
|
{
|
||||||
category: 'Microsoft',
|
category: 'Microsoft',
|
||||||
name: 'Microsoft Graph',
|
name: 'Microsoft Graph',
|
||||||
summary: 'Graph-backed inventory and restore direction without implying that the public website depends on live tenant access.',
|
summary: 'Graph-backed inventory and restore without implying the public website depends on live tenant access.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: 'Identity',
|
category: 'Identity',
|
||||||
name: 'Entra ID',
|
name: 'Entra ID',
|
||||||
summary: 'Identity context matters where change control, tenant access, and review posture intersect.',
|
summary: 'Identity context where change control, tenant access, and review posture intersect.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: 'Endpoint',
|
category: 'Endpoint',
|
||||||
name: 'Intune',
|
name: 'Intune',
|
||||||
summary: 'Configuration state, backup, restore posture, and drift visibility stay central to the public product story.',
|
summary: 'Configuration state, backup, restore posture, and drift visibility stay central to the product story.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: 'Governance',
|
category: 'Governance',
|
||||||
name: 'Review workflows',
|
name: 'Review workflows',
|
||||||
summary: 'Exceptions, evidence, and reviews stay connected to operational reality instead of becoming detached reporting artifacts.',
|
summary: 'Exceptions, evidence, and reviews stay connected to operational reality.',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
17
apps/website/src/lib/changelog.ts
Normal file
17
apps/website/src/lib/changelog.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { getCollection } from 'astro:content';
|
||||||
|
|
||||||
|
import type { ProgressTeaserEntry } from '@/types/site';
|
||||||
|
|
||||||
|
export async function getRecentChangelogEntries(limit = 3): Promise<ProgressTeaserEntry[]> {
|
||||||
|
const changelog = await getCollection('changelog');
|
||||||
|
|
||||||
|
return changelog
|
||||||
|
.filter((entry) => entry.data.draft !== true)
|
||||||
|
.sort((a, b) => b.data.publishedAt.getTime() - a.data.publishedAt.getTime())
|
||||||
|
.slice(0, limit)
|
||||||
|
.map((entry) => ({
|
||||||
|
date: entry.data.publishedAt,
|
||||||
|
description: entry.data.description,
|
||||||
|
title: entry.data.title,
|
||||||
|
}));
|
||||||
|
}
|
||||||
@ -1,31 +1,36 @@
|
|||||||
---
|
---
|
||||||
import Callout from '@/components/content/Callout.astro';
|
|
||||||
import PageShell from '@/components/layout/PageShell.astro';
|
import PageShell from '@/components/layout/PageShell.astro';
|
||||||
import Container from '@/components/primitives/Container.astro';
|
import CapabilityGrid from '@/components/sections/CapabilityGrid.astro';
|
||||||
import Grid from '@/components/primitives/Grid.astro';
|
|
||||||
import Section from '@/components/primitives/Section.astro';
|
|
||||||
import SectionHeader from '@/components/primitives/SectionHeader.astro';
|
|
||||||
import CTASection from '@/components/sections/CTASection.astro';
|
import CTASection from '@/components/sections/CTASection.astro';
|
||||||
import FeatureGrid from '@/components/sections/FeatureGrid.astro';
|
|
||||||
import LogoStrip from '@/components/sections/LogoStrip.astro';
|
import LogoStrip from '@/components/sections/LogoStrip.astro';
|
||||||
|
import OutcomeSection from '@/components/sections/OutcomeSection.astro';
|
||||||
import PageHero from '@/components/sections/PageHero.astro';
|
import PageHero from '@/components/sections/PageHero.astro';
|
||||||
|
import ProgressTeaser from '@/components/sections/ProgressTeaser.astro';
|
||||||
|
import TrustGrid from '@/components/sections/TrustGrid.astro';
|
||||||
|
import PrimaryCTA from '@/components/content/PrimaryCTA.astro';
|
||||||
|
import Container from '@/components/primitives/Container.astro';
|
||||||
|
import Section from '@/components/primitives/Section.astro';
|
||||||
|
import { getRecentChangelogEntries } from '@/lib/changelog';
|
||||||
import {
|
import {
|
||||||
|
homeCapabilities,
|
||||||
|
homeCtaSection,
|
||||||
homeEcosystem,
|
homeEcosystem,
|
||||||
homeHero,
|
homeHero,
|
||||||
homeMetrics,
|
homeOutcome,
|
||||||
homePillars,
|
homeProgressTeaser,
|
||||||
homeProofBlocks,
|
|
||||||
homeSeo,
|
homeSeo,
|
||||||
|
homeTrustSignals,
|
||||||
} from '@/content/pages/home';
|
} from '@/content/pages/home';
|
||||||
|
|
||||||
|
const recentChangelog = await getRecentChangelogEntries(3);
|
||||||
|
const progressContent = {
|
||||||
|
...homeProgressTeaser,
|
||||||
|
entries: recentChangelog.length > 0 ? recentChangelog : homeProgressTeaser.entries,
|
||||||
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageShell currentPath="/" title={homeSeo.title} description={homeSeo.description}>
|
<PageShell currentPath="/" title={homeSeo.title} description={homeSeo.description}>
|
||||||
<PageHero
|
<PageHero hero={homeHero} />
|
||||||
hero={homeHero}
|
|
||||||
metrics={homeMetrics}
|
|
||||||
calloutTitle="Governance of record for Microsoft tenant operations."
|
|
||||||
calloutDescription="The public story positions TenantAtlas as a trust-first system for version truth, safer restore posture, drift visibility, evidence, and review support."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<LogoStrip
|
<LogoStrip
|
||||||
eyebrow="Ecosystem fit"
|
eyebrow="Ecosystem fit"
|
||||||
@ -33,33 +38,53 @@ import {
|
|||||||
items={homeEcosystem}
|
items={homeEcosystem}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FeatureGrid
|
<OutcomeSection content={homeOutcome} />
|
||||||
eyebrow="Core route jobs"
|
|
||||||
title="Understand the product, the trust posture, and the next step without route sprawl."
|
<CapabilityGrid
|
||||||
description="Home should explain the operating model, show where trust lives, and surface visible product progress without pushing buyers into placeholder routes."
|
eyebrow="What TenantAtlas covers"
|
||||||
items={homePillars}
|
title="A connected product model, not a feature wall."
|
||||||
|
description="TenantAtlas groups backup, restore, inventory, drift, and governance into connected clusters instead of listing isolated features."
|
||||||
|
items={homeCapabilities}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Section tone="muted" density="base" layer="3">
|
<div data-section="trust">
|
||||||
<Container width="wide">
|
<TrustGrid
|
||||||
<div class="space-y-8">
|
eyebrow="Trust posture"
|
||||||
<SectionHeader
|
title={homeTrustSignals.title}
|
||||||
eyebrow="Public proof"
|
description={homeTrustSignals.description}
|
||||||
title="Answer the next question before a visitor has to hunt for another top-level route."
|
items={homeTrustSignals.signals}
|
||||||
description="Why this category matters, where trust claims live, and why the changelog exists should all be obvious from the first read."
|
|
||||||
/>
|
/>
|
||||||
<Grid cols="3">
|
<Section density="compact">
|
||||||
{homeProofBlocks.map((block) => <Callout content={block} />)}
|
<Container width="wide">
|
||||||
</Grid>
|
<div class="flex justify-center">
|
||||||
|
<PrimaryCTA cta={{ href: '/trust', label: 'Review the full trust posture', variant: 'secondary' }} />
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</Section>
|
</Section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{progressContent.entries.length > 0 && (
|
||||||
|
<ProgressTeaser content={progressContent} />
|
||||||
|
)}
|
||||||
|
{progressContent.entries.length === 0 && (
|
||||||
|
<Section layer="2" data-section="progress">
|
||||||
|
<Container width="wide">
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="text-[var(--color-copy)]">
|
||||||
|
Follow product progress on the <a href="/changelog" class="text-link font-semibold">changelog</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div data-section="cta">
|
||||||
<CTASection
|
<CTASection
|
||||||
eyebrow="Next step"
|
eyebrow={homeCtaSection.eyebrow}
|
||||||
title="Move from first read into product detail, trust review, or a working session."
|
title={homeCtaSection.title}
|
||||||
description="From Home, a serious buyer should be able to inspect the product model, verify public progress, or reach the contact path without guessing where to go next."
|
description={homeCtaSection.description}
|
||||||
primary={{ href: '/changelog', label: 'Read the changelog' }}
|
primary={homeCtaSection.primary}
|
||||||
secondary={{ href: '/contact', label: 'Start the working session', variant: 'secondary' }}
|
secondary={homeCtaSection.secondary}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</PageShell>
|
</PageShell>
|
||||||
|
|||||||
@ -99,8 +99,10 @@ export interface HeroContent {
|
|||||||
eyebrow: string;
|
eyebrow: string;
|
||||||
highlights?: string[];
|
highlights?: string[];
|
||||||
primaryCta: CtaLink;
|
primaryCta: CtaLink;
|
||||||
|
productVisual?: HeroVisualContent;
|
||||||
secondaryCta?: CtaLink;
|
secondaryCta?: CtaLink;
|
||||||
title: string;
|
title: string;
|
||||||
|
trustSubclaims?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MetricItem {
|
export interface MetricItem {
|
||||||
@ -156,6 +158,51 @@ export interface LegalSection {
|
|||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HeroVisualContent {
|
||||||
|
alt: string;
|
||||||
|
src: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OutcomeItemContent {
|
||||||
|
description: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OutcomeSectionContent {
|
||||||
|
audienceBias?: string;
|
||||||
|
description: string;
|
||||||
|
outcomes: OutcomeItemContent[];
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CapabilityClusterContent {
|
||||||
|
capabilities: string[];
|
||||||
|
description: string;
|
||||||
|
href?: string;
|
||||||
|
meta?: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrustSignalGroupContent {
|
||||||
|
description: string;
|
||||||
|
signals: TrustPrincipleContent[];
|
||||||
|
supportRoute: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProgressTeaserEntry {
|
||||||
|
date: Date;
|
||||||
|
description: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProgressTeaserContent {
|
||||||
|
cta: CtaLink;
|
||||||
|
description: string;
|
||||||
|
entries: ProgressTeaserEntry[];
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface VisualFoundationContract {
|
export interface VisualFoundationContract {
|
||||||
accessibilityBaseline: readonly string[];
|
accessibilityBaseline: readonly string[];
|
||||||
ctaHierarchy: readonly ButtonVariant[];
|
ctaHierarchy: readonly ButtonVariant[];
|
||||||
|
|||||||
@ -4,8 +4,12 @@ import {
|
|||||||
expectCtaHierarchy,
|
expectCtaHierarchy,
|
||||||
expectDisclosureLayer,
|
expectDisclosureLayer,
|
||||||
expectFooterLinks,
|
expectFooterLinks,
|
||||||
|
expectHomepageSectionOrder,
|
||||||
|
expectMobileReadability,
|
||||||
expectNavigationVsCtaDifferentiation,
|
expectNavigationVsCtaDifferentiation,
|
||||||
|
expectOnwardRouteReachable,
|
||||||
expectPageFamily,
|
expectPageFamily,
|
||||||
|
expectProductNearVisual,
|
||||||
expectPrimaryNavigation,
|
expectPrimaryNavigation,
|
||||||
expectShell,
|
expectShell,
|
||||||
visitPage,
|
visitPage,
|
||||||
@ -22,14 +26,8 @@ test('home uses the landing foundation to explain the product category with one
|
|||||||
await expectPrimaryNavigation(page);
|
await expectPrimaryNavigation(page);
|
||||||
await expectNavigationVsCtaDifferentiation(page);
|
await expectNavigationVsCtaDifferentiation(page);
|
||||||
await expectFooterLinks(page);
|
await expectFooterLinks(page);
|
||||||
await expect(
|
await expectCtaHierarchy(page, 'Request a working session', 'See the product model');
|
||||||
page.getByRole('heading', {
|
await expect(page.getByRole('main').getByRole('link', { name: 'Request a working session' }).first()).toBeVisible();
|
||||||
name: 'Understand the product, the trust posture, and the next step without route sprawl.',
|
|
||||||
}),
|
|
||||||
).toBeVisible();
|
|
||||||
await expectCtaHierarchy(page, 'See the product model', 'Review the trust posture');
|
|
||||||
await expect(page.getByRole('main').getByRole('link', { name: 'Read the changelog' }).first()).toBeVisible();
|
|
||||||
await expect(page.getByRole('main').getByRole('link', { name: 'Start the working session' }).first()).toBeVisible();
|
|
||||||
|
|
||||||
const skipLink = page.getByRole('link', { name: 'Skip to content' });
|
const skipLink = page.getByRole('link', { name: 'Skip to content' });
|
||||||
|
|
||||||
@ -37,6 +35,70 @@ test('home uses the landing foundation to explain the product category with one
|
|||||||
await expect(skipLink).toBeFocused();
|
await expect(skipLink).toBeFocused();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('homepage hero explains the product with a product-near visual and outcome framing', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await visitPage(page, '/');
|
||||||
|
await expectProductNearVisual(page);
|
||||||
|
await expect(page.locator('[data-section="outcome"]')).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('heading', { name: /calmer operations|governance pain|operational risk/i }).first(),
|
||||||
|
).toBeVisible();
|
||||||
|
await expectCtaHierarchy(page, 'Request a working session', 'See the product model');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('homepage maintains required section order: outcome before capability before trust before progress before cta', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await visitPage(page, '/');
|
||||||
|
await expectHomepageSectionOrder(page, ['outcome', 'capability', 'trust', 'progress', 'cta']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('homepage shows grouped capability clusters instead of a feature wall', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await visitPage(page, '/');
|
||||||
|
await expect(page.locator('[data-section="capability"]')).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('heading', { name: /product model|what TenantAtlas covers/i }),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('homepage shows explicit trust signals before the final CTA', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await visitPage(page, '/');
|
||||||
|
await expect(page.locator('[data-section="trust"]')).toBeVisible();
|
||||||
|
await expectOnwardRouteReachable(page, ['/trust']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('homepage shows dated progress signals before the final CTA', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await visitPage(page, '/');
|
||||||
|
await expect(page.locator('[data-section="progress"]')).toBeVisible();
|
||||||
|
await expectOnwardRouteReachable(page, ['/changelog']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('homepage routes into Product, Trust, Changelog, and Contact', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await visitPage(page, '/');
|
||||||
|
await expectOnwardRouteReachable(page, ['/product', '/trust', '/changelog', '/contact']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('homepage mobile', () => {
|
||||||
|
test.use({ viewport: { width: 390, height: 844 } });
|
||||||
|
|
||||||
|
test('homepage remains readable on narrow screens', async ({ page }) => {
|
||||||
|
await visitPage(page, '/');
|
||||||
|
await expectMobileReadability(page);
|
||||||
|
await expect(page.locator('[data-section="outcome"]')).toBeVisible();
|
||||||
|
await expect(page.locator('[data-section="capability"]')).toBeVisible();
|
||||||
|
await expect(page.locator('[data-section="trust"]')).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('product keeps the connected operating model readable without collapsing into a feature list', async ({
|
test('product keeps the connected operating model readable without collapsing into a feature list', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@ -93,3 +93,54 @@ export async function expectNavigationVsCtaDifferentiation(page: Page): Promise<
|
|||||||
await expect(header.locator('[data-nav-link]').first()).toBeVisible();
|
await expect(header.locator('[data-nav-link]').first()).toBeVisible();
|
||||||
await expect(header.locator('[data-cta-weight="secondary"]').first()).toBeVisible();
|
await expect(header.locator('[data-cta-weight="secondary"]').first()).toBeVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function expectHomepageSectionOrder(page: Page, sections: string[]): Promise<void> {
|
||||||
|
const main = page.getByRole('main');
|
||||||
|
const sectionElements = main.locator('[data-section]');
|
||||||
|
const count = await sectionElements.count();
|
||||||
|
const actual: string[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const name = await sectionElements.nth(i).getAttribute('data-section');
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
actual.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < sections.length; i++) {
|
||||||
|
expect(actual.indexOf(sections[i]), `Section "${sections[i]}" should appear in order`).toBeGreaterThanOrEqual(0);
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
expect(
|
||||||
|
actual.indexOf(sections[i]),
|
||||||
|
`Section "${sections[i]}" should appear after "${sections[i - 1]}"`,
|
||||||
|
).toBeGreaterThan(actual.indexOf(sections[i - 1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function expectProductNearVisual(page: Page): Promise<void> {
|
||||||
|
const main = page.getByRole('main');
|
||||||
|
|
||||||
|
await expect(main.locator('[data-hero-visual] img, [data-hero-visual]').first()).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function expectMobileReadability(page: Page): Promise<void> {
|
||||||
|
const main = page.getByRole('main');
|
||||||
|
|
||||||
|
await expect(main).toBeVisible();
|
||||||
|
await expect(page.getByRole('banner')).toBeVisible();
|
||||||
|
await expect(page.getByRole('contentinfo')).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function expectOnwardRouteReachable(page: Page, routes: string[]): Promise<void> {
|
||||||
|
const main = page.getByRole('main');
|
||||||
|
|
||||||
|
for (const route of routes) {
|
||||||
|
await expect(
|
||||||
|
main.locator(`a[href="${route}"]`).first(),
|
||||||
|
`Route "${route}" should be reachable from main content`,
|
||||||
|
).toBeVisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -17,8 +17,8 @@ test('representative pages route CTA, badge, surface, and input semantics throug
|
|||||||
await expectPageFamily(page, 'landing');
|
await expectPageFamily(page, 'landing');
|
||||||
await expectPrimaryNavigation(page);
|
await expectPrimaryNavigation(page);
|
||||||
await expectNavigationVsCtaDifferentiation(page);
|
await expectNavigationVsCtaDifferentiation(page);
|
||||||
await expectCtaHierarchy(page, 'See the product model', 'Review the trust posture');
|
await expectCtaHierarchy(page, 'Request a working session', 'See the product model');
|
||||||
await expect(page.locator('[data-interaction="button"]').filter({ hasText: 'See the product model' }).first()).toBeVisible();
|
await expect(page.locator('[data-interaction="button"]').filter({ hasText: 'Request a working session' }).first()).toBeVisible();
|
||||||
await expect(page.locator('[data-badge-tone]').first()).toBeVisible();
|
await expect(page.locator('[data-badge-tone]').first()).toBeVisible();
|
||||||
|
|
||||||
await visitPage(page, '/trust');
|
await visitPage(page, '/trust');
|
||||||
|
|||||||
35
specs/216-homepage-structure/checklists/requirements.md
Normal file
35
specs/216-homepage-structure/checklists/requirements.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Specification Quality Checklist: Website Homepage Structure & Section Model
|
||||||
|
|
||||||
|
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||||
|
**Created**: 2026-04-19
|
||||||
|
**Feature**: [spec.md](../spec.md)
|
||||||
|
|
||||||
|
## Content Quality
|
||||||
|
|
||||||
|
- [x] No implementation details (languages, frameworks, APIs)
|
||||||
|
- [x] Focused on user value and business needs
|
||||||
|
- [x] Written for non-technical stakeholders
|
||||||
|
- [x] All mandatory sections completed
|
||||||
|
|
||||||
|
## Requirement Completeness
|
||||||
|
|
||||||
|
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||||
|
- [x] Requirements are testable and unambiguous
|
||||||
|
- [x] Success criteria are measurable
|
||||||
|
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||||
|
- [x] All acceptance scenarios are defined
|
||||||
|
- [x] Edge cases are identified
|
||||||
|
- [x] Scope is clearly bounded
|
||||||
|
- [x] Dependencies and assumptions identified
|
||||||
|
|
||||||
|
## Feature Readiness
|
||||||
|
|
||||||
|
- [x] All functional requirements have clear acceptance criteria
|
||||||
|
- [x] User scenarios cover primary flows
|
||||||
|
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||||
|
- [x] No implementation details leak into specification
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Validation pass 1 completed successfully.
|
||||||
|
- No clarification questions were required because scope, required sections, routing, and exclusions were explicitly defined in the input.
|
||||||
@ -0,0 +1,128 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: TenantAtlas Homepage Surface Contract
|
||||||
|
version: 0.1.0
|
||||||
|
summary: Structural contract for the `apps/website` homepage in Spec 216.
|
||||||
|
description: >-
|
||||||
|
This contract defines the public HTML routes that participate in the
|
||||||
|
homepage journey for Spec 216. The homepage remains a static Astro surface
|
||||||
|
and must route visitors into Product, Trust, Changelog, and Contact while
|
||||||
|
satisfying the required homepage section model.
|
||||||
|
servers:
|
||||||
|
- url: http://localhost:{port}
|
||||||
|
description: Local Astro development or preview server
|
||||||
|
variables:
|
||||||
|
port:
|
||||||
|
default: '4321'
|
||||||
|
tags:
|
||||||
|
- name: Homepage Journey
|
||||||
|
description: Public HTML routes used by the homepage structure contract
|
||||||
|
paths:
|
||||||
|
/:
|
||||||
|
get:
|
||||||
|
tags: [Homepage Journey]
|
||||||
|
operationId: getHomepage
|
||||||
|
summary: Homepage
|
||||||
|
description: >-
|
||||||
|
Product-near homepage that positions the product, explains outcomes,
|
||||||
|
groups the product model, shows bounded trust and dated progress
|
||||||
|
signals, and ends in one clear next-step CTA.
|
||||||
|
x-tenantatlas-homepage-structure:
|
||||||
|
requiredBlocks:
|
||||||
|
- header
|
||||||
|
- hero
|
||||||
|
- outcome
|
||||||
|
- capability
|
||||||
|
- trust
|
||||||
|
- progress
|
||||||
|
- cta
|
||||||
|
- footer
|
||||||
|
orderedNarrative:
|
||||||
|
- hero
|
||||||
|
- outcome
|
||||||
|
- capability
|
||||||
|
- trust
|
||||||
|
- progress
|
||||||
|
- cta
|
||||||
|
primaryCtaTargets:
|
||||||
|
- /contact
|
||||||
|
- /demo
|
||||||
|
secondaryCtaTargets:
|
||||||
|
- /product
|
||||||
|
- /trust
|
||||||
|
- /changelog
|
||||||
|
onwardRoutes:
|
||||||
|
- /product
|
||||||
|
- /trust
|
||||||
|
- /changelog
|
||||||
|
- /contact
|
||||||
|
forbiddenPatterns:
|
||||||
|
- template-first-saas
|
||||||
|
- abstract-only-homepage
|
||||||
|
- feature-wall
|
||||||
|
- fake-trust
|
||||||
|
- multi-primary-cta
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Homepage HTML
|
||||||
|
content:
|
||||||
|
text/html:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HtmlDocument'
|
||||||
|
/product:
|
||||||
|
get:
|
||||||
|
tags: [Homepage Journey]
|
||||||
|
operationId: getHomepageProductTarget
|
||||||
|
summary: Product target route
|
||||||
|
description: Deeper product-model route linked from homepage hero or capability sections.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Product page HTML
|
||||||
|
content:
|
||||||
|
text/html:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HtmlDocument'
|
||||||
|
/trust:
|
||||||
|
get:
|
||||||
|
tags: [Homepage Journey]
|
||||||
|
operationId: getHomepageTrustTarget
|
||||||
|
summary: Trust target route
|
||||||
|
description: Bounded trust and credibility route linked from homepage trust cues and CTA paths.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Trust page HTML
|
||||||
|
content:
|
||||||
|
text/html:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HtmlDocument'
|
||||||
|
/changelog:
|
||||||
|
get:
|
||||||
|
tags: [Homepage Journey]
|
||||||
|
operationId: getHomepageChangelogTarget
|
||||||
|
summary: Changelog target route
|
||||||
|
description: Dated progress route linked from the homepage progress section.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Changelog page HTML
|
||||||
|
content:
|
||||||
|
text/html:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HtmlDocument'
|
||||||
|
/contact:
|
||||||
|
get:
|
||||||
|
tags: [Homepage Journey]
|
||||||
|
operationId: getHomepageContactTarget
|
||||||
|
summary: Contact target route
|
||||||
|
description: Primary conversion route used by the homepage until a distinct public `/demo` route exists.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Contact page HTML
|
||||||
|
content:
|
||||||
|
text/html:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HtmlDocument'
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
HtmlDocument:
|
||||||
|
type: string
|
||||||
|
description: Server-rendered static HTML document
|
||||||
138
specs/216-homepage-structure/data-model.md
Normal file
138
specs/216-homepage-structure/data-model.md
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# Data Model: Website Homepage Structure & Section Model
|
||||||
|
|
||||||
|
This feature introduces no database schema and no platform-side persistence. The model is file- and route-based inside `apps/website` and defines how the homepage composes required narrative blocks, proof signals, and route targets.
|
||||||
|
|
||||||
|
## 1. Homepage Composition
|
||||||
|
|
||||||
|
Represents the public homepage at `/` as one ordered narrative contract.
|
||||||
|
|
||||||
|
| Field | Type | Description | Rules |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `path` | static string | Canonical homepage route | Must remain `/` |
|
||||||
|
| `seo` | `PageSeo` | Title, description, canonical path metadata | Required |
|
||||||
|
| `requiredBlocks` | ordered list | Functional homepage blocks | Must include header, hero, outcome, capability, trust, progress, CTA, footer |
|
||||||
|
| `primaryConversionTarget` | route | Dominant next-step route | Defaults to `/contact` until a distinct public `/demo` route exists |
|
||||||
|
| `secondaryRoutes` | route list | Deeper exploration targets | Limited to `/product`, `/trust`, `/changelog` |
|
||||||
|
| `optionalBlocks` | list | Supporting context such as ecosystem, FAQ, or use-case spotlight | May appear only if they do not displace required blocks |
|
||||||
|
|
||||||
|
**Relationships**:
|
||||||
|
- Composed from homepage content exports in `src/content/pages/home.ts`
|
||||||
|
- Uses canonical route/navigation truth from `src/lib/site.ts`
|
||||||
|
- May derive proof content from `src/content/pages/trust.ts` and the changelog collection
|
||||||
|
|
||||||
|
**Validation rules**:
|
||||||
|
- All required blocks must be present exactly once as functions, even if visually combined
|
||||||
|
- Trust and progress must appear before the lower-page CTA block
|
||||||
|
- Optional blocks must not replace or bury required blocks
|
||||||
|
|
||||||
|
## 2. Hero Block
|
||||||
|
|
||||||
|
Represents the first explanatory block on the homepage.
|
||||||
|
|
||||||
|
| Field | Type | Description | Rules |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `eyebrow` | string | Short orienting label | Required |
|
||||||
|
| `title` | string | Positioning headline | Required |
|
||||||
|
| `description` | string | Supporting problem and product framing | Required |
|
||||||
|
| `primaryCta` | `CtaLink` | Main action from the hero | Exactly one primary CTA |
|
||||||
|
| `secondaryCta` | `CtaLink` | Deeper exploration route | At least one secondary deepening CTA on the homepage |
|
||||||
|
| `highlights` | string list | Short proof or focus points | Optional |
|
||||||
|
| `productVisual` | logical asset reference | Product-near screenshot or UI-adjacent visual | Must be product-near, not abstract-only |
|
||||||
|
| `trustSubclaims` | string list | Narrow supporting trust cues | Optional and only allowed when supportable by `/trust` |
|
||||||
|
|
||||||
|
**Validation rules**:
|
||||||
|
- Primary and secondary CTAs must not compete as equal primary actions
|
||||||
|
- Visual must communicate product truth or credible UI structure
|
||||||
|
- Trust subclaims cannot overstate compliance or hosting claims
|
||||||
|
|
||||||
|
## 3. Outcome Section
|
||||||
|
|
||||||
|
Explains why the product matters in buyer-oriented language.
|
||||||
|
|
||||||
|
| Field | Type | Description | Rules |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `title` | string | Outcome framing headline | Required |
|
||||||
|
| `description` | string | Problem relevance and operational impact | Required |
|
||||||
|
| `outcomes` | item list | Buyer-visible improvements, frictions removed, or use-case outcomes | Required |
|
||||||
|
| `audienceBias` | label or note | Primary audience signal such as MSP, governance, or enterprise operations | Optional |
|
||||||
|
|
||||||
|
**Validation rules**:
|
||||||
|
- Must talk in outcomes, operational improvements, or frictions reduced
|
||||||
|
- Must not devolve into feature naming or route explanations
|
||||||
|
|
||||||
|
## 4. Capability Cluster
|
||||||
|
|
||||||
|
Represents grouped product-model coverage on the homepage.
|
||||||
|
|
||||||
|
| Field | Type | Description | Rules |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `title` | string | Cluster title | Required |
|
||||||
|
| `description` | string | Short explanation of the cluster | Required |
|
||||||
|
| `capabilities` | string list | Covered product areas | Must express grouped capabilities, not one-card-per-feature sprawl |
|
||||||
|
| `href` | route | Deeper route for details | Normally `/product` |
|
||||||
|
| `meta` | string | Compact supporting label | Optional |
|
||||||
|
|
||||||
|
**Required coverage across all clusters**:
|
||||||
|
- Backup
|
||||||
|
- Restore
|
||||||
|
- Version history
|
||||||
|
- Auditability and evidence
|
||||||
|
- Inventory and drift visibility
|
||||||
|
- Governance, findings, exceptions, or review work
|
||||||
|
|
||||||
|
**Validation rules**:
|
||||||
|
- Clusters must create a readable hierarchy
|
||||||
|
- The homepage must explain the product model without acting as full documentation
|
||||||
|
|
||||||
|
## 5. Trust Signal Group
|
||||||
|
|
||||||
|
Represents the explicit homepage trust block.
|
||||||
|
|
||||||
|
| Field | Type | Description | Rules |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `title` | string | Trust block heading | Required |
|
||||||
|
| `description` | string | Why trust matters on the homepage | Required |
|
||||||
|
| `signals` | `TrustPrincipleContent[]` or equivalent | Bounded trust principles or proof cards | Required |
|
||||||
|
| `supportRoute` | route | Deeper trust destination | Must be `/trust` |
|
||||||
|
|
||||||
|
**Validation rules**:
|
||||||
|
- Signals must be substantiated and bounded
|
||||||
|
- No invented badges, fake certifications, or sweeping compliance promises
|
||||||
|
- Must appear before the final CTA block
|
||||||
|
|
||||||
|
## 6. Progress Teaser
|
||||||
|
|
||||||
|
Represents visible product movement on the homepage.
|
||||||
|
|
||||||
|
| Field | Type | Description | Rules |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `entries` | changelog entry list | Recent dated product updates | Derived from published changelog collection entries |
|
||||||
|
| `title` | string | Progress block title | Required |
|
||||||
|
| `description` | string | Explains why progress is shown | Required |
|
||||||
|
| `cta` | `CtaLink` | Route to full changelog | Must target `/changelog` |
|
||||||
|
|
||||||
|
**Validation rules**:
|
||||||
|
- Entries must be dated and publicly visible
|
||||||
|
- The block must feel factual, not like a marketing news feed
|
||||||
|
|
||||||
|
## 7. CTA Transition Block
|
||||||
|
|
||||||
|
Represents the lower-page call to action after clarity, trust, and progress have been established.
|
||||||
|
|
||||||
|
| Field | Type | Description | Rules |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `title` | string | Final CTA heading | Required |
|
||||||
|
| `description` | string | What the next step is and why | Required |
|
||||||
|
| `primaryCta` | `CtaLink` | Dominant next-step action | Must target `/contact` or `/demo`; defaults to `/contact` today |
|
||||||
|
| `secondaryCta` | `CtaLink` | Optional deeper route | Must not compete as another primary action |
|
||||||
|
|
||||||
|
**Validation rules**:
|
||||||
|
- Must not introduce multiple equally strong primary conversion actions
|
||||||
|
- Must follow trust and progress blocks in the homepage order
|
||||||
|
|
||||||
|
## Derived State and Availability
|
||||||
|
|
||||||
|
- No independent state machine is added by this feature.
|
||||||
|
- Changelog teaser visibility is derived from the published changelog collection (`draft: false`).
|
||||||
|
- Optional route visibility remains derived from `getSurfaceAvailability()` and canonical route definitions in `src/lib/site.ts`.
|
||||||
|
- If a real screenshot is unavailable, hero visual readiness falls back to a credible UI-near visual, but the hero still must satisfy the product-near rule.
|
||||||
256
specs/216-homepage-structure/plan.md
Normal file
256
specs/216-homepage-structure/plan.md
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
# Implementation Plan: Website Homepage Structure & Section Model
|
||||||
|
|
||||||
|
**Branch**: `216-homepage-structure` | **Date**: 2026-04-19 | **Spec**: `specs/216-homepage-structure/spec.md`
|
||||||
|
**Input**: Feature specification from `specs/216-homepage-structure/spec.md`
|
||||||
|
|
||||||
|
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- Rework the `apps/website` homepage into the explicit section flow required by Spec 216: hero, outcome framing, capability model, trust, progress, CTA, while preserving existing header and footer shells.
|
||||||
|
- Implement the change by extending the current Astro content-driven homepage model (`src/content/pages/home.ts`) and existing section primitives instead of adding a new section registry or CMS-like composition layer.
|
||||||
|
- Reuse existing Trust and Changelog truth for homepage proof signals, and validate the result with the current website build proof plus focused Playwright smoke coverage.
|
||||||
|
|
||||||
|
## Technical Context
|
||||||
|
|
||||||
|
**Language/Version**: Astro 6.0.0 templates + TypeScript 5.9.x
|
||||||
|
**Primary Dependencies**: Astro 6, Tailwind CSS v4, local Astro layout/section primitives, Astro content collections, Playwright browser smoke tests
|
||||||
|
**Storage**: Static filesystem content, Astro content collections, and assets under `apps/website/src` and `apps/website/public`; no database
|
||||||
|
**Testing**: `corepack pnpm build:website` plus Playwright smoke coverage in `apps/website/tests/smoke`
|
||||||
|
**Validation Lanes**: fast-feedback
|
||||||
|
**Target Platform**: Static public website for modern desktop and mobile browsers
|
||||||
|
**Project Type**: Web application in a monorepo (`apps/platform` plus `apps/website`)
|
||||||
|
**Performance Goals**: Preserve static HTML output for homepage and linked public routes, keep reading/navigation flows usable without required client-side framework hydration, and keep the primary homepage narrative legible on desktop and mobile
|
||||||
|
**Constraints**: Stay strictly inside `apps/website`; preserve canonical core routes (`/`, `/product`, `/trust`, `/changelog`, `/contact`); keep one dominant primary CTA; hide unsubstantial optional routes; avoid unsupported trust claims and platform runtime coupling
|
||||||
|
**Scale/Scope**: One homepage route, eight required functional blocks, four canonical onward routes, existing shared section components, and a small extension to the current browser smoke suite
|
||||||
|
|
||||||
|
## UI / Surface Guardrail Plan
|
||||||
|
|
||||||
|
- **Guardrail scope**: no operator-facing surface change
|
||||||
|
- **Native vs custom classification summary**: N/A - public Astro website surface only
|
||||||
|
- **Shared-family relevance**: none
|
||||||
|
- **State layers in scope**: page
|
||||||
|
- **Handling modes by drift class or surface**: N/A
|
||||||
|
- **Repository-signal treatment**: report-only
|
||||||
|
- **Special surface test profiles**: N/A
|
||||||
|
- **Required tests or manual smoke**: manual-smoke plus homepage-focused browser smoke coverage
|
||||||
|
- **Exception path and spread control**: none
|
||||||
|
- **Active feature PR close-out entry**: Smoke Coverage
|
||||||
|
|
||||||
|
## Constitution Check
|
||||||
|
|
||||||
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||||
|
|
||||||
|
- Inventory-first / snapshots / Graph contract / deterministic capabilities / RBAC-UX / Filament guardrails: N/A for this feature because all work stays on the public Astro website and changes no `/admin`, `/admin/t/{tenant}/...`, or `/system` runtime surfaces.
|
||||||
|
- Read/write separation: Pass. The homepage remains a static public read surface. No writes, remote calls, queued work, or contact submission backend are introduced in this feature.
|
||||||
|
- Workspace and tenant isolation: Pass. The homepage stays runtime-independent from `apps/platform`, with no shared auth, session, tenant data, or tenant-scoped route behavior.
|
||||||
|
- Data minimization: Pass. The feature only rearranges and extends public content, navigation, and collection-backed teasers inside `apps/website`.
|
||||||
|
- Test governance: Pass. Proof remains in `fast-feedback` through static build output and focused browser smoke coverage, with no database, membership, provider, or heavy-suite defaults.
|
||||||
|
- Proportionality / no premature abstraction: Pass. The implementation extends the existing `home.ts` content module and current section primitives instead of introducing a generic section registry, CMS abstraction, or separate homepage framework.
|
||||||
|
- Persisted truth / new state: Pass. No database artifacts, queues, or independent state machines are added. Homepage progress visibility stays derived from the existing changelog collection.
|
||||||
|
- UI semantics / few layers: Pass. The homepage contract maps directly to existing content objects and section components, with thin additions for missing blocks rather than a presentation meta-layer.
|
||||||
|
|
||||||
|
Status: ✅ No constitution violations identified before research.
|
||||||
|
|
||||||
|
## Test Governance Check
|
||||||
|
|
||||||
|
- **Test purpose / classification by changed surface**: Browser
|
||||||
|
- **Affected validation lanes**: fast-feedback
|
||||||
|
- **Why this lane mix is the narrowest sufficient proof**: The feature changes only public HTML composition, route discoverability, CTA hierarchy, and responsive section visibility. Browser smoke coverage is the narrowest layer that can prove those concerns without introducing backend or heavy end-to-end cost.
|
||||||
|
- **Narrowest proving command(s)**: `corepack pnpm build:website` and `cd apps/website && corepack pnpm exec playwright test`
|
||||||
|
- **Fixture / helper / factory / seed / context cost risks**: none; public routes do not require database, auth, provider, workspace, or tenant setup
|
||||||
|
- **Expensive defaults or shared helper growth introduced?**: no; test updates stay inside the existing `apps/website/tests/smoke` harness
|
||||||
|
- **Heavy-family additions, promotions, or visibility changes**: none
|
||||||
|
- **Surface-class relief / special coverage rule**: N/A
|
||||||
|
- **Closing validation and reviewer handoff**: Re-run the website build and smoke suite after homepage/content changes. Reviewers should verify required homepage block order, clear CTA hierarchy, reachability of `/product`, `/trust`, `/changelog`, and `/contact`, and continued mobile readability without optional-route leakage.
|
||||||
|
- **Budget / baseline / trend follow-up**: none beyond the existing lightweight website smoke runtime
|
||||||
|
- **Review-stop questions**: Does proof stay browser-focused and route-focused? Did the feature accidentally add shared helper cost or client-side runtime coupling? Are optional unpublished routes still hidden from homepage prominence?
|
||||||
|
- **Escalation path**: document-in-feature
|
||||||
|
- **Active feature PR close-out entry**: Smoke Coverage
|
||||||
|
- **Why no dedicated follow-up spec is needed**: Validation remains feature-local to the homepage and the existing website smoke harness. A separate test-governance spec is only needed if the public site later grows interactive flows or broader browser coverage families.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
### Documentation (this feature)
|
||||||
|
|
||||||
|
```text
|
||||||
|
specs/216-homepage-structure/
|
||||||
|
├── plan.md
|
||||||
|
├── research.md
|
||||||
|
├── data-model.md
|
||||||
|
├── quickstart.md
|
||||||
|
├── contracts/
|
||||||
|
│ └── homepage-surface.openapi.yaml
|
||||||
|
└── tasks.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source Code (repository root)
|
||||||
|
|
||||||
|
```text
|
||||||
|
apps/website/
|
||||||
|
├── package.json
|
||||||
|
├── src/
|
||||||
|
│ ├── content.config.ts
|
||||||
|
│ ├── content/
|
||||||
|
│ │ ├── changelog/
|
||||||
|
│ │ └── pages/
|
||||||
|
│ │ ├── changelog.ts
|
||||||
|
│ │ ├── home.ts
|
||||||
|
│ │ ├── product.ts
|
||||||
|
│ │ └── trust.ts
|
||||||
|
│ ├── components/
|
||||||
|
│ │ ├── content/
|
||||||
|
│ │ ├── layout/
|
||||||
|
│ │ ├── primitives/
|
||||||
|
│ │ └── sections/
|
||||||
|
│ │ ├── CTASection.astro
|
||||||
|
│ │ ├── FeatureGrid.astro
|
||||||
|
│ │ ├── LogoStrip.astro
|
||||||
|
│ │ ├── PageHero.astro
|
||||||
|
│ │ └── TrustGrid.astro
|
||||||
|
│ ├── lib/
|
||||||
|
│ │ └── site.ts
|
||||||
|
│ ├── pages/
|
||||||
|
│ │ ├── changelog.astro
|
||||||
|
│ │ ├── contact.astro
|
||||||
|
│ │ ├── index.astro
|
||||||
|
│ │ ├── product.astro
|
||||||
|
│ │ └── trust.astro
|
||||||
|
│ └── types/
|
||||||
|
│ └── site.ts
|
||||||
|
└── tests/
|
||||||
|
└── smoke/
|
||||||
|
├── changelog-core-ia.spec.ts
|
||||||
|
├── home-product.spec.ts
|
||||||
|
└── smoke-helpers.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**Structure Decision**: Keep the feature completely inside `apps/website`, using the existing Astro page/content/component split. Extend `src/content/pages/home.ts`, reuse current section components where possible, and add only the smallest missing homepage composition pieces required by Spec 216.
|
||||||
|
|
||||||
|
## Complexity Tracking
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Proportionality Review
|
||||||
|
|
||||||
|
- **Current operator problem**: The homepage currently explains route jobs and product seriousness only partially, which makes the public first-read weaker than the product truth already available elsewhere on the site.
|
||||||
|
- **Existing structure is insufficient because**: The current homepage composition lacks an explicit outcome section, uses the capability section for information architecture rather than product model explanation, keeps trust too implicit, and surfaces progress only as a CTA target instead of a real signal.
|
||||||
|
- **Narrowest correct implementation**: Recompose the existing homepage route with one explicit outcome block, grouped capability clusters, a dedicated trust block, and a real changelog teaser while reusing current content modules, section primitives, and collection truth.
|
||||||
|
- **Ownership cost created**: Ongoing maintenance of a slightly richer `home.ts` content shape, homepage section ordering, and a few additional browser assertions.
|
||||||
|
- **Alternative intentionally rejected**: A generic section registry or CMS-like homepage builder was rejected because only one homepage route needs this contract now, and the existing Astro content model is already sufficient.
|
||||||
|
- **Release truth**: Current-release truth
|
||||||
|
|
||||||
|
## Phase 0 — Outline & Research (complete)
|
||||||
|
|
||||||
|
- Output: `specs/216-homepage-structure/research.md`
|
||||||
|
- Key decisions captured:
|
||||||
|
- Keep the homepage local to the static Astro website and preserve runtime separation from `apps/platform`.
|
||||||
|
- Extend the current content-driven homepage model instead of adding a new section framework.
|
||||||
|
- Reorder the homepage into the explicit Spec 216 narrative flow while preserving optional supporting context only where it helps clarity.
|
||||||
|
- Reuse existing Trust page truth and changelog collection data for homepage proof signals.
|
||||||
|
- Validate via the current website build proof plus focused Playwright smoke coverage.
|
||||||
|
|
||||||
|
## Phase 1 — Design & Contracts (complete)
|
||||||
|
|
||||||
|
### Data model
|
||||||
|
|
||||||
|
- Output: `specs/216-homepage-structure/data-model.md`
|
||||||
|
- Model remains file- and route-based. No database schema changes are required.
|
||||||
|
|
||||||
|
### Public homepage contract
|
||||||
|
|
||||||
|
- Output: `specs/216-homepage-structure/contracts/homepage-surface.openapi.yaml`
|
||||||
|
- Contract captures the homepage route plus the required onward routes (`/product`, `/trust`, `/changelog`, `/contact`) and the structural rules the homepage must satisfy.
|
||||||
|
|
||||||
|
### Quickstart
|
||||||
|
|
||||||
|
- Output: `specs/216-homepage-structure/quickstart.md`
|
||||||
|
- Quickstart covers local development, homepage verification points, build proof, and smoke-test execution.
|
||||||
|
|
||||||
|
### Agent context update
|
||||||
|
|
||||||
|
- Completed via `.specify/scripts/bash/update-agent-context.sh copilot` after plan artifacts were generated.
|
||||||
|
|
||||||
|
### Constitution re-check (post-design)
|
||||||
|
|
||||||
|
- ✅ The plan remains website-only and static, with no platform-runtime coupling.
|
||||||
|
- ✅ No new persistence, state machines, background operations, or auth flows are introduced.
|
||||||
|
- ✅ The chosen shape reuses existing Astro content modules and components instead of adding speculative abstraction.
|
||||||
|
- ✅ Validation remains cheap, local, and aligned with the current website smoke harness.
|
||||||
|
|
||||||
|
## Phase 2 — Implementation Plan (next)
|
||||||
|
|
||||||
|
### Story 1 (P1): Understand the Product Fast
|
||||||
|
|
||||||
|
- Update `apps/website/src/pages/index.astro` so the homepage first-read flow establishes the hero, product-near visual, outcome framing, and clear CTA hierarchy required for immediate product understanding.
|
||||||
|
- Preserve the current header, footer, and existing hero shell while leaving clean insertion points for the later capability, trust, and progress blocks owned by the following story slices.
|
||||||
|
- Extend the homepage hero so it supports a product-near visual and optional bounded trust subclaims when they are factually supportable and traceable to `/trust`.
|
||||||
|
- Keep any optional supporting context, such as the ecosystem strip, only if it reinforces clarity instead of displacing the first-read story.
|
||||||
|
- Tests / validation:
|
||||||
|
- Extend `apps/website/tests/smoke/home-product.spec.ts` to assert the required blocks are visible in order, that the hero exposes a product-near visual, and that CTA hierarchy remains singular and clear.
|
||||||
|
- Re-run `corepack pnpm build:website`.
|
||||||
|
|
||||||
|
### Story 2 (P2): Evaluate Product Model and Trust Without a Feature Wall
|
||||||
|
|
||||||
|
- Refactor the remaining mid-page homepage content in `apps/website/src/content/pages/home.ts` so it explains grouped product capabilities instead of homepage information architecture.
|
||||||
|
- Add the smallest missing homepage content objects needed for grouped capability clusters, explicit trust proof, and dated progress signaling.
|
||||||
|
- Reuse `trust.ts` and the `TrustGrid` section to place a dedicated homepage trust block ahead of the final CTA.
|
||||||
|
- Add a homepage progress teaser backed by the changelog collection or a thin helper derived from it, keeping the signal dated and routed to `/changelog`.
|
||||||
|
- Ensure homepage claims remain bounded and traceable to `/trust`, with no fake proof systems or unsupported badges.
|
||||||
|
- Tests / validation:
|
||||||
|
- Update existing homepage smoke assertions for revised headings, grouped capability clusters, explicit trust visibility, and dated progress signaling.
|
||||||
|
- Verify the hero still exposes exactly one primary CTA and one secondary deepening CTA.
|
||||||
|
|
||||||
|
### Story 3 (P3): Move Into the Right Next Route
|
||||||
|
|
||||||
|
- Align homepage CTA targets, secondary deep links, and optional-route suppression so the homepage routes into Product, Trust, Changelog, and Contact without competing conversion paths.
|
||||||
|
- Tighten header and footer discoverability, including footer/legal reachability, while keeping optional unpublished routes de-emphasized.
|
||||||
|
- Finalize homepage onward routing in `apps/website/src/pages/index.astro` while preserving mobile readability and narrow-screen discoverability.
|
||||||
|
- Tests / validation:
|
||||||
|
- Expand smoke coverage to assert visible reachability of `/product`, `/trust`, `/changelog`, and `/contact`, plus footer/legal discoverability on desktop and narrow mobile widths.
|
||||||
|
- Add explicit narrow-screen assertions so the homepage stays readable and does not leak optional unpublished routes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Close-Out Notes
|
||||||
|
|
||||||
|
**Implementation completed**: All 19 tasks (T001–T019) across 6 phases.
|
||||||
|
|
||||||
|
### Build & Test Proof
|
||||||
|
|
||||||
|
- `corepack pnpm build:website`: ✅ 12 pages built, 0 errors
|
||||||
|
- `cd apps/website && corepack pnpm exec playwright test`: ✅ 20/20 tests pass
|
||||||
|
|
||||||
|
### Summary of Changes
|
||||||
|
|
||||||
|
**New files**:
|
||||||
|
- `apps/website/src/components/sections/OutcomeSection.astro` — outcome framing block
|
||||||
|
- `apps/website/src/components/sections/ProgressTeaser.astro` — dated progress signals
|
||||||
|
- `apps/website/src/components/sections/CapabilityGrid.astro` — grouped capability clusters
|
||||||
|
- `apps/website/src/lib/changelog.ts` — shared helper for recent changelog entries
|
||||||
|
- `apps/website/public/images/hero-product-visual.svg` — product-near hero visual
|
||||||
|
|
||||||
|
**Modified files**:
|
||||||
|
- `apps/website/src/types/site.ts` — 7 new interfaces for Spec 216 section model
|
||||||
|
- `apps/website/src/content/pages/home.ts` — full rewrite with Spec 216 content
|
||||||
|
- `apps/website/src/pages/index.astro` — full rewrite with new section flow
|
||||||
|
- `apps/website/src/components/sections/PageHero.astro` — added productVisual + trustSubclaims support
|
||||||
|
- `apps/website/src/components/primitives/Section.astro` — forward rest attributes (data-section)
|
||||||
|
- `apps/website/src/components/primitives/Card.astro` — forward rest attributes (data-hero-visual)
|
||||||
|
- `apps/website/tests/smoke/home-product.spec.ts` — rewritten for Spec 216 homepage structure
|
||||||
|
- `apps/website/tests/smoke/smoke-helpers.ts` — 4 new assertion helpers
|
||||||
|
- `apps/website/tests/smoke/visual-foundation-guardrails.spec.ts` — updated CTA labels
|
||||||
|
|
||||||
|
### Homepage Section Flow (Spec 216)
|
||||||
|
|
||||||
|
1. **PageHero** — eyebrow, headline, description, primary/secondary CTA, product visual, trust subclaims
|
||||||
|
2. **LogoStrip** — ecosystem fit (Microsoft Graph, Entra ID, Intune, Review workflows)
|
||||||
|
3. **OutcomeSection** — 3 outcome cards explaining why TenantAtlas matters
|
||||||
|
4. **CapabilityGrid** — 4 grouped capability clusters (Backup, Restore, Inventory, Governance)
|
||||||
|
5. **TrustGrid** — 3 trust signals with link to /trust
|
||||||
|
6. **ProgressTeaser** — dated changelog entries (or fallback link) with link to /changelog
|
||||||
|
7. **CTASection** — final CTA: "Request a working session" (primary) + "See the product model" (secondary)
|
||||||
|
|
||||||
|
### Removed Exports
|
||||||
|
|
||||||
|
- `homeMetrics`, `homePillars`, `homeProofBlocks` from `home.ts` (replaced by outcome/capability/trust/progress model)
|
||||||
66
specs/216-homepage-structure/quickstart.md
Normal file
66
specs/216-homepage-structure/quickstart.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Quickstart: Website Homepage Structure & Section Model
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Verify that the homepage in `apps/website` follows the Spec 216 section contract and routes visitors clearly into Product, Trust, Changelog, and Contact.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Node.js 20+
|
||||||
|
- Corepack enabled
|
||||||
|
- Repo dependencies installed with `corepack pnpm install`
|
||||||
|
|
||||||
|
## Run the website locally
|
||||||
|
|
||||||
|
From the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
corepack pnpm dev:website
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternative, inside the website app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/website
|
||||||
|
corepack pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Default local URL: `http://127.0.0.1:4321/`
|
||||||
|
|
||||||
|
## What to verify on the homepage
|
||||||
|
|
||||||
|
Check the homepage in this order:
|
||||||
|
|
||||||
|
1. Header and global navigation expose Product, Trust, Changelog, and Contact, with no prominent links to unsubstantial optional routes.
|
||||||
|
2. Hero shows one dominant primary CTA, one secondary deepening CTA, and a product-near visual.
|
||||||
|
3. Outcome framing explains why the product matters in buyer language rather than route or feature-admin language.
|
||||||
|
4. Capability section groups the product model instead of listing a flat feature wall.
|
||||||
|
5. Trust block appears before the final CTA and routes to `/trust`.
|
||||||
|
6. Progress block shows visible dated product movement and routes to `/changelog`.
|
||||||
|
7. Final CTA offers one clear next step, currently `/contact`.
|
||||||
|
8. Footer keeps Product, Trust, Changelog, Contact, Privacy, and Imprint reachable.
|
||||||
|
|
||||||
|
## Build proof
|
||||||
|
|
||||||
|
From the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
corepack pnpm build:website
|
||||||
|
```
|
||||||
|
|
||||||
|
## Browser smoke proof
|
||||||
|
|
||||||
|
Run the website smoke suite:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/website
|
||||||
|
corepack pnpm exec playwright test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Expected proof points
|
||||||
|
|
||||||
|
- Homepage required blocks are visible in the intended order.
|
||||||
|
- The hero CTA hierarchy remains clear and non-competing.
|
||||||
|
- `/product`, `/trust`, `/changelog`, and `/contact` are reachable from the homepage.
|
||||||
|
- Optional unpublished routes are not surfaced prominently.
|
||||||
|
- The homepage remains readable on desktop and mobile widths.
|
||||||
41
specs/216-homepage-structure/research.md
Normal file
41
specs/216-homepage-structure/research.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Research: Website Homepage Structure & Section Model
|
||||||
|
|
||||||
|
## Decision 1: Keep the homepage implementation local to the static Astro website
|
||||||
|
|
||||||
|
- **Decision**: Continue treating the homepage as a static `apps/website` route composed from Astro content modules and section components, with no runtime dependency on `apps/platform`.
|
||||||
|
- **Rationale**: Spec 216 is explicitly website-only. The current website already runs as a standalone Astro app, and the required homepage improvements concern structure, sequencing, and public route discoverability rather than dynamic runtime behavior.
|
||||||
|
- **Alternatives considered**:
|
||||||
|
- Couple homepage composition to `apps/platform` or a shared API: rejected because the spec forbids platform obligations and the homepage needs no dynamic platform data.
|
||||||
|
- Introduce a CMS or page-builder layer first: rejected because a single homepage route does not justify that operational overhead.
|
||||||
|
|
||||||
|
## Decision 2: Extend the existing content-driven homepage model instead of adding a section registry
|
||||||
|
|
||||||
|
- **Decision**: Preserve `apps/website/src/content/pages/home.ts` as the homepage source module and extend it with the smallest missing content objects for outcome, capability, trust, and progress sections.
|
||||||
|
- **Rationale**: The current site already uses typed content exports and section components (`PageHero`, `FeatureGrid`, `CTASection`, `TrustGrid`, `LogoStrip`). This is the narrowest correct place to express homepage-specific structure without adding a polymorphic section framework.
|
||||||
|
- **Alternatives considered**:
|
||||||
|
- Build a generic section registry with discriminated unions and render dispatch: rejected as premature abstraction for one page.
|
||||||
|
- Hardcode all new copy and structure directly in `index.astro`: rejected because it would weaken the existing typed content pattern and make future homepage iteration harder.
|
||||||
|
|
||||||
|
## Decision 3: Recompose the homepage into an explicit narrative flow
|
||||||
|
|
||||||
|
- **Decision**: Implement the homepage in the following functional order: header, hero, outcome framing, capability model, trust, progress, CTA, footer. Optional supporting context stays secondary and may only appear if it reinforces clarity.
|
||||||
|
- **Rationale**: Exploration of the current homepage showed that the site already has hero, optional ecosystem context, and CTA pieces, but the middle narrative is misaligned: the current feature grid explains route jobs instead of product outcomes or capabilities, trust is too implicit, and progress is only a CTA target.
|
||||||
|
- **Alternatives considered**:
|
||||||
|
- Keep the current hero → ecosystem → route-jobs → proof → CTA sequence: rejected because it does not satisfy Spec 216’s required block responsibilities.
|
||||||
|
- Collapse trust or progress into the CTA block: rejected because the spec requires both to appear explicitly before the final CTA.
|
||||||
|
|
||||||
|
## Decision 4: Reuse existing Trust and Changelog truth for homepage proof blocks
|
||||||
|
|
||||||
|
- **Decision**: Source homepage trust signals from the existing Trust content (`trust.ts`) and source homepage progress signals from the published changelog collection rather than inventing homepage-only proof systems.
|
||||||
|
- **Rationale**: The Trust page already contains bounded public claims and principles, while the changelog route already uses dated collection entries. Reusing those sources keeps one truth for proof-oriented content and avoids duplicate claim maintenance.
|
||||||
|
- **Alternatives considered**:
|
||||||
|
- Create homepage-only trust claims arrays disconnected from `/trust`: rejected because it would create duplicate truth and increase drift risk.
|
||||||
|
- Create manual homepage progress cards unrelated to the changelog collection: rejected because dated changelog truth already exists and is the stronger source.
|
||||||
|
|
||||||
|
## Decision 5: Validate through the existing website smoke harness
|
||||||
|
|
||||||
|
- **Decision**: Prove Spec 216 with the existing website build command and focused Playwright smoke updates for homepage section order, CTA hierarchy, and onward route reachability.
|
||||||
|
- **Rationale**: The homepage contract is about public rendering, navigational clarity, and responsive visibility. Browser smoke coverage is the narrowest proving layer that can validate those concerns.
|
||||||
|
- **Alternatives considered**:
|
||||||
|
- Build-only proof alone: rejected because static output generation does not prove section order, CTA hierarchy, or visible route reachability.
|
||||||
|
- Add visual regression or heavier browser matrices immediately: rejected because the feature scope does not require that extra cost.
|
||||||
169
specs/216-homepage-structure/spec.md
Normal file
169
specs/216-homepage-structure/spec.md
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
# Feature Specification: Website Homepage Structure & Section Model
|
||||||
|
|
||||||
|
**Feature Branch**: `216-homepage-structure`
|
||||||
|
**Created**: 2026-04-19
|
||||||
|
**Status**: Draft
|
||||||
|
**Input**: User description: "Define Spec 216 as the website-only homepage structure and section model for `apps/website`, covering required sections, ordering, CTA logic, trust signal placement, product visuals, and onward routing."
|
||||||
|
|
||||||
|
## Spec Candidate Check *(mandatory — SPEC-GATE-001)*
|
||||||
|
|
||||||
|
- **Problem**: Without a clear homepage contract, the public website can drift into template-led marketing, feature dumps, weak trust framing, or premature sales pressure, leaving the product looking less real and less credible than it is.
|
||||||
|
- **Today's failure**: A first-time visitor can see visually polished sections yet still fail to understand what the product is, why it matters, why it should be trusted, or what the next sensible step should be.
|
||||||
|
- **User-visible improvement**: Visitors can understand the product category, the operational outcomes, the bounded trust posture, and the next route from the homepage alone without hunting through placeholder or mismatched sections.
|
||||||
|
- **Smallest enterprise-capable version**: One homepage-only structure contract that fixes the required section jobs, ordering, routing rules, product-visual expectations, trust claim rules, and excluded anti-patterns; no broader design-system rewrite or full-site IA expansion.
|
||||||
|
- **Explicit non-goals**: No final copy, no pixel or spacing decisions, no pricing surface, no full Product or Trust or Changelog spec, no CMS design, no platform UI work, no Filament theming, and no cross-app behavior changes.
|
||||||
|
- **Permanent complexity imported**: A stable homepage section vocabulary, CTA hierarchy, trust-claim guardrails, optional-section rules, and a clear structural baseline for follow-up website specs.
|
||||||
|
- **Why now**: The website foundation already exists, and the homepage needs a precise structural contract before deeper hero, screenshot, trust, and product-detail work can ship consistently.
|
||||||
|
- **Why not local**: A one-off homepage rewrite without a stated section model would not reliably prevent regression into generic SaaS patterns, weak trust sequencing, or conflicting CTAs as future iterations land.
|
||||||
|
- **Approval class**: Core Enterprise
|
||||||
|
- **Red flags triggered**: #1 introduces a homepage section taxonomy; #5 could become vocabulary-heavy if left unconstrained. The scope remains justified because the contract is strictly local to the public homepage and directly improves product clarity, trust, and onward routing.
|
||||||
|
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexität: 1 | Produktnähe: 2 | Wiederverwendung: 1 | **Gesamt: 10/12**
|
||||||
|
- **Decision**: approve
|
||||||
|
|
||||||
|
## Spec Scope Fields *(mandatory)*
|
||||||
|
|
||||||
|
- **Scope**: workspace
|
||||||
|
- **Primary Routes**: `/` with onward routing to `/product`, `/trust`, `/changelog`, `/contact`, and supporting footer/legal reachability to `/privacy`, `/imprint`, and `/terms` when published
|
||||||
|
- **Data Ownership**: Workspace-owned public homepage content, section rules, CTA targets, trust/progress signals, and navigation behavior inside `apps/website`; no tenant-owned records or platform runtime data
|
||||||
|
- **RBAC**: Public-read runtime only. No authenticated membership or capability checks are required for homepage browsing; publishing remains repo-controlled.
|
||||||
|
|
||||||
|
## Proportionality Review *(mandatory when structural complexity is introduced)*
|
||||||
|
|
||||||
|
- **New source of truth?**: no
|
||||||
|
- **New persisted entity/table/artifact?**: no
|
||||||
|
- **New abstraction?**: yes
|
||||||
|
- **New enum/state/reason family?**: no
|
||||||
|
- **New cross-domain UI framework/taxonomy?**: yes
|
||||||
|
- **Current operator problem**: Buyers and technical evaluators cannot reliably judge the product from the homepage if structure drifts into generic marketing patterns or hides trust and next-step logic.
|
||||||
|
- **Existing structure is insufficient because**: The website foundation spec establishes public-site direction broadly, but it does not lock the homepage into mandatory block responsibilities, ordering, product-near rules, or claim/routing guardrails.
|
||||||
|
- **Narrowest correct implementation**: A homepage-only section model with required blocks, optional-block rules, claim constraints, and onward-routing expectations; no broader content platform, pricing model, or multi-page taxonomy expansion.
|
||||||
|
- **Ownership cost**: Ongoing review effort to keep homepage sections, CTA hierarchy, trust claims, and downstream route links aligned with the defined contract as follow-up page specs land.
|
||||||
|
- **Alternative intentionally rejected**: Letting homepage structure emerge ad hoc from design or copy iterations was rejected because it would not reliably prevent template drift, feature walls, trust being buried, or CTA pressure outpacing clarity.
|
||||||
|
- **Release truth**: Current-release truth
|
||||||
|
|
||||||
|
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
|
||||||
|
|
||||||
|
- **Test purpose / classification**: Browser
|
||||||
|
- **Validation lane(s)**: fast-feedback
|
||||||
|
- **Why this classification and these lanes are sufficient**: The feature is proven by homepage section presence, information order, route discoverability, and responsive behavior rather than by tenant data, authorization, or business-rule execution.
|
||||||
|
- **New or expanded test families**: Website browser smoke coverage for homepage section order, CTA reachability, and onward route discoverability, plus the existing static build proof.
|
||||||
|
- **Fixture / helper cost impact**: Minimal. Public homepage coverage does not require seeded tenant data, auth state, or platform helpers.
|
||||||
|
- **Heavy-family visibility / justification**: none
|
||||||
|
- **Special surface test profile**: N/A
|
||||||
|
- **Standard-native relief or required special coverage**: Homepage-specific browser coverage should verify required block presence, CTA hierarchy, and route reachability; no platform-specific fixtures or heavy suites are needed.
|
||||||
|
- **Reviewer handoff**: Reviewers must confirm that the homepage exposes all required structural blocks, routes visitors to Product, Trust, Changelog, and Contact without dead ends, omits unsubstantial optional routes, and remains understandable on desktop and mobile through build proof and browser smoke coverage.
|
||||||
|
- **Budget / baseline / trend impact**: Small increase limited to homepage/browser assertions in `apps/website`.
|
||||||
|
- **Escalation needed**: none
|
||||||
|
- **Active feature PR close-out entry**: Smoke Coverage
|
||||||
|
- **Planned validation commands**: `corepack pnpm build:website` and `cd apps/website && corepack pnpm exec playwright test`
|
||||||
|
|
||||||
|
## User Scenarios & Testing *(mandatory)*
|
||||||
|
|
||||||
|
### User Story 1 - Understand the Product Fast (Priority: P1)
|
||||||
|
|
||||||
|
A first-time buyer or technical evaluator lands on the homepage and can quickly understand what TenantAtlas is, why it matters, why it appears credible, and what to do next.
|
||||||
|
|
||||||
|
**Why this priority**: If the homepage fails to create immediate product clarity and trust, every deeper surface loses value.
|
||||||
|
|
||||||
|
**Independent Test**: This can be tested by visiting the homepage alone and confirming that the product category, relevance, credibility signals, and next-step route are all understandable without opening additional pages.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** a first-time visitor opens the homepage, **When** they read the hero and immediate follow-on sections, **Then** they can describe what the product is and who it is for.
|
||||||
|
2. **Given** a visitor is unsure why the product matters, **When** they read the outcome framing, **Then** they can identify the operational problem space and the type of improvement the product offers.
|
||||||
|
3. **Given** a visitor wants to know what to do next, **When** they reach the CTA hierarchy, **Then** they can distinguish the primary next step from deeper exploratory routes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 2 - Evaluate Product Model and Trust Without a Feature Wall (Priority: P2)
|
||||||
|
|
||||||
|
A serious evaluator can understand the connected product model, see credible trust and progress signals, and avoid mistaking the homepage for a generic feature dump.
|
||||||
|
|
||||||
|
**Why this priority**: Homepage structure must translate capabilities into a coherent operating model and establish seriousness early enough to qualify deeper interest.
|
||||||
|
|
||||||
|
**Independent Test**: This can be tested by reviewing only the homepage sections that explain outcomes, capabilities, trust, and progress and confirming that they form one coherent story rather than disconnected cards.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** a visitor wants to understand the product logic, **When** they reach the capability section, **Then** they see grouped capability areas rather than an endless list of equal-weight features.
|
||||||
|
2. **Given** a visitor needs proof the product is credible, **When** they reach the trust and progress blocks, **Then** they see bounded claims and a visible route to deeper substantiation.
|
||||||
|
3. **Given** a visitor reads the homepage end-to-end, **When** they compare the sections, **Then** trust appears early enough to support the CTA instead of arriving as an afterthought.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 3 - Move Into the Right Next Route (Priority: P3)
|
||||||
|
|
||||||
|
A qualified visitor can choose whether to go deeper into Product, Trust, Changelog, or Contact without guessing which route answers the next question.
|
||||||
|
|
||||||
|
**Why this priority**: The homepage should route deliberately instead of trying to absorb every downstream page or forcing immediate sales contact.
|
||||||
|
|
||||||
|
**Independent Test**: This can be tested by starting on the homepage and verifying that each next-question route is visible, understandable, and reachable without dead ends or conflicting CTAs.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** a visitor wants deeper product detail, **When** they act on the capability model section, **Then** the homepage routes them to `/product`.
|
||||||
|
2. **Given** a visitor wants trust or hosting context, **When** they act on the trust block or related CTA, **Then** the homepage routes them to `/trust`.
|
||||||
|
3. **Given** a visitor wants visible product movement or a direct conversation, **When** they inspect the progress block or final CTA, **Then** they can reach `/changelog` or `/contact` through clear, non-competing actions.
|
||||||
|
|
||||||
|
### Edge Cases
|
||||||
|
|
||||||
|
- What happens when optional surfaces such as Resources, Docs, Blog, Demo, or social proof are not yet substantive? The homepage must hide or de-emphasize them rather than linking to empty or immature routes.
|
||||||
|
- How does the homepage handle trust or residency claims that cannot yet be substantiated publicly? The claim must be omitted or softened instead of being implied broadly or theatrically.
|
||||||
|
- What happens if a real screenshot is not yet ready for publication? The hero may use a credible product-near visual, but it must still communicate real product structure and must not collapse into abstract decoration.
|
||||||
|
- How does the homepage behave on narrow screens? The same meaning order must survive mobile compression, and trust, progress, product-near context, and the primary CTA must remain visible without horizontal scrolling.
|
||||||
|
- What happens when the changelog surface has only a small amount of published history? The progress signal may stay concise, but it must still indicate real dated movement and link into the actual changelog route.
|
||||||
|
|
||||||
|
## Requirements *(mandatory)*
|
||||||
|
|
||||||
|
This feature changes only the public homepage in `apps/website`. It introduces no Microsoft Graph calls, no platform authorization changes, no Filament surfaces, no queued work, and no runtime coupling to `apps/platform`.
|
||||||
|
|
||||||
|
### Functional Requirements
|
||||||
|
|
||||||
|
- **FR-001**: The homepage MUST act as a product-near routing, positioning, and trust hub for `apps/website` rather than as full product documentation, a pure feature landing page, or a generic SaaS template.
|
||||||
|
- **FR-002**: The homepage MUST answer, within the initial reading flow, what the product is, who it is for, what problem it addresses, why it should be taken seriously, and what the next sensible step is.
|
||||||
|
- **FR-003**: The homepage MUST preserve the following functional block set: header or global navigation, hero, product outcome or why-it-matters section, core capability or product model section, trust or credibility signal block, product progress or changelog signal block, primary CTA or contact-transition block, and footer. Visual combination is allowed only when each block's functional job remains clear.
|
||||||
|
- **FR-004**: The homepage MUST follow a logical order of global context, hero, outcome framing, product model, trust, progress, CTA, and footer. Minor compression is allowed, but the page MUST NOT collapse into an unordered card stack or feature wall.
|
||||||
|
- **FR-005**: The header MUST include brand navigation to `/`, access to Product, Trust, Changelog, and Contact, and MAY include Resources only when substantive content exists. It MUST present one clear primary CTA and MUST NOT expose empty or immature routes.
|
||||||
|
- **FR-006**: The hero MUST include a positioning headline, supporting copy, one primary CTA, one secondary deepening CTA, and a product-near visual. The visual SHOULD be a real screenshot or a credible UI-near representation and MUST NOT rely only on abstract shapes to convey product truth.
|
||||||
|
- **FR-007**: Hero-adjacent trust subclaims MAY appear only when they are factually supportable, narrowly phrased, non-exaggerated, and traceable to the Trust surface for deeper context.
|
||||||
|
- **FR-008**: The product outcome or why-it-matters section MUST translate the product from capabilities into buyer-relevant outcomes, friction reduction, or operational improvements. It MUST explain why the problem space matters and MUST NOT rely on buzzwords or internal feature naming alone.
|
||||||
|
- **FR-009**: The core capability or product model section MUST explain the connected product model and the major capability areas without becoming full documentation. It MUST communicate grouped capability coverage spanning backup, restore, version history, auditability, inventory or drift visibility, and governance or evidence-oriented review work.
|
||||||
|
- **FR-010**: Capability explanation MUST use grouped clusters or another clear hierarchy, MUST route deeper product understanding to `/product`, and MUST NOT present the homepage as an endless list of equal-weight feature cards.
|
||||||
|
- **FR-011**: The homepage MUST include an explicit trust or credibility block before the final CTA. This block MUST signal technical seriousness and a bounded trust posture and MUST route to `/trust` for deeper context.
|
||||||
|
- **FR-012**: Any trust, hosting, residency, security, or governance claims shown on the homepage MUST be factually substantiated, narrowly phrased, and supportable by the Trust surface. The homepage MUST NOT imply unverified compliance or use invented badges, seals, or pseudo-certifications.
|
||||||
|
- **FR-013**: The homepage MUST include a dated or clearly progress-oriented signal that the product is active and evolving, such as a changelog teaser or recent public updates. It MUST route to `/changelog` and MUST NOT behave like a padded marketing news feed.
|
||||||
|
- **FR-014**: The homepage MUST provide a clear lower-page CTA transition to `/contact` or `/demo`. Until a distinct public `/demo` route exists, the primary conversion target MUST default to `/contact`.
|
||||||
|
- **FR-015**: The CTA system MUST keep exactly one dominant primary CTA and at least one secondary deepening CTA. The homepage MUST NOT present multiple equally loud primary sales actions in parallel.
|
||||||
|
- **FR-016**: The footer MUST keep Product, Trust, Changelog, Contact, Privacy, and Imprint reachable and MAY include Terms, Resources, or Docs only when those surfaces are real and maintained.
|
||||||
|
- **FR-017**: Optional homepage sections such as social proof, use-case spotlights, FAQ, or content teasers MAY be used only when backed by real substance and MUST NOT displace required sections or fake maturity.
|
||||||
|
- **FR-018**: The homepage MUST route visitors cleanly into `/product`, `/trust`, `/changelog`, and `/contact` without attempting to replace those pages completely.
|
||||||
|
- **FR-019**: The homepage MUST stay understandable and structurally equivalent on mobile. Hero, outcome, capability, trust, progress, and CTA blocks MUST remain recognizable on narrow screens, and mobile compression MUST NOT effectively hide trust or product-near context.
|
||||||
|
- **FR-020**: The homepage MUST avoid the following disallowed patterns: template-first SaaS framing, abstract-only storytelling, unstructured feature walls, hidden trust, demo-only pressure, fake social proof, and enterprise-theater claims.
|
||||||
|
- **FR-021**: The homepage MUST remain strictly local to `apps/website` and MUST NOT create implementation or contract requirements for `apps/platform`.
|
||||||
|
|
||||||
|
### Key Entities *(include if feature involves data)*
|
||||||
|
|
||||||
|
- **Homepage Block**: A functionally distinct section of the homepage such as Hero, Outcome, Capability Model, Trust, Progress, CTA, or Footer.
|
||||||
|
- **Product-Near Visual**: A screenshot or credible UI-adjacent visual that signals a real product rather than abstract marketing decoration.
|
||||||
|
- **Trust Claim**: A bounded public assertion about hosting, residency, seriousness, isolation, governance posture, or similar credibility signals that must be supportable by the Trust surface.
|
||||||
|
- **Progress Signal**: A homepage block or teaser that shows visible dated product movement and routes to the changelog.
|
||||||
|
- **CTA Target**: A next-question route reached from the homepage, especially Product, Trust, Changelog, or Contact.
|
||||||
|
|
||||||
|
## Assumptions & Dependencies
|
||||||
|
|
||||||
|
- `/product`, `/trust`, `/changelog`, `/contact`, `/privacy`, and `/imprint` remain the canonical core routes surfaced from the homepage in `apps/website`.
|
||||||
|
- `/contact` remains the primary conversion route unless and until a distinct public `/demo` surface exists and is substantively different.
|
||||||
|
- Optional surfaces such as Resources, Docs, Blog, Demo, customer references, or social proof remain hidden or secondary until they contain real, maintained content.
|
||||||
|
- If a publishable real screenshot is not yet ready, the initial hero may use a credible product-near visual that still reflects actual product structure rather than abstract illustration.
|
||||||
|
- Trust, hosting, residency, and governance claims will be limited to statements the team can substantiate publicly at release time.
|
||||||
|
|
||||||
|
## Success Criteria *(mandatory)*
|
||||||
|
|
||||||
|
### Measurable Outcomes
|
||||||
|
|
||||||
|
- **SC-001**: A first-time visitor can state what the product is, why it matters, why it appears credible, and the next step after 60 seconds or less on the homepage.
|
||||||
|
- **SC-002**: The released homepage exposes all eight mandatory functional blocks, or combined equivalents without loss of function, and each of `/product`, `/trust`, `/changelog`, and `/contact` is reachable from the homepage without dead links.
|
||||||
|
- **SC-003**: The hero presents exactly one primary CTA, at least one secondary deepening CTA, and a product-near visual on both desktop and mobile layouts.
|
||||||
|
- **SC-004**: Trust and progress signals appear before the final CTA and remain discoverable without leaving the homepage, while deeper substantiation stays reachable in one click to `/trust` and `/changelog`.
|
||||||
|
- **SC-005**: No released homepage version contains unsupported trust claims, fake logos or badges, placeholder routes, or more than one equally dominant primary conversion action.
|
||||||
|
- **SC-006**: On mobile widths, visitors can still identify the hero, outcome framing, capability model, trust block, progress block, and CTA transition without horizontal scrolling or hidden primary navigation.
|
||||||
206
specs/216-homepage-structure/tasks.md
Normal file
206
specs/216-homepage-structure/tasks.md
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
# Tasks: Website Homepage Structure & Section Model
|
||||||
|
|
||||||
|
**Input**: Design documents from `/specs/216-homepage-structure/`
|
||||||
|
**Prerequisites**: `plan.md`, `spec.md`, `research.md`, `data-model.md`, `quickstart.md`, `contracts/homepage-surface.openapi.yaml`
|
||||||
|
|
||||||
|
**Tests**: Browser smoke coverage and the root website build proof are required for this runtime-changing website feature.
|
||||||
|
|
||||||
|
## Test Governance Checklist
|
||||||
|
|
||||||
|
- [X] Lane assignment stays `Browser` in `fast-feedback`, which is the narrowest sufficient proof for this homepage-only change.
|
||||||
|
- [X] New or changed tests stay in the existing website smoke suite instead of widening into a heavier family.
|
||||||
|
- [X] Shared helpers stay cheap by default; no backend, auth, database, or provider fixtures are introduced.
|
||||||
|
- [X] Planned validation commands remain the feature-local website build proof and Playwright smoke suite.
|
||||||
|
- [X] No additional budget, baseline, or escalation path is required beyond `document-in-feature` for this slice.
|
||||||
|
|
||||||
|
## Phase 1: Setup (Shared Infrastructure)
|
||||||
|
|
||||||
|
**Purpose**: Establish the homepage-specific content/type surface before story work begins.
|
||||||
|
|
||||||
|
- [X] T001 Add homepage-specific section content types for hero visuals, optional trust subclaims, outcome framing, capability clusters, trust signals, and progress teasers in `apps/website/src/types/site.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Foundational (Blocking Prerequisites)
|
||||||
|
|
||||||
|
**Purpose**: Add the reusable homepage helpers and scaffolds that every story slice depends on.
|
||||||
|
|
||||||
|
**⚠️ CRITICAL**: No user story work should begin until this phase is complete.
|
||||||
|
|
||||||
|
- [X] T002 [P] Create reusable homepage section scaffolds in `apps/website/src/components/sections/OutcomeSection.astro` and `apps/website/src/components/sections/ProgressTeaser.astro`
|
||||||
|
- [X] T003 [P] Add a shared recent-changelog helper for homepage progress signals in `apps/website/src/lib/changelog.ts`
|
||||||
|
- [X] T004 [P] Extend homepage smoke helpers for ordered section, product-near visual, mobile viewport, and CTA assertions in `apps/website/tests/smoke/smoke-helpers.ts`
|
||||||
|
|
||||||
|
**Checkpoint**: Homepage foundations are ready. User-story work can proceed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: User Story 1 - Understand the Product Fast (Priority: P1) 🎯 MVP
|
||||||
|
|
||||||
|
**Goal**: Make the homepage explain what TenantAtlas is, why it matters, and what the next step is within the first reading pass.
|
||||||
|
|
||||||
|
**Independent Test**: Visit `/` and confirm the hero, outcome framing, and CTA hierarchy make the product category, buyer relevance, and next step understandable without opening any other route.
|
||||||
|
|
||||||
|
### Tests for User Story 1
|
||||||
|
|
||||||
|
> **NOTE**: Write this test first and confirm it fails before implementing the story.
|
||||||
|
|
||||||
|
- [X] T005 [P] [US1] Write failing homepage smoke assertions for hero clarity, product-near visual presence, outcome framing, and one dominant CTA hierarchy in `apps/website/tests/smoke/home-product.spec.ts`
|
||||||
|
|
||||||
|
### Implementation for User Story 1
|
||||||
|
|
||||||
|
- [X] T006 [P] [US1] Add Spec 216 hero content, product-near visual data, optional bounded trust subclaims, and outcome blocks in `apps/website/src/content/pages/home.ts`
|
||||||
|
- [X] T007 [US1] Implement the hero-to-outcome homepage flow and hero visual rendering in `apps/website/src/pages/index.astro` and `apps/website/src/components/sections/PageHero.astro`
|
||||||
|
|
||||||
|
**Checkpoint**: The homepage delivers the MVP story of product clarity, buyer relevance, and one clear next step.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: User Story 2 - Evaluate Product Model and Trust Without a Feature Wall (Priority: P2)
|
||||||
|
|
||||||
|
**Goal**: Turn the homepage middle section into a grouped product model with explicit trust and visible progress instead of route-job cards.
|
||||||
|
|
||||||
|
**Independent Test**: Visit `/` and confirm the homepage shows grouped capability coverage, an explicit trust block, and a dated progress signal before the final CTA.
|
||||||
|
|
||||||
|
### Tests for User Story 2
|
||||||
|
|
||||||
|
> **NOTE**: Write this test first and confirm it fails before implementing the story.
|
||||||
|
|
||||||
|
- [X] T008 [P] [US2] Write failing homepage smoke assertions for grouped capability clusters, explicit trust visibility, and pre-CTA progress signaling in `apps/website/tests/smoke/home-product.spec.ts` and `apps/website/tests/smoke/changelog-core-ia.spec.ts`
|
||||||
|
|
||||||
|
### Implementation for User Story 2
|
||||||
|
|
||||||
|
- [X] T009 [P] [US2] Replace route-job homepage cards with grouped capability and trust/progress content data in `apps/website/src/content/pages/home.ts` and `apps/website/src/content/pages/trust.ts`
|
||||||
|
- [X] T010 [US2] Render the grouped capability model and explicit trust block on the homepage in `apps/website/src/pages/index.astro` and `apps/website/src/components/sections/TrustGrid.astro`
|
||||||
|
- [X] T011 [US2] Use the shared changelog helper to wire a dated homepage progress teaser in `apps/website/src/components/sections/ProgressTeaser.astro` and `apps/website/src/pages/index.astro`
|
||||||
|
|
||||||
|
**Checkpoint**: The homepage explains the connected product model, shows explicit trust, and proves visible product movement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: User Story 3 - Move Into the Right Next Route (Priority: P3)
|
||||||
|
|
||||||
|
**Goal**: Make Product, Trust, Changelog, Contact, and legal follow-through obvious from the homepage without multiple competing conversion paths.
|
||||||
|
|
||||||
|
**Independent Test**: Start on `/` and confirm the homepage routes clearly into `/product`, `/trust`, `/changelog`, and `/contact`, while footer/legal links remain discoverable and optional unpublished routes stay de-emphasized.
|
||||||
|
|
||||||
|
### Tests for User Story 3
|
||||||
|
|
||||||
|
> **NOTE**: Write this test first and confirm it fails before implementing the story.
|
||||||
|
|
||||||
|
- [X] T012 [P] [US3] Write failing homepage smoke assertions for Product, Trust, Changelog, Contact, footer/legal discoverability, and narrow-screen/mobile visibility in `apps/website/tests/smoke/home-product.spec.ts` and `apps/website/tests/smoke/contact-legal.spec.ts`
|
||||||
|
|
||||||
|
### Implementation for User Story 3
|
||||||
|
|
||||||
|
- [X] T013 [P] [US3] Align homepage CTA targets, secondary deep links, and optional-route suppression in `apps/website/src/content/pages/home.ts` and `apps/website/src/lib/site.ts`
|
||||||
|
- [X] T014 [P] [US3] Tighten header and footer route discoverability for the homepage journey on desktop and narrow screens in `apps/website/src/components/layout/Navbar.astro` and `apps/website/src/components/layout/Footer.astro`
|
||||||
|
- [X] T015 [US3] Finalize homepage onward routing to `/product`, `/trust`, `/changelog`, and `/contact` in `apps/website/src/pages/index.astro`
|
||||||
|
|
||||||
|
**Checkpoint**: The homepage routes qualified visitors into the right next page without dead ends or competing primary actions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Polish & Cross-Cutting Concerns
|
||||||
|
|
||||||
|
**Purpose**: Validate proof commands, tighten claim wording, and capture close-out notes.
|
||||||
|
|
||||||
|
- [X] T016 [P] Review homepage proof and trust wording against bounded-claim rules in `apps/website/src/content/pages/home.ts`, `apps/website/src/content/pages/trust.ts`, and `apps/website/src/content/pages/changelog.ts`
|
||||||
|
- [X] T017 [P] Run `corepack pnpm build:website` from `package.json` and confirm homepage proof expectations in `specs/216-homepage-structure/quickstart.md`
|
||||||
|
- [X] T018 [P] Run `cd apps/website && corepack pnpm exec playwright test` against `apps/website/tests/smoke/home-product.spec.ts`, `apps/website/tests/smoke/changelog-core-ia.spec.ts`, and `apps/website/tests/smoke/contact-legal.spec.ts`
|
||||||
|
- [X] T019 Record the homepage smoke-coverage close-out and verification notes in `specs/216-homepage-structure/plan.md` and `specs/216-homepage-structure/quickstart.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies & Execution Order
|
||||||
|
|
||||||
|
### Phase Dependencies
|
||||||
|
|
||||||
|
- **Setup (Phase 1)**: Starts immediately.
|
||||||
|
- **Foundational (Phase 2)**: Depends on Setup and blocks all user stories.
|
||||||
|
- **User Stories (Phases 3-5)**: Depend on Foundational. They remain independently testable, but shared homepage assembly in `apps/website/src/pages/index.astro` should land sequentially in story order.
|
||||||
|
- **Polish (Phase 6)**: Depends on all desired user stories being complete.
|
||||||
|
|
||||||
|
### User Story Dependencies
|
||||||
|
|
||||||
|
- **User Story 1 (P1)**: Starts after Foundational and is the MVP slice.
|
||||||
|
- **User Story 2 (P2)**: Starts after Foundational for content and helper work, and remains independently valuable because it upgrades the homepage product-model and proof layer. Its shared `index.astro` assembly should follow US1.
|
||||||
|
- **User Story 3 (P3)**: Starts after Foundational for routing and shell work, and remains independently valuable because it sharpens onward routing and CTA clarity from the homepage. Its shared `index.astro` assembly should follow US2.
|
||||||
|
|
||||||
|
### Within Each User Story
|
||||||
|
|
||||||
|
- Write the story-specific browser smoke assertions first and confirm they fail.
|
||||||
|
- Update content modules and supporting helpers before final route composition in `apps/website/src/pages/index.astro`.
|
||||||
|
- Treat `apps/website/src/pages/index.astro` as a shared assembly point: content/helper work may branch, but homepage route composition should land sequentially.
|
||||||
|
- Finish the homepage route wiring before running the story proof commands.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Parallel Opportunities
|
||||||
|
|
||||||
|
- `T002`, `T003`, and `T004` can run in parallel after `T001`.
|
||||||
|
- In US1, `T005` and `T006` can run in parallel before `T007`.
|
||||||
|
- In US2, `T008` and `T009` can run in parallel before `T010`; `T011` follows the shared homepage assembly work.
|
||||||
|
- In US3, `T013` and `T014` can run in parallel before `T015`.
|
||||||
|
- `T016`, `T017`, and `T018` can run in parallel during polish before `T019`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Parallel Example: User Story 1
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# After the foundations are complete, split the first slice into test + content work:
|
||||||
|
Task: "T005 [US1] Write failing homepage smoke assertions for hero clarity, outcome framing, and one dominant CTA hierarchy"
|
||||||
|
Task: "T006 [US1] Add Spec 216 hero and outcome content blocks"
|
||||||
|
|
||||||
|
# Then assemble the homepage route:
|
||||||
|
Task: "T007 [US1] Implement the hero-to-outcome homepage flow"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parallel Example: User Story 2
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Safe split inside US2 is limited to assertions plus content prep before homepage assembly:
|
||||||
|
Task: "T008 [US2] Write failing homepage smoke assertions for grouped capability clusters, explicit trust visibility, and pre-CTA progress signaling"
|
||||||
|
Task: "T009 [US2] Replace route-job homepage cards with grouped capability and trust/progress content data"
|
||||||
|
|
||||||
|
# Then complete the shared homepage assembly sequentially:
|
||||||
|
Task: "T010 [US2] Render the grouped capability model and explicit trust block on the homepage"
|
||||||
|
Task: "T011 [US2] Use the shared changelog helper to wire a dated homepage progress teaser"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parallel Example: User Story 3
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Split route-target and shell-discoverability work once the routing assertions exist:
|
||||||
|
Task: "T013 [US3] Align homepage CTA targets, secondary deep links, and optional-route suppression"
|
||||||
|
Task: "T014 [US3] Tighten header and footer route discoverability for the homepage journey"
|
||||||
|
|
||||||
|
# Then finish the homepage route wiring:
|
||||||
|
Task: "T015 [US3] Finalize homepage onward routing to /product, /trust, /changelog, and /contact"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Strategy
|
||||||
|
|
||||||
|
### MVP First
|
||||||
|
|
||||||
|
1. Complete Phase 1: Setup.
|
||||||
|
2. Complete Phase 2: Foundational.
|
||||||
|
3. Complete Phase 3: User Story 1.
|
||||||
|
4. Run the homepage build proof and US1 browser smoke coverage.
|
||||||
|
5. Demo the homepage MVP with clear product explanation and one dominant next step.
|
||||||
|
|
||||||
|
### Incremental Delivery
|
||||||
|
|
||||||
|
1. Foundations create the reusable homepage scaffolds, changelog helper, and smoke assertions.
|
||||||
|
2. US1 establishes immediate product clarity and CTA discipline.
|
||||||
|
3. US2 upgrades the homepage middle narrative into grouped product model, trust, and progress proof.
|
||||||
|
4. US3 sharpens onward routing and discoverability for Product, Trust, Changelog, Contact, and legal follow-through.
|
||||||
|
5. Polish runs both proof commands, validates wording, and records close-out notes before merge.
|
||||||
|
|
||||||
|
### Suggested MVP Scope
|
||||||
|
|
||||||
|
- Deliver through **User Story 1** for the smallest independently valuable slice.
|
||||||
|
- Add **User Story 2** next for stronger product-model and trust proof.
|
||||||
|
- Finish with **User Story 3** for fully deliberate onward routing from the homepage.
|
||||||
Loading…
Reference in New Issue
Block a user