Другое

Почему не применяются стили кастомного варианта кнопки MUI

Решение проблем с кастомными вариантами кнопок в Material-UI. Узнайте, почему стили вашего варианта 'iconary' не работают и как это исправить.

Почему не применяются стили кастомного варианта кнопки MUI iconary?
Я создал кастомную кнопку, расширив интерфейс ButtonPropsVariantOverrides, но стили для variant='iconary' не работают. Вот моя конфигурация:

typescript
import "@mui/material/Button";

declare module "@mui/material/Button" {
  interface ButtonPropsVariantOverrides {
    iconary: true;
  }
}

MuiButton: {
  variants: [
    {
      props: { variant: 'iconary' },
      style: {
        width: 32,
        height: 32,
        minWidth: "none",
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        borderRadius: '5px'
      }
    },
  ],
  styleOverrides: {
    root: {
      textTransform: "none",
      fontWeight: 400,
      fontSize: 16,
    },
    contained: { borderRadius: "10px" },
    sizeMedium: { padding: "8px 32px" },
    sizeLarge: { padding: "12px inherit" },
  },
}

Применяю кнопку так:

jsx
<Button
  onClick={() => setIsChangeNameModalOpened(true)}
  variant="iconary"
  sx={{ backgroundColor: "primary.light" }}
>
  <EditOutlined fontSize="small" sx={{ color: "primary.main", width: 16, height: 16 }} />
</Button>;

Ошибок нет, но стили variant='iconary' не применяются. В чем может быть проблема?

Когда вы определяете кастомный вариант кнопки через ButtonPropsVariantOverrides, но стили не применяются, это обычно связано с несколькими распространенными проблемами. Наиболее вероятные причины — неправильная структура темы, отсутствие ThemeProvider или конфликт с другими стилями.

Содержание

Основные причины не применяемых стилей

Наиболее частые причины, почему ваш кастомный вариант iconary не работает:

  1. Неправильная структура темы: Ваша конфигурация должна быть внутри createTheme, а не просто в объекте MuiButton.
  2. Отсутствие ThemeProvider: Без ThemeProvider стили темы не будут применены.
  3. Конфликты со styleOverrides: Ваши стили styleOverrides могут перекрывать кастомные варианты.
  4. Неправильное объявление модуля: Хотя вы объявили iconary: true, убедитесь, что это сделано правильно.

Правильная структура темы для кастомных вариантов

Ваша основная проблема — в том, как вы структурировали тему. Кастомные варианты должны быть определены внутри createTheme:

typescript
import { createTheme, ThemeProvider } from '@mui/material/styles';

// Объявление кастомного варианта
declare module "@mui/material/Button" {
  interface ButtonPropsVariantOverrides {
    iconary: true;
  }
}

// Правильная структура темы
const theme = createTheme({
  components: {
    MuiButton: {
      variants: [
        {
          props: { variant: 'iconary' },
          style: {
            width: 32,
            height: 32,
            minWidth: "none",
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            borderRadius: '5px'
          }
        },
      ],
      styleOverrides: {
        root: {
          textTransform: "none",
          fontWeight: 400,
          fontSize: 16,
        },
        contained: { borderRadius: "10px" },
        sizeMedium: { padding: "8px 32px" },
        sizeLarge: { padding: "12px inherit" },
      },
    },
  },
});

// Обертка приложения в ThemeProvider
function App() {
  return (
    <ThemeProvider theme={theme}>
      <YourComponent />
    </ThemeProvider>
  );
}

Проверка на наличие ThemeProvider

Убедитесь, что ваш компонент обернут в ThemeProvider. Без этого стили кастомных вариантов не будут применены:

jsx
import { ThemeProvider } from '@mui/material/styles';

function YourComponent() {
  return (
    <ThemeProvider theme={theme}>
      <Button
        onClick={() => setIsChangeNameModalOpened(true)}
        variant="iconary"
        sx={{ backgroundColor: "primary.light" }}
      >
        <EditOutlined fontSize="small" sx={{ color: "primary.main", width: 16, height: 16 }} />
      </Button>
    </ThemeProvider>
  );
}

Конфликты со стилями

Ваша конфигурация может иметь конфликты между variants и styleOverrides. В Material‑UI версии 5+ структурирование стало более строгим. Попробуйте разделить стили:

typescript
const theme = createTheme({
  components: {
    MuiButton: {
      // Кастомные варианты
      variants: [
        {
          props: { variant: 'iconary' },
          style: {
            width: 32,
            height: 32,
            minWidth: "none",
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            borderRadius: '5px'
          }
        },
      ],
      // Глобальные переопределения
      styleOverrides: {
        root: {
          textTransform: "none",
          fontWeight: 400,
          fontSize: 16,
        },
        contained: {
          '&.iconary': { // Добавьте селектор для конкретного варианта
            borderRadius: "10px"
          }
        },
        sizeMedium: {
          '&.iconary': {
            padding: "8px 32px"
          }
        },
        sizeLarge: {
          '&.iconary': {
            padding: "12px inherit"
          }
        },
      },
    },
  },
});

