Tematización. Historia, razones, implementación

Introducción. Razones de la aparición

Cuando la web estaba en su infancia, su único propósito era alojar contenido (páginas de hipertexto) para que los usuarios de la World Wide Web tuvieran acceso a él. En ese momento, no podía haber cuestión de diseño, porque ¿por qué necesitamos diseño para páginas con publicaciones científicas, a menos que sean más útiles desde este ( primer sitio )? Los tiempos están cambiando y hoy en la World Wide Web no se trata solo de publicaciones científicas. Blogs, servicios, redes sociales y mucho, mucho más. Cada sitio necesita su propia individualidad, necesita interesar y atraer a los usuarios. Incluso los sitios científicos se están dando cuenta de esto gradualmente, porque la mayoría de los científicos quieren no solo estudiar ciertos aspectos, sino transmitirlos a las personas, aumentando así su popularidad y el valor de su investigación (por ejemplo, 15 de 15 sitios científicoslista ha sido rediseñada en los últimos 6 años). La gente común no está interesada en un sitio gris con contenido incomprensible. La ciencia es cada vez más accesible y los sitios se están transformando en aplicaciones con una interfaz cómoda y agradable.





Dado que cada uno tiene su propia "conveniencia", no existe una definición clara ni reglas específicas para la implementación de un servicio que sea conveniente para todos. En los últimos años, un concepto como Theming ha comenzado a vincularse a este concepto. Es sobre él de lo que quiero hablar en este artículo.





, . , , . , , , . , , « » « ». , , .





– . . 285 , – 218 [.], 2,2 [.] « »[.]. . , , . . , – .





.

. , . , , . , , , . , – , . , « ». , – . , , – – , , .. , .





– . – , , , . . , , , .





, . , , . , 5-10 . , . , , . , IE, , ES6. , . , , .





JS , . , HTML CSS. , , . . , , , IE. , . css, 2015 . 2015 – JS, HTTP/2, WebAssembly, ReactJS. , .





css-:





, , 2012 . 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); }
      
      



, , 2014 . – .





, . – . 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”.





:





  1. (, , );





  2. , ( );





  3. ;





  4. ;





  5. ;





  6. .





. , .





– , , , PWA. , . , «theme_color» «background_color». - head .





Theme_color



– . Android. , 67%.





caniuse.com
caniuse.com

Background_color



– , . , :





caniuse.com
caniuse.com

, , , .





caniuse.com
caniuse.com

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. , , , – , .








All Articles