iT邦幫忙

2024 iThome 鐵人賽

DAY 10
0
Modern Web

Dive into CSS Challenge:從問題到解決方案的實踐之旅系列 第 10

CSS Challenge Day #7:Notification、Menu、Search(上)

  • 分享至 

  • xImage
  •  

題目

CSS Challenge Day7

  • Notification Card
    https://ithelp.ithome.com.tw/upload/images/20240921/201694035gAEy0oBdC.png

題目除了基本 Notification Card 介面樣式以外,還有兩個重要的介面

  • Menu
    https://ithelp.ithome.com.tw/upload/images/20240921/201694033Smw1xJZIi.png

  • Search
    https://ithelp.ithome.com.tw/upload/images/20240921/20169403GTSfkpkerf.png

上面的圖是題目,而我們要做出幾乎一樣的樣子,題目中還有附上出題官方的CodePen,也有附上給我們解題用的template,當我們真的不會的時候,還是可以參考他們的寫法,所以沒有想像中困難。

我做好的此題CSS Challeage解答

那麼我們就開始吧。

題目分析

這個題目要求我們製作一個通知介面

  1. 通知介面內的通知會在初次進來這個頁面的時候,以動畫的方式由下往上出現。
  2. 點擊通知介面左上的Menu按鈕後,會開啟Menu畫面。
  3. 點擊通知介面右上角的Search按鈕後,會開啟搜尋介面,搜尋框會可以輸入文字。
  4. 通知內的每一項通知跟Menu內的每個項目都有滑鼠指上的變化效果。

這裡我覺得挑戰的是

  • 左上角的MenuIcon的畫法。
  • 通知介面中,線條跟藍色小圓點的CSS繪製方式。

開始解題

基礎架構

<div class="frame">
	<!-- card -->
    <div class="card">
		<header>
			<div class="menuIcon"></div>
			<h1>Notifications</h1>
			<button class="searchIcon">
				<i class="fa fa-search search-icon"></i>
			</button>
		</header>
		<div class="content"></div>
  </div>
	
	<!-- menu -->
	<div class="menuList"></div>
</div>

由於這次題目算起來有三個介面,所以照慣例,我們先開出基本的版型,複雜的內容都先不要管他。
在這裡我們就先分兩塊 .card.menuList.menuList 開出來之後,就先去CSS內先把他 display:none,我們之後再來管他,先專注在主要的通知卡片。

.card 內我們照慣例分上下,上半截我直接用 header,下面我就取名為 .content

這邊的 .searchIcon 一樣是使用 css challenge 官方的 font-awesome 來製作,當然你也可以用自己的。
https://ithelp.ithome.com.tw/upload/images/20240919/20169403YoyNAOV29v.png

那這邊因為我們需要用到一些有自帶 padding margin line-height 等等的物件,所以要記得在最前面把他做 css reset 唷。

// reset
h1, p, button, input, header, i, span, ul, li {
	padding: 0;
	margin: 0;
	line-height: 1;
	border: none;
}

Notification Card

背景

.frame {
    ...
    background: #264057;
}

先用我們第一天教的小工具來吸最外面深藍色的底色。

卡片

$cardWH: 300px;
...
.card {
    ...
	background-color: #fff;
	box-shadow: 10px 10px 15px 0 rgba(0,0,0,0.3);
	color: #666;
	width: $cardWH;
	height: $cardWH;
	z-index: 2;
	border-radius: 3px;
	overflow: hidden;
}

再來就是把template內的 .center 改名成 .card,用第一天教的小工具尺來算出寬高,把寬高定好。
由於我們等等底下會放一層Menu,所以這邊先設定 z-index: 2,並且依照題目的樣式作了一點點的圓角,並且設定 overflow: hidden 以確保東西超出去之後會被隱藏。

卡片藍色header

$blue: #5F98CD;
$headerH: 60px;
...
header {
	background-color: $blue;
	width: 100%;
	height: $headerH;
	padding: 14px 20px;
	box-sizing: border-box;
	position: relative;
}

這裡就一樣使用剛剛吸色的小工具來把上面淺藍色給吸起來。