Полный рабочий пример

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

typescript
// src/theme.ts
import { createTheme } from '@mui/material/styles';

// Объявление кастомного варианта
declare module "@mui/material/Button" {
  interface ButtonPropsVariantOverrides {
    iconary: true;
  }
}

export const theme = createTheme({
  components: {
    MuiButton: {
      variants: [
        {
          props: { variant: 'iconary' },
          style: {
            width: 32,
            height: 32,
            minWidth: "none",
            padding: 0,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            borderRadius: '5px',
            '&:hover': {
              backgroundColor: 'rgba(0, 0, 0, 0.04)',
            },
          }
        },
      ],
      styleOverrides: {
        root: {
          textTransform: "none",
          fontWeight: 400,
          fontSize: 16,
        },
        contained: {
          boxShadow: 'none',
          '&:hover': {
            boxShadow: 'none',
          },
        },
      },
    },
  },
});
jsx
// src/App.tsx
import { ThemeProvider } from '@mui/material/styles';
import { Button } from '@mui/material';
import { EditOutlined } from '@ant-design/icons';
import { theme } from './theme';

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Button
        variant="iconary"
        sx={{ backgroundColor: "primary.light" }}
      >
        <EditOutlined fontSize="small" sx={{ color: "primary.main", width: 16, height: 16 }} />
      </Button>
    </ThemeProvider>
  );
}

Проверка в режиме разработки

Если стили все еще не применяются, выполните следующие проверки:

  1. Проверьте консоль: Включите режим строгого режима разработки Material‑UI, чтобы увидеть предупреждения.
  2. Проверьте DOM: В инспекторе браузера посмотрите, какие классы применяются к кнопке.
  3. Проверьте приоритет стилей: Убедитесь, что другие стили не переопределяют ваши.

Для включения режима отладки:

javascript
import { createTheme, Experimental_CssVarsProvider as CssVarsProvider } from '@mui/material/styles';

const theme = createTheme({
  // ваша конфигурация
});

// Используйте CssVarsProvider вместо ThemeProvider для отладки
<CssVarsProvider theme={theme}>
  {/* ваши компоненты */}
</CssVarsProvider>

Дополнительные методы стилизации

Если кастомный вариант все еще не работает, попробуйте альтернативные методы:

1. Использование sx prop вместо variants

jsx
<Button
  onClick={() => setIsChangeNameModalOpened(true)}
  sx={{
    width: 32,
    height: 32,
    minWidth: "none",
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: '5px',
    backgroundColor: "primary.light",
  }}
>
  <EditOutlined fontSize="small" sx={{ color: "primary.main", width: 16, height: 16 }} />
</Button>

2. Создание кастомного компонента

typescript
import { Button } from '@mui/material';

const IconaryButton = ({ children, ...props }) => (
  <Button
    {...props}
    sx={{
      width: 32,
      height: 32,
      minWidth: "none",
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      borderRadius: '5px',
      ...props.sx,
    }}
  >
    {children}
  </Button>
);

// Использование
<IconaryButton onClick={() => setIsChangeNameModalOpened(true)} variant="contained" sx={{ backgroundColor: "primary.light" }}>
  <EditOutlined fontSize="small" sx={{ color: "primary.main", width: 16, height: 16 }} />
</IconaryButton>

3. Использование styled API

typescript
import styled from '@mui/material/styles/styled';
import { Button } from '@mui/material';

const IconaryButton = styled(Button)(({ theme }) => ({
  width: 32,
  height: 32,
  minWidth: "none",
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  borderRadius: '5px',
}));

// Использование
<IconaryButton 
  onClick={() => setIsChangeNameModalOpened(true)} 
  variant="contained" 
  sx={{ backgroundColor: "primary.light" }}
>
  <EditOutlined fontSize="small" sx={{ color: "primary.main", width: 16, height: 16 }} />
</IconaryButton>

Чаще всего проблема решается правильной структурой темы и оберткой в ThemeProvider. Если после всех проверок стиль все равно не применяется, попробуйте временно удалить styleOverrides или использовать один из альтернативных методов стилизации.

Источники

  1. Themed components - Material UI Documentation
  2. Creating custom variants with Material UI - Stack Overflow
  3. How to use Material UI custom variants in React with Typescript - Stack Overflow
  4. Material-UI Button custom variant not working - GitHub Issues
  5. Material-UI TypeScript theme customization - Softbinator Blog

Заключение

  • Основная проблема: Стили кастомного варианта не применяются из-за неправильной структуры темы или отсутствия ThemeProvider.
  • Решение: Оберните ваше приложение в ThemeProvider с правильно настроенной темой, где кастомные варианты определены в components.MuiButton.variants.
  • Альтернативы: Если вариант не работает, используйте sx prop для стилизации или создайте кастомный компонент.
  • Проверка: Всегда проверяйте консоль на наличие сообщений об ошибках и inspect DOM для просмотра примененных стилей.
  • TypeScript: Убедитесь, что объявление ButtonPropsVariantOverrides корректно и находится в правильном месте.
Авторы
Проверено модерацией
Модерация