Простое руководство по созданию адаптивного приложения React с использованием Styled-System на основе Design System.

Пример репо

Вы можете просмотреть окончательный рабочий пример по адресу:



Системы проектирования компонентов позволяют команде сотрудничать и предоставлять пользователям единообразный визуальный и функциональный интерфейс для разных приложений. Согласованность имеет четыре типа: визуальная согласованность, функциональная согласованность, внутренняя согласованность и внешняя согласованность по принципу UX-дизайна, и в этой статье я сосредоточусь на визуальной согласованности.

Проблема с фронтенд-разработкой

Проблемы с фронтенд-разработкой существуют уже давно, например:

  • Непоследовательный стиль
  • Множество дублированных компонентов пользовательского интерфейса
  • Много ненужного времени на проектирование и разработку

Это происходит потому, что у каждого дизайнера свой стиль и тон. Каждый разработчик создает свои собственные компоненты, даже не замечая, что они уже разработаны.

Чтобы решить эту проблему, наиболее важным является поддержание согласованности компонентов, даже если в одном проекте задействовано несколько дизайнеров и разработчиков. Дизайнеры должны знать, как проектировать и делиться этим с другими. Разработчикам необходимо создавать повторно используемые компоненты для каждой части проекта на основе руководства по стилю.

Наиболее важно то, что со стороны пользователя успешная компонентная система обеспечивает меньше путаницы, лучшую навигацию по продукту и большее общее удовлетворение. Пользователи быстрее научатся пользоваться вашим продуктом.

Дизайн-система

Система дизайна в основном включает в себя набор активов и компонентов, таких как библиотека шаблонов дизайна, правил и руководств по UX для создания продукта, а также предоставляет развивающуюся карту территорий бренда с указанием направления для создания нового продукта. Он должен быть централизованным и постоянно развиваться, чтобы оставаться последовательным.

Со стороны разработчика ключом к достижению настоящего сотрудничества с дизайн-системой является преодоление разрыва между дизайном и разработкой с помощью компонентов в кодовой базе.

Стильная система

Styled-System - один из вариантов достижения сотрудничества. Он разработан с набором утилит, которые сопоставляют реквизиты с системой дизайна, а также имеет набор API с функциями для CSS. Он работает с библиотеками CSS-in-JS, такими как styled-component, Emotion, и даже поддерживает Vue.js.

Давайте посмотрим, как это работает.

Установите styled-system и styled-component:

$ yarn add styled-system styled-components

И добавьте несколько стилизованных компонентов в свой App.js:

import React from "react"
import { space, width, fontSize, color } from 'styled-system';
import styled, { ThemeProvider } from 'styled-components';
import theme from './theme';

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Box>
        This is a Box
      </Box>
    </ThemeProvider>
  )
}
const Box = styled.div`
  ${space}
  ${width}
  ${fontSize}
  ${color}
`;
export default App

ThemeProvider - это встроенный поставщик стилизованных компонентов с полной поддержкой тем. Этот компонент предоставляет тему для всех компонентов React под собой через контекстный API. Используя это, styled-system расширяет функциональность за счет более многократно используемых компонентов. ${space}, ${width}, ${fontSize} и ${color} - это функции, обеспечивающие определенный стиль.

Давайте добавим стиля:

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Box bg="orange" fontSize={24} width={200} p={20} m="50px auto">
        This is a Box
      </Box>
    </ThemeProvider>
  )
}

У вас будет такая коробка:

Как видите, некоторые свойства передаются в Box и применяются стили. Так работает style-system. Вам не нужно писать CSS внутри литерала шаблона стилизованного компонента. (Если вы хотите узнать, как волшебство стилизованных компонентов творится в литерале шаблона, загляните здесь)

Помимо приведенного выше примера, есть еще куча API.

Теперь, когда вы знаете, как им пользоваться. Затем вы можете применить свою дизайн-систему к компонентам, определив theme.

Давайте отредактируем theme.js:

const theme = {
  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
  fontSizes: [12, 14, 16, 20, 24, 36, 48, 80, 96],
  fontWeights: [100, 200, 300, 400, 500, 600, 700, 800, 900],
  width: [16, 32, 64, 128, 256],
  heights: [16, 32, 64, 128, 256],
  colors: {
    black: '#000',
    gray: ' #777',
    'dark-gray': '#333',
    'light-gray': '#eee',
  },
  // .... and other styles
}

export default theme

Предполагается, что это ваша собственная дизайн-система, но пока мы определили ее, как указано выше.

fontSizes имеет 12, 14, 16, 20... размеры, и давайте применим font-size: 12px к Box компоненту:

