- PVSM.RU - https://www.pvsm.ru -
Организация отступов при верстке и композиции элементов на странице требует определенной сноровки, чтобы обеспечить правильные комбинации и желаемый результат.
А если в дизайн-макете запрашиваемые отступы указаны без учета высоты линии и пустых областей отрисовки шрифтов (заплечики, диакритики, нисходящая высота)? Как сделать верстку максимально приближенной к макету эффективно и без долгих ручных пересчетов?
Интерлиньяж и вертикальный ритм позволяют унифицировать расстояния между элементами, но не решают вопрос компенсации расстояний с учетом текстовых метрик используемых шрифтов.
При разработке приложений для клиентов класса люкс особое внимание уделяется точности имплементации интерфейса, где каждый пиксель на счету.
В данной статье мы рассмотрим подход, призванный оптимизировать рутинную работу вокруг отступов — автоматизированная корректировка отступов с учётом высоты линии целевых элементов и текстовых метрик используемых шрифтов на этапе пре- или пост-компиляции стилей.
И так, расстояние между элементами, содержащими текст, от базовой линии (baseline) одного элемента до верхней границы заглавной буквы (cap line) следующего элемента должно быть определенного значения (например: 24px), исключая область отрисовки диакритических знаков и заплечики (shoulders).
При использовании данного значения в таких декларациях, как, например, padding или margin — между элементами формируется область отступа, содержащая:
В качестве примера рассмотрим реализацию баннера с текстом:
<div class="banner">
<div class="banner__media">
<div class="banner__content">
<h2 class="banner__content-title">Men</h2>
<p class="banner__content-description">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text.</p>
<ul class="banner__content-links">
<li>
<a href="#">Women</a>
</li>
<li>
<a href="#">Men</a>
</li>
<li>
<a href="#">Boys</a>
</li>
<li>
<a href="#">Girls</a>
</li>
</ul>
</div>
</div>
</div>
Типографические стили, применяемые к условному проекту:
/*
Typography styles
*/
.type-h2 {
font-family: Arial;
font-size: 35px;
line-height: 40px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 2px;
html[lang^="de"] & {
font-family: 'Karla Bold';
font-size: 35px;
line-height: 40px;
}
}
.type-p1 {
font-family: Arial;
font-size: 16px;
line-height: 22px;
font-weight: normal;
letter-spacing: 0;
html[lang^="de"] & {
font-family: 'Karla Regular';
font-size: 16px;
line-height: 22px;
}
}
@media only screen and (min-width: 480px) and (max-width: 767px) {
.type-h2 {
font-family: Arial;
font-size: 50px;
line-height: 55px;
html[lang^="de"] & {
font-family: 'Karla Bold';
font-size: 50px;
line-height: 55px;
}
}
.type-p1 {
font-family: Arial;
font-size: 16px;
line-height: 22px;
html[lang^="de"] & {
font-family: 'Karla Regular';
font-size: 16px;
line-height: 22px;
}
}
}
@media only screen and (min-width: 768px) {
.type-h2 {
font-family: Arial;
font-size: 80px;
line-height: 90px;
html[lang^="de"] & {
font-family: 'Karla Bold';
font-size: 80px;
line-height: 90px;
}
}
.type-p1 {
font-family: Arial;
font-size: 19px;
line-height: 26px;
html[lang^="de"] & {
font-family: 'Karla Regular';
font-size: 19px;
line-height: 26px;
}
}
}
(Размер шрифта и высота линии переопределяются для класса заголовка (.type-h2) и параграфа (.type-p1) в зависимости от брэйкпоинта и локали.)
А также стили непосредственно для баннера:
/*
banner.scss
*/
@import 'path/to/typography.scss';
.banner {
display: flex;
flex-direction: column;
justify-content: flex-end;
background: url('path/to/banner-image.png') no-repeat center bottom;
background-size: cover;
&__content {
color: #fff;
padding: 48px 0 48px 48px;
&-title {
@extend .type-h2;
}
&-description {
@extend .type-p1;
max-width: 80%;
margin-top: 24px;
}
&-links {
@extend .type-p1;
display: flex;
flex-direction: row;
margin-top: 24px;
li {
margin-left: 18px;
&:first-child {
margin-left: 0;
}
a {
color: inherit;
}
}
}
}
}
(Для наглядности, области, обозначающие оступы смещены к визуальным границам элементов)
Результат для английской локали:
Результат для немецкой локали (заголовок изменен по сравнению с английским вариантом, чтобы продемонстрировать область диакритики):
Как и стоило ожидать, фактически использованное значение отступов (24px) дополнилось пустыми областями высоты линий и ограничительных рамок шрифта, областью отрисовки диакритических знаков, а также областью нисходящей высоты шрифта.
Для достижения требуемого результата, были рассмотрены следующие подходы:
1. Уменьшение высоты линии текстового элемента.
Минусы:
— Не подходит для многострочного текста, так как влияет на его интерлиньяж.
— При сбрасывании до фактического значения высоты шрифта оставляет пустые области ограничительной рамки шрифта — заплечики (shoulders).
2. Смещение на «лишнюю» высоту путём использования отрицательных значений отступов непосредственно текстового элемента (или его содержащего) или, как альтернатива, его псевдоэлемента.
.selector {
margin-top: -6px;
}
.selector-2:before {
content: '';
display: table;
margin-top: -6px;
}
Минусы:
3. Уменьшение размера ограничительной рамки шрифта до границ рендеринга глифа, путём модификации самого шрифта.
Минусы:
4. Уменьшение значения применяемого отступа для визуального соответствия требуемому значению.
Минусы:
Из перечисленых способов решения представленной задачи самым надежным и точным, но в то же время и самым трудоемким, является пункт 4.
Для приложений, поддерживающих различные брэйкпоинты и несколько локализаций это означает, что необходимо проводить точный просчет значений, применяемых к используемым декларациям, а также создавать дополнительные правила, учитывающие медиа-запросы, локализационные селекторы, а также текстовые метрики используемых шрифтов. Удвольствие, мягко говоря, сомнительное. Более того, временные затраты на описание точных стилей и их последующая верификация увеличивают сроки разработки, добавляя рутинную работу как разработчикам, так и тестировщикам.
Для автоматизирования процесса пересчета отступов между текстовыми элементами на основании типографических правил и текстовых метрик используемых шрифтов, а также автоматизированной поддержки всех зависимых комбинаций, используемых в типографических стилях был создан postcss плагин postcss-text-indentation-adjustment [2]
postcss-text-indentation-adjustment — postcss плагин, позволяющий скорректировать используемые в стилях значения отступов с учетом типографических стилей, а также с учетом текстовых метрик используемых шрифтов.
Алгоритм работы:
1. Извлечение текстовых метрик используемых в проекте шрифтов
2. Извлечение данных из используемых типографических стилей
3. Инициализация плагина и включение его в процесс сборки
4. Описание корректировки значения декларации с помощью специального синтаксиса с указанием типографических селекторов
5. Сборка проекта и получение результата корректировок со всеми возможными комбинациями внешних правил для типографических стилей (медиа-запросы, родительские селекторы и их комбинации)
Преимущества:
Пример правил, применяемых к типографическому стилю заголовка с классом .type-h2, описывающий переопределение семейства шрифта, а также его размер и высоту линии для определенной ширины экрана и немецкой локали:
.type-h2 {
font-family: Arial;
font-size: 35px;
line-height: 40px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 2px;
html[lang^="de"] & {
font-family: 'Karla Bold';
font-size: 38px;
line-height: 46px;
}
}
@media only screen and (min-width: 768px) {
.type-h2 {
font-family: Arial;
font-size: 80px;
line-height: 90px;
html[lang^="de"] & {
font-family: 'Karla Bold';
font-size: 86px;
line-height: 94px;
}
}
4. При использовании css препроцессора (например, sass, scss, less) — поддерживается вложенность обрабатываемых правил, а также использование переменных.
Точность корректировок плагина зависит от текстовых метрик шрифтов, передаваемых в качестве аргумента в момент его инициализации, поэтому прежде чем интегрировать плагин postcss-text-indentation-adjustment в наш пример, рассмотрим как текстовые метрики могут быть извлечены.
Описанный далее способ извлечения текстовых метрик оформлен в отдельный пакет font-metrics [3] и позволяет получить текстовые метрики из локально сохраненных, системных или расположенных удаленно шрифтов, путем использования CSS Font Loading API [4], отрисовывая текст на холсте в браузере Chrome при включенном флаге «experimentalCanvasFeatures: true» с последующим сохранением значений в файл.
Для извлечения текстовых метрик воспользуемся методом контекста холста measureText [5]. Согласно документации TextMetrics [6] единственное доступное значение объекта TextMetrics [7] при отрисовке в браузере — ширина отрисованного текста (width [8]).
Для решения нашей задачи необходимы более детальные параметры, такие как fontBoundingBoxAscent [9] (расстояние от базовой линии до верхней границы ограничительной рамки шрифта), fontBoundingBoxDescent [10] (расстояние между базовой линией и нижней границей ограничительной рамки шрифта), а также alphabeticBaseline [11] (расстояние между выбранной для отрисовки базовой линией [12] и алфавитной базовой линией) и hangingBaseline [13] (расстояние между выбранной для отрисовки базовой линией и верхней границей отрисовки глифа без учета области диакритических знаков), доступные лишь в хроме при включенном флаге ExperimentalCanvasFeatures: true.
Сумма значение fontBoundingBoxAscent и fontBoundingBoxDescent даст фактическую высоту области отрисовки ограничительной рамки шрифта, а разница значений hangingBaseline и alphabeticBaseline — фактическую высоту прописной букы (cap height) без области диакритических знаков, заплечиков и нисходящей высоты.
Так как значения представлены в CSS пикселях и будут высчитаны относительно значения font-size, использованного для отрисовки текста — можно воспользоваться отношением разницы высоты ограничительной рамки шрифта и высоты прописной буквы к высоте прописной буквы. Это позволит вычислить относительное значение высоты исключаемых областей для целевого размера шрифта, используемого в наших стилях.
Но обо всем по порядку.
Нам потребуется html страница с созданным на ней холстом, доступная для нашего скрипта.
Для манипуляции с холстом в браузере с включенным флагом ExperimentalCanvasFeatures, воспользуемся библиотекой Nightmare [14]:
import Nightmare from 'nightmare';
import fse from 'fs-extra';
const browser = Nightmare({
show: false,
webPreferences: {
experimentalCanvasFeatures: true
}
});
const pageUrl = 'your/path/to/canvas/page';
// параметры шрифтов, относительно которых нужно снять текстовые метрики
const fontsData = {
fonts: [{
fontFamily: 'Arial'
}, {
fontFamily: 'Karla Regular',
src: '//fonts.gstatic.com/s/karla/v6/S1bXQ0LrY7AzefpgNae9sYDGDUGfDkXyfkzVDelzfFk.woff2'
}, {
fontFamily: 'Karla Bold',
src: '//fonts.gstatic.com/s/karla/v6/r3NqIkFHFaF3esZDc3WT5BkAz4rYn47Zy2rvigWQf6w.woff2'
}],
fontSize: 24
};
browser.gotTo(pageUrl)
.evaluate(data => {
// Так как часть шрифтов может быть расположена удаленно и потребовать время на загрузку - используем Promise
return new window.Promise((rootResolve, rootReject) => {
const {fonts, fontSize} = data;
// При наличии не системных, а подключаемых извне или локально шрифтов, добавим их в очередь на загрузку, обернув в Promise
const fontsToLoad = fonts.reduce((result, fontData) => {
const {src, fontFamily} = fontData;
// При остутствии параметра src будем считать, что используется системный шрифт
if (typeof src === 'string') {
return result;
}
const promise = new window.Promise((resolve, reject) => {
// Для подключения нового шрифта воспользуемся конструктором FontFace
const fontFace = new window.FontFace(fontFamily, `url(${encodeURI(src)})`);
// и добавим новый font face, когда шрифт будет загружен
fontFace.load().then(function () {
document.fonts.add(fontFace);
resolve();
}).catch(function (err) {
reject(err);
});
});
return promise;
}, []);
const fontsMetrics = {};
// Как только все шрифты загружены - последовательно отрисуем их на холсте и извлечем текстовые метрики
window.Promise.all(fontsToLoad).then(() => {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// В качестве базовой линии используем алфавитную базовую линию
ctx.textBaseline = 'alphabetical';
fonts.forEach(fontData => {
// Применим шрифт к холсту и извлечем текстовые метрики
ctx.font = `${fontSize}px ${fontData.fontFamily}`;
const metrics = ctx.measureText('Example');
fontsMetrics[fontData.fontFamily] = metrics;
});
});
rootResolve(fontsMetrics);
});
}, fontsData)
.end()
.then(result => {
// Сохраним текстовые метрики в файл для последующего использования
const content = JSON.stringify(result, null, 4);
fse.outputFile('desired/path/to/metrics.json', content, function (err) {
if (err) {
console.error(err);
}
});
});
В результате получим текстовые метрики необходимых шрифтов:
{
"Arial": {
"actualBoundingBoxAscent": 0,
"actualBoundingBoxDescent": 24,
"actualBoundingBoxLeft": 0,
"actualBoundingBoxRight": 93,
"alphabeticBaseline": 0,
"emHeightAscent": 0,
"emHeightDescent": 0,
"fontBoundingBoxAscent": 22,
"fontBoundingBoxDescent": 5,
"hangingBaseline": 17.600000381469727,
"ideographicBaseline": -5,
"width": 93.375
},
"Karla Bold": {
"actualBoundingBoxAscent": 0,
"actualBoundingBoxDescent": 24,
"actualBoundingBoxLeft": 0,
"actualBoundingBoxRight": 85,
"alphabeticBaseline": 0,
"emHeightAscent": 0,
"emHeightDescent": 0,
"fontBoundingBoxAscent": 22,
"fontBoundingBoxDescent": 6,
"hangingBaseline": 17.600000381469727,
"ideographicBaseline": -6,
"width": 85.30078125
},
"Karla Regular": {
"actualBoundingBoxAscent": 0,
"actualBoundingBoxDescent": 24,
"actualBoundingBoxLeft": 0,
"actualBoundingBoxRight": 85,
"alphabeticBaseline": 0,
"emHeightAscent": 0,
"emHeightDescent": 0,
"fontBoundingBoxAscent": 22,
"fontBoundingBoxDescent": 6,
"hangingBaseline": 17.600000381469727,
"ideographicBaseline": -6,
"width": 85.30078125
}
}
В рассматриваемом примере потребуются текстовые метрики лишь для шрифтов Arial, «Karla Bold» и «Karla Regular», но на практике это может быть любой доступный системный, кастомный или доставляемый через cdn шрифт в том количестве, которое необходимо для поддержания всех локализаций вашего проекта.
Как правило, операция получения текстовых метрик является однократной и должна заново запускаться лишь при изменении используемых в проекте шрифтов.
Для большинства случаев достаточно однократно сгенерировать значения, сохранить их в файл и использовать при сборке.
При использовании пакета font-metrics процесс генерации текстовых метрик значительно упрощается:
import fontMetrics from 'font-metrics';
const fontParser = fontMetrics({
fonts: [{
fontFamily: 'Arial'
}, {
fontFamily: 'Karla Regular',
src: '//fonts.gstatic.com/s/karla/v6/S1bXQ0LrY7AzefpgNae9sYDGDUGfDkXyfkzVDelzfFk.woff2'
}, {
fontFamily: 'Karla Bold',
src: '//fonts.gstatic.com/s/karla/v6/r3NqIkFHFaF3esZDc3WT5BkAz4rYn47Zy2rvigWQf6w.woff2'
}],
output: './font-metrics',
filename: 'font-metrics.json'
});
fontParser.parse();
Результат:
{
"metrics": {
"Arial": {
"_fontSize": 24,
"_textBaseline": "alphabetic",
"actualBoundingBoxAscent": 0,
"actualBoundingBoxDescent": 24,
"actualBoundingBoxLeft": 0,
"actualBoundingBoxRight": 93,
"alphabeticBaseline": 0,
"emHeightAscent": 0,
"emHeightDescent": 0,
"fontBoundingBoxAscent": 22,
"fontBoundingBoxDescent": 5,
"hangingBaseline": 17.600000381469727,
"ideographicBaseline": -5,
"width": 93.375
},
"Karla Bold": {
"_fontSize": 24,
"_textBaseline": "alphabetic",
"actualBoundingBoxAscent": 0,
"actualBoundingBoxDescent": 24,
"actualBoundingBoxLeft": 0,
"actualBoundingBoxRight": 85,
"alphabeticBaseline": 0,
"emHeightAscent": 0,
"emHeightDescent": 0,
"fontBoundingBoxAscent": 22,
"fontBoundingBoxDescent": 6,
"hangingBaseline": 17.600000381469727,
"ideographicBaseline": -6,
"width": 85.30078125
},
"Karla Regular": {
"_fontSize": 24,
"_textBaseline": "alphabetic",
"actualBoundingBoxAscent": 0,
"actualBoundingBoxDescent": 24,
"actualBoundingBoxLeft": 0,
"actualBoundingBoxRight": 85,
"alphabeticBaseline": 0,
"emHeightAscent": 0,
"emHeightDescent": 0,
"fontBoundingBoxAscent": 22,
"fontBoundingBoxDescent": 6,
"hangingBaseline": 17.600000381469727,
"ideographicBaseline": -6,
"width": 85.30078125
}
},
"src": [
{
"fontFamily": "Arial"
},
{
"fontFamily": "Karla Regular",
"src": "//fonts.gstatic.com/s/karla/v6/S1bXQ0LrY7AzefpgNae9sYDGDUGfDkXyfkzVDelzfFk.woff2"
},
{
"fontFamily": "Karla Bold",
"src": "//fonts.gstatic.com/s/karla/v6/r3NqIkFHFaF3esZDc3WT5BkAz4rYn47Zy2rvigWQf6w.woff2"
}
]
}
Принцип работы плагина postcss-text-indentation-adjustment основывается на описании корректировок исходного значения, применяемого к декларации (в рассматриваемом примере это 24px) с указанием типографических классов, относительно которых нужно скорректировать это значение. Описание корректировки выполняется в виде комментария внутри значения декларации и позволяет безопасно внедрять плагин в сборку проекта.
Рассмотрим, как корректировки могут быть описаны для нашего примера:
/*
banner.scss
*/
@import 'path/to/typography.scss';
.banner {
display: flex;
flex-direction: column;
justify-content: flex-end;
background: url('path/to/banner-image.png') no-repeat center bottom;
background-size: cover;
&__content {
color: #fff;
padding: 48px 0 48px 48px /* {48px, .type-h2} 0 {48px, .type-p1} 48px */;
&-title {
@extend .type-h2;
}
&-description {
@extend .type-p1;
max-width: 80%;
margin-top: 24px /* {24px, .type-h2, .type-p1} */;
}
&-links {
@extend .type-p1;
display: flex;
flex-direction: row;
margin-top: 24px /* {24px, .type-p1, .type-p1} */;
li {
margin-left: 18px;
&:first-child {
margin-left: 0;
}
a {
color: inherit;
}
}
}
}
}
Импортируем необходимые библиотеки.
// node-sass для компиляции исходного файла типографических стилей
import nodeSass from 'node-sass';
// postcss для манипуляции со стилями
import postcss from 'postcss';
// fse или fs для чтения исходного файла типографических стилей и сохранения результата
import fse from 'fs-extra';
// path для нормализации пути к файлу
import path from 'path';
// postcss-scss для использования postcss до компиляции scss файлов в css
import postcssSCSS from 'postcss-scss';
// postcss-text-indentation-adjustment для парсинга типографических стилей, создания корректировок и комбинаций зависимых правил
import textIndentationAdjustment, {parser} from 'postcss-text-indentation-adjustment';
// postcss-partial-import для инлайн ипорта scss файлов, использованных в основных стилях проекта
import postcssPartialImport from 'postcss-partial-import';
// css-mqpacker для группировки медиа-запросов (или любой другой инструмент)
import cssMqPacker from 'css-mqpacker';
// postcss-merge-rules для объединения css селекторов на основании идентичных деклараций (или любой другой инструмент)
import mergeRules from 'postcss-merge-rules';
// Текстовые метрики, извлеченные на одном из предыдущих шагов
import {metrics} from 'path/to/metrics.json';
// Скомпилируем типографические стили из scss в css для их последующего парсинга
const typography = nodeSass.renderSync({
file: 'path/to/typography.scss'
}).css.toString();
// Инициализируем парсер, передав текстовые метрики, полученные на одном из предыдущих шагов
const typographyParser = parser({
metrics: metrics
});
// Распарсим типографические стили для их последующего использования
const parsedTypography = typographyParser.parse(typography);
// Инициализируем postcss плагин, передав подоготовленные парсером данные типографических стилей
const typographyAdjustmentPlugin = textIndentationAdjustment({
corrections: parsedTypography,
plainCSS: false // при использовании плагина для корректировки значений на этапе препроцессинга стилей (например, при использовании scss)
});
// Используем postcss с синтаксисом scss
fse.readFile('path/to/banner.scss', (err, scss) => {
postcss([postcssPartialImport(), postcssTypographyAdjustmentPlugin])
.process(scss, {
syntax: postcssSCSS,
from: 'path/to/banner.scss',
to: `path/to/banner.css`
})
.then(postcssResult => {
return new Promise((resolve, reject) => {
nodeSass.render({
data: postcssResult.css,
outputStyle: 'expanded'
}, (err, result) => {
resolve(result);
});
});
})
.then(result => {
// Объединим медиа-запросы и правила с одинаковыми декларациями
return postcss([mergeRules(), cssMqPacker()]).process(result.css);
})
.then(result => {
fse.outputFile('path/to/banner.css', result.css);
})
.catch(e => {
console.log(e);
});
});
В результате будет скомпилирован файл, содержащий следующие правила (типографические стили и результат работы @extend исключены, чтобы сделать акцент на сгенерированных корректировках):
.banner {
display: flex;
flex-direction: column;
justify-content: flex-end;
background: url("path/to/banner-image.png") no-repeat center bottom;
background-size: cover;
}
.banner__content {
color: #fff;
padding: 39.5px 0 43px 48px;
}
.banner__content-description {
max-width: 80%;
margin-top: 10.5px;
}
html[lang^="de"] .banner__content-description {
margin-top: 10.5px;
}
.banner__content-links {
display: flex;
flex-direction: row;
margin-top: 14px;
}
.banner__content-links li {
margin-left: 18px;
}
.banner__content-links li:first-child {
margin-left: 0;
}
.banner__content-links li a {
color: inherit;
}
html[lang^="de"] .banner__content-links {
margin-top: 14px;
}
html[lang^="de"] .banner__content {
padding: 39.5px 0 43px 48px;
}
@media only screen and (min-width: 480px) and (max-width: 767px) {
.banner__content-description {
margin-top: 8.5px;
}
html[lang^="de"] .banner__content-description {
margin-top: 7.5px;
}
.banner__content-links {
margin-top: 14px;
}
html[lang^="de"] .banner__content-links {
margin-top: 14px;
}
.banner__content {
padding: 37.5px 0 43px 48px;
}
html[lang^="de"] .banner__content {
padding: 36.5px 0 43px 48px;
}
}
@media only screen and (min-width: 768px) {
.banner__content-description {
margin-top: -0.5px;
}
html[lang^="de"] .banner__content-description {
margin-top: -1.5px;
}
.banner__content-links {
margin-top: 11px;
}
html[lang^="de"] .banner__content-links {
margin-top: 11px;
}
.banner__content {
padding: 30px 0 41.5px 48px;
}
html[lang^="de"] .banner__content {
padding: 29px 0 41.5px 48px;
}
}
Результат для английской локали:
Результат для немецкой локали:
Как можно увидеть, результат рендеринга отступов существенно скорректировался.
(Скомпилированные значения дополнительно пропущены через плагины объединения селекторов и медиа-запросов — css-mqpacker, postcss-merge-rules)
1. Корректировки выполняются в виде комментария и располагаются внутри значения декларации.
.rule-selector {
padding-top: 24px /* */;
}
2. Каждое значение, которое должно быть скорректировано, обрамляется в фигурные скобки (корректирующая группа)
.rule-selector {
padding-top: 24px /* {24px} */;
}
3. Типографические классы или теги, относительно которых корректировки должны быть применены, указываются через запятую после базового значения
.rule-selector {
padding-top: 24px /* {24px, h3, .type-h2} */;
}
4. При необходимости указания нескольких значений, каждое из них помещается в свою корректирующую группу, которая будет заменена вычисленным значением
.rule-selector {
padding-top: 24px 0 24px /* {24px, h3, .type-h2} 0 {24px, .type-h2, .copy-p1} */;
}
5. Контент комментария с результатом вычисления всех корректирующих групп устанавливается плагином в финальное значение декларации
.rule-selector {
padding-top: 17px 0 19px;
}
6. При наличии родительских селекторов в исходном файле типографических классов — создается дополнительное правило, включающее комбинацию родительского и целевого селектора, а также декларации с высчитанными значениями для всех комбинаций типографических селекторов.
/* typography */
.p1 {
font-size: 16px;
line-height: 24px;
font-family: Arial;
.parent-selector-1 & {
font-size: 18px;
line-height: 26px;
font-family: Arial;
}
}
.p2 {
font-size: 18px;
line-height: 22px;
font-family: Arial;
.parent-selector-2 & {
font-size: 14px;
line-height: 20px;
font-family: Arial;
}
}
/* input */
.rule-selector {
padding-top: 24px /* {24px, .p1, .p2} */;
}
/* output */
.rule-selector {
padding-top: 13px;
}
.parent-selector-1 .rule-selector {
padding-top: 12px;
}
.parent-selector-2 .rule-selector {
padding-top: 13px;
}
7. При наличии медиа-запросов или медиа-запросов в комбинации с родительскими селекторами — создаются все возможные комбинации для типографических селекторов, участвующих в корректировке:
/* typography */
.p1 {
font-size: 16px;
line-height: 24px;
font-family: Arial;
.parent-selector-1 & {
font-size: 18px;
line-height: 26px;
font-family: Arial;
}
@media (min-width: 768px) {
font-size: 22px;
line-height: 28px;
font-family: Arial;
html[lang^="de"] .parent-selector-3 & {
font-size: 28px;
line-height: 40px;
font-family: Arial;
}
}
}
.p2 {
font-size: 18px;
line-height: 22px;
font-family: Arial;
.parent-selector-2 & {
font-size: 14px;
line-height: 20px;
font-family: Arial;
}
@media (min-width: 321px) {
font-size: 22px;
line-height: 28px;
font-family: Arial;
html[lang^="de"] .parent-selector-4 & {
font-size: 28px;
line-height: 40px;
font-family: Arial;
}
}
}
/* input */
.rule-selector {
padding-top: 24px /* {24px, .p1, .p2} */;
}
/* output */
.rule-selector {
padding-top: 13px;
}
.parent-selector-1 .rule-selector {
padding-top: 12px;
}
.parent-selector-2 .rule-selector {
padding-top: 13px;
}
@media (min-width: 768px) {
.rule-selector {
padding-top: 13px;
}
html[lang^="de"] .parent-selector-3 .rule-selector {
padding-top: 14px;
}
}
@media (min-width: 321px) {
.rule-selector {
padding-top: 12px;
}
html[lang^="de"] .parent-selector-4 .rule-selector {
padding-top: 14px;
}
}
8. Проверить параметры, использованные для вычисления финальных значений можно с помощью флага --debug
/* input */
.rule-selector {
padding-top: 24px /* {24px, .p1, .p2} --debug */;
}
В результате в консоль будет выведена часть данных, используемых для создания корректировки.
const typographyAdjustmentPlugin = textIndentationAdjustment({
corrections: parsedTypography,
plainCSS: false
});
Спасибо за внимание. Буду рад услышать разумную критику, а также предложения по улучшению и оптимизации
Автор: Dashukin
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/270981
Ссылки в тексте:
[1] Источник: http://indians.ru/a-font-sizes.htm
[2] postcss-text-indentation-adjustment: https://github.com/dashukin/postcss-text-indentation-adjustment
[3] font-metrics: https://github.com/dashukin/font-metrics
[4] CSS Font Loading API: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Font_Loading_API
[5] measureText: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/measureText
[6] TextMetrics: https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
[7] объекта TextMetrics: https://html.spec.whatwg.org/multipage/canvas.html#textmetrics
[8] width: https://html.spec.whatwg.org/multipage/canvas.html#dom-textmetrics-width
[9] fontBoundingBoxAscent: https://html.spec.whatwg.org/multipage/canvas.html#dom-textmetrics-fontboundingboxascent
[10] fontBoundingBoxDescent: https://html.spec.whatwg.org/multipage/canvas.html#dom-textmetrics-fontboundingboxdescent
[11] alphabeticBaseline: https://html.spec.whatwg.org/multipage/canvas.html#dom-textmetrics-alphabeticbaseline
[12] базовой линией: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
[13] hangingBaseline: https://html.spec.whatwg.org/multipage/canvas.html#dom-textmetrics-hangingbaseline
[14] Nightmare: http://www.nightmarejs.org/
[15] Источник: https://habrahabr.ru/post/344984/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.