О дизайне, медиа, мире

Сайт-портфолио как отдельный продукт

→Статья переехала в новый блог

Время чтения ±20 минут

За 8 лет опыта я сменил несколько десятков портфолио. Только за последние три года — пять. Надо понимать, что для дизайнера портфолио — тоже инструмент. Иногда нужно иметь несколько версий для разных целей — рассказать потенциальному заказчику на фрилансе о своём визуале и рассказать нанимающему менеджеру об умении выстраивать процессы — это две разные задачи. Обычно портфолио для меня — это способ рассказать о сделанной работе. В этот раз мне хотелось, чтобы сам сайт тоже стал работой: не декоративной оболочкой вокруг кейсов, а действующим примером того, как я проектирую сложные интерфейсы.

Большинство портфолио устроено одинаково: обложка, короткое представление автора и вертикальная лента проектов. Такой формат понятен, но у него есть ограничение — он показывает результаты последовательно, хотя реальная работа продуктового дизайнера почти никогда не устроена как последовательность независимых экранов.

Она больше похожа на карту связей. Один проект ведёт к другому, продуктовые решения опираются на исследования, рядом существуют рабочие эксперименты, результаты, клиенты и контекст самого автора. Мне хотелось показать эту структуру буквально — через пространственный интерфейс, похожий на node-based-системы, Miro и FigJam.

При этом за визуальной метафорой должна была стоять полноценная продуктовая логика: управляемый Canvas, физика карточек, несколько пространств, мобильная версия, альтернативная лента, бесшовное открытие кейсов и WordPress как единый источник данных.

Эта статья не столько про необычный вид портфолио, сколько про способ работы над ним. Про решения, которые пришлось принимать между Figma, браузером, структурой данных и редактором WordPress. И про то, почему современному дизайнеру полезно понимать фронтенд достаточно хорошо, чтобы проектировать не только состояния экрана, но и поведение всей системы.

Кому будет полезно?

  • ▸ Продуктовым дизайнерам, которые растут в сторону Lead или Principal Designer и хотят влиять не только на макеты, но и на устройство продукта.
  • ▸ Дизайнерам, которые начинают работать с AI coding agents и ищут более зрелый процесс, чем «написать промпт и принять результат».
  • ▸ Командам, которые проектируют нестандартные интерфейсы с большим количеством состояний, жестов и динамического контента.
  • ▸ Тем, кто использует WordPress как CMS, но не хочет ограничиваться устройством классической темы.

Контекст

У меня уже был работающий сайт на WordPress с кейсами, изображениями и базовыми текстами. Поэтому задача не состояла в том, чтобы сделать ещё одну статичную страницу и вручную перенести в неё контент.

К WordPress я пришёл не сразу, но возвращение к этой «волшебной палочке» стало каким-то грандиозным инсайтом. В нём всё есть: возможность быстро развернуть его и локально, и на хостинге, работа с БД, мобильная наладка процессов и бизнес-логики, headless-режим для своего фронта и прекрасная админка. В нынешних условиях писать для WP визуалы и плагины — одно удовольствие.

Админка WP может сразу показать какой-нибудь нужный самописный виджет

Я хотел сохранить WordPress как канонический источник данных, но полностью заменить способ их представления. Это важное ограничение: новая тема должна была не просто отрисовать существующие записи, а превратить CMS в редактор пространственной системы.

В центре первого макета находилась карточка Persona — моя цифровая визитка. Слева от неё располагались контакты, клиенты и результаты, справа — проекты и Playground. Все карточки соединялись с центральной точкой, образуя читаемую карту портфолио.

Так появился основной образ интерфейса: не страница с блоками, а рабочее пространство, внутри которого человек сам выбирает направление движения.

Визуально эта идея была достаточно ясной уже в Figma. Главная сложность началась дальше: как сделать так, чтобы метафора не рассыпалась при первом же реальном взаимодействии.

Постановка задачи

Я сформулировал несколько обязательных свойств системы.

Во-первых, Canvas должен ощущаться «бесконечным». Пользователь может двигать его рукой, зажатым пробелом или средней кнопкой мыши, менять масштаб колесом и жестами, как в знакомых графических инструментах.

Во-вторых, бесконечность не должна быть буквальной. Если разрешить двигаться без границ, человек легко потеряет и карточки, и точку возврата. Поэтому фактическая область перемещения рассчитывается по геометрии контента и получает дополнительный свободный пояс. Canvas остаётся просторным, но не превращается в пустую вселенную.

В-третьих, карточки должны быть живыми объектами. Их можно перемещать, они не накладываются друг на друга, новые сущности автоматически находят место, а связи перестраиваются вслед за ними.