<Box bg="orange" fontSize={0} width={200} p={20} m="50px auto">
  This is a Box
</Box>

0 передается не 12, что означает, что вам нужно передать номер индекса массива fontSizes. В этом случае 0 означает 12, 1 означает 14 и так далее.

Но вы можете сделать его более декларативным в theme.js вот так:

const theme = {
  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
  fontSizes: {
    small: 14,
    medium: 16,
    large: 18
  },
  ...
}

export default theme

И отредактируйте App.js:

<Box bg="orange" fontSize="small" width={200} p={20} m="50px auto">
  This is a Box
</Box>

Способ стилизации компонента разумен и удобен в обслуживании, потому что вам как разработчику не нужно думать о том, какой размер следует применять в поле, просто следуйте руководству по стилю и передайте ему small.

Адаптивный дизайн со стильной системой

Далее я хочу продемонстрировать, как сделать компоненты адаптивными. В адаптивном дизайне вы в основном используете Media Queries в CSS. Styled-system предоставляет свою собственную адаптивную систему, которая предлагает удобный сокращенный синтаксис для добавления адаптивного стиля с подходом, ориентированным на мобильные устройства, а также альтернативно предоставляет вам breakpoints для определения вашего собственного адаптивного значения.

Добавим его в theme.js:

const theme = {
  ...
  breakpoints: {
    xs: '0',
    sm: '600px',
    md: '960px',
    lg: '1280px',
    xl: '1920px',
  },
}

Я слежу за значением контрольных точек Материального дизайна. Эти значения должны работать следующим образом:

|0px     600px    960px    1280px   1920px
|xs      sm       md       lg       xl
|--------|--------|--------|--------|-------->
|   xs   |   sm   |   md   |   lg   |   xl

Сделаем Box отзывчивым:

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Box
        bg={{xs: 'orange', sm: 'grey'}}
        fontSize={{xs: 'small', sm: 'medium', md: 'large'}}
        width={{ xs: 200, sm: 400, md: 600, lg: 1000 }}
        p={20}
        m="50px auto">
        This is a Box
      </Box>
    </ThemeProvider>
  )
}

Он более читабелен и помогает понять, какое значение следует использовать на каждом устройстве.

Типография

Размер шрифта важен при разработке. Как дизайнеру важно организовать масштабируемую дизайн-систему, а также для типографики, чтобы нужно было настраивать уровни заголовков и создавать иерархию для четкой видимости.

Разработчику очень сложно применять каждый шрифт на каждом устройстве, даже если это только мобильные устройства. Но в styled-system вам не нужно указывать определенный размер шрифта для каждого компонента.

Предположим, какой-то уровень заголовка в theme.js:

const theme = {
  fontSizes: {
    small: 14,
    medium: 16,
    large: 18,
    h5: 12,
    h4: 18,
    h3: 20,
    h2: 24,
    h1: 30,
  },
}

В качестве альтернативы есть другой подход:

const theme = {
  fontSizes: {
    small: 14,
    medium: 16,
    large: 18,
    h5: {
      sm: 12,
      md: 14,
      lg: 16,
    },
    h4: {
      sm: 16,
      md: 18,
      lg: 22,
    },
    h3: {
      sm: 19,
      md: 22,
      lg: 28,
    },
    h2: {
      sm: 20,
      md: 22,
      lg: 24
    },
    h1: {
      sm: 24,
      md: 26,
      lg: 30
    }
  }
}

Система может иметь более ограниченный набор уровней заголовков и указывать размер для каждого устройства.

Вы можете вникнуть в подробности в Размер в дизайн-системах, это действительно потрясающий пост.

Адаптивный макет

Как упоминалось выше, width может быть отзывчивым, и вы можете определить адаптивный макет. Но если вы предпочитаете макет, основанный на системе сеток, в качестве альтернативы Material-UI имеет собственный макет Grid.

Давайте установим Material-UI:

yarn add @material-ui/core

И добавляем в App.js:

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Grid container spacing={2}>
        <Grid item xs={12} md={3}>
          <Box
            bg={{xs: 'orange', sm: 'grey'}}
            fontSize={{xs: 'small', sm: 'medium', md: 'large'}}
            p={20}
            m="50px auto">
            This is a Box
          </Box>
        </Grid>
        <Grid item xs={12} md={3}>
          <Box
            bg={{xs: 'orange', sm: 'grey'}}
            fontSize={{xs: 'small', sm: 'medium', md: 'large'}}
            p={20}
            m="50px auto">
            This is a Box
          </Box>
        </Grid>
        <Grid item xs={12} md={3}>
          <Box
            bg={{xs: 'orange', sm: 'grey'}}
            fontSize={{xs: 'small', sm: 'medium', md: 'large'}}
            p={20}
            m="50px auto">
            This is a Box
          </Box>
        </Grid>
      </Grid>
    </ThemeProvider>
  )
}

