01 GitLab Pajamas 设计系统界面预览 按状态筛选作业,选择作业查看其输出,模拟链接检查失败后恢复成功,并重置原始示例数据。
原生 @gitlab/ui 138.0.0 CSS;原始分阶段布局、HTML 和本地状态逻辑。不涉及真实 CI 活动。·
实现参考 02
GitLab Pajamas 设计系统设计参考 @gitlab/ui 138.0.0 已发布的浅色设计令牌导出和原生 CSS。GL_* 名称是 JavaScript 导出项;其小写 --gl-* 对应项出现在编译后的 CSS 中。示例使用原生样式表和原始 HTML/状态逻辑,而非挂载的 Vue 组件。
名称 / 规则 值 / 配置 用途 GL_BACKGROUND_COLOR_DEFAULT#fff使用设计令牌指定的角色;这是浅色参考导出项。 GL_BACKGROUND_COLOR_SUBTLE#f7f7f5 使用设计令牌指定的角色;这是浅色参考导出项。 GL_BACKGROUND_COLOR_STRONG#ecebea 使用设计令牌指定的角色;这是浅色参考导出项。 GL_TEXT_COLOR_DEFAULT#3a383d 使用设计令牌指定的角色;这是浅色参考导出项。 GL_TEXT_COLOR_SUBTLE#646163 使用设计令牌指定的角色;这是浅色参考导出项。 GL_TEXT_COLOR_STRONG#18171d 使用设计令牌指定的角色;这是浅色参考导出项。 GL_TEXT_COLOR_HEADING#18171d 使用设计令牌指定的角色;这是浅色参考导出项。 GL_TEXT_COLOR_LINK#27599f 使用设计令牌指定的角色;这是浅色参考导出项。 GL_BORDER_COLOR_DEFAULT#dcdbd9 使用设计令牌指定的角色;这是浅色参考导出项。 GL_BORDER_COLOR_SUBTLE#ecebea 使用设计令牌指定的角色;这是浅色参考导出项。 GL_BORDER_COLOR_STRONG#c1bfbe 使用设计令牌指定的角色;这是浅色参考导出项。
名称 / 规则 值 / 配置 用途 GL_SPACING_SCALE_00px间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_10.125rem间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_20.25rem间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_30.5rem间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_40.75rem间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_51rem间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_61.5rem间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_72rem间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_82.5rem间距比例的数值并非索引乘以同一个常数。 GL_SPACING_SCALE_93rem间距比例的数值并非索引乘以同一个常数。
名称 / 规则 值 / 配置 用途 GL_FONT_SIZE_1000.75rem较大的字号角色使用 clamp;需要流体排版时,请保留该表达式。 GL_FONT_SIZE_2000.8125rem较大的字号角色使用 clamp;需要流体排版时,请保留该表达式。 GL_FONT_SIZE_3000.875rem较大的字号角色使用 clamp;需要流体排版时,请保留该表达式。 GL_FONT_SIZE_4001rem较大的字号角色使用 clamp;需要流体排版时,请保留该表达式。 GL_FONT_SIZE_500clamp(1.125rem, 0.9027777778rem + 0.462962963vw, 1.25rem)较大的字号角色使用 clamp;需要流体排版时,请保留该表达式。 GL_FONT_SIZE_600clamp(1.3125rem, 0.8680555556rem + 0.9259259259vw, 1.5625rem)较大的字号角色使用 clamp;需要流体排版时,请保留该表达式。 GL_FONT_SIZE_700clamp(1.5rem, 0.8333333333rem + 1.3888888889vw, 1.875rem)较大的字号角色使用 clamp;需要流体排版时,请保留该表达式。 GL_FONT_SIZE_800clamp(1.75rem, 0.8611111111rem + 1.8518518519vw, 2.25rem)较大的字号角色使用 clamp;需要流体排版时,请保留该表达式。
名称 / 规则 值 / 配置 用途 GL_BORDER_RADIUS_NONE0px圆角体现组件形态,而非操作的重要程度。 GL_BORDER_RADIUS_XS1px圆角体现组件形态,而非操作的重要程度。 GL_BORDER_RADIUS_SM0.125rem圆角体现组件形态,而非操作的重要程度。 GL_BORDER_RADIUS_MD0.25rem圆角体现组件形态,而非操作的重要程度。 GL_BORDER_RADIUS_LG0.5rem圆角体现组件形态,而非操作的重要程度。 GL_BORDER_RADIUS_XL0.75rem圆角体现组件形态,而非操作的重要程度。 GL_BORDER_RADIUS_FULL9999px圆角体现组件形态,而非操作的重要程度。 GL_BORDER_RADIUS_DEFAULT0.25rem圆角体现组件形态,而非操作的重要程度。
名称 / 规则 值 / 配置 用途 GL_COLOR_GREEN_50#e6f5ea 克制的成功状态背景层。 GL_COLOR_GREEN_800#075048 浅色背景层上的成功状态前景色。 GL_COLOR_RED_50#feeef0 克制的失败状态背景层。 GL_COLOR_RED_800#891c2f 浅色背景层上的失败状态前景色。 GL_COLOR_BLUE_50#e8f2fd 已选作业的界面表面。 GL_COLOR_BLUE_700#27599f 选中状态边框与可见焦点参考。
03 GitLab Pajamas 设计系统完整指南 实用指南,介绍如何为真实界面选择、应用和评审此系统。
DESIGN-gitlab-pajamas.md 完整指导、实现起点和源代码链接。
下载指南 GitLab Pajamas 设计系统流水线信息架构 先说明故障背景,再提供恢复操作 页眉标明虚构的流水线、分支和提交,为作业检查器提供上下文。Build、Test 和 Publish 以清晰分组呈现,选中作业后可查看其独立输出。链接检查失败时,先展示说明故障原因的路由和退出条件,再显示重试操作。
保持紧凑图表清晰易读 这个示例使用阶段列和按钮,而不是假装实现完整的依赖关系图。在窄屏上,各阶段按文档顺序纵向排列。生产环境中的流水线可能需要并行作业、依赖关系、等待、手动触发和已取消等状态;这些都应明确建模,而不是从三个示例标题中推断出来。
GitLab Pajamas 设计系统语义色彩与状态 使用语义化 UI 角色呈现工作区域 浅色导出提供暖色中性色界面表面、层次分明的文本和细微边框。示例使用默认界面表面显示作业卡片,用蓝色界面表面表示选中状态,并以深色区域呈现日志。指南列出确切的包中取值,用户无需从截图中取样像素,也能复现这些配色关系。
让状态标签清楚表达含义 每个作业行和徽标中都以文字标明“已通过”或“失败”。勾号或叉号只是对文字的补充;仅使用彩色徽标会造成歧义。品牌紫色不会被用作通用的成功指示色。表格中的状态颜色与选中状态的蓝色有意区分。
GitLab Pajamas 设计系统字号阶梯与技术文本 适用时保留流体表达式 该包提供固定的小字号角色,以及基于 clamp 的较大字号角色。将 clamp 替换为其最大端点会改变响应式行为。原始示例使用适用于应用界面的 24px 标题,以及紧凑的 12–14px 辅助文本角色;这些设置被标明为布局决策,而非额外的包令牌。
标识符和输出使用等宽字体 分支和提交信息采用代码样式,作业名称则保留易读的界面文本。输出显示在支持换行的等宽文本块中,该文本块可通过键盘聚焦。较长路径可以换行,避免整页宽度超出手机屏幕;选择作业时会替换文本,而不会注入原始的 HTML。
GitLab Pajamas 设计系统间距与边框节奏 不要根据令牌编号计算取值 GL_SPACING_SCALE_3 为 0.5rem,scale_5 为 1rem;索引是键,而不是直接的乘数。实现该比例尺时应使用导出的取值。参考表保留了精确单位,包括零像素这一档。
用边界明确区域划分 GitLab Pajamas 设计系统作业选择与筛选 让选中项的标识不受筛选位置影响 脚本使用稳定的字符串 ID,便于关联和实现无障碍功能。筛选器会更改作业列表,并在存在匹配项时选择其中一个。点击作业会同步更新其按下状态、阶段、状态徽标和日志。这样可避免作业标题与其他作业的输出错配。
结果消失后保留恢复上下文 模拟失败作业已通过后,“失败的作业”筛选器将没有结果。空状态消息会说明如何恢复列表,同时检查器会保留刚刚完成的结果。这样设计是为了让用户了解该行为何消失,而不会丢失确认信息。
GitLab Pajamas 设计系统重试与重置行为 如实标明模拟操作 操作按钮标为“模拟重试成功”,只会更改本地测试夹具,并将日志替换为文字形式的模拟结果。它不会将作业加入运行队列、创建作业或检查代码仓库。流水线徽标会根据所有作业状态重新计算,而不是在任意点击后直接设为成功。
恢复完整的初始测试夹具 重置操作会创建一份初始作业数据的新副本,选中失败的链接作业,恢复“所有作业”筛选器,并重新渲染两个面板。它无需重新加载页面即可清除模拟结果。这样可确保示例能够重复演示,也避免后续查看者误把先前模拟出的成功状态当作初始状态。
选择“失败的作业”并查看失败路由。 模拟成功,查看保留的检查器以及筛选后为空的列表。 重置示例,确认失败状态和原始输出已恢复。 GitLab Pajamas 设计系统无障碍作业控件 使用按钮进行本地选择 每个作业都是真正的按钮,并带有 aria-pressed 属性。状态会显示在持续时间旁边,可见的键盘焦点样式与选中外观彼此独立。筛选器带有可见标签。对于这些简单控件,我们不会声称实现了仅靠键盘操作的自定义图形导航。
播报操作结果,但不移动读者焦点 重试或重置后,操作结果会写入状态区域。日志是可选择的文本,也可通过键盘聚焦以便阅读;但每次选择后,应用不会自动将焦点移入日志。这样可以让用户稳定地反复比较不同作业。
GitLab Pajamas 设计系统 CSS 与 Vue 集成边界 明确选择一种集成方式 此源代码导入已发布的 CSS 分发包,并提供原创的 HTML/JavaScript 行为。它不会实例化 GlButton、GlFormSelect 或其他 Vue 导出项。Vue 应用应使用相应的原生组件及其当前 props/events,而不应将此 DOM 实现当作 Vue 示例。
CSS 分发包:原生外观与语义化变量。 原创 HTML:无障碍控件与区域结构。 原创 JavaScript:测试夹具数据、选择、筛选与模拟。 令牌应与导入的发行版本保持一致 GitLab Pajamas 设计系统组件示例 index.html — 流水线工作区 完整的语义化布局与控件。
html 复制代码
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Pipeline specimen</title>
<body>
<main class="pipeline-example">
<header>
<p class="eyebrow">sample-team / documentation</p>
<div class="title-row">
<div>
<h1>Pipeline #1842</h1>
<p>
Preview build for <code>docs/update-navigation</code> ·
<code>7c31a82</code>
</p>
</div>
<span id="pipeline-state" class="state failed">Failed</span>
</div>
</header>
<div class="toolbar">
<label for="job-filter">Show jobs</label
><select id="job-filter" class="gl-form-select">
<option value="all">All jobs</option>
<option value="failed">Failed jobs</option>
<option value="passed">Passed jobs</option></select
><button id="reset" class="btn btn-default gl-button">
Reset specimen
</button>
</div>
<div class="pipeline-layout">
<section aria-label="Pipeline stages">
<div id="stages" class="stages"></div>
<p id="empty" hidden>
No jobs match this filter. Choose All jobs to restore the pipeline.
</p>
</section>
<section class="job-detail" aria-labelledby="job-title">
<div class="detail-heading">
<h2 id="job-title"></h2>
<span id="job-state" class="state"></span>
</div>
<dl>
<dt>Stage</dt>
<dd id="job-stage"></dd>
<dt>Runner</dt>
<dd>local-example-runner</dd>
<dt>Execution</dt>
<dd>Simulated in this page</dd>
</dl>
<h3>Job output</h3>
<pre id="job-log" tabindex="0" aria-label="Selected job output"></pre>
<button id="retry" class="btn btn-confirm gl-button">
Simulate successful retry
</button>
<p id="feedback" role="status"></p>
</section>
</div>
<footer>
Native @gitlab/ui 138.0.0 CSS with original HTML and state logic. Fictional
pipeline; no runner, repository or CI job is started.
</footer>
</main>
<script type="module" src="/src/main.js"></script>
</body></html> 代码参考
main.js — 测试夹具与恢复逻辑 原生 CSS 导入及完整的分阶段任务行为。
js 复制代码
import "@gitlab/ui/dist/index.css";
import "./pajamas.css";
const initial = [
{
id: "lint",
stage: "Build",
state: "passed",
time: "12s",
log: "$ npm run lint\nNo lint errors.\nJob succeeded.",
},
{
id: "bundle",
stage: "Build",
state: "passed",
time: "31s",
log: "$ npm run build\nStatic assets generated.\nJob succeeded.",
},
{
id: "links",
stage: "Test",
state: "failed",
time: "18s",
log: "$ npm run check:links\n/docs/old-navigation returned 404\n1 broken internal link.\nJob failed: exit code 1.",
},
{
id: "accessibility",
stage: "Test",
state: "passed",
time: "24s",
log: "$ npm run check:a11y\nExample fixture checks passed.\nJob succeeded.",
},
{
id: "preview",
stage: "Publish",
state: "passed",
time: "9s",
log: "$ npm run package:preview\nLocal preview archive generated.\nJob succeeded.",
},
];
let jobs = structuredClone(initial),
selected = "links";
const filter = document.querySelector("#job-filter"),
stages = document.querySelector("#stages"),
retry = document.querySelector("#retry"),
feedback = document.querySelector("#feedback");
function render() {
stages.replaceChildren();
let count = 0;
for (const name of ["Build", "Test", "Publish"]) {
const matches = jobs.filter(
(j) =>
j.stage === name &&
(filter.value === "all" || j.state === filter.value),
);
if (!matches.length) continue;
const section = document.createElement("div");
section.className = "stage";
const h = document.createElement("h2");
h.textContent = name;
section.append(h);
for (const j of matches) {
count++;
const b = document.createElement("button");
b.type = "button";
b.className = "job";
b.setAttribute("aria-pressed", String(j.id === selected));
const strong = document.createElement("strong"),
small = document.createElement("small");
strong.textContent = j.id;
small.textContent =
(j.state === "passed" ? "✓ Passed" : "× Failed") + " · " + j.time;
b.append(strong, small);
b.onclick = () => {
selected = j.id;
feedback.textContent = "";
render();
};
section.append(b);
}
stages.append(section);
}
document.querySelector("#empty").hidden = count !== 0;
const j = jobs.find((j) => j.id === selected);
document.querySelector("#job-title").textContent = j.id;
document.querySelector("#job-stage").textContent = j.stage;
document.querySelector("#job-log").textContent = j.log;
const state = document.querySelector("#job-state");
state.className = "state " + j.state;
state.textContent = j.state === "passed" ? "Passed" : "Failed";
retry.hidden = j.state !== "failed";
const failed = jobs.some((j) => j.state === "failed"),
overall = document.querySelector("#pipeline-state");
overall.textContent = failed ? "Failed" : "Passed";
overall.className = "state " + (failed ? "failed" : "passed");
}
filter.onchange = () => {
const match = jobs.find(
(j) => filter.value === "all" || j.state === filter.value,
);
if (match) selected = match.id;
feedback.textContent = "";
render();
};
retry.onclick = () => {
const j = jobs.find((j) => j.id === selected);
j.state = "passed";
j.time = "15s";
j.log =
"$ npm run check:links\nSimulation: updated route found.\nNo broken internal links.\nJob succeeded (local simulation).";
render();
feedback.textContent =
"Simulated retry completed locally. No CI job was started.";
};
document.querySelector("#reset").onclick = () => {
jobs = structuredClone(initial);
selected = "links";
filter.value = "all";
feedback.textContent = "Specimen restored.";
render();
};
render();
代码参考
保存为 src/main.js。不会发起任何 CI 请求。 pajamas.css — 原始构成 响应式阶段、选中卡片、元数据和自动换行的日志输出。
css 复制代码
[hidden] {
display: none !important;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Arial, sans-serif;
font-size: 14px;
color: var(--gl-text-color-default, #3a383d);
background: var(--gl-background-color-default, #fff);
}
.pipeline-example {
padding: 28px;
}
.pipeline-example header {
padding-bottom: 20px;
border-bottom: 1px solid var(--gl-border-color-default);
}
.eyebrow {
font-size: 12px;
color: var(--gl-text-color-subtle);
margin-bottom: 16px;
}
.title-row {
display: flex;
justify-content: space-between;
gap: 16px;
align-items: flex-start;
}
h1 {
font-size: 24px;
font-weight: 600;
margin: 0 0 8px;
}
h2 {
font-size: 18px;
font-weight: 600;
margin: 0;
}
h3 {
font-size: 14px;
font-weight: 600;
}
.title-row p {
margin: 0;
color: var(--gl-text-color-subtle);
}
code {
font: 12px monospace;
background: #f7f7f5;
color: #3a383d;
padding: 2px 4px;
overflow-wrap: anywhere;
}
.toolbar {
display: flex;
align-items: center;
gap: 12px;
padding: 20px 0;
}
.toolbar label {
margin: 0;
font-weight: 600;
}
.toolbar select {
max-width: 180px;
}
.toolbar button {
margin-left: auto;
}
.pipeline-layout {
display: grid;
grid-template-columns: minmax(0, 1.4fr) minmax(300px, 1fr);
gap: 24px;
}
.stages {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.stage h2 {
font-size: 13px;
margin-bottom: 12px;
color: var(--gl-text-color-subtle);
}
.job {
display: block;
width: 100%;
text-align: left;
background: #fff;
border: 1px solid var(--gl-border-color-default);
border-radius: 4px;
padding: 12px;
margin-bottom: 8px;
color: inherit;
font: inherit;
cursor: pointer;
}
.job[aria-pressed="true"] {
border-color: #27599f;
box-shadow: inset 0 0 0 1px #27599f;
background: #e8f2fd;
}
.job strong {
display: block;
font-size: 13px;
overflow-wrap: anywhere;
}
.job small {
display: block;
margin-top: 6px;
color: var(--gl-text-color-subtle);
}
.job-detail {
border: 1px solid var(--gl-border-color-default);
border-radius: 4px;
padding: 20px;
min-width: 0;
}
.detail-heading {
display: flex;
justify-content: space-between;
gap: 16px;
align-items: center;
}
.state {
font-size: 12px;
font-weight: 600;
border-radius: 9999px;
padding: 3px 9px;
white-space: nowrap;
}
.state.failed {
background: #feeef0;
color: #891c2f;
}
.state.passed {
background: #e6f5ea;
color: #075048;
}
dl {
display: grid;
grid-template-columns: 90px minmax(0, 1fr);
gap: 8px;
margin: 20px 0;
font-size: 12px;
}
dt {
color: var(--gl-text-color-subtle);
font-weight: 400;
}
dd {
margin: 0;
overflow-wrap: anywhere;
}
pre {
background: #18171d;
color: #ecebea;
padding: 16px;
border-radius: 4px;
font: 12px/1.7 monospace;
white-space: pre-wrap;
overflow-wrap: anywhere;
min-height: 160px;
margin-bottom: 16px;
}
#feedback {
font-size: 12px;
margin: 12px 0 0;
}
footer {
margin-top: 28px;
padding-top: 16px;
border-top: 1px solid var(--gl-border-color-subtle);
font-size: 12px;
color: var(--gl-text-color-subtle);
}
button:focus-visible,
select:focus-visible,
pre:focus-visible {
outline: 2px solid #27599f;
outline-offset: 3px;
}
@media (max-width: 1050px) {
.pipeline-layout {
grid-template-columns: 1fr;
}
.stages {
gap: 16px;
}
}
@media (max-width: 550px) {
.pipeline-example {
padding: 16px;
}
.toolbar {
flex-wrap: wrap;
}
.toolbar button {
margin-left: 0;
}
.stages {
grid-template-columns: 1fr;
}
.stage {
border-bottom: 1px solid var(--gl-border-color-subtle);
padding-bottom: 8px;
}
.title-row {
flex-wrap: wrap;
}
.job-detail {
padding: 16px;
}
}
代码参考
保存为 src/pajamas.css。明确指定系统字体回退;未捆绑 GitLab 品牌字体。 04
GitLab Pajamas 设计系统 AI Agent 配置 实现起点 此集成仅导入原生 CSS 发行包。如需采用其 Vue 组件,请另行遵循 GitLab UI 对等依赖要求。
sh 复制示例
npm create vite@latest pajamas-pipeline -- --template vanilla
cd pajamas-pipeline
npm install
npm install @gitlab/[email protected]
# Replace index.html; add src/main.js and src/pajamas.css from the complete examples.
npm run dev 适用于你项目的提示词 Agent 提示词 复制提示词
使用 Pajamas 语义化界面表面、实际的间距与字体规格参考、稳定的任务 ID 和无障碍选择功能,构建技术任务检查器。使用完整的夹具源代码,保留可见的失败上下文,让筛选后的空状态易于理解,并明确标注所有本地模拟。不要暗示 CSS 或示例重试会创建 GitLab CI 任务。 05
GitLab Pajamas 设计系统的来源与范围 OpenDesign 是面向 GitLab Pajamas 和 @gitlab/ui 138.0.0 的应用指南。原生包 CSS 为这个原创的 HTML/JavaScript 示例提供样式。此流水线为虚构内容,不会调用 GitLab API、Vue 运行时、CI 运行器或仓库操作。