diff --git a/apps/web/app/docs/page.tsx b/apps/web/app/docs/page.tsx index 52823fe2..545793b5 100644 --- a/apps/web/app/docs/page.tsx +++ b/apps/web/app/docs/page.tsx @@ -30,7 +30,7 @@ export default function DocsIndexPage() { Let’s get you started.

From your first board to your next big idea.

- + Start the quickstart ↗ diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index f6cf93bd..4d0c7cef 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -19,7 +19,7 @@ const communities = [ body: 'Keep the conversations that bring your people together.', }, { - name: 'Clubs & associations', + name: 'Clubs and associations', slug: 'clubs-and-associations', body: 'A shared space for members, plans and club life.', }, @@ -95,14 +95,19 @@ export default async function LandingPage() { On your domain. On your terms.

- + Start your forum + + Try the demo + - See Meith in use + Join the community
-

Free & open source. Self-hosted. Yours to keep.

+

+ Free and open source. Self-hosted. Yours to keep. The demo runs on sample data. +

@@ -265,6 +270,9 @@ export default async function LandingPage() { Create your community + + Try the demo + Read the docs diff --git a/apps/web/content/docs.manifest.json b/apps/web/content/docs.manifest.json index 2908301f..3401f179 100644 --- a/apps/web/content/docs.manifest.json +++ b/apps/web/content/docs.manifest.json @@ -851,6 +851,16 @@ "primary": false, "group": "Development workflow" }, + { + "slug": "brand", + "file": "contributing/brand.md", + "section": "contributing", + "title": "Brand guidelines", + "blurb": "How Meith looks, sounds and spends its one colour.", + "generated": false, + "primary": false, + "group": "Development workflow" + }, { "slug": "repository-checks", "file": "contributing/repository-checks.md", diff --git a/apps/web/e2e/marketing.spec.ts b/apps/web/e2e/marketing.spec.ts index 101aa506..079709fe 100644 --- a/apps/web/e2e/marketing.spec.ts +++ b/apps/web/e2e/marketing.spec.ts @@ -40,19 +40,22 @@ test('colour scheme persists across navigation and resets to the system preferen page, }) => { await page.goto('/') - await page.getByRole('button', { name: 'Colour scheme: system. Switch to light' }).click() + const scheme = page.getByRole('group', { name: 'Colour scheme' }) + await scheme.getByRole('button', { name: 'Light' }).click() await expect(page.locator('html')).toHaveAttribute('data-theme', 'light') - await page.getByRole('button', { name: 'Colour scheme: light. Switch to dark' }).click() + await scheme.getByRole('button', { name: 'Dark' }).click() await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark') await page.goto('/about') await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark') - await page.getByRole('button', { name: 'Colour scheme: dark. Switch to system' }).click() + await expect(scheme.getByRole('button', { name: 'Dark' })).toHaveAttribute('aria-pressed', 'true') + await scheme.getByRole('button', { name: 'System' }).click() await expect(page.locator('html')).not.toHaveAttribute('data-theme') await page.reload() - await expect( - page.getByRole('button', { name: 'Colour scheme: system. Switch to light' }), - ).toBeVisible() + await expect(scheme.getByRole('button', { name: 'System' })).toHaveAttribute( + 'aria-pressed', + 'true', + ) }) test('mobile navigation closes on Escape, outside interaction, and link selection', async ({ @@ -70,7 +73,10 @@ test('mobile navigation closes on Escape, outside interaction, and link selectio await expect(menu).toBeFocused() await menu.click() - await page.getByRole('button', { name: /^Colour scheme:/ }).click() + await page + .getByRole('group', { name: 'Colour scheme' }) + .getByRole('button', { name: 'System' }) + .click() await expect(navigation).toBeHidden() await menu.click() @@ -92,6 +98,10 @@ test('site navigation points to real destinations and exposes customisation guid ] as const) { await expect(nav.getByRole('link', { name, exact: true })).toHaveAttribute('href', href) } + await expect(nav.getByRole('link', { name: /^Demo/ })).toHaveAttribute( + 'href', + /^https:\/\/demo\./, + ) await expect(nav.getByRole('link', { name: /^Community/ })).toHaveAttribute( 'href', /^https:\/\/forum\./, @@ -110,7 +120,7 @@ test('site navigation points to real destinations and exposes customisation guid { name: /^Developers/, slug: 'developers' }, { name: /^Open-source projects/, slug: 'open-source' }, { name: /^Communities/, slug: 'communities' }, - { name: /^Clubs & associations/, slug: 'clubs-and-associations' }, + { name: /^Clubs and associations/, slug: 'clubs-and-associations' }, ] for (const audience of audiences) { diff --git a/apps/web/src/components/docs-search.tsx b/apps/web/src/components/docs-search.tsx index 98c50cee..40cb592f 100644 --- a/apps/web/src/components/docs-search.tsx +++ b/apps/web/src/components/docs-search.tsx @@ -105,7 +105,7 @@ export function DocsSearch() { className="inline-flex items-center gap-2 rounded-[var(--radius-control)] border border-border px-2.5 py-1.5 text-micro text-fg-subtle transition-colors hover:border-border-strong hover:text-fg" > ⌕ - Search + Search the docs ⌘K diff --git a/apps/web/src/components/forum-link.tsx b/apps/web/src/components/forum-link.tsx index 52893ebe..2aa63e29 100644 --- a/apps/web/src/components/forum-link.tsx +++ b/apps/web/src/components/forum-link.tsx @@ -2,9 +2,17 @@ import type { ReactNode } from 'react' import { site } from '../content/site' -export function ForumLink({ children, className }: { children: ReactNode; className?: string }) { +export function ForumLink({ + children, + className, + href = site.forum, +}: { + children: ReactNode + className?: string + href?: string +}) { return ( - + {children} (opens in a new tab) diff --git a/apps/web/src/components/nav-link.tsx b/apps/web/src/components/nav-link.tsx new file mode 100644 index 00000000..92e1c2d3 --- /dev/null +++ b/apps/web/src/components/nav-link.tsx @@ -0,0 +1,23 @@ +'use client' + +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import type { ReactNode } from 'react' + +export function NavLink({ + href, + children, +}: { + readonly href: string + readonly children: ReactNode +}) { + const pathname = usePathname() + const current = + href === '/' ? pathname === '/' : pathname === href || pathname.startsWith(`${href}/`) + + return ( + + {children} + + ) +} diff --git a/apps/web/src/components/site-bands.tsx b/apps/web/src/components/site-bands.tsx index 7911cd98..6ad51c77 100644 --- a/apps/web/src/components/site-bands.tsx +++ b/apps/web/src/components/site-bands.tsx @@ -24,7 +24,7 @@ export function ClosingBand({
- Get started → + Get started ↗ {docsHref === undefined ? ( @@ -32,7 +32,7 @@ export function ClosingBand({ ) : ( - Explore the docs → + Explore the docs ↗ )}
diff --git a/apps/web/src/components/site-footer.tsx b/apps/web/src/components/site-footer.tsx index 30f3d379..c5bc1ab8 100644 --- a/apps/web/src/components/site-footer.tsx +++ b/apps/web/src/components/site-footer.tsx @@ -4,6 +4,7 @@ import { about } from '../content/about' import { audienceIndexHref } from '../content/segments' import { licence, licenceHref, site } from '../content/site' import { version } from '../content/version' +import { ForumLink } from './forum-link' export function SiteFooter() { return ( @@ -18,6 +19,12 @@ export function SiteFooter() { Who it’s for Extensions Docs + + Demo ↗ + + + Community ↗ + GitHub ↗ diff --git a/apps/web/src/components/site-header.tsx b/apps/web/src/components/site-header.tsx index b737a54c..812ce088 100644 --- a/apps/web/src/components/site-header.tsx +++ b/apps/web/src/components/site-header.tsx @@ -2,11 +2,13 @@ import Link from 'next/link' import { about } from '../content/about' import { audienceIndexHref } from '../content/segments' +import { site } from '../content/site' import { quickstartHref } from '../docs/registry' import { DocsSearch } from './docs-search' import { ForumLink } from './forum-link' import { Logomark } from './logomark' import { MobileMenu } from './mobile-menu' +import { NavLink } from './nav-link' import { ThemeToggle } from './theme-toggle' export function SiteHeader() { @@ -21,10 +23,13 @@ export function SiteHeader() {