Skip to content

Commit fc5ebea

Browse files
Template build details pixel perfect tweaks (#210)
Minor cosmetic changes to bring the Template build details page up-to-date with Figma **Template Link Styling:** - Added underline with 2px offset for cleaner appearance - Orange hover state on template link **Build Header:** - Changed all header items to secondary text color - Removed ID truncation in details section (now shows full ID) - Added truncated Build ID to page breadcrumb (first 6 + last 6 chars) **Logs Table:** - Reduced header height from 40px to 32px - Added conditional prose-label-highlight styling for selected table headers (using data-state="selected") - Implemented 16px column spacing using pr-4 on non-last columns - Updated column width constants to account for padding (timestamp: 176+16, level: 52+16) - Added 8px top padding below table header (using virtualizer paddingStart) - Reduced log level badge height from 20px to 18px - Changed info badge variant from 'positive' to 'info' Before: <img width="4112" height="2572" alt="Arc - 2025-12-19 at 16 16 39@2x" src="https://github.com/user-attachments/assets/b9aa295d-0c20-494e-ace2-797455a190d7" /> After: <img width="4112" height="2572" alt="Arc - 2025-12-19 at 16 16 03@2x" src="https://github.com/user-attachments/assets/96299a4f-6102-4f32-9016-883515954541" /> --------- Co-authored-by: ben-fornefeld <[email protected]>
1 parent a00dd36 commit fc5ebea

File tree

9 files changed

+49
-38
lines changed

9 files changed

+49
-38
lines changed

src/configs/layout.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,16 @@ const DASHBOARD_LAYOUT_CONFIGS: Record<
3838
'/dashboard/*/templates/*/builds/*': (pathname) => {
3939
const parts = pathname.split('/')
4040
const teamIdOrSlug = parts[2]!
41-
const buildId = parts.pop()
41+
const buildId = parts.pop()!
42+
const buildIdSliced = `${buildId.slice(0, 6)}...${buildId.slice(-6)}`
4243

4344
return {
4445
title: [
4546
{
4647
label: 'Templates',
4748
href: PROTECTED_URLS.TEMPLATES_BUILDS(teamIdOrSlug),
4849
},
49-
{ label: `Build ${buildId}` },
50+
{ label: `Build ${buildIdSliced}` },
5051
],
5152
type: 'custom',
5253
custom: {

src/features/dashboard/build/header-cells.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function Template({
3131
<Button
3232
variant="link"
3333
className={cn(
34-
'text-fg h-auto p-0 gap-1 font-sans prose-table normal-case max-w-full',
34+
'text-fg-secondary h-auto p-0 gap-1 font-sans prose-body normal-case max-w-full underline underline-offset-2 hover:text-accent-main-highlight',
3535
className
3636
)}
3737
onClick={(e) => {
@@ -43,7 +43,6 @@ export function Template({
4343
}}
4444
>
4545
<p className="truncate">{template}</p>
46-
<ArrowUpRight className="size-3 min-w-3" />
4746
</Button>
4847
)
4948
}
@@ -76,7 +75,7 @@ export function RanFor({
7675
// no timestamp to copy - just show duration
7776
if (isBuilding || !finishedAt) {
7877
return (
79-
<span className="whitespace-nowrap text-fg-tertiary">
78+
<span className="whitespace-nowrap text-fg-secondary">
8079
{formatDurationCompact(duration)}
8180
</span>
8281
)
@@ -86,8 +85,8 @@ export function RanFor({
8685
const formattedTimestamp = formatCompactDate(finishedAt)
8786

8887
return (
89-
<CopyButtonInline value={iso} className="whitespace-nowrap group/time">
90-
{formatDurationCompact(duration)}{' '}
88+
<CopyButtonInline value={iso} className="whitespace-nowrap text-fg-secondary group/time">
89+
In {formatDurationCompact(duration)}{' '}
9190
<span className="text-fg-tertiary group-hover/time:text-current transition-colors">
9291
· {formattedTimestamp}
9392
</span>
@@ -101,7 +100,7 @@ export function StartedAt({ timestamp }: { timestamp: number }) {
101100
const formattedTimestamp = formatCompactDate(timestamp)
102101

103102
return (
104-
<CopyButtonInline value={iso} className="whitespace-nowrap group/time">
103+
<CopyButtonInline value={iso} className="whitespace-nowrap text-fg-secondary group/time">
105104
{formatTimeAgoCompact(elapsed)}{' '}
106105
<span className="text-fg-tertiary group-hover/time:text-current transition-colors">
107106
· {formattedTimestamp}

src/features/dashboard/build/header.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ export default function BuildHeader({
3030
<DetailsItem label="ID">
3131
<CopyButtonInline
3232
value={buildId}
33-
className="font-mono prose-body-numeric"
33+
className="font-mono prose-table-numeric text-fg-secondary"
3434
>
35-
{buildId.slice(0, 6)}...{buildId.slice(-6)}
35+
{buildId}
3636
</CopyButtonInline>
3737
</DetailsItem>
3838
<DetailsItem label="Template">
@@ -52,7 +52,7 @@ export default function BuildHeader({
5252
<StartedAt timestamp={buildDetails.startedAt} />
5353
)}
5454
</DetailsItem>
55-
<DetailsItem label={isBuilding ? 'Ran for' : 'Finished'}>
55+
<DetailsItem label={isBuilding ? 'Running for' : 'Finished'}>
5656
{isLoading ? (
5757
<Skeleton className="w-36 h-5" />
5858
) : (

src/features/dashboard/build/logs-cells.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const mapLogLevelToBadgeProps: Record<BuildLogDTO['level'], BadgeProps> = {
1414
variant: 'default',
1515
},
1616
info: {
17-
variant: 'positive',
17+
variant: 'info',
1818
},
1919
warn: {
2020
variant: 'warning',
@@ -26,7 +26,7 @@ const mapLogLevelToBadgeProps: Record<BuildLogDTO['level'], BadgeProps> = {
2626

2727
export const LogLevel = ({ level }: LogLevelProps) => {
2828
return (
29-
<Badge {...mapLogLevelToBadgeProps[level]} className="uppercase">
29+
<Badge {...mapLogLevelToBadgeProps[level]} className="uppercase h-[18px]">
3030
{level}
3131
</Badge>
3232
)

src/features/dashboard/build/logs.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ import { type LogLevelFilter } from './logs-filter-params'
4242
import { useBuildLogs } from './use-build-logs'
4343
import useLogFilters from './use-log-filters'
4444

45-
const COLUMN_WIDTHS_PX = { timestamp: 192, level: 92 } as const
45+
// Column width are calculated as max width of the content + padding
46+
const COLUMN_WIDTHS_PX = { timestamp: 176 + 16, level: 52 + 16 } as const
4647
const ROW_HEIGHT_PX = 26
4748
const VIRTUAL_OVERSCAN = 16
4849
const SCROLL_LOAD_THRESHOLD_PX = 200
@@ -207,15 +208,21 @@ function LogsTableHeader() {
207208
>
208209
<TableRow style={{ display: 'flex', minWidth: '100%' }}>
209210
<TableHead
210-
className="text-fg"
211+
data-state="selected"
212+
className="px-0 pr-4"
211213
style={{ display: 'flex', width: COLUMN_WIDTHS_PX.timestamp }}
212214
>
213215
Timestamp <ArrowDownIcon className="size-3 rotate-180" />
214216
</TableHead>
215-
<TableHead style={{ display: 'flex', width: COLUMN_WIDTHS_PX.level }}>
217+
<TableHead
218+
className="px-0 pr-4"
219+
style={{ display: 'flex', width: COLUMN_WIDTHS_PX.level }}
220+
>
216221
Level
217222
</TableHead>
218-
<TableHead style={{ display: 'flex', flex: 1 }}>Message</TableHead>
223+
<TableHead className="px-0" style={{ display: 'flex', flex: 1 }}>
224+
Message
225+
</TableHead>
219226
</TableRow>
220227
</TableHeader>
221228
)
@@ -224,7 +231,7 @@ function LogsTableHeader() {
224231
function LoaderBody() {
225232
return (
226233
<TableBody style={{ display: 'grid' }}>
227-
<TableRow style={{ display: 'flex', minWidth: '100%' }}>
234+
<TableRow style={{ display: 'flex', minWidth: '100%', marginTop: 8 }}>
228235
<TableCell className="flex-1">
229236
<div className="h-[35svh] w-full flex justify-center items-center">
230237
<Loader variant="slash" size="lg" />
@@ -242,7 +249,7 @@ interface EmptyBodyProps {
242249
function EmptyBody({ hasRetainedLogs }: EmptyBodyProps) {
243250
return (
244251
<TableBody style={{ display: 'grid' }}>
245-
<TableRow style={{ display: 'flex', minWidth: '100%' }}>
252+
<TableRow style={{ display: 'flex', minWidth: '100%', marginTop: 8 }}>
246253
<TableCell className="flex-1">
247254
<div className="h-[35vh] w-full gap-2 relative flex flex-col justify-center items-center p-6">
248255
<div className="flex items-center gap-2">
@@ -279,11 +286,10 @@ function LevelFilter({ level, onLevelChange }: LevelFilterProps) {
279286
<DropdownMenuTrigger asChild>
280287
<Button
281288
variant="outline"
282-
size="sm"
283-
className="font-sans w-min normal-case"
289+
className="font-sans w-min normal-case prose-body-highlight h-9"
284290
>
285291
<LevelIndicator level={selectedLevel} />
286-
Min Level {selectedLabel}
292+
Min Level · {selectedLabel}
287293
</Button>
288294
</DropdownMenuTrigger>
289295
<DropdownMenuContent align="start">
@@ -310,7 +316,7 @@ function LevelIndicator({ level }: { level: LogLevelFilter }) {
310316
'size-3.5 rounded-full bg-bg border-[1.5px] border-dashed',
311317
{
312318
'border-fg-tertiary': level === 'debug',
313-
'border-accent-positive-highlight': level === 'info',
319+
'border-accent-info-highlight': level === 'info',
314320
'border-accent-warning-highlight': level === 'warn',
315321
'border-accent-error-highlight': level === 'error',
316322
}
@@ -377,6 +383,7 @@ function VirtualizedLogsBody({
377383
estimateSize: () => ROW_HEIGHT_PX,
378384
getScrollElement: () => scrollContainerRef.current,
379385
overscan: VIRTUAL_OVERSCAN,
386+
paddingStart: 8,
380387
})
381388

382389
const containerWidth = scrollContainerRef.current?.clientWidth ?? 0
@@ -586,7 +593,7 @@ function LogRow({ log, virtualRow, virtualizer, startedAt }: LogRowProps) {
586593
}}
587594
>
588595
<TableCell
589-
className="py-0"
596+
className="py-0 px-0 pr-4"
590597
style={{
591598
display: 'flex',
592599
alignItems: 'center',
@@ -599,7 +606,7 @@ function LogRow({ log, virtualRow, virtualizer, startedAt }: LogRowProps) {
599606
/>
600607
</TableCell>
601608
<TableCell
602-
className="py-0"
609+
className="py-0 px-0 pr-4"
603610
style={{
604611
display: 'flex',
605612
alignItems: 'center',
@@ -609,7 +616,7 @@ function LogRow({ log, virtualRow, virtualizer, startedAt }: LogRowProps) {
609616
<LogLevel level={log.level} />
610617
</TableCell>
611618
<TableCell
612-
className="py-0"
619+
className="py-0 px-0"
613620
style={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap' }}
614621
>
615622
<Message message={log.message} />
@@ -652,7 +659,7 @@ function StatusRow({
652659
justifyContent: 'start',
653660
}}
654661
>
655-
<span className="prose-body-highlight text-fg-tertiary uppercase">
662+
<span className="prose-body text-fg-tertiary pb-1">
656663
{isFetchingNextPage ? (
657664
<span className="inline-flex gap-1">
658665
Loading more logs

src/features/dashboard/layouts/details-row.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface DetailItemProps extends React.HTMLAttributes<HTMLDivElement> {
77

88
export function DetailsItem({ label, children, ...props }: DetailItemProps) {
99
return (
10-
<div className={cn('flex flex-col gap-2')} {...props}>
10+
<div className={cn('flex flex-col gap-1')} {...props}>
1111
<span className="text-fg-tertiary prose-label uppercase">{label}</span>
1212
{children}
1313
</div>

src/features/dashboard/layouts/header.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,17 @@ export default function DashboardLayoutHeader({
3636
className
3737
)}
3838
>
39-
<div className="flex items-center gap-2 w-full relative min-h-7">
40-
<SidebarTrigger className="w-7 h-7 md:hidden -translate-x-1" />
39+
<div className="flex items-center w-full relative min-h-6 gap-2">
40+
<SidebarTrigger className="w-7 h-7 md:hidden -translate-x-1 shrink-0" />
4141

42-
<h1 className="mr-auto align-middle truncate">
42+
<h1 className="truncate min-w-0 flex-1">
4343
<HeaderTitle title={config.title} />
4444
</h1>
4545

46-
{children}
46+
{/* Ghost element - reserves width but not height */}
47+
<div className="h-0 overflow-visible shrink-0 flex items-center">
48+
{children}
49+
</div>
4750

4851
<ClientOnly>
4952
<ThemeSwitcher />
@@ -62,16 +65,16 @@ function HeaderTitle({ title }: { title: string | TitleSegment[] }) {
6265
<span className="flex items-center gap-1">
6366
{title.map((segment, index) => (
6467
<Fragment key={index}>
65-
{index > 0 && <span className="text-fg-tertiary select-none">/</span>}
68+
{index > 0 && <span className="text-fg-tertiary select-none shrink-0">/</span>}
6669
{segment.href ? (
6770
<Link
6871
href={segment.href}
69-
className="text-fg-secondary hover:text-fg transition-colors hover:underline"
72+
className="text-fg-secondary hover:text-fg transition-colors hover:underline shrink-0"
7073
>
7174
{segment.label}
7275
</Link>
7376
) : (
74-
<span>{segment.label}</span>
77+
<span className="truncate">{segment.label}</span>
7578
)}
7679
</Fragment>
7780
))}

src/features/dashboard/layouts/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default function DashboardLayout({
1515
<div className="max-h-dvh h-full relative flex flex-col min-h-0">
1616
<DashboardLayoutHeader>
1717
<LiveSandboxCounterServer
18-
className="top-1/2 -translate-y-1/2 absolute right-10 max-md:hidden"
18+
className="max-md:hidden"
1919
params={params}
2020
/>
2121
</DashboardLayoutHeader>

src/ui/primitives/table.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ const TableHead = React.forwardRef<
7979
<th
8080
ref={ref}
8181
className={cn(
82-
'h-10 px-4 text-left align-middle',
83-
'font-mono prose-label-highlight uppercase',
82+
'h-8 px-4 text-left align-middle',
83+
'font-mono prose-label uppercase',
8484
'text-fg-tertiary',
85+
'data-[state=selected]:prose-label-highlight data-[state=selected]:text-fg',
8586
'[&:has([role=checkbox])]:pr-0 first:pl-0 last:pr-0',
8687
className
8788
)}

0 commit comments

Comments
 (0)