fix(web): compact CLI/Prompt toggle on skill install card (#2815)

* fix(web): compact CLI/Prompt toggle on skill install card

Replace pill tablist with a flat text toggle for CLI vs Prompt install
options, with matching skeleton and styles.

* chore: add UI proof screenshot for install toggle PR
This commit is contained in:
Vyctor H. Brzezowski
2026-06-23 23:50:29 -07:00
committed by GitHub
parent 173e28dd84
commit 2bf8113ef1
6 changed files with 82 additions and 21 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+2 -2
View File
@@ -1114,8 +1114,8 @@ describe("SkillDetailPage", () => {
expect(screen.getAllByText("@steipete/weather").length).toBeGreaterThan(0);
expect(screen.queryByText("npx clawhub@latest install @steipete/weather")).toBeNull();
expect(screen.queryByRole("tab", { name: "ClawHub" })).toBeNull();
expect(screen.getByRole("tab", { name: "CLI" }).getAttribute("aria-selected")).toBe("true");
expect(screen.getByRole("tab", { name: "Prompt" })).toBeTruthy();
expect(screen.getByRole("button", { name: "CLI" }).getAttribute("aria-pressed")).toBe("true");
expect(screen.getByRole("button", { name: "Prompt" })).toBeTruthy();
expect(screen.queryByText(/After install, inspect the skill metadata/i)).toBeNull();
expect(screen.getAllByText("Security audit").length).toBeGreaterThan(0);
expect(screen.getByRole("link", { name: "View Security Audit" }).getAttribute("href")).toBe(
+9 -4
View File
@@ -83,6 +83,7 @@ describe("SkillInstallSurface", () => {
</TooltipProvider>,
);
expect(screen.getByRole("heading", { name: "Install" })).toBeTruthy();
expect(screen.getByText("openclaw skills install")).toBeTruthy();
expect(screen.getByText("@steipete/weather")).toBeTruthy();
expect(document.querySelector(".skill-install-command-verb")?.textContent).toBe(
@@ -92,8 +93,10 @@ describe("SkillInstallSurface", () => {
" @steipete/weather",
);
expect(screen.queryByText("npx clawhub@latest install @steipete/weather")).toBeNull();
expect(screen.getByRole("tab", { name: "CLI" }).getAttribute("aria-selected")).toBe("true");
expect(screen.getByRole("tab", { name: "Prompt" }).getAttribute("aria-selected")).toBe("false");
expect(screen.getByRole("button", { name: "CLI" }).getAttribute("aria-pressed")).toBe("true");
expect(screen.getByRole("button", { name: "Prompt" }).getAttribute("aria-pressed")).toBe(
"false",
);
fireEvent.click(screen.getByRole("button", { name: "Copy OpenClaw CLI command" }));
@@ -101,10 +104,12 @@ describe("SkillInstallSurface", () => {
expect(writeTextMock).toHaveBeenCalledWith("openclaw skills install @steipete/weather");
});
fireEvent.click(screen.getByRole("tab", { name: "Prompt" }));
fireEvent.click(screen.getByRole("button", { name: "Prompt" }));
expect(screen.getByText(/Install the skill "Weather"/i)).toBeTruthy();
expect(screen.getByRole("tab", { name: "Prompt" }).getAttribute("aria-selected")).toBe("true");
expect(screen.getByRole("button", { name: "Prompt" }).getAttribute("aria-pressed")).toBe(
"true",
);
fireEvent.click(screen.getByRole("button", { name: "Copy OpenClaw prompt" }));
+10 -14
View File
@@ -192,6 +192,7 @@ export function SkillCommandLineCard({
ownerId,
clawdis,
}: SkillInstallSurfaceProps) {
const headingId = useId();
const [activeInstallTab, setActiveInstallTab] = useState<"cli" | "prompt">("cli");
const [installTabDirection, setInstallTabDirection] = useState<"left" | "right">("right");
const installTarget = buildSkillInstallTarget(ownerHandle, ownerId, slug);
@@ -215,29 +216,24 @@ export function SkillCommandLineCard({
};
return (
<article className="skill-install-command-card">
<article className="skill-install-command-card" aria-labelledby={headingId}>
<div className="skill-install-command-header detail-hero-summary-row">
<h3 className="skill-install-panel-title">Install</h3>
<div
className="install-switcher-toggle"
role="tablist"
aria-label="Install option"
data-active={activeInstallTab}
>
<h3 id={headingId} className="skill-install-panel-title">
Install
</h3>
<div className="skill-install-tab-toggle" role="group" aria-label="Install option">
<button
type="button"
role="tab"
aria-selected={activeInstallTab === "cli"}
className={`install-switcher-pill${activeInstallTab === "cli" ? " is-active" : ""}`}
className={`skill-install-tab${activeInstallTab === "cli" ? " is-active" : ""}`}
aria-pressed={activeInstallTab === "cli"}
onClick={() => selectInstallTab("cli")}
>
CLI
</button>
<button
type="button"
role="tab"
aria-selected={activeInstallTab === "prompt"}
className={`install-switcher-pill${activeInstallTab === "prompt" ? " is-active" : ""}`}
className={`skill-install-tab${activeInstallTab === "prompt" ? " is-active" : ""}`}
aria-pressed={activeInstallTab === "prompt"}
onClick={() => selectInstallTab("prompt")}
>
Prompt
@@ -105,7 +105,7 @@ export function SkillDetailSkeleton({ kind = "skill" }: SkillDetailSkeletonProps
<div className="skill-hero-main-extra">
<div className="detail-mobile-install">
<article className="skill-install-command-card">
<div className="skill-install-command-header">
<div className="skill-install-command-header detail-hero-summary-row">
<Skeleton className="h-7 w-20" />
{!isPlugin ? (
<div className="detail-skeleton-install-tabs" aria-hidden="true">
+60
View File
@@ -7902,10 +7902,70 @@ code {
box-shadow: none;
}
/* Skill/plugin install card — flat CLI | Prompt text toggle */
.skill-install-command-card .skill-install-tab-toggle {
display: inline-flex;
align-items: center;
gap: 0;
padding: 0;
border: 0;
border-radius: 0;
background: none;
}
.skill-install-command-card .skill-install-tab {
appearance: none;
border: 0;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 24px;
padding: 4px 8px;
border-radius: 0;
background: none;
color: var(--ink-soft);
font-family: inherit;
font-size: 0.72rem;
font-weight: 650;
letter-spacing: -0.01em;
line-height: 1;
transition: color 0.15s ease;
}
.skill-install-command-card .skill-install-tab + .skill-install-tab {
position: relative;
}
.skill-install-command-card .skill-install-tab + .skill-install-tab::before {
content: "";
position: absolute;
left: 0;
top: 50%;
width: 1px;
height: 12px;
transform: translateY(-50%);
background: color-mix(in srgb, var(--ink-soft) 45%, transparent);
}
.skill-install-command-card .skill-install-tab:hover {
color: var(--ink);
}
.skill-install-command-card .skill-install-tab.is-active {
color: var(--ink);
}
.skill-install-command-card .skill-install-tab:focus-visible {
outline: 2px solid color-mix(in srgb, var(--ink) 35%, transparent);
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
.skill-install-prompt-trigger,
.skill-install-copy-button,
.skill-install-command-reveal,
.skill-install-tab,
.install-switcher-toggle::before,
.install-switcher-pill {
transition: none;