В-четвёртых, пространственный интерфейс не должен становиться обязательным испытанием для каждого посетителя. В любой момент его можно переключить в Feed — обычную вертикальную ленту с тем же контентом.

Режим ленты был придуман и реализован с самого начала

И наконец, всё это должно управляться из WordPress. Новая карточка, опубликованная в админке, должна появиться на открытом сайте, попасть в нужное пространство, получить исходную позицию, пройти через коллизии и, если требуется, соединиться с родителем. Без правки JavaScript и без ручной сборки страницы.

На этом этапе я сознательно отложил pixel-perfect. Было бессмысленно идеально выравнивать иконку внутри панели, пока не определено, что происходит с камерой при открытии кейса или кто владеет координатами карточки. Сначала нужен был контракт ядра, потом — визуальная точность.

Один из первых концептов портфолио

Canvas — это не большой экран

Первая архитектурная ошибка, которую легко совершить в таком интерфейсе, — воспринимать Canvas как огромный DOM-контейнер, который просто двигается под маской viewport.

На деле это несколько независимых состояний:

  • ▸ активное пространство;
  • ▸ режим Canvas или Feed;
  • ▸ открытый документ;
  • ▸ координаты и масштаб камеры;
  • ▸ расположение карточек;
  • ▸ scroll-позиция ленты или открытого кейса;
  • ▸ выбранный инструмент ввода.

Если эти состояния смешать, любое переключение начинает иметь побочные эффекты. Открытие кейса сбрасывает зум, Feed теряет позицию, а возврат в предыдущее пространство складывает карточки в одну точку.

Поэтому основой стала постоянная оболочка приложения — app shell. Верхняя навигация, Canvas и системные контролы не пересоздаются при переходах. Меняется состояние внутри них: открывается документ, переключается проекция данных или подставляется другое пространство.

Это решение позволило сделать то, что было важно в исходной идее: кейс раскрывается внутри холста, как объект в Miro, а не ведёт на визуально чужую страницу. URL при этом меняется, работают Back и Forward, сохраняется камера, а прямой переход по ссылке всё равно открывает полноценный документ.

Именно здесь макет перестал быть набором кадров и стал моделью приложения.

Три слоя системы

Чтобы не превратить код в набор исключений, я разделил систему на три слоя.

1. Контент

WordPress хранит карточки, кейсы, пространства, типы, подписи, теги, действия, изображения и редакторские настройки. Это единственный источник истины для того, что опубликовано и как контент должен быть изначально организован.

2. Геометрия и состояние

Отдельное ядро рассчитывает координаты, размеры, коллизии, связи, границы движения и положение камеры. Оно не знает, какого цвета карточка и какой easing используется в анимации.

3. Представление и motion

Рендереры превращают одну модель данных в Canvas, Feed или открытый документ. GSAP анимирует переход из текущего состояния в уже рассчитанное конечное, но не принимает геометрические решения.

Это разделение оказалось одним из главных условий устойчивости. Если отдать GSAP владение координатами, визуально плавное движение быстро начинает расходиться с фактической раскладкой. Если хранить камеру в WordPress, редакторский контент смешивается с локальным состоянием посетителя. Если собрать Canvas и Feed из разных запросов, между ними появляются несовпадающие карточки.

В готовой системе у каждого слоя одна ответственность. Это звучит как инженерная формальность, но именно такие границы определяют, можно ли дальше развивать интерфейс без постоянного ремонта.

Карточка как система свойств

Карточки в макете сильно отличались друг от друга. Persona содержит фотографию, город и время. Project — обложку, описание, теги и действие. Contacts состоит из ссылок. Clients — из логотипов. Results — почти полностью текстовый блок.

Можно было сделать один универсальный компонент с десятками условий или, наоборот, собрать каждый вид отдельно. Оба варианта плохо масштабируются: первый превращается в монолит, второй размножает одинаковые элементы и поведение.

Я остановился на общей оболочке и реестре типов. У карточек единые шапка, бордеры, правила фокуса, размещение, теги, действие и коннектор. Внутреннее тело рендерится в зависимости от типа.

Это позволило вынести в общую модель свойства, которые сначала казались локальными:

  • ▸ обложка может быть у карточки любого типа;
  • ▸ SVG-иконка типа всегда помещается в фиксированный монохромный контейнер;
  • ▸ обычные и якорные теги используют один формат;
  • ▸ действие может открыть кейс, перейти в пространство, открыть внешний URL, скачать файл, поставить лайк или быть отключённым;
  • ▸ ссылка независимо определяет, нужно ли открывать её в новой вкладке;
  • ▸ важная карточка получает настраиваемую плашку, иконку и акцентную кнопку, не меняя свою роль в раскладке.

