Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **Awesome GitHub Site: Phosphor Icon System** — Replaced all emoji icons (🤖 📖 💬 ✨ 🛡️ ⚙️ 🧩 🔧 🗺️ ✅ 📚 🍳 ☀️ 🌙) with Phosphor SVG icons via a new `Icon.astro` component that reads from `@phosphor-icons/core` at build time — zero runtime JS. Covers catalogue type icons, learning track icons, getting-started cards, cookbook placeholder, and theme toggle buttons. ([#844](https://github.com/lightspeedwp/.github/issues/844), [#843](https://github.com/lightspeedwp/.github/pull/843))

### Fixed

- **Awesome GitHub Site: Mobile Nav Menu** — Fixed `z-index` on the fixed-position mobile menu so it renders above page content; added body scroll-lock (`overflow: hidden`) while the menu is open to prevent background scroll. ([#844](https://github.com/lightspeedwp/.github/issues/844), [#843](https://github.com/lightspeedwp/.github/pull/843))

---

- **Awesome GitHub Site: Complete Astro Rebuild** — Rebuilt the Awesome GitHub site from a React/Babel prototype into a production-ready Astro 5 static site. Includes: a fully-typed TypeScript data layer (`catalogue.ts`, `learn.ts`, `glossary.ts`) porting 110+ catalogue items across 8 categories; Svelte `SearchPalette` component with Cmd/Ctrl+K activation; 252 statically-generated pages (catalogue, learn tracks, glossary, cookbook, getting-started, why); detail pages with Install-in-VS-Code, copy-URL, copy-file, and View-on-GitHub action buttons; mobile hamburger navigation with animated X icon and keyboard Escape support; expanded footer with brand and navigation columns; and a `why.astro` editorial page. ([#841](https://github.com/lightspeedwp/.github/pull/841), [#842](https://github.com/lightspeedwp/.github/issues/842))

- **LightSpeedWP Agency Homepage: Complete Component System** — Built a production-ready homepage for the LightSpeedWP Agency website with 9 modular Astro components (Nav, HeroPlanner, TrustStrip, SolutionPaths, WhyLightSpeed, FAQ, FinalCTA, Footer, ContactOverlay). Features include sticky navigation with scroll detection, mobile drawer with theme toggle, AI project planner with form validation, responsive stats grid, 4 solution path cards with highlighting, single-open accordion FAQ with 7 questions, contact modal with success state, and comprehensive design system with light/dark mode support across 9 responsive breakpoints. All components include WCAG 2.2 AA accessibility attributes, semantic HTML5, and CSS variable theming for consistent branding.
Expand Down
7 changes: 7 additions & 0 deletions website/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
},
"dependencies": {
"@astrojs/svelte": "^5.7.3",
"@phosphor-icons/core": "^2.1.1",
"astro": "^5.11.0",
"gray-matter": "^4.0.3",
"svelte": "^5.0.0"
Expand Down
45 changes: 29 additions & 16 deletions website/src/components/AwesomeGithub/AwesomeGithubNav.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
---
import Icon from "./Icon.astro";

interface Props {
base: string;
pathname: string;
Expand Down Expand Up @@ -59,8 +61,8 @@ const isActive = (linkPath: string, exact: boolean): boolean => {
aria-pressed="false"
aria-label="Toggle light and dark mode"
>
<span class="theme-icon-light" aria-hidden="true">☀️</span>
<span class="theme-icon-dark" aria-hidden="true">🌙</span>
<span class="theme-icon-light"><Icon name="sun" size={18} /></span>
<span class="theme-icon-dark"><Icon name="moon" size={18} /></span>
</button>
<button
class="ag-nav-hamburger"
Expand All @@ -82,31 +84,41 @@ const isActive = (linkPath: string, exact: boolean): boolean => {
const menu = document.getElementById("ag-nav-menu");

if (hamburger && menu) {
const closeMenu = () => {
hamburger.setAttribute("aria-expanded", "false");
hamburger.setAttribute("aria-label", "Open navigation menu");
menu.classList.remove("ag-nav-menu--open");
document.body.style.overflow = "";
};
hamburger.addEventListener("click", () => {
const isOpen = hamburger.getAttribute("aria-expanded") === "true";
hamburger.setAttribute("aria-expanded", String(!isOpen));
hamburger.setAttribute("aria-label", isOpen ? "Open navigation menu" : "Close navigation menu");
menu.classList.toggle("ag-nav-menu--open", !isOpen);
if (isOpen) {
closeMenu();
} else {
hamburger.setAttribute("aria-expanded", "true");
hamburger.setAttribute("aria-label", "Close navigation menu");
menu.classList.add("ag-nav-menu--open");
document.body.style.overflow = "hidden";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear scroll-lock when leaving mobile width

If a visitor opens the drawer at ≤768px and then rotates/resizes to desktop, this inline overflow: hidden remains on body while the hamburger is hidden by the media query, so the desktop page can become unscrollable until the user happens to press Escape or follows a nav link. Please add a resize/media-query cleanup path, or only apply the lock while the mobile query still matches.

Useful? React with 👍 / 👎.

}
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Close on link click (mobile)
menu.querySelectorAll("a").forEach((link) => {
link.addEventListener("click", () => {
hamburger.setAttribute("aria-expanded", "false");
hamburger.setAttribute("aria-label", "Open navigation menu");
menu.classList.remove("ag-nav-menu--open");
});
link.addEventListener("click", () => closeMenu());
});

// Close on Escape
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && hamburger.getAttribute("aria-expanded") === "true") {
hamburger.setAttribute("aria-expanded", "false");
hamburger.setAttribute("aria-label", "Open navigation menu");
menu.classList.remove("ag-nav-menu--open");
closeMenu();
hamburger.focus();
}
});

// Clear scroll-lock if the viewport widens past the mobile breakpoint while the menu is open
window.matchMedia("(max-width: 768px)").addEventListener("change", (e) => {
if (!e.matches) closeMenu();
});
}
</script>

Expand Down Expand Up @@ -248,9 +260,9 @@ const isActive = (linkPath: string, exact: boolean): boolean => {

.theme-icon-light,
.theme-icon-dark {
font-size: 16px;
line-height: 1;
display: inline-block;
display: inline-flex;
align-items: center;
justify-content: center;
}

:global([data-theme="dark"]) .theme-icon-light {
Expand Down Expand Up @@ -319,6 +331,7 @@ const isActive = (linkPath: string, exact: boolean): boolean => {
left: 0;
right: 0;
bottom: 0;
z-index: 99;
flex-direction: column;
gap: 0;
background: var(--bg);
Expand Down
43 changes: 43 additions & 0 deletions website/src/components/AwesomeGithub/Icon.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";

interface Props {
name: string;
size?: number;
weight?: "regular" | "bold" | "fill" | "light" | "thin";
class?: string;
}

const { name, size = 24, weight = "regular", class: className } = Astro.props;

let svgContent = "";
try {
const svgPath = fileURLToPath(
import.meta.resolve(`@phosphor-icons/core/assets/${weight}/${name}.svg`),
);
const raw = readFileSync(svgPath, "utf-8");
svgContent = raw
.replace(/\bwidth="\d+"/, `width="${size}"`)
.replace(/\bheight="\d+"/, `height="${size}"`);
} catch (err) {
console.warn(`[Icon.astro] Missing Phosphor icon: "${weight}/${name}"`, err);
svgContent = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="${size}" height="${size}" fill="currentColor"></svg>`;
}
---

<span class:list={["ph-icon", className]} aria-hidden="true" set:html={svgContent} />

<style>
.ph-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
line-height: 1;
}

.ph-icon svg {
display: block;
}
</style>
7 changes: 7 additions & 0 deletions website/src/lib/learn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ export function readingTime(body: string | undefined | null): number {
return Math.max(1, Math.round(words / 200));
}

export const TRACK_ICONS: Record<string, string> = {
oriented: "compass",
governance: "shield-star",
quality: "check-circle",
agents: "robot",
};

export function getTrack(id: string): LearnTrack | undefined {
return LEARN_TRACKS.find((t) => t.id === id);
}
Expand Down
16 changes: 8 additions & 8 deletions website/src/lib/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ export interface ResourceTypeInfo {
}

const RESOURCE_TYPES: Record<string, ResourceTypeInfo> = {
agents: { type: "agents", label: "Agents", icon: "🤖", description: "AI agent specifications and configurations", blurb: "Specialised AI agents with defined behaviour, scope, and escalation rules." },
instructions: { type: "instructions", label: "Instructions", icon: "📖", description: "Organisation-wide instructions and standards", blurb: "Canonical coding, accessibility, and WordPress standards Copilot must follow." },
prompts: { type: "prompts", label: "Prompts", icon: "💬", description: "Prompt library and templates", blurb: "Reusable prompt templates you can grab and run for common engineering tasks." },
skills: { type: "skills", label: "Skills", icon: "✨", description: "Self-contained reusable skills", blurb: "Portable, self-contained skill packages the team can run on demand." },
hooks: { type: "hooks", label: "Hooks", icon: "🛡️", description: "Portable hooks and guardrails", blurb: "Pre-commit and lint guardrails that enforce quality before code lands." },
workflows: { type: "workflows", label: "Workflows", icon: "⚙️", description: "Portable agentic workflows", blurb: "Portable agentic workflow specs, each paired with a runnable GitHub Action." },
plugins: { type: "plugins", label: "Plugins", icon: "🧩", description: "Installable plugin packs", blurb: "Installable, versioned plugin packs bundling governance and AI-ops." },
tools: { type: "tools", label: "Tools", icon: "🔧", description: "Utility scripts and tools", blurb: "The toolchain layer — AI defaults, scripts, schemas, and editor config." },
agents: { type: "agents", label: "Agents", icon: "robot", description: "AI agent specifications and configurations", blurb: "Specialised AI agents with defined behaviour, scope, and escalation rules." },
instructions: { type: "instructions", label: "Instructions", icon: "book-open", description: "Organisation-wide instructions and standards", blurb: "Canonical coding, accessibility, and WordPress standards Copilot must follow." },
prompts: { type: "prompts", label: "Prompts", icon: "chat-text", description: "Prompt library and templates", blurb: "Reusable prompt templates you can grab and run for common engineering tasks." },
skills: { type: "skills", label: "Skills", icon: "sparkle", description: "Self-contained reusable skills", blurb: "Portable, self-contained skill packages the team can run on demand." },
hooks: { type: "hooks", label: "Hooks", icon: "shield-check", description: "Portable hooks and guardrails", blurb: "Pre-commit and lint guardrails that enforce quality before code lands." },
workflows: { type: "workflows", label: "Workflows", icon: "gear", description: "Portable agentic workflows", blurb: "Portable agentic workflow specs, each paired with a runnable GitHub Action." },
plugins: { type: "plugins", label: "Plugins", icon: "puzzle-piece", description: "Installable plugin packs", blurb: "Installable, versioned plugin packs bundling governance and AI-ops." },
tools: { type: "tools", label: "Tools", icon: "wrench", description: "Utility scripts and tools", blurb: "The toolchain layer — AI defaults, scripts, schemas, and editor config." },
};

export function getAvailableResourceTypes(): ResourceTypeInfo[] {
Expand Down
8 changes: 5 additions & 3 deletions website/src/pages/c/[type]/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import AwesomeGithubLayout from "../../../layouts/AwesomeGithubLayout.astro";
import AwesomeGithubButton from "../../../components/AwesomeGithub/AwesomeGithubButton.astro";
import AwesomeGithubCard from "../../../components/AwesomeGithub/AwesomeGithubCard.astro";
import Icon from "../../../components/AwesomeGithub/Icon.astro";
import { getResourcesByType, getAvailableResourceTypes } from "../../../lib/resources";

const base = import.meta.env.BASE_URL;
Expand Down Expand Up @@ -49,7 +50,7 @@ export async function getStaticPaths() {

<section class="ag-hero">
<div class="ag-container">
<div class="ag-section-label">{currentType.icon} {currentType.label.toUpperCase()}</div>
<div class="ag-section-label"><Icon name={currentType.icon} size={16} /> {currentType.label.toUpperCase()}</div>
<h1 class="ag-h1">{currentType.label}</h1>
<p class="ag-lead">
Browse all available {currentType.label.toLowerCase()} from the LightSpeed control plane.
Expand Down Expand Up @@ -108,7 +109,7 @@ export async function getStaticPaths() {
.filter(t => t.type !== type)
.map((t) => (
<a href={`${base}c/${t.type}/`} class="ag-type-card">
<div class="ag-type-icon" aria-hidden="true">{t.icon}</div>
<div class="ag-type-icon"><Icon name={t.icon} size={32} /></div>
<h3 class="ag-type-name">{t.label}</h3>
<p class="ag-type-count">{t.count} resource{t.count !== 1 ? 's' : ''}</p>
</a>
Expand Down Expand Up @@ -335,7 +336,8 @@ export async function getStaticPaths() {
}

.ag-type-icon {
font-size: 32px;
display: flex;
color: var(--accent);
}

.ag-type-name {
Expand Down
5 changes: 3 additions & 2 deletions website/src/pages/cookbook/[slug].astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
import AwesomeGithubLayout from "../../layouts/AwesomeGithubLayout.astro";
import AwesomeGithubButton from "../../components/AwesomeGithub/AwesomeGithubButton.astro";
import Icon from "../../components/AwesomeGithub/Icon.astro";
import { COOKBOOK_RECIPES, getRecipe } from "../../lib/learn";

const base = import.meta.env.BASE_URL;
Expand Down Expand Up @@ -47,7 +48,7 @@ const githubUrl = `https://github.com/lightspeedwp/.github/blob/develop/${recipe
<div class="recipe-body">
<div class="recipe-container recipe-container--narrow">
<div class="recipe-placeholder">
<div class="recipe-placeholder-icon" aria-hidden="true">🍳</div>
<div class="recipe-placeholder-icon"><Icon name="cooking-pot" size={40} /></div>
<h2>Recipe lives on GitHub</h2>
<p>
This recipe is maintained as a real Markdown file in the control plane repository at
Expand Down Expand Up @@ -174,7 +175,7 @@ const githubUrl = `https://github.com/lightspeedwp/.github/blob/develop/${recipe
margin-bottom: var(--space-12);
}

.recipe-placeholder-icon { font-size: 40px; }
.recipe-placeholder-icon { display: flex; justify-content: center; color: var(--accent); }

.recipe-placeholder h2 {
font-size: var(--fs-h4);
Expand Down
12 changes: 7 additions & 5 deletions website/src/pages/getting-started.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
import AwesomeGithubLayout from "../layouts/AwesomeGithubLayout.astro";
import AwesomeGithubButton from "../components/AwesomeGithub/AwesomeGithubButton.astro";
import Icon from "../components/AwesomeGithub/Icon.astro";
import { cloneCmd } from "../lib/catalogue";

const base = import.meta.env.BASE_URL;
Expand Down Expand Up @@ -108,22 +109,22 @@ const steps = [
<p class="gs-lead">Explore the catalogue, follow the learning tracks, or run a cookbook recipe.</p>
<div class="gs-next-grid">
<a href={`${base}c/agents/`} class="gs-next-card">
<div class="gs-next-icon" aria-hidden="true">🤖</div>
<div class="gs-next-icon"><Icon name="robot" size={28} /></div>
<h3>Browse Agents</h3>
<p>Specialised AI agents with defined behaviour, scope, and escalation rules.</p>
</a>
<a href={`${base}learn/`} class="gs-next-card">
<div class="gs-next-icon" aria-hidden="true">📚</div>
<div class="gs-next-icon"><Icon name="books" size={28} /></div>
<h3>Learning Centre</h3>
<p>Four tracks covering architecture, governance, quality, and agents.</p>
</a>
<a href={`${base}cookbook/`} class="gs-next-card">
<div class="gs-next-icon" aria-hidden="true">🍳</div>
<div class="gs-next-icon"><Icon name="cooking-pot" size={28} /></div>
<h3>Cookbook</h3>
<p>Step-by-step playbooks for common engineering scenarios.</p>
</a>
<a href={`${base}glossary/`} class="gs-next-card">
<div class="gs-next-icon" aria-hidden="true">📖</div>
<div class="gs-next-icon"><Icon name="book-open" size={28} /></div>
<h3>Glossary</h3>
<p>Plain-language definitions for every term in the control plane.</p>
</a>
Expand Down Expand Up @@ -341,7 +342,8 @@ const steps = [
}

.gs-next-icon {
font-size: 28px;
display: flex;
color: var(--accent);
}

.gs-next-card h3 {
Expand Down
13 changes: 4 additions & 9 deletions website/src/pages/learn/[track]/index.astro
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
import AwesomeGithubLayout from "../../../layouts/AwesomeGithubLayout.astro";
import AwesomeGithubButton from "../../../components/AwesomeGithub/AwesomeGithubButton.astro";
import { LEARN_TRACKS, getTrack } from "../../../lib/learn";
import Icon from "../../../components/AwesomeGithub/Icon.astro";
import { LEARN_TRACKS, getTrack, TRACK_ICONS } from "../../../lib/learn";

const base = import.meta.env.BASE_URL;
const { track: trackId } = Astro.params;
Expand All @@ -13,12 +14,6 @@ export async function getStaticPaths() {
const track = getTrack(trackId!);
if (!track) return Astro.redirect(`${base}learn/`);

const trackIcons: Record<string, string> = {
oriented: "🗺️",
governance: "🛡️",
quality: "✅",
agents: "🤖",
};
---

<AwesomeGithubLayout
Expand All @@ -37,7 +32,7 @@ const trackIcons: Record<string, string> = {

<header class="track-hero">
<div class="track-container">
<div class="track-icon" aria-hidden="true">{trackIcons[track.id] || "📘"}</div>
<div class="track-icon"><Icon name={TRACK_ICONS[track.id] || "book-open"} size={48} /></div>
<div class="track-eyebrow">Learning track</div>
<h1 class="track-h1">{track.label}</h1>
<p class="track-blurb">{track.blurb}</p>
Expand Down Expand Up @@ -146,7 +141,7 @@ const trackIcons: Record<string, string> = {
padding: var(--space-24) var(--gutter);
}

.track-icon { font-size: 48px; margin-bottom: var(--space-4); display: block; }
.track-icon { display: flex; margin-bottom: var(--space-4); color: var(--c-light-blue); }

.track-eyebrow {
display: inline-flex;
Expand Down
Loading
Loading