Разработка интерфейсов

Как сделать разрядку прописных на статическом сайте

Типографика говорит, что идущие подряд прописные следует разрежать:

PROTECTIONPROTECTION

Капс периодически встречается в контенте любого сайта, но без обработки на этапе сборки остаётся неразреженным. Сегодня напишем авторазрядку. С точки зрения разработчика это значит, что, когда прописные в тексте идут подряд, их нужно обнаружить, обернуть в <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,
	})
})

См. также

Советы бюро Горбунова:

Прожмите реакцию