Самые важные уточнения касались не внешнего вида, а независимости свойств.

Например, перемещение карточки и pinned сначала оказались связаны: если я задавал новую координату, система автоматически закрепляла объект. Но редакторская позиция не означает запрет физики. Карточка может начинать движение из выбранной точки и всё равно отталкиваться от соседей. Координата и блокировка — два разных свойства.

То же произошло с коннекторами. Playground сначала оказался вариантом в одном списке с Dashed. Но Playground — это маршрут связи с другим пространством, а Dashed — её визуальный стиль. Пространственный коннектор может быть и сплошным, и пунктирным. После разделения на route и style модель стала описывать смысл, а не текущее количество макетов.

Такие исправления легко принять за мелкие баги админки. На самом деле это признаки неправильно выделенных осей системы. Если два свойства могут изменяться независимо, они не должны жить в одном enum.

Физика без игрового движка

Карточки остаются обычными HTML-элементами. Это важно для доступности, текста, ссылок, адаптивности и поисковых систем. Поверх них работает небольшое геометрическое ядро.

Для такого интерфейса можно было взять готовую whiteboard-библиотеку или отрисовать всё на HTML Canvas. Но сайт не является редактором схем: его основные объекты — статьи, изображения, ссылки и кнопки. Мне были важны нативный текст, семантика HTML, доступность и нормальная индексация.

Поэтому фронтенд собран на vanilla JavaScript, CSS и WordPress Script Modules, а собственное ядро решает только специфические задачи пространства. Цена этого выбора — необходимость самостоятельно описать жесты, коллизии и камеру. Преимущество — модель взаимодействия не приходится подгонять под ограничения чужого редактора.

При добавлении новой карточки система определяет её логическую сторону. Проекты по умолчанию уходят вправо, информационные блоки — влево. Затем карточка получает начальную позицию и проходит через collision pass: занятые объекты сдвигаются, пока между ними не появится необходимый зазор.

Публичный клиент получает версионированный REST payload и сверяет его ревизию, когда вкладка возвращается в фокус. Если редактор опубликовал новую карточку, интерфейс не пересобирает весь Canvas: он сопоставляет сущности по стабильным ID, сохраняет неизменившиеся DOM-узлы и пользовательские координаты, а новый объект добавляет в существующую раскладку.

Для поиска столкновений используется spatial hash. Вместо проверки каждой карточки с каждой ядро рассматривает только локальных соседей. Это стало особенно важно после появления нескольких пространств и карточек разного размера.

Закреплённые карточки не участвуют в вытеснении, но обычные пользовательские координаты сохраняются отдельно от pinned. Есть и другие независимые признаки: карточку можно исключить из Fit, оставить без стороны и родителя или скрыть в одной из проекций.

Коннекторы строятся по фактическим границам карточек. Точка крепления находится ровно на стыке шапки и тела, а кривая Безье меняется вслед за положением объектов. Во время анимации связь следует не за будущей координатой из модели, а за текущим положением DOM. Иначе карточка двигалась бы плавно, а линия мгновенно перескакивала бы в финальную точку.

Даже точечный фон пришлось включить в координатную систему мира. Сначала карточки двигались, а паттерн оставался приклеенным к экрану. Формально Canvas работал, но пропадал основной навигационный сигнал: пользователь не видел, что перемещает камеру относительно пространства. После привязки позиции и шага сетки к камере фон начал вести себя как в Miro и FigJam.

Управление — это матрица, а не один drag

На десктопе один и тот же жест может означать разные вещи. Левая кнопка двигает карточку в режиме Pointer. Средняя кнопка или зажатый пробел перемещают холст. В режиме Hand холст должен двигаться даже тогда, когда курсор находится над карточкой. Правая кнопка не должна случайно запускать drag.

На мобильном к этому добавляются тап, вертикальный scroll и pinch двумя пальцами. А внутри карточек остаются настоящие кнопки: «Открыть», «Скачать», «Перейти», «Лайк». Они должны нажиматься даже при выбранной руке.

Я зафиксировал эти правила как единую матрицу input modality, а не стал исправлять каждый конфликт отдельным обработчиком. Выбранный инструмент запоминается при переключении режимов, первым по умолчанию становится Hand, а Reset восстанавливает не только камеру, но и каноническую раскладку карточек.

Клавиатура получила собственную модель. Карточки используют roving focus: стрелки выбирают ближайший объект по направлению на Canvas и следующий по порядку в Feed, Enter запускает основное действие. Focus ring появляется только при клавиатурной навигации. Обычный клик мышью не оставляет на карточке случайную синюю обводку.

