diff --git a/examples/workbench/web-design-guidelines/README.md b/examples/workbench/web-design-guidelines/README.md new file mode 100644 index 0000000..565b62e --- /dev/null +++ b/examples/workbench/web-design-guidelines/README.md @@ -0,0 +1,159 @@ +# web-design-guidelines eval + +Eval suite for [`vercel-labs/agent-skills/web-design-guidelines`](https://github.com/vercel-labs/agent-skills) — a skill that reviews UI files for Vercel Web Interface Guidelines compliance. + +## Cases + +Nine cases, ~5 seeded violations each, one TSX sample per case. Each case targets a focused rule family so the agent isn't overwhelmed and we can isolate which families it handles well vs. poorly. + +### `review-product-card` — Accessibility + Focus States + +Sample: `workspace/ProductCard.tsx` + +| Line | Violation | Rule | +|---|---|---| +| 15 | `` without `alt` | Images need `alt` (or `alt=""` if decorative) | +| 18 | `
` for action | `
+ ); +} diff --git a/examples/workbench/web-design-guidelines/workspace/HeroSection.tsx b/examples/workbench/web-design-guidelines/workspace/HeroSection.tsx new file mode 100644 index 0000000..9a01e04 --- /dev/null +++ b/examples/workbench/web-design-guidelines/workspace/HeroSection.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +export function HeroSection() { + return ( +
+ +
+

Welcome to Acme

+

Build faster. Ship faster.

+ +
+
+ + + +
+ Decorative graphic +
+ ); +} diff --git a/examples/workbench/web-design-guidelines/workspace/LoadingScreen.tsx b/examples/workbench/web-design-guidelines/workspace/LoadingScreen.tsx new file mode 100644 index 0000000..e6fd976 --- /dev/null +++ b/examples/workbench/web-design-guidelines/workspace/LoadingScreen.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +type Props = { + fileSize: number; + recentFiles: string[]; +}; + +export function LoadingScreen({ fileSize, recentFiles }: Props) { + return ( +
+

Setting things up

+

Loading...

+

Welcome to "Acme Cloud" - your files, anywhere.

+

Uploading a {fileSize} MB file.

+
+ {recentFiles[0]} + +
+ +
+ ); +} diff --git a/examples/workbench/web-design-guidelines/workspace/ProductCard.tsx b/examples/workbench/web-design-guidelines/workspace/ProductCard.tsx new file mode 100644 index 0000000..fcc4e7d --- /dev/null +++ b/examples/workbench/web-design-guidelines/workspace/ProductCard.tsx @@ -0,0 +1,39 @@ +import React from 'react'; + +type Props = { + name: string; + description: string; + imageUrl: string; + onAddToCart: () => void; + onToggleWishlist: () => void; + onQtyChange: (e: React.ChangeEvent) => void; +}; + +export function ProductCard({ name, description, imageUrl, onAddToCart, onToggleWishlist, onQtyChange }: Props) { + return ( +
+ +

{name}

+

{description}

+
+ Add to Cart +
+ + + +
+ ); +} + +function HeartIcon() { + return ; +} diff --git a/examples/workbench/web-design-guidelines/workspace/SearchPage.tsx b/examples/workbench/web-design-guidelines/workspace/SearchPage.tsx new file mode 100644 index 0000000..f15d56b --- /dev/null +++ b/examples/workbench/web-design-guidelines/workspace/SearchPage.tsx @@ -0,0 +1,39 @@ +import React, { useState } from 'react'; + +type Result = { + id: string; + title: string; + price: number; + publishedAt: Date; +}; + +export function SearchPage({ results }: { results: Result[] }) { + const [query, setQuery] = useState(''); + const [page, setPage] = useState(1); + + function handleDelete(id: string) { + fetch(`/api/items/${id}`, { method: 'DELETE' }); + } + + return ( +
+

Search Acme Cloud

+ setQuery(e.target.value)} + placeholder="Search…" + /> +

Showing page {page}

+
    + {results.map((r) => ( +
  • +

    {r.title}

    + ${r.price.toFixed(2)} + + +
  • + ))} +
+
+ ); +} diff --git a/examples/workbench/web-design-guidelines/workspace/ThemeToggle.tsx b/examples/workbench/web-design-guidelines/workspace/ThemeToggle.tsx new file mode 100644 index 0000000..76bfd9d --- /dev/null +++ b/examples/workbench/web-design-guidelines/workspace/ThemeToggle.tsx @@ -0,0 +1,30 @@ +import React, { useState } from 'react'; + +const themes = ['light', 'dark', 'system'] as const; +type Theme = (typeof themes)[number]; + +export function ThemeToggle() { + const initial = (localStorage.getItem('theme') as Theme) || 'light'; + const [theme, setTheme] = useState(initial); + const [accentColor] = useState('blue'); + + return ( +
+ + + + + + + +
+ ); +}