Типографика говорит, что идущие подряд прописные следует разрежать:
PROTECTION → PROTECTION
Капс периодически встречается в контенте любого сайта, но без обработки на этапе сборки остаётся неразреженным. Сегодня напишем авторазрядку. С точки зрения разработчика это значит, что, когда прописные в тексте идут подряд, их нужно обнаружить, обернуть в <span> и стилизовать.
Воспользуемся регулярным выражением:
/\p{Uppercase_Letter}[\p{Uppercase_Letter}\d]+/guЭто выражение ищет все последовательности символов, которые начинаются с заглавной буквы и заканчиваются тоже заглавной буквой либо цифрой. Флаг /u включает поиск по свойствам Юникода.
Как применить в проекте
Напишем трансформацию — Яваскрипт-код, который изменяет статическую ХТМЛ-страницу при постпроцессинге. Трансформация обернёт капитель в <span> с классом .caps:
/**
* @param {Window} window
*/
module.exports = (window) => {
const capitals = /\p{Lu}[\p{Lu}\d]+/gu
const { document } = window
// NodeFilter.SHOW_TEXT
const SHOW_TEXT = 4
// Находим в дереве текстовые узлы
const walker = document.createTreeWalker(document.body, SHOW_TEXT)
let node
while ((node = walker.nextNode())) {
const text = node.nodeValue
if (!text.match(capitals)) continue
const parent = node.parentNode
// Помещаем в обёртку все символы, кроме последнего, так как
// `letter-spacing` добавляет отступ справа от каждой буквы
const updatedHtml = text.replace(capitals, (match) => {
const head = match.slice(0, -1)
const tail = match.slice(-1)
return `<span class="caps">${head}</span>${tail}`
})
const tempDiv = document.createElement('div')
tempDiv.innerHTML = updatedHtml
while (tempDiv.firstChild) {
parent.insertBefore(tempDiv.firstChild, node)
}
parent.removeChild(node)
}
}Пример вывода:
<p>ХТМЛ-страница</p>
↓
<p><span class="caps">ХТМ</span>Л-страница</p>Теперь напишем класс .caps.
Два способа выполнить разрядку в ЦСС
Первый. Если шрифт поддерживает капитель — уменьшенные прописные буквы, — удобно использовать встроенную Опентайп-функцию.
Капитель — красивая и не отвлекает внимание в наборе:
.caps {
letter-spacing: 0.05em;
}
.small-caps {
font-synthesis: none;
font-feature-settings: 'smcp';
font-variant-caps: unicase;
}Чтобы использовать капитель, нужно обернуть прописные в два спана. Один уменьшает буквы, второй разрежает. Отредактируйте трансформацию:
const updatedHtml = text.replace(capitals, (match) => {
const head = match.slice(0, -1)
const tail = match.slice(-1)
return `<span class="small-caps"><span class="caps">${head}</span>${tail}</span>`
})Второй. Помогает визуально приблизиться к капители, если в шрифте такой функции нет:
.caps {
letter-spacing: 0.07em;
}
Интеграция с движками
Eleventy:
// eleventy.config.js
const capitalsTransform = require('./src/transforms/capitals-transform.js')
const transforms = [capitalsTransform, quotedLinkTransform]
const { parseHTML } = require('linkedom')
config.addTransform('htmlTransforms', async (content, outputPath) => {
if (outputPath && outputPath.endsWith('.html')) {
const window = parseHTML(content)
for (const transform of transforms) {
await transform(window, content, outputPath)
}
return window.document.toString()
}
return content
})Astro:
// src/middleware.ts
import { defineMiddleware } from 'astro:middleware'
import { parseHTML } from 'linkedom'
import { transformCapitals } from './transforms/capitals'
export const onRequest = defineMiddleware(async (_, next) => {
const response = await next()
const html = await response.text()
const window = parseHTML(html)
transformCapitals(window)
const updatedHtml = window.document.toString()
return new Response(updatedHtml, {
status: 200,
headers: response.headers,
})
})См. также
Советы бюро Горбунова:

