Skip to content

Commit e21bbcd

Browse files
feat(site): Discord community banner, header, and footer (#14)
* feat(site): promote Discord community in banner, header, and footer Move the top NEW announcement to the Create Awesome Discord invite. Flagship templates remain featured in the mid-page banner section. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(site): support external CTA on announcement banner for Discord Co-authored-by: Cursor <cursoragent@cursor.com> * fix(site): unblock megalinter for discord banner prefer collaboration wording for cspell and bump next to 15.5.21 Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7163eb6 commit e21bbcd

9 files changed

Lines changed: 106 additions & 69 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"input-otp": "^1.4.2",
5656
"lucide-react": "^0.485.0",
5757
"mermaid": "^11.16.0",
58-
"next": "15.5.20",
58+
"next": "15.5.21",
5959
"next-themes": "^0.4.6",
6060
"react": "^19.1.0",
6161
"react-day-picker": "^8.10.1",

pnpm-lock.yaml

Lines changed: 41 additions & 41 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/page.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { AnimatedTerminal } from '@/components/animated-terminal';
55
import { AnnouncementBanner } from '@/components/announcement-banner';
66
import { ContributorsSection } from '@/components/contributors-section';
77
import { CopyButton } from '@/components/copy-button';
8+
import { DiscordIcon } from '@/components/discord-icon';
89
import { EcosystemSection } from '@/components/ecosystem-section';
910
import { FeaturedTemplate } from '@/components/featured-template';
1011
import { HeroSection } from '@/components/hero-section';
@@ -14,6 +15,7 @@ import { StatsBar } from '@/components/stats-bar';
1415
import { TemplateCategories } from '@/components/template-categories';
1516
import { Button } from '@/components/ui/button';
1617
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
18+
import { DISCORD_INVITE_URL } from '@/lib/community';
1719
import { getTemplatesData } from '@/lib/data';
1820

1921
const PRIMARY_COMMAND = 'uvx create-awesome-python-app my-app';
@@ -31,11 +33,14 @@ export default async function Home() {
3133
<div className="flex min-h-screen flex-col">
3234
<main className="flex-1">
3335
<AnnouncementBanner
36+
icon={<DiscordIcon className="h-5 w-5 shrink-0 text-yellow-200" />}
37+
label="NEW"
3438
message={
35-
<>New: FastAPI Starter — production-ready API with uv, Ruff, pytest, and composable Python extensions.</>
39+
<>Join Create Awesome on Discord — chat, good first issues, and collaboration across Node / Python / V.</>
3640
}
37-
ctaHref="/templates/fastapi-starter"
38-
ctaLabel="Explore template"
41+
ctaHref={DISCORD_INVITE_URL}
42+
ctaLabel="Join Discord"
43+
ctaExternal
3944
/>
4045
<HeroSection
4146
title={

src/components/announcement-banner.tsx

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const bannerVariants = cva(
1111
{
1212
variants: {
1313
variant: {
14-
gradient: 'bg-gradient-to-r from-blue-600 via-blue-500 to-green-600 text-white',
14+
gradient: 'bg-gradient-to-r from-amber-600 via-amber-500 to-teal-600 text-white',
1515
subtle: 'bg-[hsl(var(--background))] border-b border-border/60 text-foreground/90 dark:text-foreground/80',
1616
accent: 'bg-[hsl(var(--primary)/0.15)] text-foreground',
1717
},
@@ -31,6 +31,7 @@ export interface AnnouncementBannerProps extends VariantProps<typeof bannerVaria
3131
message: ReactNode;
3232
ctaHref?: string;
3333
ctaLabel?: string;
34+
ctaExternal?: boolean;
3435
className?: string;
3536
dismissible?: boolean; // future enhancement
3637
}
@@ -41,17 +42,37 @@ export function AnnouncementBanner({
4142
message,
4243
ctaHref,
4344
ctaLabel = 'Learn more',
45+
ctaExternal = false,
4446
className,
4547
variant,
4648
size,
4749
}: AnnouncementBannerProps) {
50+
const ctaClassName =
51+
'inline-flex shrink-0 items-center gap-1 underline underline-offset-4 decoration-white/40 hover:decoration-white transition-colors group';
52+
const ctaArrow = (
53+
<svg
54+
xmlns="http://www.w3.org/2000/svg"
55+
viewBox="0 0 24 24"
56+
fill="none"
57+
stroke="currentColor"
58+
strokeWidth="2"
59+
strokeLinecap="round"
60+
strokeLinejoin="round"
61+
className="h-4 w-4 group-hover:translate-x-1 transition-transform"
62+
aria-hidden="true"
63+
>
64+
<path d="M5 12h14" />
65+
<path d="m12 5 7 7-7 7" />
66+
</svg>
67+
);
68+
4869
return (
4970
<div className={cn(bannerVariants({ variant, size }), className)}>
5071
<div className="absolute inset-0 pointer-events-none select-none overflow-hidden" aria-hidden>
5172
{variant === 'gradient' && (
5273
<>
5374
<div className="absolute inset-0 bg-[url('/grid.svg')] bg-center opacity-40 [mask-image:linear-gradient(180deg,white,rgba(255,255,255,0))]" />
54-
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/20 via-blue-400/15 to-green-500/20 animate-gradient-text" />
75+
<div className="absolute inset-0 bg-gradient-to-r from-amber-500/20 via-amber-400/15 to-teal-500/20 animate-gradient-text" />
5576
</>
5677
)}
5778
</div>
@@ -63,27 +84,18 @@ export function AnnouncementBanner({
6384
</span>
6485
)}
6586
<span className="min-w-0 text-balance">{message}</span>
66-
{ctaHref && (
67-
<Link
68-
href={ctaHref}
69-
className="inline-flex shrink-0 items-center gap-1 underline underline-offset-4 decoration-white/40 hover:decoration-white transition-colors group"
70-
>
71-
{ctaLabel}
72-
<svg
73-
xmlns="http://www.w3.org/2000/svg"
74-
viewBox="0 0 24 24"
75-
fill="none"
76-
stroke="currentColor"
77-
strokeWidth="2"
78-
strokeLinecap="round"
79-
strokeLinejoin="round"
80-
className="h-4 w-4 group-hover:translate-x-1 transition-transform"
81-
>
82-
<path d="M5 12h14" />
83-
<path d="m12 5 7 7-7 7" />
84-
</svg>
85-
</Link>
86-
)}
87+
{ctaHref &&
88+
(ctaExternal ? (
89+
<a href={ctaHref} target="_blank" rel="noreferrer" className={ctaClassName}>
90+
{ctaLabel}
91+
{ctaArrow}
92+
</a>
93+
) : (
94+
<Link href={ctaHref} className={ctaClassName}>
95+
{ctaLabel}
96+
{ctaArrow}
97+
</Link>
98+
))}
8799
</div>
88100
</div>
89101
);

src/components/discord-icon.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { cn } from '@/lib/utils';
2+
3+
/** Discord brand mark (simple-icons path). */
4+
export function DiscordIcon({ className }: { className?: string }) {
5+
return (
6+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className={cn('h-5 w-5', className)}>
7+
<path d="M20.317 4.37a19.8 19.8 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.3 18.3 0 0 0-5.487 0 12.6 12.6 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.7 19.7 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.08.08 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14 14 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.8 19.8 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.548-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
8+
</svg>
9+
);
10+
}

src/components/layout-shell.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export function LayoutShell({ children }: { children: ReactNode }) {
117117
<p className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">Community</p>
118118
<nav className="flex flex-col gap-2">
119119
{[
120+
{ href: 'https://discord.gg/bR5VyATgka', label: 'Discord Community', external: true },
120121
{ href: 'https://github.com/Create-Python-App', label: 'GitHub Organization', external: true },
121122
{
122123
href: 'https://pypi.org/project/create-awesome-python-app/',

src/components/saas-ai-banner.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export function SaasAiBanner() {
4646
{/* Badge */}
4747
<div className="inline-flex items-center gap-2 rounded-full border border-blue-400/30 bg-blue-500/10 px-4 py-1.5 text-sm font-medium text-blue-200 backdrop-blur-sm">
4848
<span></span>
49-
<span>Flagship Template</span>
49+
<span>NEW · Flagship</span>
5050
</div>
5151

5252
{/* Heading */}

src/components/site-header.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { Gauge, Github, Menu, Package, Search } from 'lucide-react';
44
import Link from 'next/link';
55
import { usePathname } from 'next/navigation';
66
import { useEffect, useState } from 'react';
7+
import { DiscordIcon } from '@/components/discord-icon';
78
import { usePerformanceMode } from '@/components/performance-provider';
89
import { ThemeToggle } from '@/components/theme-toggle';
910
import { Button } from '@/components/ui/button';
1011
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
12+
import { DISCORD_INVITE_URL } from '@/lib/community';
1113
import { cn } from '@/lib/utils';
1214

1315
interface NavItem {
@@ -139,6 +141,11 @@ export function SiteHeader({ onOpenCommand }: { onOpenCommand?: () => void }) {
139141
<Package className="h-5 w-5" />
140142
</Button>
141143
</Link>
144+
<Link href={DISCORD_INVITE_URL} target="_blank" rel="noreferrer" aria-label="Discord Community">
145+
<Button variant="ghost" size="icon" className="hidden sm:inline-flex">
146+
<DiscordIcon className="h-5 w-5" />
147+
</Button>
148+
</Link>
142149
<Link href="https://github.com/Create-Python-App" target="_blank" aria-label="GitHub">
143150
<Button variant="ghost" size="icon">
144151
<Github className="h-5 w-5" />

src/lib/community.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/** Canonical Create Awesome Discord invite (shared across CNA / CPA / CVA). */
2+
export const DISCORD_INVITE_URL = 'https://discord.gg/bR5VyATgka';

0 commit comments

Comments
 (0)