在電商網頁中,很常見到格狀的商品列表排列,前陣子也有機會碰到這種列表的排版,記錄當時處理的一些小細節~
預期最後的排版方式如下:

問題:卡片寬度 + 間隔導致非預期換行#
當時第一時間想到的是列表用 flex,再用 w-1/4給定卡片寬度、gap 處理卡片之間的間隔,程式碼如下(完整程式碼):
(以下程式皆使用 React.js 撰寫,部分樣式與資料參考自Pinkoi)
<ul className='flex flex-wrap gap-4'> {products.map((product) => ( <li className='w-full sm:w-1/2 md:w-1/3 lg:w-1/4' key={product.id}> ... </li> ))}</ul>;但這樣會導致非預期的換行,預期一列要有 4 個商品,實際上一列卻只排了3 個商品,如下圖:

會產生這樣的原因是因為我指定每個商品寬度都是 1/4,又希望商品之間間隔 16px,而導致一列的總寬度超過 100%,因此 flex-wrap 讓多出的商品換行。
為了解決這問題,我研究了以下 3 種解法:
解法一:grid#
gird 應該是處理格狀排版最常見也最直覺的方式了,參考 tailwindui 提供的 Product List components 就可快速排出整齊的商品列表,於是我將原本的程式碼改寫成 grid,主要更動 ul 標籤的 class,程式碼如下(完整程式碼):
<ul className='grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4'> {products.map((product) => ( <li key={product.id}> ... </li> ))}</ul>;畫面就出現一列 4 個商品的整齊排版啦👏👏👏
另外,為了確認格線有對齊其他元件,多寫了一個“全部商品”的標題,用來確認整體格線是對齊的~以下 2 種解法也都會用這種方式檢查對齊

解法二:flex + -margin + padding#
如果我還是想要用 flex 排版呢? 我逛了一些電商的網頁,發現另一種寫法,也可能是 grid 還沒那麼普及時用的方法:
一樣用 w-1/4 給定卡片寬度,但在每個 li 寫 padding 向內推擠,產生卡片間的空白間隔,不過這樣列表的左右側會產生多餘的空白,因此整個 ul 列表需要再給一個負值的 margin,讓列表將多的空白回推~示意圖如下:

主要就是在 li 寫 padding,ul 寫 -margin ,程式碼如下(完整程式碼):
<ul className='flex flex-wrap -m-2'> {products.map((product) => ( <li key={product.id} className='w-full sm:w-1/2 md:w-1/3 lg:w-1/4 p-2'> ... </li> ))}</ul>;畫面就出現一列 4 個商品的整齊排版啦👏👏👏

此解法主要是參考嘖嘖的專案探索頁,有興趣的讀者可以點進去看看~截圖他們的樣式如下:


補充,其實 Bootstrap 的格線系統也是用 padding 處理間隔的唷,有興趣也可以去官方文件看看。
解法三:flex + gap + basis-calc(寬度-gap)#
解法三其實是解法二的變形,也是我逛到別的網頁發現的解法,這個解法應該最接近一開始我想用的方法:
一樣用 flex 和 gap,但卡片寬度要扣掉 gap 的寬度,讓整體寬度加起來是 100%。公式如下:
卡片寬度- (gap 寬度 * gap數量/一列的卡片數)
舉例來說,如果卡片寬度 25%,gap 寬度 16px,因為一列 4 個卡片會有 3 個 gap,所以每個商品卡片的寬度是 25%-(16*3/4) px。
程式碼如下(完整程式碼):
<ul className='flex flex-wrap gap-4'> {products.map((product) => ( <li key={product.id} className='basis-full sm:basis-[calc(50%-(16*1/2*1px))] md:basis-[calc(33%-(16*2/3*1px))] lg:basis-[calc(25%-(16*3/4*1px))]' > ... </li> ))}</ul>;畫面就出現一列 4 個商品的整齊排版啦👏👏👏

此解法主要是參考 RHINOSHIELD 的商品列表,有興趣的讀者可以點進去看看,不過有發現他們的寬度沒有考量到 gap 只會在中間有(4 個商品只要扣掉 3 個 gap),因此每列的最右側都會空出空白,一列沒有完全填滿 100%。以md:basis-[calc(100%*4/12–20px)] 為例,應該要改成 md:basis-[calc(100%*4/12–15px)] 才會讓左右都貼齊外層框,讀者可以自己試試看😎

以上就是我在用 Tailwind CSS 排列商品列表時發現的小細節,希望對大家有幫助 :D
如有任何問題歡迎聯絡、不吝指教✍️