Это хороший пример разницы между состоянием из UI-kit и поведением продукта. В макете можно нарисовать active, hover и focus. Но только в браузере становится понятно, что именно переводит интерфейс в каждое состояние и как разные способы ввода конкурируют между собой.

Камера и контролируемая бесконечность

Масштаб Canvas можно менять напрямую колесом или pinch-жестом. Такое управление должно быть мгновенным: если добавить easing между пальцами пользователя и интерфейсом, Canvas начинает ощущаться вязким.

Программные переходы работают иначе. Сброс, Fit, возврат к стартовой карточке или восстановление пространства анимируются коротко и плавно. Пользователь видит, куда переместилась камера, но не ждёт декоративного пролёта.

Для каждого пространства из админки можно задать начальный масштаб от 10 до 200 процентов и выбрать карточку, которая станет центром первого экрана. Это редакторское решение, а не попытка угадать универсальный Fit. Если проектов мало, Persona можно показать крупнее. Если карта выросла — отдалить камеру и дать больше контекста.

На самом Canvas появился отдельный индикатор масштаба с быстрыми значениями. В Feed его нет, потому что там отсутствует сама концепция камеры.

Границы движения рассчитываются по канонической раскладке и расширяются на безопасный запас. Карточка с ignore fit не влияет на начальное кадрирование, но остаётся внутри доступного мира: её можно найти, если сознательно уйти в сторону. За счёт этого пространство остаётся исследуемым, но пользователь не может бесконечно уехать от содержимого.

Canvas и Feed — две проекции одних данных

Feed не стал отдельной мобильной страницей или вторым шаблоном WordPress. Это другая проекция того же REST payload.

В Portfolio центральная колонка содержит проекты, а боковые — информационные карточки. Скроллится только лента кейсов. Если боковая колонка выше viewport, она движется вместе с лентой до собственного конца и затем останавливается. Нижняя панель лежит поверх интерфейса, а последний кейс получает достаточный отступ, чтобы не прятаться за контролами.

В остальных пространствах Feed проще: все карточки идут одной лентой без специальных боковых зон.

При переключении Canvas и Feed карточки не исчезают и не возникают заново как несвязанные элементы. Система измеряет их экранную геометрию и морфит одно представление в другое. Панель инструментов тоже меняет форму: в Feed остаётся только переключатель режима, потому что Pointer, Hand и Reset там не нужны.

Для первого мобильного визита Feed включается автоматически. Это не сохранённый выбор, а безопасный старт. Если пользователь явно вернулся на Canvas, его решение запоминается.

Так мобильная версия перестала быть уменьшенным десктопом. Она использует ту же модель данных, но меняет приоритет взаимодействия.

Несколько пространств

Portfolio — особое пространство. Здесь карточки образуют связанную композицию вокруг Persona.

Playground и любое новое пространство — свободный Canvas с обычными карточками. Проекты можно публиковать не только в Portfolio, но и в Playground. Переход доступен и из верхнего меню, и через карточку-портал внутри самой карты.

Переключение происходит без перезагрузки app shell. Навигационный island плавно меняет ширину, камера и выбранный режим хранятся отдельно для каждого пространства, а реестр разделов позволяет добавлять новые пункты без переписывания хлебных крошек.

Здесь обнаружился один из характерных асинхронных багов: при быстром переключении ответы нескольких запросов могли приходить в другом порядке, и карточки нового пространства складывались в одну стопку. Исправление было не в увеличении задержки анимации, а в правиле latest request wins и проверке актуальности данных перед применением раскладки.

Это снова не визуальная проблема, хотя проявлялась она визуально.

Кейс внутри интерфейса

Карточка проекта открывает кейс внутри текущего Canvas. Остальные карточки и связи плавно уходят в нулевую прозрачность, нижние контролы скрываются, а выбранный объект разворачивается в document layer.

Внешняя страница при этом не скроллится. Шапка кейса и оболочка приложения остаются на месте, прокручивается только контент внутри документа. Короткий кейс обтягивается по высоте, длинный занимает доступную область viewport.

У этого сценария есть две равноправные точки входа:

  1. переход из карточки без перезагрузки;
  2. прямой URL с внешнего сайта, поисковика или социальной сети.

Сначала они расходились. По прямой ссылке WordPress показывал самостоятельный шаблон кейса, визуально похожий на документ, но лишённый Canvas shell. Для пользователя это выглядело как сломанная версия сайта. Я привёл обе точки входа к одной оболочке: сервер отдаёт полноценную страницу, а клиент после загрузки восстанавливает пространство и открывает тот же case layer.

