Skip to content

Commit 6d5da40

Browse files
Adebesin-Cellclaude
andcommitted
fix: improve OG grid layout with proper Satori flex gap and truncation
- Use numeric gap values (Satori-compatible) instead of CSS string gaps - Use flex-wrap with 220px items for grid layout (4 columns) - Add maxWidth on name spans for reliable text truncation - Remove version from grid tier to prevent layout misalignment - Redesign summary tier: smaller "13 packages" text to not compete with title - Improve /wk text visibility (#d4d4d4) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 935c48b commit 6d5da40

File tree

1 file changed

+68
-82
lines changed

1 file changed

+68
-82
lines changed

app/components/OgImage/Compare.vue

Lines changed: 68 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,6 @@ const layoutTier = computed<LayoutTier>(() => {
5454
return 'summary'
5555
})
5656
57-
const NAME_MAX_LENGTH = 40
58-
59-
function truncateName(name: string): string {
60-
if (name.length <= NAME_MAX_LENGTH) return name
61-
return name.slice(0, NAME_MAX_LENGTH - 1) + ''
62-
}
63-
6457
interface PkgStats {
6558
name: string
6659
downloads: number
@@ -88,7 +81,7 @@ if (layoutTier.value !== 'summary') {
8881
}).catch(() => null),
8982
])
9083
return {
91-
name: truncateName(name),
84+
name,
9285
downloads: dlData?.downloads ?? 0,
9386
version: pkgData?.['dist-tags']?.latest ?? '',
9487
color: ACCENT_COLORS[index % ACCENT_COLORS.length]!,
@@ -99,7 +92,7 @@ if (layoutTier.value !== 'summary') {
9992
stats.value = results.sort((a, b) => b.downloads - a.downloads)
10093
} catch {
10194
stats.value = displayPackages.value.map((name, index) => ({
102-
name: truncateName(name),
95+
name,
10396
downloads: 0,
10497
version: '',
10598
color: ACCENT_COLORS[index % ACCENT_COLORS.length]!,
@@ -126,33 +119,7 @@ function barPct(downloads: number): string {
126119
return `${Math.min(BAR_MAX_PCT, Math.max(pct, BAR_MIN_PCT))}%`
127120
}
128121
129-
// Grid layout: aim for 2 balanced rows
130-
const GRID_COLS_SMALL = 4 // for up to 8 packages (2 rows of 4)
131-
const GRID_COLS_LARGE = 5 // for 9+ packages (2 rows of 5)
132-
133-
const gridColumns = computed(() => {
134-
const count = stats.value.length
135-
if (count <= GRID_COLS_SMALL * 2) return GRID_COLS_SMALL
136-
return GRID_COLS_LARGE
137-
})
138-
139-
const GRID_ITEM_GAP = 10
140-
const gridItemWidth = computed(
141-
() => `${Math.floor(CONTENT_WIDTH / gridColumns.value) - GRID_ITEM_GAP}px`,
142-
)
143-
144-
const gridRows = computed(() => {
145-
const cols = gridColumns.value
146-
const rows: PkgStats[][] = []
147-
for (let i = 0; i < stats.value.length; i += cols) {
148-
rows.push(stats.value.slice(i, i + cols))
149-
}
150-
return rows
151-
})
152-
153-
const summaryTopNames = computed(() =>
154-
displayPackages.value.slice(0, SUMMARY_TOP_COUNT).map(truncateName),
155-
)
122+
const summaryTopNames = computed(() => displayPackages.value.slice(0, SUMMARY_TOP_COUNT))
156123
const summaryRemainder = computed(() =>
157124
Math.max(0, displayPackages.value.length - SUMMARY_TOP_COUNT),
158125
)
@@ -213,8 +180,14 @@ const summaryRemainder = computed(() =>
213180
<div v-for="pkg in stats" :key="pkg.name" class="flex flex-col gap-1">
214181
<div class="flex items-center gap-3" style="font-family: 'Geist', sans-serif">
215182
<span
216-
class="text-2xl font-semibold tracking-tight truncate max-w-[400px]"
217-
:style="{ color: pkg.color }"
183+
class="text-2xl font-semibold tracking-tight"
184+
:style="{
185+
color: pkg.color,
186+
maxWidth: '400px',
187+
overflow: 'hidden',
188+
textOverflow: 'ellipsis',
189+
whiteSpace: 'nowrap',
190+
}"
218191
>
219192
{{ pkg.name }}
220193
</span>
@@ -248,8 +221,14 @@ const summaryRemainder = computed(() =>
248221
<div v-for="pkg in stats" :key="pkg.name" class="flex flex-col gap-0.5">
249222
<div class="flex items-center gap-2" style="font-family: 'Geist', sans-serif">
250223
<span
251-
class="text-xl font-semibold tracking-tight truncate max-w-[300px]"
252-
:style="{ color: pkg.color }"
224+
class="text-xl font-semibold tracking-tight"
225+
:style="{
226+
color: pkg.color,
227+
maxWidth: '300px',
228+
overflow: 'hidden',
229+
textOverflow: 'ellipsis',
230+
whiteSpace: 'nowrap',
231+
}"
253232
>
254233
{{ pkg.name }}
255234
</span>
@@ -278,63 +257,70 @@ const summaryRemainder = computed(() =>
278257
</div>
279258
</div>
280259

281-
<!-- GRID layout (7-12 packages): packages in a side-by-side grid -->
260+
<!-- GRID layout (7-12 packages): flex-wrap grid -->
282261
<div
283262
v-else-if="layoutTier === 'grid'"
284-
class="flex flex-col gap-6"
285-
style="font-family: 'Geist', sans-serif"
263+
:style="{
264+
display: 'flex',
265+
flexWrap: 'wrap',
266+
rowGap: 24,
267+
columnGap: 40,
268+
fontFamily: 'Geist, sans-serif',
269+
}"
286270
>
287-
<div v-for="(row, ri) in gridRows" :key="ri" class="flex items-start">
288-
<!-- Using <span> as grid items because Satori treats <div> as full-width flex columns -->
271+
<span
272+
v-for="pkg in stats"
273+
:key="pkg.name"
274+
:style="{
275+
display: 'flex',
276+
flexDirection: 'column',
277+
gap: 2,
278+
width: '220px',
279+
}"
280+
>
289281
<span
290-
v-for="pkg in row"
291-
:key="pkg.name"
282+
class="font-semibold tracking-tight"
292283
:style="{
293-
display: 'flex',
294-
flexDirection: 'column',
295-
gap: '2px',
296-
width: gridItemWidth,
284+
fontSize: '18px',
285+
maxWidth: '220px',
286+
overflow: 'hidden',
287+
textOverflow: 'ellipsis',
288+
whiteSpace: 'nowrap',
289+
color: pkg.color,
297290
}"
291+
>{{ pkg.name }}</span
298292
>
299-
<span class="flex items-baseline gap-1.5">
300-
<span
301-
class="font-semibold tracking-tight"
302-
:style="{
303-
fontSize: '18px',
304-
overflow: 'hidden',
305-
textOverflow: 'ellipsis',
306-
whiteSpace: 'nowrap',
307-
color: pkg.color,
308-
}"
309-
>{{ pkg.name }}</span
310-
>
311-
<span v-if="pkg.version" class="text-xs text-[#a3a3a3]">{{ pkg.version }}</span>
312-
</span>
313-
<span class="flex items-baseline gap-0.5">
314-
<span class="text-2xl font-bold text-[#e5e5e5]">{{
315-
formatDownloads(pkg.downloads)
316-
}}</span>
317-
<span class="text-sm font-medium text-[#a3a3a3]">/wk</span>
318-
</span>
293+
<span :style="{ display: 'flex', alignItems: 'baseline', gap: 2 }">
294+
<span class="text-2xl font-bold text-[#e5e5e5]">{{
295+
formatDownloads(pkg.downloads)
296+
}}</span>
297+
<span class="text-sm font-medium text-[#d4d4d4]">/wk</span>
319298
</span>
320-
</div>
299+
</span>
321300
</div>
322301

323-
<!-- SUMMARY layout (13+ packages): top names + remainder count -->
324-
<div v-else class="flex flex-col gap-4" style="font-family: 'Geist', sans-serif">
325-
<div class="text-5xl font-bold tracking-tight">
326-
Comparing {{ displayPackages.length }} packages
302+
<!-- SUMMARY layout (13+ packages): package count + top names -->
303+
<div v-else class="flex flex-col gap-3" style="font-family: 'Geist', sans-serif">
304+
<div class="text-2xl text-[#a3a3a3]">
305+
<span class="text-4xl font-bold text-[#fafafa]">{{ displayPackages.length }}</span>
306+
packages
327307
</div>
328-
<div class="flex items-center gap-2 text-2xl text-[#a3a3a3]">
308+
<div :style="{ display: 'flex', alignItems: 'baseline', gap: 8, whiteSpace: 'nowrap' }">
329309
<span
330310
v-for="(name, i) in summaryTopNames"
331311
:key="name"
332-
class="font-semibold"
333-
:style="{ color: ACCENT_COLORS[i % ACCENT_COLORS.length] }"
312+
class="text-xl font-semibold"
313+
:style="{
314+
color: ACCENT_COLORS[i % ACCENT_COLORS.length],
315+
maxWidth: '280px',
316+
overflow: 'hidden',
317+
textOverflow: 'ellipsis',
318+
whiteSpace: 'nowrap',
319+
flexShrink: 1,
320+
}"
321+
>{{ name }}{{ i < summaryTopNames.length - 1 ? ',' : '' }}</span
334322
>
335-
{{ name }}<span v-if="i < summaryTopNames.length - 1" class="text-[#525252]">,</span>
336-
</span>
337-
<span v-if="summaryRemainder > 0" class="text-[#737373]">
323+
<span v-if="summaryRemainder > 0" class="text-xl text-[#737373]">
338324
+{{ summaryRemainder }} more
339325
</span>
340326
</div>

0 commit comments

Comments
 (0)