寬度我希望他能夠順應外面的卡片寬,所以我用 width: 100%
接著設定卡片內上下左右的 padding 好讓裡面的東西不會黏在邊緣,因為要設定 padding,這邊記得加上 box-sizing: border-box 才能讓寬度不會變成 100% + padding

然後我們希望這裡會是一個畫布,他上面的東西都要以他的寬高為基準來移動,所以加上這行 position: relative

卡片內白色content

$contentH: $cardWH - $headerH;
...
.content {
	position :relative;
	width: $cardWH;
	height: $contentH;
	overflow: hidden;
}

這邊就是基本的設定它的寬高,然後我們就可以開始做裡面。
現在全部弄好了的話長這樣:
https://ithelp.ithome.com.tw/upload/images/20240922/20169403W7tlmemz51.png


Notification Card - 藍色 header

...
header {
	...
    display: flex;
	justify-content: space-between;
	align-items: center;
}

因為我們要讓裡面的 menuIconH1.searchIcon 左右並排在同一排,所以加上 display: flex 並使用 align-items: center 讓他們在同一個水平線高度。
接著使用 justify-content: space-between 這個屬性,讓裡面的物件左右貼到容器的最邊緣,並平均分配間距。
https://ithelp.ithome.com.tw/upload/images/20240922/20169403WUkaNm4D5L.png
像圖片我們可以看到,虛線外圍那一圈綠色,就是我們一開始給 headerpadding
最左邊 menuIcon 裡面因為目前沒有放東西,所以看起來沒有東西,但他仍然有被算在顯示的物件內。

menuIcon 右邊先有了一個紫色間距,然後才是 H1

H1 右邊又是一個一樣寬度的紫色間距,然後才是 .searchIcon

1. H1 Title

header {
    ...
    h1 {
		color: #fff;
		font-size: 16px;
		font-weight: 600;
		text-align: center;
	}
}

確定前面有做好 css reset 的話,這邊我們只需要簡單的把樣式設定好就可以。

2. .searchIcon

$headerIconWH: 30px;
...
header{
    ...
	.searchIcon {
		border: none;
		background-color: transparent;
		width: $headerIconWH;
		height: $headerIconWH;
		color: #fff;
		cursor: pointer;
		font-size: 21px;
		opacity: 0.5;
		font-weight: bold;
		text-align: right;
		transition: all ease-in .2s;
		&:hover {
			opacity: 1;
		}
	}
}

由於 button 本身會自帶邊框跟背景色,所以在這邊先把它們移除。
接著設定一個寬高,我用變數,因為等等左邊的 menuIcon 應該也會用到,所以設定一個高度讓兩邊一致。

接著把它的視覺樣式調整一下,設定好大小,粗細等等。
不要忘記加上滑鼠指上的效果,設定 cursor: pointer

然後是視覺樣式上,一開始預設的透明度是 opacity: 0.5,等滑鼠指上後要變成 opacity: 1,為了讓這個動態是漸變的,我們加上 transition: all ease-in .2s
https://ithelp.ithome.com.tw/upload/images/20240922/20169403LBuCS8ymdl.png

3. .menuIcon

https://ithelp.ithome.com.tw/upload/images/20240922/201694037pRJHJGosO.png
我個人覺得這顆 MenuIcon 有點討厭...
依照題目,他分成上下兩條線條,邊緣都有圓角,兩條線條之間的 gap 很寬。
上面那條線條的右邊是一顆稍大的圓點,他跟線條之間的 gap 很小。

這顆按鈕內的元素,除了高度不一樣(線條與圓點),它們之間的 gap 也不太一樣,個人覺得處理起來有點煩,我又不想要用 absolute 的方式來畫(好啦,可能是我自己的問題,我就是覺得用 absolute 來畫太無聊),所以我用其他方式畫了看看。

<div class="card">
    <header>
        <div class="menuIcon">
			<div class="menuLineTop"></div>
			<div class="menuDot"></div>
			<div class="menuLineBottom"></div>
		</div>
    </header>
</div>

首先,拉出這顆 icon 的基本版型:
.menuLineTop:這是左上角那條線條。
.menuDot:這是右上角的圓點。
.menuLineBottom:這是下面那條線條。
都取好名字之後,我們就開始做樣式。