Компонент сетки может принимать такие точки останова, как xs, sm, md lg и xl, и должен соответствовать вашим точкам останова.

Отдельный компонент на ПК и мобильный

Затем, что, если вы хотите разделить HTML на ПК и мобильные устройства. Затем вы можете заархивировать его, используя Hidden.

Давайте создадим компонент, который переключает значение видимости. Я называю его Media компонентом, и он требует двух реквизитов, pc и mobile.

Перед этим установите lodash:

yarn add lodash

И добавьте Media.js:

// Media.js
import React from 'react'
import HiddenCss from '@material-ui/core/Hidden/HiddenCss'
import omit from 'lodash/omit'
const Media = (props) => {
  const hiddenProps = omit(props, 'mobile', 'pc')
  switch (true) {
    case Boolean(props.mobile):
      return (
        <HiddenCss {...hiddenProps} smUp>
          {props.children}
        </HiddenCss>
      )
    case Boolean(props.pc):
      return (
        <HiddenCss {...hiddenProps} xsDown>
          {props.children}
        </HiddenCss>
      )
    default:
      return <>{props.children}</>
  }
}
export default Media

И примените его к App.js:

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Media pc>
        <p>Hi I'm from PC</p>
      </Media>
      <Media mobile>
        <p>Hi I'm from mobile</p>
      </Media>
    </ThemeProvider>
  )
}

Теперь он четко разделяет HTML на компьютерный и мобильный на уровне компонентов.

Стиль сочинения

В styled-system есть множество стилевых функций. Возможно, вам будет сложно добавить их к каждому компоненту. Чтобы решить эту проблему, можно использовать компонент-оболочку, который имеет функции стиля, следующим образом:

// utils/styledSystem.js
import styled from 'styled-components'
import {
  compose,
  space,
  color,
  layout,
  typography,
  flexbox,
  border,
  background,
  position,
  grid,
  shadow,
  width,
  minWidth,
  height,
  minHeight,
} from 'styled-system'

const styledSystem = (tag) => {
  return styled(tag)(
    compose(
      space,
      color,
      layout,
      typography,
      flexbox,
      border,
      background,
      position,
      grid,
      shadow,
      width,
      minWidth,
      height,
      minHeight,
    ),
  )
}
export default styledSystem

И оберните его стилизованным компонентом:

import React from "react"
import styled, { ThemeProvider } from 'styled-components';
import theme from './theme';
import Grid  from '@material-ui/core/Grid';
import styledSystem from './utils/styledSystem'
const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Heading fontSize={{ xs: 'h1' }}>Title here</Heading>
      <Grid container spacing={2}>
        <Grid item xs={12} md={3}>
          <Box
            bg={{xs: 'orange', sm: 'grey'}}
            fontSize={{xs: 'small', sm: 'medium', md: 'large'}}
            p={20}
            m="50px auto">
            This is a Box
          </Box>
        </Grid>
        <Grid item xs={12} md={3}>
          <Box2
            bg={{xs: 'orange', sm: 'grey'}}
            fontSize={{xs: 'small', sm: 'medium', md: 'large'}}
            p={20}
            m="50px auto">
            This is a Box
          </Box2>
        </Grid>
        <Grid item xs={12} md={3}>
          <Box3
            bg={{xs: 'orange', sm: 'grey'}}
            fontSize={{xs: 'small', sm: 'medium', md: 'large'}}
            p={20}
            m="50px auto">
            This is a Box
          </Box3>
        </Grid>
      </Grid>
    </ThemeProvider>
  )
}
const Box = styledSystem(styled.div``)
const Box2 = styledSystem(styled.div``)
const Box3 = styledSystem(styled.div``)
const Heading = styledSystem(styled.h1``)
export default App

Все функции стиля применяются с компонентами.

Другой вариант - использовать Rebass, компонент пользовательского интерфейса React, созданный с использованием стилизованной системы и хорошо совместимый с системой дизайна.

Заключение

По большей части вы можете быть готовы к созданию адаптивных компонентов. Каждый компонент должен быть читаемым, поддерживаемым и масштабируемым. Сделать отзывчивым - непросто, но оно того стоит.

Надеюсь, это тебе поможет.

Вы можете просмотреть окончательный рабочий пример по адресу: