給還不熟 css @container 讀者的懶人包:在過去,前端時常透過 @media screen and (min-width: 900px) 來根據螢幕寬度改變畫面樣式(比如 flex-direction 在裝置螢幕變寬時,從 column 變成 row)。而 @container 的出現讓比較基準從此可以脫離「螢幕寬」,現在開發者可以將「容器(親元件)的寬度」作為條件來改變子元件的樣式設定。
個人目前的部落格首頁右半部就是使用 @container 來處理最新五篇文章(圖佐說明)的排版。今天會來簡單解說製作方式。
完整程式碼位置如下:
處理首頁排版
首先設定 .featured-posts-container 來處理文章流向。在螢幕寬低於 1200px 時,透過 display: flex 搭配 flex-direction: column 保持垂直排版:
.featured-posts-container { display: flex; flex-direction: column; gap: 16px; margin-bottom: 16px;}而當螢幕寬於 1200px 後,改用 display: grid 搭配 grid-template-areas 來安排文章元件佈局。以下設定代表:整片 grid 區塊呈現 3 x 2 的格位,除 post-0 佔據第一行的兩格位置外,其餘的文章都各自佔據一個格子。
.featured-posts-container { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 1fr); grid-template-areas: "post-0 post-0 post-1" "post-2 post-3 post-4"; gap: 16px;}而在透過 postShouldWithSummary 渲染文章元件時,將 index 資訊(此文排序第X篇)透過 class:list 傳入文章元件 FeaturedPostLayout 中:
postShouldWithSummary.map((post, index) => ( <div class:list={["post", `post-${index}`]}> <FeaturedPostLayout title={post.frontmatter.title} date={post.frontmatter.date} tag={post.frontmatter.tag} banner={post.frontmatter.banner} summary={post.frontmatter.summary || ""} url={post.url} /> </div>));接著就可以在 <style></style> 裡透過 .post-{index} 來指定每一個文章元件會與哪一部分的 grid-area 掛鉤:
.post-0 { grid-area: post-0;}
.post-1 { grid-area: post-1;}
.post-2 { grid-area: post-2;}
.post-3 { grid-area: post-3;}
.post-4 { grid-area: post-4;}首頁完工,接下來會來介紹 FeaturedPostLayout 中的設定。
處理 FeaturedPostLayout 樣式
首先在元件的 Component Script 部位取出 Astro.props 中的背景圖位置,設定為變數 bannerUrl:
const { title, date, tag, banner, summary, url } = Astro.props;const bannerUrl = banner ? `url(${banner})` : "none";接著將背景圖變數透過 define:vars 傳入 astro 元件的樣式標籤中:
<style define:vars={{ bannerUrl }}> /* styles ... */</style>這樣就能在樣式區塊取用 Astro.props 提供的文章標題配圖了 (́◉◞౪◟◉‵)
接著來處理元件本身的排版。首先設定最外層的 div 元件「寬(inline-size)」為 @container 參考值:
.featured-post-wrapper { container-type: inline-size;}搭配 @container (min-width: 520px) 即代表 .featured-post-wrapper 的寬度至少有 520px 時,圖片與文字欄位要改為 flex-direction: row 流向:
@container (min-width: 520px) { .featured-post-layout { flex-direction: row; }
.banner-wrapper, .text-wrapper { flex: 1 1 50%; }}對比 .featured-post-wrapper 的寬度未滿 520px 時,圖文要採取 column 排版:
.featured-post-layout { height: 100%; display: flex; flex-direction: column; gap: 16px;}這樣就完成 css container query 的設定了。現在 .banner-wrapper 與 .text-wrapper 會根據 .featured-post-wrapper 的寬度來改變排版。
最後因為想要做出「滑鼠 hover 後、圖片放大(要能搭配 transition)」的效果,決定使用 background-image 搭配偽元素 ::before 來實現。首先將 .banner-wrapper 設定為 positive: relative 來作為 偽元素的定位點:
.banner-wrapper { position: relative; min-height: 200px; overflow: hidden;}接著將背景塞到 ::before 中,並設定 transition 效果:
.banner-wrapper::before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-size: cover; background-position: center center; background-repeat: no-repeat; background-image: var(--bannerUrl); transform: scale(1); transition: transform 0.3s ease;}最後設定 :hover 時,要微微放大 ::before 內容:
.banner-wrapper:hover::before { transform: scale(1.1);}大功告成。
總結
目前四大主流瀏覽器都已經支援 container query 了,讓樣式不再根據裝置、而是親容器寬度來安排,可以調整出視覺效果更好的排版。
十分推薦在比較不需要擔心相容性的 side project 練手唷 (・`ω´・)