Sakuga Back to Sakuga
Theme
Sound
Jump to a component

AniCanonKit · AniCanon™

SpendTiles

OptionTiles whose large cards carry fragments: an option's amount (what a pack or plan gives, the card's headline) and cost (what choosing it charges, under its description), each with the fragment mark. It takes every OptionTiles prop, with amount and cost as strings. The core OptionTiles knows nothing of fragments; its amount and cost are neutral, and this is the part that fills them.

@anicanon/sakuga/anicanonkitv1.0Used onWeb · iOS · AndroidFrom AniCanon™

Live, from @anicanon/sakuga 2.0.0

Sakuga for iOS and Android is coming

iOS

Coming soon

SpendTiles for iOS is in the roughs. The tokens it will read are the ones on this page, the same on every platform.

Android

Coming soon

SpendTiles for Android is in the roughs. The tokens it will read are the ones on this page, the same on every platform.

Use it for
A choice whose cards carry fragments: the Fragments packs and plans, and the first choice of a flow when each way costs something different.
Not for

Guidelines

  • cost is what an option charges, never what it gives; a pack's or plan's fragments are its amount.
  • Put the money price in description ("$24.99 · never expires") and on the action ("Buy Bloom · $24.99"), never in amount.
SpendTilesExample.jsxReact
import { useState } from 'react';
import { SpendTiles } from '@anicanon/sakuga/anicanonkit';

function Demo() {
  const [value, setValue] = useState('bloom');
  return (
    <SpendTiles
      size="lg"
      columns={3}
      label="Fragments packs"
      value={value}
      onChange={setValue}
      options={[
        {
          value: 'spark',
          label: 'Spark',
          amount: '9,990',
          description: '$9.99 · never expires',
        },
        {
          value: 'bloom',
          label: 'Bloom',
          amount: '24,990',
          description: '$24.99 · never expires',
        },
        {
          value: 'cascade',
          label: 'Cascade',
          amount: '99,990',
          description: '$99.99 · never expires',
        },
      ]}
    />
  );
}

export default function SpendTilesExample() {
  return <Demo />;
}

PropsSpendTilesProps

optionsrequired
(Omit<TileOption, 'amount' | 'cost'> & { /** The fragments it gives, e.g. "24,990": the card's headline. */ amount?: string; /** What choosing it charges, e.g. "~10 a second". */ cost?: string; })[]

From OptionTilesProps

valuerequired
string | string[]
className
string
size
'lg'
lg: choice cards with description, footnote and badge.
label
string
id
string
multiple
boolean
columns
number
iconSize
number
note
string
help
{ title: string; text: ReactNode; }
helpOpen
boolean
Shows the help's tip open (a board).
ariaLabel
string
Names the group when there is no label.
onChange
(value: string) => void

Types

interface TileOption {
    value: string;
    label: string;
    icon?: IconName;
    description?: string;
    /** Large tiles: the thing's own art on a stage above its name (a Fragments pack or plan). */
    image?: string;
    /** Large tiles: what it gives, set as the headline, e.g. "24,990"; as a ButtonTrailing it takes a mark before it and words read after it. Fragments: AniCanonKit's SpendTiles. */
    amount?: string | ButtonTrailing;
    /** Large tiles: what choosing it charges, under the description, e.g. "~10 a second"; the same shape as amount. */
    cost?: string | ButtonTrailing;
    /** Shown dimmed and cannot be chosen (a plan not open yet). */
    disabled?: boolean;
    badge?: string;
    footnote?: string;
}
SpendTilesExample.swiftiOS · coming soon
SpendTilesExample.ktAndroid · coming soon

It has no tokens of its own: it draws with OptionTiles’s, listed here.

Each token reads a role, and the role lands on a value in each theme. Change the role and every component that reads it follows.

Colour

option-tile-bgOptionTiles: idle tile.
bg-fill-subtleshade-a06rgba(11,10,16,0.06)
bg-fill-subtlewhite-a08rgba(255,255,255,0.08)
option-tile-fgOptionTiles: idle icon and name.
text-secondaryneutral-800#2e2d36
text-secondaryneutral-200#dcdcde
option-tile-hover-bgOptionTiles: hovered tile.
bg-fillshade-a12rgba(11,10,16,0.12)
bg-fillwhite-a14rgba(255,255,255,0.14)
option-tile-selected-bgOptionTiles: chosen tile wash.
accent-subtlecamellia-600-a10rgba(194,51,95,0.1)
accent-subtlecamellia-400-a14rgba(244,119,159,0.14)
option-tile-selected-borderOptionTiles: chosen tile edge.
accentcamellia-600#c2335f
accentcamellia-400#f4779f
option-tile-selected-fgOptionTiles: chosen tile icon and name.
text-primaryneutral-925#0b0a10
text-primaryneutral-0#ffffff
option-tile-labelOptionTiles: the field name above the grid.
text-mutedneutral-600#5c5b67
text-mutedneutral-400#a3a3a7
option-tile-noteOptionTiles: the line describing the chosen option.
text-mutedneutral-600#5c5b67
text-mutedneutral-400#a3a3a7
option-card-bgOptionTiles large: card.
bg-surfaceneutral-0#ffffff
bg-surfaceneutral-900#111117
option-card-borderOptionTiles large: edge.
border-dividershade-a12rgba(11,10,16,0.12)
border-dividerwhite-a12rgba(255,255,255,0.12)
option-card-selected-bgOptionTiles large: chosen card.
accent-subtlecamellia-600-a10rgba(194,51,95,0.1)
accent-subtlecamellia-400-a14rgba(244,119,159,0.14)
option-card-selected-borderOptionTiles large: chosen edge.
accentcamellia-600#c2335f
accentcamellia-400#f4779f
option-card-iconOptionTiles large: icon.
accentcamellia-600#c2335f
accentcamellia-400#f4779f
option-card-textOptionTiles large: description.
text-secondaryneutral-800#2e2d36
text-secondaryneutral-200#dcdcde
option-card-footOptionTiles large: footnote.
text-mutedneutral-600#5c5b67
text-mutedneutral-400#a3a3a7

