При разработке блока Gutenberg иногда бывает, что необходимо задать уникальные css-стили блока, как это сделать без костылей.
Простой пример, как задать уникальный класс и стилизовать блок Gutenberg
Для начала заполним наш block.json, я для примера напишу самый базовую структуру.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "ourslug/ourblock",
"title": "Our block",
"attributes": {
"blockId": {
"type": "string"
},
"colors": {
"type":"object",
"default": {
"button": "#454545",
"headline": "#595959"
}
}
},
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}Тут в атрибуты мы добавили colors, который является объектом и содержит значения по умолчанию. Как видно, стилизовать мы будем кнопку и заголовок. Это все в рамках примера. Ну и ключевой наш атрибут blockId именно в него мы будем заносить уникальную часть класса.
Далее переходим к edit.js
import {
useBlockProps, // свойства блока
InspectorControls, // панель управления
useSetting // настройки темы
} from '@wordpress/block-editor';
import {
PanelBody, // панель управления
ColorPalette // палитра цветов
} from '@wordpress/components';
import {
useEffect // эффекты
} from '@wordpress/element';
export default function Edit({ attributes, setAttributes, clientId }) { // функция редактора
const { colors, blockId } = attributes; // атрибуты блока
const themeColors = useSetting('color.palette') || []; // цвета из настроек темы
// устанавливаем блок id
useEffect(() => {
if (!blockId) { // если блок id не установлен
setAttributes({ blockId: clientId }); // устанавливаем блок id
}
}, [clientId, blockId]);
// возвращаем JSX
return (
<>
{/* панель управления */}
<InspectorControls>
<PanelBody title="Colors">
<h2>Button Color</h2>
<ColorPalette
enableAlpha={true}
colors={themeColors} // цвета из настроек темы
value={colors.button} // установленный цвет кнопки
onChange={(val) => {
// устанавливаем цвет кнопки
setAttributes({
colors: {
...colors,
button: val
}
});
}}
/>
<hr />
<h2>Headline Color</h2>
<ColorPalette
enableAlpha={true}
colors={themeColors} // цвета из настроек темы
value={colors.headline} // установленный цвет заголовка
onChange={(val) => {
// устанавливаем цвет заголовка
setAttributes({
colors: {
...colors,
headline: val
}
});
}}
/>
</PanelBody>
</InspectorControls>
{/* стили */}
<style
dangerouslySetInnerHTML={{
__html: `
.our-block-${blockId} h3 {
color: ${colors.headline};
}
.our-block-${blockId} button {
background-color: ${colors.button};
}
`,
}}
/>
{/* блок */}
<div {...useBlockProps({ className: `our-block-${blockId}` })}>
<h3>Заголовок</h3>
<button>Кнопка</button>
</div>
</>
);
}В целом, если вы уже разрабатывали блок для Gutenberg или что-либо под ReactJS, то синтаксис вам должен быть понятен. Но есть несколько моментов на которых я хотел бы остановиться:
- На 14 строчке из Edit мы получаем помимо привычных
attributesиsetAttributesеще иclientId— именно он дает нам возможность создать уникальный класс для блока и чуть позднее мы обязательно должны занести его в наш атрибутblockId - На 16 строчке мы получаем палитру цветов из настроек темы, это нужно, если мы в своем элементе выбора цвета хотим использовать предустановки
- На 19 строчке используя
UseEffectмы проверяем пустой ли наш атрибутblockIdи если он пустой, то заносим в негоclientId, который мы получили выше - А далее уже пишем панель управления с выбором цвета, уникальный
<style></style>и непосредственно наш простенький блок - В
InspectorControlsиPanelBodyя использую компонентColorPaletteдля выбора цвета. В него я передаю цвета из настроек темы (themeColors) и пишу обработку изменения цвета - Далее в теге
styleя использую специальный атрибутdangerouslySetInnerHTMLдля того, чтобы написать свой CSS - Ну а в нашем блоке я добавляю в
useBlockProps()наш уникальный класс{ className: `our-block-${blockId}` }, чтобы все изменения применялись
Но edit.js это только часть редактора Gutenberg, там у нас теперь всё редактируется и сохраняется по красоте, но на самом сайте у нас пока выводится примерно ничего. Вывод содержимого на сайте мы будет писать в save.js (спойлер: там тоже самое только меньше)
import {
useBlockProps,
} from '@wordpress/block-editor';
export default function save({ attributes}) {
const { colors, blockId } = attributes; // атрибуты блока
// возвращаем JSX
return (
<>
{/* стили */}
<style
dangerouslySetInnerHTML={{
__html: `
.our-block-${blockId} h3 {
color: ${colors.headline};
}
.our-block-${blockId} button {
background-color: ${colors.button};
}
`,
}}
/>
{/* блок */}
<div {...useBlockProps.save({ className: `our-block-${blockId}` })}>
<h3>Заголовок</h3>
<button>Кнопка</button>
</div>
</>
);
}Как видно, в save.js мы не делаем никаких обработок, а только используем сохраненные атрибуты.
Вывод
Выше я написал простую реализацию редактирования стилей блока. Ключевой момент тут — это использование clientId, но конкретно в этом примере этот путь был необязателен.
Можно было просто написать инлайн-стиль к необходимому элементу. Но иногда бывает так, что нужно стилизовать псевдоэлементы, типа ::before или ::after, к которым так просто не подступиться 😅
И если мы пошли все-таки путем использования clientId, то лучше не использовать <style dangerouslySetInnerHTML>. Да, это работает, но не является лучим решением.
Лучше написать стили в style.scss, а в качестве значений параметров использовать css-переменные, например var(--our-block-headline-color) и var(--our-block-button-color). А сами эти переменные передать в useBlockProps
<div
{...useBlockProps({
style: {
'--our-block-headline-color': colors.headline,
'--our-block-button-color': colors.button
}
})}
>Но в данном варианте нам и задавать уникальный класс смысла нет, а это была основная цель данной заметки 😅
