This is the second day of my participation in Gwen Challenge

Developers focus on using technology to create things, moving forward in technological innovation and product iteration; Enthusiasts have a different perspective, but the strangeness of the industry inspires curiosity, want to try to find out the development of this thing from ancient times to the present.

CSS specification development

In 1995, the World Wide Web Consortium (W3C) was established to organize working groups and promote the development of Web technical standards and implementation guidelines.

CSS 1 was released at the end of 1996, CSS 2 was released in 1998, and CSS 2.1 was released in 2011, but IT took nine years for CSS 2 to reach overall recommended status, mainly due to minor features that dragged on. So the W3C decided to split CSS features into modules, each standardized independently, with a “version” better known as Level. The W3C no longer publishes general standards such as CSS3 or CSS4. Instead, it periodically defines a Snapshot that includes the latest maturity Level of each module, such as the latest snapshot CSS Snapshot 2020 (W3.org).

Specification formulation process

Developing a CSS specification goes through the following stages:

  1. Editor’s Draft (ED) : Ideas are proposed by the specification editor
  2. First Public draft (FPWD) : The first public release of a specification that is formally presented and ready for working group and community feedback
  3. Working draft (WD) : Browsers start early implementations based on WD, specification editors receive feedback from working groups and the community, and release version after version of WD iterative improvements to the specification.
  4. Candidate Recommendation specification (CR) : When the specification reaches a relatively stable state, it is more suitable for implementation and testing. Each feature in the specification must have two separate implementations and full testing before it can move to the next stage
  5. Nomination and Recommendation Specification (PR) : A preview of the specification before its official release and a final opportunity to voice objections to the specification
  6. Formal Recommended Specification (REC) : Release the final version of the specification

Unlike the ECMAScript specification development process, the CSS specification starts browser implementation at the draft stage. The draft phase is the active discussion period for the specification, and reaching the REC phase means that the current level of the specification is fixed and new improvements need to be proposed as the next level.

Browser prefix scheme

Draft versions are used in production to get developer feedback, but the final version is not expected to be tied down. Therefore, a way is needed to separate the draft version from the final version.

In the past, the browser prefix scheme has been used to separate experimental features. When a browser implements an experimental feature, it adds its own prefix to the name of the feature.

Each browser can implement these experimental (or even proprietary, non-standard) features, but with its own unique prefix in front of the name. The most common prefixes are -moz- for Firefox, -ms- for IE, -o- for Opera, and -webkit- for Safari and Chrome. Web developers are free to experiment with these prefixed features and feed the results back to the working group, which then incorporates the feedback into the specification and gradually refines the design of the feature. Since the final standardized version will have a different name (without a prefix), it will not conflict with the prefixed version in practice.

— From CSS Revealed

However, the browser prefix scheme is not a perfect solution because it restricts the syntax of the final version and is prone to abuse by developers until a stable version is established.

Experimental Features

Now the prefix scheme is gradually replaced by “experimental function scheme”. Developers enable experimental features by configuring switches to avoid abuse in production environments and to try out new features in “personal LABS.”

Such as the :dir pseudo-class in the WD phase, which matches elements in the text direction. From CSS text – the justify | Can I use it Can be seen that in the current default Firefox browser support, and the latest version of Chrome with a small green flag logo, representing the feature in Chrome as the implemented experimental functions exist, Can open the use, Hover to the version block will prompt the start mode.

The entry for setting experimental functions in different browsers is different:

  • Chrome: Chrome: / / flags
  • Edge: Edge: / / flags
  • Firefox: about: config
  • Safari: Develop -> Experimental Features from the top menu on MacOS, iOS/iPadOS Setup -> Safari -> Advanced -> Experimental Features

Problems of the CSS

Some things to consider when applying CSS:

  • How to ensure browser compatibility: Forward and backward compatibility based on browser support
  • How to keep your code DRY: Don’t Repeat Yourself, “When changing a parameter, change as few things as possible, preferably one,” is a key step to improving your code’s maintainability