Space and shape

option-field-gap
gap-md
8px
option-field-label-gap
gap-sm
6px
Aa
option-tiles-gap
gap-sm
6px
option-tile-gap
gap-sm
6px
option-tile-min-height
its own value
64px
option-tile-padding-y
inset-sm
8px
option-tile-padding-x
inset-xs
4px
option-tile-radius
radius-md
12px
option-tile-active-scale
scale-press
.97
option-tile-active-scale-lg
scale-press-lg
.98
option-tile-pressed-inset-ring-width
line-strong
2px
option-tile-focus-outline-offset
focus-ring-offset
3px
option-tiles-gap-lg
gap-xl
16px
option-tile-gap-lg
gap-md
8px
option-tile-padding-lg
inset-lg
16px
option-tile-radius-lg
radius-lg
16px
option-tile-inset-ring-width-lg
line-hairline
1px
option-tile-hover-inset-ring-width-lg
line-hairline
1px
option-tile-pressed-inset-ring-width-lg
line-strong
2px
option-tile-foot-padding-top-lg
inset-xs
4px
option-tile-top-icon-size
icon-size-lg
24px
option-tile-amount-icon-size
icon-size-lg
24px
option-tile-cost-icon-size
icon-size-xs
14px
option-tile-icon-size
icon-size-md
20px
option-tile-cost-gap
gap-xs
4px
option-tiles-glide-radius
radius-md
12px
option-tiles-glide-inset-ring-width
line-strong
2px
option-tiles-glide-radius-lg
radius-lg
16px
option-tiles-glide-inset-ring-width-lg
line-strong
2px
option-tile-disabled-opacity-lg
opacity-disabled
0.35
option-tiles-tile-top-image-inset-lg
inset-lg
16px
option-tile-art-height
its own value
148px
option-tile-art-margin-bottom
gap-md
8px
option-tile-art-img-pressed-scale-lg
scale-zoom
1.04
option-tile-amount-gap
gap-md
8px
option-tiles-rows-tile-image-column-1-lg
its own value
84px
option-tiles-rows-tile-image-column-gap-lg
gap-xl
16px
option-tiles-rows-tile-image-row-gap-lg
gap-2xs
2px
option-tiles-rows-tile-art-size
its own value
84px
option-tile-forced-edge-width
line-hairline
1px
option-tile-forced-selected-width
line-heavy
3px

Type

option-field-label-font
text-micro
700 12px/16px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
option-field-label-letter-spacing
tracking-caps
.06em
option-tile-font
text-small-strong
600 12px/16px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
option-field-note-font
text-small
500 12px/16px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
option-tile-title-font-lg
text-title-4
700 17px/22px "Funnel Display", "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
option-tile-words-font-lg
text-body
400 14px/20px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
option-tile-foot-font-lg
text-small
500 12px/16px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
option-tile-cost-font
text-small-strong
600 12px/16px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
option-tile-amount-font
text-title-1
700 30px/36px "Funnel Display", "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
option-tile-amount-letter-spacing
its own value
calc(-1 * -.01em)
option-tiles-rows-tile-amount-font-size
text-subtitle-size
24px
Aa
option-tiles-rows-tile-amount-line-height
text-subtitle-line-height
32px

Motion

option-tile-duration
motion-fast
160ms
option-tile-easing
ease-standard
cubic-bezier(0.2, 0, 0, 1)
option-tile-active-duration
motion-instant
100ms
option-tile-art-img-duration
motion-base
240ms
option-tile-art-img-easing
ease-standard
cubic-bezier(0.2, 0, 0, 1)

Checked automatically with axe-core 4.11.4 against WCAG 2.2 A and AA, on the live preview above, in light and dark, on 26 October 2026.

Result
No issues found, in light or in dark.
By design
OptionTiles' own: with one choice the cards are a radio group, one Tab stop that the arrows move through. A screen reader hears each card's name, the amount or cost as "24,990 fragments", the description and the footnote; the mark and art are hidden.
Not tested
The automated check doesn't test keyboard use, focus order or what a screen reader says. The lines above are the design system's own account of them.