Skip to content

feat(RadioGroup,CheckboxGroup): rich settings-card layout via #label slot#54

Open
IgorShevchik wants to merge 8 commits into
mainfrom
claude/settings-block-layout-6eg32
Open

feat(RadioGroup,CheckboxGroup): rich settings-card layout via #label slot#54
IgorShevchik wants to merge 8 commits into
mainfrom
claude/settings-block-layout-6eg32

Conversation

@IgorShevchik
Copy link
Copy Markdown
Collaborator

@IgorShevchik IgorShevchik commented May 27, 2026

Кратко о работе

Добавлен рецепт «жирной» карточки выбора настроек для B24RadioGroup и B24CheckboxGroup — заголовок, описание, превью и инлайн-ссылка «Learn more» прямо внутри радио-/чекбокс-кнопки. Под этот кейс рендер слота label в вариантах card и table переведён с <p> на <span>, чтобы туда можно было класть flex-вёрстку с картинкой и ссылкой без HTML-парсинг сюрпризов.

Что сделано

Компоненты (src/runtime/components/)

  • RadioGroup.vue, Checkbox.vue — слот label для variant ∈ {card, table} рендерится как <span>. Атрибут :for биндится условно — только для <label> в list-варианте.
  • CheckboxGroup.vue — добавлен тип-хелпер NormalizeItem<T> (симметрично с RadioGroup); item в слоте теперь сужается до объектной формы.

