fix(desktop): remove remaining Projects panel fills (#3742)

## Summary

- let the Projects page surface flow through repository, pull request,
and issue list/grid layouts
- remove opaque fills from project-detail content across Overview,
Files, Commits, Issues, Pull Requests, and Contributors
- preserve borders, hover feedback, and intentional nested fills for
code, inputs, badges, and warnings

## Related

Follow-up to #3416.

## Testing

- `pnpm exec biome check` on the changed Projects UI and E2E files
- `pnpm build:e2e`
- focused Playwright smoke coverage for overview, subsection list/grid,
and project-detail transparency (3 passed)
- pre-commit checks passed
- desktop pre-push checks passed; the unrelated integration hook remains
blocked by a stale local checksum for migration 25

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
This commit is contained in:
thomaspblock
2026-07-30 15:31:10 +01:00
committed by GitHub
parent 85edc0572a
commit c55e421a06
13 changed files with 162 additions and 25 deletions
@@ -421,7 +421,8 @@ export function ProjectGridCard({
}: ProjectItemProps) {
return (
<Card
className="group relative flex min-h-44 flex-col overflow-hidden border-border/60 bg-card shadow-none transition-colors duration-150 hover:bg-muted/20"
className="group relative flex min-h-44 flex-col overflow-hidden border-border/60 bg-transparent shadow-none transition-colors duration-150 hover:bg-muted/20"
data-projects-grid-card
data-testid={`project-card-${project.dtag}`}
>
<ProjectCardButton onOpen={onOpen} project={project} />
@@ -10,6 +10,7 @@ import {
} from "@/features/profile/lib/identity";
import type { ProjectRepoCommit, ProjectRepoDiff } from "@/shared/api/types";
import { CopyCommitHashButton } from "./ProjectCommitCopyButton";
import { PROJECT_DETAIL_PANEL_CLASS } from "./projectPanelStyles";
import { ProfileIdentityButton } from "./ProjectProfileIdentity";
import { ProjectDiffFilesPanel } from "./ProjectPullRequestFilesChangedPanel";
import { ProjectRichContent } from "./ProjectRichContent";
@@ -56,7 +57,10 @@ export function ProjectCommitDetailPanel({
return (
<div className="space-y-3">
<header className="space-y-2 rounded-xl border border-border/60 bg-card p-4">
<header
className={`space-y-2 p-4 ${PROJECT_DETAIL_PANEL_CLASS}`}
data-project-detail-panel
>
<p className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground">
<GitCommitHorizontal className="h-3.5 w-3.5" />
Commit from {authorLabel}
@@ -25,6 +25,10 @@ import {
ProjectFeedRowCluster,
ProjectFeedRowMonoCell,
} from "./ProjectFeedRow";
import {
PROJECT_DETAIL_PANEL_CLASS,
PROJECT_DETAIL_PANEL_MESSAGE_CLASS,
} from "./projectPanelStyles";
import { ProfileIdentityButton } from "./ProjectProfileIdentity";
function pluralize(count: number, singular: string, plural = `${singular}s`) {
@@ -66,14 +70,17 @@ export function ContributorsPanel({
if (rows.length === 0) {
return (
<p className="rounded-xl border border-border/60 bg-card p-4 text-sm text-muted-foreground">
<p
className={PROJECT_DETAIL_PANEL_MESSAGE_CLASS}
data-project-detail-panel
>
No git contributors are available yet.
</p>
);
}
return (
<div className="overflow-hidden rounded-xl border border-border/60 bg-card">
<div className={PROJECT_DETAIL_PANEL_CLASS} data-project-detail-panel>
{rows.map((row, index) => (
<div
className={cn(
@@ -144,7 +151,10 @@ export function ActivityPanel({
if (isLoading) {
return (
<p className="rounded-xl border border-border/60 bg-card p-4 text-sm text-muted-foreground">
<p
className={PROJECT_DETAIL_PANEL_MESSAGE_CLASS}
data-project-detail-panel
>
Loading activity
</p>
);
@@ -152,7 +162,10 @@ export function ActivityPanel({
if (commits.length === 0) {
return (
<p className="rounded-xl border border-border/60 bg-card p-4 text-sm text-muted-foreground">
<p
className={PROJECT_DETAIL_PANEL_MESSAGE_CLASS}
data-project-detail-panel
>
{error
? "Could not load repository activity from git."
: "No commits are available yet."}
@@ -161,7 +174,7 @@ export function ActivityPanel({
}
return (
<section className="overflow-hidden rounded-xl border border-border/60 bg-card">
<section className={PROJECT_DETAIL_PANEL_CLASS} data-project-detail-panel>
<div className="flex min-h-14 items-center gap-2 border-border/50 border-b px-4 py-3">
<GitCommitHorizontal className="h-4 w-4 text-muted-foreground" />
<h3 className="min-w-0 flex-1 truncate text-sm font-medium text-foreground">
@@ -23,6 +23,7 @@ import {
} from "@/features/projects/lib/projectLanguages";
import { normalizePubkey } from "@/shared/lib/pubkey";
import { UserAvatar } from "@/shared/ui/UserAvatar";
import { PROJECT_DETAIL_PANEL_CLASS } from "./projectPanelStyles";
import { ReadmePanel } from "./ProjectReadmePanel";
import type { RepoSourceHeaderControls } from "./ProjectRepositorySource";
@@ -152,7 +153,10 @@ export function ProjectOverviewPanel({
const latestCommit = snapshot?.latestCommit ?? null;
return (
<div className="grid overflow-hidden rounded-xl border border-border/60 bg-card xl:grid-cols-[minmax(0,1fr)_18rem]">
<div
className={`grid xl:grid-cols-[minmax(0,1fr)_18rem] ${PROJECT_DETAIL_PANEL_CLASS}`}
data-project-detail-panel
>
<div className="min-w-0">
{/* ReadmePanel renders its own "no README" fallback while keeping
the branch + source controls reachable. */}
@@ -39,6 +39,7 @@ import type { UserProfileLookup } from "@/features/profile/lib/identity";
import { useIdentityQuery } from "@/shared/api/hooks";
import { cn } from "@/shared/lib/cn";
import type { ProjectRepoDiff, ProjectRepoDiffFile } from "@/shared/api/types";
import { PROJECT_DETAIL_PANEL_CLASS } from "./projectPanelStyles";
import { ProjectPullRequestInlineCommentThread } from "./ProjectPullRequestInlineComments";
function fileName(path: string) {
@@ -772,9 +773,7 @@ export function ProjectDiffFilesPanel({
inlineComments?: InlineCommentControls;
subjectLabel: string;
}) {
const outerBorderClass = embedded
? ""
: "rounded-xl border border-border/60 bg-card";
const outerBorderClass = embedded ? "" : PROJECT_DETAIL_PANEL_CLASS;
const [query, setQuery] = React.useState("");
const [selectedPath, setSelectedPath] = React.useState<string | null>(null);
const files = diff?.files ?? [];
@@ -818,6 +817,7 @@ export function ProjectDiffFilesPanel({
return (
<div
className={cn("p-4 text-sm text-muted-foreground", outerBorderClass)}
data-project-detail-panel={embedded ? undefined : true}
>
Loading changed files
</div>
@@ -832,6 +832,7 @@ export function ProjectDiffFilesPanel({
"space-y-1 p-4 text-sm text-muted-foreground",
outerBorderClass,
)}
data-project-detail-panel={embedded ? undefined : true}
>
<p>Could not load changed files for this {subjectLabel}.</p>
{message ? (
@@ -850,6 +851,7 @@ export function ProjectDiffFilesPanel({
"p-6 text-center text-sm text-muted-foreground",
outerBorderClass,
)}
data-project-detail-panel={embedded ? undefined : true}
>
No changed files are available for this {subjectLabel} yet.
</div>
@@ -862,6 +864,7 @@ export function ProjectDiffFilesPanel({
"grid min-h-0 overflow-hidden lg:grid-cols-[17rem_minmax(0,1fr)]",
outerBorderClass,
)}
data-project-detail-panel={embedded ? undefined : true}
>
<aside className="border-border/50 border-b bg-background/30 lg:border-r lg:border-b-0">
<div className="space-y-3 p-3">
@@ -34,6 +34,10 @@ import { cn } from "@/shared/lib/cn";
import { normalizePubkey } from "@/shared/lib/pubkey";
import { SyntaxHighlightedCode } from "@/shared/ui/markdown";
import { UserAvatar } from "@/shared/ui/UserAvatar";
import {
PROJECT_DETAIL_PANEL_CLASS,
PROJECT_DETAIL_PANEL_MESSAGE_CLASS,
} from "./projectPanelStyles";
import {
type RepoSourceHeaderControls,
RepoSourceDropdown,
@@ -550,7 +554,7 @@ function FileContentPanel({
const directorySegments = pathSegments.slice(0, -1);
return (
<div className="overflow-hidden rounded-xl border border-border/60 bg-card">
<div className={PROJECT_DETAIL_PANEL_CLASS} data-project-detail-panel>
<div className="flex min-h-14 items-center gap-1 border-border/50 border-b bg-muted/20 px-3 py-3">
<BreadcrumbButton onClick={() => onOpenPath("")}>
Files
@@ -687,13 +691,16 @@ export function RepositoryFilesPanel({
if (stateMessage) {
if (!sourceControls) {
return (
<div className="rounded-xl border border-border/60 bg-card p-4 text-sm text-muted-foreground">
<div
className={PROJECT_DETAIL_PANEL_MESSAGE_CLASS}
data-project-detail-panel
>
{stateMessage}
</div>
);
}
return (
<div className="overflow-hidden rounded-xl border border-border/60 bg-card">
<div className={PROJECT_DETAIL_PANEL_CLASS} data-project-detail-panel>
<div className="flex min-h-14 min-w-0 items-center gap-1 border-border/50 border-b px-3 py-3">
<RepoSourceDropdown controls={sourceControls} />
<RepositoryBranchDropdown
@@ -733,7 +740,7 @@ export function RepositoryFilesPanel({
}
return (
<div className="overflow-hidden rounded-xl border border-border/60 bg-card">
<div className={PROJECT_DETAIL_PANEL_CLASS} data-project-detail-panel>
<div className="flex min-h-14 min-w-0 items-center gap-1 border-border/50 border-b px-3 py-3">
{sourceControls ? (
<>
@@ -42,6 +42,10 @@ import {
PullRequestTabsList,
} from "./ProjectWorkspaceTabList";
import { ProjectPullRequestFilesChangedPanel } from "./ProjectPullRequestFilesChangedPanel";
import {
PROJECT_DETAIL_PANEL_CLASS,
PROJECT_DETAIL_PANEL_MESSAGE_CLASS,
} from "./projectPanelStyles";
import { CreatePullRequestDialog } from "./CreatePullRequestDialog";
import {
CreateIssueDialog,
@@ -303,7 +307,7 @@ export function WorkspaceTabs({
) : null}
</div>
{selectedPullRequest ? (
<div className="overflow-hidden rounded-xl border border-border/60 bg-card">
<div className={PROJECT_DETAIL_PANEL_CLASS} data-project-detail-panel>
{/* Two full-height columns: the meta rail runs all the way to the
top of the card, alongside the header and tabs. */}
<div className="grid xl:grid-cols-[minmax(0,1fr)_18rem]">
@@ -409,7 +413,8 @@ export function WorkspaceTabs({
</TabsContent>
<TabsContent
className="m-0 overflow-hidden rounded-xl border border-border/60 bg-card"
className={`m-0 ${PROJECT_DETAIL_PANEL_CLASS}`}
data-project-detail-panel
value="prs"
>
<WorkItemListHeader
@@ -437,7 +442,8 @@ export function WorkspaceTabs({
</TabsContent>
<TabsContent
className="m-0 overflow-hidden rounded-xl border border-border/60 bg-card"
className={`m-0 ${PROJECT_DETAIL_PANEL_CLASS}`}
data-project-detail-panel
value="issues"
>
<WorkItemListHeader
@@ -458,7 +464,10 @@ export function WorkspaceTabs({
<TabsContent className="m-0" value="files">
{repoSource === "local" && !localSnapshot && !localSnapshotLoading ? (
<div className="mb-3">
<div className="rounded-xl border border-border/60 bg-card p-4 text-sm text-muted-foreground">
<div
className={PROJECT_DETAIL_PANEL_MESSAGE_CLASS}
data-project-detail-panel
>
No local checkout found.
</div>
</div>
@@ -103,7 +103,10 @@ function IssueGridCard({
project: Project;
}) {
return (
<Card className="group relative flex min-h-40 flex-col overflow-hidden border-border/60 bg-card p-4 shadow-none transition-colors duration-150 hover:bg-muted/20">
<Card
className="group relative flex min-h-40 flex-col overflow-hidden border-border/60 bg-transparent p-4 shadow-none transition-colors duration-150 hover:bg-muted/20"
data-projects-grid-card
>
<button
className="absolute inset-0"
onClick={() => onOpen(project, issue)}
@@ -279,7 +282,10 @@ export function ProjectsIssuesList({
return (
<div className="space-y-3">
{loadNotice}
<div className={PROJECT_LIST_CONTAINER_CLASS}>
<div
className={PROJECT_LIST_CONTAINER_CLASS}
data-testid="projects-list-container"
>
{issues.map(({ project, issue }) => (
<IssueListRow
issue={issue}
@@ -85,7 +85,10 @@ function PullRequestGridCard({
});
return (
<Card className="group relative flex min-h-40 flex-col overflow-hidden border-border/60 bg-card p-4 shadow-none transition-colors duration-150 hover:bg-muted/20">
<Card
className="group relative flex min-h-40 flex-col overflow-hidden border-border/60 bg-transparent p-4 shadow-none transition-colors duration-150 hover:bg-muted/20"
data-projects-grid-card
>
<button
className="absolute inset-0"
onClick={() => onOpen(project, pullRequest)}
@@ -305,7 +308,10 @@ export function ProjectsPullRequestsList({
return (
<div className="space-y-3">
{loadNotice}
<div className={PROJECT_LIST_CONTAINER_CLASS}>
<div
className={PROJECT_LIST_CONTAINER_CLASS}
data-testid="projects-list-container"
>
{pullRequests.map(({ project, pullRequest }) => (
<PullRequestListRow
key={pullRequest.id}
@@ -481,7 +481,10 @@ export function ProjectsView() {
})}
</div>
) : (
<div className={PROJECT_LIST_CONTAINER_CLASS}>
<div
className={PROJECT_LIST_CONTAINER_CLASS}
data-testid="projects-list-container"
>
{visibleProjects.map((project) => {
const summary = activitySummariesQuery.data?.[project.repoAddress];
return (
@@ -1,6 +1,6 @@
/** Inbox-aligned spacing and typography for top-level Projects list rows. */
export const PROJECT_LIST_CONTAINER_CLASS =
"divide-y divide-border/60 overflow-hidden rounded-xl border border-border/60 bg-card";
"divide-y divide-border/60 overflow-hidden rounded-xl border border-border/60 bg-transparent";
export const PROJECT_LIST_ROW_CLASS =
"group relative px-3 py-3 transition-colors duration-150 hover:bg-muted/20";
@@ -1,3 +1,11 @@
/** Settings-aligned action button used at the right edge of panel headers. */
export const PROJECT_PANEL_ACTION_BUTTON_CLASS =
"h-auto shrink-0 gap-1.5 rounded-full border-transparent bg-muted px-3 py-1.5 text-sm font-medium text-foreground shadow-none hover:bg-muted/80";
/** Bordered shell that lets the project page surface show through. */
export const PROJECT_DETAIL_PANEL_CLASS =
"overflow-hidden rounded-xl border border-border/60 bg-transparent";
/** Empty or loading state using the same transparent project panel shell. */
export const PROJECT_DETAIL_PANEL_MESSAGE_CLASS =
"rounded-xl border border-border/60 bg-transparent p-4 text-sm text-muted-foreground";
@@ -945,6 +945,79 @@ test("project overview does not paint a background behind its cards", async ({
}
});
test("project subsections do not paint backgrounds behind list or grid items", async ({
page,
}) => {
await enableProjectsFeature(page);
await installMockBridge(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByTestId("open-projects-view").click();
for (const section of ["Repositories", "Pull Requests", "Issues"]) {
await page.getByRole("button", { name: section, exact: true }).click();
await page.getByRole("button", { name: "List layout" }).click();
const listContainer = page.getByTestId("projects-list-container");
await expect(listContainer).toBeVisible();
await expect(listContainer).toHaveCSS(
"background-color",
"rgba(0, 0, 0, 0)",
);
await expect(listContainer).toHaveCSS("border-style", "solid");
await page.getByRole("button", { name: "Grid layout" }).click();
const gridCards = page.locator("[data-projects-grid-card]");
await expect(gridCards.first()).toBeVisible();
const gridCardCount = await gridCards.count();
for (let index = 0; index < gridCardCount; index += 1) {
await expect(gridCards.nth(index)).toHaveCSS(
"background-color",
"rgba(0, 0, 0, 0)",
);
await expect(gridCards.nth(index)).toHaveCSS("border-style", "solid");
}
}
});
test("project detail content areas do not paint background fills", async ({
page,
}) => {
await enableProjectsFeature(page);
await installMockBridge(page);
await openBuzzProject(page);
const expectVisiblePanelsToBeTransparent = async () => {
const panels = page.locator("[data-project-detail-panel]:visible");
await expect(panels.first()).toBeVisible();
const panelCount = await panels.count();
for (let index = 0; index < panelCount; index += 1) {
await expect(panels.nth(index)).toHaveCSS(
"background-color",
"rgba(0, 0, 0, 0)",
);
await expect(panels.nth(index)).toHaveCSS("border-style", "solid");
}
};
for (const tab of [
"Overview",
"Files",
"Commits",
"Issues",
"Pull Request",
"Contributors",
]) {
await page.getByRole("tab", { name: tab, exact: true }).click();
await expectVisiblePanelsToBeTransparent();
}
await page.getByRole("tab", { name: "Pull Request", exact: true }).click();
const pullRequest = page.getByTestId("project-pull-request-row").first();
await expect(pullRequest).toBeVisible();
await pullRequest.getByRole("button", { name: /^#/ }).click();
await expectVisiblePanelsToBeTransparent();
});
test("project without a checkout offers fetch feedback and dropdown cloning", async ({
page,
}) => {