Обычные WordPress Pages — например Privacy Policy — используют этот же document layer, но остаются отдельным типом документа. Их тело продолжает редактироваться Gutenberg, а Canvas переиспользует только навигацию, внутренний scroll и lifecycle открытия.

Так получилось сохранить и цельность интерфейса, и нормальную работу URL, истории браузера, индексации и сценария без JavaScript.

Админка — вторая половина продукта

В начале проекта часть данных жила в старых настройках темы, часть — в полях записей, часть была захардкожена в рендерере. Persona и Contacts даже присутствовали на Canvas, но не имели очевидной точки редактирования.

Внешне всё работало, но система не была управляемой. Это важное различие: контент на экране ещё не означает, что перед нами CMS.

Я собрал все сущности в раздел Canvas Cards → All. Системные карточки, проекты и обычные записи получили реальные формы редактирования. Отдельно появились:

  • ▸ визуальная карта Canvas для координат и раскладки;
  • ▸ реестр типов карточек с заменяемыми SVG-иконками;
  • ▸ пространства;
  • ▸ настройки стартовой камеры;
  • ▸ единая схема тегов и действий;
  • ▸ формы Persona, Contacts и двух островов верхней навигации.
Прямо внутри админки можно расставить карточки на точно таком же холсте

Для проекта метаданные превью теперь собраны в начале редактора: обложка из Media Library, заголовок, краткое описание, обычные и якорный теги, кнопка действия. Редактору не нужно искать части одной карточки в разных metabox.

Визуальная карта использует то же геометрическое ядро, что и публичный Canvas. Это принципиально: если административный preview рассчитывает коллизии иначе, сохранённая композиция никогда не совпадёт с сайтом.

Редактор кейса как модель композиции

Сначала кейс состоял из фиксированного набора полей: задача, решение, результат, картинка, видео. Пустые блоки всё равно оставляли место, а сложная история быстро упиралась в структуру формы.

Я рассматривал возможность полностью отдать кейсы Gutenberg. Для обычных юридических страниц это правильный выбор. Но кейсам требовались контролируемые композиционные блоки, одинаковый рендеринг внутри Canvas и на прямой PHP-странице, особые правила ширины, локализованные подписи и интеграция с lightbox.

Поэтому появился упорядоченный редактор блоков: Text, Callout, Image, Gallery, Visual media, Two columns, Metrics и Wide media. Пустой блок не попадает в результат. Текстовые поля поддерживают безопасный Markdown: жирное и курсивное начертания, ссылки, маркированные, нумерованные и вложенные списки.

Здесь модель тоже уточнялась через реальное использование. Первая версия позволяла поставить текст либо на всю ширину, либо в левую, либо в правую колонку. Формально все три позиции существовали, но нельзя было собрать простой сценарий:

  1. общий заголовок;
  2. текст слева;
  3. его продолжение справа;
  4. изображение на всю ширину ниже.

Проблема была не в интерфейсе селекта. Сама схема данных предполагала один текстовый поток. Я заменил её на отдельный layout текста и независимое содержимое правой колонки. На мобильном обе части последовательно складываются в одну колонку.

Такая итерация хорошо показывает разницу между количеством настроек и выразительностью модели. Можно добавить много переключателей, но если структура не соответствует авторскому сценарию, редактор остаётся негибким.

Изображения, видео и lightbox

Медиа прошли похожий путь. В ранней версии картинки попадали в заранее заданные рамки, обрезались через object-fit: cover, а слишком крупное изображение внутри кейса приходилось горизонтально скроллить.

Блок Video/embed содержал поля WEBM URL, MP4 URL, Embed URL и Poster URL. Для разработчика их назначение можно восстановить, но редактор не должен разгадывать внутреннее устройство HTML5 video.

Я заменил технический набор полей на понятный выбор контента:

  • ▸ изображение;
  • ▸ загруженное видео;
  • ▸ внешний плеер;
  • ▸ локализованная подпись;
  • ▸ ширина контентной колонки или всего кейса;
  • ▸ autoplay, muted и loop для видео.

Высота всегда определяется пропорциями материала. Никакой скрытой обрезки.

Для изображений внутри кейсов появился отдельный lightbox. Он умеет переключать элементы галереи, увеличивать от 100 до 400 процентов, возвращаться в Fit, масштабироваться колесом, двойным кликом и pinch-жестом, а также ограничивать pan границами изображения.

После первой реализации панель контролов находилась сверху и отнимала часть полезной высоты — низ увеличенной картинки становился недоступен. Панель переехала под изображение, а размеры стали рассчитываться по реальному viewport и safe area мобильного устройства.