Документация

  • docs/content/docs/2.components/radio-group.md, …/checkbox-group.md — добавлены секции ## Examples → ### Settings layout :badge{label="Soon"} с перекрёстными ссылками между Radio ↔ Checkbox ↔ PageCardGroup.
  • docs/app/components/content/examples/{radio,checkbox}-group/*SettingsExample.vue — новые example-компоненты с локальным type-interface, английским текстом и <Placeholder> в качестве превью.

Скилл (skills/b24-ui-nuxt/)

  • references/recipes/settings.md — новый рецепт: decision matrix, полный код single- и multi-select, implementation notes (валидация item.href против javascript:/data:, упоминание <Placeholder> для прототипов).
  • references/recipes/card-pickers.md — обратная ссылка на settings.
  • SKILL.md — рецепт зарегистрирован, routing-table обновлена.

Плейграунды (playgrounds/{nuxt,demo}/app/pages/components/)

  • radio-group.vue, checkbox-group.vue — добавлен блок «Settings layout» (английский текст, <Placeholder> вместо <img>).

Тесты (test/components/__snapshots__/)

  • Перегенерированы снапшоты Radio/Checkbox(Group) и Table (<p data-slot="label"><span data-slot="label"> без for).

⚠ BREAKING CHANGE

Слот label в B24RadioGroup, B24Checkbox и B24CheckboxGroup теперь рендерится как <span> вместо <p>, когда variant ∈ {card, table}. Селекторы p[data-slot="label"] и зависимости от дефолтных <p>-отступов внутри этих вариантов потребуют правки. Для variant="list" поведение не изменилось — там по-прежнему <label>.


Чек-лист на ручную проверку

Docs

  • pnpm run docs/docs/components/radio-group — секция «Settings layout» с badge Soon видна, пример рендерится, переключение работает, активная карточка имеет акцентную обводку.
  • То же на /docs/components/checkbox-group (можно отметить обе одновременно).
  • Cross-link «See CheckboxGroup → Settings layout» / «See RadioGroup → Settings layout» работает.
  • Cross-link «B24PageCardGroup» ведёт на нужную страницу.

Playgrounds

  • pnpm run dev/components/radio-group и /components/checkbox-group — блок «Settings layout» виден, поведение совпадает с docs.
  • То же в pnpm run demo:dev.

Поведение карточки

  • Клик в любую часть карточки (заголовок / описание / область превью) — выбирает опцию.
  • Клик по «Learn more» — открывает ссылку в новой вкладке, не переключает радио/чекбокс (это @click.stop).
  • Сжать окно до мобильной ширины — <Placeholder> скрывается (hidden sm:flex), текст занимает всю ширину.
  • Клавиатурная навигация (Tab → стрелки для RadioGroup, Space для Checkbox) работает.

Регрессы

  • variant="list" для всех трёх компонентов — поведение не изменилось, <label> по-прежнему оборачивает текст, клик активирует контрол.
  • B24Table со столбцом выбора строк («Select all» / per-row чекбоксы) — рендерится, выбор работает.
  • HTML-валидатор (или devtools) на странице с card-вариантом — нет ошибки «for attribute is only allowed on label/output».
  • У слот-лейбла больше нет лишнего margin от <p> в card/table.

Skill

  • Открыть skills/b24-ui-nuxt/references/recipes/settings.md — decision matrix читается, multi-select код полный (без «same as above»), warning про URL-валидацию на месте.
  • skills/b24-ui-nuxt/SKILL.md — рецепт перечислен в Recipes, routing-table корректно матчит запрос про «layout chooser with preview».

Автоматика (уже прогнано)

  • pnpm run lint
  • pnpm run typecheck
  • pnpm vitest run — 214 файлов, 4884 passed / 6 skipped / 0 failed

Что дальше

  • Реальные превью. Сейчас в docs/playgrounds стоит <Placeholder> (SVG-штриховка). Когда дизайн нарисует скриншоты — заменить <Placeholder> на &lt;img :src="item.preview" :alt="item.title"&gt; и снять badge Soon. Поля preview в item уже задокументированы в рецепте.
  • Вынести NormalizeItem<T> в общий util — сейчас тип продублирован в RadioGroup.vue и CheckboxGroup.vue. Маленький отдельный PR.
  • Опционально: симметричный перевод слота description с <p> на <span> (текущий рецепт не использует — оставлено как есть).

https://claude.ai/code/session_01VvH54bVMmtobwj6QsVmDjC

claude added 8 commits May 7, 2026 18:39
Add a rich radio cards example (preview image + description + link) to
both the nuxt and demo playground pages, demonstrating how to compose
the existing card variant with the label slot to build a settings-style
selection block (responsive: image hides below sm).
Mirror the rich-card recipe from RadioGroup on CheckboxGroup: same
preview image + description + link layout via the label slot, demonstrated
on both nuxt and demo playground pages.
…t variants

Switch the inner label element from <p> to <span> when variant is "card"
or "table". The list variant still renders the reka-ui Label so clicking
the label keeps activating the radio/checkbox.

Why: <p> auto-closes when it encounters block-level children, which made
it impossible to compose rich label slots (preview image + flex layout +
inline link) without HTML parsing surprises. <span> is phrasing content
and accepts span/img/a children cleanly.

Also narrow the CheckboxGroup slot item type via a NormalizeItem helper,
mirroring the RadioGroup approach, so the slot's "item" excludes the
primitive value branch and exposes user-defined fields directly.

Snapshot updates cover RadioGroup, Checkbox(Group) and Table (Table
embeds a Checkbox in its row-selection column).
Add Examples sections to both component docs that show the rich card
layout (preview image + title + description + "Learn more" link),
backed by example components under docs/app/components/content/examples.

Add a new skill recipe references/recipes/settings.md that frames the
decision: when to use Switch vs Radio vs Checkbox vs PageCardGroup,
plus the full code for the rich-card pattern. Register it in SKILL.md.
…-layout-6eg32

# Conflicts:
#	test/components/__snapshots__/Checkbox-vue.spec.ts.snap
#	test/components/__snapshots__/Checkbox.spec.ts.snap
#	test/components/__snapshots__/CheckboxGroup-vue.spec.ts.snap
#	test/components/__snapshots__/CheckboxGroup.spec.ts.snap
#	test/components/__snapshots__/RadioGroup-vue.spec.ts.snap
#	test/components/__snapshots__/RadioGroup.spec.ts.snap
#	test/components/__snapshots__/Table-vue.spec.ts.snap
#	test/components/__snapshots__/Table.spec.ts.snap
…n label

When the label slot is rendered as a <span> (for variant="card" / "table"),
the for= attribute is invalid HTML — it is only defined for <label> and
<output>. Browsers ignored it, but a11y linters and HTML validators flag
it. Bind :for conditionally so it stays only on the <label> in the list
variant.

Also addresses review feedback on the rest of the settings-layout PR:
- docs/examples: English copy and a locally-typed item interface to keep
  autocomplete / type-checking on slot fields (RadioGroupItem widens
  user-defined fields to any via [key: string]: any)
- docs/examples: alt="item.title" instead of empty alt for the
  meaning-carrying preview image
- docs: Settings layout H3 uses sentence case + Soon badge + cross-link
  between RadioGroup ↔ CheckboxGroup ↔ PageCardGroup
- skill recipe: full multi-select code (no "same as above" placeholder),
  warning to validate item.href (reject javascript:/data: / open redirect)
  when it comes from an untrusted source, mention that target="_blank"
  is safe because B24Link adds rel="noopener noreferrer" automatically
- SKILL.md: routing table row reworded to avoid overlap with
  card-pickers ("capability flags" was matched by both rows)
- card-pickers.md: back-link to the new settings recipe
- playgrounds: same English copy + alt + local type interface

BREAKING CHANGE: The label slot of B24RadioGroup, B24Checkbox and
B24CheckboxGroup now renders as <span> instead of <p> when variant is
"card" or "table". Selectors that targeted "p[data-slot=label]" or
relied on <p>'s default block margin inside these variants need to be
updated. For the default list variant the label is still rendered as
<label> — no change there.
…eviews

Replace the PNG preview placeholders in docs examples and playgrounds with
the in-house <Placeholder> component (diagonal-hatch SVG pattern). The
Placeholder already exists in docs/app/components/content/Placeholder.vue
and in both playgrounds — no need to ship binary assets just to demo the
card layout. Real screenshots can be wired in later by swapping
<Placeholder> back to <img :src :alt>.

- Drop the `preview: '...'` field from item interfaces (no longer needed).
- Drop the 6 PNG files (docs/public/b24ui/radio-card-*.png,
  playgrounds/{nuxt,demo}/public/radio-card-*.png).
- Recipe (settings.md) keeps <img> in the code sample since users will
  bring their own artwork; added a note about <Placeholder> as a stand-in
  while artwork is being designed.
@IgorShevchik IgorShevchik changed the title feat(RadioGroup,CheckboxGroup): settings card layout с превью, описанием и ссылкой feat(RadioGroup,CheckboxGroup): rich settings-card layout via #label slot May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants