diff --git a/docs/decisions/065-message-side-alignment.md b/docs/decisions/065-message-side-alignment.md new file mode 100644 index 0000000..211da2f --- /dev/null +++ b/docs/decisions/065-message-side-alignment.md @@ -0,0 +1,28 @@ +# ADR-065: сообщения выравниваются по стороне — свои вправо, чужие влево + +Пересматривает часть [ADR-024](024-identity-and-ui.md): десктопную сетку ленты «автор 132 px + текст» из `docs/identity/brief.md`, «Компоновка». Уточняет `docs/ui.md`, «Чат». + +## Контекст + +Тест с реальными пользователями: единый список с колонкой автора — десктоп сеткой `132px 1fr`, мобильный автором над группой — читается медленно. Чтобы понять, кто автор строки, нужно каждый раз смотреть в колонку слева; с чужого сообщения на своё и обратно глаз идёт через всю ширину. + +Разделение по стороне, как в обычных мессенджерах, снимает это одним взглядом: своё — у своего края, чужое — у чужого. Айдентика «Скобы» этому не мешает и не помогает — про стороны сообщений там ничего нет, — но прямо запрещает пузыри, скругления и тени (`identity/brief.md`, «Цвет»). Значит выравнивание, а не bubble-чат: то же плоское сообщение, что и сейчас, просто у другого края. + +Десктопная сетка `132px 1fr` для этого не годится: колонка автора у неё одна на весь список, слева, общая для всех строк. Разделить строки по сторонам в фиксированной сетке нечем — под это нужен один и тот же поток и на десктопе, и на мобильном, каким мобильный уже пользуется («автор над группой»). Решение — не расширять сетку, а убрать её: десктоп переходит на тот же поток, что мобильный. + +## Решение + +- Лента — везде (десктоп и мобильный) один поток блоков сверху вниз: автор и время строкой, текст под ними, без сетки. +- Блок своего сообщения выравнивается по правому краю ленты, чужого — по левому. Свой ник в колонке автора остаётся цветом `mark` (ADR-024) — сторона и цвет вместе, не одно вместо другого. +- Ширина блока ограничена (`min(640px, 85%)`), иначе выравнивать нечего: абзац во всю ширину ленты выглядит одинаково с любого края. Текст внутри блока остаётся выровнен по левому краю независимо от стороны — многострочное сообщение с рваным правым краем читается быстрее, чем с рваным левым. +- Разделители дат и «новые» — во всю ширину ленты, не по ширине сообщения: они не принадлежат стороне. +- Никаких фонов, рамок, скруглений и теней вокруг сообщения — это не бабл, а положение блока. +- CSS-класс `feed-list` вместо `grid`: сетки в разметке больше нет, имя не должно об этом врать. + +## Следствия + +- `docs/identity/brief.md`, «Компоновка»: строка про сетку `132px 1fr` заменена на «поток, свои сообщения — у правого края, чужие — у левого, ширина блока ограничена». +- `docs/ui.md`, «Чат»: абзац про ленту переписан под общий для обеих ширин экрана поток. +- `docs/identity/screens.html` (эталон 2a/2b) обновлён: десктопный мок 2a больше не показывает сетку, оба мока показывают сообщения по сторонам. +- Десктопная и мобильная лента визуально сближаются: раньше это были заметно разные раскладки, теперь — одна, с разной шириной блока и отступов. Разница экранов остаётся в сайдбаре, шапке и размере поля ввода (ADR-021, `identity/brief.md`). +- Комната с тремя и более участниками: у чужих сообщений сторона всех, кроме себя, одна и та же (левая) — различает их по-прежнему только ник и цвет, сторона тут ничего не добавляет и не мешает. diff --git a/docs/identity/brief.md b/docs/identity/brief.md index c42d934..7f1fc74 100644 --- a/docs/identity/brief.md +++ b/docs/identity/brief.md @@ -39,8 +39,9 @@ font-family: ui-monospace, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monos ## Компоновка -- Десктоп: сайдбар 224 px с правой границей `line`, шапка 64 px, отступы контента 32 px; сообщения — сетка `132px 1fr`, column-gap 20, row-gap 6. +- Десктоп: сайдбар 224 px с правой границей `line`, шапка 64 px, отступы контента 32 px. - Мобильный: шапка 56 px, отступы 20 px, ввод с min-height 44 px. +- Сообщения — поток на обеих ширинах, автор и время над текстом; свои — у правого края ленты, чужие — у левого, ширина блока не больше `min(640px, 85%)` (ADR-065). - Ввод — рамка 1 px ink, без скруглений, `>` цветом mark слева. - Активный элемент списка — инверсия: фон ink, текст bone. - Разделители — 1 px `line`; разделитель «новые» — 1 px mark. diff --git a/docs/identity/screens.html b/docs/identity/screens.html index a903ed0..54ccbda 100644 --- a/docs/identity/screens.html +++ b/docs/identity/screens.html @@ -23,13 +23,17 @@ .item { font-size:13px; padding:6px 8px; color:var(--text2); display:flex; } .item.active { background:var(--ink); color:var(--bone); } .item .n { margin-left:auto; color:var(--mark); } - .divider { display:flex; align-items:center; gap:14px; font-size:11px; color:var(--stone); } + .divider { display:flex; align-items:center; align-self:stretch; gap:14px; font-size:11px; color:var(--stone); } .divider::before, .divider::after { content:""; flex:1; height:1px; background:var(--line); } .divider.new { color:var(--mark); } .divider.new::before, .divider.new::after { background:var(--mark); } - .author { color:var(--mute); font-size:12px; padding-top:2px; } + .author { color:var(--mute); font-size:12px; } .author.me { color:var(--mark); } .author .t { color:var(--stone); } + /* поток сообщений: свои — у правого края, чужие — у левого (ADR-065) */ + .msg { display:flex; flex-direction:column; gap:4px; max-width:min(640px, 85%); } + .msg.me { align-self:flex-end; align-items:flex-end; } + .msg p { margin:0; } .input { display:flex; align-items:center; gap:12px; border:1px solid var(--ink); padding:14px 16px; font-size:14px; } .input .p { color:var(--mark); } .input .caret { width:8px; height:17px; background:var(--ink); } @@ -46,21 +50,17 @@ .me i { width:6px; height:6px; background:var(--ink); } .main { display:flex; flex-direction:column; min-width:0; } .head { height:64px; display:flex; align-items:center; gap:14px; padding:0 32px; border-bottom:1px solid var(--line); font-size:15px; } - .feed { flex:1; padding:28px 32px; display:flex; flex-direction:column; justify-content:flex-end; overflow:hidden; } + .feed { flex:1; padding:28px 32px; display:flex; flex-direction:column; align-items:flex-start; justify-content:flex-end; gap:14px; overflow:hidden; font-size:14px; line-height:1.55; } .feed .divider { padding:10px 0 22px; } - .grid { display:grid; grid-template-columns:132px 1fr; column-gap:20px; row-gap:6px; font-size:14px; line-height:1.55; } - .grid .divider { grid-column:1 / -1; padding:16px 0; } .compose { padding:0 32px 28px; } /* 2b — мобильный */ .mobile { width:390px; height:700px; display:flex; flex-direction:column; } .mobile .head { height:56px; padding:0 20px; gap:12px; font-size:14px; } .mobile .mark { width:16px; height:16px; stroke-width:8; } - .mobile .feed { padding:20px; gap:14px; } + .mobile .feed { padding:20px; font-size:14px; line-height:1.5; } .mobile .feed .divider { padding:0; font-size:10px; } - .msg { display:flex; flex-direction:column; gap:4px; } .msg .author { font-size:11px; } - .msg p { margin:0; font-size:14px; line-height:1.5; } .mobile .compose { padding:0 16px 20px; } .mobile .input { padding:13px 14px; min-height:44px; gap:10px; } .mobile .input .caret { width:7px; height:16px; } @@ -92,23 +92,12 @@
выкатила статику на bare.xmatic.team, кэш чистится сам
вес страницы — 14 кб. без шрифтов было бы 9, но mono того стоит
смотрю network: один html, один css, ноль js до первого сообщения. красиво
это и есть план. если фича требует бандлер — фича не нужна
доки пишу прямо в readme, отдельного сайта не будет
кто-то с hn спрашивает, где мобильное приложение
ответил: браузер и есть приложение
выкатила статику на bare.xmatic.team, кэш чистится сам
вес страницы — 14 кб
один html, один css, ноль js до первого сообщения. красиво
это и есть план. если фича требует бандлер — фича не нужна
это и есть план. если фича требует бандлер — фича не нужна
кто-то с hn спрашивает, где мобильное приложение
ответил: браузер и есть приложение