The simple design of CSS can be adapted to early web needs, but in today’s world of increasingly complex front-end engineering, some features of native CSS can be a barrier to “reducing complexity.”

How to ensure browser compatibility

Query the CSS supported by the browser

  • caniuse.com/Support query CSS, HTML5, SVG, JS Syntax, JS API and other Web technology platform compatibility, caniuse source data in open source projectsFyrd/caniuseIn the maintenance,autoperfixerbrowserslistWorking with this source data;
  • MDN is also a reliable query resource

Use cascade mechanism to implement rollback

Prefixed features work in older browsers. Use Autoprefixer to automatically generate browser prefixes according to browser automaticity.

Also, if necessary, provide a safe fallback mechanism to make sure your site doesn’t die on older browsers, but just doesn’t look as cool.

background: rgb(255.128.0);
background: -webkit-linear-gradient(0deg,yellow,red);
background: linear-gradient(0deg,yellow,red);
Copy the code

Detection features added auxiliary classes

Check whether the current browser supports CSS features and handle the two cases separately.

  • Use Modernizr as an option
  • For more than@supportUpdated features that can be used@supportdetection
  • You can also write your own JavaScript code
    • Detect properties: whether thedocument.documentElement.styleThere is a
    • Detect attribute values: Tests on hidden elements

Polyfill?

New ES features can be polyfilled to simulate compatibility. Is there a similar compatibility solution for CSS? There are postCSS-PRESET – Env and CSS Houdini

  • postcss-preset-envUsing PostCSS and CSSDB, analyze AST and polyfill to regenerate the code
  • CSS Houdini relies on the underlying API provided by the browser to function on CSSOM. Developers can use the underlying API to polyfill new CSS features.

However, the current situation is that postCSS-PRESET – ENV and CSSDB have not been updated for more than a year, and the project seems to have some difficulties in maintenance, but some old “new features” are still applicable; CSS Houdini Is still a work in progress, and major browsers have implemented some apis (data from Is Houdini Ready Yet?) .

CSS Houdini as a W3C standard in the future is a major compatibility killer. But for now, it’s more realistic and safe to choose features that are already widely implemented and use AutopreFixer for backward compatibility.

It can only be used in the production environment after browsers are widely implemented, which is also the reason why the implementation of new CSS features is slower than that of ES features. Therefore, it can be seen that the use of CSS Houdini is a key step to promote the development of CSS.

Want to learn about CSS Houdini? You’ve heard of CSS in JS, and JS in CSS? (juejin.cn)

How do I keep my code DRY

The best feature of CSS (Cascading Style Sheets) is “Cascading”, which gives stylesheets the ability to overlay and compose. In the past, you tried to achieve DRY by using relative units, using transparency overlays, and so on.

But CSS is essentially a tiled list of rule declarations, and unlike regular programming languages, it has no logical, reusable, or nested programming capabilities, meaning it has limited room for optimization. CSS preprocessors are designed to solve this problem.

Sass/Less/Stylus

Sass, Less, Stylus provide programming features such as variables, nested selectors, mixins, inheritance, functions, and more. CSS preprocessors work a bit like TypeScript in that they build a superset on top of the native language and use the compiler to convert the superset code into native code.

Sass was originally implemented (in 2006) in Ruby, largely to cater to the Ruby ecosystem that was emerging at the time, so the initial use of Sass required a Ruby environment to be configured.

As Node.js became ubiquitous in front-end development and Ruby faded into the background, the Sass team began to think about breaking away from the programming language and created LibSass, the Sass engine implemented in C/C++. LibSass can be wrapped in different programming languages to suit different locales, such as Node-sass wrapped in Node.js. For Node-sass to work, it relies on Node-gyp to build binaries into code that can be executed by Node.js, and requires additional binaries to be downloaded from the SASS binary resource site. Additional “remote” requests often end in failure. Therefore, configuring the Sass_binary_site and miley L variables is also routine.

