Dans le monde du développement Web, CSS est un élément clé pour rendre les interfaces utilisateur belles et fonctionnelles.
Cependant, à mesure que la complexité des applications Web augmente, la gestion CSS est devenue une tâche de plus en plus difficile. Les conflits de style, la dégradation des performances et les difficultés de maintenance préoccupent de nombreux développeurs.
Ces problématiques freinent-elles l'avancée de vos projets ? (source de l'image)
Cet article approfondit les nouvelles approches pour résoudre ces problèmes, en particulier CSS dans JS.
En commençant par le contexte historique du CSS, il couvre un large éventail de sujets depuis les méthodes de style modernes jusqu'aux futurs systèmes de conception.
La structure de l'article est la suivante :
En particulier, cet article introduit de nouveaux concepts appelés méthodologie SCALE CSS et StyleStack, et propose un projet mincho basé sur ceux-ci. Il vise à implémenter du CSS dans JS qui soit compatible avec CSS et évolutif.
Le but ultime de cet article est de présenter la possibilité de meilleures solutions de style aux développeurs, concepteurs et autres parties prenantes du projet Web.
Maintenant, approfondissons le monde du CSS dans JS dans le texte principal. Ce sera un long voyage, mais j'espère qu'il vous apportera une nouvelle inspiration et des opportunités de défi.
CSS en JS est une technique qui vous permet d'écrire des styles CSS directement dans votre code JavaScript (ou TypeScript).
Au lieu de créer des fichiers CSS séparés, vous pouvez définir des styles aux côtés des composants dans vos fichiers JavaScript.
/** @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> ); }
Pouvoir l'intégrer à JavaScript semble certainement pratique ?
Le CSS dans JS a été introduit à partir d'une présentation intitulée « React : CSS in JS – NationJS » par Vjeux, un développeur Facebook.
Les problèmes que CSS-in-JS visait à résoudre étaient les suivants :
Quel est le problème plus précisément ?
Et comment CSS dans JS le résout-il ?
Je les ai organisés dans le tableau suivant :
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:
레이아웃을 표현하는 것은 CSS의 또 다른 장애물이며, 다양한 속성 간의 상호 작용으로 인해 더욱 복잡해졌습니다.
CSS는 표면적으로 단순해 보일 수 있지만 익히기가 쉽지 않습니다. 단순한 중심 정렬에도 많은 사람들이 어려움을 겪는다는 사실은 잘 알려져 있습니다(1, 2). CSS의 겉보기 단순성은 그 깊이와 뉘앙스로 인해 처음에 보이는 것보다 더 어려워지기 때문에 기만적일 수 있습니다.
예를 들어 CSS의 디스플레이에는 블록, 인라인, 테이블, 플렉스, 그리드 등 다양한 레이아웃 모델이 있습니다.
상자 모델, 반응형 디자인, 부동 소수점, 위치 지정, 변환, 쓰기 모드, 마스크 등의 속성을 조합하여 사용할 때의 복잡성을 상상해 보십시오.
프로젝트 규모가 커질수록 DOM 포지셔닝, 캐스케이딩, 특이성과 관련된 부작용으로 인해 더욱 어려워집니다.
레이아웃 문제는 잘 디자인된 CSS 프레임워크를 통해 해결해야 하며, 앞서 언급한 것처럼 JS에서 CSS를 사용하여 스타일을 분리하면 부작용을 완화할 수 있습니다.
그러나 이 접근 방식이 모든 문제를 완전히 해결하는 것은 아닙니다. 스타일 분리는 각 구성 요소의 중복 스타일 선언으로 인해 파일 크기가 커지거나 애플리케이션 전체에서 공통 스타일의 일관성을 유지하기가 어렵다는 등의 새로운 부작용을 초래할 수 있습니다.
이는 다음에 소개할 디자인 조합폭발과 일관성 문제와 정면으로 충돌합니다.
지금은 레이아웃 문제를 Bootstrap이나 Bulma와 같은 프레임워크에 위임하고 관리에 더 집중할 수 있습니다.
기본적으로 CSS는 웹 개발에서 디자인을 표현하고 구현하는 강력한 도구입니다.
UI/UX를 만들 때 고려해야 할 요소는 다양하며, 디자인에서 표현하는 데 중요한 요소는 다음과 같습니다.
다양한 조건에 걸쳐 다양한 디자인 요소를 정확하게 표현하는 것은 중요한 과제입니다.
장치(휴대폰, 태블릿, 노트북, 모니터, TV), 입력 장치(키보드, 마우스, 터치, 음성), 가로/세로 모드, 어두운/밝은 테마, 고대비 모드, 국제화(언어)를 고려해야 한다는 점을 고려하세요. , LTR/RTL) 등이 있습니다.
또한 사용자 설정에 따라 다른 UI가 표시되어야 할 수도 있습니다.
그러므로 조합 폭발은 불가피하며, 수동으로 하나씩 구현하는 것은 불가능합니다. (이미지 출처)
대표적인 예로 내 Firefox 테마의 탭 표시줄 레이아웃 정의 및 편집을 참조하세요.
OS와 사용자 옵션만 고려하더라도 약 360줄의 파일이 약 1400줄에 달하는 컴파일 결과를 낳는다.
결론은 효과적인 디자인 구현은 본질적으로 확장 가능해야 하며 일반적으로 프로그래밍 방식으로 또는 잘 정의된 규칙 세트를 통해 관리된다는 것입니다.
그 결과 규모에 맞게 일관되게 관리할 수 있는 설계 시스템이 탄생했습니다.
디자인 시스템은 시각적 스타일부터 UI 패턴 및 코드 구현에 이르기까지 디자인 및 개발의 모든 측면을 포괄하는 정보의 단일 소스 역할을 합니다.
Nielsen Norman Group에 따르면 디자인 시스템에는 다음이 포함됩니다.
디자인 시스템은 기능성, 형태, 접근성, 맞춤화를 지원하는 디자이너와 개발자의 교차로 역할을 해야 합니다.
하지만 디자이너와 개발자는 다르게 생각하고 관점이 다릅니다.
컴포넌트를 렌즈로 삼아 디자이너와 개발자의 관점 차이를 인식해보자!!
디자이너는 체크박스 컨트롤에 사용할 아이콘도 결정해야 합니다.
디자이너는 형태에 집중하는 경향이 있고, 개발자는 기능에 집중하는 경향이 있습니다.
디자이너의 경우 버튼은 누르기 좋아 보이는 버튼인 반면, 개발자의 경우 누를 수 있는 버튼은 버튼입니다.
컴포넌트가 복잡해지면 디자이너와 개발자의 격차가 더욱 벌어질 수 있습니다.
시각적 옵션: 기본, 악센트, 윤곽선, 텍스트 전용 등 설정된 옵션에 따라 모양이 변경됩니다.
상태 옵션: 상태와 상황에 따라 모양이 달라집니다
디자인 결정: 구성요소 구조, 시각적/상태 옵션, 시각적 속성(색상, 타이포그래피, 아이콘 등) 등을 통해 값 결정
최종 형태는 Option, State, Context의 조합으로 위에서 언급한 조합 폭발을 초래합니다.
이 중 Option은 디자이너의 관점에 부합하지만 State와 Context는 그렇지 않습니다.
디자이너 관점으로 돌아가기 위해 병렬 상태, 계층 구조, 가드 등을 고려하여 상태 압축을 수행합니다.
지금쯤이면 아시겠지만 고품질 UI를 만들고 유지하는 것은 어려운 일입니다.
그렇다면 상태 관리 라이브러리에서 다양한 상태를 다루고 있는데 스타일 관리는 어떻게 이루어졌나요?
솔루션이 아직 확립되지 않아 방법론, 라이브러리, 프레임워크가 계속해서 등장하고 있지만 세 가지 주요 패러다임이 있습니다.
이중 JS의 CSS는 스타일을 표현하고 관리하는 방식이 근본적으로 다른 패러다임처럼 느껴집니다.
JS의 CSS는 메커니즘과 같고 Semantic CSS와 Atomic CSS는 정책과 같기 때문입니다.
이러한 차이점으로 인해 JS의 CSS는 다른 두 가지 접근 방식과 별도로 설명되어야 합니다. (이미지 출처)
JS 메커니즘의 CSS를 논의할 때 CSS 사전/사후 프로세서가 떠오를 수 있습니다.
마찬가지로 정책을 이야기할 때 'CSS 방법론'이 떠오를 수도 있습니다.
그러므로 스타일 관리 방법을 다음 순서로 소개하겠습니다: JS의 CSS, 프로세서, Semantic CSS 및 Atomic CSS, 기타 스타일 방법론
그렇다면 JS에서 CSS의 정체는 무엇일까요?
그 답은 위의 정의에 있습니다.
각 구성 요소 단위는JavaScript로 작성하고 CSS를 분리
합니다.
이 중 CSS 격리를 기존 CSS에 충분히 적용하면 전역 네임스페이스 및 격리 해제 문제를 해결할 수 있습니다.
CSS 모듈입니다.
위에서 언급한 JS 분석 기사의 CSS 링크를 바탕으로 기능을 다음과 같이 분류했습니다.
각 기능에는 장단점이 있으며 이는 JS에서 CSS를 생성할 때 중요한 요소입니다.
특히 주목할만한 콘텐츠는 SSR(Server Side Rendering)과 RSC(React Server Component)일 것입니다.
이는 프론트엔드를 대표하는 React와 NEXT가 지향하는 방향이며, 구현에 지대한 영향을 미치기 때문에 중요합니다.
서버사이드 렌더링은 서버에서 HTML을 생성해 클라이언트로 보내기 때문에 문자열로 추출해야 하고, 스트리밍에 대한 응답이 필요합니다. Styled Component의 예시와 같이 추가적인 설정이 필요할 수 있습니다. (이미지 출처)
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.