給還不熟 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
中的設定。
首先在元件的 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 練手唷 (・ω´・)
本文同步發佈於普通文組 2.0