DevUI is a team with both design and engineering perspectives, serving huawei Cloud DevCloud platform and several huawei internal middle and background systems, as well as designers and front-end engineers. Design Ng component library: Ng-Devui (Welcome Star)
The introduction
Since the introduction of Dark Mode in iOS13, apps and websites have started to support Dark Mode. Before that, dark mode was more common in application IDE development interface and video site interface. The former reduces the brightness of the screen, so that the user’s eyes are not so tired when staring at the screen for a long time; The latter uses a dark mode to reduce noise and thus highlight the main content. Is it hard to develop a dark mode quickly? In modern browsers that support CSS custom properties (also known as CSS variables), this is fairly easy. You can even add themes in real time at run time, getting rid of the disadvantages of traditional CSS theme file loading mode, where themes need to be precompiled and cannot be modified at any time. Let’s take a look at how to use CSS custom properties to complete the development of dark mode and theme.
Theme switcher development
First we need to get through a set of development patterns that support CSS custom properties.
Use custom CSS properties
Here is a brief introduction to CSS custom properties, sometimes referred to as CSS variables or cascading variables. It contains values that can be reused throughout the document. Custom attributes to use – ` ` variable name ` ` : ` ` < span > < / span > to define the variable values, using var (– < span > variable name < / span > [, < / span > < span > default value]) function to get the value. Here’s a simple example:
<! --html--> <div><p>text</p></div> /* css */ div { --my-color: red; border: 1px solid var(--my-color); } p { color: var(--my-color); }Copy the code
The border of the div and the P element inside the div can then use this defined variable to set its own color.
Usually CSS custom attributes need to be defined within an element. By setting custom attributes on the :root pseudo-class, they can be used wherever they are needed throughout the document. CSS variables can be inherited, which means we can create some local topics through CSS inheritance. Instead of discussing local topics here, we just need to use the root pseudo-class to theme the whole site.
P {color: var(– variable 1, color value 1) {color: var(– variable 1, color value 1); } so, CSS custom properties are used to dynamically load different theme color values at run time.
Sass/Less support
If you directly use CSS variables in the development of CSS, it is easy to write problems, definition problems eventually lead to a large number of variables, management difficulties, change the default color value replacement cost high problems. In the development of large websites, sass/ LESS is often used to predefined some color variables for color management.
When using sass and less, you can change the way you pass color values to CSS custom properties and default values. Color definition file:
before
|
after
|
// sass
$brand-primary: #5e7ce0;
// less
@brand-primary: #5e7ce0;
|
// sass
$brand-primary: var(–brand-primary, #5e7ce0);
// less
@brand-primary: var(–brand-primary, #5e7ce0);
|
A side effect of this is that once the color value is defined as a var variable, the var expression can no longer be evaluated by the less/ Sass color evaluation function, which we will discuss in a later section.
After the corresponding variables are defined, they can be used directly where they are used to facilitate unified management.
Using media queries
Prefer-color-scheme is the CSS API for the browser to get the user’s preference for the color theme on the system. Using this API, we can easily make the theme of the website show different colors according to the color setting of the system.
The CSS apis are as follows:
// css@media (Groups-colour-scheme: light) {:root{-- variable 1: colour-value 1; -- Variable 2: color value 2; ... }} @media (Group-color scheme: dark) {:root{-- variable 1: group-color value 3; -- Variable 2: color value 4; ... }}Copy the code
There are also corresponding media query schemes for scripts. The JS API is as follows:
// js
function isDarkSchemePreference(){
return window.matchMedia('screen and (prefers-color-scheme: dark)').matches;
}
Copy the code
Topic switching Service
Finally, we need to write a subject service, the main purpose is to support when switch to apply different CSS variable data, assuming that our CSS variable data is stored in an object, the key value of CSS variable name, the value value of CSS variables under the theme of value, so we switch the theme of the service key core functions are as follows:
// theme.ts export class Theme { id: ThemeId; name: string; data: { [cssVarName: string]: string }; } // theme-service.ts class ThemeService { contentElement; eventBus; / /... applyTheme(theme: Theme) { this.currentTheme = theme; if (! this.contentElement) { const styleElement = document.getElementById('devuiThemeVariables'); if ( styleElement) { this.contentElement = <HTMLStyleElement>styleElement; } else { this.contentElement = document.createElement('style'); this.contentElement.id = 'devuiThemeVariables'; document.head.appendChild(this.contentElement); } } this.contentElement.innerText = ':root { ' + this.formatCSSVariables(theme.data) + ' }'; document.body.setAttribute('ui-theme', this.currentTheme.id); This.notify (theme, 'themeChanged'); } formatCSSVariables(themeData: Theme['data']) { return Object.keys(themeData).map( cssVar => ('--' + cssVar + ':' + themeData[cssVar]) ).join('; '); } private notify(theme: Theme, eventType: string) { if (! this.eventBus) { return; } this.eventBus.trigger(eventType, theme); }}Copy the code
The applyTheme function creates a style element, or changes the content of the style element if it is already created. If additional functions are required to support the following system, we will not expand on them here. The principle is to listen for changes in media queries via animation end events, which can be done using the Enquirejs library.
Now that we’ve got the subject service and CSS variable values in the works, we’re ready to develop a dark mode.
Dark pattern development
Semantic color variables
Dark mode involves a large number of websites visual “inverse color”, in the existing websites, should be well investigated and comb the website color, color normalization and constraints to a certain range and number of variables, and give different scenarios of color use a different semantic variable name, so as to achieve the effect of scene separation.
Let’s take a simple example from text color:
Most websites have text (main text), help messages (secondary text), and text placeholders. We can use three variables to describe the text here: text-color-primary, text-color-secondary, text-color-tertiary, or text-color-normal. The text-color-help-info and text-color-placeholder values describe these colors.
It is strongly recommended to use more semantic variables than the description of the color value itself. For example: error background color, use background-color-danger instead of background-color-red, as the color value may be different for different themes.
FIG. 1 Schematic diagram of semantic variables
Use uniform semantic variables to control component presentation
How many variables to define is appropriate depends on the scope of the site’s color space constraints and the granularity of the definition of the usage scenario. When we define a set of variables we can unify the variables for the different components of the component/site.
For example, the search box and the drop down box use the same variables to control the performance of the same parts, so that the components can use the same color rules when changing the theme.
Figure 2 defines components using variables
Provides a dark theme color value
After completing the important two steps above, we can achieve the theme change by providing a new set of color values for the variables.
Figure 3. Dark theme switching is realized by switching color values
Image processing
Image processing does not invert color or brightness like text, which may not be appropriate. Usually, if you have two sets of images with light and dark colors, you can use the variational image address to cut black images under different themes. If the image is from user input, a screenshot from somewhere else, then you need to do a little bit to reduce the brightness. To simply get the current theme state of the image, you can add a property on the body to determine whether the UI theme is in dark mode.
Dark scheme one: the picture increases transparency. Application scenario: Simple article picture and solid color background.
// css
body[ui-theme-mode='dark'] img {
opacity: 0.8;
}
Copy the code
Dark scheme 2: overlay a gray translucent layer on the position with the picture, applicable to the scene: background image, non-solid color background, etc.
// css
body[ui-theme-mode='dark'] .dark-mode-image-overlay {
position: relative;
}
body[ui-theme-mode='dark'] .dark-mode-image-overlay::before {
content: '';
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(50, 50, 50, 0.5);
}
Copy the code
The former is not suitable for layering with background images, nor is it suitable for rendering effects by overlaying image occlusion, but it is very simple and effective to insert images into posts and blogs, and images can naturally be overlaid on solid dark background colors. The latter gives another way to complete the overlay of background layers, but with some intrusion into the code.
Provide topic change subscriptions for third party component scenarios
Following these basic steps, you can obtain the ability to use variables to specify color values during coding. However, in the face of a large number of third-party components, with their own themes, and may have their own dark themes, it is not always appropriate to intrudingly modify this into a custom variable.
In this case, you need to provide a topic subscription to be notified when a topic changes, and then set the changes to the third party components.
We need a simple EventBus that can be implemented in any way. Here is a simple version of the interface:
// theme/interface.ts
export interface IEventBus {
on(eventName: string, callbacks: Function): void;
off(eventName: string, callbacks: Function): void;
trigger(eventName: string, data: any): void;
}
Copy the code
When switching themes, issue themeChanged, use the on listener to get the current theme change event, determine the theme, attach the theme to third party components, change the JS color variable, etc.
Demote support and use script putty
Demote the PostCSS interpolation script
Once var is used, older browsers that do not support var will be colorless. Here we use the PostCSS plug-in for the last stage of CSS.
// postcss-plugin-add-var-value.js var postcss = require('postcss'); var cssVarReg = new RegExp('var\\\\(\\\\-\\\\-(? :. *?) , (. *?) \\\\)', 'g'); module.exports = postcss.plugin('postcss-plugin-add-origin-css-var-value', () => { return (root) => { root.walkDecls(decl => { if (decl.type ! == 'comment' && decl.value && decl.value.match(cssVarReg)) { decl.cloneBefore({value: decl.value.replace(cssVarReg, (match, item) => item) }); }}); }});Copy the code
The postCSS plug-in inserts a value that replaces the direct variable value by iterating through the var(– variable name, variable value) in the CSS rule, and is compatible with browsers that do not support CSS var.
before
|
after
|
color: var(–brand-primary, #5e7ce0);
|
color: #5e7ce0;
color: var(–brand-primary, #5e7ce0);
|
Css-vars-ponyfill enables IE9+ and Edge 12+ to support topic switching on
Css-vars-ponyfill is an NPM package that enables IE9 +/ Edge12 + to support CSS custom properties. It is a compatible solution with options. The idea is to listen for the value of the custom property with var in style, replace it with the value and insert it. This compatibility scheme is currently not compatible with local CSS custom attribute definitions that hang directly on elements. The solution also provides the option to listen for style inserts in real time, and supports var chained values. Simply add a polyfill and you can use it.
// polyfill.ts
import cssVars from 'css-vars-ponyfill';
cssVars({ watch: true, silent: true});
Copy the code
Discussion of some problems
What websites need to develop dark patterns?
Dark mode is suitable for long time reading and long time immersive browsing of websites, including news, blog, knowledge base and other articles browsing, video sites, development of IDE interface and other immersive interaction. The use of dark patterns on these sites can reduce eye irritation by reducing the brightness, reducing the fatigue and dizziness of prolonged browsing.
The dark color mode is not suitable for the display of some non-dark style products. The deep background color will affect the presentation of product style, the emotion transmitted and the mood of users when watching, and improper color collocation is easy to cause disgust. Like some e-commerce sites dark mode should be carefully handled, dark color may make the product image presented positive style by a certain degree of inhibition, color may affect the user’s desire to buy. The same is true for some thematic sites, where colors may diminish the expression of the theme.
Is there a simpler dark mode mapping toggle? For example, use HSL instead of RGB color values.
HSL color value is expressed by hue, saturation and brightness. Since dark mode is to adjust brightness and saturation, can it be automatically calculated by HSL color value? This kind of automatic dark version of the color value is still to be explored, mainly for two reasons: 1) the comfort of dark mode is not the linear brightness and saturation mapping can be completed, the function of color calculation of dark mapping is relatively monotonous. 2) The reality is that one color may map to multiple dark scene colors.
In view of the first point, some UIs will introduce nonlinear color inversion algorithm at present, which is also to solve the problem that the color becomes hard to see after the brightness is adjusted together, and the impact of color inversion is too large. There’s a lot of room for optimization in these kinds of algorithms. Colors that might look good in lighter colors can be uncomfortable in darker ones: the wrong contrast can make it hard to see; Improper color collision will cause antipathy; Improper saturation and brightness will make the UI a little dirty.
The second point can be illustrated by the following scenario: The same white on a colored background may remain white in dark mode; The background color of white is changed to a darker color in a darker scene.
Figure 4 shows a white map with multiple toggle themes
At this time, the automatic color value calculation needs to distinguish the color of the surrounding color or the underlying superposition color to calculate, which undoubtedly increases the calculation difficulty.
So this piece of automatic computation is not so easy, it needs some exploration.
Sass/Less < var > < Sass/Less > < Sass/Less >
Sass /less’s variables and CSS custom properties are not a system of variables. Sass /less’s are compile-type variables (compile-time values that do not exist after compilation), while CSS is a run-time variable (run-time values). < span style = “font-size: 10.5em; font-size: 10.5em;” For sass and less, var(– XXX, # XXX) is a string and not a color value. At present, there is no good method for this part, and some articles also discuss some solutions, such as linking. The general idea is to split the expression of color into HSL form, and then operate and process the color dimension. In fact, the built-in color transformation function cannot be used without perception. Another solution/solution is to make the color changes uniform and then give the new CSS variable names, instead of changing the color in mixins and other functions and making regular changes to the variable names. If you have other ideas, please share them in the comments.
conclusion
This article has shown that using CSS custom properties to define some color variables for CSS makes it easy to develop dark themes and even support more themes. Through the color variable definition, the use of variables, processing images and processing tripartite components to support the implementation of the whole site of the specification and improvement of dark mode. The methods of degrading support are further introduced, and the scope of application of dark pattern and some other implementations are discussed.
Join us
We are DevUI team, welcome to come here and build elegant and efficient man-machine design/R&D system with us. Recruitment email: [email protected].
The text/DevUI rhlin
Previous article recommended
Cyclomatic Complexity in the Front End
How to Build a Grayscale Publishing Environment
How to Develop Pagination Components using Vue/React/Angular