Template Components

Bento Card

A visually-rich feature showcase card. The Bento Card renders a tiered information block with an icon, badge, title, hero metric, feature list, and call-to-action. Ideal for plan comparisons, service tiers, product offerings, and KPI summaries.

A Bento Card (bento_card) is a self-contained presentation card designed for side-by-side tier comparisons and feature showcases. It combines four optional zones into a single cohesive block:

  1. Header - icon composite tile, badge, title, and subtitle
  2. Hero - large metric with period and description (for example, price, score, or capacity)
  3. Feature list - icon rows with optional labels and values
  4. CTA - one or two action labels

Four pre-built variants (light, standard, premium, dark) automatically adapt colors, backgrounds, and accent tones to the active theme. Every color property can be overridden from the payload at runtime.

Bento Cards are designed to sit inside a Layout Group for side-by-side column arrangements. When placed inside a Layout Group, borders and backgrounds are promoted to the parent cell so all columns share equal heights.


Variants

VariantBackgroundAccentBest for
lighttheme.headerShade (light grey tint)theme.primaryEntry-level or free tiers
standard#FFFFFF (white)theme.primaryMid-range plans, default
premiumSoft tint of theme.secondarytheme.secondaryFeatured or recommended tiers
dark#1F2937 (slate)#FBBF24 (amber)Enterprise, top-tier, highlight

