关于 primer-design
指导实现符合 GitHub Primer 品牌规范的前端界面的设计技能,面向构建组件、页面或应用的开发者。内容涵盖 Primer React 组件、Octicons、布局样式、无障碍要求及测试步骤。
主要功能
- 01遵循 Primer 品牌规范
- 02使用 Primer React 组件
- 03使用 Octicons 图标
- 04提供布局与样式指导
- 05涵盖无障碍检查
- 06要求构建和运行测试
适用场景
- 01按 Primer 规范构建前端页面
- 02使用 Primer 组件实现界面布局
- 03为交互界面检查无障碍要求
- 04完成界面改动后的构建与验证
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ bas/ agent-skills --skill primer-design安装后在 Claude Code 中输入/primer-design确认已加载。
详细介绍
Primer 设计说明
此技能旨在指导创建符合 HitHub Primer 品牌设计指南的生产级前端界面。编写可实际运行的代码,并格外注重遵循 Primer 设计系统指南、Brand UI、Primer React 组件和 Octicons。
用户会提供前端需求:要构建的组件、页面、应用或界面。他们可能还会说明用途、受众或技术限制。
设计思路
开始编写代码前,先了解背景并确定清晰的美学方向:
- 用途:此界面要解决什么问题?谁会使用它?
- 基调:遵循 GitHub 专业而友好的品牌语调——清晰、亲切,并以开发者为中心
- 限制:技术要求(框架、性能、无障碍性)
- 差异化:如何让界面令人印象深刻?着重于简洁、实用的设计和明确的视觉层次
重要:选择清晰的概念方向,并精准执行。Primer 设计系统强调清晰易用,而非装饰性。
然后编写可实际运行的代码,要求:
- 达到生产级质量且功能完善
- 视觉清晰、易于使用
- 符合 Primer 的设计原则
- 精心打磨每个细节
限制
始终遵循品牌指南:https://brand.github.com/。
从这里开始:https://primer.style/brand/introduction/getting-started/。
安装与设置
- 安装所需的软件包:
npm install @primer/react-brand @primer/octicons-react
- 重要:在根布局/应用文件中导入 Primer CSS:
import "@primer/react-brand/lib/css/main.css";
- 重要:处理 Vite/React Router 的 CommonJS 兼容性。使用默认导入并进行解构:
// ✅ CORRECT - Use default import with destructuring for CommonJS compatibility
import pkg from "@primer/react-brand";
const { ThemeProvider, Button, Hero, Heading, Text, Grid, Stack } = pkg;
- 在根节点使用 ThemeProvider 包装应用:
import pkg from "@primer/react-brand";
const { ThemeProvider } = pkg;
function App() {
return (
<ThemeProvider>
<div>...</div>
</ThemeProvider>
)
}
使用 Primer React 组件
始终使用官方 Primer React Brand 组件,不要使用自定义 HTML/CSS。关键组件:
Hero 组件
使用 className 属性进行自定义样式设置,或使用 Box 组件控制内边距/外边距:
import pkg from "@primer/react-brand";
const { Hero, Box } = pkg;
// ✅ OPTION 1: Using className with custom CSS
<Hero className="custom-hero-padding" align="center">
<Hero.Label>Label</Hero.Label>
<Hero.Heading>This is my super sweet hero heading</Hero.Heading>
<Hero.Description>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</Hero.Description>
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
<Hero.SecondaryAction href="#">Secondary action</Hero.SecondaryAction>
<Hero.Image src="/path/to/image.png" alt="Description" />
</Hero>
// ✅ OPTION 2: Using Box component for layout
<Box paddingBlockStart={128} paddingBlockEnd={128}>
<Hero align="center">
<Hero.Label>Label</Hero.Label>
<Hero.Heading>This is my super sweet hero heading</Hero.Heading>
<Hero.Description>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</Hero.Description>
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
</Hero>
</Box>
使用 Octicons 的 Button 组件
参考:https://primer.style/brand/components/Button/react/
import pkg from "@primer/react-brand";
const { Button } = pkg;
import { PackageIcon, SearchIcon } from "@primer/octicons-react";
// ✅ CORRECT - Use leadingVisual/trailingVisual props for icons
<Button
variant="primary"
leadingVisual={<PackageIcon />}
hasArrow={false}
block
>
Add to Cart
</Button>
// Available variants: 'primary', 'secondary', 'subtle', 'accent'
// Available sizes: 'small', 'medium', 'large'
// Props: block, hasArrow, leadingVisual, trailingVisual
// ✅ Octicons support fill, className, and style props
<Button leadingVisual={<PackageIcon fill="#0969da" />}>
Add to Cart
</Button>
// ✅ Also valid - using className
<Button leadingVisual={<PackageIcon className="custom-icon" />}>
Add to Cart
</Button>
// ✅ Also valid - wrapping in div for additional styling
<Button leadingVisual={<div style={{ color: "#0969da" }}><PackageIcon /></div>}>
Add to Cart
</Button>
布局组件
import pkg from "@primer/react-brand";
const { Grid, Stack, Box } = pkg;
// Grid for responsive layouts
<Grid>
<Grid.Column span={{ medium: 4 }}>Content</Grid.Column>
<Grid.Column span={{ medium: 8 }}>Content</Grid.Column>
</Grid>
// Stack for consistent spacing
<Stack direction="vertical" gap="normal" padding="spacious">
<div>Item 1</div>
<div>Item 2</div>
</Stack>
// Box for padding, margin, background, and borders
<Box
paddingBlockStart={64}
paddingBlockEnd={64}
backgroundColor="subtle"
borderRadius="medium"
>
<Heading>Content with spacing</Heading>
</Box>
Box 组件:使用 Box 替代 div 进行布局,并设置适当的间距:
- 内边距属性:
padding、paddingBlockStart、paddingBlockEnd、paddingInlineStart、paddingInlineEnd - 外边距属性:
margin、marginBlockStart、marginBlockEnd、marginInlineStart、marginInlineEnd - 值:使用间距比例(8、16、24、32、40、48、64、80、96、128)或响应式对象
- 背景:
backgroundColor="default"或"subtle" - 边框:
borderColor、borderRadius、borderStyle
排版组件
import pkg from "@primer/react-brand";
const { Heading, Text } = pkg;
<Heading as="h1" size="1">Main Heading</Heading>
<Heading as="h2" size="3">Section Heading</Heading>
<Text size="300" weight="semibold">Body text</Text>
使用 Octicons 图标
任何图标都必须使用来自 @primer/octicons-react 的 Octicons:
import { SearchIcon, PackageIcon, ChevronDownIcon } from "@primer/octicons-react";
// Use with Button leadingVisual/trailingVisual
<Button leadingVisual={<SearchIcon />}>Search</Button>
图标列表:https://primer.style/octicons/
样式设置方法
- 优先使用 Primer 组件,而不是自定义 HTML/CSS
- 使用 Box 组件设置布局间距(内边距、外边距),不要使用包装 div
- 使用 Primer 组件上的 className 属性,通过外部 CSS 设置自定义样式
- 尽量少用内联样式——仅在 Box 和 className 不适用时使用
- 使用标准 CSS 值(px、#0969da 等颜色),而不是 CSS 变量
- 遵循 GitHub 的配色方案:
- 主蓝色:#0969da
- 成功绿色:#1f883d
- 背景:#ffffff(白色)、#f6f8fa(浅灰色)
- 边框:#d0d7de
- 文本:#24292f(主要)、#57606a(次要)
常见模式
使用 Box 的页面布局:
import pkg from "@primer/react-brand";
const { Box } = pkg;
<Box backgroundColor="subtle" style={{ minHeight: "100vh" }}>
<Box
as="header"
backgroundColor="default"
borderColor="default"
borderStyle="solid-bottom"
paddingBlockStart={16}
paddingBlockEnd={16}
>
{/* Navigation */}
</Box>
<Box
as="main"
style={{ maxWidth: "1280px", margin: "0 auto" }}
paddingBlockStart={48}
paddingBlockEnd={48}
paddingInlineStart={24}
paddingInlineEnd={24}
>
{/* Content */}
</Box>
<Box
as="footer"
borderColor="default"
borderStyle="solid-top"
paddingBlockStart={32}
paddingBlockEnd={32}
>
{/* Footer */}
</Box>
</Box>
使用 Box 的卡片组件:
<Box
backgroundColor="default"
borderColor="default"
borderStyle="solid"
borderRadius="medium"
padding={24}
>
{/* Card content */}
</Box>
设置图标样式:
// ✅ OPTION 1: Use fill prop directly on icon
<PackageIcon size={24} fill="#0969da" />
// ✅ OPTION 2: Use className prop
<PackageIcon size={24} className="custom-icon" />
// ✅ OPTION 3: Wrap in div (inherits currentColor)
<div style={{ color: "#0969da" }}>
<PackageIcon size={24} />
</div>
// Octicons support: size, fill, className, style, aria-label, and all SVG props
组件参考
完整的组件文档和 API:
- 组件概览:https://primer.style/brand/components/
- Button:https://primer.style/brand/components/Button/react/
- Hero:https://primer.style/brand/components/Hero/react/
- 排版:https://primer.style/brand/typography/
- 网格/布局:https://primer.style/brand/layout/
无障碍
始终遵循无障碍指南:https://primer.style/accessibility/
- 使用语义化 HTML
- 使用恰当的标题层级
- 为图片添加替代文本
- 为交互元素设置焦点状态
- 必要时添加 ARIA 标签
测试要求
完成工作前,务必构建并测试更改:
- 运行
npm run build,检查 TypeScript 错误 - 在开发模式下测试应用(
npm run dev) - 验证所有路由和导航是否正常工作
- 检查响应式行为和无障碍性
- 切勿交付无法构建或运行的更改
常见问题与解决方案
问题:“Named export not found”错误或 CommonJS 模块警告
// ❌ WRONG - Named imports fail with CommonJS in dev/SSR
import { Button } from '@primer/react-brand';
// ❌ WRONG - Namespace imports may be undefined
import * as PrimerBrand from "@primer/react-brand";
// ✅ CORRECT - Use default import with destructuring
import pkg from "@primer/react-brand";
const { Button } = pkg;
问题:需要为 Primer 组件设置自定义间距
// ❌ AVOID - Wrapper divs add unnecessary DOM nesting
<div style={{ padding: "80px 24px" }}>
<Hero>...</Hero>
</div>
// ✅ BETTER - Use className with CSS file
<Hero className="hero-spacing">...</Hero>
// ✅ BEST - Use Box component for layout
<Box paddingBlockStart={80} paddingBlockEnd={80} paddingInlineStart={24} paddingInlineEnd={24}>
<Hero>...</Hero>
</Box>
问题:Octicon 颜色未生效
// ✅ CORRECT - Use fill prop for icon color
<PackageIcon fill="#0969da" />
// ✅ ALSO CORRECT - Set color on parent (icon inherits via currentColor)
<div style={{ color: "#0969da" }}>
<PackageIcon />
</div>
// ✅ ALSO CORRECT - Use className
<PackageIcon className="text-blue" />
问题:路由无法正常工作(404 错误)
- 始终使用
route()函数将新路由添加到app/routes.ts
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
index("routes/home.tsx"),
route("products", "routes/products.tsx"),
] satisfies RouteConfig;