header {
    ...
    .menuIcon {
		width: $headerIconWH;
		height: $headerIconWH;
		display: grid;
		gap:0 ;
		opacity: 0.6;
		box-sizing: border-box;
		padding: 4px 0;
		cursor: pointer;
		transition: all ease-in .2s;
		z-index: 2;
		grid-template-columns: 1fr 7px;
        grid-template-rows: 1fr 1fr;
        
		&:hover {
			opacity: 1;
		}
    }
}

首先,設定 .menuIcon 的寬高,這邊就用跟剛剛 .searchIcon 一樣的寬高就可以了。
然後再這邊設定他的 opacity 數值跟滑鼠樣式,因為等等是滑鼠指上這一整個區塊,都要觸發透明度的變化,所以要寫在這裡。最後不忘加上 transition

然後我們把他設定成 display: grid之後,開始設定這兩條的樣式:

  • grid-template-columns: 1fr 7px:這一句是設定 column 的樣式。

    • 每一列的第一個 column 會自動延展填滿可用空間。
    • 每一列的第二個 column 則固定 7px 寬。
  • grid-template-rows: 1fr 1fr:這一句是設定 row 的樣式。

    • 確保兩行均勻地分配高度,讓每個 row 自動延展至剩餘的空間。

接著來寫線條的樣式:

header {
    ...
    .menuIcon {
    ...
		[class^="menuLine"] {
			background-color: #fff;
			height: 3px;		[class^="menuLine"] {
			background-color: #fff;
			height: 3px;
			border-radius: 6px;
			align-self: center;
		}
    }
}

這邊先使用選擇器,選擇名字前面是 menuLine 的物件 [class^="menuLine"],去設定它們的樣式。
基本的顏色跟高度還有圓角,然後不要忘記把它們上下置中。

header {
    ...
    .menuIcon {
    ...
		[class^="menuLine"] {
            ...
		}
		.menuLine {
			&Top {
				grid-column: 1 / span 1;
			}
			&Bottom {
				grid-column: 1 / span 2;
			}
		}
    }
}

接著就去設定它們各自佔的比例。
我們剛剛前面已經設定過了 grid-template-columns: 1fr 7px,所以可以知道我們每行 row 是有 2 個 column 的。

而這邊的 menuLineTopgrid-column: 1 / span 1 這是指他佔有的 column 是從該行 ( row ) 的第一格 column 開始算,佔據 1 格。

menuLineBottomgrid-column: 1 / span 2 這是指他佔有的 column 是從該行 (也就是第二個 row) 的第一格 column 開始算,佔據 2 格。

header {
    ...
    .menuIcon {
    ...
        .menuDot {
			width: 7px;
			height: 7px;
			border-radius: 5px;
			background-color: #fff;
			align-self: center;
            justify-self: end;
		}
    }
}

然後就是 icon 右上角的小點點 .menuDot 了,這邊設定好我們原先預計的寬高之後,加上圓角,剛做好的時候會長這樣。
https://ithelp.ithome.com.tw/upload/images/20240922/20169403XmDvHOJJtu.png

看的出來這整個 grid 是有依照我們本來的預測在跑的。
第一行的右側圓點 7px,左側線條跟第二行的線條,都跟我們預期的一樣,是自動延展填滿整個空間。看起來沒有任何問題,除了第一行的兩個 column 之間應該要有點小空隙以外。

...
.menuLine {
	&Top {
		grid-column: 1 / span 1;
        margin-right: 3px;
	}

這時候我們就回到剛剛第一行左邊的線條那邊,加上一行 margin-right: 3px 讓他往右邊多一點空隙,這樣就完成囉~

https://ithelp.ithome.com.tw/upload/images/20240922/20169403CjvfyYuZS2.png

由於篇幅太長了,我決定分兩篇寫,下一篇再來寫後面的 Notification Card 的 .content、Menu、Search。


Wrap up and go home

希望改變了這種按照步驟的寫法,能讓更多人看得懂,也能跟我一樣喜歡上寫CSS。

那今天就先到這裡,明天我們再繼續來玩下一集。


上一篇
CSS Challenge Day #6:Profile卡片介面(下)
下一篇
CSS Challenge Day #7:Notification、Menu、Search(中)
系列文
Dive into CSS Challenge:從問題到解決方案的實踐之旅14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言