CSS 繼承深度解析

FROM ME:css

以前在研究前端性能優化的時候,就有學習關於CSS中「善用CSS中的繼承」。html

原文:CSS Inheritance, The Cascade And Global Scope: Your New Old Worst Best Friends前端

譯文:掘金翻譯計劃git

  我酷愛模塊化設計。長期以來我都熱衷於將網站分離成組件,而不是頁面,而且動態地將那些組件合併到界面上。這種作法靈活,高效而且易維護。github

  可是我不想個人設計看上去是由一些不相關的東西組成的。我是在創造一個界面,而不是一張超現實主義的照片。web

  很幸運的是,已經有一項叫作 CSS 的技術,就是特地設計用來解決這個問題的。使用 CSS,我就能夠在 HTML 組件之間處處傳遞樣式,從而以最小的代價來保證一致性的設計。這很大程度上要感謝兩個 CSS 特性:性能優化

  • 繼承,
  • 層疊 (CSS 當中的 C,cascade)。

  儘管這些特性讓咱們可以以一種 DRY 且有效率的方式來給 Web 文檔添加樣式,同時也是 CSS 存在的緣由,但很明顯,它們已經再也不受到青睞。在一些 CSS 方法論裏,如 BEM 和 Atomic CSS 這些經過程序化封裝 CSS 模塊的方法,許多都盡力去規避或者抑制這些特性。這也讓開發者有了更多機會去控制他們的 CSS,但這僅僅是一種基於頻繁干預的專項控制。架構

  我準備帶着對模塊化界面設計的尊敬在此從新審視繼承、層疊和做用域。我想要的告訴你的是如何利用這些特性讓你的 CSS 代碼更簡潔,實現更好的自適應,而且提升頁面的可擴展性。前端性能

繼承和 font-family

  儘管許多人在抱怨 CSS 爲何不僅僅提供一個全局做用域,但若是它這麼作的話,那麼就會有不少重複樣式了。反之,CSS 有全局做用域和局部做用域。就像在 JavaScript 裏,局部做用域有權限訪問父級和全局做用域,而在 CSS 裏,局部做用域則幫助了繼承。編輯器

  例如,若是給根部(也做:全局)的 html 元素定義一個 font-family 屬性,那麼能夠肯定這條規則會在文檔裏應用到全部祖先元素(有一些例外狀況,將在下個部分討論)。

html {
    font-family: sans-serif;
}

/*
This rule is not needed ↷
p {
    font-family: sans-serif;
}
*/

  就像在 JavaScript 裏那樣,若是我在局部做用域裏定義了某些規則,那麼它們在全局,或者說在任意祖先級的做用域中都是無效的,只有在它們本身的子做用域裏是有效的(就像在上面代碼中的 p 元素裏)。在下個例子當中,1.5 的 line-height 並無被 html 元素用上。可是,p 裏的 a 元素則運用上了 line-height 的值。

    html {
      font-family: sans-serif;
    }

    p {
      line-height: 1.5;
    }

    /*
    This rule is not needed ↷
    p a {
      line-height: 1.5;
    }
    */

  繼承最大的好處就是你能夠用不多量的代碼爲一致性的可視化設計創建一個基礎。並且這些樣式甚至將做用到你還沒寫的 HTML 上。咱們在討論不會過期的代碼!

替代方法

  固然有另一種方式提供公用樣式。好比,我能夠建立一個 .sans-serif 類...

    .sans-serif {
      font-family: sans-serif;
    }

  ...並將它應用到任意我想要它有這個樣式的元素上去:

    <p class="sans-serif">Lorem ipsum.</p>

  這種方法提供了一些控制上的權利:我能夠準確地挑選決定哪些元素應用這個樣式,哪些元素不用。

  任何可以控制的機會都是很吸引人的,但有一些明顯的問題。我不只須要手動地給須要應用樣式的元素添加類名(這也意味着我要首先肯定這個樣式類是什麼效果),並且在這種狀況下也已經有效地放棄了支持動態內容的可能性:無論是富文本編輯器仍是 Markdown 解析器都沒辦法給任意的 p 元素提供 sans-serif 類。

  class="sans-serif" 和 style="font-family: sans-serif" 的用法差很少 - 除了前者意味着要同時在樣式表和 HTML 當中添加代碼。使用繼承,咱們就能夠在其中一個少寫點,而另一個則不用再寫了。相比給每一個字體樣式寫一個類,咱們能夠只在一個聲明裏,給 html 元素添加想要的規則。

    html {
      font-size: 125%;
      font-family: sans-serif;
      line-height: 1.5;
      color: #222;
    }

inherit 關鍵字

  某些類型的屬性是不會默認繼承的,而某些元素則不會繼承某些屬性。可是在某些狀況下,可使用 [property name]: inherit 來強制繼承。

  舉個例子,input 元素在以前的例子中不會繼承任何字體的屬性,textarea 也同樣不會繼承。爲了確保全部元素均可以從全局做用域中繼承這些屬性,可使用通配選擇符和 inherit 關鍵字。這樣,就能夠最大程度地使用繼承了。

    * {
      font-family: inherit;
      line-height: inherit;
      color: inherit;
    }

    html {
      font-size: 125%;
      font-family: sans-serif;
      line-height: 1.5;
      color: #222;
    }

  注意到我忽略了 font-size。我不想直接繼承 font-size 的緣由是,它會將 heading 元素(譯者注:如 h1)、small 元素以及其餘一些元素的默認 user-agent 樣式給覆蓋掉。這麼作我就能夠節省一行代碼,而且讓 user-agent 決定想要什麼樣式。

  另一個我不想繼承的屬性是 font-style:我不想重設 em 的斜體,而後再次添加上它。這將成爲無謂的工做並會產生多餘的代碼。

  如今,全部無論是能夠繼承或者是強制繼承的字體樣式都是我所指望的。咱們已經花了很長時間只用兩個聲明區塊來傳遞一個一致性的理念和做用域。從如今開始,除開一些例外狀況,沒有人會在構造組件的時候還須要去考慮 font-familyline-height 或者 color 了。這就是層疊的由來。

基於例外的樣式

  我可能想要主要的 heading 元素(h1)採用相同的 font-familycolor 和 line-height。使用繼承就是很好的解決方案,可是我又想要它的 font-size 不同。由於默認的 user-agent 樣式已經給 h1 元素提供了一個大號的 font-size(但這時它就會被我設置的相對基礎字體大小爲 125% 的樣式覆蓋掉),可能的話我不須要這裏發生覆蓋。

  然而,難道我須要調整全部元素的字體大小嗎?這時我就利用了全局做用域的優點,在局部做用域裏只調整我須要調整的地方。

    * {
      font-family: inherit;
      line-height: inherit;
      color: inherit;
    }

    html {
      font-size: 125%;
      font-family: sans-serif;
      line-height: 1.5;
      color: #222;
    }

    h1 {
      font-size: 3rem;
    }

  若是 CSS 元素的樣式默認被封裝,那麼下面的狀況就不可能了:須要明確地給 h1 添加全部字體樣式。反而,我能夠將樣式分爲幾個單獨的樣式類,而後經過空格分隔來逐一給 h1 添加樣式:

    <h1 class="Ff(sans) Fs(3) Lh(1point5) C(darkGrey)">Hello World</h1>

  無論哪一種方式,都須要更多的工做,並且最終目的都是一個具有樣式的 h1。使用層疊,我已經給大部分元素賦上了想要的樣式,而且只在一個方面使得 h1 成爲一個例外。層疊做爲一個過濾器,意味着樣式只在添加新樣式覆蓋的時候纔會發生改變。

元素樣式

  咱們已經開了個好頭,但想要真正地掌握層疊,還須要儘量多地給公共元素添加樣式。爲何?由於咱們的混合組件是由獨立的 HTML 元素構成,而且一個屏幕閱讀器友好的界面充分利用了語義化結構標記。

  換句話說,讓你的界面「分子化」(使用了 atomic 設計術語)的 「atoms」 樣式應該在很大程度上可定位而且使用元素選擇符。元素選擇符的優先級很低,因此它們不會覆蓋你以後可能加進來的基於類的樣式。

  首先應該作的事情就是給全部你即將須要使用的元素添加樣式:

    a { … }
    p { … }
    h1, h2, h3 { … }
    input, textarea { … }
    /* etc */

  若是你想在無冗餘的狀況下有個一致性界面的話,那麼下一步很是重要:每當你建立一個新組件的時候,若是它採用了一些新元素,那麼就用元素選擇符來給它們添加樣式。如今不是時候去使用限制性、高優先級的選擇符,也沒有任何須要去編寫一個樣式類。語義化元素就使用其自己。

  舉個例子,若是我尚未給 button 元素 (就像前一個例子)添加樣式,而且新組件加入了一個 button 元素,那麼這就是一個給整個界面的 button 元素添加樣式的好機會。

    button {
      padding: 0.75em;
      background: #008;
      color: #fff;
    }

    button:focus {
      outline: 0.25em solid #dd0;
    }

  如今,當你想要再寫一個新組件而且一樣加入按鈕的時候,就少了一件須要操心的事情了。在不一樣的命名空間下,不要去重寫相同的 CSS,而且也沒有類名須要記住或編寫。CSS 本就應該老是致力於讓事情變得簡單和高效 - 它自己就是爲此而設計的。

使用元素選擇符有三個主要的優點:

  • 生成的 HTML 更加簡潔(沒有多餘的各類樣式類)。
  • 生成的樣式表更加簡潔(樣式在組件間共享,不須要在每一個組件裏重寫)。
  • 生成的添加好樣式的界面基於語義化 HTML。

  使用類來專門提供樣式經常被定義爲「關注點分離」。這是對 W3C 的關注點分離原則的誤解。它的目的是用 HTML 和 CSS 樣式來描述整個結構。由於類專門是爲了樣式目的而制定,並且是在結構標記裏出現,因此不管它們在哪裏使用,技術上都是在打破分離,你不得不改變實質結構來獲得樣式。

  無論在哪裏都不要依賴表面的結構標記(樣式類,內聯樣式),你的 CSS 應該兼容通用的結構和語義化的約定。這就能夠簡單地擴展內容和功能而無需它也變成一個樣式的任務。一樣在不一樣傳統語義化結構的項目裏,也可讓你的 CSS 變得更加可複用(可是這一點 CSS 的「方法論」可能會有所不一樣)。

特殊狀況

  在有人指責我過度簡單化以前,我意識到界面上不是全部的按鈕都作一樣的事情,我還意識到作不一樣事情的按鈕在某種程度上可能應該看起來不同。

  但這並非說咱們就須要用樣式類、繼承或者層疊來處理了。讓一個界面上的按鈕看起來徹底不同是在混淆你的用戶。爲了可訪問性和一致性,大多數按鈕在外觀上只須要經過標籤來進行區分。

    <button>create</button>

    <button>edit</button>

    <button>delete</button>

  記住樣式並非視覺上惟一的區分方法。內容一樣能夠在視覺上區分,並且在必定程度上它更加明確一些,由於你但是在文字上告訴了用戶不一樣的地方。

  大多數狀況下,單獨使用樣式來區份內容都不是必要或者正確的。一般,樣式區分應該是附加條件,好比一個紅色背景或者一個帶圖標的文本標籤。文本標籤對那些使用聲音激活的軟件有着特定的效果:當說出 「red button」 或者 「button with cross icon」 的時候並無引發軟件的識別時。

  我將在「工具類」部分探討關於添加細微差異到看起來類似的元素上的話題。

標籤屬性

  語義化 HTML 並不只僅關於元素。標籤屬性定義類型、樣式屬性和狀態。這些對可訪問性來講也很重要,因此它們須要寫在 HTML 裏合適的地方。並且由於都在 HTML 裏,因此它們還提供了作樣式鉤子的機會。

  舉個例子,input 元素有一個 type 屬性,那麼你應該想要利用它的好處,還有像 aria-invalid 屬性是用來描述狀態的。

    input, textarea {
      border: 2px solid;
      padding: 0.5rem;
    }

    [aria-invalid] {
      border-color: #c00;
      padding-right: 1.5rem;
      background: url(images/cross.svg) no-repeat center 0.5em;
    }

