Usage
PageCardGroup renders a responsive grid of selectable PageCard items. The selected card gets a highlighted ring and a corner badge. Items can be grouped into category sections.
PageCardGroup for picking one option (or several) out of a small set of presented choices. For plain checkbox/radio lists prefer CheckboxGroup or RadioGroup.Items
Pass an array of objects via the items prop. Each item supports label, description, icon, value, disabled and an optional category for grouping. Customize the field names with value-key, label-key, description-key, icon-key, category-key.
Multiple
Set the multiple prop to switch to multi-select mode. The v-model binds to an array of values.
<B24PageCardGroup v-model="picks" :items="items" multiple />
Columns
Use the columns prop ('1', '2', '3', '4') to set the maximum number of columns on desktop. The grid is always responsive — 1 column on mobile, 2 on tablet, then up to columns on lg+.
Size
Use the size prop ('sm', 'md', 'lg') to scale the icon circle, the inner icon, the inner gap, and the title/description font sizes. Defaults to 'md'. The corner badge size is derived from size unless badge-size is set explicitly.
Variant
The variant prop is forwarded to each inner PageCard and controls its base style. See PageCard variants for the full list.
Color
The color prop is the umbrella accent — it drives both the highlighted border on the selected card and the corner badge color. Defaults to air-primary-success.
<B24PageCardGroup v-model="value" :items="items" color="air-primary" />
Highlight color
Override the umbrella color for the card border only with highlight-color. Falls back to color when not set.
Badge color
Override the umbrella color for the corner Badge only with badge-color. Falls back to color when not set.
Badge size
Override the badge size derived from size with badge-size.
Icon vs Avatar
Each item picks one of two leading visuals:
iconfield — plain icon, no circle. Theme controls size vialeadingIcon. Use this for compact, non-decorated lists.avatarfield — B24Avatar with full color control (color,src,chip,icon). Avatar size is derived from the groupsize(sm→lg,md→xl,lg→2xl) and the avatar'saltfalls back to the item'slabel.
icon wins when both are set on the same item — the plain icon renders and avatar is ignored. Pick one per item:
// shows the icon via B24Avatar (color circle around the icon)
{ avatar: { color: 'air-primary-warning', icon: FeedbackIcon }, label: 'Feedback' }
// shows the plain icon only — `icon` at the top level takes precedence
{ icon: FeedbackIcon, avatar: { color: 'air-primary-warning' }, label: 'Feedback' }
Set a group-level Avatar default that every avatar-mode item inherits:
<B24PageCardGroup v-model="value" :items="items" :avatar="{ color: 'air-secondary' }" />
Per-item avatar config merges on top of the group default:
The avatar field accepts the full B24Avatar API — same hook drives src (photo), chip, or a different icon per item.
Category grouping
When items contain a category field (or another field set via category-key), the group renders one section per category with a heading above each grid. Pass category-key="" to disable grouping.
Prompt
Render a payment-methods picker on the settings page.
On a settings page, render the list of payment methods as a grid of cards. Each card carries an icon and a short description. The user can enable several methods at once.
Requirements:
- Use
B24PageCardGroupwithmultipleand bindv-modelto aref<string[]> - Each item exposes
value,label,descriptionandicon(an icon component imported from@bitrix24/b24icons-vue) - Keep the umbrella accent in sync — set
coloron the group instead ofhighlight-color/badge-colorseparately - Wrap the group in
B24FormFieldwith a label so it integrates with the surrounding settings form - Pick a sensible
size(mdby default) andcolumns(2or3) for the layout
Examples
Click / change handler
Listen to the change event to react to user selections. The event fires after v-model updates and receives the native change event.
<script setup lang="ts">
const value = ref('callback')
function onChange(event: Event) {
console.log('selected', value.value, event)
}
</script>
<template>
<B24PageCardGroup v-model="value" :items="items" @change="onChange" />
</template>
Disable an item
Set disabled: true on an individual item to lock it out without removing it from the grid.
const items = [
{ value: 'callback', label: 'Callback', icon: PhoneIcon },
{ value: 'pay', label: 'Payment', icon: CardIcon, disabled: true }
]
Custom badge
Override the corner badge through the badge slot. Receives { item, selected }.
<B24PageCardGroup v-model="value" :items="items">
<template #badge="{ selected }">
<B24Badge v-if="selected" label="Picked" color="air-primary-success" size="sm" />
</template>
</B24PageCardGroup>
Custom leading icon
The leading slot is an escape hatch for cases where the built-in B24Avatar (driven by the avatar prop / item avatar field) isn't flexible enough. Override the icon circle through the leading slot — receives { item, selected }.
<B24PageCardGroup v-model="value" :items="items">
<template #leading="{ item, selected }">
<B24Avatar
:alt="item.label"
:icon="item.icon"
:color="selected ? 'air-primary' : 'air-secondary-no-accent'"
/>
</template>
</B24PageCardGroup>
Custom category label
Override category headings through the categoryLabel slot. Receives { category, items }.
<B24PageCardGroup v-model="value" :items="items">
<template #categoryLabel="{ category, items }">
<h3 class="text-(length:--ui-font-size-lg) font-(--ui-font-weight-semi-bold)">
{{ category }} <span class="text-(--ui-color-design-outline-content-secondary)">({{ items.length }})</span>
</h3>
</template>
</B24PageCardGroup>
API
Props
Slots
Theme
export default {
slots: {
root: 'flex flex-col gap-8',
fieldset: 'flex flex-col gap-8 border-0 p-0 m-0 min-w-0',
legend: 'text-(length:--ui-font-size-2xl)/[normal] font-(--ui-font-weight-semi-bold) mb-2',
group: 'flex flex-col gap-4',
categoryLabel: 'text-(length:--ui-font-size-base)/[normal] font-(--ui-font-weight-medium) text-(--ui-color-design-outline-content-secondary)',
grid: 'grid gap-4',
item: 'relative flex cursor-pointer transition-shadow duration-200 rounded-lg [&:not([data-disabled])]:hover:shadow-(--ui-shadow-bottom-l) data-[disabled]:cursor-not-allowed',
card: 'flex-1',
cardContainer: '',
cardWrapper: 'flex flex-row items-center',
cardLeading: 'mb-0 shrink-0',
cardBody: 'flex-1 min-w-0',
cardTitle: '',
cardDescription: '',
leadingIcon: '',
leadingAvatar: '',
leadingAvatarSize: '',
badge: 'absolute -top-2 -end-2 z-10',
badgeIcon: 'rounded-full'
},
variants: {
size: {
sm: {
cardWrapper: 'gap-2.5',
cardTitle: 'text-(length:--ui-font-size-base)/[normal]',
cardDescription: 'text-(length:--ui-font-size-sm)/[normal]',
leadingIcon: 'size-7 shrink-0',
leadingAvatarSize: 'sm'
},
md: {
cardWrapper: 'gap-3',
cardTitle: 'text-(length:--ui-font-size-lg)/[normal]',
cardDescription: 'text-(length:--ui-font-size-base)/[normal]',
leadingIcon: 'size-8 shrink-0',
leadingAvatarSize: 'md'
},
lg: {
cardWrapper: 'gap-4',
cardTitle: 'text-(length:--ui-font-size-xl)/[normal]',
cardDescription: 'text-(length:--ui-font-size-base)/[normal]',
leadingIcon: 'size-10 shrink-0',
leadingAvatarSize: 'lg'
}
},
columns: {
'1': {
grid: 'grid-cols-1'
},
'2': {
grid: 'grid-cols-1 sm:grid-cols-2'
},
'3': {
grid: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3'
},
'4': {
grid: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4'
}
},
disabled: {
true: {
root: 'opacity-30 pointer-events-none'
}
}
},
defaultVariants: {
size: 'md',
columns: '3'
}
}