The premium variant automatically computes its background by blending theme.secondary at 25% opacity onto white. For a blue primary or indigo secondary theme, this produces a soft lavender (approximately #D8D9FC). The icon tile uses theme.primary at 25% opacity to ensure hue contrast against the blended background.

The variant can be set statically on the node or driven from the data payload. See Payload-driven overrides.


Top-level Properties

PropertyTypeDefaultDescription
variant"light" | "standard" | "premium" | "dark""standard"Visual treatment. Can be overridden at runtime via _repeatItem.variant.
titlestring"BentoCard"Main card heading. Supports {{FIELDNAME}} tokens.
subtitlestring-Optional secondary line rendered beneath the title in muted grey.
iconstring-Icon key from the icon registry (for example, "star", "shield", "package", "user"). A key that is not in the registry resolves to no icon and is dropped silently rather than raising an error - check the registry before using a key.
badgestring-Short label rendered above the title in accent color (for example, "BEST VALUE", "POPULAR"). Supports {{FIELDNAME}} tokens.
backgroundColorcolor(variant default)Override the card background. Accepts any hex color or theme token.
textColorcolor(variant default)Override the main text color for title, period, and feature labels.
accentColorcolor(variant default)Override the accent color used for badge, hero value, feature icons, and CTA.
iconBackgroundColorcolor(variant default)Override the icon tile background fill. Accepts full hex (#RRGGBB) or 8-digit hex (#RRGGBBAA) for opacity.

Hero Section

The hero section displays a large primary metric - typically a price, score, or capacity - with optional period and description text below it.

Add it by setting the heroSection property object on the card.

PropertyTypeDefaultDescription
heroSection.valuestring-The main hero metric (for example, "$299", "{{price}}", "99.9%"). Rendered at 28pt bold in accent color. Supports {{FIELDNAME}} tokens.
heroSection.periodstring-Unit or frequency label below the hero value (for example, "/ month", "per user"). Rendered at 14pt in text color.
heroSection.descriptionstring-Supporting text below the period (for example, "billed annually", "includes tax"). Rendered at 11pt in muted grey.
heroSection.hasSeparatorbooleantrueWhen true, draws a hairline separator between the header and hero sections at 85% card width. Set to false to suppress the divider.

Features

The features array renders a vertical list of icon and label rows below the hero section. Each feature shows a check icon by default, with an optional custom icon, secondary value, and highlight treatment.

PropertyTypeDefaultDescription
featuresFeature[][]Array of feature objects. Can also be a {{path}} token resolving to an array at runtime.

Feature object

PropertyTypeDefaultDescription
labelstring-Feature name or description. Supports {{FIELDNAME}} tokens.
valuestring-Optional secondary value rendered below the label in a smaller font (for example, "Up to 50 users", "{{storageLimit}} GB").
iconstring-Override the row icon. Defaults to "check" when omitted.
colorcolor(accent color)Override the icon and value color for this row.
highlightedbooleanfalseWhen true, renders the label in accent color and bold. Useful for marking key differentiators.

Zebra striping

When there are more than 5 features and the card has a light background, alternate rows automatically receive a subtle #F8FAFC background tint. This improves scannability on long feature lists without requiring any configuration.


CTA Section

The cta object renders one or two call-to-action labels at the bottom of the card, centered, in accent color.

PropertyTypeDefaultDescription
cta.primarystring-Primary action label (for example, "Get Started", "{{ctaLabel}}").
cta.secondarystring-Secondary action label separated from primary by `

Payload-driven Overrides

Every visual property - including the variant itself - can be driven entirely from the JSON data payload. This is the recommended approach for generating tier-comparison grids from a CRM or product catalogue.

When the card is used inside a _repeat context, two props read from the repeat item directly, ahead of the static prop of the same name:

  • _repeatItem.variant - sets the card variant per item
  • _repeatItem.features - used as the feature list whenever the current repeat item has a features array, regardless of what the static features prop is set to

title, subtitle, badge, heroSection and cta are not read from _repeatItem, and {{token}} strings in these props are not rewritten to the per-item repeat context the way card field values are (see Card). Inside a _repeat loop, a "title": "{{name}}" binding resolves against the full payload root, not the current item, so it will not vary per card. To vary these fields per item, generate one bento_card node per item in the payload builder instead of relying on _repeat for anything other than variant and features.

You can also pass a {{path}} binding for the features property to resolve a nested array from any fixed path in the payload when the node is not inside a _repeat loop:

"features": "{{plans.professional.features}}"

Usage in a Layout Group

Bento Cards are designed to be placed as direct children of a Layout Group. The Layout Group assigns equal column widths and the cards automatically delegate their border and background to the parent cell, ensuring all cards in a row share the same height regardless of content length.

{
  "type": "layout_group",
  "props": { "columns": [{"width": "star"}, {"width": "star"}, {"width": "star"}], "columnGap": 12 },
  "children": [
    { "type": "bento_card", "props": { "variant": "light", ... } },
    { "type": "bento_card", "props": { "variant": "standard", ... } },
    { "type": "bento_card", "props": { "variant": "premium", ... } }
  ]
}

Examples

Simple Static Card

A minimal card with a title, icon, and feature list - no hero section, no data binding.

{
  "type": "bento_card",
  "props": {
    "variant": "standard",
    "title": "Starter",
    "subtitle": "For individuals and small teams",
    "icon": "package",
    "features": [
      { "label": "5 active projects" },
      { "label": "2 GB storage" },
      { "label": "Email support" }
    ]
  }
}

Card with Hero Pricing

A standard pricing card with badge, hero price, period, and CTA.

{
  "type": "bento_card",
  "props": {
    "variant": "premium",
    "title": "Professional",
    "subtitle": "For growing teams",
    "icon": "shield",
    "badge": "MOST POPULAR",
    "heroSection": {
      "value": "$149",
      "period": "/ month",
      "description": "billed annually, per workspace",
      "hasSeparator": true
    },
    "features": [
      { "label": "Unlimited projects" },
      { "label": "50 GB storage" },
      { "label": "Priority support", "highlighted": true },
      { "label": "SSO & SCIM provisioning" },
      { "label": "Audit logs (90 days)" }
    ],
    "cta": {
      "primary": "Start Free Trial",
      "secondary": "Talk to Sales"
    }
  }
}

Full Tier Comparison Grid (Static)

A 4-column grid using all four variants, demonstrating a complete service tier comparison.

{
  "type": "layout_group",
  "props": {
    "columns": [
      {"width": "star"}, {"width": "star"}, {"width": "star"}, {"width": "star"}
    ],
    "columnGap": 10
  },
  "children": [
    {
      "type": "bento_card",
      "props": {
        "variant": "light",
        "title": "Starter",
        "subtitle": "Individuals & freelancers",
        "icon": "package",
        "heroSection": { "value": "Free", "period": "forever" },
        "features": [
          { "label": "3 active projects" },
          { "label": "1 GB storage" },
          { "label": "Community support" }
        ],
        "cta": { "primary": "Sign Up Free" }
      }
    },
    {
      "type": "bento_card",
      "props": {
        "variant": "standard",
        "title": "Team",
        "subtitle": "Growing companies",
        "icon": "user",
        "heroSection": { "value": "$49", "period": "/ month", "description": "up to 10 seats" },
        "features": [
          { "label": "20 active projects" },
          { "label": "25 GB storage" },
          { "label": "Email & chat support" },
          { "label": "Role-based access control" }
        ],
        "cta": { "primary": "Start Trial" }
      }
    },
    {
      "type": "bento_card",
      "props": {
        "variant": "premium",
        "title": "Business",
        "subtitle": "Scaling organizations",
        "icon": "star",
        "badge": "BEST VALUE",
        "heroSection": { "value": "$149", "period": "/ month", "description": "billed annually" },
        "features": [
          { "label": "Unlimited projects" },
          { "label": "200 GB storage" },
          { "label": "Priority support", "highlighted": true },
          { "label": "SSO & SCIM" },
          { "label": "Audit logs (1 year)" },
          { "label": "Custom integrations" }
        ],
        "cta": { "primary": "Get Started", "secondary": "Schedule Demo" }
      }
    },
    {
      "type": "bento_card",
      "props": {
        "variant": "dark",
        "title": "Enterprise",
        "subtitle": "Dedicated infrastructure",
        "icon": "shield",
        "heroSection": { "value": "Custom", "period": "per agreement", "description": "volume pricing available" },
        "features": [
          { "label": "Unlimited everything" },
          { "label": "Dedicated cloud region" },
          { "label": "24/7 SLA support", "highlighted": true },
          { "label": "MSA & DPA included" },
          { "label": "On-premise option" },
          { "label": "Dedicated CSM" }
        ],
        "cta": { "primary": "Contact Sales" }
      }
    }
  ]
}

Payload-driven Cards (Repeat Context)

_repeat must be inside props, not a sibling of type and children - the renderer only reads node.props._repeat. Within a repeated bento_card, only variant and features are payload-driven per item; title, subtitle, icon, badge, heroSection and cta stay fixed across every clone because they are not rewritten to the per-item context (see above). This example uses _repeat for what it actually varies - the feature list and variant - and keeps the rest static:

Template node:

{
  "type": "layout_group",
  "props": {
    "columns": [{"width": "star"}, {"width": "star"}, {"width": "star"}],
    "columnGap": 12,
    "_repeat": "plans"
  },
  "children": [
    {
      "type": "bento_card",
      "props": {
        "title": "Plan",
        "icon": "package",
        "features": []
      }
    }
  ]
}

Example payload:

{
  "plans": [
    {
      "variant": "light",
      "features": [
        { "label": "3 active matters" },
        { "label": "Document vault (500 MB)" },
        { "label": "Email delivery" }
      ]
    },
    {
      "variant": "premium",
      "features": [
        { "label": "Unlimited matters" },
        { "label": "Document vault (10 GB)" },
        { "label": "Priority support", "highlighted": true },
        { "label": "E-signature included" },
        { "label": "Client portal access" }
      ]
    },
    {
      "variant": "dark",
      "features": [
        { "label": "Unlimited everything" },
        { "label": "Dedicated storage region", "highlighted": true },
        { "label": "On-premise deployment" }
      ]
    }
  ]
}

For plan names, prices and CTAs that differ per tier, build one bento_card node per plan in the payload builder instead of relying on _repeat - see the static "Full Tier Comparison Grid" example above.


Per-card Color Override

All four color properties can be set explicitly to override variant defaults - useful for branded one-off cards or when the theme colors do not match the intended hierarchy.

{
  "type": "bento_card",
  "props": {
    "variant": "standard",
    "title": "Regulated Markets Package",
    "subtitle": "Financial services add-on",
    "icon": "shield",
    "badge": "REGULATED MARKETS",
    "backgroundColor": "#FFF7ED",
    "textColor": "#1C1917",
    "accentColor": "#EA580C",
    "iconBackgroundColor": "#EA580C33",
    "heroSection": {
      "value": "$1,200",
      "period": "/ year per entity",
      "description": "includes annual review"
    },
    "features": [
      { "label": "Financial reporting templates", "highlighted": true },
      { "label": "Append-only audit trail (7 years)" },
      { "label": "SEC filing export" },
      { "label": "Dedicated compliance officer" }
    ],
    "cta": { "primary": "Request Proposal" }
  }
}

KPI Summary Grid

Bento Cards are not limited to pricing. Use the hero section to show any large metric (throughput, SLA scores, error rates, etc.) and the feature list to show supporting breakdowns.

{
  "type": "layout_group",
  "props": {
    "columns": [{"width": "star"}, {"width": "star"}, {"width": "star"}],
    "columnGap": 10
  },
  "children": [
    {
      "type": "bento_card",
      "props": {
        "variant": "light",
        "title": "API Gateway",
        "subtitle": "Last 30 days",
        "icon": "integration",
        "heroSection": { "value": "99.98%", "period": "uptime", "hasSeparator": true },
        "features": [
          { "label": "Avg latency", "value": "42 ms" },
          { "label": "Requests", "value": "14.2 M" },
          { "label": "Error rate", "value": "0.02%", "highlighted": true }
        ]
      }
    },
    {
      "type": "bento_card",
      "props": {
        "variant": "standard",
        "title": "Data Pipeline",
        "subtitle": "Last 30 days",
        "icon": "module",
        "heroSection": { "value": "2.4 TB", "period": "processed", "hasSeparator": true },
        "features": [
          { "label": "Jobs completed", "value": "8,410" },
          { "label": "Failed jobs", "value": "3" },
          { "label": "Avg processing time", "value": "1.8 s" }
        ]
      }
    },
    {
      "type": "bento_card",
      "props": {
        "variant": "dark",
        "title": "Security Events",
        "subtitle": "Last 30 days",
        "icon": "shield",
        "heroSection": { "value": "0", "period": "critical alerts", "hasSeparator": true },
        "features": [
          { "label": "Auth attempts blocked", "value": "127" },
          { "label": "Policy violations", "value": "4", "highlighted": true },
          { "label": "Scans completed", "value": "360" }
        ]
      }
    }
  ]
}

Color Contrast Guard

The renderer automatically checks whether the accent color is readable on the card background using a luminance-ratio formula. If the ratio falls below 1.5 (for example, a very light accent on a light background), the accent is automatically swapped for theme.textOnShade to preserve legibility. This affects the badge, hero value, feature icons, highlighted feature labels, and CTA text.

The guard applies to whichever accent color is in effect, including one set explicitly via accentColor in the node props or payload - there is no way to bypass it. If your chosen accent renders as theme.textOnShade instead of the color you set, the contrast ratio against the card background fell below 1.5.


Best Use Cases

Use CaseRecommended VariantNotes
Pricing or plan comparisonAll four in a gridUse premium for the featured plan, dark for enterprise
Product feature highlightslight or standardHero value for a key spec; features for capability list
Service tier proposalspremium and dark side by sideBadge on the recommended tier
KPI dashboards in reportslight / standard / dark mixHero value for the headline metric; features for breakdowns
Quote line summariesstandardHero for total price; features for line items
Compliance or certification blocksCustom color overrideAmber or orange accent for regulated or flagged status

Bento Cards produce fixed-height PDF blocks. Very long feature lists (10 or more items) will make the card tall. Consider splitting into two cards side-by-side or limiting the feature list to the 6 - 8 most important items for clean visual balance.

On this page