1. 程式人生 > >css 揭秘-讀書筆記

css 揭秘-讀書筆記

let 組合 team cancel 詳細 適應 數據庫 響應式 clas

css 揭秘
[希]Lea verou 著
css 魔法 譯

該書涵蓋7大主題,47個css技巧,是css進階必備書籍,開闊思路,探尋更優雅的解決方案。這本書完全用css渲染出的html寫成的(布局、圖片、顏色、頁碼、章節號等)。

涵蓋7大主題

  • 背景與邊框
  • 形狀
  • 視覺效果
  • 字體排印
  • 用戶體驗
  • 結構與布局
  • 過渡與動畫

資料

  • 書籍註解
  • Lea verou博客
  • Lea verou github
  • O‘Reilly的HTMLBook標準
  • Atlas 將css用於書籍排印
  • Dabblet 在線演示
  • 本書在Espresso編輯器寫成
  • csssecrets all demos

瀏覽器支持與回退機制

  1. 提供及時有效的瀏覽器兼容信息的網站:
    • Can i use...?
    • WebPlatform.org
    • Mozilla Develope Network
  2. 瀏覽器前綴書寫:應該把標準語法排在最後,通過層疊機制確保哪條聲明最終生效。

    background: rgb(255, 128, 0); /* 回退機制 */
    background: -moz-linear-gradient(90deg, yellow, red); /* 火狐 */
    background: -o-linear-gradient(90deg, yellow, red); /* Opera */
    background: -webkit-linear-gradient(90deg, yellow, red); /* Safari、Chrome*/
    background: linear-gradient(0deg, yellow, red);
    tip: 瀏覽器前綴生成工具:
    • Autoprefixer:根據 Can i use 數據庫判斷添加前綴,本地編譯。
    • 作者開發的-prefix-free:在瀏覽器中進行特性檢測,不需要更新,在真實環境中跑出來的結果。
  3. 使用Modernizr工具給根元素()添加輔助類,比如 textshadowno-textshadow針對支持或不支持某些特性的瀏覽器來分別編寫樣式

    h1 { color: gray; }
    
    .textshadow h1 {
      color: transparent;
      text-shadow: 0 0 .3em gray;
    }
  4. 嘗試的 css 特性非常新,可以試試 @supports 規則實現回退,將其視作瀏覽器“原生”的 Modernizr。

    h1 { color: gray; }
    
    @supports (text-shadow: 0 0 .3em gray) {
      h1 {
        color: transparent;
        text-shadow: 0 0 .3em gray;
      }
    }

    tip: 慎用 @supports, 存在兼容性

  5. js 實現:做一些特性檢測然後給根元素加一些輔助類。如果要檢測某個樣式屬性是否被支持,核心思路就是在任一元素的 element.style 對象上檢查該屬性是否存在。

    var root = document.documentElement; // <html>
    if ('textShadow' in root.style) {
      root.classList.add('textshadow');
    } else {
      root.classList.add('no-textshadow');
    }
    
    // 多個
    function testProperty (property) {
      var root = document.documentElement; // <html>
      if (property in root.style) {
        root.classList.add(property.toLowerCase());
        return true;
      }
      root.classList.add('no-' + property.toLowerCase());
      return false;
    }

    檢查某個具體的屬性值是否支持,需要把它賦給對應的屬性,然後再檢查瀏覽器是否還保存著這個值。

    var dummy = document.createElement('p');
    dummy.style.backgroundImage = 'linear-gradient(red, tan)';
    
    if (dummy.style.backgroundImage) {
      root.classList.add('lineargradients');
    } else {
      root.classList.add('no-lineargradients');
    }
    
    // 多個
    function testValue(id, value, property) {
      var dummy = document.createElement('p');
      dummy.style[property] = value;
    
      if (dummy.style[property]) {
        root.classList.add(id);
        return true;
      }
      root.classList.add('no-' + id);
      return false;
    }

關於 web 標準

標準的每項規範從最初啟動到最終成熟,必經階段:

  1. 編輯草案(ED)
  2. 首個公開工作草案(FPWD)
  3. 工作草案(WD)
  4. 候選推薦規範(CR)
  5. 提名推薦規範(PR)
  6. 正式推薦規範(REC)
    可以認為到第5步就已經是趨於成熟了,到最後階段只是時間問題。

關於css3、css4及其他傳說

  • css2 之後,css變得異常龐大,全量更新css版本已不再可能
  • css3 是一個全新的版本,將css模塊化,以後針對模塊進行版本更新,比如
    • css 語法(http://w3.org/TR/css-syntax-3)
    • css 層疊與繼承(http://w3.org/TR/css-cascade-3)
  • 全新的模塊版本號從1開始:
    • css 變形(http://w3.org/TR/css-transforms-1)
    • 圖像混合效果(http://w3.org/TR/compositing-1)
  • 無css4

tip:為什麽不會有CSS4了?

css 編碼技巧

  • DRY:Don‘t Repeat Yourself。盡量減少改動時要編輯的地方
  • 可維護性、可擴展。

舉例:

  • 當某些值相互依賴時,應當把它們的相互關系用代碼表達出來。
font-size: 20px;
line-height: 1.5; // 等同於 line-height: 30px;
  • 單位:px 改 em、rem,同步放大、縮小
  • 顏色:只要把半透明的黑色或白色疊加在主色調上,即可產生主色調的亮色和暗色變體
button {
  padding: .3em .8em;
  border: 1px solid rgba(0, 0, 0, .1);
  background: #58a linear-gradient(hsla(0, 0%, 100%, .2), transparent);
  border-radius: .2em;
  box-shadow: 0 .05em .25em rgba(0, 0, 0, .5);
  color: white;
  text-shadow: 0 -.05em .05em rgba(0, 0, 0, .5);
  font-size: 125%;
  line-height: 1.5;
}

button.cancel {
  background-color: #c00;
}
button.ok {
  background-color: #6b0;
}
  • 代碼易維護
border-width: 10px;
border-left-width: 0;
  • currentColor,具體可看文章currentColor-CSS3超高校級好用CSS變量
  • 繼承:inherit 可以用在任何css屬性中,而且它總是綁定到父元素的計算值(對偽元素lais來說,則會取生成該偽元素的宿主元素)。
// 舉例,在創建提示框的時候,小箭頭能夠自動繼承背景和邊框的樣式
.callout {
  position: relative;
}
.callout::before {
  content: '';
  position: absolute;
  top: -.4em; left: 1em;
  padding: .35em;
  background: inherit;
  border: inherit;
  border-right: 0;
  border-bottom: 0;
  transform: rotate(45deg);
}

技術分享圖片

  • 相信你的眼睛,而不是數字:視覺錯誤,有針對性的調整
    • 眼睛在看到一個完美垂直居中的物體時,會感覺它並不居中。需要把這個物體從幾何學的中心點再稍微往上挪一點
    • 圓形的字形(比如0)需要比矩形字形稍微放大一些。圓形占據的寬高和矩形一致,但圓形看著要小一些,因為我們傾向於把圓形感知得比其實際尺寸更小一些。
    • 文本容器設置內邊距,文字:yolo,四邊指定相同,但實際看起來上下空得多。需要減少頂部、底部的內邊距。原因在於:字母的形狀在兩端都比較整齊,而頂部和底部則往往參差不齊。
  • 響應式網頁設計(靈活有彈性):媒體查詢是最後的手段。盡最大努力實現彈性可伸縮的布局,並在媒體查詢的各個斷點區間內指定相應的尺寸。
    • 使用百分比單位,或其他相對單位,比如 vw、vh、vmin、vmax,而非絕對單位px
    • 當需要在較大分辨率下得到固定寬度時,使用max-width而不是width。因為能適應較小的分辨率
    • 不要忘記偽替換元素(比如img、object、video、iframe等)設置一個max-width,值為100%
    • 假如背景圖片需要完整地鋪滿一個容器,不管容器的尺寸如何變化,請設置 background-size:cover.
    • 在使用多列文本時,指定 column-width(列寬)而不是指定 column-count(列數),這樣它就可以在較小的屏幕上自動顯示為單列布局。
  • 合理使用簡寫
    • 比如:background: url(tr.png) no-repeat rop right / 2em em;
    • 怪異的簡寫語法:即使是初始值也要寫出來,使用/作為分隔。這通常是為了消除歧義。在上面的例子中,top right 顯然是 background-position,而 2em 2em 是 background-size。但是,設想一下 50% 50%,解析器就無法解析了。
  • 合理使用預處理器
    • 使用原生特性,比預處理器提供的版本強大的多
      • css 自定義變量
      ul {--accent-color: purple;}
      ol {--accent-color: rebeccapurple;}
      li {background: var(--accent-color);}
      • css 值與單位:calc() 函數
      • css 顏色:color() 函數
      • css 嵌套:正在討論
    • 合理使用預處理器,讓代碼靈活

背景與邊框

  1. 半透明邊框(translucent-borders)
border: 10px solid hsla(0, 0%, 100%, .5);
background: white;
background-clip: padding-box;

技術分享圖片

  1. 多重邊框(multiple-borders)

box-shadow 方案

background: yellowgreen;
box-shadow: 0 0 0 10px #655,
            0 0 0 15px deeppink,
            0 2px 5px 15px rgba(0, 0, 0, .6);

技術分享圖片

註意事項:不影響布局,不受 box-sizing 屬性影響,outset的區域不響應鼠標事件,改為inset。

outline 方案

background: #655;
outline: 1px dashed #fff;
outline-offset: -10px;

技術分享圖片

不貼合border-radius

  1. 靈活的背景定位

background-position 方案

background: url(http://csssecrets.io/images/code-pirate.svg)
          no-repeat bottom right #58a;
background-position: right 20px bottom 10px;

技術分享圖片

background-origin 方案

background: url(http://csssecrets.io/images/code-pirate.svg)
        no-repeat bottom right #58a;
background-origin: content-box;

background-position是以padding box為準的

calc() 方案

background: url(http://csssecrets.io/images/code-pirate.svg)
          no-repeat bottom right #58a;
background-position: calc(100% - 20px) calc(100% - 10px);
  1. 邊框內圓角
background: tan;
border-radius: .8em;
padding: 1em;
box-shadow: 0 0 0 .6em #655;
outline: .6em solid #655;

技術分享圖片

勾股定理:a*a + b*b = c*c

擴張半徑需要比描邊的寬度值小,但同時要比 (Math.sqrt(2) - 1) * r (根號2 - 1)r 大,r表示border-radius

  1. 條紋背景

橫向

background: linear-gradient(#fb3 50%, #58a 0);
background-size: 100% 30px;

垂直條紋

background: linear-gradient(to right, #fb3 50%, #58a 0);
background-size: 30px 100%;

斜向條紋

background: linear-gradient(45deg, 
                #fb3 25%, #58a 0, #58a 50%,
                #fb3 0, #fb3 75%, #58a 0);
background-size: 42.4px 42.4px;

靈活的同色系條紋

background: #58a;
background-image: repeating-linear-gradient(30deg, 
        hsla(0,0%,100%,.1), hsla(0,0%,100%,.1) 15px,
        transparent 0, transparent 30px);

技術分享圖片

  1. 復雜背景

網格

background: #58a;
background-image: linear-gradient(white 2px, transparent 0),
                linear-gradient(90deg, white 2px, transparent 0),
                linear-gradient(hsla(0,0%,100%,.3) 1px, transparent 0),
                linear-gradient(90deg, hsla(0,0%,100%,.3) 1px, transparent 0);
background-size: 50px 50px, 50px 50px,
              10px 10px, 10px 10px;

波點: 兩層,第二層背景偏移定位置必須是貼片寬高的一半

background: #655;
background-image: radial-gradient(tan 20%, transparent 0),
                  radial-gradient(tan 20%, transparent 0);
background-size: 30px 30px;
background-position: 0 0, 15px 15px;

scss mixin

// scss mixin
@mixin polka($size, $dot, $base, $accent) {
    background: $base;
    background-image: radial-gradient($accent $dot, transparent 0),
                      radial-gradient($accent $dot, transparent 0);
    background-size: $size $size;
    background-position: 0 0, $size/2 $size/2;
}
// 調用
@include polka(30px, 30%, #655, tan);

棋盤: 用兩個直角三角形來拼合出我們想要的方塊,把第二層漸變在水平和垂直方向均移動貼片長度的一半

.checkerboard {
    background: #eee;
    background-image: 
      linear-gradient(45deg, rgba(0,0,0,.25) 25%, transparent 0, transparent 75%, rgba(0,0,0,.25) 0),
      linear-gradient(45deg, rgba(0,0,0,.25) 25%, transparent 0, transparent 75%, rgba(0,0,0,.25) 0);
    background-position: 0 0, 15px 15px;
    background-size: 30px 30px;
}
// scss mixin
@mixin checkerboard($size, $base, $accent: rgba(0, 0, 0, .25)) {
    background: $base;
    background-image: linear-gradient(45deg, $accent 25%, transparent 0, transparent 75%, $accent 0),
                      linear-gradient(45deg, $accent 25%, transparent 0, transparent 75%, $accent 0);
    background-size: 2*$size 2*$size;
    background-position: 0 0, $size $size;                  
}
// 調用
@include checkerboard(15px, #58a, tan);

棋盤-svg

background: #eee url('data:image/svg+xml,
              <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill-opacity=".25" >              <rect x="50" width="50" height="50" />              <rect y="50" width="50" height="50" />              </svg>');
background-size: 30px 30px;

棋盤-圓錐漸變實現:存在兼容性

background: repeating-conic-gradient(#bbb 0, #bbb 25%, #eee 0, #eee 50%);
background-size: 30px 30px;

調色盤: 圓錐漸變

border-radius: 50%;
background: conic-gradient(red, yellow, lime, aqua, blue, fuchsia, red);

技術分享圖片

  1. 連續的圖像邊框

技術分享圖片

技巧:背景邊框:在石雕背景圖片之上,再疊加一層純白的實色背景

padding: 1em;
border: 1em solid transparent;
background: linear-gradient(white, white) padding-box,
            url(http://csssecrets.io/images/stone-art.jpg) border-box  0 / cover;

width: 21em;
overflow: hidden;
resize: both;

信封

padding: 1em;
border: 16px solid transparent;
border-image: 16 repeating-linear-gradient(-45deg, red 0, red 1em, transparent 0, transparent 2em,
              #58a 0, #58a 3em, transparent 0, transparent 4em);

行軍的螞蟻

.ants {
    padding: 1em;
    border: 1px solid transparent;
    background: linear-gradient(white, white) padding-box,
                repeating-linear-gradient(-45deg, black 0, black 25%, transparent 0, transparent 50%) 0 / .6em .6em;
    animation: ants 12s linear infinite;
    
    max-width: 20em;
    font: 100%/1.6 Baskerville, Palatino, serif;
}
@keyframes ants { to { background-position: 100% 100% } }

border-image原理:九宮格伸縮發:把圖片切割成九塊,然後把它們應用到元素邊框相應的邊和角。

原理體驗:border-image

腳註

border-top: .15em solid transparent;
border-image: 100% 0 0 linear-gradient(90deg, currentColor 4em, transparent 0);
padding-top: .5em;

技術分享圖片

由於後面特別多,就不一一列舉了展示圖片了,感興趣的去看源碼及效果吧。

形狀

  1. 自適應的橢圓:ellipse
border-radius: 50%;

若是寬高一致,就顯示為一個圓,寬高不等,就顯示一個橢圓。

若border-radius值為寬高小的一邊,就表現為小的邊為半圓,長的邊為直線。

可單獨制定水平和垂直半徑:用 / 分隔值即可。border-radius: 100px / 75px

  1. 半橢圓:half-ellipse
border-radius: 50% / 100% 100% 0 0;
  • 垂直堆成,左上角和右上角半徑相同,且半徑之和等於整個形狀的寬度。
  • 頂部的兩個圓角占據了整個元素的高度,底部沒有任何圓角。
  1. 四分之一橢圓:quarter-ellipse
border-radius: 100% 0 0 0;
  1. 平行四邊形:主要利用 transform:skew()變形:parallelograms

嵌套方案:外層skew變形,但內容變形了,對內容再應用一次反向的 skew() 變形,從而抵消容器的變形。

<a class="button"><div>click me</div></a>

.button { transform: skewX(-45deg); }
.button > div { transform: skewX(45deg); }

偽元素方案:parallelograms-pseudo

  1. 菱形圖片

嵌套變形方案:diamond-images

<div class="pic">
  <img src=""adam-catlace.jpg alt="..."/>
</div>

.pic {
    width: 400px;
    transform: rotate(45deg);
    overflow: hidden;
}
.pic > img {
    max-width: 100%;
    transform: rotate(-45deg) scale(1.42);
}

裁切路徑方案:diamond-clip

.img {
    clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
    transition: 1s clip-path;
}
.img:hover {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
  1. 切角效果

一個角

background: #58a;
background: linear-gradient(-45deg, transparent 15px, #58a 0);

二個角

background: #58a;
background: linear-gradient(-45deg, transparent 15px, #655 0) right,
            linear-gradient(45deg, transparent 15px, #58a 0) left;
background-size: 50% 100%;
background-repeat: no-repeat;

四個角: bevel-corners-gradients

background: #58a;
background: linear-gradient(135deg, transparent 15px, #58a 0) top left,
            linear-gradient(-135deg, transparent 15px, #58a 0) top right,
            linear-gradient(-45deg, transparent 15px, #58a 0) bottom right,
            linear-gradient(45deg, transparent 15px, #58a 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;

// scss mixin
@mixin beveled-corners ($bg, $tl:0, $tr:$tl, $br:$tl, $bl:$tr) {
    background: $bg;
    background: linear-gradient(135deg, transparent $tl, $bg 0) top left,
                linear-gradient(225deg, transparent $tr, $bg 0) top right,
                linear-gradient(-45deg, transparent $br, $bg 0) bottom right,
                linear-gradient(45deg, transparent $bl, $bg 0) bottom left;
    background-size: 50% 50%;
    background-repeat: no-repeat;
}
// 調用
@include beveled-corners(#58a, 15px, 5px);

弧形切角(內凹圓角):用徑向漸變替代上述線性漸變。scoop-corners

background: #58a;
background: radial-gradient(circle at top left, transparent 15px, #58a 0) top left,
            radial-gradient(circle at top right, transparent 15px, #58a 0) top right,
            radial-gradient(circle at bottom right, transparent 15px, #58a 0) bottom right,
            radial-gradient(circle at bottom left, transparent 15px, #58a 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;

// scss mixin
@mixin scoop-corners($bg, $tl:0, $tr:$tl, $br:&tl, $bl:$tr) {
    background: $bg;
    background: radial-gradient(circle at top left, transparent $tl, #58a 0) top left,
                radial-gradient(circle at top right, transparent $tr, #58a 0) top right,
                radial-gradient(circle at bottom right, transparent $br, #58a 0) bottom right,
                radial-gradient(circle at bottom left, transparent $bl, #58a 0) bottom left;
    background-size: 50% 50%;
    background-repeat: no-repeat;  
}
// 調用
@include scoop-corners();

內聯 svg 與 border-image 方案:bevel-corners

border: 20px solid #58a;
border-image: 1 url('data:image/svg+xml,              <svg xmlns="http://www.w3.org/2000/svg"              width="3" height="3" fill="%2358a">              <polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>              </svg>');
background: #58a;
background-clip: padding-box;

裁切路徑方案:好處:可使用任意類型的背景,比如圖片。bevel-corners-clipped

background: #58a;
clip-path: polygon(
            20px 0, calc(100% - 20px) 0, 100% 20px,
            100% calc(100% - 20px), calc(100% - 20px) 100%,
            20px 100%, 0 calc(100% - 20px), 0 20px);
  1. 梯形標簽頁

利用 3D 旋轉模擬實現:trapezoid-tabs

nav > a {
    position: relative;
    display: inline-block;
    padding: .3em 1em 0;
}
nav > a::before {
    content: ''; /* 用偽元素來生成一個矩形 */
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    z-index: -1;
    background: #ccc;
    transform: perspective(.5em) rotateX(5deg);
    transform-origin: bottom;
    
    background-image: linear-gradient(hsla(0, 0%, 100%, .6), hsla(0, 0%, 100%, 0));
    border: 1px solid rgba(0, 0, 0, .4);
    border-bottom: none;
    box-shadow: 0 .15em white inset;
}

對元素使用了 3D 變形之後,其內部的變形效應是“不可逆轉”的。但 2D 變形,內部是可抵消外部的變形效應。

該方案優點:能添加背景、邊框、圓角、投影燈,並且,只需要把 transform-origin 改成 bottom left 或 bottom right,就可以立即得到左側傾斜或右側傾斜的標簽頁。

  1. 簡單的餅圖

基於 transform 的解決方案:pie-animated

該方案結構最簡:只需一個元素作為容器,其他部分由偽元素、變形屬性和css漸變實現。

@keyframes spin {
    to { transform: rotate(.5turn); }
}
@keyframes bg {
    50% { background: #655; }
}
.pie {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background: yellowgreen;
}
.pie::before {
    content: '';
    display: block;
    margin-left: 50%;
    height: 100%;
    border-radius: 0 100% 100% 0 / 50%;
    background-color: inherit;
    transfrom-origin: left;
    animation: spin 3s linear infinite,
               bg 6s step-end infinite;
}

制作多個不同比率的靜態餅圖:pie-static

<div class="pie">20%</div>
<div class="pie">60%</div>

@keyframes spin {
    to { transform: rotate(.5turn); }
}
@keyframes bg {
    50% { background: #655; }
}
.pie {
    position: relative;
    width: 100px;
    line-height: 100px; // line-height 本身就可以起到設置高度的作用
    border-radius: 50%;
    background: yellowgreen;
    background-image: linear-gradient(to right, transparent 50%, #655 0);
    color: transparent;
    text-align: cener;
}
.pie::before {
    content: '';
    position: absolute;
    top: 0; left: 0;
    width: 100%;
    height: 100%;
    border-radius: 0 100% 100% 0 / 50%;
    background-color: inherit;
    transfrom-origin: left;
    animation: spin 3s linear infinite,
               bg 6s step-end infinite;
    animation-play-state: paused;
    animation-delay: inherit;
}

document.querySelectorAll('.pie').forEach(function (pie) {
    var p = parseFloat(pie.textContent);
    pie.style.animationDelay = '-' + p + 's';
})

動畫暫停,用負的動畫延時(animation-delay: -20s)來直接跳至動畫中的任意時間點。

.pie 元素內聯樣式,偽元素繼承 inherit

svg 解決方案:pie-svg

<svg viewBox="0 0 32 32">
    <circle r="16" cx="16" cy="16" />
</svg>

// css
svg {
    width: 100px;
    height: 100px;
    transform: rotate(-90deg);
    background: yellowgreen;
    border-radius: 50%;
}
svg > circle {
    fill: yellowgreen;
    stroke: #655;
    stroke-width: 32;
    stroke-dasharray: 38 100; /* 可得到比率為38%的扇區 */
}

視覺效果

  1. 單側投影:shadow-one-side
box-shadow: 0 5px 4px -4px black;

box-shadow 第四個長度參數,擴張半徑,排在模糊半徑參數之後,這個參數會根據你指定的值去擴大或縮小(負值)投影的尺寸。應用一個負的擴張半徑,而它的值剛好等於模糊半徑,那麽投影的尺寸就會與投影所屬元素的尺寸完全一致。

  1. 鄰邊投影:shadow-2-sides
box-shadow: 3px 3px 6px -3px black;
  1. 雙側投影:shadow-opposite-sides
box-shadow: 5px 0 5px -5px black,
            -5px 0 5px -5px black;
  1. 不規則投影:drop-shadow
filter: drop-shadow(2px 2px 10px rgba(0, 0, 0, .5));

能與點狀、虛線、半透明邊框、切角、折角等不規則的形狀貼合。

可參考文章:CSS3 filter:drop-shadow濾鏡與box-shadow區別應用

  1. 染色效果

濾鏡方案:color-tint-filter

img {
    transition: .5s filter;
    filter: sepia(1) saturate(4) hue-rotate(295deg);
}
img:hover,
img:focus {
    filter: none;
}

sepia():給圖片增加一種降飽和度的橙黃色染色效果。幾乎所有像素的色相值會被收斂到35-40.

saturate():給每個像素提升飽和度

hue-rotate():把每個像素的色相以指定的度數進行偏移。

混合模式方案:color-tint

<a>
  <img src="tiger.jpg"/>
</a>

a { background: hsl(335, 100%, 50%); }
a > img { mix-blend-mode: luminosity; }

當兩個元素疊加時,“混合模式”控制了上層元素的顏色與下層顏色進行混合的方式。

luminosity:會保留上層元素的 hsl 亮度信息,並從它的下層吸取色相和飽和度信息。

染色:下層放主色調,上層設置 luminosity 混合模式。

mix-blend-mode:為整個元素設置混合模式

background-blend-mode:為每層背景單獨制定混合模式。

  1. 毛玻璃效果:frosted-glass
body, main::before {
    background: url('tigger.jpg') 0 / cover fixed;
}
main {
    position: relative;
    backgrond: hsla(0, 0%, 100%, .3);
    overflow: hidden;
}
main::before {
    content: '';
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    filter: blur(20px);
    margin: -30px;
}

由於不能直接對元素本身設置模糊效果,就對一個偽元素進行處理,然後將其定位宿主到元素的下層,它的背景將會無縫匹配 的背景。

模糊效果會削減實色像素所能覆蓋的範圍,削減的幅度正是模糊半徑的長度。因此讓偽元素相對宿主元素的尺寸向外誇大至少20px

  1. 折角效果

45° 折角的解決方案:folded-corner

background: #58a; /* 回退樣式 */
background: linear-gradient(to left bottom, transparent 50%, rgba(0, 0, 0, .4) 0) no-repeat 100% 0 / 2em 2em, linear-gradient(-135deg, transparent 1.5em, #58a 0);

其他角度解決方案:

folded-corner-realistic

folded-corner-mixin

.note {
    positon: relative;
    background: #58a;
    background: linear(-150deg, transparent 1.5em, #58a 0);
    border-radius: .5em;
}
.note::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    background: linear-gradient(to left bottom, transparent 50%, rgba(0, 0, 0, .2) 0, rgba(0, 0, 0, .4)) 100% no-repeat;
    width: 1.73em;
    height: 3em;
    transform: translateY(-1.3em) rotate(-30deg);
    transform-origin: bottom right;
    border-bottom-left-radius: inheirt;
    box-shadow: -.2em .2em .3em -.1em rgba(0, 0, 0, .15);
}

// scss mixin
@mixin folded-corner($bg, $size, $angle:30deg) {
    positon: relative;
    background: $bg;
    background: linear($angle - 180deg, transparent $size, $bg 0);
    border-radius: .5em;
    
    $x: $size / sin($angle);
    $y: $size / cos($angle);
    
    &::before {
        content: '';
        position: absolute;
        top: 0;
        right: 0;
        background: linear-gradient(to left bottom, transparent 50%, rgba(0, 0, 0, .2) 0, rgba(0, 0, 0, .4)) 100% no-repeat;
        width: $y;
        height: $x;
        transform: translateY($y - $x) rotate(2*$angle - 90deg);
        transform-origin: bottom right;
        border-bottom-left-radius: inheirt;
        box-shadow: -.2em .2em .3em -.1em rgba(0, 0, 0, .15);
    }
}

// 調用
.note {
    @include folded-corner(#58a, 2em, 40deg);
}

字體排印

  1. 連字符斷行:hyphenation
text-align: justify;
hyphens: auto;

兩端對齊,英文,有些單詞之間的間隔太大,損傷了可讀性。實際中,兩端對齊總是與連字符斷行相輔相成。

文本折行算法:主要是 貪婪算法 和 Knuth-Plass 算法。

貪婪算法:每次分析一行,把盡可能多的單詞填充改行,當遇到第一個裝不下的單詞或音節時,就移至下一行繼續處理。

Knuth-Plass 算法:高級很多,把整段文本納入考慮範圍,從而產生出美學上更令人愉悅的效果。但計算性能稍差。

絕大多數桌面文字處理程序采用 Knuth-Plass 算法,處於性能考慮,瀏覽器采用 貪婪算法。

  1. 插入換行:line-breaks
<dl>
  <dt>Name:</dt>
  <dd>Lea Verou</dd>

  <dt>Email:</dt>
  <dd>[email protected]</dd>
  <dd>[email protected]</dd>

  <dt>Location:</dt>
  <dd>Earth</dd>
</dl>

dt,dd 塊級元素。

在每個 dd 後面添加一個 br 換行,糟糕的結構。使用生成性內容來添加換行,並以此取代br。

有一個 Unicode 字符專門代表換行:0x000A,在 css 中這個字符可以寫作 "\000A",或簡化為"\A"

在 html 直接輸入換行符,會與響鈴的其他空白符合並。保留空白和換行,white-space: pre

dt, dd { display: inline; }
dd {
    margin: 0;
    font-weight: bold;
}
dd + dt::before {
    content: '\A';
    white-space: pre;
}
dd + dd::before {
    content: ',';
    margin-left: -.2em;
    font-weight: normal;
}
  1. 文本行的斑馬條紋:zebra-lines

表格

tr: nth-child(even) {
    background: rgba(0, 0, 0, .2);
}

文本:漸變背景條紋

padding: .5em;
line-height: 1.5;
background: beige;
background: auto 3em;
background-origin: content-box;
background-image: linear-gradient(rgba(0, 0, 0, .2) 50%, transparent 0);

background-size 設置為 line-height 的兩倍,每個背景貼片需要覆蓋兩行代碼。

  1. 調整 tab 的寬度:tab-size

使用 pre 和 code 顯示代碼,tab 縮進代碼,瀏覽器會把其寬度顯示為 8 個字符。

pre { tab-size: 2; }
  1. 連字:ligatures

大多數襯線字體中的 f 和 i ,i 的圓點往往會與 f 的升部發生沖突,導致兩者顯示不清。為了緩解這種情況,出現連字。設計成 雙字形或三字形的單一組合體。

font-variant-ligatures:common-ligatures
                        discretionary-ligatures
                        historical-ligatures;

開啟通用連字,關閉酌情連字

font-variant-ligatures: common-ligatures
                        no-discretionary-ligatures
                        no-historical-ligatures;
  1. 華麗的 & 符號:ampersands

  2. 自定義下劃線:

underlines

wavy-underlines

background: linear-gradient(gray, gray) no-repeat;
background-size: 100% 1px;
background-origin: 0 1.15em;
text-shadow: .05em 0 white, -.05em 0 white;
  1. 現實中的文字效果

凸版印刷效果:letterpress

background: hsl(210, 13%, 40%);
color: hsl(210, 13%, 75%);
text-shadow: 0 -1px 1px black;

原理:出現在底部的淺色投影或出現在頂部的暗色投影,會讓人產生物體時凹進平面內的錯覺;同理,出現在底部的暗色投影或出現在頂部的淺色投影,會讓人產生物體從平面上凸起的錯覺。

形成錯覺的原因:現實世界中光源總是懸在頭頂。

空心字效果:stroked-text

background: deeppink;
color: white;
text-shadow: 1px 1px black, -1px -1px black,
             1px -1px black, -1px 1px black;

文字外發光效果:glow

a {
    background: #203;
    color: white;
    transition: 1s;
}
a:hover {
    color: transparent;
    text-shadow: 0 0 .1em white, 0 0 .3em white;
}

// 或
a:hover {
    filter: blur(.1em);
}

文字凸起效果:extruded

background: #58a;
color: white;
text-shadow: 0 1px hsl(0, 0%, 85%),
             0 2px hsl(0, 0%, 80%),
             0 3px hsl(0, 0%, 75%),
             0 4px hsl(0, 0%, 70%),
             0 5px hsl(0, 0%, 65%),
             0 5px 10px black;

// scss mixin
@mixin text-3d($color:white, $depth: 5) {
    $shadows: ();
    $shadow-color: $color;
    
    @for $i from 1 through $depth {
        $shadow-color: darken($shadow-color, 10%);
        $shadow: append($shadows, 0 ($i * 1px) $shadow-color, comma);
    }
    
    color: $color;
    text-shadow: append($shadows, 0 ($depth * 1px) 10px black, comma);
}

h1 { @include text-3d(#eee, 4)};

環形文字-svg:circular-text

用戶體驗

  1. 選用合適的鼠標光標
cursor: not-allowed; // 禁用
  1. 擴大可點擊區域:hit-area-border
border: 10px solid transparent;
box-shadow: 0 0 0 1px rgba(0, 0, 0. .3) inset;
background-clip: padding-box;

偽元素實現:偽元素可以代表其宿主元素來響應鼠標交互。hit-area

button {
    position: relative;
}
button::before {
    content: '';
    position: absolute;
    top: -10px; right: -10px;
    bottom: -10px; left: -10px;
}
  1. 自定義復選框:checkboxes

:checked,改偽類只在復選框被勾選時才匹配,不論這個勾選狀態是由用戶交互觸發,還是腳本觸發。

借助組合選擇符來給其他元素設置樣式。當 label 元素與復選框關聯之後,也可以起到觸發開關的作用。

<input type="checkbox" id="awesome"/>
<label for="awesome">Awesome</label>

input[type="checkbox"] + label::before {
    content: '\a0'; /* 不換行空格 */
    display: inline-block;
    vertical-align: .2em;
    width .8em;
    height: .8em;
    margin-right: .2em;
    border-radius: .2em;
    background: silver;
    text-indent: .15em;
    line-height: .65;
}
input[type="checkbox"]:checked + label::before {
    content: '\2713';
    background: yellowgreen;
}

// 不損失可訪問性的方式隱藏,不能使用display:none,會把它從鍵盤tab鍵切換焦點的隊列中完全刪除
input[type="checkbox"] {
    position: absolute;
    clip: rect(0, 0, 0, 0);
}

input[type="checkbox"]:focus + label::before {
    box-shadow: 0 0 .1em .1em #58a;
}
input[type="checkbox"]:disabled + label::before {
    background: gray;
    box-shadow: none;
    color: #555;
}

結構與布局

  1. 自適應內部元素:intrinsic-sizing
<p>Some text [...]</p>
<figure>
  <img src="adamcatlace.jpg" />
  <figcaption>
    The great Sir Adam Catlace was named after Countess Ada Lovelace, the first programmer.
  </figcaption>
</figure>
<p>More text [...]</p>

希望這個 figure 元素能跟 它所包含的圖片一樣框,而且水平居中。

figure {
    max-width: 300px; /* 回退 */
    max-width: min-content;
    margin: auto;
}
figure > img {
    max-width: inherit;
}

min-content: 解析為這個容器內部最大的不可斷行元素的寬度(即最寬的單詞、圖片或具有固定寬度的盒元素)

  1. 精確控制表格列寬
table {
    table-layout: fixed;
    width: 100%;
}
  1. 根據兄弟元素的數量來設置樣式:styling-sibling-count

通過兄弟元素的總數設置樣式

li:only-child {
    /* 只有一個列表項是的樣式 */
}
li:first-child:nth-last-child(1) {
    /* 相當於li:only-child,第一項同時是最後一項 */
}
li:first-child:nth-last-child(4) {
    /* 這個元素是父元素的第一個子元素,同時是從後往前數的第四個子元素,即命中一個正好有四個列表項的第一個列表項 */
}
li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li {
    /* 當列表正好是四項時,命中所有列表項 */
}

根據兄弟元素的數量範圍來匹配元素

li:nth-child(n+b) {
    /* n+b, n從0開始,選中從b個元素開始的所有子元素*/  
}
li:nth-child(n+4) {
    /* 選中除了第1、第2、第3個子元素之外的所有子元素 */
}
li:first-child:nth-last-child(n+4),
li:first-child:nth-last-child(n+4) ~ li {
    /* 當列表至少包含四項時,命中所有列表項 */
}

-n+b:可以選中開頭的b個元素,當列表中有4個或更少的列表項時:

li:first-child:nth-last-child(-n+4),
li:first-child:nth-last-child(-n+4) ~ li {
    /* 當列表最多包含四項時,命中所有列表項 */
}
li:first-child:nth-last-child(n+2):nth-last-child(-n+6),
li:first-child:nth-last-child(n+2):nth-last-child(-n+6) ~ li {
    /* 當列表包含2-6項時,命中所有列表項 */
}

實際應用篇:偽類匹配列表數目實現微信群頭像CSS布局的技巧

  1. 滿幅的背景,定寬的內容:fluid-fixed

定寬內容居中,正常寫法

<footer>
  <div class="wrap">內容</div>
</footer>

footer { background: #333; }
.wrap {
    max-width: 900px;
    margin: 1em auto;
}

一層實現:

footer {
    padding: 1em; /* 回退 */
    padding: 1em calc(50% - 450px);
    background: #333
}
  1. 垂直居中

絕對定位方案:

main {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

彈性盒子方案:

body {
    display: flex;
    min-height: 100vh;
    margin: 0;
}
main { /* 水平垂直居中 */
    margin: auto;
}
main {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18em;
    height: 10em;
}
  1. 緊貼底部的頁腳

塊級頁腳,頁面內容足夠長在頁面底部,頁面內容很少,需要緊貼在視口底部。

固定高度的方案:sticky-footer-fixed

main {
    min-height: calc(100vh - 7em)
}

更靈活的方案:sticky-footer

body {
    display: flex;
    flex-flow: column;
    min-height: 100vh;
}
main { flex: 1; }

過渡與動畫

  1. 緩動效果:具有回彈效果,利用貝塞爾曲線:bounce

ease-out 是 ease-in 的反向版本。

cubic-bezier(x1, y1, x2, y2),把控制錨點的水平坐標和垂直坐標互換,就可以得到任何調速函數的反向版本。

cubic-bezier 圖形化工具

@keyframes bounce {
    60%, 80%, to {
        transform: translateY(400px);
        animation-timing-function: ease;
    }
    70% { transform: translateY(300px); }
    90% { transform: translateY(360px); }
}
.ball {
    animation: bounce 3s cubic-bezier(.1, .25, 1 ,.25);
}
  1. 逐幀動畫:steps():frame-by-frame
@keyframes loader {
    to { background-position: -800px 0; }
}

.loader {
    width: 100px;
    height: 100px;
    background: url(img/loader.png) 0 0;
    animation: loader 1s infinite steps(8);
}

steps() 會根據你指定的步數進量,把整個動畫切分為多幀,而且每個動畫會在幀與幀之間硬切。

關於steps:CSS3 animation屬性中的steps功能符深入介紹

  1. 打字效果:typing

ch 單位:表示“0”字形的寬度。在等寬字體中,“0”字形的寬度和其他所有字形的寬度一致。用 ch 單位表示寬度,那取值實際上就是字符的數量。

@keyframes typing {
    from { width: 0; }
}
@keyframes caret {
    50% { border-color: transparent; }
}
h1 {
    width: 15ch; /* 文本的寬度 */
    overflow: hidden;
    white-space: nowrap;
    border-right: .5em solid;
    animation: typing 6s steps(15),
               caret 1s steps(1) infinite;
    
}

document.querySelectAll('h1').forEach(function (h1) {
    var len = h1.textContent.length, s = h1.style;
    s.width = len + 'ch';
    s.animationTimingFunction = "steps("+len+"), steps(1)";
})
  1. 狀態平滑的動畫:動畫暫停、開啟,state-animations
@keyframes panoramic {
    to { background-position: 100% 0; }
}
.panoramic {
    width: 150px;
    height: 150px;
    background: url(img/naxos-greece.jpg);
    background-size: auto 100%;
    animation: panoramic 10s linear infnte alternate;
    animation-play-state: paused;
}
.panoramic:hover, .panoramic:focus {
    animation-play-state: running;
}

整張圖片從左滾蛋到右側。

  1. 沿環形路徑平移的動畫:circular

沿著環形進行移動,同時保持自己本來的朝向。

每個transform-origin都是可以被兩個translate()模擬出來的。下面兩段代碼是等效的。

transform: rotate(30deg);
transform-origin: 200px 300px;

transform: translate(200px, 300px) rotate(30deg) translate(-200px, -300px);
transform-origin: 0 0;

牢記變形函數並不是彼此獨立的。每個變形函數並不是只對這個元素進行變形,而是會把整個元素的坐標系統進行變形,從而影響後面的變形操作。這也說明了為什麽變形函數的順序是很重要的。

@keyframes spin {
    from {
        transform: translateY(150deg) translateY(-50%)
                   rotate(0turn)
                   translateY(-150deg) translateY(50%)
                   rotate(1turn);
    }
    to {
        transform: translateY(150deg) translateY(-50%)
                   rotate(1turn)
                   translateY(-150deg) translateY(50%)
                   rotate(0turn);
    }
}
.avatar { animation: spin 3s infinite linear; }

最後

書中記錄的這些效果,都有很詳細的啟發作用,每個案例都敲一遍,理解了感覺css真的有魔力呢。

向該書的作者致敬,表示感謝。

css 揭秘-讀書筆記