Верстка сайту з використанням Emotion CSS-in-JS

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Верстка сайту з використанням Emotion CSS-in-JS
Проста
~2-3 робочих дні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Вёрстка сайта з використанням Emotion CSS-in-JS

Emotion — CSS-in-JS бібліотека з двома API: @emotion/css для vanilla JS та @emotion/react для React. Використовується як styling engine в MUI, Chakra UI та ряді інших бібліотек. Серед CSS-in-JS інструментів показує найкращу продуктивність: підтримує статичну екстракцію через Babel-плагін, що усуває runtime-overhead для статичних стилів.

Установка

# Для React
npm install @emotion/react @emotion/styled
# Опціонально: Babel-плагін для оптимізації
npm install -D @emotion/babel-plugin
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    react({
      jsxImportSource: '@emotion/react',
      babel: {
        plugins: ['@emotion/babel-plugin'],
      },
    }),
  ],
});

При використанні jsxImportSource: '@emotion/react' можна використовувати css prop напрямку без /** @jsxImportSource @emotion/react */ у кожному файлі.

Два підходи: css prop та styled

css prop — інлайн-стилі з повною мощью CSS

import { css } from '@emotion/react';

// Статичний блок — обчислюється один раз
const heroStyles = css`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100svh;
  padding: 2rem;
  text-align: center;

  @media (min-width: 1024px) {
    flex-direction: row;
    text-align: left;
    padding: 4rem;
    gap: 5rem;
  }
`;

const HeroSection = () => (
  <section css={heroStyles}>
    <div>
      <h1 css={css`
        font-size: clamp(1.75rem, 4vw, 3rem);
        font-weight: 700;
        margin-bottom: 1rem;
        line-height: 1.2;
      `}>
        Заголовок сторінки
      </h1>
    </div>
  </section>
);

styled — компонентний API

import styled from '@emotion/styled';

interface CardProps {
  elevated?: boolean;
  interactive?: boolean;
}

const Card = styled.article<CardProps>`
  background: var(--color-surface);
  border-radius: 0.75rem;
  padding: 1.5rem;
  border: 1px solid var(--color-border);
  box-shadow: ${({ elevated }) =>
    elevated ? '0 10px 15px -3px rgb(0 0 0 / 0.1)' : '0 1px 2px rgb(0 0 0 / 0.05)'};

  ${({ interactive }) =>
    interactive &&
    `
    cursor: pointer;
    transition: transform 200ms ease, box-shadow 200ms ease;
    &:hover {
      transform: translateY(-3px);
      box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1);
    }
  `}
`;

const CardTitle = styled.h3`
  font-size: 1.125rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
  color: var(--color-text-primary);
`;

const CardBody = styled.p`
  font-size: 0.875rem;
  color: var(--color-text-secondary);
  line-height: 1.6;
`;

Тема через ThemeProvider

// src/theme/emotion-theme.ts
export const theme = {
  colors: {
    primary: '#2563eb',
    primaryDark: '#1d4ed8',
    primaryLight: '#eff6ff',
    background: '#f9fafb',
    surface: '#ffffff',
    textPrimary: '#111827',
    textSecondary: '#6b7280',
    border: '#e5e7eb',
  },
  space: (n: number) => `${n * 0.25}rem`,
  radius: {
    sm: '4px',
    md: '8px',
    lg: '12px',
  },
  shadow: {
    sm: '0 1px 2px rgb(0 0 0 / 0.05)',
    md: '0 4px 6px -1px rgb(0 0 0 / 0.1)',
  },
} as const;

export type Theme = typeof theme;

// Додати в типи Emotion
declare module '@emotion/react' {
  export interface Theme {
    colors: typeof theme.colors;
    space: typeof theme.space;
    radius: typeof theme.radius;
    shadow: typeof theme.shadow;
  }
}
// src/App.tsx
import { ThemeProvider, Global, css } from '@emotion/react';
import { theme } from './theme/emotion-theme';

const globalStyles = css`
  *, *::before, *::after { box-sizing: border-box; }
  body {
    font-family: 'Inter', system-ui, sans-serif;
    background: ${theme.colors.background};
    color: ${theme.colors.textPrimary};
    -webkit-font-smoothing: antialiased;
  }
`;

const App = () => (
  <ThemeProvider theme={theme}>
    <Global styles={globalStyles} />
    <Router />
  </ThemeProvider>
);

Використання теми в styled-компонентах:

import styled from '@emotion/styled';

const PrimaryButton = styled.button`
  background: ${({ theme }) => theme.colors.primary};
  color: #fff;
  padding: ${({ theme }) => `${theme.space(2)} ${theme.space(4)}`};
  border-radius: ${({ theme }) => theme.radius.md};
  box-shadow: ${({ theme }) => theme.shadow.sm};

  &:hover {
    background: ${({ theme }) => theme.colors.primaryDark};
  }
`;

keyframes для анімацій

import { keyframes } from '@emotion/react';
import styled from '@emotion/styled';

const fadeInUp = keyframes`
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
`;

const AnimatedCard = styled.div<{ delay?: number }>`
  animation: ${fadeInUp} 400ms ease both;
  animation-delay: ${({ delay = 0 }) => `${delay}ms`};
`;

// Використання
const FeaturesList = ({ features }) => (
  <>
    {features.map((feature, index) => (
      <AnimatedCard key={feature.id} delay={index * 80}>
        {/* ... */}
      </AnimatedCard>
    ))}
  </>
);

cx() для умовних класів

import { css, cx } from '@emotion/css';

const base = css`
  padding: 1rem;
  border-radius: 8px;
`;

const active = css`
  background: #eff6ff;
  color: #2563eb;
  font-weight: 600;
`;

const MenuItem = ({ label, isActive }) => (
  <a className={cx(base, isActive && active)}>
    {label}
  </a>
);

Порівняння зі Styled Components

Emotion Styled Components
Розмір ~8 КБ ~15 КБ
Продуктивність Вища Трохи нижча
SSR Вбудована Потребує ServerStyleSheet
css prop Так Ні (тільки через babel)
Популярність Растуча Усталена
Babel-плагін Так Так

Терміни

Налаштування Emotion з ThemeProvider та типізацією: 2–3 години. Вёрстка посадочної сторінки: 1–2 дні. Emotion особливо хороший в проектах, де Styled Components не хватає продуктивності або потрібен css prop для динамічних стилів.