ui: unify aero chrome surfaces and shared hero across kanban/graph
This commit is contained in:
parent
c8d7f8eb0d
commit
e6317594b6
18 changed files with 540 additions and 995 deletions
|
|
@ -29,10 +29,14 @@ const STATUS_META: Record<(typeof KANBAN_STATUSES)[number], { label: string; dot
|
|||
};
|
||||
|
||||
const STATUS_COLUMN_CLASS: Record<(typeof KANBAN_STATUSES)[number], string> = {
|
||||
ready: 'bg-sky-500/10',
|
||||
in_progress: 'bg-amber-500/10',
|
||||
blocked: 'bg-rose-500/10',
|
||||
closed: 'bg-emerald-500/10',
|
||||
ready:
|
||||
'bg-[radial-gradient(circle_at_0%_0%,rgba(56,189,248,0.2),transparent_62%),linear-gradient(180deg,rgba(22,27,40,0.66),rgba(10,12,20,0.84))]',
|
||||
in_progress:
|
||||
'bg-[radial-gradient(circle_at_0%_0%,rgba(251,191,36,0.2),transparent_62%),linear-gradient(180deg,rgba(22,27,40,0.66),rgba(10,12,20,0.84))]',
|
||||
blocked:
|
||||
'bg-[radial-gradient(circle_at_0%_0%,rgba(244,63,94,0.2),transparent_62%),linear-gradient(180deg,rgba(22,27,40,0.66),rgba(10,12,20,0.84))]',
|
||||
closed:
|
||||
'bg-[radial-gradient(circle_at_0%_0%,rgba(16,185,129,0.2),transparent_62%),linear-gradient(180deg,rgba(22,27,40,0.66),rgba(10,12,20,0.84))]',
|
||||
};
|
||||
|
||||
export function KanbanBoard({
|
||||
|
|
@ -86,8 +90,10 @@ export function KanbanBoard({
|
|||
key={status}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
onDrop={(event) => onDropLane(status, event)}
|
||||
className={`rounded-2xl border border-border-soft ${STATUS_COLUMN_CLASS[status]} p-2.5 transition ${
|
||||
activeStatus === status ? 'shadow-card' : 'opacity-90'
|
||||
className={`rounded-2xl border border-white/[0.04] ${STATUS_COLUMN_CLASS[status]} p-2.5 transition shadow-[0_24px_52px_-20px_rgba(0,0,0,0.82),0_10px_26px_-14px_rgba(0,0,0,0.75),inset_0_1px_0_rgba(255,255,255,0.08)] ${
|
||||
activeStatus === status
|
||||
? 'shadow-[0_30px_62px_-18px_rgba(0,0,0,0.86),0_0_0_1px_rgba(125,211,252,0.14)]'
|
||||
: 'opacity-95'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -103,11 +109,11 @@ export function KanbanBoard({
|
|||
}}
|
||||
className="flex w-full items-center justify-between rounded-lg px-1 py-0.5 text-left"
|
||||
>
|
||||
<strong className="inline-flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-text-body">
|
||||
<strong className="ui-text inline-flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-text-body">
|
||||
<span className={`h-2 w-2 rounded-full ${STATUS_META[status].dot}`} />
|
||||
{STATUS_META[status].label}
|
||||
</strong>
|
||||
<span className="font-mono text-xs text-text-muted">{columns[status].length}</span>
|
||||
<span className="system-data text-xs text-text-muted">{columns[status].length}</span>
|
||||
</button>
|
||||
{activeStatus === status ? (
|
||||
<button
|
||||
|
|
@ -127,6 +133,7 @@ export function KanbanBoard({
|
|||
<KanbanCard
|
||||
key={issue.id}
|
||||
issue={issue}
|
||||
issues={allIssues}
|
||||
parentEpic={parentEpicByIssueId.get(issue.id) ?? null}
|
||||
graphBaseHref={graphBaseHref}
|
||||
pending={pendingIssueIds.has(issue.id)}
|
||||
|
|
@ -150,11 +157,11 @@ export function KanbanBoard({
|
|||
key={issue.id}
|
||||
type="button"
|
||||
onClick={() => handleExpandAndSelect(status, issue)}
|
||||
className="max-w-full rounded-lg border border-border-soft bg-surface-muted/60 px-2 py-1 text-left hover:border-border-strong hover:bg-surface-raised/70"
|
||||
className="max-w-full rounded-lg border border-border-soft bg-gradient-to-b from-surface-muted/50 to-surface-muted/70 px-2 py-1 text-left hover:border-border-strong hover:from-surface-raised/70 hover:to-surface-raised/90 shadow-[0_1px_3px_rgba(0,0,0,0.1)]"
|
||||
title={issue.title}
|
||||
>
|
||||
<div className="font-mono text-[10px] text-text-muted">{issue.id}</div>
|
||||
<div className="line-clamp-1 text-xs font-medium text-text-body">{issue.title}</div>
|
||||
<div className="system-data text-[10px] text-text-muted">{issue.id}</div>
|
||||
<div className="ui-text line-clamp-1 text-sm font-medium text-text-body">{issue.title}</div>
|
||||
</button>
|
||||
))}
|
||||
{columns[status].length > 6 ? (
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ import Link from 'next/link';
|
|||
import { motion } from 'framer-motion';
|
||||
import type { DragEvent } from 'react';
|
||||
|
||||
import { formatUpdatedRecency } from '../../lib/kanban';
|
||||
import { hasOpenBlockers } from '../../lib/kanban';
|
||||
import type { BeadIssue } from '../../lib/types';
|
||||
|
||||
import { Chip } from '../shared/chip';
|
||||
|
||||
interface KanbanCardProps {
|
||||
issue: BeadIssue;
|
||||
issues?: BeadIssue[];
|
||||
parentEpic?: { id: string; title: string } | null;
|
||||
graphBaseHref: string;
|
||||
selected: boolean;
|
||||
|
|
@ -20,23 +21,61 @@ interface KanbanCardProps {
|
|||
onSelect: (issue: BeadIssue) => void;
|
||||
}
|
||||
|
||||
function priorityClass(priority: number): string {
|
||||
switch (priority) {
|
||||
case 0:
|
||||
return 'border-rose-300/45 bg-rose-500/20 text-rose-50';
|
||||
case 1:
|
||||
return 'border-amber-300/40 bg-amber-500/20 text-amber-50';
|
||||
case 2:
|
||||
return 'border-teal-300/40 bg-teal-500/20 text-teal-50';
|
||||
case 3:
|
||||
return 'border-slate-300/35 bg-slate-500/22 text-slate-50';
|
||||
function statusGradient(status: string): string {
|
||||
switch (status) {
|
||||
case 'ready':
|
||||
case 'open':
|
||||
return 'bg-[linear-gradient(145deg,rgba(34,45,42,0.92)_0%,rgba(24,32,30,0.88)_50%,rgba(18,28,26,0.9)_100%)]';
|
||||
case 'in_progress':
|
||||
return 'bg-[linear-gradient(145deg,rgba(42,40,32,0.92)_0%,rgba(32,30,24,0.88)_50%,rgba(26,24,18,0.9)_100%)]';
|
||||
case 'blocked':
|
||||
return 'bg-[linear-gradient(145deg,rgba(60,24,30,0.95)_0%,rgba(45,18,24,0.9)_50%,rgba(32,12,16,0.92)_100%)]';
|
||||
case 'closed':
|
||||
return 'bg-[linear-gradient(145deg,rgba(28,30,34,0.75)_0%,rgba(22,24,28,0.72)_50%,rgba(18,20,24,0.75)_100%)] opacity-75';
|
||||
default:
|
||||
return 'border-slate-400/35 bg-slate-600/20 text-slate-50';
|
||||
return 'bg-[linear-gradient(145deg,rgba(38,40,48,0.92)_0%,rgba(28,30,36,0.88)_50%,rgba(22,24,30,0.9)_100%)]';
|
||||
}
|
||||
}
|
||||
|
||||
function statusBorder(status: string): string {
|
||||
switch (status) {
|
||||
case 'ready':
|
||||
case 'open':
|
||||
return 'border-emerald-500/20';
|
||||
case 'in_progress':
|
||||
return 'border-amber-500/20';
|
||||
case 'blocked':
|
||||
return 'border-rose-500/20';
|
||||
case 'closed':
|
||||
return 'border-rose-500/30';
|
||||
default:
|
||||
return 'border-white/[0.06]';
|
||||
}
|
||||
}
|
||||
|
||||
function statusDotColor(status: string): string {
|
||||
switch (status) {
|
||||
case 'ready':
|
||||
case 'open':
|
||||
return 'bg-emerald-400';
|
||||
case 'in_progress':
|
||||
return 'bg-amber-400';
|
||||
case 'blocked':
|
||||
return 'bg-rose-400';
|
||||
case 'closed':
|
||||
return 'bg-slate-400';
|
||||
default:
|
||||
return 'bg-slate-400';
|
||||
}
|
||||
}
|
||||
|
||||
function titleColor(status: string): string {
|
||||
return status === 'closed' ? 'text-text-muted/70' : 'text-text-strong/95';
|
||||
}
|
||||
|
||||
export function KanbanCard({
|
||||
issue,
|
||||
issues = [],
|
||||
parentEpic = null,
|
||||
graphBaseHref,
|
||||
selected,
|
||||
|
|
@ -45,13 +84,15 @@ export function KanbanCard({
|
|||
onNativeDragStart,
|
||||
onSelect,
|
||||
}: KanbanCardProps) {
|
||||
const projectName = (issue as BeadIssue & { project?: { name?: string } }).project?.name ?? null;
|
||||
const unblocksCount = new Set(
|
||||
issue.dependencies.filter((dependency) => dependency.type === 'blocks').map((dependency) => dependency.target),
|
||||
).size;
|
||||
const blockerCount = issues.length > 0 ? (hasOpenBlockers(issues, issue.id) ?
|
||||
issue.dependencies.filter(d => d.type === 'blocks').filter(d => {
|
||||
const blocker = issues.find(i => i.id === d.target);
|
||||
return blocker && blocker.status !== 'closed';
|
||||
}).length : 0) : 0;
|
||||
|
||||
const selectedClass = selected
|
||||
? 'border-amber-200/60 bg-surface-raised shadow-card ring-1 ring-amber-200/20'
|
||||
: 'border-border-soft bg-surface/95 shadow-[0_6px_18px_rgba(4,8,17,0.5)] hover:border-border-strong hover:bg-surface-raised/95';
|
||||
? 'ring-1 ring-amber-200/20 shadow-[0_24px_48px_-18px_rgba(0,0,0,0.88),0_0_26px_rgba(251,191,36,0.14)]'
|
||||
: 'shadow-[0_18px_38px_-18px_rgba(0,0,0,0.82),0_6px_18px_-10px_rgba(0,0,0,0.72)] hover:shadow-[0_24px_52px_-16px_rgba(0,0,0,0.9),0_10px_26px_-10px_rgba(0,0,0,0.78)]';
|
||||
|
||||
const graphDetailHref = parentEpic
|
||||
? (() => {
|
||||
|
|
@ -78,52 +119,44 @@ export function KanbanCard({
|
|||
onSelect(issue);
|
||||
}
|
||||
}}
|
||||
className={`w-full cursor-pointer rounded-2xl border px-3 py-2.5 text-left transition ${selectedClass} ${
|
||||
className={`w-full cursor-pointer rounded-xl border ${statusBorder(issue.status)} ${statusGradient(issue.status)} px-3.5 py-3 text-left transition duration-200 shadow-[inset_0_1px_0_rgba(255,255,255,0.06)] ${selectedClass} ${
|
||||
pending ? 'opacity-70' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="font-mono text-[11px] text-text-muted break-all">{issue.id}</div>
|
||||
{projectName ? (
|
||||
<div className="mt-1">
|
||||
<span className="rounded-md border border-sky-300/25 bg-sky-500/10 px-1.5 py-0.5 font-mono text-[10px] text-sky-200">
|
||||
project: {projectName}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-1 text-sm font-semibold leading-5 text-text-strong break-words">{issue.title}</div>
|
||||
<div className="mt-2 flex flex-wrap gap-1.5">
|
||||
<span
|
||||
className={`inline-flex items-center rounded-full border px-2 py-1 font-mono text-[11px] font-semibold ${priorityClass(issue.priority)}`}
|
||||
>
|
||||
P{issue.priority}
|
||||
</span>
|
||||
<Chip>{issue.issue_type}</Chip>
|
||||
<Chip tone="status">deps {issue.dependencies.length}</Chip>
|
||||
{unblocksCount > 0 ? <Chip tone="status">Unblocks {unblocksCount}</Chip> : null}
|
||||
{/* ID row with status dot */}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`h-1.5 w-1.5 rounded-full ${statusDotColor(issue.status)} shadow-[0_0_6px_currentColor]`} />
|
||||
<span className="system-data text-[11px] text-text-muted/60">{issue.id}</span>
|
||||
</div>
|
||||
<div className="mt-2 break-words font-mono text-xs text-amber-100/90">
|
||||
{issue.assignee ? `@${issue.assignee}` : 'unassigned'}
|
||||
|
||||
{/* Title */}
|
||||
<div className={`ui-text mt-2 text-sm font-semibold leading-5 break-words ${titleColor(issue.status)}`}>
|
||||
{issue.title}
|
||||
</div>
|
||||
<div className="mt-1 font-mono text-[11px] text-text-muted">{formatUpdatedRecency(issue.updated_at)}</div>
|
||||
|
||||
{/* Labels/Tags row */}
|
||||
<div className="mt-3 flex flex-wrap items-center gap-1.5">
|
||||
{issue.labels.slice(0, 3).map((label) => (
|
||||
<Chip key={`${issue.id}-${label}`}>{label}</Chip>
|
||||
))}
|
||||
{blockerCount > 0 && (
|
||||
<Chip tone="status">{blockerCount} Blocker{blockerCount > 1 ? 's' : ''}</Chip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{parentEpic ? (
|
||||
<div className="mt-2">
|
||||
<div className="mt-2.5">
|
||||
<Link
|
||||
href={graphDetailHref ?? graphBaseHref}
|
||||
className="inline-flex items-center gap-1 rounded-md border border-sky-300/25 bg-sky-500/10 px-2 py-1 font-mono text-[11px] text-sky-200 hover:border-sky-300/45 hover:bg-sky-500/15"
|
||||
className="system-data inline-flex items-center gap-1 rounded border border-white/8 bg-white/[0.04] px-1.5 py-0.5 text-[10px] text-text-muted/80 hover:text-text-body hover:bg-white/[0.08]"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
epic: {parentEpic.title}
|
||||
{parentEpic.title}
|
||||
</Link>
|
||||
</div>
|
||||
) : null}
|
||||
{issue.labels.length > 0 ? (
|
||||
<div className="mt-2 flex flex-wrap gap-1.5">
|
||||
{issue.labels.slice(0, 3).map((label) => (
|
||||
<Chip key={`${issue.id}-${label}`}>#{label}</Chip>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{pending ? <div className="mt-2 text-[11px] font-medium text-amber-200">Saving…</div> : null}
|
||||
|
||||
{pending ? <div className="ui-text mt-2 text-[11px] font-medium text-amber-200">Saving…</div> : null}
|
||||
</motion.article>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export function KanbanControls({
|
|||
<option className="ui-option" value="3">P3</option>
|
||||
<option className="ui-option" value="4">P4</option>
|
||||
</select>
|
||||
<label className="inline-flex w-full items-center justify-center gap-2 rounded-xl border border-border-soft bg-surface-muted/60 px-3 py-2 text-sm text-text-body sm:w-auto sm:justify-start">
|
||||
<label className="ui-text inline-flex w-full items-center justify-center gap-2 rounded-xl border border-border-soft bg-gradient-to-b from-surface-muted/50 to-surface-muted/70 px-3 py-2 text-sm text-text-body sm:w-auto sm:justify-start shadow-[0_1px_3px_rgba(0,0,0,0.1)]">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={filters.showClosed ?? false}
|
||||
|
|
@ -72,7 +72,7 @@ export function KanbanControls({
|
|||
<button
|
||||
type="button"
|
||||
onClick={onNextActionable}
|
||||
className="w-full rounded-xl border border-border-soft bg-surface-muted/70 px-3 py-2 text-sm font-semibold text-text-body transition hover:border-border-strong hover:bg-surface-raised sm:w-auto"
|
||||
className="ui-text w-full rounded-xl border border-border-soft bg-gradient-to-b from-surface-muted/60 to-surface-muted/80 px-3 py-2 text-sm font-semibold text-text-body transition hover:from-surface-muted/75 hover:to-surface-muted/90 shadow-[0_1px_3px_rgba(0,0,0,0.1)] sm:w-auto"
|
||||
>
|
||||
Next Actionable
|
||||
</button>
|
||||
|
|
@ -85,9 +85,7 @@ export function KanbanControls({
|
|||
<StatPill label="Done" value={stats.done} />
|
||||
<StatPill label="P0" value={stats.p0} tone={stats.p0 > 0 ? 'critical' : 'default'} />
|
||||
</motion.div>
|
||||
{nextActionableFeedback ? (
|
||||
<p className="text-xs text-text-muted">{nextActionableFeedback}</p>
|
||||
) : null}
|
||||
{nextActionableFeedback ? <p className="ui-text text-xs text-text-muted">{nextActionableFeedback}</p> : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -22,6 +22,7 @@ import { KanbanBoard } from './kanban-board';
|
|||
import { KanbanControls } from './kanban-controls';
|
||||
import { KanbanDetail } from './kanban-detail';
|
||||
import { ProjectScopeControls } from '../shared/project-scope-controls';
|
||||
import { WorkspaceHero } from '../shared/workspace-hero';
|
||||
|
||||
interface KanbanPageProps {
|
||||
issues: BeadIssue[];
|
||||
|
|
@ -242,34 +243,42 @@ export function KanbanPage({
|
|||
|
||||
return (
|
||||
<main className="mx-auto min-h-screen max-w-[1800px] px-4 py-4 sm:px-6 sm:py-6">
|
||||
<header className="mb-4 rounded-2xl border border-border-soft bg-surface/90 px-4 py-4 shadow-card backdrop-blur md:px-5">
|
||||
<p className="font-mono text-xs uppercase tracking-[0.14em] text-text-muted">BeadBoard</p>
|
||||
<div className="mt-1 flex flex-wrap items-center gap-3">
|
||||
<h1 className="text-2xl font-semibold text-text-strong sm:text-3xl">Kanban Dashboard</h1>
|
||||
<Link href={graphHref} className="rounded-lg border border-border-soft bg-surface-muted/70 px-2.5 py-1 text-xs text-text-body hover:bg-surface-raised">
|
||||
<WorkspaceHero
|
||||
eyebrow="BeadBoard Workspace"
|
||||
title="Swimlanes"
|
||||
description="Epic-driven dependency visualization. Drill into task relationships, triage blockers, and understand downstream impact at a glance."
|
||||
className="mb-4"
|
||||
action={(
|
||||
<Link
|
||||
href={graphHref}
|
||||
className="ui-text rounded-xl border border-white/10 bg-white/5 px-3 py-1.5 text-[10px] font-bold text-text-body transition-all hover:bg-white/10 hover:border-white/20 sm:px-4 sm:text-xs"
|
||||
>
|
||||
Open Graph
|
||||
</Link>
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-text-muted">Tracer Bullet 1 from live `.beads/issues.jsonl` on Windows-native paths.</p>
|
||||
{activeScope ? (
|
||||
<p className="mt-2 text-xs text-text-muted">
|
||||
)}
|
||||
scope={activeScope ? (
|
||||
<p className="ui-text text-xs text-text-muted/90">
|
||||
Scope:{' '}
|
||||
<span className="rounded-md border border-border-soft bg-surface-muted/50 px-2 py-0.5 font-mono text-[11px] text-text-body">
|
||||
<span className="system-data rounded-md border border-white/10 bg-white/5 px-2 py-0.5 text-[11px] text-text-body">
|
||||
{activeScope.source === 'local' ? 'local workspace' : activeScope.displayPath}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
<div className="mt-3">
|
||||
<ProjectScopeControls
|
||||
projectScopeKey={projectScopeKey}
|
||||
projectScopeMode={projectScopeMode}
|
||||
projectScopeOptions={projectScopeOptions}
|
||||
/>
|
||||
</div>
|
||||
{!allowMutations ? (
|
||||
<p className="mt-2 text-xs text-amber-200/90">Aggregate mode is read-only. Switch to single project mode to edit status/details.</p>
|
||||
) : null}
|
||||
</header>
|
||||
) : undefined}
|
||||
controls={(
|
||||
<>
|
||||
<ProjectScopeControls
|
||||
projectScopeKey={projectScopeKey}
|
||||
projectScopeMode={projectScopeMode}
|
||||
projectScopeOptions={projectScopeOptions}
|
||||
/>
|
||||
{!allowMutations ? (
|
||||
<p className="ui-text mt-2 text-xs text-amber-200/90">
|
||||
Aggregate mode is read-only. Switch to single project mode to edit status/details.
|
||||
</p>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<KanbanControls
|
||||
filters={filters}
|
||||
stats={stats}
|
||||
|
|
@ -278,10 +287,10 @@ export function KanbanPage({
|
|||
nextActionableFeedback={nextActionableFeedback}
|
||||
/>
|
||||
{mutationError ? (
|
||||
<div className="mt-3 rounded-xl border border-rose-300/40 bg-rose-950/40 px-3 py-2 text-sm text-rose-100">{mutationError}</div>
|
||||
<div className="ui-text mt-3 rounded-xl border border-rose-300/40 bg-rose-950/40 px-3 py-2 text-sm text-rose-100">{mutationError}</div>
|
||||
) : null}
|
||||
<section
|
||||
className={`mt-3 overflow-hidden rounded-2xl border border-border-soft bg-surface/82 shadow-card ${
|
||||
className={`mt-3 overflow-hidden rounded-2xl border border-white/5 bg-[linear-gradient(180deg,rgba(255,255,255,0.02),rgba(255,255,255,0.005))] shadow-[0_28px_62px_-18px_rgba(0,0,0,0.8),0_8px_24px_-10px_rgba(0,0,0,0.72)] backdrop-blur-xl ${
|
||||
showDesktopDetail ? 'lg:grid lg:grid-cols-[minmax(0,1fr)_minmax(22rem,26rem)]' : ''
|
||||
}`}
|
||||
>
|
||||
|
|
@ -303,20 +312,20 @@ export function KanbanPage({
|
|||
/>
|
||||
</motion.div>
|
||||
{showDesktopDetail ? (
|
||||
<div className="hidden border-t border-border-soft bg-surface/72 p-3 lg:block lg:border-l lg:border-t-0">
|
||||
<aside className="rounded-xl border border-border-soft bg-surface/78 p-3">
|
||||
<div className="hidden border-t border-white/5 bg-[rgba(9,13,22,0.78)] p-3 lg:block lg:border-l lg:border-t-0">
|
||||
<aside className="rounded-xl border border-white/6 bg-[linear-gradient(180deg,rgba(42,44,52,0.54),rgba(18,20,30,0.78))] p-3 shadow-[0_18px_42px_-20px_rgba(0,0,0,0.85),inset_0_1px_0_rgba(255,255,255,0.08)]">
|
||||
<div className="mb-2 flex items-center justify-end gap-2 border-b border-border-soft pb-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDesktopDetailMinimized(true)}
|
||||
className="rounded-md border border-border-soft bg-surface-muted/70 px-2 py-1 text-xs text-text-body"
|
||||
className="ui-text rounded-md border border-border-soft bg-surface-muted/70 px-2 py-1 text-xs text-text-body"
|
||||
>
|
||||
Minimize
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedIssueId(null)}
|
||||
className="rounded-md border border-border-soft bg-surface-muted/70 px-2 py-1 text-xs text-text-muted"
|
||||
className="ui-text rounded-md border border-border-soft bg-surface-muted/70 px-2 py-1 text-xs text-text-muted"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
|
|
@ -341,7 +350,7 @@ export function KanbanPage({
|
|||
<div className="fixed inset-0 z-40 lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
className="absolute inset-0 bg-black/70 backdrop-blur-sm"
|
||||
className="absolute inset-0 bg-black/82 backdrop-blur-md"
|
||||
aria-label="Close details"
|
||||
onClick={() => setMobileDetailOpen(false)}
|
||||
/>
|
||||
|
|
@ -350,13 +359,13 @@ export function KanbanPage({
|
|||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: 36, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: 'easeOut' }}
|
||||
className="absolute inset-x-3 bottom-3 top-20 overflow-y-auto rounded-2xl border border-border-soft bg-surface/98 p-3 shadow-panel backdrop-blur-2xl"
|
||||
className="absolute inset-x-3 bottom-3 top-20 overflow-y-auto rounded-2xl border border-border-soft bg-surface/96 p-3 shadow-panel backdrop-blur-3xl"
|
||||
>
|
||||
<div className="mb-2 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMobileDetailOpen(false)}
|
||||
className="rounded-lg border border-border-soft bg-surface-muted/70 px-3 py-1 text-xs font-semibold text-text-body"
|
||||
className="ui-text rounded-lg border border-border-soft bg-surface-muted/70 px-3 py-1 text-xs font-semibold text-text-body"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue