[ISUX譯]為什么我為css變量如此興奮

jopen 8年前發布 | 20K 次閱讀 CSS 設計 前端技術

幾個星期前,CSS Variables ——CSS 變量草案發布在了W3C官方 ,更準確的應該叫 CSS 自定義屬性 ,目前在 Chrome Canary 版里面已經支持,開啟該功能見附[1]

當Chrome瀏覽器工程師 Addy Osmani 第一時間把這消息發布在推ter后,遭到了數萬人的 否定敵視懷疑 。于我而言,更多的感到是一個驚喜,因為這個功能實在讓人太興奮了。

快速的掃了一遍之后,發現99%人抱怨的無外乎這兩點:

  • 語法太丑和不夠簡潔
  • Sass 、Less早就有這些玩意了,不太care
  • </ul>

    雖然我承認我也對這語法很反感,更重要的是理解語法不只是反復無常的在選擇。CSS工作組討論很久語法的長度,他們 提取了一些點 ,考慮到CSS的語法兼容不會與未來增加的其他語言沖突。

    CSS 預處理器是一個非常出色的工具,但是它們的變量是靜態的,有語法作用域。Native CSS 變量,從另一面來看,它們是一個完全不同類型的變量:因為它們是動態的,他們的作用域是DOM,事實上,這也是困惑該不該稱他們為變量,它們實際上是CSS 屬性,這也給了他們一個機會,來解決這個功能完全不同的問題。

    在這篇文章中,我將討論一些CSS 自定義屬性這個功能,而且不用CSS 預處理器來做。當然我還演示一些新的設計模式,自定義功能的啟用。文章最后討論一下,我認為在未來最有可能的是預處理變量和自定義變量一起使用,兩個東西取長補短,珠聯璧合。

    注意:這篇文章不是介紹CSS 自定義屬性,如果你還從來沒聽說過他們,不熟悉他們是如何工作的,可以看看 這里

    </div>

    預處理器變量的限制

    在繼續寫之前,我想強調的是,我真的很喜歡CSS 預處理器,我的所有項目都在使用它。預處理器做了一件非常了不起的事情,即時你知道他最終出來的就是原始的CSS,任然可以感受這個神器的時代。

    任何工具,都有他的局限性,有一個炫酷的外觀會讓人驚喜而忽略了其中的限制,特別是新用戶。

    Preprocessor variables aren’t live

    也許受預處理限制,在媒體查詢中,最常見的新手也無力吐槽定義變量或使用 @extend

    <code>
    $gutter: 1em;

    @media (min-width: 30em) { $gutter: 2em; }

    .Container { padding: $gutter; } </code></pre></div>

    如果你編譯上面的代碼,你得到是:

    .Container {
      padding: 1em;
    }

    如你所見,媒體查詢被廢棄,變量賦值被忽略。

    從理論上講,雖然sass 負責申明條件變量,但這樣做也是一個挑戰,枚舉所有Permutations—exponentially 會增加CSS的最終大小。

    預處理器變量不能級聯(層疊)

    每當你使用變量,作用域的范圍不可避免,這個變量應該全局嗎?應該是file/module?還是塊作用域?

    CSS 最終是為HTML的樣式,事實證明還有另外一種有用的方法是變量的范圍:DOM 元素,但是preprocessors不能運行在瀏覽器且從未看見標記

    參考一個網站,試圖給 <html> 的元素添加一個 class user-setting-large-text 他們更傾向于更大的文本大小 。

    一旦這個class設置,更大 $font-size 變量賦值就會運用:

    </div>

    $font-size: 1em;

    .user-setting-large-text { $font-size: 1.5em; }

    body { font-size: $font-size; }</pre></div>

    但是,就像上面媒體查詢例子,Sass 直接忽略變量賦值, 意味著這種事是不可能的。他輸出的:

    body {
      font-size: 1em;
    }

    預處理器變量不繼承

    雖然繼承是級聯的一部分,但是我還是要提一下,因為很多次我想使用這個功能都未能用成。

    有一種情況,你有Dom元素在顏色風格基礎上有什么的變化時候,你可以用在他們的父元素上。

    .alert { background-color: lightyellow; }
    .alert.info { background-color: lightblue; }
    .alert.error { background-color: orangered; }

    .alert button { border-color: darken(background-color, 25%); }</pre></div>

    上面的Sass代碼是無效的,但你應該能理解這代碼試圖要完成什么。

    它最后試圖使用sass的 darken 函數用在 background-color 屬性,但button元素繼承它的父class元素 .alert 。如果class info 或者 error 已經添加到 alert (或者通過JavaScript設置背景顏色或用戶樣式),按鈕元素希望能夠得到這兩個顏色。

    現在這個雖然不會在sass 運行,因為預處理器不知道DOM結構,但還是希望搞清楚這類型的東西可能有哪些用處。

    說一個特定的用例:這也是在繼承DOM屬性的可訪問性運行color 函數的原因。舉個栗子,為了確保文本總是可讀,且充分與背景顏色形成鮮明對比。通過自定義屬性和新的 CSS Color函數 ,這將很快成為可能!

    預處理器變量不能相互協作

    這是一個明顯呈下降趨勢的預處理器,如果你用PostCSS 建立一個網站,你想使用第三方組件,不好意思,你只有通過Sass的 themeable

    與第三方分享預處理器變量在不同的工具集成或第三方托管的CND樣式與都非常困難(至少不容易)

    本地CSS自定義屬性將與任何CSS預處理或者原CSS正好相反。

    自定義屬性有何不同

    你可能已經猜到了,我上面列出的適用于CSS 自定義屬性沒有任何限制,但也許更重要的不是說他們不適用,而是為什么他們不用。

    CSS自定義屬性就像常規的CSS屬性一樣,他們的操作方式完全相同

    像普通的CSS屬性,自定義屬性是動態的,他們可以在運行時修改,也可以在媒體查詢時通過更改DOM添加一個新類,同時也可以指派內聯元素和一個常規CSS里申明選擇器。還可以通過正常的cascade規則或者使用JavaScript覆蓋。最主要的是,他們的可以繼承的,所以當他們應用到DOM元素的時候,他們的子元素也會繼承屬性。

    為了更簡潔,預處理器變量是語法作用域和編譯后靜態。自定義屬性作用域是DOM,他們都很靈活。

    實際案例

    如果你仍然不確定自定義屬性可以做到這一點,而預處理器不行,我這里給一些例子。

    不論真假,有大量非常好的例子我都很想展示,但為了不讓這篇文章太丑,我選了兩個。

    我選擇這些例子不僅僅因為它們的理論,它們也是我們過去實際面臨的挑戰,我依然記得試圖用預處理器,但這是不可能的。現在好了,直接自定義屬性走起。

    媒體查詢的響應式特性

    很多網站在項目布局使用“ gap ”和“ gutter ” 定義默認間距和填充頁面各個部分,很多時候,你想要這個“ gutter ”的值根據瀏覽器窗口的大小而不同。在大屏幕上你想要每一項之間有足夠的空間,但小屏幕又負擔不起那么大的空間,所以“ gutter ”的值要較小。

    正如我上面提到的,在媒體查詢里面Sass 不能正常運行,所以你必須每個單獨處理。

    下面的例子定義了變量 $gutterSm , $gutterMd$gutterLg ,然后給每個變量申明一個單獨的規則:

    / Declares three gutter values, one for each breakpoint /

    $gutterSm: 1em; $gutterMd: 2em; $gutterLg: 3em; / Base styles for small screens, using $gutterSm. /

    .Container { margin: 0 auto; max-width: 60em; padding: $gutterSm; } .Grid { display: flex; margin: -$gutterSm 0 0 -$gutterSm; } .Grid-cell { flex: 1; padding: $gutterSm 0 0 $gutterSm; } / Override styles for medium screens, using $gutterMd. /

    @media (min-width: 30em) { .Container { padding: $gutterMd; } .Grid { margin: -$gutterMd 0 0 -$gutterMd; } .Grid-cell { padding: $gutterMd 0 0 $gutterMd; } } / Override styles for large screens, using $gutterLg. /

    @media (min-width: 48em) { .Container { padding: $gutterLg; } .Grid { margin: -$gutterLg 0 0 -$gutterLg; } .Grid-cell { padding: $gutterLg 0 0 $gutterLg; } }</pre></div>

    使用自定義屬性來完成相同的東西,你只需要定義樣式即可。你可以使用一個 gutter 屬性,然后隨著媒體查詢的變化,更新 gutter 的值,它就會做出相應的變化。

    :root { --gutter: 1.5em; }

    @media (min-width: 30em) { :root { --gutter: 2em; } } @media (min-width: 48em) { :root { --gutter: 3em; } } /*

    • Styles only need to be defined once because
    • the custom property values automatically update. */

    .Container { margin: 0 auto; max-width: 60em; padding: var(--gutter); } .Grid { --gutterNegative: calc(-1 * var(--gutter)); display: flex; margin-left: var(--gutterNegative); margin-top: var(--gutterNegative); } .Grid-cell { flex: 1; margin-left: var(--gutter); margin-top: var(--gutter); }</pre></div>

    雖然有額外增加的自定義屬性語法,但是相比冗長的代碼完成同樣的事明顯好很多。這里只考慮了三個變量,如果變量越多,這將節省更多的代碼。

    下面的演示使用的是上面的代碼自動構建的一個基本的網站布局,gutter的值跟隨窗口的變化而變化,瀏覽器的支持自定義屬性的話,效果屌屌的!

    View the demo on CodePen: editor view / full page

    </div>

    語境樣式

    語境樣式(樣式元素根據它出現在Dom)在CSS里是一個有爭議的話題。 一方面,它是最受人尊敬的CSS開發者警告,另一方面,大多數人每天都還要用它。

    Harry Roberts 最近寫了 這篇 文章以及他對此的看法:

    If you need to change the cosmetics of a UI component based on where it is placed, your design system is failing…Things should be designed to be ignorant; things should be designed so that we always just have “this component” and not “this component when inside…

    </div>

    當我站在Harry這一邊,我認為大多數人走捷徑這種情況可能表面一個更大的問題:CSS 表現能力是有限的,大部分人不滿意當前的“最佳實踐”。

    下面例子顯示了大部分人在CSS使用語境樣式方法,使用子代選擇器

    <code><span class="comment">/ Regular button styles. /</span>
    <span class="variable">.Button</span> { }

    <span class="comment">/ Button styles that are different when inside the header. /</span> <span class="variable">.Header</span> <span class="variable">.Button</span> { }</code></pre></div>

    這種方法有很多問題(在我的文章有 解釋 ),這種模式一個代碼味道,它違反了 open/closed 軟件開發原則;修改了一個封閉組件的實現細節

    軟件體 (類, 模塊, 函數等) 擴展開放, 對修改關閉。

    </div>

    自定義屬性的改變范圍式定義組件是一個有趣的方式,用自定義屬性,我們可以在第一次就寫一個實際上是開放擴展的組件,這里有一個例子:

    .Button {
      background: var(--Button-backgroundColor, #eee);
      border: 1px solid var(--Button-borderColor, #333);
      color: var(--Button-color, #333);
      / ... /
    }

    .Header { --Button-backgroundColor: purple; --Button-borderColor: transparent; --Button-color: white; }</pre></div>

    這和子選擇器之間的區別很微妙而且很重要。

    當使用子選擇器我們宣傳在頁眉按鈕會這樣,這樣不同的按鈕如何定義自己,這樣的聲明是獨裁(借Harry’s 的詞),很難撤銷例外的情況,頁眉的一個按鈕不需要這樣的方式。

    另外,自定義屬性,按鈕組件仍是沒有語境且不能完全與header 組件解耦,

    按鈕組件簡單的說申明:無論它們現狀如何,我要自己的風格基于這些自定義屬性;

    header 組件:我要設置這些屬性值,由我的子代來確定和如何使用它們。

    </div>

    主要的區別是,該擴展由按鈕組件選擇,并輕易消除例外情況。

    下面的演示說明了語境樣式的鏈接和按鈕在網站的標題及內容區

    在CodePen查看demo: editor view / full page

    </div>

    創建例外

    如果像 .promo 的組件加到header,然后buttons又加到 .promo 里面,使其看起來像一個正常按鈕,而不是標題按鈕。

    如果你用子代選擇器,那你將要給header buttons寫一大串樣式,而且還不能影響promo buttons,混亂,容易出錯,而且容易失控的數量會增加:

    /* Regular button styles. */
    .Button { }
     /* Button styles that are different when inside the header. */
    .Header .Button { }
     /* Undo button styles in the header that are also in promo. */
    .Header .Promo .Button { }

    使用自定義屬性,你可以簡單的更新任何你想要的新按鈕屬性,或重置他們回默認樣式,無視這些例外,改變的方式總是相同的。

    .Promo {
      --Button-backgroundColor: initial;
      --Button-borderColor: initial;
      --Button-color: initial;
    }

    跟React學

    當我第一次探索自定義屬性語境樣式的時候,我很懷疑自己。像前面說的,我傾向于喜歡組件自己定義自己的變化,而不是任何屬性都繼承自父元素。

    但是有一件事,動搖了我在CSS自定義屬性的觀點,那就是React的 props

    React的 props 依然是動態的,DOM-scoped variables,他們繼承,允許組件上下文關聯,在React,父組件將數據傳遞給子組件,然后子組件定義 props ,他們愿意接受和使用它們。這種建筑模型通常被稱為one-way data flow。

    盡管自定義組件是全新的未測試的領域,我認為React model 給了成功的信心,一個復雜的系統可以建立在屬性繼承——此外,DOM-scoped variables 是一個非常有用的設計模式。

    最大限度的減少副作用

    CSS 自定義屬性繼承默認,在某些情況下,這導致組件的樣式可能沒有達到他們的預期。

    在文章上一節中,我提到可以重置單個屬性,這可以防止未知值被應用到元素的子元素:

    .MyComponent {
      --propertyName: initial;
    }

    盡管這不是規范的一部分,——正在討論屬性附[2],這個可以用來重置所有自定義屬性,如果你想白名單幾個屬性,你可以將他們單獨繼承,其他的正常即可:

    .MyComponent {
      / Resets all custom properties. /
      --: initial;

    / Whitelists these individual custom properties / --someProperty: inherit; --someOtherProperty: inherit; }</pre></div>

    管理全局names

    如果你一直關注自定義屬性,那你可能已經注意到本身帶有components-specific前綴的組件,如 --Button-backgroundColor.

    與CSS 大多數名字一樣,自定義屬性是全局,很是有可能將正在使用命名與其他開發團隊的名稱產生沖突。

    有一個簡單的方法可以避免這個問題,就是堅持命名約定,我現在團隊就是這么做的。

    對于更復雜的項目,你可以考慮像 CSS模塊 localifies所有全局名稱,而且他們最近也 表示有興趣 支持自定義屬性。

    結束語

    如果你在閱讀這篇文章之前,不熟悉CSS 自定義屬性,我希望你能給他一個機會。如果你還在懷疑他的必要性,希望我能改變你的想法。

    我敢肯定,自定義屬性能給CSS帶來一系列的強大的功能和面貌,它還有更多的優勢等待我們去發現。

    自定義屬性preprocessor 變量是無可替代的。盡管如此,preprocessor variables 仍然是許多情況下的不二選擇。正因如此,我堅信未來很多網站都會結合使用二者。自定義屬性為動態主題和預處理器變量靜態模板。

    我不認為這是二選一的情況,讓他們相互競爭,就像對手一樣傷害每一個人。

    特別感謝 Addy Osmani 和 Matt Gaunt 審查文章 ,Shane Stephens并及時修復了一些bug才能使demo正常運行,再次感謝。

    腳注:

    1.你可以啟用chrome 的”Experimental Web Platform Features”功能,方法是:地址輸入 about:flags 然后搜索“Experimental Web Platform Features”,然后點擊“開啟”按鈕

    2.使用——屬性(如定制相關樣式元素)是Atkins 在 github comment 提到的,此外,給www-style 發送 建議郵件 ,也會很快得到處理的。

    </div>

    本文原文地址: http://philipwalton.com/articles/why-im-excited-about-native-css-variables/

    </div>

    來自: http://isux.tencent.com/why-im-excited-about-native-css-variables.html

    </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!