fix: stabilize ProductShowcaseSection accordion height across items

Use CSS grid stacking (col-start-1 row-start-1) so all FeatureCard
variants and inactive button variants occupy the same cell. The cell
height equals the tallest child, keeping total section height constant
regardless of which accordion item is active.

Amp-Thread-ID: https://ampcode.com/threads/T-019d9305-89f9-77d0-a0e5-8243084dc162
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
DrJKL
2026-04-15 14:31:11 -07:00
parent 58e8d6a345
commit edf0c4e19e

View File

@@ -54,36 +54,77 @@ const activeIndex = ref(0)
<!-- Image area (desktop only) -->
<BorderedPlaceholder class="hidden flex-1 lg:flex" />
<!-- Feature accordion -->
<div class="flex w-full flex-col lg:w-80 lg:gap-4 lg:pl-5">
<!-- Feature accordion (desktop) -->
<div class="hidden w-full lg:flex lg:w-80 lg:flex-col lg:gap-4 lg:pl-5">
<template v-for="(feature, i) in features" :key="feature.title">
<!-- Image area (mobile, rendered before active item) -->
<BorderedPlaceholder
v-if="activeIndex === i"
class="lg:hidden"
:class="activeIndex !== 0 ? 'mt-4 lg:mt-0' : 'mt-0'"
/>
<!-- Active: connector + card -->
<!-- Active: connector + stacked cards (constant height) -->
<div v-if="activeIndex === i" class="flex items-stretch lg:-ml-5">
<img
src="/icons/node-link.svg"
alt=""
class="-mx-px hidden self-center lg:block"
class="-mx-px self-center"
aria-hidden="true"
/>
<div class="grid w-full">
<FeatureCard
v-for="(f, j) in features"
:key="f.title"
:title="f.title"
:description="f.description"
:class="activeIndex === j ? 'visible' : 'invisible'"
class="col-start-1 row-start-1"
@click="activeIndex = j"
/>
</div>
</div>
<!-- Inactive: stacked buttons (constant height) -->
<div v-else class="grid">
<button
v-for="(f, j) in features"
:key="f.title"
class="rounded-5xl col-start-1 row-start-1 w-full cursor-pointer p-8 text-left transition-all"
:class="
j === i
? 'bg-transparency-white-t4 text-primary-comfy-canvas visible'
: 'invisible'
"
:tabindex="j === i ? 0 : -1"
@click="activeIndex = j"
>
<div class="flex items-start justify-between gap-3">
<h3 class="text-2xl/tight font-medium">
{{ f.title }}
</h3>
<img
src="/icons/plus.svg"
alt=""
class="size-5 shrink-0"
aria-hidden="true"
/>
</div>
</button>
</div>
</template>
</div>
<!-- Feature accordion (mobile) -->
<div class="flex w-full flex-col lg:hidden">
<template v-for="(feature, i) in features" :key="feature.title">
<!-- Image area (mobile, rendered before active item) -->
<BorderedPlaceholder
v-if="activeIndex === i"
:class="activeIndex !== 0 ? 'mt-4' : 'mt-0'"
/>
<!-- Active: connector (mobile only) -->
<div v-if="activeIndex === i" class="flex items-stretch">
<img
src="/icons/node-link.svg"
alt=""
class="-my-2.25 ml-20 block scale-x-75 scale-y-50 rotate-90 lg:hidden"
class="-my-2.25 ml-20 block scale-x-75 scale-y-50 rotate-90"
aria-hidden="true"
/>
<FeatureCard
:title="feature.title"
:description="feature.description"
class="hidden lg:block"
@click="activeIndex = i"
/>
</div>
<!-- Active card (mobile only) -->
@@ -91,14 +132,13 @@ const activeIndex = ref(0)
v-if="activeIndex === i"
:title="feature.title"
:description="feature.description"
class="lg:hidden"
@click="activeIndex = i"
/>
<!-- Inactive card -->
<!-- Inactive card (mobile) -->
<button
v-else
class="rounded-5xl bg-transparency-white-t4 text-primary-comfy-canvas mt-4 w-full cursor-pointer p-8 text-left transition-all lg:mt-0"
class="rounded-5xl bg-transparency-white-t4 text-primary-comfy-canvas mt-4 w-full cursor-pointer p-8 text-left transition-all"
@click="activeIndex = i"
>
<div class="flex items-start justify-between gap-3">