Introduction. Reasons for the appearance
When the web was just in its infancy, its sole purpose was to host content (hypertext pages) so that users on the World Wide Web had access to it. At that time, there could be no question of design, because why do we need design for pages with scientific publications, unless they will become more useful from this ( first site ). Times are changing and today on the World Wide Web it is not only scientific publications. Blogs, services, social networks and much, much more. Each site needs its own individuality, it needs to interest and attract users. Even scientific sites are gradually realizing this, because most scientists want not only to study certain aspects, but to convey them to people, thereby increasing their popularity and the value of their research (for example, 15 out of 15 scientific siteslist has been redesigned in the last 6 years). Ordinary people are not interested in a gray site with incomprehensible content. Science is becoming more accessible, and sites are being transformed into applications with a convenient and pleasant interface.
Since everyone has their own "convenience" - there is no clear definition and specific rules for the implementation of a service that is convenient for everyone. In recent years, such a concept as Theming has begun to be tied to this concept. It is about him that I want to talk about in this article.
, . , , . , , , . , , Β« Β» Β« Β». , , .
β . . 285 , β 218 [.], 2,2 [.] Β« Β»[.]. . , , . . , β .
.
. , . , , . , , , . , β , . , Β« Β». , β . , , β β , , .. , .
β . β , , , . . , , , .
, . , , . , 5-10 . , . , , . , IE, , ES6. , . , , .
JS , . , HTML CSS. , , . . , , , IE. , . css, 2015 . 2015 β JS, HTTP/2, WebAssembly, ReactJS. , .
css-:
:
:root {
var-header-color: #06c;
}
h1 { background-color: var(header-color); }
, , . , css- firefox 2015 . , 2016, google safari.
, , :
:root {
--header-color: #06c;
}
h1 { background-color: var(--header-color); }
, . β . 2015 . , β .
, . , β , , . , , , , . , , , β , .
Css . , . stylus, 2011, sass less. , , css. . , js, css. js .
10 , . . HTML5, ES6,7,8,9,10. JS , . β react, vue angular, HTML , js. JS css, , βcss in jsβ, , ( , ). JS , .
, , , β . , β . , .
, , . . , . , , , , β .
β β . - . , ( , , ). , . , , β , . , β Avocode, Zeplin, Figma, Sketch. , , . , , . . β css , , margin- padding-. , . , . , , , , , .
β . , . , . .
. . .
, , β blue200. , , , . , css, , - [.]. , , css , . , , blue200 β - , - - β blue800. primary-color, blue200, blue800, , .
colors: {
body: '#ECEFF1',
antiBody: '#263238',
shared: {
primary: '#1565C0',
secondary: '#EF6C00',
error: '#C62828',
default: '#9E9E9E',
disabled: '#E0E0E0',
},
},
(, , , ), :
colors: {
...
text: {
lvl1: '#263238',
lvl3: '#546E7A',
lvl5: '#78909C',
lvl7: '#B0BEC5',
lvl9: '#ECEFF1',
},
},
, 2- .. β .
:
shared-primary-color
,
text-lvl1-color
.
, , ( ) .
, , .
.
, 3 β ( ), βcss in jsβ, . , , IE . 2 β css βcss in jsβ.
:
(, , );
, ( );
;
;
;
.
. , .
β , , , PWA. , . , Β«theme_colorΒ» Β«background_colorΒ». - head .
Theme_color
β . Android. , 67%.
Background_color
β , . , :
, , , .
IE, Safari , , , . , IE Safari (5,87% 3,62% 2020).
.
1. dark light, .
Β« Β».
, .
.theme-light {
--body-color: #ECEFF1;
--antiBody-color: #263238;
--shared-primary-color: #1565C0;
--shared-secondary-color: #EF6C00;
--shared-error-color: #C62828;
--shared-default-color: #9E9E9E;
--shared-disabled-color: #E0E0E0;
--text-lvl1-color: #263238;
--text-lvl3-color: #546E7A;
--text-lvl5-color: #78909C;
--text-lvl7-color: #B0BEC5;
--text-lvl9-color: #ECEFF1;
}
.theme-dark {
--body-color: #263238;
--antiBody-color: #ECEFF1;
--shared-primary-color: #90CAF9;
--shared-secondary-color: #FFE0B2;
--shared-error-color: #FFCDD2;
--shared-default-color: #BDBDBD;
--shared-disabled-color: #616161;
--text-lvl1-color: #ECEFF1;
--text-lvl3-color: #B0BEC5;
--text-lvl5-color: #78909C;
--text-lvl7-color: #546E7A;
--text-lvl9-color: #263238;
}
, body.
2. , .
β , . , , .
2
2.1) css
, - .theme-auto
media :
@media (prefers-color-scheme: dark) {
body.theme-auto {
--background-color: #111;
--text-color: #f3f3f3;
}
}
@media (prefers-color-scheme: light) {
body.theme-auto {
--background-color: #f3f3f3;
--text-color: #111;
}
}
:
:
(
.theme-dark
.theme-light
)
, -
2.2) js
js β css. , , .
:
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
body.classlist.add('theme-dark')
} else {
body.classlist.add('theme-light')
}
:
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (e.matches) {
body.classlist.remove('theme-light')
body.classlist.add('theme-dark')
} else {
body.classlist.remove('theme-dark')
body.classlist.add('theme-light')
}
});
:
:
(head body). .
3.
./button.css
.button {
color: var(--text-lvl1-color);
background: var(--shared-default-color);
...
&:disabled {
background: var(--shared-disabled-color);
}
}
.button-primary {
background: var(--shared-primary-color);
}
.button-secondary {
background: var(--shared-secondary-color)
}
./appbar.css
.appbar {
display: flex;
align-items: center;
padding: 8px 0;
color: var(--text-lvl9-color);
background-color: var(--shared-primary-color);
}
4.
, . , :
, :
body.classlist.remove('theme-light', 'theme-high')
:
body.classlist.add('theme-dark')
5. .
, . , : theme: 'light' | 'dark' | 'rose'
body. , :
const savedTheme = localStorage.getItem('theme')
if (['light', 'dark', 'rose'].includes(savedTheme)) {
body.classlist.remove('theme-light', 'theme-dark', 'theme-rose')
body.classList.add(`theme-${savedTheme}`)
}
, β , , , .
Css-in-js
, .
React + styled-components + typescript.
1. dark light, .
Β« Β».
, .
, Provider-.
./App.tsx
import { useState } from 'react'
import { ThemeProvider } from 'styled-components'
import themes from './theme'
const App = () => {
const [theme, setTheme] = useState<'light' | 'dark'>('light')
const onChangeTheme = (newTheme: 'light' | 'dark') => {
setTheme(newTheme)
}
return (
<ThemeProvider theme={themes[theme]}>
// ...
</ThemeProvide>
)
}
2. , .
β , . , , .
:
useEffect(() => {
if (window.matchMedia?.('(prefers-color-scheme: dark)').matches) {
onChangeTheme('dark')
}
}, [])
:
useEffect(() => {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (e.matches) {
onChangeTheme('dark')
} else {
onChangeTheme('light')
}
})
}, [])
3.
./src/components/atoms/Button/index.tsx
- git
import type { ButtonHTMLAttributes } from 'react'
import styled from 'styled-components'
interface StyledProps extends ButtonHTMLAttributes<HTMLButtonElement> {
fullWidth?: boolean;
color?: 'primary' | 'secondary' | 'default'
}
const Button = styled.button<StyledProps>(({ fullWidth, color = 'default', theme }) => `
color: ${theme.colors.text.lvl9};
width: ${fullWidth ? '100%' : 'fit-content'};
...
&:not(:disabled) {
background: ${theme.colors.shared[color]};
cursor: pointer;
&:hover {
opacity: 0.8;
}
}
&:disabled {
background: ${theme.colors.shared.disabled};
}
`)
export interface Props extends StyledProps {
loading?: boolean;
}
export default Button
./src/components/atoms/AppBar/index.tsx
- git
import styled from 'styled-components'
const AppBar = styled.header(({ theme }) => `
display: flex;
align-items: center;
padding: 8px 0;
color: ${theme.colors.text.lvl9};
background-color: ${theme.colors.shared.primary};
`)
export default AppBar
4.
context api redux/mobx
./App.tsx
- git
import { useState } from 'react'
import { ThemeProvider } from 'styled-components'
import themes from './theme'
const App = () => {
const [theme, setTheme] = useState<'light' | 'dark'>('light')
const onChangeTheme = (newTheme: 'light' | 'dark') => {
setTheme(newTheme)
}
return (
<ThemeProvider theme={themes[theme]}>
<ThemeContext.Provider value={{ theme, onChangeTheme }}>
...
</ThemeContext.Provider>
</ThemeProvide>
)
}
.src/components/molecules/Header/index.tsx
- git
import { useContext } from 'react'
import Grid from '../../atoms/Grid'
import Container from '../../atoms/Conrainer'
import Button from '../../atoms/Button'
import AppBar from '../../atoms/AppBar'
import ThemeContext from '../../../contexts/ThemeContext'
const Header: React.FC = () => {
const { theme, onChangeTheme } = useContext(ThemeContext)
return (
<AppBar>
<Container>
<Grid container alignItems="center" justify="space-between" gap={1}>
<h1>
Themization
</h1>
<Button color="secondary" onClick={() => onChangeTheme(theme === 'light' ? 'dark' : 'light')}>
set theme
</Button>
</Grid>
</Container>
</AppBar>
)
}
export default Header
5. .
, . , : theme: 'light' | 'dark' | 'rose'
. , :
./App.tsx
- git
...
function App() {
const [theme, setTheme] = useState<'light' | 'dark'>('light')
const onChangeTheme = (newTheme: 'light' | 'dark') => {
localStorage.setItem('theme', newTheme)
setTheme(newTheme)
}
useEffect(() => {
const savedTheme = localStorage?.getItem('theme') as 'light' | 'dark' | null
if (savedTheme && Object.keys(themes).includes(savedTheme)) setTheme(savedTheme)
else if (window.matchMedia?.('(prefers-color-scheme: dark)').matches) {
onChangeTheme('dark')
}
}, [])
useEffect(() => {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (e.matches) {
onChangeTheme('dark')
} else {
onChangeTheme('light')
}
})
}, [])
return (
...
)
}
β css-in-js ( css ). api , .
, . , , , . , , , .
, . , , . , , -.
Google apple, , . , , github gitlab. , , , β , .