Я разрабатываю расширение для браузера Chrome и столкнулся с проблемой определения размера шрифтов в компонентах, использующих библиотеки UI, такие как Vuetify, PrimeVue или MUI. Обратите внимание, что размеры шрифтов в этих библиотеках задаются в единицах rem. При использовании библиотек в shadow DOM размер rem, установленный в HTML в пределах shadow DOM, игнорируется, и вместо этого берется значение rem из корневого HTML документа. В результате, на различных сайтах размер шрифта может различаться в зависимости от font-size в корневом элементе. Какой лучший способ решить эту проблему: нужно ли переопределять стили в используемой UI-библиотеке, или существует другой способ заставить UI библиотеки использовать значение rem, установленное в shadow DOM, а не в корневом документе?
Работа с `rem` в контексте Shadow DOM действительно может быть затруднительной, поскольку единицы `rem` вычисляются на основе размера шрифта корневого элемента (`<html>` в документе). При этом Shadow DOM изолирует стили и контент, но не переопределяет базовые вычисления CSS, которые продолжают ссылаться на корневый документ. Вот несколько подходов, которые можно использовать для решения этой проблемы: ### 1. Переопределение корневого размера шрифта В Shadow DOM вы можете изменить размер шрифта для данного компонента, установив свойство `font-size` в корневом `<style>` Shadow DOM. Это приведет к тому, что все значения `rem` будут пересчитываться относительно нового размера шрифта. ```javascript const shadowRoot = document.createElement('div').attachShadow({ mode: 'open' }); const style = document.createElement('style'); style.textContent = ` :host { font-size: 16px; /* Ваш размер шрифта */ } `; shadowRoot.appendChild(style); // Добавьте остальные элементы в shadowRoot ``` ### 2. Использование `em` вместо `rem` Если возможно, попробуйте использовать единицы измерения `em` вместо `rem`. `em` вычисляются относительно размера шрифта родительского элемента. Таким образом, если вы установите размер шрифта для Shadow DOM, значения `em` будут соответствовать этому размеру. ### 3. Применение CSS-переменных Вы можете использовать CSS-переменные для управления размером шрифта и использовать эти переменные в стилях компонент. Например: ```css :host { --font-size: 16px; font-size: var(--font-size); } .some-class { font-size: calc(var(--font-size) * 1rem); /* Используйте переменную */ } ``` ### 4. Переопределение стилей UI-библиотек Если метод с настройкой размера шрифта не подходит, вы можете переопределить стили конкретных компонентов UI-библиотек в вашем стилизованном `shadow DOM`. Это можно сделать, указав стиль для каждого необходимого элемента. ### 5. Использование встроенных стилей При создании компонентов убедитесь, что вы устанавливаете необходимые стили в самих компонентах. Возможно, вам придется использовать встроенные стили или создавать CSS-переменные, чтобы обеспечить совместимость с размером шрифта. ### Заключение Наиболее подходящий способ зависит от вашего конкретного приложения и требований. Используйте методы, описанные выше, чтобы обеспечить необходимую гибкость при работе с `rem` в Shadow DOM. Если ваши компоненты требуют полной изоляции и вы планируете их использовать в разных контекстах, лучше всего переопределить базовый размер шрифта в самом Shadow DOM.
Не знаю, насколько это решение правильное, но вдохновившись этим решением, я написал аналогичный плагин для Vite для замены rem на em во всём CSS во Vuetify.
import { type Plugin } from "vite"; import postcss, { Root } from "postcss"; /** * @see https://github.com/vuetifyjs/vuetify/issues/12532#issuecomment-2755171940 */ export default function vuetifyRemToEmPlugin(): Plugin { return { name: "vite-vuetify-rem-to-em", transform(css: string, id: string) { // Обрабатываем только файлы, связанные с Vuetify if (!id.includes("vuetify") || !id.includes(".css")) { return; } return postcss([ (root: Root) => { root.walkDecls((decl) => { if (decl.value.includes("rem")) { decl.value = convertRemToEm(decl.value); } }); }, ]) .process(css, { from: id }) .then((result) => { return { code: result.css, }; }); }, }; } function convertRemToEm(value: string): string { return value.replace(/(\d*\.?\d+)rem/g, (match, p1) => { return `${parseFloat(p1).toFixed(4)}em`; }); }
Подключаем плагин к Vite
... plugins: [ vuetify(), vuetifyRemToEmPlugin() ], ...
Теперь можно задать стили глобально в shadow DOM и em будет подхватываться:
html, body, :host, :root { font-family: Roboto, sans-serif; font-size: 16px; }