Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
35ea3b3
docs(specs): website statusbrew-inspired refactor design
blove May 12, 2026
15e0e87
docs(plans): Phase 1 plan — website tokens + UI primitives
blove May 12, 2026
326b2de
feat(design-tokens): add surfaces/shadows/radius/space + type scale (…
blove May 12, 2026
b9027e4
feat(website): wire new tokens, add cn() helper
blove May 12, 2026
2e910b1
feat(website): add 9 UI primitives for the new design system
blove May 12, 2026
42d9195
feat(website): add /dev/primitives showcase + Playwright coverage
blove May 12, 2026
8528ace
chore(website): drop unused card-primitive className
blove May 12, 2026
58501e3
docs(plans): Phase 2 — Nav + Footer + AnnouncementToast refactor
blove May 12, 2026
a46d961
refactor(website): Phase 2 — Nav/Footer/AnnouncementToast use new pri…
blove May 12, 2026
7affb95
docs(plans): Phase 4 — homepage refactor
blove May 12, 2026
1e12d57
feat(website): Phase 4 — rebuild homepage to 11-section IA
blove May 12, 2026
f729a12
refactor(website): /pricing uses new primitives, drops glass
blove May 12, 2026
8dd3298
refactor(website): /pilot-to-prod uses new primitives, drops blobs
blove May 12, 2026
437de27
refactor(website): /angular, /chat, /render use new primitives
blove May 12, 2026
d3cbc9b
refactor(website): /solutions index + per-library whitepaper variants
blove May 12, 2026
4f92bb4
chore(website): Phase 6 — delete orphaned landing components
blove May 12, 2026
06f7204
refactor(website): /solutions/[slug] uses new primitives; drop framer…
blove May 12, 2026
90d832b
docs(specs): website refactor Phase 7 — docs pages design
blove May 12, 2026
77bf1a9
docs(plans): Phase 7 — docs pages refactor
blove May 12, 2026
5ff41b8
refactor(website): docs chrome — drop glass, use new tokens (Phase 7.1)
blove May 12, 2026
9cecd5e
refactor(website): docs MDX primitives — drop glass, use new tokens (…
blove May 12, 2026
f4fab0a
fix(website): restore copy-to-clipboard button in mdx/CodeBlock (Phas…
blove May 12, 2026
d0e4e28
feat(website): docs ancillary refactor + DocsPrevNext + anchor links …
blove May 12, 2026
5d94c66
fix(website): Phase 7 review fixes — dedupe breadcrumb + normalize im…
blove May 12, 2026
d5908de
docs(specs): Phase 8 — cockpit + final token purge design
blove May 12, 2026
14df6a8
docs(plans): Phase 8 — cockpit refactor + final token purge
blove May 12, 2026
8c3b948
feat(ui-react): extend cssVars with Phase 1 token namespaces (Phase 8.1)
blove May 12, 2026
dad8fb0
refactor(cockpit): migrate to Linear-style light app shell (Phase 8.2)
blove May 12, 2026
738f0a9
chore: purge legacy glass/gradient/glow tokens + orphaned ui-react co…
blove May 12, 2026
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
2 changes: 1 addition & 1 deletion apps/cockpit/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
<body
className="min-h-screen font-sans antialiased"
style={{
background: 'var(--ds-gradient-bg-flow)',
background: 'var(--ds-surface)',
color: 'var(--ds-text-primary)',
}}
>
Expand Down
7 changes: 5 additions & 2 deletions apps/cockpit/src/components/cockpit-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,11 @@ export function CockpitShell({
onClose={() => setIsSidebarOpen(false)}
/>

<section className="grid grid-rows-[auto_1fr] gap-2 p-4 overflow-hidden bg-[var(--ds-glass-bg)] backdrop-blur-[var(--ds-glass-blur)]">
<header className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
<section className="grid grid-rows-[auto_1fr] gap-2 p-4 overflow-hidden bg-[var(--ds-surface)]">
<header
className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between pb-3"
style={{ borderBottom: '1px solid var(--ds-border)' }}
>
<div className="flex items-center gap-3">
<button
className="md:hidden"
Expand Down
6 changes: 3 additions & 3 deletions apps/cockpit/src/components/code-mode/code-mode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ function CodeFileContent({ path, content }: { path: string; content: string | un

return (
<div className="code-mode-block" style={{
borderRadius: 8,
border: '1px solid var(--ds-glass-border)',
boxShadow: 'var(--ds-glass-shadow)',
borderRadius: 'var(--ds-radius-md)',
border: '1px solid var(--ds-border)',
boxShadow: 'var(--ds-shadow-sm)',
overflow: 'hidden',
}}>
<div style={{
Expand Down
11 changes: 5 additions & 6 deletions apps/cockpit/src/components/mobile-nav-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,8 @@ export function MobileNavOverlay({
data-state={state}
className="fixed inset-0 z-50 md:hidden flex flex-col"
style={{
background: 'var(--ds-glass-bg)',
backdropFilter: 'blur(var(--ds-glass-blur))',
WebkitBackdropFilter: 'blur(var(--ds-glass-blur))',
background: 'var(--ds-surface)',
boxShadow: 'var(--ds-shadow-lg)',
opacity: state === 'open' ? 1 : 0,
transform: state === 'open' ? 'translateY(0)' : 'translateY(8px)',
transition: state === 'open'
Expand All @@ -77,7 +76,7 @@ export function MobileNavOverlay({
{/* Header */}
<header
className="flex items-center justify-between px-4 py-3"
style={{ borderBottom: '1px solid var(--ds-glass-border)' }}
style={{ borderBottom: '1px solid var(--ds-border)' }}
>
<p
className="font-mono text-xs font-semibold uppercase tracking-wide"
Expand Down Expand Up @@ -118,8 +117,8 @@ export function MobileNavOverlay({
<div
key={product.product}
style={{
background: 'var(--ds-glass-bg)',
border: '1px solid var(--ds-glass-border)',
background: 'var(--ds-surface-tinted)',
border: '1px solid var(--ds-border)',
borderRadius: 10,
padding: 12,
}}
Expand Down
7 changes: 3 additions & 4 deletions apps/cockpit/src/components/modes/mode-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ export function ModeSwitcher<T extends string>({
gap: 0,
padding: 3,
borderRadius: 999,
background: 'var(--ds-glass-bg)',
border: '1px solid var(--ds-glass-border)',
boxShadow: 'var(--ds-glass-shadow)',
background: 'var(--ds-surface-tinted)',
border: '1px solid var(--ds-border)',
}}
>
{/* Sliding indicator */}
Expand Down Expand Up @@ -75,7 +74,7 @@ export function ModeSwitcher<T extends string>({
borderRadius: 999,
border: 'none',
background: 'transparent',
color: isActive ? '#fff' : 'var(--ds-text-secondary)',
color: isActive ? 'var(--ds-text-inverted)' : 'var(--ds-text-secondary)',
fontFamily: 'var(--ds-font-mono)',
fontSize: '0.8rem',
fontWeight: isActive ? 500 : 400,
Expand Down
9 changes: 7 additions & 2 deletions apps/cockpit/src/components/sidebar/cockpit-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ export function CockpitSidebar({
return (
<aside
aria-label="Cockpit sidebar"
className="grid gap-4 py-6 px-0 border-r border-[var(--ds-glass-border)] bg-[var(--ds-glass-bg)] backdrop-blur-[var(--ds-glass-blur)] content-start overflow-y-auto"
style={{ position: 'sticky', top: 0, minHeight: '100vh' }}
className="grid gap-4 py-6 px-0 border-r bg-[var(--ds-surface-tinted)] content-start overflow-y-auto"
style={{
position: 'sticky',
top: 0,
minHeight: '100vh',
borderRightColor: 'var(--ds-border-strong)',
}}
>
<header className="flex items-center justify-between px-4">
<p className="text-[var(--ds-text-muted)] font-mono text-xs font-medium tracking-wide uppercase">Cockpit</p>
Expand Down
13 changes: 5 additions & 8 deletions apps/cockpit/src/components/sidebar/language-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ export function LanguagePicker({ manifest, entry }: LanguagePickerProps) {
style={{
padding: '4px 10px',
borderRadius: 6,
border: '1px solid var(--ds-accent-border)',
background: 'rgba(255, 255, 255, 0.7)',
backdropFilter: 'blur(var(--ds-glass-blur))',
border: '1px solid var(--ds-border)',
background: 'var(--ds-surface)',
color: 'var(--ds-text-secondary)',
fontSize: '0.75rem',
fontFamily: 'var(--ds-font-mono)',
Expand All @@ -73,11 +72,9 @@ export function LanguagePicker({ manifest, entry }: LanguagePickerProps) {
minWidth: 120,
padding: 4,
borderRadius: 8,
border: '1px solid var(--ds-glass-border)',
background: 'rgba(255, 255, 255, 0.92)',
backdropFilter: 'blur(var(--ds-glass-blur))',
WebkitBackdropFilter: 'blur(var(--ds-glass-blur))',
boxShadow: 'var(--ds-glass-shadow)',
border: '1px solid var(--ds-border)',
background: 'var(--ds-surface)',
boxShadow: 'var(--ds-shadow-md)',
zIndex: 50,
}}
>
Expand Down
3 changes: 2 additions & 1 deletion apps/cockpit/src/components/sidebar/navigation-groups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,13 @@ function ProductGroup({
aria-current={isActive ? 'page' : undefined}
style={{
display: 'block',
padding: '5px 16px',
padding: isActive ? '5px 16px 5px 14px' : '5px 16px',
margin: '0 8px',
borderRadius: 6,
fontSize: '0.825rem',
color: isActive ? 'var(--ds-accent)' : 'var(--ds-text-secondary)',
background: isActive ? 'var(--ds-accent-surface)' : 'transparent',
borderLeft: isActive ? '2px solid var(--ds-accent)' : 'none',
textDecoration: 'none',
transition: 'all 0.15s',
}}
Expand Down
2 changes: 1 addition & 1 deletion apps/cockpit/src/components/ui/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const TabsList = React.forwardRef<
<TabsPrimitive.List
ref={ref}
className={cn(
'inline-flex items-center gap-0 border-b border-[var(--ds-accent-border)]',
'inline-flex items-center gap-0 border-b border-[var(--ds-border)]',
className
)}
{...props}
Expand Down
68 changes: 68 additions & 0 deletions apps/website/e2e/docs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { test, expect } from '@playwright/test';

test.describe('Docs landing page', () => {
test('renders library cards + popular topics + search prompt', async ({ page }) => {
await page.goto('/docs');
// Header
await expect(page.locator('#docs-heading')).toBeVisible();
// Library grid — all 4 libraries (agent, render, chat, ag-ui)
await expect(page.locator('main a[href="/docs/agent/getting-started/introduction"]').first()).toBeVisible();
await expect(page.locator('main a[href="/docs/render/getting-started/introduction"]').first()).toBeVisible();
await expect(page.locator('main a[href="/docs/chat/getting-started/introduction"]').first()).toBeVisible();
await expect(page.locator('main a[href="/docs/ag-ui/getting-started/introduction"]').first()).toBeVisible();
// Popular topics — 3 cards
await expect(page.getByText('Streaming with signals').first()).toBeVisible();
await expect(page.getByText('Generative UI fundamentals').first()).toBeVisible();
await expect(page.getByText('Production patterns').first()).toBeVisible();
// Search prompt
await expect(page.getByText('Looking for something specific?').first()).toBeVisible();
});
});

test.describe('Docs slug page', () => {
const route = '/docs/agent/getting-started/introduction';

test('renders breadcrumb + h1 + sidebar', async ({ page }) => {
await page.goto(route);
await expect(page.locator('aside').first()).toBeVisible();
await expect(page.locator('nav[aria-label="Breadcrumb"]').first()).toBeVisible();
await expect(page.locator('article').first()).toBeVisible();
});

test('breadcrumb shows the library + page title', async ({ page }) => {
await page.goto(route);
const breadcrumb = page.locator('nav[aria-label="Breadcrumb"]').first();
await expect(breadcrumb).toContainText('Docs');
});

test('renders DocsPrevNext at the bottom (next-only for the first page)', async ({ page }) => {
await page.goto(route);
const prevNext = page.locator('nav[aria-label="Previous and next page"]').first();
await expect(prevNext).toBeVisible();
});

test('headings have ID anchors for hash links', async ({ page }) => {
await page.goto(route);
const h2 = page.locator('article h2').first();
await expect(h2).toBeVisible();
const id = await h2.getAttribute('id');
expect(id).toBeTruthy();
expect(id?.length).toBeGreaterThan(0);
});

test('breadcrumb renders exactly once', async ({ page }) => {
await page.goto('/docs/agent/getting-started/introduction');
await expect(page.locator('nav[aria-label="Breadcrumb"]')).toHaveCount(1);
});
});

test.describe('Docs search', () => {
test('Cmd+K opens the search modal', async ({ page, browserName }) => {
await page.goto('/docs/agent/getting-started/introduction');
// Mac uses Meta; other platforms emulate the same shortcut via keydown.
const modifier = browserName === 'webkit' ? 'Meta' : 'Control';
await page.keyboard.press(`${modifier}+KeyK`);
// The modal mounts somewhere — assert by visible input role with placeholder text.
await expect(page.locator('input[placeholder*="Search"], input[type="search"]').first()).toBeVisible({ timeout: 3000 });
});
});
70 changes: 70 additions & 0 deletions apps/website/e2e/primitives.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { test, expect } from '@playwright/test';

test.describe('UI primitives showcase', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/dev/primitives');
});

test('renders the page heading', async ({ page }) => {
await expect(page.locator('[data-testid="primitives-page-title"]')).toBeVisible();
});

test('renders LogoMark', async ({ page }) => {
// Scope to the dev showcase main content (Nav + Footer also render LogoMark site-wide).
const logos = page.locator('main [data-ui="logo-mark"]');
await expect(logos).toHaveCount(3);
});

test('renders Eyebrow in all tones', async ({ page }) => {
// Scope to main; Footer renders accent-toned Eyebrows for its column headings.
await expect(page.locator('main [data-ui="eyebrow"][data-tone="muted"]').first()).toBeVisible();
await expect(page.locator('main [data-ui="eyebrow"][data-tone="accent"]').first()).toBeVisible();
await expect(page.locator('main [data-ui="eyebrow"][data-tone="angular"]').first()).toBeVisible();
});

test('renders Pill in all variants', async ({ page }) => {
await expect(page.locator('[data-ui="pill"][data-variant="neutral"]')).toBeVisible();
await expect(page.locator('[data-ui="pill"][data-variant="accent"]')).toBeVisible();
await expect(page.locator('[data-ui="pill"][data-variant="angular"]')).toBeVisible();
});

test('renders Button variants', async ({ page }) => {
await expect(page.locator('[data-ui="button"][data-variant="primary"]').first()).toBeVisible();
await expect(page.locator('[data-ui="button"][data-variant="secondary"]')).toBeVisible();
await expect(page.locator('[data-ui="button"][data-variant="ghost"]')).toBeVisible();
// Large primary renders as an <a> with href
const linkButton = page.locator('a[data-ui="button"][data-size="lg"]');
await expect(linkButton).toHaveAttribute('href', '/docs');
});

test('renders Card variants including hoverable', async ({ page }) => {
const cards = page.locator('[data-ui="card"]');
await expect(cards).toHaveCount(3);
await expect(page.locator('[data-ui="card"][data-hoverable]')).toHaveCount(1);
});

test('renders BrowserFrame with URL pill', async ({ page }) => {
const frame = page.locator('[data-ui="browser-frame"]');
await expect(frame).toBeVisible();
await expect(frame).toContainText('cockpit.cacheplane.ai');
});

test('renders Section with tinted surface variant', async ({ page }) => {
await expect(page.locator('[data-ui="section"][data-surface="tinted"]')).toBeVisible();
});

test('FAQ items toggle open and closed', async ({ page }) => {
const firstItem = page.locator('[data-ui="faq-item"]').first();
await expect(firstItem).not.toHaveAttribute('open', '');
await firstItem.locator('summary').click();
await expect(firstItem).toHaveAttribute('open', '');
await firstItem.locator('summary').click();
await expect(firstItem).not.toHaveAttribute('open', '');
});

test('FAQ summary is keyboard-focusable', async ({ page }) => {
const summary = page.locator('[data-ui="faq-item"]').first().locator('summary');
await summary.focus();
await expect(summary).toBeFocused();
});
});
31 changes: 31 additions & 0 deletions apps/website/e2e/solutions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { test, expect } from '@playwright/test';

const SLUGS = ['compliance', 'analytics', 'customer-support'];

test.describe('Solutions detail pages', () => {
for (const slug of SLUGS) {
test(`/solutions/${slug} renders the new sections`, async ({ page }) => {
await page.goto(`/solutions/${slug}`);
// Hero
await expect(page.locator('#solution-hero-heading')).toBeVisible();
// Pain points section heading
await expect(page.getByText('Why this is hard today.').first()).toBeVisible();
// Architecture section heading
await expect(page.getByText('How the three libraries compose.').first()).toBeVisible();
// Capabilities section heading
await expect(page.getByText('Capabilities the framework delivers.').first()).toBeVisible();
// FinalCTA picks up the per-solution headline
await expect(page.locator('#final-cta-heading')).toBeVisible();
});
}

test('/solutions/compliance shows the @ngaf/langgraph package pill', async ({ page }) => {
await page.goto('/solutions/compliance');
await expect(page.getByText('@ngaf/langgraph').first()).toBeVisible();
});

test('/solutions/unknown-slug returns 404', async ({ page }) => {
const response = await page.goto('/solutions/unknown-slug');
expect(response?.status()).toBe(404);
});
});
16 changes: 9 additions & 7 deletions apps/website/e2e/website.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import { test, expect } from '@playwright/test';

test('landing page renders hero headline', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toBeVisible();
const headline = await page.locator('h1').textContent();
expect(headline).toContain('Angular');
await expect(page.locator('#hero-heading')).toBeVisible();
const headline = await page.locator('#hero-heading').textContent();
expect(headline?.toLowerCase()).toContain('angular');
});

test('landing page renders architecture section', async ({ page }) => {
test('landing page renders differentiator section', async ({ page }) => {
await page.goto('/');
await expect(page.getByText('Architecture').first()).toBeVisible();
await expect(page.getByText('Built for Angular, not retrofitted.').first()).toBeVisible();
});

test('landing page renders libraries section', async ({ page }) => {
test('landing page renders feature blocks (Stream/Render/Ship)', async ({ page }) => {
await page.goto('/');
await expect(page.getByText('Four libraries. One architecture.').first()).toBeVisible();
await expect(page.locator('#stream-heading')).toBeVisible();
await expect(page.locator('#render-heading')).toBeVisible();
await expect(page.locator('#ship-heading')).toBeVisible();
});

test('pricing page shows plan cards', async ({ page }) => {
Expand Down
Loading
Loading