As of now, Sass is implemented using Dart. Dart will always be associated with Flutter, but Dart as a programming language is portable. It can be compiled to ARM and x86 code, and converted to JavaScript code. Because of this, Dart Sass is now compiled into pure JavaScript code and released as a Sass package on NPM. Dart Sass takes the hassle out of building binaries while avoiding platform constraints.

Less is determined from the start to use JavaScript and stick to the SYNTAX of CSS, running both on Node.js and in the browser.

Stylus, born out of the Node.js community, isn’t much of a brusque Stylus — you can do without parentheses and semicolons, but it has plenty of tools to help developers achieve DRY.

Sass, Less and Stylus have similar functions and have their own user groups. Weekly NPM downloads are 4,669,138, 3,432,039 and 2,276,929, respectively. Sass is still better in terms of user numbers, and the preprocessor market seems to be in a stable saturation state.

PostCSS

PostCSS is different from the above. It does not provide special services. Instead, it is responsible for parsing CSS rules into PostCSS AST, and provides apis for plug-ins to add, delete, change, and search trees, and then regenerate CSS code. PostCSS provides the translation framework, and plug-ins take care of the hands-on tree modification. Compared to a precompiler for language extensions like Sass, PostCSS can handle a wider range of transactions under the plugin system, such as:

  • Resolve compatibility issues: Postcss/Autoprefixer automatically adds prefixes
  • Code optimization: CSSNano code compression tool
  • Code check: stylelint

PostCSS not only parses CSS files, but also handles CSS syntax in other files. For example, postCSs-HTML parses the styles of

Native CSS support

The CSS specification also makes some advances in programmability, such as:

  • Custom properties: Custom properties are the variables of the standard preprocessor. But unlike preprocessed variables, custom attributes have native advantages:

    • Evaluate dynamically at run time
    • Has the same cascading properties as normal properties

    CSS Custom Properties for Cascading Variables Module Level 1 (w3.org)

  • Calculation function: CALC /min/ Max /clamp, which is realized by fixed value calculation in the preprocessor, while the native calculation function is dynamic calculation, which also handles relative value and absolute value calculation (CALC (100%+10px))

The “Flaws” of CSS

As front-end engineering gets more complex, some features of native CSS become “bugs” :

  • Global namespace: causing naming conflicts and difficulty in encapsulating local styles;
  • Non-explicit dependencies: The presence of a style definition in the overall style file, which works even if there is no explicit reference in the component, also makes it difficult to separate styles;
  • Garbage style stack: CSS definitions and references are loose, and styles that are no longer used are often forgotten as the project iterates;
  • Class names cannot be compressed: Developers tend to use semantically styled class names, which can become more complex to avoid naming conflicts;
  • Sharing variables with JS: CSS and JS are independent of each other and cannot synchronize their states.
  • Nondeterministic resolution: Whether the same-weight selector takes effect or not depends on the order in which it is defined, and there is uncertainty between the resolution result and the source code order.
  • Break the isolation: the child selector of the parent component also applies to elements in the child component.
  • Critical CSS: The downloading and parsing of CSS resources can block the rendering process. The smaller the CSS file, the easier it is to load on the first screen. One optimization method is to embed important CSS code<style>Other code is downloaded asynchronously. But traditional CSS writing makes it difficult to determine which CSS code is important.

Refer to theChristopher Chedeau 的 React: CSS in JSDemo, if there are mistakes, please correct

The CSS optimization scheme proposed by the community follows the development of the front-end trend, from the unified naming specification to the discussion about Tailwind CSS and CSS in JS, it is the situation of a hundred schools of thought arguing in full bloom.

CSS Management Methods

Through protocol control style management, we must have heard of Atomic CSS, OOCSS, BEM, SMACSS, ITCSS.

  • Atomic CSS (utility-first), meaning atomized CSS, breaks away from semantic style classes and uses basic style classes to create style effects. Reducing duplicate code is an advantage of atomization, while eliminating useless code is the problem that sets of atoms need to solve.

  • Object-oriented CSS (OOCSS) uses the idea of Object orientation to separate a style class into multiple style classes with a single responsibility, so that each style class can be reused. OOCSS relies on human design.

  • BEM provides a naming convention for module names __ element names — modifiers such as navbar__button, navbar__button–active. BEM implements unique class names by convention, and uses class names to define element styles to avoid using child selectors. It can be said that BEM solves two major problems of naming conflict and selector penetration. However, BEM also has its own defects — class names are too long, increasing code volume;

  • SMACSS classifies styles by features: Layout (L -), module (M -), state (IS -), theme (Theme -), and base.

  • ITCSS is used with a preprocessor, which abstracts styles into layers. Through file system management, the hierarchy is divided from bottom to top:

    • Settings: Global variables used by the preprocessor, including font, color, etc.
    • Tools: globally used mixins and functions;
    • Generic: reset/normalize;
    • Base: Custom HTML element default style;
    • Objects: Use class selectors to define design patterns such as media Objects, three-column structures, etc.
    • Components: UI component-specific styles. Components are a combination of Components and Objects
    • Utilities: classes of auxiliary tools, such as:display:hidden,clearfix

    ITCSS abstracts SCSS style files into layers, plus CSS Modules.

The CSS approach above is about “how to manage CSS styles” to control the complexity of continuous iterations. There are several ways to outline the solution: atomization, modularization, and abstraction.

However, the management mode can not solve the problems of useless style accumulation, sharing variables with JS, simplifying compression, Critical CSS, and so on. It seems to be a feasible idea to automate the process of packaging.

CSS Modules

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default.

BEM came up with the idea of modularizing style naming. Is there anything that can be improved?

  • Without true style modularity, the resulting code still piles up, making it difficult to implement Critical CSS
  • Class names are too long and cannot be confusingly simplified

These two problems can be solved by CSS Modules. The key to CSS Modules is to refer to CSS files as Modules explicitly in JS or CSS, and support for packaging tools can solve many problems:

  • The style Module and JS Module load, easy to implement Critical CSS
  • The resulting code package that makes up the module dependency diagram does not contain unreferenced modules, and PurgeCSS is used to remove useless code
  • Class names are easy to compress during the build process, and SourceMap readability is not a problem with matching

In addition to style modularity, CSS Modules provide additional support:

  • Global scope: Local scope is supported by default, but can also be used:globalModify the global scope
  • Code reuse:composes:Combine existing code snippets
  • Manual import and export: The underlying support for CSS Modules isicss: Interoperable CSSIt is an add-on to native CSS:importand:exportTwo pseudo-class selectors. You can also use these selectors to set up additional imports and exports at the CSS Modules level, for example to share variables with JS
  • CSS Modules can also work well with the preprocessor. If the original structure of the project is ITCSS + SCSS, CSS Modules can be easily added: ITCSS specification abstraction layer, SCSS provides programmability support, CSS Modules provide local scope support.

In keeping with the growing maturity of front-end modularity and packaging tools, CSS Modules can be implemented cheaply and effectively on existing projects, which may be why they are easy to adopt.

Tailwind CSS

A utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup.

Advantages of Atomic CSS: easy static maintenance, high code reuse, small size of code. There are two ways to realize Atomic CSS: one is dynamic atomization, that is, using tools to automatically split the style classes defined by developers into Atomic classes, which is the scheme adopted by Atomic CSS in JS; The other is that given a huge set of base atoms and a reliable garbage removal tool, developers can assemble them from that set, which is what Tailwindcss does.

TailwindCSS implements useless code removal through PurgeCSS, while providing a large number of utility classes, custom configurations, complete documentation, and VSCode plug-in TailwindCSS IntelliSense, maintaining active project maintenance and community satisfaction. Is a great CSS framework (good things, high user satisfaction).

TailwindCSS highlights “atomization support” and “design system constraints”. But TailwindCSS doesn’t cover all CSS “flaws” :

  • Cannot share constants with JS
  • Tailwindcss is atomically fine-grained, with plugin tips, so it’s not a problem because it doesn’t reuse the same set of style classes in the same element.