Это небольшая деталь, но она отражает общий принцип проекта: интерфейс нельзя оценить только по спокойному состоянию на большом экране. Нужно пройти весь сценарий руками.

Типографика тоже является поведением

В кейсах много русского текста, длинных заголовков, чисел, сокращений и ссылок. Одного text-wrap: pretty недостаточно, чтобы убрать висячие предлоги или сохранить осмысленное соединение слов и чисел.

Поэтому текст проходит через Typograf, а CSS отвечает уже за баланс строк и перенос внутри конкретного контейнера. Ручное форматирование автора — жирное начертание, курсив, ссылки и списки — сохраняется из WordPress без попыток найти «важные цифры» регулярным выражением.

Так исчез ещё один ранний хардкод: Results сам выделял проценты и числа, из-за чего перед жирным фрагментом терялись пробелы. Теперь рендерер не интерпретирует смысл текста за автора. Он показывает то, что размечено в редакторе.

То же внимание потребовалось компонентной типографике: подписям рядом с иконками, тексту внутри badge, высоте шапок и вертикальному положению символов. Эти вещи я доводил уже после стабилизации ядра — когда исправление line-height больше не могло замаскировать ошибку структуры.

Motion объясняет изменение состояния

GSAP появился в проекте не ради эффектности. Его задача — сделать изменение системы читаемым.

Я использовал короткие анимации для:

  • ▸ вытеснения карточек при коллизиях;
  • ▸ появления новых объектов и skeleton-состояния загрузки;
  • ▸ открытия и закрытия кейса;
  • ▸ перехода Canvas ↔ Feed;
  • ▸ смены пространства;
  • ▸ программного Fit и Reset;
  • ▸ morphing панели инструментов и навигационных islands;
  • ▸ обратной связи для Share и Like.

Прямое управление остаётся без tween. Карточка следует за указателем, камера — за колесом или пальцами. Анимация подключается только там, где интерфейс сам меняет состояние и пользователю важно увидеть причинно-следственную связь.

Все вызовы GSAP находятся в отдельном motion-адаптере. Геометрия и REST-слой ничего о нём не знают. Активные tween можно прервать или перенаправить, а prefers-reduced-motion приводит систему сразу в конечное состояние.

Это позволило избежать распространённой ловушки: движения выглядят плавно, но данные уже находятся в другом месте. В моём случае motion визуализирует решение ядра, а не подменяет его.

Навигация как часть пространства

Верхняя панель состоит из двух interface islands. Слева — логотип, имя и динамические хлебные крошки. Справа — участники и Share.

Название активного пространства является переключателем. Portfolio и Playground не захардкожены: меню строится из реестра и допускает новые разделы. При открытии кейса цепочка продолжается до его названия.

На мобильном полный маршрут не помещается. Вместо уменьшения шрифта или выхода island за границу я оставил только две смысловые точки: начало «лого + Denis Lykin» и текущую сущность. Длинный конечный заголовок обрезается многоточием внутри доступной ширины.

Даже здесь понадобилось отделить анимацию от верстки. Во время morphing ширина панели на долю секунды становилась меньше содержимого, и «Denis Lykin» с «Knowledge Base» переносились на две строки. Минимальные размеры и запрет переноса стали частью layout-контракта, а не настройкой easing.

Настройки логотипа, аватаров, текста и ссылки Share находятся в WordPress. После копирования ссылка не просто меняет надпись: иконка и ширина кнопки переходят в состояние «Скопировано!» согласованно, без скачка текста.

Дизайн в паре с AI coding agent

Значительная часть проекта создавалась в постоянном диалоге с AI coding agent. Но я не воспринимал его как генератор, которому можно показать Figma и получить готовый сайт.

Для меня это был способ сократить расстояние между решением и проверяемым интерфейсом.

Единицей работы стал не экран, а контракт поведения. Я описывал не только желаемый результат, но и ограничения: кто владеет состоянием, что должно сохраниться после перехода, какие свойства независимы, где допустима анимация и что происходит при прямом URL или другом способе ввода.

Рабочий цикл выглядел так:

  1. Сформулировать продуктовый инвариант.
  2. Получить работающую реализацию, а не статичную картинку.
  3. Проверить её в браузере через DevTools и Playwright.
  4. Отличить локальный визуальный дефект от ошибки модели.
  5. Исправить общий контракт и добавить регрессионную проверку.

