發表文章

目前顯示的是有「css」標籤的文章
圖片
以前如果說 CSS 是「程式碼」的話,通常都會被糾正為「原始碼」,但在 @function 等功能性的語法出現之後,現在這個傳統界線也越來越模糊。 本文將透過之前提過的 if() 和 sibling-index()、sibling-count() 語法,帶大家體驗 @function 的強大功能。 1. 結合 attr() 與型別定義的狀態樣式 過去我們使用 attr() 取值時,通常只能用於 content 屬性,且無法參與運算或邏輯判斷。新的語法允許我們指定型別 type(<custom-ident>) ,這讓 HTML 的屬性值可以被 CSS 的 if() 邏輯正確識別。 在這個範例中,我們定義了一個 --status-color 函數,它接收一個狀態參數,並根據傳入的值決定回傳的顏色。你可以注意到,它和 JavaScript 一樣可以設定變數的預設值: @function --status-color(--status: xxx, --success: #7dfaa5, --error: #ff3874) { result: if( style(--status: success): var(--success); style(--status: error): var(--error); else: #fccd81 ); } .card { /* 使用 attr() 抓取 HTML 的 data-status,並強制轉型為 custom-ident */ background-color: --status-color(attr(data-status type(<custom-ident>))); } 跟 JavaScript 不同的地方在於,JS 是用 return 拋出結果,CSS 則是用 result: 。 這樣的寫法讓我們可以直接在 HTML 中控制語意,而樣式的邏輯完全封裝在 CSS 函數內: <div class="card" data-status="error">error</div> <div class="card" data-status=...
圖片
CSS 中「兄弟元素」的應用與痛點 在網頁切版與動畫設計中,我們經常遇到需要根據「元素是第幾個」或「總共有幾個元素」來改變樣式的情境。例如: 交錯動畫 (Staggered Animation): 讓列表中的項目一個接一個出現,而不是同時出現。 動態寬度: 根據容器內有多少個項目,自動計算每個項目的寬度。 階梯狀視覺: 依序改變背景色深淺或位置偏移。 在過去,要純用 CSS 達成這些效果非常繁瑣。我們通常只能依賴 :nth-child() 寫死一堆規則(例如寫 50 行 CSS 來對應 50 個項目),或是使用 SCSS 的迴圈在編譯時產生代碼。更靈活的做法則是依賴 JavaScript 去計算並動態賦予 CSS 變數(如 --index , --total )。因此原生的 CSS 解決方案因此備受期待。 此圖片由AI生成 sibling-index() 與 sibling-count() sibling-index()返回該元素在兄弟節點中的 索引值 (通常從 1 開始計算,具體依實作而定)。它相當於 JavaScript 中遍歷 DOM 得到的 index。 sibling-count()返回該元素的父容器下,包含自己在內共有多少個 兄弟節點總數 。它相當於 JavaScript 中的 parentNode.children.length 。 基本語法範例: .item { /* 讓每個項目的動畫延遲時間隨索引增加 */ animation-delay: calc(sibling-index() * 0.1s); /* 根據總數量自動計算寬度 */ width: calc(100% / sibling-count()); } CodePen 實例解析 接下來我們透過 CodePen 實作一些簡單的範例。 See the Pen sibling-index(), sibling-count() by cj ( @cjzopen ) on CodePen . 自動計數與視覺堆疊 在範例中,透過 sibling-index() ,我們可以為每一個卡片或列表項目設定獨立的 Z 軸層級或顏色變化,而...
圖片
不知道大家有沒有看了Apple的 WWDC25 ?先不論他們說了什麼,我只在想他們的液態玻璃(Liquid Glass)UI要如何實現(這裡討論的是靜態效果)。 部分UI改成毛玻璃樣式。 然而大部分元件則是改成不太一樣的液態玻璃樣式。 backdrop-filter 一開始當然是先想到從以前就有的毛玻璃效果(Glassmorphism),也就是用backdrop-filter來嘗試: See the Pen backdrop-filter by cj ( @cjzopen ) on CodePen . 但你可以發現,做不出底下文字或圖片扭曲的效果,於是就想到了SVG的filter。 <svg style="display: none;"> <filter id="wavy-distort"> <feTurbulence type="turbulence" baseFrequency="0.02" numOctaves="2" result="turbulence"/> <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="10" xChannelSelector="R" yChannelSelector="G"/> </filter> </svg> feTurbulence 我們可以先在HTML裡創造一個擁有filter的SVG,其中包含feTurbulence: type: turbulence (亂流) / fractalNoise (雲)。 baseFrequency: 雜訊圖的密度。 numOctaves: 雜訊圖疊的層數。 result: 類似 id,後續拿來引用的名稱。 再使用feDisplacementMap引用feTurbulence: ...
圖片
CSS 的 if() 搭配 變數系統 或 attr() ,讓我們能直接用 CSS 判斷條件或 HTML 屬性自動達成樣式變化,在某些情況下不再需要寫任何 JavaScript。 範例 我們可以用不同 class 搭配變數不同的值,再加上 if 和 else 判斷來自動設定 background-color 的值: :root { --color: #333; } .element { background-color: if( style(--color: #fafafa): black; style(--color: #ed5fe8): #000a75; else: white); } .dark { --color: #fafafa; } .esport { --color: #ed5fe8; } 你可以注意到,寫複數個判斷條件還可以實現「else if」的效果。 但直接使用的缺點也很明顯,就是直接判斷變數的值太不直觀了,所以我們還可以用上 attr() 來改良。 用 attr() 優化 if() 的判斷條件 這裡我們使用 attr() 搭配 if() 判斷狀態(data-status)來給不同的值,程式寫起來就會直觀很多。 <div class="container"> <div class="card" data-status="success"></div> <div class="card" data-status="warning"></div> <div class="card" data-status="success"></div> <div class="card" data-status="error"></div> <div class="card" data-status="warning"></div>...
圖片
其實 HTML 已經有內建 Popover API 了,在某些簡單的場合下,你可以考慮直接用 HTML 和 CSS 就能寫出燈箱(彈出視窗)效果。 本圖片由AI生成 popovertarget 在 button 加上 popovertarget 對應燈箱的 id。 建立一個有 id 的元素,作為燈箱使用。 大功告成! 疑?就這樣? 對,就是這麼簡單。再來只要用 CSS 美化就好了。 See the Pen pure HTML popup by cj ( @cjzopen ) on CodePen . ::backdrop 這個 CSS 語法可以設計開啟燈箱的時候,燈箱以外的區域,例如背景顏色。 :popover-open 當你有多個 HTML 原生燈箱元素時,可以用這個語法先設計出 default 燈箱。 @starting-style 你可以用這個語法搭配 transition 做出開啟彈出視窗時的過場動畫。 要注意的缺點 不過,原生 Popover API 有個明顯的缺點:當你點擊 ::backdrop 關閉 popover 時,這個點擊事件會「穿透」到底層元素,導致下方的按鈕或其他元素也意外被觸發。
圖片
很久以前看到有人用 CSS 的 padding-box 與 border-box 涵蓋範圍的不同做出 border,現在讓我們一起來運用這個技巧。 padding-box padding-box 指定這個背景只應用於元素的內邊距(padding)。也就是說如果元素有內邊距,這個顏色將填充內邊距;如果沒有,它可能不會顯示任何可見的效果,或者被下層的 background 覆蓋。實際也是如此,因為 0 0 表示漸層的起點和終點都在同一位置,所以它只是一個單一的顏色。 簡單來說,linear-gradient(var(--bg-color) 0 0) padding-box 是用來蓋掉內部顏色的。 border-box border-box指定這個漸層只應用於元素的包含邊框(border)的區域,但因為內部被padding-box那段原始碼蓋掉了,所以只會看到邊框的部分。 最後,加上 border dashed 把 border-box 那一段漸層蓋過去就大功告成了。 See the Pen Untitled by cj ( @cjzopen ) on CodePen . 另外,你想做一個齒輪的話,把用來蓋掉顏色的padding-box那部分拿掉,就會變成齒輪了。 用途 除了可以裝飾一些資訊物件之外,最常用的地方就是按鈕。 但我想要實線邊框 前面也提到最後一步是用 border 的顏色蓋掉漸層,所以只要把 border 的顏色設定成 transparent 就可以了。
圖片
color-mix() 讓開發者不用再開其它程式,直接在 CSS 中混合兩種顏色。明明是在寫原始碼,但卻有成為調色大師的感覺。 語法說明 color-mix() 的參數有這些: color-mix(in [color space], [顏色1] [比例1], [顏色2] [比例2]) 。 color space:包含 srgb、srgb-linear、display-p3、prophoto-rgb、lab、oklab、xyz、xyz-d50、xyz-d65、hsl、hwb、lch、oklch…等,若使用的是hsl、hwb、lch、oklch這些非線性空間的時候,還會多一個「路徑」參數(shorter hue、longer hue、increasing hue、decreasing hue)。 顏色1、顏色2:兩種要混合的顏色,當然,你也可以在這裡使用 color-mix()。 比例:指定各顏色所佔的比例,通常以 % 表示。 如果只提供一個顏色的比例,則另一個顏色會自動補足 100%;若兩個比例都沒寫,則會都是 50%。 若兩個比例相加超過 100%,則 最終顏色1比例= 顏色1 / 顏色1+顏色2 , 最終顏色2比例= 顏色2 / 顏色1+顏色2 。 兩個顏色的比例相加小於 100% 的話,最終顏色還要與(100% – sum)的透明色混合。沒錯,可以混出透明效果。 color-mix(in srgb, red, blue); /* 等於 color-mix(in srgb, red 50%, blue 50%) */ color-mix(in srgb, red 40%, blue); /* blue 的比例會自動視為 60% */ color-mix(in oklch shorter hue, transparent, blue 80%); /* 路徑沒寫的話,預設為 shorter hue */ color-mix(in oklch, red, color-mix(in srgb, yellow, red) 60%); /* 在 color-mix() 裡面塞 color-mix() */ 但要注意,color-mix() 裡面不能使用 calc,兩個顏色的比例也不能都是 0%。 另外,也可以自訂 color space...
圖片
dark mode 的CSS寫法一直以來都不是很方便,在偵測使用者作業系統是不是dark mode的時候,我們可以用prefers-color-scheme來另外寫一套CSS。現在多了另一種寫法 light-dark() 。 prefers-color-scheme prefers-color-scheme寫法如下,當然還有很多東西要考慮,例如border、hightlight…等等,但為了方便說明,只用文字和背景顏色舉例。 :root { --text-color: #333; --background-color: #eee; } @media (prefers-color-scheme: dark) { :root { --text-color: #efefef; --background-color: #222; } } 新的語法 light-dark() light-dark()可以搭配color-scheme使用達到自動偵測使用者作業系統是不是dark mode: :root { color-scheme: light dark; --text-color: light-dark(#333, #efefef); --background-color: light-dark(#eee, #222); } 除此之外,還可以用input讓使用者自己選擇要不要用dark mode: :root { /* 偵測使用者作業系統 */ &:has(input[name="scheme"][value="light dark"]:checked) { color-scheme: light dark; } /* 不用 dark mode */ &:has(input[name="scheme"][value="light"]:checked) { color-scheme: light; } /* 使用 dark mode */ &:has(input[name="scheme"][value="dark"]:...
圖片
我們只要用很古老以前就存在的 border-image-source 和比較新的漸層語法 conic-gradient 互相結合就可以辦到。 簡單的漸層邊框範例 #example-card{ width: 200px; height: 200px; border: 20px solid hsl(80 100% 50%); border-image-slice: 1; border-image-source: conic-gradient( from 0deg, hsl(80 100% 50%), hsl(200 100% 60%), hsl(80 100% 50%) ); } EXAMPLE 因為 conic-gradient 的關係,這裡只要注意不要用在 IE 和舊 edge 就可以了。如果還是很擔心,其實用 linear-gradient 也可以。 邊框圓角 大家都知道,漸層屬性的CSS是寫在「xxx-image」底下(這裡是 border-image),而此屬性的 border-radius 會失效。因此,若要實現圓角就必須使用其它方法實現,這裡我們用擁有裁切功能的 clip-path。 clip-path: inset(0 round 24px); inset 的意思是內部裁切,0 是貼合主體,這樣就能簡單實現外邊框圓角。
圖片
你是否有想過,max/min font size 的解法?能不能單靠 CSS 設計出會自動調整大小的 Responsive Font Size?答案是可以的! 文章目錄: vw 基本運用 極限問題 max()、min()、clamp() Container Queries與cqw/cqi 為何使用 vw? 在編寫 CSS 的時候,你可能習慣將網頁上的字體大小(font-size)使用 rem、em、px 等單位設定,但在講究 RWD 的情況下,甚至是 Google SEO 著重在手機介面的現在,總是會遇到桌機看起來很舒適,但畫面一變小,標題類的字就又太佔版面。 而使用 viewport width (vw) 當作字體大小的單位,就可以讓字體隨著視窗寬度的不同自動縮放其大小。 各瀏覽器支援 vw 的情況 vw 基本運用: vw 是視窗寬度的百分比,所以 1vw = 1%的視窗寬度。舉例來說,當視窗寬度為 1000px 且 font-size: 1.5vw 的時候,字體的大小就是 1000*1.5% = 15px。 h1,.h1{ font-size: 1.5vw; } 除了直接運用之外,也能搭配 calc 使用: h1,.h1{ font-size: calc(10px + 1.5vw); } 極限問題(max or min font size) 這是不管設計什麼東西都存在且必需要考慮的事情,vw 也不例外。 設計網頁一定會設一個網頁寬度,以此寬度設定做為最大值;而人的視力有限,以 16px 當作最小值。所以,當視窗被拉到極大或極小的時候,以 vw 為單位的字體勢必會變成你不想要的樣子,這時候我們就必需設定最大值和最小值,讓字體在合理的區間變動,而 media query 和 min()、max()就出現在解決辦法的清單之中: 以前的做法:Media Query 下面為使用的例子: h1,.h1{ font-size: 28px; } @media (min-width: 1600px) { h1,.h1{ font-size: 40px; } } @media (max-width: 480px) { h1,.h1{ font-size: 16px; } } ...
圖片
在一般情況下,往下滑動(往上撥)的時候,網址列和底部選單會收起消失,反之往上滑動則會出現。 overflow hidden 但在某些情況下,網頁設計師會將 html 和 body tag 的 CSS 設定成 overflow:hidden。 這時候網址列和底部選單不管怎麼滑動都不會消失,造成部分內容被擋住看不到也點不到,記得要多留一些空白空間。
圖片
在 html 下 scroll-behavior: smooth 的好處如同它的語意,若使用者點擊頁面的描點超連結時,不會讓使用者「瞬移」,而是會產生如同在 JavaScript 寫 scroll animation 一樣的體驗,對使用者的操作相對友善。但當使用者在進入頁面時,網址就帶有描點(#),可能會事得其反,破壞使用者體驗。 CSS 的載入方式 如果是傳統的直接載入或內嵌方法就可以跳出不用看了,但若是在 load 之後才用 JavaScript 載入,就有可能造成初始畫面位置並非描點的位置。比如出現下列這種情況: <link rel="preload" href="style.css" as="style"> <script> window.addEventListener("load", function() { var prload_css = document.querySelectorAll('link[as="style"]'); var i = 0; for (i; i < prload_css.length; ++i) { prload_css[i].rel='stylesheet'; } }); </script> 網址可能帶有 #title,預期讓使用者一開始看到 #title 元素在畫面之中,但 load 前後 #title 的位置不一樣,產生進入頁面且載入後,畫面往下滑動到比預期更下面的地方,讓 scroll-behavior: smooth 變成使用者體驗的殺手。 accesskey 操作 這個影響不大,只是當使用者鍵盤操作的時候,畫面會有一瞬間滑向該連結的效果出現。
在 parent 為 flex 的情況下,裡面放了許多圖片(img),當視窗寬度小於這些圖片時,子層的圖片高度不會因為你下了 height:auto 而改變,造成圖片被拉長。 為什麼圖片會被拉長? 因為 safari 預設的 flex 狀態和別人不太一樣,它的預設狀況為 stretch。 因此,parent 再加上 align-items:flex-start 就能解決這個只有 safari 版型會出狀況問題。 .d-flex{ display:flex; flex-wrap:wrap; } img{ width:25%; height:auto; } <div class="d-flex"> <img width="200" height="200" src="" alt=""> <img width="200" height="200" src="" alt=""> <img width="200" height="200" src="" alt=""> <img width="200" height="200" src="" alt=""> <img width="200" height="200" src="" alt=""> <img width="200" height="200" src="" alt=""> <img width="200" height="200" src="" alt=""> <img width=...
圖片
chrome80, firefox75……以上,基本上各大知名的瀏覽器都可以支援。而雖然說是 3 種語法,但其實 clamp 可以被其它兩者取代。 Sass / SCSS 它的開發環境目前還不支援(2020-06),所以若要用 Sass / SCSS 開發的話目前可以寫成: font-size: m#{i}n(m#{a}x(9vw,2.5rem),5rem); For Exmaple 這些語法可以用在 width、 font-size 、甚至是 grid 的 repeat()……等等的地方,例如: .example{ font-size:16px; /* if not support */ font-size: clamp(min, default, max); font-size: max(default, min); width: min(99.2px * 10, 100%); left: min(30%, calc(100% - 220px)); font-size: max(value1, value2, min); /* value1 and value2 are different unit. */ /* clamp() is not a necessity */ font-size: max(min(0.5vw, 0.5em), 1rem); } English bold 中文粗體 See the Pen CSS clamp()、max()、min() by cj ( @cjzopen ) on CodePen .
行動裝置預設阻擋 icon font 勢必會成為趨勢,除非你也有處理未載入時的畫面呈現,或有沒有icon對使用者的 UX 都沒有影響(這裡要捫心自問,既然有沒有 icon 都可以,為何還要放icon),否則只會有更多 browser 跟進如 firefox focus 這樣的瀏覽器或套件出現,就跟使用者主動安裝 adblock 是相同的道理。 直接說結論:直接用 SVG 好於用 JS 產生 SVG,而 JS 產生 SVG 又好於載入字體檔的 CSS。 Font Awesome 的升級 若你是使用 Font Awesome 且沒有時間處理這個問題,官方剛好有出一個快速解套的解決方案,讓開發者無痛從 4 升級到 5: Upgrading from Version 4 。 但這必需多載入 v4-shims.css 和 v4-shims.js ,對使用者而言就是多兩個 request,所以有時間的話,還是全面改寫成正常的第5版會比較好,也就是不需要用CSS載入字體檔,只有JavaScript的版本(用JS產生SVG)。 就算第4版現在大部分的情況都能正常顯示,但由於 CSS 檔案肥大(因為包含字體檔),加上沒有 font-display: swap; 的語法,通常都會拖慢載入速度。所以,放棄需要載入字體檔的icon,投入 svg 的懷抱吧! 造成排版位移 網站若有使用其它字體 (例如 Google font) 的人都知道,就算有 font-display:swap 解決載入前後文字閃爍的情況,還是無法解決載入前後字體位移的問題,因為每種字體的大小與定位都有些微的不同。這個影響雖然不大,Google 強調的 CLS 看似也不把它當作問題,但這個問題確實存在。 還能更好 而前面提到直接貼<svg>的方式,難道第5版還不夠好嗎? 在 chrome 的 Lightinghouse 測試中,有無載入第5版的 Font Awesome 速度分數最多可以差到20分,Time to Interactive (TTI)更可以相差到 8 秒之多,若又選用有載入字體的CSS,分數又會差更多。所以才又提了直接放<svg>的方法,既少了一個 request,又不用載入 Font Awesome,TTI 也從 12 秒降到 4 秒,但開發上會麻煩很多。...
在 IE 和 Edge 中使用 display:flex 的情況下, background-clip ( -webkit- background-clip ):text 不會有作用。 拿掉 flexbox 就又能正常顯示… 如下面 codepen 中的範例:  See the Pen background-clip by cj ( @cjzopen ) on CodePen . html,body{ margin:0; padding:0; } div{ background-image:url(https://picsum.photos/1920/600/?random); background-size:cover; height:100vh; /*if display flex, background-clip will not working in ie and edge17 */ /*display:flex;*/ /*justify-content:center;*/ /*align-items:center;*/ /* fixed */ line-height:100vh; text-align: center; font-size:15vw; font-weight:700; background-clip: text; -webkit-background-clip: text; color: rgba(0,0,0,.2); position:relative; /* mix-blend-mode: difference; */ } div::after{ content:''; position:absolute; z-index:-1; left:0; top:0; width:100%; height:100%; background-size:cover; /*style1*/ /* Not all images are suitable for this gradient. You can change it. */ background-image:linea...
圖片
在 iphone6 safari (版本更之前的iphone沒有實驗) 會出現以下情況才會出現,而 ipad 和其它瀏覽器順利地運作換行: flexbox 子層在 width:auto 的情況下,flex-wrap:wrap 會失效。 另外在子層 flex-grow:1 的情況下,在 IE 中,右邊的子層會直接蓋在左邊的子層上。 -- 解決: 子層 width:auto 拿掉,右邊改成 width:100%
用 box-shadow 實現 .text{ text-decoration: none; box-shadow:0 -1px 0 #2858aa inset; transition:box-shadow .2s ease-in-out; } .text:hover{ box-shadow:0 -1px 0 rgba(0,0,0,0) inset, 0 3px 0 #2858aa; transition:box-shadow .2s ease-in-out; } 結果如下 HOVER ME 用 psudo element 和 scaleX 實現 .text { text-decoration: none; position: relative; } .text::before{ content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background-color: #6ac7ef; transform-origin: bottom right; transform: scaleX(0); transition: transform .3s ease; } .text:hover::before{ transform-origin: bottom left; transform: scaleX(1); } 效果如下 HOVER ME 用 linear-gradient 實現 .text { text-decoration: none; background-image: linear-gradient(#333, #333); background-position: 100% 100%; background-repeat: no-repeat; background-size: 0 2px; transition: background-size .3s; } .text:hover{ ...