CSS правила слияния
Я работаю над реализацией поддержки RTL на большой платформе. Для этого я заменил все правила, которые являются направленными, на такие миксины, как:
a {
@include padding(0, 1px, 0, 0);
@include margin(0, 1px, 0, 0);
}
Для этого нужно добавить соответствующие отступы / поля в зависимости от направления документа.
Каждый миксин создает случай для ltr
и случай для rtl
,
Это результат этих миксинов:
[dir="ltr"] a {
padding: 0 1px 0 0;
}
[dir="rtl"] a {
padding: 0 0 0 1px;
}
[dir="ltr"] a {
margin: 0 1px 0 0;
}
[dir="rtl"] a {
margin: 0 0 0 1px;
}
Это работает, и это хорошо, но создает много дублирующих селекторов (по 2 на каждый миксин), поэтому размер пакета CSS увеличивается на 100 КБ (20%), и большая часть этого происходит из-за этого дублирования.
Ожидаемый результат:
[dir="ltr"] a {
padding: 0 1px 0 0;
margin: 0 1px 0 0;
}
[dir="rtl"] a {
padding: 0 0 0 1px;
margin: 0 0 0 1px;
}
Какую постобработку я могу сделать, чтобы объединить соответствующие повторяющиеся селекторы, не затрагивая порядок выполнения CSS?
Нежелательный случай:
Допустим, у меня есть этот код:
b.s1 {
padding-left: 1px;
margin: 0;
}
b.s2 {
padding-left: 0;
margin: 1px;
}
b.s1 {
padding-left: 1px;
}
Если я сливаю b.s1 вверх, то s2's padding-left может переопределить его. Если я сливаю b.s1 вниз, то поле s2 переопределяется.
Есть ли решение этой проблемы?
РЕДАКТИРОВАТЬ: оригинальный код
// Add property for all sides
// @param {string} $prop
// @param {string} $top
// @param {string} $end
// @param {string} $bottom
// @param {string} $start
// @param {boolean} $content include content or use default
// ----------------------------------------------------------
@mixin property($prop, $top, $end: $top, $bottom: $top, $start: $end, $content: false) {
@if $top == $end and $top == $bottom and $top == $start {
@include multi-dir() {
#{$prop}: $top;
}
} @else if $top == $bottom and $end == $start and $top != null and $end != null {
@include multi-dir() {
#{$prop}: $top $end;
}
} @else if $end == $start and $top != null and $end != null and $bottom != null {
@include multi-dir() {
#{$prop}: $top $end $bottom;
}
} @else if $top != null and $end != null and $bottom != null and $start != null {
@include ltr() {
#{$prop}: $top $end $bottom $start;
}
@include rtl() {
#{$prop}: $top $start $bottom $end;
}
} @else {
@if $content == true { // TODO check if @content exists instead
@content;
} @else {
@include property-horizontal($prop, $start, $end);
@include multi-dir() {
#{$prop}-top: $top;
#{$prop}-bottom: $bottom;
}
}
}
}
// Add padding for all sides
// @param {string} $top
// @param {string} $end
// @param {string} $bottom
// @param {string} $start
// ----------------------------------------------------------
@mixin padding($top, $end: $top, $bottom: $top, $start: $end) {
@include property(padding, $top, $end, $bottom, $start);
}
// Add margin for all sides
// @param {string} $top
// @param {string} $end
// @param {string} $bottom
// @param {string} $start
// ----------------------------------------------------------
@mixin margin($top, $end: $top, $bottom: $top, $start: $end) {
@include property(margin, $top, $end, $bottom, $start);
}
1 ответ
Я написал конкретное исправление для dir
, поскольку это моя главная проблема, а не другие дубликаты, которые составляют крошечную часть моего пакета.
Довольно просто, и делает свою работу, 0 секунд. Он не объединяется вверх или вниз, но удаляет код и добавляет фрагмент с объединенным направленным кодом в конце. (это не имеет значения для направления, так как все сохраняет свой порядок и специфику)
function joinDirections(contents) {
// This includes multi directional selectors, like `[dir="ltr"] sel, [dir="rtl"] sel`,
// Which go into the ltr pile, but it is ok as the rest (`sel, [dir="rtl"] sel`) is still good.
const dirExp = /\[dir="(.*?)"\](.*?){\s*([^}]*?)\s*}/gm;
let directions = {};
let matches;
while (matches = dirExp.exec(contents)) {
if (!(matches[1] in directions))
directions[matches[1]] = {};
if (!(matches[2] in directions[matches[1]]))
directions[matches[1]][matches[2]] = '';
directions[matches[1]][matches[2]] += matches[3];
}
contents = contents.replace(dirExp, '');
let directionalContents = '';
Object.keys(directions).forEach(dir => {
Object.keys(directions[dir]).forEach(selector => {
directionalContents += `[dir="${dir}"]${selector}{${directions[dir][selector]}}\n`;
});
});
return contents + directionalContents;
}