這裏有幾點須要注意一下:

  • 這裏我不須要設置 colorfont-family 或者 line-height,由於這些都從 html 上繼承了,得益於上面使用的 inherit關鍵字。若是我想在整個應用的層面上改變 font-family,只須要在 html 那一塊對其中一個聲明進行編輯就能夠了。
  • border 的顏色關聯到 color,因此它一樣是從全局 color 中繼承。我只需聲明 border 的寬度和風格。
  • [aria-invalid] 屬性選擇符是沒有限制的。這意味着它有着更好的應用(它能夠同時做用在 input 和 textarea 選擇符)以及最低的優先級。簡單的屬性選擇符和類選擇符有着一樣的優先級。無限制使用它們意味着以後任何寫在層疊下的樣式類均可以覆蓋它們。

  BEM 方法論經過一個修飾符類來解決這個問題,好比 input--invalid。可是考慮到無效的狀態應該只在可通訊的時候起做用,input--invalid 仍是必定的冗餘。換句話說,aria-invalid 屬性不得不寫在那裏,因此這個樣式類的目的在哪裏?

只寫 HTML

  在層疊方面關於大多數元素和屬性選擇符我絕對喜歡的事情是:組件的構造變成更少地瞭解公司或組織的命名約定,更多地關注 HTML。任何精通寫出像樣 HTML 的開發者被分配到項目中時,都會從已經寫到位的繼承樣式當中獲益。這些樣式顯著地減小了讀文檔和寫新 CSS 的須要。大多數狀況下,他們能夠只寫一些死記硬背應該知道的(meta)語言。Tim Baxter 一樣爲此在Meaningful CSS: Style It Like You Mean It 裏寫了一個案例。

佈局

  目前爲止,咱們尚未寫任何指定組件的 CSS,但這並非說咱們尚未添加任何相關樣式。全部組件都是 HTML 元素的組合。造成更復雜的組件主要是靠這些元素的組合順序和排列。

這就給咱們引出了佈局這個概念。

  主要咱們須要處理流式佈局 - 連續塊元素之間的間距。你可能已經注意到目前爲止我沒有給任何元素設置任何的外邊距。那是由於外邊距不該該考慮成一個元素的屬性,而應該是元素上下文的屬性。也就是說,它們應該只在遇到元素的時候才起做用。

  幸運的是,直接相鄰選擇符能夠準確地描述這種關係。利用層疊,咱們可使用一個統一默認貫穿全部連續塊級元素的選擇符,只有少數例外狀況。

    * {
      margin: 0;
    }

    * + * {
      margin-top: 1.5em;
    }

    body, br, li, dt, dd, th, td, option {
      margin-top: 0;
    }

  使用優先級極低的貓頭鷹選擇符確保了任意元素(除了那些公共的例外狀況)都經過一行來間隔。這意味着在全部狀況下都會有一個默認的白色間隔,全部編寫組件流內容的開發者都將有一個合理的起點。

  在大多數狀況下,外邊距只會關心它們本身。不過由於低優先級,很輕易就能夠在須要的時候覆蓋掉那基礎的一行間隔。舉個例子,我可能想要去掉標籤和其相關元素之間的間隔,好表示它們是一對的。在下面的示例裏,任意在標籤以後的元素(inputtextareaselect 等等)都不會有間隔。

    label {
      display: block
    }

    label + * {
      margin-top: 0.5rem;
    }

  再次,使用層疊意味着只須要在須要的時候寫一些特定的樣式就能夠了,而其餘的元素都符合一個合理的基準。

  須要注意的是,由於外邊距只在元素之間出現,因此它們不會和可能包括在容器內的內邊距重疊。這也是一件不須要擔憂或者預防的事情。

  還注意到無論你是否決定引入包裝元素都獲得了一樣的間隔。就是說,你能夠像下面這樣作並實現相同的佈局 - 外邊距在 div之間出現比在標籤和輸入框之間出現要好得多。

    <form>
      <div>
        <label for="one">Label one</label>
        <input id="one" name="one" type="text">
      </div>
      <div>
        <label for="two">Label two</label>
        <input id="two" name="two" type="text">
      </div>
      <button type="submit">Submit</button>
    </form>

  用像 atomic CSS 這樣的方法能實現一樣的效果,只需組合各類外邊距相關的樣式類並在各類狀況下手動添加它們,包括被 * + * 隱式控制的 first-child 這種例外狀況:

    <form class="Mtop(1point5)">
      <div class="Mtop(0)">
        <label for="one" class="Mtop(0)">Label one</label>
        <input id="one" name="one" type="text" class="Mtop(0point75)">
      </div>
      <div class="Mtop(1point5)">
        <label for="two" class="Mtop(0)">Label two</label>
        <input id="two" name="two" type="text" class="Mtop(0point75)">
      </div>
      <button type="submit" class="Mtop(1point5)">Submit</button>
    </form>

  記住若是堅持使用 atomic CSS 的話,像上面那麼寫只會覆蓋到頂部外邊距的狀況。你必須還要爲 colorbackground-color以及其餘屬性創建獨立的樣式類,由於 atomic CSS 不會控制繼承或者元素選擇符。

    <form class="Mtop(1point5) Bdc(#ccc) P(1point5)">
      <div class="Mtop(0)">
        <label for="one" class="Mtop(0) C(brandColor) Fs(bold)">Label one</label>
        <input id="one" name="one" type="text" class="Mtop(0point75) C(brandColor) Bdc(#fff) B(2) P(1)">
      </div>
      <div class="Mtop(1point5)">
        <label for="two" class="Mtop(0) C(brandColor) Fs(bold)">Label two</label>
        <input id="two" name="two" type="text" class="Mtop(0point75) C(brandColor) Bdc(#fff) B(2) P(1)">
      </div>
      <button type="submit" class="Mtop(1point5) C(#fff) Bdc(blue) P(1)">Submit</button>
    </form>

  Atomic CSS 使開發者能夠直接控制樣式而再也不使用內聯樣式,內聯樣式不像樣式類同樣能夠複用。經過爲各類獨立的屬性提供樣式類,減小了樣式表中的重複聲明。

  可是,它須要直接介入標記從而實現這些目的。這就要求學習並投入它那冗長的 API,一樣還須要編寫大量額外的 HTML 代碼。

  相反,若是隻用來對任意 HTML 元素及其空間關係設計樣式的話,那麼 CSS 「方法論」就要被大範圍棄用了。使用一致性設計的系統有着很大的優點,相比一個疊加樣式的 HTML 系統更方便考慮和分開管理。

  不管如何,下面是咱們的 CSS 架構和流式佈局內容解決方案應該具有的特徵:

    1.   全局(html)樣式並強制繼承,
    2.   流式佈局方法及部分例外(使用貓頭鷹選擇符),
    3.   元素及屬性樣式。

  咱們尚未編寫一個特定組件或者構思一個 CSS 樣式類,但咱們大部分的樣式都已經寫好了,前提是若是咱們可以將樣式類寫得合理且可複用。

工具類

  關於樣式類它們有一個全局做用域:在 HTML 裏任何地方使用,它們都會被關聯的 CSS 所影響。對大多數人來講,這都被看作一個弊端,由於兩個獨立的開發者有可能以一樣的命名來編寫一個樣式類,從而互相影響工做。

  CSS modules 最近被用來解決這種狀況,經過以程序來生成惟一的樣式類名,綁定到它們的局部或組件做用域當中。

    <!-- my module's button -->
    <button class="button_dysuhe027653">Press me</button>

    <!-- their module's button -->
    <button class="button_hydsth971283">Hit me</button>

  忽略掉生成代碼的醜陋,你應該可以看到兩個獨立組件之間的不一樣,而且能夠輕易地放在一塊兒:惟一的標識符被用來區分同類的樣式。在這麼多更好的努力和冗餘代碼下,結果界面將要麼不一致,要麼一致。

  沒有理由對公共元素來進行惟一性區分。你應該對元素類型添加樣式,而不是元素實例。謹記 「class」 意味着「某種可能存在不少的東西的類型」。換句話說,全部的樣式類都應該是工具類:全局可複用。

  固然,在這個示例裏,總之 .button 類是冗餘的:咱們能夠用 button 元素選擇符來替代。可是若是有一種特殊類型的按鈕呢?好比,咱們可能編寫一個 .danger 類來指明這個按鈕是作危險性操做,好比刪除數據:

   .danger {
      background: #c00;
      color: #fff;
    }

  由於類選擇符的優先級比元素選擇符的優先級高,而和屬性選擇符優先級相同,因此這種方式添加在樣式表後面的樣式規則會覆蓋前面元素和屬性選擇符的規則。因此,危險按鈕會以紅色背景配白色文本出現,但它其餘的屬性,好比內邊距,聚焦輪廓以及外邊距都會經過以前的流式佈局方法添加,保持不變。

    <button class="danger">delete</button>

  若是多位開發人員長時間在一樣的代碼基礎上工做,那麼偶爾就會發生命名衝突。可是有幾種避免這種狀況的方法,好比,噢,我不太知道,但對於你想要採用的名稱我建議首先作一個文本搜索,看看是否已經存在了。由於你不知道,可能已經有人解決了你正在定位的問題。

局部做用域的各類工具類

  對於工具類來講,我最喜歡作的事情就是把它們設置在容器上,而後用這個鉤子去影響內部子元素的佈局。舉個例子,我能夠快速對任意元素設置一個等間隔、響應式以及居中的佈局。

    .centered {
      text-align: center;
      margin-bottom: -1rem; /* adjusts for leftover bottom margin of children */
    }

    .centered > * {
      display: inline-block;
      margin: 0 0.5rem 1rem;
    }

  使用這個方法,我能夠把列表項、按鈕、按鈕組合以及連接等隨便什麼元素居中展現。全靠 > * 的使用,在這個做用域中,它意味着帶有 .centered 樣式的元素下最近的子元素將會採用這些樣式,而且還繼承全局和父元素的樣式。

  並且我調整了外邊距,好讓元素能夠自由進行包裹,並且不會破壞使用 * + * 選擇符設置的垂直設定。這少許的代碼經過對不一樣元素設置一個局部做用域,就提供了一個通用、響應式的佈局解決方案。

  個人一個小型(壓縮後 93B)的基於 flexbox 網格佈局系統 就是一個相似這種方法的工具類。它高度可複用,並且由於它使用了 flex-basis,因此不須要斷點干預。我只是用了 flexbox 佈局的方法。

    .fukol-grid {
      display: flex;
      flex-wrap: wrap;
      margin: -0.5em; /* adjusting for gutters */
    }

    .fukol-grid > * {
      flex: 1 0 5em; /* The 5em part is the basis (ideal width) */
      margin: 0.5em; /* Half the gutter value */
    }

  使用 BEM 的方法,你會被鼓勵在每一個網格項上放置一個明確的「元素」樣式類:

 

    <div class="fukol"> <!-- the outer container, needed for vertical rhythm -->
      <ul class="fukol-grid">
        <li class="fukol-grid__item"></li>
        <li class="fukol-grid__item"></li>
        <li class="fukol-grid__item"></li>
        <li class="fukol-grid__item"></li>
      </ul>
    </div>

  但這不是必要的。只需一個標識符去實例化本地做用域。這裏的列表項相比起我版本當中的列表項,再也不受外部影響的保護,也不該該被 > * 所影響。僅有的區別就是充斥了大量樣式類的標記。

  因此,如今咱們已經開始合併樣式類,但只在通用性上合併,和它們所預期的效果同樣。咱們仍然尚未獨立地給複雜組件添加樣式。反而,咱們在以一種可複用的方式解決一些系統性的問題。固然,你將須要在註釋裏寫清楚這些樣式類是如何使用的。

  像這些的工具類同時採用了 CSS 的全局做用域、局部做用域、繼承以及層疊的優勢。這些樣式類能夠在各個地方使用,它們實例化局部做用域從而隻影響它們的子元素,它們從父級或全局做用域中繼承沒有設置在自身的樣式,並且咱們沒有過分使用元素或類選擇符。

  下面是如今咱們的層疊看上去的樣子:

    1. 全局(html)樣式和強制性繼承,
    2. 流式佈局方法和一些例外(使用貓頭鷹選擇符),
    3. 元素和屬性樣式,
    4. 通用的工具類。

  固然,可能沒有必要去編寫全部這些示例工具類。重點是,若是在使用組件的時候出現了需求,那麼解決方案應該對全部組件都有效才行。必定要老是站在系統層面去思考。

特定組件樣式

  咱們從一開始就已經給組件添加了樣式,而且學習樣式結合組件的方法,因此不少人有可能會忽略掉立刻要講到這個部分。但值得說明的是,任何不是從其餘組件中建立的組件(甚至包括單個 HTML 元素)都是有必要存在的。它們是使用 ID 選擇符的組件,以及有可能成爲系統問題的風險。

  事實上,一個好的實踐是隻使用 ID 來給複雜組件標識(「molecules」、「organisms」),而且不在 CSS 裏使用這些 ID。好比,你能夠在登陸表單組件上寫一個 #login,那麼你就不該該在 CSS 裏以元素、屬性或者流式佈局方法的樣式來使用 #login,即便你可能會發現你在創造一個或兩個能夠在其餘表單組件裏使用的通用工具類。

  若是你確實使用了 #login,那麼它只會影響那個組件。值得提醒的是若是這麼作,那麼你就已經偏離了開發一個設計系統方向,而且朝着只有不停糾結像素的冗長代碼前進。

結論

  當我告訴人們我不使用諸如 BEM 這樣的方法論或者 CSS 模塊這樣的工具時,多數人會認爲我會編寫下面這樣的 CSS:

    header nav ul li {
      display: inline-block;
    }

    header nav ul li a {
      background: #008;
    }

  我沒有這樣作。一份清晰的陳述已經在這兒了,還有咱們須要當心去避免的事情也已經闡述了。只是想說明 BEM(還有 OOCSS、SMACSS、atomic CSS 等)並非避免複雜、不可能管理的 CSS 的惟一方法。

  爲了解決優先級問題,許多方法論幾乎都選擇了使用類選擇符。問題在於這產生了大量的樣式類:讓 HTML 標記變得臃腫的各類神奇代碼,以及失去了對文檔的注意力,這些都會讓新來的開發者對他們所處的系統感到困擾和迷惑。

  經過大量地使用樣式類,你還須要管理一個樣式系統,並且這個系統很大程度上是和 HTML 系統分離的。這種不太合適的所謂「關注點分離」能夠形成冗餘,甚至更糟糕,致使不可訪問性:有可能會在可訪問的狀態下影響一個視覺上的樣式:

    <input id="my-text" aria-invalid="false" class="text-input--invalid" />

爲了替換掉大量的編寫和各類樣式類,我找到了其餘一些方法:

  • 爲了一致性掌握繼承去設置一個前置條件;
  • 充分使用元素和屬性選擇符去支持透明度和基於標準的組合樣式;
  • 使用簡便的的流式佈局系統;
  • 合併一些高度通用的工具類,解決影響多元素的共同佈局問題。

  全部這些方法都是爲了建立一個設計系統,使編寫一個新組件變得更簡單,以及當項目成熟的時候,減小添加新的 CSS 代碼的依賴。而且這並非獲益於嚴格的命名和合並,反而是由於缺乏了它們。

  可能你會對我在這裏推薦的特殊技巧並不感冒,但我仍是但願這篇文章至少可讓你從新思考一下組件是什麼。它們不是你獨立建立的東西。有的時候,在標準 HTML 元素的狀況下,它們甚至不是你所建立的東西。你的組件從其餘組件拿來的東西越多,那麼界面的可訪問性和視覺上的一致性就會變得更好,而且最後會用更少的 CSS 去實現它們。

  (這些問題)CSS 並無太多過錯。事實上,讓你作不少事情是很是好的,咱們只是沒有利用罷了。

相關文章
相關標籤/搜索