Например, просьба «не закреплять карточку после перемещения» превратилась в разделение редакторской координаты и физического lock. Замечание про Playground и Dashed — в две ортогональные оси коннектора. Невозможность одновременно заполнить левую и правую колонки — в новую схему контентного блока. Сломанный прямой URL — в единый lifecycle документа для серверного и клиентского входа.

AI хорошо ускоряет локальную реализацию. Но он не снимает с дизайнера ответственность за целостность. Если давать только визуальные команды, система быстро покрывается исключениями, потому что каждая следующая правка оптимизирует один кадр. Чем точнее дизайнер понимает браузер и модель данных, тем лучше он может перевести наблюдение в устойчивое правило.

Зачем дизайнеру разбираться во фронтенде

Речь не о том, что каждый дизайнер обязан самостоятельно писать production-код. Важнее уметь видеть технические последствия продуктового решения.

Понимание DOM помогло оставить карточки семантическими HTML-элементами, а не рисовать весь интерфейс на Canvas. Знание Pointer Events позволило разделить мышь, touch, pinch и клавиатуру. History API сделал бесшовный кейс совместимым с URL и навигацией браузера. Понимание compositing понадобилось, когда текст и изображения начинали пикселизоваться после масштабирования. Работа с REST и post meta помогла не смешать данные редактора с состоянием посетителя.

Без этого разговор легко сводится к формулировкам «сделай плавнее» или «здесь что-то прыгает». С технической грамотностью дизайнер может сказать: прямой жест должен обновляться синхронно, программная камера — через interruptible tween; background-position должен зависеть от world transform; старый async-response не имеет права перезаписывать активное пространство.

Это не попытка стать разработчиком вместо разработчика, а, скорее, способность проектировать на уровне причин.

На уровне Principal Designer ценность часто находится именно там: не нарисовать ещё один экран, а определить границы между системами, назвать инварианты и сделать так, чтобы разные решения не противоречили друг другу.

Продакшен — часть дизайна

Новая тема живёт отдельно от старой и активируется без миграции или удаления контента. WordPress отдаёт первый серверный render, а REST поддерживает обновления внутри постоянной оболочки.

CI/CD доставляет код темы и плагинов, но не перезаписывает базу, uploads и редакторский контент. Это позволяет развивать интерфейс независимо от production-данных.

Кроме основной темы появились небольшие системные слои:

  • ▸ юридические страницы и управление Cookies;
  • ▸ настраиваемые Open Graph и X/Twitter previews для сайта и отдельных кейсов;
  • ▸ обезличенная агрегированная аналитика без идентификаторов посетителя и сессии;
  • ▸ локальные SVG-ассеты и GSAP без зависимости от CDN.

Playwright проверяет переходы между Canvas, Feed и кейсом, сохранение камеры, клавиатурную навигацию, мобильные размеры, административные контролы и прямые маршруты. Визуальные снимки обновляются отдельно, после ручной проверки diff. Lighthouse desktop baseline достиг 100 баллов по Accessibility, Best Practices и SEO.

Тесты не гарантируют хорошего дизайна. Но они защищают уже принятые дизайнерские решения от случайного разрушения следующей функцией.

Где система ломалась

Проект развивался итерациями, и самые полезные выводы появились не в Figma, а в местах, где поведение расходилось с ожиданием.

Внешне похожие свойства оказывались разными

Так произошло с маршрутом и стилем коннектора, координатой и pinned, видимостью во время Fit и доступностью карточки в пространстве. После каждого такого случая я проверял, не объединяет ли модель независимые признаки только потому, что сейчас у них по два варианта.

Красивое состояние не гарантировало правильный переход

Кейс мог хорошо выглядеть открытым, но зависать на загрузке. Карточки могли плавно двигаться, но резко появляться. Навигация могла быть выровнена, но на один кадр складывать текст в две строки во время morphing.

Поэтому motion стал отдельной областью проектирования со своими loading, interruption, error и reduced-motion состояниями.

Desktop скрывал проблемы модели

На большом экране можно было не заметить конкуренцию scroll-контейнеров, слишком широкую колонку кейса или недоступный низ lightbox. Мобильный сценарий заставил определить, что именно скроллится, где находится safe area и какой жест принадлежит браузеру, а какой Canvas.

Админка быстро показывала хардкод

Если видимый объект нельзя найти и изменить, значит интерфейс ещё не стал системой. Невозможность отредактировать Contacts или выбрать обложку из Media Library была не «нехваткой поля», а сигналом, что контентная модель не совпадает с публичным рендерером.

Результат

В итоге портфолио стало небольшой контентной платформой.

