post.md
23 жовт. 2026 р. · 3 хв читання
Як побудовано цей сайт
Коротка екскурсія архітектурою cmkrw.com: від терміналоподібного макета до Markdown, діаграм і звітів про помилки.
- nextjs
- design
Зміст
Цей сайт навмисно невеликий: одна сторінка, один блог і якомога менше JavaScript. Ось як усе влаштовано — якщо захочете зробити щось подібне.
Термінал — це і є макет
Сайт побудовано як термінальний застосунок. Угорі — панель вкладок, унизу — рядок стану, а все між ними — панелі: рамки із заголовком, вбудованим у верхню межу. Усі панелі малює один невеликий компонент Pane.
Блог у вигляді «список — вміст»: список залишається на місці, поки змінюється допис.
Навігація слідує тій самій ідеї. У блозі список дописів залишається ліворуч, а допис відкривається праворуч, тож для переходу між дописами не потрібне посилання «назад». Комбінації клавіш працюють так само, як у терміналі, а цю таблицю можна відсортувати клацанням по заголовку стовпця:
| 1 | Відкрити головну | усюди |
| 2 | Відкрити блог | усюди |
| j | Прокрутити вниз | усюди |
| k | Прокрутити вгору | усюди |
| g g | На початок | усюди |
| G | У кінець | усюди |
| n | Старіший допис | блог |
| p | Новіший допис | блог |
| d | Змінити тему | усюди |
| ? | Показати комбінації | усюди |
Анімація — переважно CSS. Команда в шапці друкується анімацією steps(), панелі випливають під час прокручування, а лінія під панеллю вкладок і відсоток у рядку стану стежать за позицією прокручування. За prefers-reduced-motion усе це вимикається.
Як пишуться дописи
Дописи — це звичайні Markdown-файли в content/blog/<slug>/<locale>.md, які відображає TanStack Markdown. Якщо перекладу немає, використовується англійський файл, а draft: true не пускає допис у продакшен-збірку.
Решту роблять три інструменти TanStack:
- Markdown розбирає допис і відображає його як елементи React — без проміжного HTML-рядка.
- Highlight розфарбовує блоки коду. До збірки потрапляють лише зареєстровані мною мови, а кольори беруться з тих самих CSS-змінних, що й уся тема.
- Charts і Table вбудовуються однорядковим коментарем з іменем JSON-файлу поруч із дописом.
Блоки коду можуть мати заголовок з іменем файлу:
export function createEmbeds(slug: string, locale: string) {
return {
chart: ({ data = "", kind = "bar", x = "x", y = "y", title = "Chart" }) => (
<TuiChart
rows={getPostDataset(slug, data, locale)}
kind={kind}
x={x}
y={y}
label={title}
/>
),
}
}Звичайні таблиці Markdown теж працюють і отримують той самий стиль:
| Шар | Тека | Відповідальність |
|---|---|---|
| Маршрути | app/ | Сторінки, метадані, значки та зображення |
| Інтерфейс | components/tui/ | Панелі, верхня панель, рядок стану, прив'язки клавіш |
| Функції | features/ | Розділи головної сторінки та блог |
| Утиліти | lib/, hooks/ | Метадані, спостережуваність, введення з клавіатури |
Ось скільки файлів вихідного коду лежить у кожній теці на момент написання:
Показати дані
| folder | files |
|---|---|
| app | 17 |
| components | 16 |
| lib | 16 |
| features | 15 |
| i18n | 4 |
| hooks | 3 |
| types | 1 |
Згенеровані значки та картки для соцмереж
Next.js 16 створює зображення метаданих із файлів у каталозі app/:
icon.tsxіapple-icon.tsxмалюють фавікон і значок для iOS з одного SVG —assets/logo-icon.svg.opengraph-image.tsxмалює картку головної сторінки, а ще один файл — картку для кожного допису.
Файла favicon.tsx немає. У документації прямо сказано, що фавікон згенерувати не можна, тому цю роботу виконує icon.tsx. Усе це спирається на невеликий спільний набір примітивів, тож зміна знака бренду змінює фавікон і всі картки одразу.
Інструментування
За спостережуваність відповідають два файли. instrumentation.ts працює на сервері й повідомляє про помилки запитів. instrumentation-client.ts запускається до гідратації та відстежує помилки клієнта, вебметрики й зміну маршрутів. Обидва передають події репортеру, який розсилає їх одержувачам:
export const serverReporter = createReporter([
consoleSink,
...(endpoint ? [endpointSink(endpoint)] : []),
])Події клієнта надсилаються на /api/observability, де їх записують у журнал. Щоб згодом підключити справжній збирач, досить додати одного одержувача, не чіпаючи жоден із вхідних файлів.
Переклади
Сайт доступний десятьма мовами, зокрема арабською та івритом, які читаються справа наліво. Усі рядки лежать у файлі повідомлень для кожної мови; якщо ключа раптом бракує, знизу підмішуються повідомлення мови за замовчуванням, тож сторінка ніколи не лишається порожньою. Дописи працюють так само: у кожної мови свій файл, а за його відсутності використовується англійський.