If you are interested in learning about the implementation of PurgeCSS, please refer to this articleTailwind generated a 3.7MB CSS file! Is this still working?

CSS in JS

How to synchronize the state of JS and CSS is a problem worth thinking about. In the traditional form of JS and CSS being maintained independently, the two can’t be connected. CSS Modules provide some connectivity mechanisms:

  • From CSS to JS: CSS passes:exportShare variables with JS
  • From JS to CSS: JS modifies custom properties that share variables with CSS and dynamically modifies elements when state changesclassname

React/Vue frameworks implement the state-driven DOM. How about state-driven styles? At the same time, CSS integration into JS can also solve the “defects” of CSS itself.

CSS in JS is the practice of this idea. Styles are defined in JS files, and CSS, JS, and HTML are managed in the same component. There are many implementation frameworks of CSS-IN-JS. There are several existing schemes:

  • styled-components 和 EmotionAt runtime JavaScript dynamically generates unique style classes based on state (add browser prefix, hash class name) and then add<style>Tags and updates elementsclassname. Completely break down the barrier between CSS and JS. The disadvantages are the additional runtime code required and the run-time performance penalty.
  • Linaria: No runtime, static CSS files are generated at compile time, JS states are compiled as CSS custom properties.

Atomic CSS-in-JS

Atomic CSS alone cannot solve the problem of JS and CSS state sharing, and the biggest advantage of CSS-in-JS is this.

Atomic CSS has the advantages of being static and easy to maintain, and of having small overall code size that no other solution can offer.

Combine the two, biu~ : Atomic CSS-in-js. Atomic CSS-in-JS is an atomization of CSS-in-JS. The developer writes the code in the original way, and the tool compiles the code to Atomic state.

Here’s the code

const Button = createComponent(() = > ({
  display: 'block'.border: 'none'.padding: '1.25 rem 1 rem'.borderRadius: '0.25 rem'.backgroundColor: '#BBB'.color: 'white',}))const Block = createComponent(() = > ({
  display: 'block'.borderRadius: '0.25 rem',}))export default() = > (<Provider renderer={renderer}>
    <div>
      <Button>Hello</Button>
      <Block>World</Block>
    </div>
  </Provider>
);
Copy the code

Finally compiled to

<div>
  <div class="a b c d e f">Hello</div>
  <div class="a d">World</div>
</div>
Copy the code
.a{display:block}
.b{border:none}
.c{padding:1.25 rem 1rem}
.d{border-radius:0.25 rem}
.e{background-color:#BBB}.f{color:white}
Copy the code

Libraries that support Atomic CSS-IN-JS include:

  • Fela
  • styletron
  • Stitches

If you are concerned that the style of Atomic css-in-js is too free to take advantage of the Tailwindcss constrained design System for Atomic sets, you can build your own Atomic sets based on the Atomic css-in-js framework, or create Atomic sets using the Styled System.

Css-in-js is in the stage of active improvement and optimization. At present, it may be awkward to implement, the cost of renovation on the original project is too high, and it is not the first choice for long-term maintenance projects that are not standardized. But the power of state-driven styles is tempting enough to give it a try.

The resources

CSS3 – Archive of obsolete content | MDN (mozilla.org)

CSS Revealed -Lea Verou- wechat Reading (QQ.com)

Sass: Ruby Sass | Sass Chinese website (bootcss.com)

Sass: LibSass | Sass Chinese website (bootcss.com)

Sass: Dart Sass | Sass Chinese website (bootcss.com)

The pros and cons of CSS in JS

Css-in-js: A Controversial Technical Solution – Zhihu (Zhihu.com)

Tailwind generated a whopping 3.7MB CSS file! This still works?

Facebook refactoring: Ditching Sass/Less for atomized CSS

React: CSS in JS

Css-modules/ICSS: Interoperable CSS — a standard for Loadable, linkable CSS (github.com)

CSS -modules/ readme. md at master · CSS -modules/ CSS -modules (github.com)

The State of CSS 2020

An Introduction To Object Oriented CSS (OOCSS) — Smashing Magazine