У неё есть пространственный Canvas с физикой, связями, несколькими пространствами и управляемой камерой. Есть Feed как альтернативный способ чтения тех же данных. Кейсы, Persona и обычные страницы открываются внутри постоянной оболочки и сохраняют нормальные URL. WordPress управляет не только текстом, но и типами карточек, композицией, медиа, действиями, тегами и стартовым состоянием Canvas.

Самое важное — система остаётся расширяемой. Новое пространство не требует новой навигации. Новый тип карточки получает общую оболочку. Новый визуальный стиль коннектора не меняет его маршрут. Новая запись не требует пересборки карты вручную.

Портфолио показывает проекты, но одновременно демонстрирует способ мышления: как я работаю с неоднозначностью, раскладываю интерфейс на независимые свойства, проверяю гипотезы в коде и превращаю отдельные правки в правила системы.

Выводы

Сильная метафора должна определять поведение

Node-based-интерфейс имеет смысл не потому, что выглядит необычно. Он задаёт отношения между объектами, свободу исследования и модель навигации. Если точки не движутся вместе с миром, коннекторы не следуют за карточками, а переход открывает обычную страницу, метафора перестаёт работать.

Состояния важнее экранов

Canvas, Feed, кейс и пространство нельзя проектировать только как набор кадров. Важнее определить, что сохраняется между ними, какая зона скроллится и кто владеет камерой, URL и фокусом.

Админка — часть пользовательского опыта

CMS является продуктом для редактора. Если создание карточки требует знания внутреннего URL или поиска полей по разным экранам, публичный интерфейс нельзя считать законченным.

Независимые свойства нужно моделировать независимо

Большая часть сложных багов возникала там, где два понятия случайно объединялись в одно поле. Хорошая архитектура начинается с правильных различий.

Анимация должна объяснять, а не управлять

Motion показывает связь между состояниями, но не хранит истину о них. Прямое взаимодействие остаётся быстрым, программное — коротким и читаемым.

Мобильная версия меняет приоритеты

На телефоне я начинаю с Feed, сокращаю навигацию до начала и конечной точки, складываю колонки и сохраняю нативный scroll. Это не компромиссная копия десктопа, а другая точка входа в ту же систему.

AI ускоряет производство решений, но не заменяет дизайн-суждение

Чем быстрее появляется код, тем важнее способность заметить неверную модель. Основной навык смещается от производства макетов к формулированию инвариантов, проверке поведения и удержанию целостности продукта.

Что бы я сделал иначе

Ретроспективно я бы ещё до первой визуальной реализации описал state machine оболочки: пространство, режим, документ, камера, scroll и input modality. Часть ранних ошибок была следствием того, что эти состояния проявлялись постепенно.

Я бы раньше собрал единую схему карточки и редакторский inventory. Это сократило бы период, когда Contacts, Persona, теги и действия жили в разных местах.

Я бы с самого начала проверял touch на нескольких физических устройствах, а не только через эмуляцию. Браузерный viewport помогает с адаптивностью, но не воспроизводит все конфликты scroll, tap и pinch.

И я бы раньше ввёл правило отмены устаревших запросов при переключении пространств. Асинхронность редко видна в спокойном прототипе, но становится частью дизайна, как только интерфейс начинает жить в реальной сети.

При этом я бы сохранил общий порядок работы: сначала архитектурный контракт, затем функциональный browser QA и только потом pixel-perfect. В сложном интерфейсе визуальная полировка становится устойчивой лишь тогда, когда устойчиво его ядро.

Вместо заключения

Этот сайт начинался как новая форма портфолио, а превратился в исследование границы между дизайном и разработкой.

Я всё меньше верю в модель, где дизайнер заканчивает работу после передачи макета. Для систем с живыми данными, множеством состояний и прямым взаимодействием дизайн продолжается в браузере, в структуре CMS, в контрактах API, в поведении при ошибке и в том, что происходит между двумя красивыми кадрами.

Principal Designer не обязан лично реализовывать каждый слой. Но он должен видеть систему целиком: понимать, где визуальное решение становится моделью данных, где анимация конфликтует с состоянием, где редакторский сценарий важнее ещё одного компонента и где локальная правка должна превратиться в общее правило.

В этом смысле портфолио выполнило свою главную задачу. Оно не только рассказывает, как я проектирую продукты, но ещё и само работает тем же способом.

Связаться со мной

Если есть желание обсудить этот кейс, другие кейсы или пообщаться по иным вопросам в сфере продуктового дизайна — пишите мне в телеграм или на почту. Подробнее обо мне и моей работе можно узнать на моём сайте. Сейчас я в поисках продуктовой команды, в которой смогу развивать продукт и быть полезным игроком. Пишите, буду рад пообщаться!