v2.7.1

PageCardGroup

A selectable group of PageCard items. Single or multi-select with optional grouping by category.

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.

Customer communications
Sales
<script setup lang="ts">
import TelephonyHandset1Icon from '@bitrix24/b24icons-vue/main/TelephonyHandset1Icon'
import MailIcon from '@bitrix24/b24icons-vue/main/MailIcon'
import FeedbackIcon from '@bitrix24/b24icons-vue/main/FeedbackIcon'
import DocumentStreamIcon from '@bitrix24/b24icons-vue/main/DocumentStreamIcon'
import CartWithCursorIcon from '@bitrix24/b24icons-vue/main/CartWithCursorIcon'
import CreditDebitCardIcon from '@bitrix24/b24icons-vue/main/CreditDebitCardIcon'
import PictureIcon from '@bitrix24/b24icons-vue/main/PictureIcon'

const items = [
  { value: 'callback', category: 'Customer communications', icon: TelephonyHandset1Icon, label: 'Callback', description: 'Capture the phone number and start a callback' },
  { value: 'contacts', category: 'Customer communications', icon: MailIcon, label: 'Contact details', description: 'Collect phone, email and client name' },
  { value: 'feedback', category: 'Customer communications', icon: FeedbackIcon, label: 'Feedback', description: 'Gather customer reviews' },
  { value: 'multi', category: 'Customer communications', icon: DocumentStreamIcon, label: 'Multi-page form', description: 'Split a long form across several pages' },
  { value: 'no-image', category: 'Sales', icon: CartWithCursorIcon, label: 'Products without images', description: 'Order a product without a picture' },
  { value: 'pay', category: 'Sales', icon: CreditDebitCardIcon, label: 'Products with payment', description: 'Pay for a product right from the form' },
  { value: 'small-img', category: 'Sales', icon: PictureIcon, label: 'Products with small photo', description: 'Order a product with a small thumbnail preview' }
]

const value = ref('contacts')
</script>

<template>
  <B24PageCardGroup v-model="value" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TelephonyHandset1Icon from '@bitrix24/b24icons-vue/main/TelephonyHandset1Icon'
import MailIcon from '@bitrix24/b24icons-vue/main/MailIcon'
import FeedbackIcon from '@bitrix24/b24icons-vue/main/FeedbackIcon'
import DocumentStreamIcon from '@bitrix24/b24icons-vue/main/DocumentStreamIcon'
import CartWithCursorIcon from '@bitrix24/b24icons-vue/main/CartWithCursorIcon'
import CreditDebitCardIcon from '@bitrix24/b24icons-vue/main/CreditDebitCardIcon'
import PictureIcon from '@bitrix24/b24icons-vue/main/PictureIcon'

const items = [
  { value: 'callback', category: 'Customer communications', icon: TelephonyHandset1Icon, label: 'Callback', description: 'Capture the phone number and start a callback' },
  { value: 'contacts', category: 'Customer communications', icon: MailIcon, label: 'Contact details', description: 'Collect phone, email and client name' },
  { value: 'feedback', category: 'Customer communications', icon: FeedbackIcon, label: 'Feedback', description: 'Gather customer reviews' },
  { value: 'multi', category: 'Customer communications', icon: DocumentStreamIcon, label: 'Multi-page form', description: 'Split a long form across several pages' },
  { value: 'no-image', category: 'Sales', icon: CartWithCursorIcon, label: 'Products without images', description: 'Order a product without a picture' },
  { value: 'pay', category: 'Sales', icon: CreditDebitCardIcon, label: 'Products with payment', description: 'Pay for a product right from the form' },
  { value: 'small-img', category: 'Sales', icon: PictureIcon, label: 'Products with small photo', description: 'Order a product with a small thumbnail preview' }
]

const value = ref('contacts')
</script>

<template>
  <B24PageCardGroup v-model="value" :items="items" />
</template>
Use 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:

  • icon field — plain icon, no circle. Theme controls size via leadingIcon. Use this for compact, non-decorated lists.
  • avatar field — B24Avatar with full color control (color, src, chip, icon). Avatar size is derived from the group size (smlg, mdxl, lg2xl) and the avatar's alt falls back to the item's label.

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:

<script setup lang="ts">
import TelephonyHandset1Icon from '@bitrix24/b24icons-vue/main/TelephonyHandset1Icon'
import MailIcon from '@bitrix24/b24icons-vue/main/MailIcon'
import FeedbackIcon from '@bitrix24/b24icons-vue/main/FeedbackIcon'
import type { PageCardGroupItem } from '@bitrix24/b24ui-nuxt'

const items: PageCardGroupItem[] = [
  {
    value: 'callback',
    avatar: { color: 'air-primary-success', icon: TelephonyHandset1Icon },
    label: 'Callback',
    description: 'Capture the phone number and start a callback'
  },
  {
    value: 'contacts',
    avatar: { color: 'air-primary-copilot', icon: MailIcon },
    label: 'Contact details',
    description: 'Collect phone, email and client name'
  },
  {
    value: 'feedback',
    avatar: { color: 'air-primary-warning', icon: FeedbackIcon },
    label: 'Feedback',
    description: 'Gather customer reviews'
  }
]

