feat: перепиши кейс Card Columns без черновиков

This commit is contained in:
root committed 2026-09-29 14:48:53 +02:00
1 parent d25c2e2185
commit 8b50c821a7
5 files changed
+387 -1396

No files matched your search

+92 -331
View File
@@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta
name="description"
content="Кейс: Card Columns, плагин-вид для Obsidian Bases. Показывает заметки как карточки, разложенные по колонкам, с фильтрацией тегов по И/ИЛИ."
content="Как я создал Card Columns для Obsidian Bases: группировка заметок по свойствам, фильтрация тегов AND/OR и перенос карточек между колонками."
/>
<title>Card Columns, плагин для Obsidian Bases: кейс</title>
<title>Card Columns для Obsidian Bases: кейс о канбан-виде</title>
<script>
(function () {
var FONT_CSS = {
@@ -112,20 +112,20 @@
<meta property="og:site_name" content="Николай Мамадаев — продуктовый дизайнер" />
<meta property="og:locale" content="ru_RU" />
<meta property="og:locale:alternate" content="en_US" />
<meta property="og:title" content="Card Columns, плагин для Obsidian Bases: кейс" />
<meta property="og:description" content="Кейс: Card Columns, плагин-вид для Obsidian Bases. Показывает заметки как карточки, разложенные по колонкам, с фильтрацией тегов по И/ИЛИ." />
<meta property="og:title" content="Card Columns для Obsidian Bases: кейс о канбан-виде" />
<meta property="og:description" content="Как я создал Card Columns для Obsidian Bases: группировка заметок по свойствам, фильтрация тегов AND/OR и перенос карточек между колонками." />
<meta property="og:url" content="https://mamadaevv.site/cases/card-columns-obsidian/" />
<meta property="og:image" content="https://mamadaevv.site/assets/og/og-default.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Card Columns, плагин для Obsidian Bases: кейс" />
<meta property="og:image:alt" content="Card Columns для Obsidian Bases: кейс о канбан-виде" />
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Card Columns, плагин для Obsidian Bases: кейс" />
<meta name="twitter:description" content="Кейс: Card Columns, плагин-вид для Obsidian Bases. Показывает заметки как карточки, разложенные по колонкам, с фильтрацией тегов по И/ИЛИ." />
<meta name="twitter:title" content="Card Columns для Obsidian Bases: кейс о канбан-виде" />
<meta name="twitter:description" content="Как я создал Card Columns для Obsidian Bases: группировка заметок по свойствам, фильтрация тегов AND/OR и перенос карточек между колонками." />
<meta name="twitter:image" content="https://mamadaevv.site/assets/og/og-default.png" />
<meta name="twitter:image:alt" content="Card Columns, плагин для Obsidian Bases: кейс" />
<meta name="twitter:image:alt" content="Card Columns для Obsidian Bases: кейс о канбан-виде" />
<script type="application/ld+json">
{
@@ -169,8 +169,8 @@
"@type": "CreativeWork",
"@id": "https://mamadaevv.site/cases/card-columns-obsidian/#page",
"url": "https://mamadaevv.site/cases/card-columns-obsidian/",
"name": "Card Columns, плагин для Obsidian Bases: кейс",
"description": "Кейс: Card Columns, плагин-вид для Obsidian Bases. Показывает заметки как карточки, разложенные по колонкам, с фильтрацией тегов по И/ИЛИ.",
"name": "Card Columns для Obsidian Bases: кейс о канбан-виде",
"description": "Как я создал Card Columns для Obsidian Bases: группировка заметок по свойствам, фильтрация тегов AND/OR и перенос карточек между колонками.",
"isPartOf": {
"@id": "https://mamadaevv.site/#website"
},
@@ -203,7 +203,7 @@
{
"@type": "ListItem",
"position": 3,
"name": "Card Columns, плагин для Obsidian Bases: кейс",
"name": "Card Columns для Obsidian Bases: кейс о канбан-виде",
"item": "https://mamadaevv.site/cases/card-columns-obsidian/"
}
]
@@ -256,72 +256,7 @@
.case-article a:not(.btn) {
color: var(--accent);
}
.case-article .case-brief {
list-style: none;
margin: 1.5rem 0 0;
padding: 1.1rem 1.25rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
gap: 0.85rem 1.5rem;
font-size: 0.9rem;
border: 1px solid color-mix(in srgb, var(--text) 14%, transparent);
border-radius: var(--radius-md, 12px);
}
.case-article .case-brief li {
margin: 0;
}
.case-article .case-brief span {
display: block;
margin-bottom: 0.15rem;
color: var(--text-muted);
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.case-article .case-todo {
margin: 1rem 0 1.25rem;
padding: 0.9rem 1.1rem;
border-left: 2px solid var(--accent);
background: color-mix(in srgb, var(--accent) 8%, transparent);
border-radius: 0 var(--radius-sm, 8px) var(--radius-sm, 8px) 0;
font-size: 0.92rem;
}
.case-article .case-todo p {
margin: 0 0 0.5rem;
}
.case-article .case-todo p:last-child {
margin-bottom: 0;
}
.case-article .case-todo__q {
font-weight: 600;
color: var(--accent);
}
.case-todo__label {
display: block;
margin-bottom: 0.35rem;
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
opacity: 0.85;
}
.case-article details.case-details {
margin: 0.75rem 0 1rem;
padding: 0.6rem 0.9rem;
border: 1px solid color-mix(in srgb, var(--text) 14%, transparent);
border-radius: var(--radius-sm, 8px);
}
.case-article details.case-details > summary {
cursor: pointer;
font-weight: 600;
color: var(--accent);
padding: 0.25rem 0;
}
.case-article details.case-details[open] > summary {
margin-bottom: 0.5rem;
}
.case-block {
margin-top: 3rem;
padding-top: 2.5rem;
@@ -492,311 +427,137 @@
<div class="section__inner case-article">
<p class="hero__eyebrow">Obsidian Bases · Plugin</p>
<h1 class="case-page__h1">
Канбан-вид для Obsidian Bases,<br />который не&nbsp;трогает ваши заметки
Канбан-доска для Obsidian Bases<br />с фильтрацией и перетаскиванием
</h1>
<p class="case-page__sub">
Card Columns: заметки как карточки по колонкам,<br />
отбор по тегам И/ИЛИ за два клика
Card Columns: группировка по свойствам,<br />
быстрый отбор тегов AND/OR
</p>
<figure class="case-media case-media--wide">
<img src="../../assets/card-columns-obsidian/overview.webp" alt="База ссылок в Obsidian, сгруппированная по тегам в колонки Card Columns" width="2084" height="1296" loading="eager" decoding="async" data-image-viewer />
<figcaption>База заметок, сгруппированная по тегам в Columns</figcaption>
<figcaption>Links: 196 результатов в режиме Columns</figcaption>
</figure>
<ul class="case-brief">
<li><span>Роль</span>Продуктовый дизайн и разработка</li>
<li><span>Платформа</span>Obsidian Bases</li>
<li><span>Статус</span>Рабочий плагин в <a href="https://community.obsidian.md/plugins/card-columns" target="_blank" rel="noopener noreferrer">каталоге плагинов Obsidian</a>, open source (MIT)</li>
<li><span>Команда</span>Соло, пет-проект</li>
<li><span>Срок</span>Черновой ответ, заменить</li>
</ul>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Сколько заняла работа от первой идеи до рабочей версии? Недели? Месяц выходных?
</p>
<p>
Черновой ответ: собрал за три недели вечером, потом ещё месяц доводил по ходу
собственной работы. Заменить на реальное.
</p>
</div>
<p class="section__prose">
В Obsidian появились Bases, отдельный тип файла, который показывает заметки не текстом, а
в разных видах: таблицей, карточками, деревом. Стандартный вид карточек мне не зашёл: он
плоский, без группировки, и в нём не разобраться, когда заметок сотни. Я хотел видеть их
раскладку по колонкам, как канбан, но без лишнего веса. Так появился плагин-вид Card Columns.
Card Columns - мой плагин-вид для Obsidian Bases. Я спроектировал и разработал его сам:
хотел видеть заметки колонками, а не искать нужные в длинной ленте штатного Cards. Плагин
опубликован в <a href="https://community.obsidian.md/plugins/card-columns" target="_blank" rel="noopener noreferrer">каталоге Obsidian Community</a>,
исходный код открыт по лицензии MIT.
</p>
<div class="case-block" id="kontekst">
<h2>Контекст</h2>
<h2>От запроса Bases к колонкам</h2>
<p class="section__prose">
Bases, это не заметка и не отдельная база данных. Это запрос к vault-у: выбираешь
заметки по фильтру, и фреймворк рисует их в выбранном виде. У самого Obsidian есть
встроенный вид карточек, но он одноплановый: нет группировки по свойствам, нет
нормальной фильтрации. Я решил сделать свой вид, который встраивается в переключатель
Bases наравне со штатными.
Файл <code>.base</code> описывает запрос к заметкам в vault: какие записи показать и какие
свойства вывести. Штатный вид Cards в моей базе складывал результаты в длинную ленту.
Я хотел видеть ту же выборку по группам, например по статусу или тегу.
</p>
<p class="section__prose">
Главное ограничение было в самом фреймворке: плагин не хранит данные, а рисует то, что
выдал Bases-запрос (Sort, Filter, Properties, Search из тулбара). Значит, вся логика
выборки остаётся на Bases, а моя задача, только представление и навигация по нему.
Columns подключается как ещё один BasesView и получает результат запроса. Сортировка,
фильтры, набор свойств и поиск остаются за штатным тулбаром. Я не стал заводить отдельную
доску: заметки и настройки запроса остаются в Bases, плагин меняет только их представление.
</p>
</div>
<div class="case-block" id="pochemu-svoi">
<h2>Почему не готовое</h2>
<div class="case-block" id="filtry">
<h2>Сначала сгруппировать, потом отобрать</h2>
<p class="section__prose">
Прежде чем писать код, я посмотрел, что уже есть. Три пути, все мимо:
Группировку настраиваю через Group by в тулбаре Bases: по тегу, статусу, категории или
любому frontmatter-свойству. Если свойство не задано, Columns группирует по тегам. Колонки
строятся из значений выбранного свойства, а исходный запрос остаётся тем же. Одна база
подходит для разных разрезов: меняю только группировку.
</p>
<p class="section__prose">
<strong>Штатный вид карточек Bases.</strong> Рисует ровный поток карточек без группировки.
Когда заметок за сотню, это лента, в которой нужно скроллить и вглядываться в заголовки.
Отпал первым.
Теги для быстрого отбора вынесены отдельными пилюлями под тулбаром. Щёлкнул один раз,
оставил этот тег. Выбрал следующий, добавил его по OR. Для точного переключения использую
Ctrl+клик или правую кнопку мыши, а кнопкой OR/AND меняю логику отбора:
</p>
<p class="section__prose">
<strong>Канбан-плагины из комьюнити.</strong> Большинство тянет за собой свою систему
хранения: доска живёт отдельно от vault-а, заметки дублируются. Получается вторая база,
которую надо синхронизировать. Мне это было не нужно.
Эти пилюли не заменяют Filter в тулбаре. Bases формирует исходную выборку, а теговый фильтр
сужает её уже внутри Columns. Так я не меняю запрос, когда нужно быстро посмотреть подмножество.
</p>
<ul class="section__prose">
<li>первый тег оставляет только заметки с ним;</li>
<li>следующий добавляет заметки с любым из выбранных тегов;</li>
<li>Ctrl+клик или правый клик добавляет и снимает один тег, не сбрасывая остальные;</li>
<li>OR/AND переключает условие между «любой из» и «все выбранные», All очищает выбор.</li>
</ul>
<p class="section__prose">
<strong>Соседний bases-kanban.</strong> Ближайший по духу: цепляется к Bases, делает
канбан. Но он пишет в заметки: перетаскивание карточки меняет frontmatter. Для view-плагина
это риск, о котором ниже.
Фильтр остаётся перед глазами вместе с доской. Модальное окно здесь только мешало бы:
пришлось бы закрывать его, чтобы проверить результат.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>
Отказ от drag-and-drop, это не недоделка, а позиция. Плагин представления не должен
писать в пользовательские данные. Это решение определило всё остальное.
</p>
</div>
</aside>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Ты реально ставил и тестировал чужие плагины или ориентировался по описаниям? Какие
именно смотрел?
</p>
<p>
Черновой ответ: поставил три штуки, погонял неделю на своей базе, удалил. bases-kanban
смотрел в исходниках, не ставил. Заменить на реальное, названия плагинов вписать.
</p>
</div>
<figure class="case-media case-media--wide">
<img src="../../assets/card-columns-obsidian/filter-use.webp" alt="Теги-фильтры над колонками карточек Card Columns" width="1280" height="796" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Фильтры тегов остаются рядом с колонками</figcaption>
</figure>
</div>
<div class="case-block" id="zadacha">
<h2>Задача</h2>
<div class="case-block" id="kartochki">
<h2>Карточка показывает нужные поля</h2>
<p class="section__prose">
Спроектировать вид для Bases, который показывает заметки карточками по колонкам и позволяет
быстро отбирать нужное. Жёсткое ограничение: плагин ничего не пишет в заметки и не хранит
своего состояния, а сортировка, фильтр, свойства и поиск из штатного тулбара Bases должны
работать как раньше.
Состав карточки задаётся штатным списком Properties. Первое выбранное свойство становится
заголовком, остальные отображаются чипами: текст, теги, ссылки, булевы значения и даты.
Для чипов можно выбрать вертикальный стек или компактную сетку, настроить перенос и формат даты.
</p>
<p class="section__prose">
Если базе нужны обложки, Columns возьмёт первую картинку из заметки или значение указанного
frontmatter-свойства. Можно настроить пропорции (Auto, 1:1, 3:2, 4:3 или 16:9), вписывание
и место обложки. Карточка открывается в активной панели, модальном окне, новой вкладке или
сплите. В сплите обычный клик переиспользует панель, Ctrl+клик открывает новую вкладку.
Так можно читать заметку рядом с доской и не терять контекст выборки.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>
Главный риск, сделать вид, который конфликтует со штатным тулбаром Bases. Тогда
пользователь теряет сортировку и поиск, ради которых Bases и берут. Этого нельзя
было допустить.
</p>
</div>
</aside>
</div>
<div class="case-block" id="reshenie">
<h2>Решения</h2>
<h2>Перетаскивание меняет свойство заметки</h2>
<p class="section__prose">
Плагин добавляет в Bases вид «Columns». Карточки строятся из результатов Bases-запроса,
группировка задаётся свойством, а отбор, кликабельными тегами. Дальше, пять выборов,
которые определили результат. В каждом есть то, от чего я отказался.
</p>
<!-- МЕДИА: скриншот вида с колонками и карточками внутри.
<figure class="case-media case-media--wide">
<img src="../assets/card-columns-obsidian/board.webp" alt="Колонки карточек с группировкой по свойству" width="1920" height="1080" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Вид с колонками</figcaption>
</figure>
-->
<h3>Группировка по любому свойству</h3>
<p class="section__prose">
Колонки формируются из Group by в тулбаре Bases, по тегу, статусу, категории или любому
frontmatter-свойству. Меняешь свойство в запросе, и доска пересобирается сама. Так одна
и та же база закрывает разные разрезы: проекты по статусу, заметки по тегам, задачи по
приоритету.
</p>
<h3>Фильтр тегов по И и ИЛИ</h3>
<p class="section__prose">
Это то, ради чего плагин и задумывался. Под тулбаром, пилюли тегов, по клику они
отсеивают карточки по всем колонкам сразу:
</p>
<ul class="section__prose">
<li>Левый клик по тегу, показать только его (сбрасывает остальные фильтры).</li>
<li>Левый клик по следующему тегу, показать заметки с ЛЮБЫМ из выбранных (режим ИЛИ).</li>
<li>Ctrl+Клик или правый клик, переключить тег, не сбрасывая выбор.</li>
<li>Кнопка OR/AND, переключить между «любой из» и «все перечисленные» для нескольких тегов.</li>
<li>All, снять все фильтры.</li>
</ul>
<p class="section__prose">
Связка «ИЛИ-добавление левым кликом + точный toggle через Ctrl» получилась интуитивной:
быстрый отбор не сбивает то, что уже выбрано. Альтернативу, вынести фильтр в модалку,
отверг сразу: лишний клик и потеря контекста того, что уже отфильтровано.
В релизе 0.9.0 карточку можно перетащить в другую колонку, и Columns обновит
frontmatter-свойство, по которому построена группировка, через API
Obsidian <code>processFrontMatter()</code>. Если колонки собраны по статусу, после переноса
меняется именно статус заметки. Если в свойстве несколько значений, старое убирается,
новое добавляется, остальные сохраняются.
</p>
<p class="section__prose">
Выбор здесь не в самой фиче, а в том, где она живёт. Я выбрал пилюли под тулбаром вместо
отдельного окна фильтров, потому что отбор по тегам, это действие на каждом шаге работы
с базой. Модалка превращает его в прерванный сценарий: открыл, настроил, закрыл, проверил.
Пилюли оставляют результат перед глазами, а состояние фильтра видно всегда.
Я оставил этот сценарий настраиваемым. Drag-and-drop включён по умолчанию, но его можно
отключить в General. Так пользователь сам выбирает: только смотреть на группы или менять
значение свойства прямо с доски.
</p>
<h3>Настройки под разные базы</h3>
<p class="section__prose">
У вида четыре группы настроек: General, Title, Properties и Cover. В них задаются ширина
карточек, число колонок внутри группы (от 1 до 6), порядок колонок, зебра и masonry.
Порядок можно оставить автоматическим или задать вручную; пустые колонки в этом списке
сохраняются. Сетка показывает несколько карточек рядом, masonry заполняет вертикальные
пустоты, а зебра помогает не сбиться при чтении широкой доски. Отдельно настраиваются заголовки, чипы, даты
и обложки. Один вид подстраивается под разные базы, а не наоборот.
</p>
<figure class="case-media case-media--wide">
<img src="../../assets/card-columns-obsidian/filter-use.webp" alt="Панель тегов для фильтрации карточек в Card Columns" width="1280" height="796" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Фильтрация карточек по тегам</figcaption>
</figure>
<h3>Карточки и обложки</h3>
<p class="section__prose">
По каждой заметке рисуются свойства как чипы, текст, теги, ссылки, булевы, даты. Можно
показать обложку: из первой картинки в заметке или из отдельного свойства. У обложки
полный контроль, соотношение сторон (Auto, 1:1, 3:2, 4:3, 16:9), рамка, вписывание
(cover/contain), ориентация и место на карточке (над заголовком, под ним, после всего).
</p>
<h3>Решение: настройки вынесены из кода</h3>
<p class="section__prose">
Сначала параметры были захардкожены под мою базу. Это ошибка: вид перестал бы работать
для чужого сценария. Вынес всё в gear-меню, разбив на четыре секции, чтобы вид
подстраивался под разные базы, а не под мой единственный случай. Больше 15 параметров.
</p>
<details class="case-details">
<summary>Показать полный список настроек</summary>
<p class="section__prose"><strong>General</strong></p>
<ul class="section__prose">
<li>Куда открывать карточку: активная панель, всплывающее окно, новая вкладка, сплит вправо / вниз.</li>
<li>Ширина карточки (px).</li>
<li>Колонок в группе (1–6), сетка внутри колонки.</li>
<li>Зебра колонок (чередование фона для сканирования).</li>
<li>Masonry-раскладка (карточки заполняют вертикальные пустоты).</li>
</ul>
<p class="section__prose"><strong>Title</strong></p>
<ul class="section__prose">
<li>Перенос заголовка, жирный заголовок, размер шрифта (px).</li>
</ul>
<p class="section__prose"><strong>Properties</strong></p>
<ul class="section__prose">
<li>Раскладка чипов: стек (вертикально) или сетка (компактно в строку).</li>
<li>Перенос многострочных значений, размер шрифта.</li>
<li>Формат даты и даты-времени + локаль (Moment.js).</li>
</ul>
<p class="section__prose"><strong>Cover</strong></p>
<ul class="section__prose">
<li>Источник: нет / первая картинка в заметке / свойство-обложка.</li>
<li>Стиль (без рамки / с рамкой), соотношение сторон, ориентация, вписывание, позиция на карточке.</li>
</ul>
</details>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Это правда про захардкоженные параметры? Если нет, замени на реальную причину выноса
в настройки: просьбы пользователей, несколько баз с разной структурой, что угодно.
</p>
<p>
Черновой ответ: да, первая версия была под мою базу, настройки появились, когда
понадобилось открыть плагин наружу. Проверить и поправить.
</p>
</div>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">💡</span>
<div class="case-callout__content">
<p>
Сплит-превью сделал как в самом Obsidian: левый клик переиспользует панель, Ctrl+Клик,
новую вкладку внутри сплита. Без этого на широком экране карточки неудобно
просматривать рядом с доской.
</p>
</div>
</aside>
<h3>Работает внутри штатного тулбара</h3>
<p class="section__prose">
Вид цепляется к BasesView-фреймворку, поэтому сортировка, фильтр, свойства и поиск из
тулбара Bases остаются рабочими. Плагин только рисует результат запроса, данные не
дублируются и не зависят от отдельного хранилища.
</p>
<figure class="case-media case-media--wide">
<img src="../../assets/card-columns-obsidian/view-configure.webp" alt="Окно настройки вида Columns с параметрами карточек и ширины колонок" width="1280" height="1183" loading="lazy" decoding="async" data-image-viewer />
<img src="../../assets/card-columns-obsidian/view-configure.webp" alt="Экран настройки вида Columns" width="1280" height="1183" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Настройки вида Columns</figcaption>
</figure>
</div>
<div class="case-block" id="rezultat">
<h2>Результат и как я это мерял</h2>
<h2>Результат: доска внутри Obsidian Bases</h2>
<p class="section__prose">
Вид закрыл мою боль: Bases-запрос теперь читается как доска, а не как плоская лента
карточек. Группировка по свойству + отбор по тегам И/ИЛИ, то, чего не хватало штатному
виду. Всё крутится на запросе Bases, так что перенос vault-а и синхронизация ничего не
ломают.
Card Columns оставляет заметки в Bases, но меняет способ их обзора: я задаю группировку,
отбираю теги и открываю карточки рядом с доской. Когда нужна смена статуса, перетаскивание
правит то же frontmatter-свойство, по которому строятся колонки. Я продолжаю пользоваться
Columns в своей базе: заметки остаются в vault, отдельную доску синхронизировать не нужно.
</p>
<h3>Замер: сколько действий на отбор</h3>
<p class="section__prose">
Главная задача, по которой я мерял успех: отобрать заметки по двум тегам и найти нужную
карточку. В штатном виде карточек это скролл ленты с поиском по заголовкам глазами. В
Columns, два клика по пилюлям и колонка перед глазами.
</p>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Можешь прогнать этот замер по-настоящему? Засечь время на 10 повторах задачи: штатный
вид против Columns. Или посчитать точное число действий.
</p>
<p>
Черновой ответ: на выборке из 25 заметок отбор по двум тегам занимал около 40 секунд и
требовал скроллить ленту; в Columns, два клика, примерно 5 секунд. Цифры примерные,
заменить на замеренные.
</p>
</div>
<h3>Замер: живучесть в ежедневной работе</h3>
<p class="section__prose">
Второй критерий субъективнее, но для инструмента под себя он важнее: продолжаю ли я
пользоваться плагином через месяц. Пользуюсь, он встроился в работу с базой и не требует
вспоминать, как им управлять.
</p>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Есть ли внешние сигналы: установки, звёзды на GitHub, отзывы, вопросы в issue? Любая
цифра, подтверждающая, что плагином пользуются не только ты.
</p>
<p>
Черновой ответ: плагин пока ставится вручную, через каталог комьюнити не прошёл, так
что установок единицы и звёзд пока нет. Если есть реальные данные, вписать их сюда,
это сильнее любого-self-report.
</p>
</div>
<p class="section__prose">
Что сделал бы иначе. Drag-and-drop карточек между колонками не стал делать сознательно,
это меняет свойство в заметке, и для view-плагина это отдельный риск целостности данных.
Но вижу запрос на это у пользователей, и, возможно, добавлю как опциональный режим. А
пока плагин живёт вручную (через community-каталог ещё не прошёл), это единственное, что
мешает им пользоваться сходу.
На скриншоте моя база Links: Obsidian показывает 196 результатов. Плагин уже опубликован
в каталоге Obsidian Community. На момент обновления кейса там указаны версия 0.9.0 и
846 загрузок; поддерживаются Obsidian 1.8.0+, Desktop и Mobile, лицензия MIT.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">✅</span>
<div class="case-callout__content">
<p>
Главный вывод: вид для Bases получается рабочим только тогда, когда он уважает
тулбар фреймворка. Я добавил представление, а не вторую систему хранения, и заметки
остались обычными заметками.
Для меня важна граница между обзором и изменением данных. Группировка и фильтр помогают
читать базу; перенос карточки меняет её свойство. В Columns это действие видно и его
можно отключить.
</p>
</div>
</aside>
+91 -335
View File
@@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta
name="description"
content="Case Study: Card Columns, a view plugin for Obsidian Bases. It displays notes as cards arranged in columns, with tag filtering using AND/OR."
content="How I built Card Columns for Obsidian Bases: group notes by properties, filter tags with AND/OR, and move cards between columns."
/>
<title>Card Columns, a plugin for Obsidian Bases: a case study</title>
<title>Card Columns for Obsidian Bases: Kanban view case study</title>
<script>
(function () {
var FONT_CSS = {
@@ -112,20 +112,20 @@
<meta property="og:site_name" content="Nikolai Mamadaev — Product Designer" />
<meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="ru_RU" />
<meta property="og:title" content="Card Columns, a plugin for Obsidian Bases: a case study" />
<meta property="og:description" content="Case Study: Card Columns, a view plugin for Obsidian Bases. It displays notes as cards arranged in columns, with tag filtering using AND/OR." />
<meta property="og:title" content="Card Columns for Obsidian Bases: Kanban view case study" />
<meta property="og:description" content="How I built Card Columns for Obsidian Bases: group notes by properties, filter tags with AND/OR, and move cards between columns." />
<meta property="og:url" content="https://mamadaevv.site/en/cases/card-columns-obsidian/" />
<meta property="og:image" content="https://mamadaevv.site/assets/og/og-default.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Card Columns, a plugin for Obsidian Bases: a case study" />
<meta property="og:image:alt" content="Card Columns for Obsidian Bases: Kanban view case study" />
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Card Columns, a plugin for Obsidian Bases: a case study" />
<meta name="twitter:description" content="Case Study: Card Columns, a view plugin for Obsidian Bases. It displays notes as cards arranged in columns, with tag filtering using AND/OR." />
<meta name="twitter:title" content="Card Columns for Obsidian Bases: Kanban view case study" />
<meta name="twitter:description" content="How I built Card Columns for Obsidian Bases: group notes by properties, filter tags with AND/OR, and move cards between columns." />
<meta name="twitter:image" content="https://mamadaevv.site/assets/og/og-default.png" />
<meta name="twitter:image:alt" content="Card Columns, a plugin for Obsidian Bases: a case study" />
<meta name="twitter:image:alt" content="Card Columns for Obsidian Bases: Kanban view case study" />
<script type="application/ld+json">
{
@@ -169,8 +169,8 @@
"@type": "CreativeWork",
"@id": "https://mamadaevv.site/en/cases/card-columns-obsidian/#page",
"url": "https://mamadaevv.site/en/cases/card-columns-obsidian/",
"name": "Card Columns, a plugin for Obsidian Bases: a case study",
"description": "Case Study: Card Columns, a view plugin for Obsidian Bases. It displays notes as cards arranged in columns, with tag filtering using AND/OR.",
"name": "Card Columns for Obsidian Bases: Kanban view case study",
"description": "How I built Card Columns for Obsidian Bases: group notes by properties, filter tags with AND/OR, and move cards between columns.",
"isPartOf": {
"@id": "https://mamadaevv.site/#website"
},
@@ -203,7 +203,7 @@
{
"@type": "ListItem",
"position": 3,
"name": "Card Columns, a plugin for Obsidian Bases: a case study",
"name": "Card Columns for Obsidian Bases: Kanban view case study",
"item": "https://mamadaevv.site/cases/card-columns-obsidian/"
}
]
@@ -256,72 +256,7 @@
.case-article a:not(.btn) {
color: var(--accent);
}
.case-article .case-brief {
list-style: none;
margin: 1.5rem 0 0;
padding: 1.1rem 1.25rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
gap: 0.85rem 1.5rem;
font-size: 0.9rem;
border: 1px solid color-mix(in srgb, var(--text) 14%, transparent);
border-radius: var(--radius-md, 12px);
}
.case-article .case-brief li {
margin: 0;
}
.case-article .case-brief span {
display: block;
margin-bottom: 0.15rem;
color: var(--text-muted);
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.case-article .case-todo {
margin: 1rem 0 1.25rem;
padding: 0.9rem 1.1rem;
border-left: 2px solid var(--accent);
background: color-mix(in srgb, var(--accent) 8%, transparent);
border-radius: 0 var(--radius-sm, 8px) var(--radius-sm, 8px) 0;
font-size: 0.92rem;
}
.case-article .case-todo p {
margin: 0 0 0.5rem;
}
.case-article .case-todo p:last-child {
margin-bottom: 0;
}
.case-article .case-todo__q {
font-weight: 600;
color: var(--accent);
}
.case-todo__label {
display: block;
margin-bottom: 0.35rem;
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
opacity: 0.85;
}
.case-article details.case-details {
margin: 0.75rem 0 1rem;
padding: 0.6rem 0.9rem;
border: 1px solid color-mix(in srgb, var(--text) 14%, transparent);
border-radius: var(--radius-sm, 8px);
}
.case-article details.case-details > summary {
cursor: pointer;
font-weight: 600;
color: var(--accent);
padding: 0.25rem 0;
}
.case-article details.case-details[open] > summary {
margin-bottom: 0.5rem;
}
.case-block {
margin-top: 3rem;
padding-top: 2.5rem;
@@ -492,315 +427,136 @@
<div class="section__inner case-article">
<p class="hero__eyebrow">Obsidian Bases · Plugin</p>
<h1 class="case-page__h1">
A Kanban view for Obsidian Bases<br />
that leaves your notes untouched
A Kanban board for Obsidian Bases<br />
with filtering and drag-and-drop
</h1>
<p class="case-page__sub">
Card Columns: Notes as cards in columns,<br />
filtered by AND/OR tags with just two clicks
Card Columns: group by properties,<br />
filter tags with AND/OR
</p>
<figure class="case-media case-media--wide">
<img src="../../../assets/card-columns-obsidian/overview.webp" alt="An Obsidian links database grouped into Card Columns by tags" width="2084" height="1296" loading="eager" decoding="async" data-image-viewer />
<figcaption>A notes database grouped into Columns by tags</figcaption>
<figcaption>Links: 196 results in Columns view</figcaption>
</figure>
<ul class="case-brief">
<li>Role: Product Design and Development</li>
<li><span>Platform:</span> Obsidian Bases</li>
<li><span>Status</span>: Working plugin in the <a href="https://community.obsidian.md/plugins/card-columns" target="_blank" rel="noopener noreferrer">Obsidian plugin directory</a>, open source (MIT)</li>
<li><span>Team: Solo</span>, side project</li>
<li><span>Status:</span> Draft response, to be replaced</li>
</ul>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
How long did it take from the initial idea to a working version? Weeks? A month of weekends?
</p>
<p>
Draft answer: I put it together in three weeks in the evenings, then spent another month refining it while
working on my own projects. Replace with the actual time.
</p>
</div>
<p class="section__prose">
Obsidian now has Bases, a separate file type that displays notes not as text, but
in various formats: tables, cards, and trees. The standard card view didn’t work for me: it’s
flat, with no grouping, and it’s hard to make sense of when there are hundreds of notes. I wanted to see them
arranged in columns, like a Kanban board, but without the extra overhead. That’s how the Card Columns view plugin came about.
Card Columns is my view plugin for Obsidian Bases. I designed and built it myself because I wanted
to scan notes in columns instead of searching through the standard Cards feed. The plugin is
available in the <a href="https://community.obsidian.md/plugins/card-columns" target="_blank" rel="noopener noreferrer">Obsidian Community directory</a>
and is open source under the MIT license.
</p>
<div class="case-block" id="kontekst">
<h2>Context</h2>
<h2>From a Bases query to columns</h2>
<p class="section__prose">
A “Bases” is neither a note nor a separate database. It’s a query to the vault: you select
notes using a filter, and the framework displays them in the chosen view. Obsidian itself has
a built-in card view, but it’s one-dimensional: no grouping by properties, no
proper filtering. I decided to create my own view that integrates into the
Bases toggle alongside the default ones.
A <code>.base</code> file describes a query over notes in the vault: which entries to show and
which properties to display. In my database, Obsidian’s built-in Cards view produced a long
feed. I wanted to see the same results grouped by a property such as status or tag.
</p>
<p class="section__prose">
The main limitation was in the framework itself: the plugin doesn’t store data, but rather renders what
the Bases query returns (Sort, Filter, Properties, Search from the toolbar). This means that all the logic
for data retrieval remains with Bases, and my task is solely to display and navigate the results.
Columns registers as another BasesView and receives the query results. Sorting, filters, visible
properties, and search stay in the native toolbar. I chose not to create a separate board: the
notes and query settings remain in Bases; the plugin changes only how they are displayed.
</p>
</div>
<div class="case-block" id="pochemu-svoi">
<h2>Why not use a ready-made solution?</h2>
<div class="case-block" id="filters">
<h2>Group first, filter second</h2>
<p class="section__prose">
Before writing any code, I checked what was already available. Three options, none of them worked:
I set grouping through Group by in the Bases toolbar: tags, status, category, or any frontmatter
property. If none is selected, Columns groups by tags. The columns use that property’s values;
the underlying query stays the same. To view the database from another angle, I only change
its grouping.
</p>
<p class="section__prose">
<strong>The default Bases card view.</strong>
It displays a continuous stream of cards without grouping.
When there are over a hundred notes, it becomes a feed where you have to scroll and squint at the titles.
I ruled that out first.
I put tag filters right below the toolbar. Click one tag to keep only notes with it. Click another
to add it in OR mode. Ctrl-click or right-click toggles one tag without clearing the rest, and
the OR/AND control changes how multiple selections combine:
</p>
<p class="section__prose">
<strong>Kanban plugins from the community.</strong>
Most come with their own storage system:
the board exists separately from the vault, and notes are duplicated. This results in a second database
that needs to be synchronized. I didn’t need that.
These pills do not replace Filter in the toolbar. Bases creates the initial result set; the tag
filter narrows it inside Columns. I can inspect a subset without changing the underlying query.
</p>
<ul class="section__prose">
<li>the first tag narrows the view to notes that have it;</li>
<li>the next adds notes with any of the selected tags;</li>
<li>Ctrl-click or right-click toggles a single tag in the current selection;</li>
<li>OR/AND switches between “any selected” and “all selected”; All clears the selection.</li>
</ul>
<p class="section__prose">
<strong>The neighboring bases-kanban.</strong>
The closest in spirit: it hooks into Bases to create
a Kanban board. But it writes to the notes: dragging a card changes the frontmatter. For a view plugin,
this is a risk, which I’ll discuss below.
The filter stays visible beside the board. A modal would interrupt the flow: I would have to close
it just to check what changed.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>
The decision to forgo drag-and-drop isn’t a shortcoming—it’s a design choice. A view plugin should not
write to user data. This decision shaped everything else.
</p>
</div>
</aside>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
Did you actually install and test other people’s plugins, or did you just go by the descriptions? Which
ones did you look at specifically?
</p>
<p>
Preliminary answer: I installed three of them, ran them for a week on my database, then uninstalled them. I looked at the source code for bases-kanban
but didn’t install it. Replace with actual information; include the plugin names.
</p>
</div>
<figure class="case-media case-media--wide">
<img src="../../../assets/card-columns-obsidian/filter-use.webp" alt="Tag filters above Card Columns cards and columns" width="1280" height="796" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Tag filters stay beside the columns</figcaption>
</figure>
</div>
<div class="case-block" id="zadacha">
<h2>Task</h2>
<div class="case-block" id="cards">
<h2>Cards show the fields I need</h2>
<p class="section__prose">
Design a view for Bases that displays notes as cards arranged in columns and allows you to
quickly select the ones you need. Strict constraint: the plugin does not write anything to the notes and does not store
its own state, and sorting, filtering, properties, and search from the standard Bases toolbar must
work as before.
The card contents follow the native Properties selection. The first selected property becomes
the title; the rest appear as chips: text, tags, links, booleans, and dates. Chips can use a vertical
stack or compact grid, with configurable wrapping and date formats.
</p>
<p class="section__prose">
When a database needs covers, Columns can use the first image in a note or a chosen frontmatter
property. I can set the aspect ratio (Auto, 1:1, 3:2, 4:3, or 16:9), fit, and position. Cards can
open in the active pane, a floating modal, a new tab, or a split pane. In a split, a click reuses
the pane; Ctrl-click opens a new tab. That keeps the note beside the board without losing the
current query context.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>
The main risk is creating a view that conflicts with the standard Bases toolbar. If that happens,
the user loses the sorting and search capabilities—the very features that make Bases worth using. This could not
be allowed to happen.
</p>
</div>
</aside>
</div>
<div class="case-block" id="reshenie">
<h2>Solutions</h2>
<h2>Dragging a card edits its note</h2>
<p class="section__prose">
The plugin adds a “Columns” view to Bases. Cards are built from the results of a Bases query;
grouping is defined by a property, and filtering is done via clickable tags. Next, there are five options
that determine the result. Each one includes something I decided against.
</p>
<!-- МЕДИА: скриншот вида с колонками и карточками внутри.
<figure class="case-media case-media--wide">
<img src="../assets/card-columns-obsidian/board.webp" alt="Columns of cards grouped by property" width="1920" height="1080" loading="lazy" decoding="async" data-image-viewer />
<figcaption>View with columns</figcaption>
</figure>
-->
<h3>Grouping by any property</h3>
<p class="section__prose">
Columns are generated using the “Group by” option in the Bases toolbar, by tag, status, category, or any
frontmatter property. Change the property in the query, and the board reloads automatically. This way, a single
database covers different perspectives: projects by status, notes by tags, tasks by
priority.
</p>
<h3>Tag filter by AND and OR</h3>
<p class="section__prose">
This is exactly what the plugin was designed for. Below the toolbar, there are tag icons; when clicked, they
filter cards across all columns at once:
</p>
<ul class="section__prose">
<li>Left-click on a tag to show only that tag (resets the other filters).</li>
<li>Left-click on the next tag to show notes with ANY of the selected tags (OR mode).</li>
<li>Ctrl+Click or right-click to switch tags without resetting the selection.</li>
<li>The OR/AND button: toggle between “any of” and “all of” for multiple tags.</li>
<li>All: Clear all filters.</li>
</ul>
<p class="section__prose">
The combination of “OR—add with a left-click + exact toggle via Ctrl” turned out to be intuitive:
quick selection doesn’t disrupt what’s already selected. I immediately rejected the alternative—moving the filter to a modal window—
because it requires an extra click and loses the context of what’s already been filtered.
In version 0.9.0, a card can be dragged into another column. That movement changes data: Columns
updates the frontmatter property used for grouping through Obsidian’s
<code>processFrontMatter()</code> API. If the board is grouped by status, dropping a card into a
different column changes that note’s status. For a multi-value property, the old value is removed,
the new one is added, and the other values stay in place.
</p>
<p class="section__prose">
The choice here isn’t about the feature itself, but where it lives. I chose the pills below the toolbar instead of
a separate filter window because filtering by tags is an action performed at every step of working
with the database. A modal window turns it into an interrupted workflow: open, configure, close, check.
The pill-shaped icons keep the result right in front of you, and the filter status is always visible.
Drag-and-drop is enabled by default, but it can be turned off in General settings. The choice is
explicit: use the board to inspect groups, or edit the grouping property by moving a card.
</p>
<h3>Settings for different databases</h3>
<p class="section__prose">
The view has four settings groups: General, Title, Properties, and Cover. They cover card width,
one to six cards per group, column order, zebra striping, and masonry. Column order can be automatic
or set manually, with empty columns kept in place. The grid places cards side by side,
masonry fills vertical gaps, and zebra striping helps scan a wide board. Title style,
property chips, dates, and covers have their own controls. The view adapts to the database.
</p>
<figure class="case-media case-media--wide">
<img src="../../../assets/card-columns-obsidian/filter-use.webp" alt="Tag pills used to filter cards in Card Columns" width="1280" height="796" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Filtering cards by tag</figcaption>
</figure>
<h3>Cards and Covers</h3>
<p class="section__prose">
Properties such as chips, text, tags, links, Booleans, and dates are displayed for each note. You can
show a cover: from the first image in the note or from a separate property. You have
full control over the cover, including aspect ratio (Auto, 1:1, 3:2, 4:3, 16:9), border, fit
(cover/contain), orientation, and position on the card (above the title, below it, at the end).
</p>
<h3>Solution: Settings have been moved out of the code</h3>
<p class="section__prose">
At first, the parameters were hardcoded for my database. This was a mistake: the view would stop working
for someone else’s scenario. I moved everything to the gear menu, dividing it into four sections so that the view
could adapt to different databases, rather than just my single use case. More than 15 parameters.
</p>
<details class="case-details">
<summary>Show the full list of settings</summary>
<p class="section__prose"><strong>General</strong></p>
<ul class="section__prose">
<li>Where to open the card: active panel, pop-up window, new tab, split to the right / down.</li>
<li>Card width (px).</li>
<li>Columns per group (1–6), grid within a column.</li>
<li>Zebra columns (alternating background colors for easy scanning).</li>
<li>Masonry layout (cards fill vertical gaps).</li>
</ul>
<p class="section__prose"><strong>Title</strong></p>
<ul class="section__prose">
<li>Title wrapping, bold title, font size (px).</li>
</ul>
<p class="section__prose"><strong>Properties</strong></p>
<ul class="section__prose">
<li>Chip layout: stack (vertical) or grid (compact in a row).</li>
<li>Wrap multi-line values, font size.</li>
<li>Date and date-time formats + locale (Moment.js).</li>
</ul>
<p class="section__prose"><strong>Cover</strong></p>
<ul class="section__prose">
<li>Source: none / first image in the post / cover property.</li>
<li>Style (borderless / with border), aspect ratio, orientation, fit, position on the card.</li>
</ul>
</details>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
Is it true about the hard-coded parameters? If not, replace it with the real reason for moving them
to the settings: user requests, multiple databases with different structures, whatever.
</p>
<p>
Preliminary answer: Yes, the first version was tailored to my database; the settings were added when
I needed to make the plugin available externally. I’ll check and fix it.
</p>
</div>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">💡</span>
<div class="case-callout__content">
<p>
I implemented the split preview just like in Obsidian itself: left-click reuses the panel, Ctrl+click opens
a new tab within the split. Without this, on a wide screen, it’s inconvenient to
view cards next to the board.
</p>
</div>
</aside>
<h3>Works within the standard toolbar</h3>
<p class="section__prose">
The view hooks into the BasesView framework, so sorting, filtering, properties, and search from
the Bases toolbar remain functional. The plugin only renders the query results; the data is not
duplicated and does not depend on a separate storage.
</p>
<figure class="case-media case-media--wide">
<img src="../../../assets/card-columns-obsidian/view-configure.webp" alt="Configure view popover with card and column width settings" width="1280" height="1183" loading="lazy" decoding="async" data-image-viewer />
<img src="../../../assets/card-columns-obsidian/view-configure.webp" alt="Columns view configuration screen" width="1280" height="1183" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Columns view settings</figcaption>
</figure>
</div>
<div class="case-block" id="rezultat">
<h2>The Result and How I Tested It</h2>
<h2>Result: a board inside Obsidian Bases</h2>
<p class="section__prose">
This view solved my problem: a Bases query now reads like a board, rather than a flat stream
of cards. Grouping by property + filtering by AND/OR tags—exactly what the default
view was missing. Everything runs on the Bases query, so moving the vault and syncing don’t
break anything.
Card Columns keeps notes in Bases but changes how I scan them: I choose a grouping, filter tags,
and open cards beside the board. When I need to change a status, dragging a card updates the same
frontmatter property that forms the columns. I still use Columns with my own database; notes stay
in the vault, so there is no separate board to keep in sync.
</p>
<h3>Metrics: number of steps to filter</h3>
<p class="section__prose">
The main task I used to measure success: filter notes by two tags and find the right
card. In the standard card view, this involves scrolling through the feed and scanning the titles with your eyes. In
Columns, two clicks on the pills and the column is right in front of you.
</p>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
Can you run this test for real? Time 10 repetitions of the task: the default
view versus Columns. Or count the exact number of steps.
</p>
<p>
Preliminary answer: on a sample of 25 notes, filtering by two tags took about 40 seconds and
required scrolling through the feed; in Columns, two clicks, about 5 seconds. These are rough estimates—
replace them with actual measurements.
</p>
</div>
<h3>Test: Long-term usability in daily work</h3>
<p class="section__prose">
The second criterion is more subjective, but for a tool tailored to my needs, it’s more important: will I still be
using the plugin a month from now? I am—it’s become an integral part of my work with the database and doesn’t require
me to remember how to use it.
</p>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
Are there any external indicators: installs, GitHub stars, reviews, or questions in the issue tracker? Any
number confirming that you’re not the only one using the plugin.
</p>
<p>
Preliminary answer: The plugin currently needs to be installed manually; it hasn’t been approved for the community repository yet, so
there are only a handful of installs and no stars so far. If there’s actual data, include it here—
it’s more convincing than any self-report.
</p>
</div>
<p class="section__prose">
What I’d do differently. I deliberately chose not to implement drag-and-drop of cards between columns,
since that changes a property in the note, and for a view plugin, that poses a separate data integrity risk.
But I see a demand for this among users, and I might add it as an optional mode. For
now, since the plugin is maintained manually (it hasn’t been reviewed by the community directory yet), this is the only thing
preventing users from using it right away.
The screenshot shows my Links database; Obsidian displays 196 results. The plugin is listed in
Obsidian Community. When I updated this case, the listing showed version 0.9.0 and 846 downloads;
it supports Obsidian 1.8.0+, desktop and mobile, under the MIT license.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">✅</span>
<div class="case-callout__content">
<p>
Main conclusion: a view for Bases only works when it respects
the framework’s toolbar. I added a view, not a second storage system, and the notes
remained regular notes.
I care about the boundary between viewing and changing data. Grouping and filtering help me
read the database; moving a card edits its property. In Columns, that action is visible and
can be turned off.
</p>
</div>
</aside>
+82 -326
View File
@@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta
name="description"
content="Case Study: Card Columns, a view plugin for Obsidian Bases. It displays notes as cards arranged in columns, with tag filtering using AND/OR."
content="How I built Card Columns for Obsidian Bases: group notes by properties, filter tags with AND/OR, and move cards between columns."
/>
<title>Card Columns, a plugin for Obsidian Bases: a case study</title>
<title>Card Columns for Obsidian Bases: Kanban view case study</title>
{{> head-links}}
{{> head-plyr}}
<style>
@@ -48,72 +48,7 @@
.case-article a:not(.btn) {
color: var(--accent);
}
.case-article .case-brief {
list-style: none;
margin: 1.5rem 0 0;
padding: 1.1rem 1.25rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
gap: 0.85rem 1.5rem;
font-size: 0.9rem;
border: 1px solid color-mix(in srgb, var(--text) 14%, transparent);
border-radius: var(--radius-md, 12px);
}
.case-article .case-brief li {
margin: 0;
}
.case-article .case-brief span {
display: block;
margin-bottom: 0.15rem;
color: var(--text-muted);
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.case-article .case-todo {
margin: 1rem 0 1.25rem;
padding: 0.9rem 1.1rem;
border-left: 2px solid var(--accent);
background: color-mix(in srgb, var(--accent) 8%, transparent);
border-radius: 0 var(--radius-sm, 8px) var(--radius-sm, 8px) 0;
font-size: 0.92rem;
}
.case-article .case-todo p {
margin: 0 0 0.5rem;
}
.case-article .case-todo p:last-child {
margin-bottom: 0;
}
.case-article .case-todo__q {
font-weight: 600;
color: var(--accent);
}
.case-todo__label {
display: block;
margin-bottom: 0.35rem;
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
opacity: 0.85;
}
.case-article details.case-details {
margin: 0.75rem 0 1rem;
padding: 0.6rem 0.9rem;
border: 1px solid color-mix(in srgb, var(--text) 14%, transparent);
border-radius: var(--radius-sm, 8px);
}
.case-article details.case-details > summary {
cursor: pointer;
font-weight: 600;
color: var(--accent);
padding: 0.25rem 0;
}
.case-article details.case-details[open] > summary {
margin-bottom: 0.5rem;
}
.case-block {
margin-top: 3rem;
padding-top: 2.5rem;
@@ -179,315 +114,136 @@
<div class="section__inner case-article">
<p class="hero__eyebrow">Obsidian Bases · Plugin</p>
<h1 class="case-page__h1">
A Kanban view for Obsidian Bases<br />
that leaves your notes untouched
A Kanban board for Obsidian Bases<br />
with filtering and drag-and-drop
</h1>
<p class="case-page__sub">
Card Columns: Notes as cards in columns,<br />
filtered by AND/OR tags with just two clicks
Card Columns: group by properties,<br />
filter tags with AND/OR
</p>
<figure class="case-media case-media--wide">
<img src="{{assetPrefix}}assets/card-columns-obsidian/overview.webp" alt="An Obsidian links database grouped into Card Columns by tags" width="2084" height="1296" loading="eager" decoding="async" data-image-viewer />
<figcaption>A notes database grouped into Columns by tags</figcaption>
<figcaption>Links: 196 results in Columns view</figcaption>
</figure>
<ul class="case-brief">
<li>Role: Product Design and Development</li>
<li><span>Platform:</span> Obsidian Bases</li>
<li><span>Status</span>: Working plugin in the <a href="https://community.obsidian.md/plugins/card-columns" target="_blank" rel="noopener noreferrer">Obsidian plugin directory</a>, open source (MIT)</li>
<li><span>Team: Solo</span>, side project</li>
<li><span>Status:</span> Draft response, to be replaced</li>
</ul>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
How long did it take from the initial idea to a working version? Weeks? A month of weekends?
</p>
<p>
Draft answer: I put it together in three weeks in the evenings, then spent another month refining it while
working on my own projects. Replace with the actual time.
</p>
</div>
<p class="section__prose">
Obsidian now has Bases, a separate file type that displays notes not as text, but
in various formats: tables, cards, and trees. The standard card view didn’t work for me: it’s
flat, with no grouping, and it’s hard to make sense of when there are hundreds of notes. I wanted to see them
arranged in columns, like a Kanban board, but without the extra overhead. That’s how the Card Columns view plugin came about.
Card Columns is my view plugin for Obsidian Bases. I designed and built it myself because I wanted
to scan notes in columns instead of searching through the standard Cards feed. The plugin is
available in the <a href="https://community.obsidian.md/plugins/card-columns" target="_blank" rel="noopener noreferrer">Obsidian Community directory</a>
and is open source under the MIT license.
</p>
<div class="case-block" id="kontekst">
<h2>Context</h2>
<h2>From a Bases query to columns</h2>
<p class="section__prose">
A “Bases” is neither a note nor a separate database. It’s a query to the vault: you select
notes using a filter, and the framework displays them in the chosen view. Obsidian itself has
a built-in card view, but it’s one-dimensional: no grouping by properties, no
proper filtering. I decided to create my own view that integrates into the
Bases toggle alongside the default ones.
A <code>.base</code> file describes a query over notes in the vault: which entries to show and
which properties to display. In my database, Obsidian’s built-in Cards view produced a long
feed. I wanted to see the same results grouped by a property such as status or tag.
</p>
<p class="section__prose">
The main limitation was in the framework itself: the plugin doesn’t store data, but rather renders what
the Bases query returns (Sort, Filter, Properties, Search from the toolbar). This means that all the logic
for data retrieval remains with Bases, and my task is solely to display and navigate the results.
Columns registers as another BasesView and receives the query results. Sorting, filters, visible
properties, and search stay in the native toolbar. I chose not to create a separate board: the
notes and query settings remain in Bases; the plugin changes only how they are displayed.
</p>
</div>
<div class="case-block" id="pochemu-svoi">
<h2>Why not use a ready-made solution?</h2>
<div class="case-block" id="filters">
<h2>Group first, filter second</h2>
<p class="section__prose">
Before writing any code, I checked what was already available. Three options, none of them worked:
I set grouping through Group by in the Bases toolbar: tags, status, category, or any frontmatter
property. If none is selected, Columns groups by tags. The columns use that property’s values;
the underlying query stays the same. To view the database from another angle, I only change
its grouping.
</p>
<p class="section__prose">
<strong>The default Bases card view.</strong>
It displays a continuous stream of cards without grouping.
When there are over a hundred notes, it becomes a feed where you have to scroll and squint at the titles.
I ruled that out first.
I put tag filters right below the toolbar. Click one tag to keep only notes with it. Click another
to add it in OR mode. Ctrl-click or right-click toggles one tag without clearing the rest, and
the OR/AND control changes how multiple selections combine:
</p>
<p class="section__prose">
<strong>Kanban plugins from the community.</strong>
Most come with their own storage system:
the board exists separately from the vault, and notes are duplicated. This results in a second database
that needs to be synchronized. I didn’t need that.
These pills do not replace Filter in the toolbar. Bases creates the initial result set; the tag
filter narrows it inside Columns. I can inspect a subset without changing the underlying query.
</p>
<ul class="section__prose">
<li>the first tag narrows the view to notes that have it;</li>
<li>the next adds notes with any of the selected tags;</li>
<li>Ctrl-click or right-click toggles a single tag in the current selection;</li>
<li>OR/AND switches between “any selected” and “all selected”; All clears the selection.</li>
</ul>
<p class="section__prose">
<strong>The neighboring bases-kanban.</strong>
The closest in spirit: it hooks into Bases to create
a Kanban board. But it writes to the notes: dragging a card changes the frontmatter. For a view plugin,
this is a risk, which I’ll discuss below.
The filter stays visible beside the board. A modal would interrupt the flow: I would have to close
it just to check what changed.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>
The decision to forgo drag-and-drop isn’t a shortcoming—it’s a design choice. A view plugin should not
write to user data. This decision shaped everything else.
</p>
</div>
</aside>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
Did you actually install and test other people’s plugins, or did you just go by the descriptions? Which
ones did you look at specifically?
</p>
<p>
Preliminary answer: I installed three of them, ran them for a week on my database, then uninstalled them. I looked at the source code for bases-kanban
but didn’t install it. Replace with actual information; include the plugin names.
</p>
</div>
<figure class="case-media case-media--wide">
<img src="{{assetPrefix}}assets/card-columns-obsidian/filter-use.webp" alt="Tag filters above Card Columns cards and columns" width="1280" height="796" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Tag filters stay beside the columns</figcaption>
</figure>
</div>
<div class="case-block" id="zadacha">
<h2>Task</h2>
<div class="case-block" id="cards">
<h2>Cards show the fields I need</h2>
<p class="section__prose">
Design a view for Bases that displays notes as cards arranged in columns and allows you to
quickly select the ones you need. Strict constraint: the plugin does not write anything to the notes and does not store
its own state, and sorting, filtering, properties, and search from the standard Bases toolbar must
work as before.
The card contents follow the native Properties selection. The first selected property becomes
the title; the rest appear as chips: text, tags, links, booleans, and dates. Chips can use a vertical
stack or compact grid, with configurable wrapping and date formats.
</p>
<p class="section__prose">
When a database needs covers, Columns can use the first image in a note or a chosen frontmatter
property. I can set the aspect ratio (Auto, 1:1, 3:2, 4:3, or 16:9), fit, and position. Cards can
open in the active pane, a floating modal, a new tab, or a split pane. In a split, a click reuses
the pane; Ctrl-click opens a new tab. That keeps the note beside the board without losing the
current query context.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>
The main risk is creating a view that conflicts with the standard Bases toolbar. If that happens,
the user loses the sorting and search capabilities—the very features that make Bases worth using. This could not
be allowed to happen.
</p>
</div>
</aside>
</div>
<div class="case-block" id="reshenie">
<h2>Solutions</h2>
<h2>Dragging a card edits its note</h2>
<p class="section__prose">
The plugin adds a “Columns” view to Bases. Cards are built from the results of a Bases query;
grouping is defined by a property, and filtering is done via clickable tags. Next, there are five options
that determine the result. Each one includes something I decided against.
</p>
<!-- МЕДИА: скриншот вида с колонками и карточками внутри.
<figure class="case-media case-media--wide">
<img src="../assets/card-columns-obsidian/board.webp" alt="Columns of cards grouped by property" width="1920" height="1080" loading="lazy" decoding="async" data-image-viewer />
<figcaption>View with columns</figcaption>
</figure>
-->
<h3>Grouping by any property</h3>
<p class="section__prose">
Columns are generated using the “Group by” option in the Bases toolbar, by tag, status, category, or any
frontmatter property. Change the property in the query, and the board reloads automatically. This way, a single
database covers different perspectives: projects by status, notes by tags, tasks by
priority.
</p>
<h3>Tag filter by AND and OR</h3>
<p class="section__prose">
This is exactly what the plugin was designed for. Below the toolbar, there are tag icons; when clicked, they
filter cards across all columns at once:
</p>
<ul class="section__prose">
<li>Left-click on a tag to show only that tag (resets the other filters).</li>
<li>Left-click on the next tag to show notes with ANY of the selected tags (OR mode).</li>
<li>Ctrl+Click or right-click to switch tags without resetting the selection.</li>
<li>The OR/AND button: toggle between “any of” and “all of” for multiple tags.</li>
<li>All: Clear all filters.</li>
</ul>
<p class="section__prose">
The combination of “OR—add with a left-click + exact toggle via Ctrl” turned out to be intuitive:
quick selection doesn’t disrupt what’s already selected. I immediately rejected the alternative—moving the filter to a modal window—
because it requires an extra click and loses the context of what’s already been filtered.
In version 0.9.0, a card can be dragged into another column. That movement changes data: Columns
updates the frontmatter property used for grouping through Obsidian’s
<code>processFrontMatter()</code> API. If the board is grouped by status, dropping a card into a
different column changes that note’s status. For a multi-value property, the old value is removed,
the new one is added, and the other values stay in place.
</p>
<p class="section__prose">
The choice here isn’t about the feature itself, but where it lives. I chose the pills below the toolbar instead of
a separate filter window because filtering by tags is an action performed at every step of working
with the database. A modal window turns it into an interrupted workflow: open, configure, close, check.
The pill-shaped icons keep the result right in front of you, and the filter status is always visible.
Drag-and-drop is enabled by default, but it can be turned off in General settings. The choice is
explicit: use the board to inspect groups, or edit the grouping property by moving a card.
</p>
<h3>Settings for different databases</h3>
<p class="section__prose">
The view has four settings groups: General, Title, Properties, and Cover. They cover card width,
one to six cards per group, column order, zebra striping, and masonry. Column order can be automatic
or set manually, with empty columns kept in place. The grid places cards side by side,
masonry fills vertical gaps, and zebra striping helps scan a wide board. Title style,
property chips, dates, and covers have their own controls. The view adapts to the database.
</p>
<figure class="case-media case-media--wide">
<img src="{{assetPrefix}}assets/card-columns-obsidian/filter-use.webp" alt="Tag pills used to filter cards in Card Columns" width="1280" height="796" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Filtering cards by tag</figcaption>
</figure>
<h3>Cards and Covers</h3>
<p class="section__prose">
Properties such as chips, text, tags, links, Booleans, and dates are displayed for each note. You can
show a cover: from the first image in the note or from a separate property. You have
full control over the cover, including aspect ratio (Auto, 1:1, 3:2, 4:3, 16:9), border, fit
(cover/contain), orientation, and position on the card (above the title, below it, at the end).
</p>
<h3>Solution: Settings have been moved out of the code</h3>
<p class="section__prose">
At first, the parameters were hardcoded for my database. This was a mistake: the view would stop working
for someone else’s scenario. I moved everything to the gear menu, dividing it into four sections so that the view
could adapt to different databases, rather than just my single use case. More than 15 parameters.
</p>
<details class="case-details">
<summary>Show the full list of settings</summary>
<p class="section__prose"><strong>General</strong></p>
<ul class="section__prose">
<li>Where to open the card: active panel, pop-up window, new tab, split to the right / down.</li>
<li>Card width (px).</li>
<li>Columns per group (1–6), grid within a column.</li>
<li>Zebra columns (alternating background colors for easy scanning).</li>
<li>Masonry layout (cards fill vertical gaps).</li>
</ul>
<p class="section__prose"><strong>Title</strong></p>
<ul class="section__prose">
<li>Title wrapping, bold title, font size (px).</li>
</ul>
<p class="section__prose"><strong>Properties</strong></p>
<ul class="section__prose">
<li>Chip layout: stack (vertical) or grid (compact in a row).</li>
<li>Wrap multi-line values, font size.</li>
<li>Date and date-time formats + locale (Moment.js).</li>
</ul>
<p class="section__prose"><strong>Cover</strong></p>
<ul class="section__prose">
<li>Source: none / first image in the post / cover property.</li>
<li>Style (borderless / with border), aspect ratio, orientation, fit, position on the card.</li>
</ul>
</details>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
Is it true about the hard-coded parameters? If not, replace it with the real reason for moving them
to the settings: user requests, multiple databases with different structures, whatever.
</p>
<p>
Preliminary answer: Yes, the first version was tailored to my database; the settings were added when
I needed to make the plugin available externally. I’ll check and fix it.
</p>
</div>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">💡</span>
<div class="case-callout__content">
<p>
I implemented the split preview just like in Obsidian itself: left-click reuses the panel, Ctrl+click opens
a new tab within the split. Without this, on a wide screen, it’s inconvenient to
view cards next to the board.
</p>
</div>
</aside>
<h3>Works within the standard toolbar</h3>
<p class="section__prose">
The view hooks into the BasesView framework, so sorting, filtering, properties, and search from
the Bases toolbar remain functional. The plugin only renders the query results; the data is not
duplicated and does not depend on a separate storage.
</p>
<figure class="case-media case-media--wide">
<img src="{{assetPrefix}}assets/card-columns-obsidian/view-configure.webp" alt="Configure view popover with card and column width settings" width="1280" height="1183" loading="lazy" decoding="async" data-image-viewer />
<img src="{{assetPrefix}}assets/card-columns-obsidian/view-configure.webp" alt="Columns view configuration screen" width="1280" height="1183" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Columns view settings</figcaption>
</figure>
</div>
<div class="case-block" id="rezultat">
<h2>The Result and How I Tested It</h2>
<h2>Result: a board inside Obsidian Bases</h2>
<p class="section__prose">
This view solved my problem: a Bases query now reads like a board, rather than a flat stream
of cards. Grouping by property + filtering by AND/OR tags—exactly what the default
view was missing. Everything runs on the Bases query, so moving the vault and syncing don’t
break anything.
Card Columns keeps notes in Bases but changes how I scan them: I choose a grouping, filter tags,
and open cards beside the board. When I need to change a status, dragging a card updates the same
frontmatter property that forms the columns. I still use Columns with my own database; notes stay
in the vault, so there is no separate board to keep in sync.
</p>
<h3>Metrics: number of steps to filter</h3>
<p class="section__prose">
The main task I used to measure success: filter notes by two tags and find the right
card. In the standard card view, this involves scrolling through the feed and scanning the titles with your eyes. In
Columns, two clicks on the pills and the column is right in front of you.
</p>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
Can you run this test for real? Time 10 repetitions of the task: the default
view versus Columns. Or count the exact number of steps.
</p>
<p>
Preliminary answer: on a sample of 25 notes, filtering by two tags took about 40 seconds and
required scrolling through the feed; in Columns, two clicks, about 5 seconds. These are rough estimates—
replace them with actual measurements.
</p>
</div>
<h3>Test: Long-term usability in daily work</h3>
<p class="section__prose">
The second criterion is more subjective, but for a tool tailored to my needs, it’s more important: will I still be
using the plugin a month from now? I am—it’s become an integral part of my work with the database and doesn’t require
me to remember how to use it.
</p>
<div class="case-todo">
<span class="case-todo__label">Question for the author</span>
<p class="case-todo__q">
Are there any external indicators: installs, GitHub stars, reviews, or questions in the issue tracker? Any
number confirming that you’re not the only one using the plugin.
</p>
<p>
Preliminary answer: The plugin currently needs to be installed manually; it hasn’t been approved for the community repository yet, so
there are only a handful of installs and no stars so far. If there’s actual data, include it here—
it’s more convincing than any self-report.
</p>
</div>
<p class="section__prose">
What I’d do differently. I deliberately chose not to implement drag-and-drop of cards between columns,
since that changes a property in the note, and for a view plugin, that poses a separate data integrity risk.
But I see a demand for this among users, and I might add it as an optional mode. For
now, since the plugin is maintained manually (it hasn’t been reviewed by the community directory yet), this is the only thing
preventing users from using it right away.
The screenshot shows my Links database; Obsidian displays 196 results. The plugin is listed in
Obsidian Community. When I updated this case, the listing showed version 0.9.0 and 846 downloads;
it supports Obsidian 1.8.0+, desktop and mobile, under the MIT license.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">✅</span>
<div class="case-callout__content">
<p>
Main conclusion: a view for Bases only works when it respects
the framework’s toolbar. I added a view, not a second storage system, and the notes
remained regular notes.
I care about the boundary between viewing and changing data. Grouping and filtering help me
read the database; moving a card edits its property. In Columns, that action is visible and
can be turned off.
</p>
</div>
</aside>
+83 -322
View File
@@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta
name="description"
content="Кейс: Card Columns, плагин-вид для Obsidian Bases. Показывает заметки как карточки, разложенные по колонкам, с фильтрацией тегов по И/ИЛИ."
content="Как я создал Card Columns для Obsidian Bases: группировка заметок по свойствам, фильтрация тегов AND/OR и перенос карточек между колонками."
/>
<title>Card Columns, плагин для Obsidian Bases: кейс</title>
<title>Card Columns для Obsidian Bases: кейс о канбан-виде</title>
{{> head-links}}
{{> head-plyr}}
<style>
@@ -48,72 +48,7 @@
.case-article a:not(.btn) {
color: var(--accent);
}
.case-article .case-brief {
list-style: none;
margin: 1.5rem 0 0;
padding: 1.1rem 1.25rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
gap: 0.85rem 1.5rem;
font-size: 0.9rem;
border: 1px solid color-mix(in srgb, var(--text) 14%, transparent);
border-radius: var(--radius-md, 12px);
}
.case-article .case-brief li {
margin: 0;
}
.case-article .case-brief span {
display: block;
margin-bottom: 0.15rem;
color: var(--text-muted);
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.case-article .case-todo {
margin: 1rem 0 1.25rem;
padding: 0.9rem 1.1rem;
border-left: 2px solid var(--accent);
background: color-mix(in srgb, var(--accent) 8%, transparent);
border-radius: 0 var(--radius-sm, 8px) var(--radius-sm, 8px) 0;
font-size: 0.92rem;
}
.case-article .case-todo p {
margin: 0 0 0.5rem;
}
.case-article .case-todo p:last-child {
margin-bottom: 0;
}
.case-article .case-todo__q {
font-weight: 600;
color: var(--accent);
}
.case-todo__label {
display: block;
margin-bottom: 0.35rem;
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
opacity: 0.85;
}
.case-article details.case-details {
margin: 0.75rem 0 1rem;
padding: 0.6rem 0.9rem;
border: 1px solid color-mix(in srgb, var(--text) 14%, transparent);
border-radius: var(--radius-sm, 8px);
}
.case-article details.case-details > summary {
cursor: pointer;
font-weight: 600;
color: var(--accent);
padding: 0.25rem 0;
}
.case-article details.case-details[open] > summary {
margin-bottom: 0.5rem;
}
.case-block {
margin-top: 3rem;
padding-top: 2.5rem;
@@ -179,311 +114,137 @@
<div class="section__inner case-article">
<p class="hero__eyebrow">Obsidian Bases · Plugin</p>
<h1 class="case-page__h1">
Канбан-вид для Obsidian Bases,<br />который не&nbsp;трогает ваши заметки
Канбан-доска для Obsidian Bases<br />с фильтрацией и перетаскиванием
</h1>
<p class="case-page__sub">
Card Columns: заметки как карточки по колонкам,<br />
отбор по тегам И/ИЛИ за два клика
Card Columns: группировка по свойствам,<br />
быстрый отбор тегов AND/OR
</p>
<figure class="case-media case-media--wide">
<img src="{{assetPrefix}}assets/card-columns-obsidian/overview.webp" alt="База ссылок в Obsidian, сгруппированная по тегам в колонки Card Columns" width="2084" height="1296" loading="eager" decoding="async" data-image-viewer />
<figcaption>База заметок, сгруппированная по тегам в Columns</figcaption>
<figcaption>Links: 196 результатов в режиме Columns</figcaption>
</figure>
<ul class="case-brief">
<li><span>Роль</span>Продуктовый дизайн и разработка</li>
<li><span>Платформа</span>Obsidian Bases</li>
<li><span>Статус</span>Рабочий плагин в <a href="https://community.obsidian.md/plugins/card-columns" target="_blank" rel="noopener noreferrer">каталоге плагинов Obsidian</a>, open source (MIT)</li>
<li><span>Команда</span>Соло, пет-проект</li>
<li><span>Срок</span>Черновой ответ, заменить</li>
</ul>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Сколько заняла работа от первой идеи до рабочей версии? Недели? Месяц выходных?
</p>
<p>
Черновой ответ: собрал за три недели вечером, потом ещё месяц доводил по ходу
собственной работы. Заменить на реальное.
</p>
</div>
<p class="section__prose">
В Obsidian появились Bases, отдельный тип файла, который показывает заметки не текстом, а
в разных видах: таблицей, карточками, деревом. Стандартный вид карточек мне не зашёл: он
плоский, без группировки, и в нём не разобраться, когда заметок сотни. Я хотел видеть их
раскладку по колонкам, как канбан, но без лишнего веса. Так появился плагин-вид Card Columns.
Card Columns - мой плагин-вид для Obsidian Bases. Я спроектировал и разработал его сам:
хотел видеть заметки колонками, а не искать нужные в длинной ленте штатного Cards. Плагин
опубликован в <a href="https://community.obsidian.md/plugins/card-columns" target="_blank" rel="noopener noreferrer">каталоге Obsidian Community</a>,
исходный код открыт по лицензии MIT.
</p>
<div class="case-block" id="kontekst">
<h2>Контекст</h2>
<h2>От запроса Bases к колонкам</h2>
<p class="section__prose">
Bases, это не заметка и не отдельная база данных. Это запрос к vault-у: выбираешь
заметки по фильтру, и фреймворк рисует их в выбранном виде. У самого Obsidian есть
встроенный вид карточек, но он одноплановый: нет группировки по свойствам, нет
нормальной фильтрации. Я решил сделать свой вид, который встраивается в переключатель
Bases наравне со штатными.
Файл <code>.base</code> описывает запрос к заметкам в vault: какие записи показать и какие
свойства вывести. Штатный вид Cards в моей базе складывал результаты в длинную ленту.
Я хотел видеть ту же выборку по группам, например по статусу или тегу.
</p>
<p class="section__prose">
Главное ограничение было в самом фреймворке: плагин не хранит данные, а рисует то, что
выдал Bases-запрос (Sort, Filter, Properties, Search из тулбара). Значит, вся логика
выборки остаётся на Bases, а моя задача, только представление и навигация по нему.
Columns подключается как ещё один BasesView и получает результат запроса. Сортировка,
фильтры, набор свойств и поиск остаются за штатным тулбаром. Я не стал заводить отдельную
доску: заметки и настройки запроса остаются в Bases, плагин меняет только их представление.
</p>
</div>
<div class="case-block" id="pochemu-svoi">
<h2>Почему не готовое</h2>
<div class="case-block" id="filtry">
<h2>Сначала сгруппировать, потом отобрать</h2>
<p class="section__prose">
Прежде чем писать код, я посмотрел, что уже есть. Три пути, все мимо:
Группировку настраиваю через Group by в тулбаре Bases: по тегу, статусу, категории или
любому frontmatter-свойству. Если свойство не задано, Columns группирует по тегам. Колонки
строятся из значений выбранного свойства, а исходный запрос остаётся тем же. Одна база
подходит для разных разрезов: меняю только группировку.
</p>
<p class="section__prose">
<strong>Штатный вид карточек Bases.</strong> Рисует ровный поток карточек без группировки.
Когда заметок за сотню, это лента, в которой нужно скроллить и вглядываться в заголовки.
Отпал первым.
Теги для быстрого отбора вынесены отдельными пилюлями под тулбаром. Щёлкнул один раз,
оставил этот тег. Выбрал следующий, добавил его по OR. Для точного переключения использую
Ctrl+клик или правую кнопку мыши, а кнопкой OR/AND меняю логику отбора:
</p>
<p class="section__prose">
<strong>Канбан-плагины из комьюнити.</strong> Большинство тянет за собой свою систему
хранения: доска живёт отдельно от vault-а, заметки дублируются. Получается вторая база,
которую надо синхронизировать. Мне это было не нужно.
Эти пилюли не заменяют Filter в тулбаре. Bases формирует исходную выборку, а теговый фильтр
сужает её уже внутри Columns. Так я не меняю запрос, когда нужно быстро посмотреть подмножество.
</p>
<ul class="section__prose">
<li>первый тег оставляет только заметки с ним;</li>
<li>следующий добавляет заметки с любым из выбранных тегов;</li>
<li>Ctrl+клик или правый клик добавляет и снимает один тег, не сбрасывая остальные;</li>
<li>OR/AND переключает условие между «любой из» и «все выбранные», All очищает выбор.</li>
</ul>
<p class="section__prose">
<strong>Соседний bases-kanban.</strong> Ближайший по духу: цепляется к Bases, делает
канбан. Но он пишет в заметки: перетаскивание карточки меняет frontmatter. Для view-плагина
это риск, о котором ниже.
Фильтр остаётся перед глазами вместе с доской. Модальное окно здесь только мешало бы:
пришлось бы закрывать его, чтобы проверить результат.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>
Отказ от drag-and-drop, это не недоделка, а позиция. Плагин представления не должен
писать в пользовательские данные. Это решение определило всё остальное.
</p>
</div>
</aside>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Ты реально ставил и тестировал чужие плагины или ориентировался по описаниям? Какие
именно смотрел?
</p>
<p>
Черновой ответ: поставил три штуки, погонял неделю на своей базе, удалил. bases-kanban
смотрел в исходниках, не ставил. Заменить на реальное, названия плагинов вписать.
</p>
</div>
<figure class="case-media case-media--wide">
<img src="{{assetPrefix}}assets/card-columns-obsidian/filter-use.webp" alt="Теги-фильтры над колонками карточек Card Columns" width="1280" height="796" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Фильтры тегов остаются рядом с колонками</figcaption>
</figure>
</div>
<div class="case-block" id="zadacha">
<h2>Задача</h2>
<div class="case-block" id="kartochki">
<h2>Карточка показывает нужные поля</h2>
<p class="section__prose">
Спроектировать вид для Bases, который показывает заметки карточками по колонкам и позволяет
быстро отбирать нужное. Жёсткое ограничение: плагин ничего не пишет в заметки и не хранит
своего состояния, а сортировка, фильтр, свойства и поиск из штатного тулбара Bases должны
работать как раньше.
Состав карточки задаётся штатным списком Properties. Первое выбранное свойство становится
заголовком, остальные отображаются чипами: текст, теги, ссылки, булевы значения и даты.
Для чипов можно выбрать вертикальный стек или компактную сетку, настроить перенос и формат даты.
</p>
<p class="section__prose">
Если базе нужны обложки, Columns возьмёт первую картинку из заметки или значение указанного
frontmatter-свойства. Можно настроить пропорции (Auto, 1:1, 3:2, 4:3 или 16:9), вписывание
и место обложки. Карточка открывается в активной панели, модальном окне, новой вкладке или
сплите. В сплите обычный клик переиспользует панель, Ctrl+клик открывает новую вкладку.
Так можно читать заметку рядом с доской и не терять контекст выборки.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>
Главный риск, сделать вид, который конфликтует со штатным тулбаром Bases. Тогда
пользователь теряет сортировку и поиск, ради которых Bases и берут. Этого нельзя
было допустить.
</p>
</div>
</aside>
</div>
<div class="case-block" id="reshenie">
<h2>Решения</h2>
<h2>Перетаскивание меняет свойство заметки</h2>
<p class="section__prose">
Плагин добавляет в Bases вид «Columns». Карточки строятся из результатов Bases-запроса,
группировка задаётся свойством, а отбор, кликабельными тегами. Дальше, пять выборов,
которые определили результат. В каждом есть то, от чего я отказался.
</p>
<!-- МЕДИА: скриншот вида с колонками и карточками внутри.
<figure class="case-media case-media--wide">
<img src="../assets/card-columns-obsidian/board.webp" alt="Колонки карточек с группировкой по свойству" width="1920" height="1080" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Вид с колонками</figcaption>
</figure>
-->
<h3>Группировка по любому свойству</h3>
<p class="section__prose">
Колонки формируются из Group by в тулбаре Bases, по тегу, статусу, категории или любому
frontmatter-свойству. Меняешь свойство в запросе, и доска пересобирается сама. Так одна
и та же база закрывает разные разрезы: проекты по статусу, заметки по тегам, задачи по
приоритету.
</p>
<h3>Фильтр тегов по И и ИЛИ</h3>
<p class="section__prose">
Это то, ради чего плагин и задумывался. Под тулбаром, пилюли тегов, по клику они
отсеивают карточки по всем колонкам сразу:
</p>
<ul class="section__prose">
<li>Левый клик по тегу, показать только его (сбрасывает остальные фильтры).</li>
<li>Левый клик по следующему тегу, показать заметки с ЛЮБЫМ из выбранных (режим ИЛИ).</li>
<li>Ctrl+Клик или правый клик, переключить тег, не сбрасывая выбор.</li>
<li>Кнопка OR/AND, переключить между «любой из» и «все перечисленные» для нескольких тегов.</li>
<li>All, снять все фильтры.</li>
</ul>
<p class="section__prose">
Связка «ИЛИ-добавление левым кликом + точный toggle через Ctrl» получилась интуитивной:
быстрый отбор не сбивает то, что уже выбрано. Альтернативу, вынести фильтр в модалку,
отверг сразу: лишний клик и потеря контекста того, что уже отфильтровано.
В релизе 0.9.0 карточку можно перетащить в другую колонку, и Columns обновит
frontmatter-свойство, по которому построена группировка, через API
Obsidian <code>processFrontMatter()</code>. Если колонки собраны по статусу, после переноса
меняется именно статус заметки. Если в свойстве несколько значений, старое убирается,
новое добавляется, остальные сохраняются.
</p>
<p class="section__prose">
Выбор здесь не в самой фиче, а в том, где она живёт. Я выбрал пилюли под тулбаром вместо
отдельного окна фильтров, потому что отбор по тегам, это действие на каждом шаге работы
с базой. Модалка превращает его в прерванный сценарий: открыл, настроил, закрыл, проверил.
Пилюли оставляют результат перед глазами, а состояние фильтра видно всегда.
Я оставил этот сценарий настраиваемым. Drag-and-drop включён по умолчанию, но его можно
отключить в General. Так пользователь сам выбирает: только смотреть на группы или менять
значение свойства прямо с доски.
</p>
<h3>Настройки под разные базы</h3>
<p class="section__prose">
У вида четыре группы настроек: General, Title, Properties и Cover. В них задаются ширина
карточек, число колонок внутри группы (от 1 до 6), порядок колонок, зебра и masonry.
Порядок можно оставить автоматическим или задать вручную; пустые колонки в этом списке
сохраняются. Сетка показывает несколько карточек рядом, masonry заполняет вертикальные
пустоты, а зебра помогает не сбиться при чтении широкой доски. Отдельно настраиваются заголовки, чипы, даты
и обложки. Один вид подстраивается под разные базы, а не наоборот.
</p>
<figure class="case-media case-media--wide">
<img src="{{assetPrefix}}assets/card-columns-obsidian/filter-use.webp" alt="Панель тегов для фильтрации карточек в Card Columns" width="1280" height="796" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Фильтрация карточек по тегам</figcaption>
</figure>
<h3>Карточки и обложки</h3>
<p class="section__prose">
По каждой заметке рисуются свойства как чипы, текст, теги, ссылки, булевы, даты. Можно
показать обложку: из первой картинки в заметке или из отдельного свойства. У обложки
полный контроль, соотношение сторон (Auto, 1:1, 3:2, 4:3, 16:9), рамка, вписывание
(cover/contain), ориентация и место на карточке (над заголовком, под ним, после всего).
</p>
<h3>Решение: настройки вынесены из кода</h3>
<p class="section__prose">
Сначала параметры были захардкожены под мою базу. Это ошибка: вид перестал бы работать
для чужого сценария. Вынес всё в gear-меню, разбив на четыре секции, чтобы вид
подстраивался под разные базы, а не под мой единственный случай. Больше 15 параметров.
</p>
<details class="case-details">
<summary>Показать полный список настроек</summary>
<p class="section__prose"><strong>General</strong></p>
<ul class="section__prose">
<li>Куда открывать карточку: активная панель, всплывающее окно, новая вкладка, сплит вправо / вниз.</li>
<li>Ширина карточки (px).</li>
<li>Колонок в группе (1–6), сетка внутри колонки.</li>
<li>Зебра колонок (чередование фона для сканирования).</li>
<li>Masonry-раскладка (карточки заполняют вертикальные пустоты).</li>
</ul>
<p class="section__prose"><strong>Title</strong></p>
<ul class="section__prose">
<li>Перенос заголовка, жирный заголовок, размер шрифта (px).</li>
</ul>
<p class="section__prose"><strong>Properties</strong></p>
<ul class="section__prose">
<li>Раскладка чипов: стек (вертикально) или сетка (компактно в строку).</li>
<li>Перенос многострочных значений, размер шрифта.</li>
<li>Формат даты и даты-времени + локаль (Moment.js).</li>
</ul>
<p class="section__prose"><strong>Cover</strong></p>
<ul class="section__prose">
<li>Источник: нет / первая картинка в заметке / свойство-обложка.</li>
<li>Стиль (без рамки / с рамкой), соотношение сторон, ориентация, вписывание, позиция на карточке.</li>
</ul>
</details>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Это правда про захардкоженные параметры? Если нет, замени на реальную причину выноса
в настройки: просьбы пользователей, несколько баз с разной структурой, что угодно.
</p>
<p>
Черновой ответ: да, первая версия была под мою базу, настройки появились, когда
понадобилось открыть плагин наружу. Проверить и поправить.
</p>
</div>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">💡</span>
<div class="case-callout__content">
<p>
Сплит-превью сделал как в самом Obsidian: левый клик переиспользует панель, Ctrl+Клик,
новую вкладку внутри сплита. Без этого на широком экране карточки неудобно
просматривать рядом с доской.
</p>
</div>
</aside>
<h3>Работает внутри штатного тулбара</h3>
<p class="section__prose">
Вид цепляется к BasesView-фреймворку, поэтому сортировка, фильтр, свойства и поиск из
тулбара Bases остаются рабочими. Плагин только рисует результат запроса, данные не
дублируются и не зависят от отдельного хранилища.
</p>
<figure class="case-media case-media--wide">
<img src="{{assetPrefix}}assets/card-columns-obsidian/view-configure.webp" alt="Окно настройки вида Columns с параметрами карточек и ширины колонок" width="1280" height="1183" loading="lazy" decoding="async" data-image-viewer />
<img src="{{assetPrefix}}assets/card-columns-obsidian/view-configure.webp" alt="Экран настройки вида Columns" width="1280" height="1183" loading="lazy" decoding="async" data-image-viewer />
<figcaption>Настройки вида Columns</figcaption>
</figure>
</div>
<div class="case-block" id="rezultat">
<h2>Результат и как я это мерял</h2>
<h2>Результат: доска внутри Obsidian Bases</h2>
<p class="section__prose">
Вид закрыл мою боль: Bases-запрос теперь читается как доска, а не как плоская лента
карточек. Группировка по свойству + отбор по тегам И/ИЛИ, то, чего не хватало штатному
виду. Всё крутится на запросе Bases, так что перенос vault-а и синхронизация ничего не
ломают.
Card Columns оставляет заметки в Bases, но меняет способ их обзора: я задаю группировку,
отбираю теги и открываю карточки рядом с доской. Когда нужна смена статуса, перетаскивание
правит то же frontmatter-свойство, по которому строятся колонки. Я продолжаю пользоваться
Columns в своей базе: заметки остаются в vault, отдельную доску синхронизировать не нужно.
</p>
<h3>Замер: сколько действий на отбор</h3>
<p class="section__prose">
Главная задача, по которой я мерял успех: отобрать заметки по двум тегам и найти нужную
карточку. В штатном виде карточек это скролл ленты с поиском по заголовкам глазами. В
Columns, два клика по пилюлям и колонка перед глазами.
</p>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Можешь прогнать этот замер по-настоящему? Засечь время на 10 повторах задачи: штатный
вид против Columns. Или посчитать точное число действий.
</p>
<p>
Черновой ответ: на выборке из 25 заметок отбор по двум тегам занимал около 40 секунд и
требовал скроллить ленту; в Columns, два клика, примерно 5 секунд. Цифры примерные,
заменить на замеренные.
</p>
</div>
<h3>Замер: живучесть в ежедневной работе</h3>
<p class="section__prose">
Второй критерий субъективнее, но для инструмента под себя он важнее: продолжаю ли я
пользоваться плагином через месяц. Пользуюсь, он встроился в работу с базой и не требует
вспоминать, как им управлять.
</p>
<div class="case-todo">
<span class="case-todo__label">Вопрос к автору</span>
<p class="case-todo__q">
Есть ли внешние сигналы: установки, звёзды на GitHub, отзывы, вопросы в issue? Любая
цифра, подтверждающая, что плагином пользуются не только ты.
</p>
<p>
Черновой ответ: плагин пока ставится вручную, через каталог комьюнити не прошёл, так
что установок единицы и звёзд пока нет. Если есть реальные данные, вписать их сюда,
это сильнее любого-self-report.
</p>
</div>
<p class="section__prose">
Что сделал бы иначе. Drag-and-drop карточек между колонками не стал делать сознательно,
это меняет свойство в заметке, и для view-плагина это отдельный риск целостности данных.
Но вижу запрос на это у пользователей, и, возможно, добавлю как опциональный режим. А
пока плагин живёт вручную (через community-каталог ещё не прошёл), это единственное, что
мешает им пользоваться сходу.
На скриншоте моя база Links: Obsidian показывает 196 результатов. Плагин уже опубликован
в каталоге Obsidian Community. На момент обновления кейса там указаны версия 0.9.0 и
846 загрузок; поддерживаются Obsidian 1.8.0+, Desktop и Mobile, лицензия MIT.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">✅</span>
<div class="case-callout__content">
<p>
Главный вывод: вид для Bases получается рабочим только тогда, когда он уважает
тулбар фреймворка. Я добавил представление, а не вторую систему хранения, и заметки
остались обычными заметками.
Для меня важна граница между обзором и изменением данных. Группировка и фильтр помогают
читать базу; перенос карточки меняет её свойство. В Columns это действие видно и его
можно отключить.
</p>
</div>
</aside>
+39 -82
View File
@@ -1,115 +1,72 @@
# Card Columns — плагин-вид для Obsidian Bases
# Card Columns для Obsidian Bases
- **Источник:** `public/cases/card-columns-obsidian/index.html`
- **Источник:** `src/pages/cases/card-columns-obsidian.html`
- **eyebrow:** Obsidian Bases · Plugin
- **meta description:** Кейс: Card Columns, плагин-вид для Obsidian Bases. Показывает заметки как карточки, разложенные по колонкам, с фильтрацией тегов по И/ИЛИ.
- **meta description:** Как я создал Card Columns для Obsidian Bases: группировка заметок по свойствам, фильтрация тегов AND/OR и перенос карточек между колонками.
---
## Канбан-доска для Obsidian Bases с фильтрацией и перетаскиванием
**Заметка по путям к медиа:** файлы кладутся в `public/assets/card-columns-obsidian/`, в разметке страницы пути `../assets/card-columns-obsidian/файл.webp`. Форматы: WebP (изображения, постеры), WebM + MP4 (видео).
Card Columns: группировка по свойствам, быстрый отбор тегов AND/OR.
## Brief (блок метаданных на странице)
На скриншоте моя база Links: Obsidian показывает 196 результатов.
- **Роль:** Продуктовый дизайн и разработка
- **Платформа:** Obsidian Bases
- **Статус:** Рабочий плагин в [каталоге плагинов Obsidian](https://community.obsidian.md/plugins/card-columns), open source (MIT)
- **Команда:** Соло, пет-проект
Card Columns - мой плагин-вид для Obsidian Bases. Я спроектировал и разработал его сам: хотел видеть заметки колонками, а не искать нужные в длинной ленте штатного Cards. Плагин опубликован в [каталоге Obsidian Community](https://community.obsidian.md/plugins/card-columns), исходный код открыт по лицензии MIT.
## Введение
## От запроса Bases к колонкам
В Obsidian появились Bases, отдельный тип файла, который показывает заметки не текстом, а в разных видах: таблицей, карточками, деревом. Стандартный вид карточек мне не зашёл: он плоский, без группировки, и в нём не разобраться, когда заметок сотни. Я хотел видеть их раскладку по колонкам, как канбан, но без лишнего веса. Так появился плагин-вид Card Columns.
Файл `.base` описывает запрос к заметкам в vault: какие записи показать и какие свойства вывести. Штатный вид Cards в моей базе складывал результаты в длинную ленту. Я хотел видеть ту же выборку по группам, например по статусу или тегу.
## Контекст
Columns подключается как ещё один BasesView и получает результат запроса. Сортировка, фильтры, набор свойств и поиск остаются за штатным тулбаром. Я не стал заводить отдельную доску: заметки и настройки запроса остаются в Bases, плагин меняет только их представление.
Bases, это не заметка и не отдельная база данных. Это запрос к vault-у: выбираешь заметки по фильтру, и фреймворк рисует их в выбранном виде. У самого Obsidian есть встроенный вид карточек, но он одноплановый: нет группировки по свойствам, нет нормальной фильтрации. Я решил сделать свой вид, который встраивается в переключатель Bases наравне со штатными.
## Сначала сгруппировать, потом отобрать
Главное ограничение было в самом фреймворке: плагин не хранит данные, а рисует то, что выдал Bases-запрос (Sort, Filter, Properties, Search из тулбара). Значит, вся логика выборки остаётся на Bases, а моя задача, только представление и навигация по нему.
Группировку настраиваю через Group by в тулбаре Bases: по тегу, статусу, категории или любому frontmatter-свойству. Если свойство не задано, Columns группирует по тегам. Колонки строятся из значений выбранного свойства, а исходный запрос остаётся тем же. Одна база подходит для разных разрезов: меняю только группировку.
## Задача
Теги для быстрого отбора вынесены отдельными пилюлями под тулбаром. Щёлкнул один раз, оставил этот тег. Выбрал следующий, добавил его по OR. Для точного переключения использую Ctrl+клик или правую кнопку мыши, а кнопкой OR/AND меняю логику отбора:
Спроектировать и собрать view-плагин для Obsidian Bases: заметки из запроса показываются карточками, разложенными по колонкам, а по колонкам легко ориентироваться и отбирать нужное. Вид должен жить в переключателе Bases рядом со штатными и не ломать встроенный тулбар (сортировку, фильтр, поиск).
Эти пилюли не заменяют Filter в тулбаре. Bases формирует исходную выборку, а теговый фильтр сужает её уже внутри Columns. Так я не меняю запрос, когда нужно быстро посмотреть подмножество.
> **Callout (📌):** Главный риск, сделать вид, который конфликтует со штатным тулбаром Bases. Тогда пользователь теряет сортировку и поиск, ради которых Bases и берут. Этого нельзя было допустить.
- первый тег оставляет только заметки с ним;
- следующий добавляет заметки с любым из выбранных тегов;
- Ctrl+клик или правый клик добавляет и снимает один тег, не сбрасывая остальные;
- OR/AND переключает условие между «любой из» и «все выбранные», All очищает выбор.
## Решение
Фильтр остаётся перед глазами вместе с доской. Модальное окно здесь только мешало бы: пришлось бы закрывать его, чтобы проверить результат.
Плагин добавляет в Bases вид «Columns». Карточки строятся из результатов Bases-запроса, группировка задаётся свойством, а отбор, кликабельными тегами.
## Карточка показывает нужные поля
### Группировка по любому свойству
Состав карточки задаётся штатным списком Properties. Первое выбранное свойство становится заголовком, остальные отображаются чипами: текст, теги, ссылки, булевы значения и даты. Для чипов можно выбрать вертикальный стек или компактную сетку, настроить перенос и формат даты.
Колонки формируются из Group by в тулбаре Bases, по тегу, статусу, категории или любому frontmatter-свойству. Меняешь свойство в запросе, и доска пересобирается сама. Так одна и та же база закрывает разные разрезы: проекты по статусу, заметки по тегам, задачи по приоритету.
Если базе нужны обложки, Columns возьмёт первую картинку из заметки или значение указанного frontmatter-свойства. Можно настроить пропорции (Auto, 1:1, 3:2, 4:3 или 16:9), вписывание и место обложки. Карточка открывается в активной панели, модальном окне, новой вкладке или сплите. В сплите обычный клик переиспользует панель, Ctrl+клик открывает новую вкладку. Так можно читать заметку рядом с доской и не терять контекст выборки.
### Фильтр тегов по И и ИЛИ
## Перетаскивание меняет свойство заметки
Это то, ради чего плагин и задумывался. Под тулбаром, пилюли тегов, по клику они отсеивают карточки по всем колонкам сразу:
В релизе 0.9.0 карточку можно перетащить в другую колонку, и Columns обновит frontmatter-свойство, по которому построена группировка, через API Obsidian `processFrontMatter()`. Если колонки собраны по статусу, после переноса меняется именно статус заметки. Если в свойстве несколько значений, старое убирается, новое добавляется, остальные сохраняются.
- Левый клик по тегу, показать только его (сбрасывает остальные фильтры).
- Левый клик по следующему тегу, показать заметки с ЛЮБЫМ из выбранных (режим ИЛИ).
- Ctrl+Клик или правый клик, переключить тег, не сбрасывая выбор.
- Кнопка OR/AND, переключить между «любой из» и «все перечисленные» для нескольких тегов.
- All, снять все фильтры.
Я оставил этот сценарий настраиваемым. Drag-and-drop включён по умолчанию, но его можно отключить в General. Так пользователь сам выбирает: только смотреть на группы или менять значение свойства прямо с доски.
Связка «ИЛИ-добавление левым кликом + точный toggle через Ctrl» получилась интуитивной: быстрый отбор не сбивает то, что уже выбрано. Альтернативу, вынести фильтр в модалку, отверг сразу: лишний клик и потеря контекста того, что уже отфильтровано.
### Настройки под разные базы
### Карточки и обложки
У вида четыре группы настроек: General, Title, Properties и Cover. В них задаются ширина карточек, число колонок внутри группы (от 1 до 6), порядок колонок, зебра и masonry. Порядок можно оставить автоматическим или задать вручную; пустые колонки в этом списке сохраняются. Сетка показывает несколько карточек рядом, masonry заполняет вертикальные пустоты, а зебра помогает не сбиться при чтении широкой доски. Отдельно настраиваются заголовки, чипы, даты и обложки. Один вид подстраивается под разные базы, а не наоборот.
По каждой заметке рисуются свойства как чипы, текст, теги, ссылки, булевы, даты. Можно показать обложку: из первой картинки в заметке или из отдельного свойства. У обложки полный контроль, соотношение сторон (Auto, 1:1, 3:2, 4:3, 16:9), рамка, вписывание (cover/contain), ориентация и место на карточке (над заголовком, под ним, после всего).
## Результат: доска внутри Obsidian Bases
### Что заведено в настройки
Card Columns оставляет заметки в Bases, но меняет способ их обзора: я задаю группировку, отбираю теги и открываю карточки рядом с доской. Когда нужна смена статуса, перетаскивание правит то же frontmatter-свойство, по которому строятся колонки. Я продолжаю пользоваться Columns в своей базе: заметки остаются в vault, отдельную доску синхронизировать не нужно.
Всю внешнюю вариативность я вынел в gear-меню, разбив на четыре секции, чтобы вид подстраивался под разные базы, а не под мой единственный сценарий.
Плагин опубликован в каталоге Obsidian Community. На момент обновления кейса там указаны версия 0.9.0 и 846 загрузок; поддерживаются Obsidian 1.8.0+, Desktop и Mobile, лицензия MIT.
**General**
- Куда открывать карточку: активная панель, всплывающее окно, новая вкладка, сплит вправо / вниз.
- Ширина карточки (px).
- Колонок в группе (1–6), сетка внутри колонки.
- Зебра колонок (чередование фона для сканирования).
- Masonry-раскладка (карточки заполняют вертикальные пустоты).
**Title**
- Перенос заголовка, жирный заголовок, размер шрифта (px).
**Properties**
- Раскладка чипов: стек (вертикально) или сетка (компактно в строку).
- Перенос многострочных значений, размер шрифта.
- Формат даты и даты-времени + локаль (Moment.js).
**Cover**
- Источник: нет / первая картинка в заметке / свойство-обложка.
- Стиль (без рамки / с рамкой), соотношение сторон, ориентация, вписывание, позиция на карточке.
> **Callout (💡):** Сплит-превью сделал как в самом Obsidian: левый клик переиспользует панель, Ctrl+Клик, новую вкладку внутри сплита. Без этого на широком экране карточки неудобно просматривать рядом с доской.
### Работает внутри штатного тулбара
Вид цепляется к BasesView-фреймворку, поэтому сортировка, фильтр, свойства и поиск из тулбара Bases остаются рабочими. Плагин только рисует результат запроса, данные не дублируются и не зависят от отдельного хранилища.
## Результат
Вид закрыл мою боль: Bases-запрос теперь читается как доска, а не как плоская лента карточек. Группировка по свойству + отбор по тегам И/ИЛИ, то, чего не хватало штатному виду. Всё крутится на запросе Bases, так что перенос vault-а и синхронизация ничего не ломают.
Метрики у пет-проекта нет, и придумывать её не стану. Проверяю проще: юзабилити на себе в ежедневной работе. Критерий, сколько действий нужно, чтобы отобрать заметки по двум тегам и найти нужную карточку. В штатном виде карточек это был скролл ленты с ручным поиском по заголовкам; здесь, два клика по пилюлям и колонка перед глазами. Если бы замерял всерьёз, считал бы время на задачу на выборке из 20–30 заметок до и после, но на своей повседневной базе разница заметна и без секундомера.
Что сделал бы иначе. Drag-and-drop карточек между колонками не стал делать сознательно, это меняет свойство в заметке, и для view-плагина это отдельный риск целостности данных. Но вижу запрос на это у пользователей, и, возможно, добавлю как опциональный режим. А пока плагин живёт вручную (через community-каталог ещё не прошёл), это единственное, что мешает им пользоваться сходу.
> **Callout (✅):** Главный вывод: вид для Bases получается рабочим только тогда, когда он уважает тулбар фреймворка. Я добавил представление, а не вторую систему хранения, и заметки остались обычными заметками.
Для меня важна граница между обзором и изменением данных. Группировка и фильтр помогают читать базу; перенос карточки меняет её свойство. В Columns это действие видно и его можно отключить.
## Медиа
| Файл | Что показывает | Источник |
|---|---|---|
| `overview.webp` | База заметок в режиме Columns, сгруппированная по тегам | `card-columns/images/overview.webp` |
| `filter-use.webp` | Фильтрация карточек кликабельными тегами | `card-columns/images/filter use.webp` |
| `view-configure.webp` | Настройка вида и ширины колонок | `card-columns/images/view configure.webp` |
Изображения взяты из репозитория Card Columns и лежат в `public/assets/card-columns-obsidian/`:
- `overview.webp` - обзор базы Links;
- `filter-use.webp` - фильтрация тегами;
- `view-configure.webp` - настройки вида.
## Источники фактуры
- Репозиторий: `https://github.com/mamadaevv/card-columns` (лицензия MIT).
- Каталог плагинов Obsidian: `https://community.obsidian.md/plugins/card-columns`.
- Скриншоты взяты из `images/` репозитория Card Columns.
- Все параметры кастомизации и логика фильтрации взяты из README репозитория.
- Drag-and-drop в плагине **отсутствует**: он есть у соседнего `ewerx/obsidian-bases-kanban`, не путать.
- Репозиторий Card Columns, версия `0.9.0`: `README.md`, `manifest.json`, `main.ts`.
- Карточка плагина в [Obsidian Community](https://community.obsidian.md/plugins/card-columns): версия, совместимость и число загрузок.
- Скриншот обзора показывает 196 результатов в базе Links.