在 Web 開發領域,CSS 是使用者介面美觀且實用的關鍵元素。
然而,隨著 Web 應用程式複雜性的增加,CSS 管理已成為一項越來越具有挑戰性的任務。風格衝突、效能下降、維護困難是許多開發者關心的問題。
這些問題是否阻礙了您的專案進度? (圖片來源)
本文深入探討了解決這些問題的新方法,特別是 JS 中的 CSS。
它從 CSS 的歷史背景開始,涵蓋了廣泛的主題從現代樣式方法到未來的設計系統。
文章架構如下:
特別是,本文介紹了稱為 SCALE CSS 方法論和 StyleStack 的新概念,並基於這些概念提出了一個 mincho 項目。它的目標是在 JS 中實現 CSS,對 CSS 友好且可擴展。
本文的最終目的是向開發人員、設計師和其他 Web 專案利害關係人展示更好的樣式解決方案的可能性。
現在,讓我們在正文中深入探討 JS 中 CSS 的世界。這將是一段漫長的旅程,但我希望它能為你帶來新的靈感和挑戰的機會。
JS 中的 CSS 是一種允許您直接在 JavaScript(或 TypeScript)程式碼中編寫 CSS 樣式的技術。
您可以在 JavaScript 檔案中與元件一起定義樣式,而不是建立單獨的 CSS 檔案。
/** @jsxImportSource @emotion/react */ import { css } from "@emotion/react"; const buttonStyles = (primary) => css({ backgroundColor: primary ? "blue" : "white", color: primary ? "white" : "black", fontSize: "1em", padding: "0.25em 1em", border: "2px solid blue", borderRadius: "3px", cursor: "pointer", }); function Button({ primary, children }) { return ( <button css={buttonStyles(primary)}> {children} </button> ); } function App() { return ( <div> <Button>Normal Button</Button> <Button primary>Primary Button</Button> </div> ); }
能夠將其整合到 JavaScript 中當然看起來很方便?
CSS in JS 是在 Facebook 開發者 Vjeux 的題為「React: CSS in JS – NationJS」的演講中介紹的。
CSS-in-JS旨在解決的問題如下:
更具體的問題是什麼?
而JS中的CSS又是如何解決的呢?
我將它們整理在下表中:
Problem | Solution | |
---|---|---|
Global namespace | Need unique class names that are not duplicated as all styles are declared globally | Use Local values as default - Creating unique class names - Dynamic styling |
Implicit Dependencies | The difficulty of managing dependencies between CSS and JS - Side effect: CSS is applied globally, so it still works if another file is already using that CSS - Difficulty in call automation: It's not easy to statically analyze and automate CSS file calls, so developers have to manage them directly |
Using the module system of JS |
Dead Code Elimination | Difficulty in removing unnecessary CSS during the process of adding, changing, or deleting features | Utilize the optimization features of the bundler |
Minification | Dependencies should be identified and reduced | As dependencies are identified, it becomes easier to reduce them. |
Sharing Constants | Unable to share JS code and state values | Use JS values as they are, or utilize CSS Variables |
Non-deterministic Resolution | Style priority varies depending on the CSS loading order | - Specificity is automatically calculated and applied - Compose and use the final value |
Breaking Isolation | Difficulty in managing external modifications to CSS (encapsulation) | - Encapsulation based on components - Styling based on state - Prevent styles that break encapsulation, such as .selector > * |
But it's not a silverbullet, and it has its drawbacks.
Aside from the DevTools issue, it appears to be mostly a performance issue.
Of course, there are CSS in JS, which overcomes these issues by extracting the CSS and making it zero runtime, but there are some tradeoffs.
Here are two examples.
Therefore, pursuing zero(or near-zero) runtime in CSS-in-JS implementation methods creates a significant difference in terms of expressiveness and API.
Where did CSS come from?
Early web pages were composed only of HTML, with very limited styling options.
<p><font color="red">This text is red.</font></p> <p>This is <strong>emphasized</strong> text.</p> <p>This is <em>italicized</em> text.</p> <p>This is <u>underlined</u> text.</p> <p>This is <strike>strikethrough</strike> text.</p> <p>This is <big>big</big> text, and this is <small>small</small> text.</p> <p>H<sub>2</sub>O is the chemical formula for water.</p> <p>2<sup>3</sup> is 8.</p>
For example, the font tag could change color and size, but it couldn't adjust letter spacing, line height, margins, and so on.
You might think, "Why not just extend HTML tags?" However, it's difficult to create tags for all styling options, and when changing designs, you'd have to modify the HTML structure itself.
This deviates from HTML's original purpose as a document markup language and also means that it's hard to style dynamically.
If you want to change an underline to a strikethrough at runtime, you'd have to create a strike element, clone the inner elements, and then replace them.
const strikeElement = document.createElement("strike"); strikeElement.innerHTML = uElement.innerHTML; uElement.parentNode.replaceChild(strikeElement, uElement);
When separated by style, you only need to change the attributes.
element.style.textDecoration = "line-through";
If you convert to inline style, it would be as follows:
<p style="color: red;">This text is red.</p> <p>This is <span style="font-weight: bold;">bold</span> text.</p> <p>This is <span style="font-style: italic;">italic</span> text.</p> <p>This is <span style="text-decoration: underline;">underlined</span> text.</p> <p>This is <span style="text-decoration: line-through;">strikethrough</span> text.</p> <p>This is <span style="font-size: larger;">large</span> text, and this is <span style="font-size: smaller;">small</span> text.</p> <p>H<span style="vertical-align: sub; font-size: smaller;">2</span>O is the chemical formula for water.</p> <p>2<span style="vertical-align: super; font-size: smaller;">3</span> is 8.</p>
However, inline style must be written repeatedly every time.
That's why CSS, which styles using selectors and declarations, was introduced.
<p>This is the <strong>important part</strong> of this sentence.</p> <p>Hello! I want to <strong>emphasize this in red</strong></p> <p>In a new sentence, there is still an <strong>important part</strong>.</p> <style> strong { color: red; text-decoration: underline; } </style>
Since CSS is a method that applies multiple styles collectively, rules are needed to determine which style should take precedence when the target and style of CSS Rulesets overlap.
CSS was created with a feature called Cascade to address this issue. Cascade is a method of layering styles, starting with the simple ones and moving on to the more specific ones later. The idea was that it would be good to create a system where basic styles are first applied to the whole, and then increasingly specific styles are applied, in order to reduce repetitive work.
Therefore, CSS was designed to apply priorities differently according to the inherent specificity of CSS Rules, rather than the order in which they were written.
/* The following four codes produce the same result even if their order is changed. */ #id { color: red; } .class { color: green; } h1 { color: blue; } [href] { color: yellow; } /* Even if the order is changed, the result is the same as the above code. */ h1 { color: blue; } #id { color: red; } [href] { color: yellow; } .class { color: green; }
However, as CSS became more scalable, a problem arose..
Despite the advancements in CSS, issues related to scalability in CSS are still being discussed.
In addition to the issues raised by CSS in JS, several other obstacles exist in CSS.
These issues can be addressed as follows:
Menyatakan reka letak ialah satu lagi halangan dalam CSS, yang dibuat lebih kompleks dengan interaksi antara pelbagai sifat.
CSS mungkin kelihatan mudah di permukaan, ia tidak mudah untuk dikuasai. Umum mengetahui bahawa ramai orang bergelut walaupun dengan penjajaran tengah yang mudah(1, 2). Kesederhanaan CSS yang jelas boleh mengelirukan, kerana kedalaman dan nuansanya menjadikannya lebih mencabar daripada yang kelihatan pada mulanya.
Sebagai contoh, paparan dalam CSS mempunyai model reka letak yang berbeza: blok, sebaris, jadual, lentur dan grid.
Bayangkan kerumitan apabila sifat berikut digunakan dalam kombinasi: model kotak, Reka bentuk responsif, Terapung, Kedudukan, transformasi, mod penulisan, topeng, dll.
Apabila skala projek meningkat, ia menjadi lebih mencabar kerana kesan sampingan yang berkaitan dengan Penempatan DOM, lata dan kekhususan.
Isu reka letak harus ditangani melalui rangka kerja CSS yang direka dengan baik, dan seperti yang dinyatakan sebelum ini, menggunakan CSS dalam JS untuk mengasingkan gaya boleh mengurangkan kesan sampingan.
Bagaimanapun, pendekatan ini tidak menyelesaikan semua masalah sepenuhnya. Pengasingan gaya boleh membawa kepada kesan sampingan baharu, seperti saiz fail meningkat disebabkan oleh pengisytiharan gaya pendua dalam setiap komponen atau kesukaran dalam mengekalkan konsistensi gaya biasa merentas aplikasi.
Ini secara langsung bercanggah dengan reka bentuk isu letupan dan konsistensi gabungan yang akan diperkenalkan seterusnya.
Buat masa ini, kami boleh mewakilkan kebimbangan reka letak kepada rangka kerja seperti Bootstrap atau Bulma dan memfokuskan lebih pada aspek pengurusan.
Pada terasnya, CSS ialah alat yang berkuasa untuk menyatakan dan melaksanakan reka bentuk dalam pembangunan web.
Terdapat banyak faktor yang perlu dipertimbangkan semasa mencipta UI/UX dan elemen berikut adalah penting untuk diwakili dalam reka bentuk anda:
Menyatakan pelbagai elemen reka bentuk dengan tepat merentas pelbagai keadaan memberikan cabaran yang ketara.
Pertimbangkan bahawa anda perlu mengambil kira peranti (telefon, tablet, komputer riba, monitor, TV), peranti input (papan kekunci, tetikus, sentuhan, suara), mod landskap/potret, tema gelap/cerah, mod kontras tinggi, pengantarabangsaan (bahasa). , LTR/RTL), dan banyak lagi.
Selain itu, UI yang berbeza mungkin perlu dipaparkan berdasarkan tetapan pengguna.
Oleh itu, Letupan Kombinatorial tidak dapat dielakkan, dan adalah mustahil untuk melaksanakannya satu demi satu secara manual. (sumber imej)
作為代表性範例,請參閱我的 Firefox 主題中標籤欄佈局的定義和編譯。
儘管只考慮作業系統和使用者選項,但大約 360 行的檔案編譯結果達到大約 1400 行。
結論是,有效的設計實作本質上需要可擴展,通常以程式設計方式或透過明確定義的規則集進行管理。
結果是一個用於大規模一致管理的設計系統。
設計系統作為單一事實來源,涵蓋從視覺樣式到 UI 模式和程式碼實現的設計和開發的各個方面。
根據 Nielsen Norman Group 的說法,設計系統包括以下內容:
設計系統應該充當設計師和開發人員的十字路口,支援功能、表單、可訪問性和自訂。
但設計師和開發者的想法不同,視角也不同。
讓我們以組件為鏡頭,來認識設計師和開發者視角的差異! !
設計者也應該決定複選框控制項將使用哪個圖示。
設計師傾向於關注形式,而開發者傾向於關注功能。
對設計師來說,按鈕看起來很吸引人按就是按鈕,而對開發者來說,只要能按就是按鈕。
如果組件更複雜,設計師和開發人員之間的差距可能會進一步拉大。
視覺選項:外觀根據設定的選項而變化,如主要、重音、輪廓、純文字等
狀態選項:外觀會根據狀態和上下文而變化
設計決策:透過元件結構、視覺/狀態選項、視覺屬性(顏色、版式、圖示等)等來決定值。
最終的形式是 Option、State 和 Context 的組合,從而導致上面提到的組合爆炸。
其中,Option 符合設計者的觀點,而 State 和 Context 則不然。
考慮並行狀態、層級結構、守衛等進行狀態壓縮,以返回設計者的角度。
正如您現在可能已經意識到的那樣,創建和維護高品質的 UI 是一項艱苦的工作。
所以狀態管理庫涵蓋了各種狀態,但是樣式是如何管理的呢?
雖然由於解決方案尚未建立,方法論、函式庫和框架不斷出現,但有三個主要範例。
其中,JS 中的 CSS 感覺像是一種範式,使用完全不同的方法來表達和管理樣式。
這是因為JS中的CSS就像機制,而語意CSS和原子CSS就像策略。
由於這種差異,JS 中的 CSS 需要與其他兩種方法分開解釋。 (圖片來源)
在討論 JS 機制中的 CSS 時,可能會想到 CSS 前/後處理器。
同樣,在談論政策時,可能會想到「CSS 方法」。
因此,我將按照以下順序介紹樣式管理方法:JS中的CSS、處理器、語義CSS和原子CSS以及其他樣式方法。
那麼,CSS在JS中的真實身分是什麼?
答案就在上面的定義中。
用 JavaScript 寫 和 為每個元件單元隔離 CSS。
其中,CSS隔離可以充分應用於現有CSS,解決全域命名空間和破壞隔離問題。
這是 CSS 模組。
根據上面提到的CSS in JS分析文章的鏈接,我將功能分類如下。
每個功能都有權衡,這些是在 JS 中創建 CSS 時重要的因素。
特別值得注意的內容是SSR(伺服器端渲染)和RSC(React Server Component)。
這些是代表前端的React和NEXT所追求的方向,它們很重要,因為它們對實現有重大影響。
伺服器端渲染在伺服器上建立 HTML 並將其傳送到客戶端,因此需要將其提取為字串,並且需要對串流進行回應。與樣式組件的範例一樣,可能需要額外的設定。 (圖片來源)
Server components have more limitations. [1, 2]
Server and client components are separated, and dynamic styling based on props, state, and context is not possible in server components.
It should be able to extract .css files as mentioned below.
As these are widely known issues, I will not make any further mention of them.
The notable point in the CSS output is Atomic CSS.
Styles are split and output according to each CSS property.