const value = ref('contacts')
</script>

<template>
  <B24PageCardGroup
    v-model="value"
    :items="items"
    color="air-primary-copilot"
    columns="3"
    category-key=""
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TelephonyHandset1Icon from '@bitrix24/b24icons-vue/main/TelephonyHandset1Icon'
import MailIcon from '@bitrix24/b24icons-vue/main/MailIcon'
import FeedbackIcon from '@bitrix24/b24icons-vue/main/FeedbackIcon'
import type { PageCardGroupItem } from '@bitrix24/b24ui-nuxt'

const items: PageCardGroupItem[] = [
  {
    value: 'callback',
    avatar: { color: 'air-primary-success', icon: TelephonyHandset1Icon },
    label: 'Callback',
    description: 'Capture the phone number and start a callback'
  },
  {
    value: 'contacts',
    avatar: { color: 'air-primary-copilot', icon: MailIcon },
    label: 'Contact details',
    description: 'Collect phone, email and client name'
  },
  {
    value: 'feedback',
    avatar: { color: 'air-primary-warning', icon: FeedbackIcon },
    label: 'Feedback',
    description: 'Gather customer reviews'
  }
]

const value = ref('contacts')
</script>

<template>
  <B24PageCardGroup
    v-model="value"
    :items="items"
    color="air-primary-copilot"
    columns="3"
    category-key=""
  />
</template>

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.

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

Prop Default Type
as'div'any

The element or component this component should render as.

legend string
items PageCardGroupItem[]
modelValue null | string | number | bigint | false | true | AcceptableValue[]

The controlled value. Can be bound with v-model. Single value when multiple is false, array otherwise.

defaultValue null | string | number | bigint | false | true | AcceptableValue[]

The initial value when uncontrolled.

valueKey'value' string
labelKey'label' string
descriptionKey'description' string
iconKey'icon'IconComponent
categoryKey'category' string

Item field used to group cards into sections. Set to an empty string to disable grouping.

multiplefalseboolean

Switch to multi-select mode (checkbox semantics, value is an array).

disabledboolean
requiredboolean
name string
size'md' "md" | "sm" | "lg"

Card size — drives the inner avatar size, inner gap and title/description text size.

variant'outline' "outline" | "filled" | "filled-success" | "filled-alert" | "filled-warning" | "filled-copilot" | "filled-no-accent" | "filled-black" | "tinted" | "tinted-alt" | "tinted-accent-1" | "tinted-success" | "tinted-alert" | "tinted-warning" | "tinted-no-accent" | "outline-alt" | "outline-accent" | "outline-accent-2" | "outline-success" | "outline-alert" | "outline-warning" | "outline-copilot" | "outline-no-accent" | "plain" | "plain-accent" | "plain-no-accent" | "selection"

Variant forwarded to each PageCard.

color"'air-primary-success'""air-primary" | "air-primary-success" | "air-primary-alert" | "air-primary-warning" | "air-primary-copilot"

Umbrella accent color. Drives both the highlighted card border and the corner badge unless highlightColor / badgeColor are set explicitly.

highlightColor"air-primary" | "air-primary-success" | "air-primary-alert" | "air-primary-warning" | "air-primary-copilot"

Highlight color forwarded to the selected PageCard. Falls back to color when not set.

badgeColor"air-primary" | "air-primary-success" | "air-primary-alert" | "air-primary-warning" | "air-primary-copilot" | "air-secondary" | "air-secondary-alert" | "air-secondary-accent" | "air-secondary-accent-1" | "air-secondary-accent-2" | "air-tertiary" | "air-selection"

Color of the corner badge shown on the selected card. Falls back to color when not set.

badgeSize "md" | "sm" | "lg" | "xl" | "xss" | "xs"

Size of the corner badge shown on the selected card. Falls back to a value derived from the group size when not set.

avatar Partial<AvatarProps>

Group-level Avatar defaults forwarded to every card whose item opts into avatar mode (item.avatar set or this prop set). Per-item avatar field merges on top.

columns3 "3" | "1" | "2" | "4"

Max columns on desktop.

b24ui { root?: ClassNameValue; fieldset?: ClassNameValue; legend?: ClassNameValue; group?: ClassNameValue; categoryLabel?: ClassNameValue; grid?: ClassNameValue; item?: ClassNameValue; card?: ClassNameValue; cardContainer?: ClassNameValue; cardWrapper?: ClassNameValue; cardLeading?: ClassNameValue; cardBody?: ClassNameValue; cardTitle?: ClassNameValue; cardDescription?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; badge?: ClassNameValue; badgeIcon?: ClassNameValue; }

Slots

Slot Type
legend{ b24ui: object; }
categoryLabel{ category: string; items: PageCardGroupItem[]; b24ui: object; }
leading{ item: PageCardGroupItem; selected: boolean; b24ui: object; }
badge{ item: PageCardGroupItem; selected: boolean; b24ui: object; }

Theme

https://github.com/bitrix24/b24ui/tree/main/src/theme/page-card-group.ts
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'
  }
}