Skip to content

SKU

Category
Media / Reward display
Status
WIP (wireframe-fidelity)
Owner
Ofek (variations gated on “Ask Ofek Before Use”)
Number-format author
Natalia Kornejewa

The SKU is a fixed-square container that displays a reward or currency at one of six preset sizes. It is a presentational component, not a control: it does not respond to input on its own. What the SKU shows (the coin, the card, the booster art) is injected through the skuType slot.

SKU anatomy diagram: the 100% variant with numbered callouts for Container, SKU Slot, and Number. Screenshot shown with GrayCast off so the coin art is visible in color.
# Part Required Notes (Figma layer / code slot)
1 Container Yes Fixed-square root. Figma variant Size=*. Renders overflow: clip. No fill, no border by default (the dark surround visible above is Figma page bleed-through, not part of the component).
2 SKU Slot Yes The instance slot the reward art is injected into. Figma layer SKU Slot (node 474:10498 on 100%). Default sample instance is Credit 1 (a Credits coin). Bound to the skuType slot prop.
3 Number No The reward count text. Figma layer 240k (node 474:10500 on 100%). Rendered when showNumber=true. Not present in the 35% variant at all (structural omission, not a prop).

Parts not shown in the diagram (hidden by default in Figma or conditional):

  • GrayCast. A #d9d9d9 fill layer covering the whole container with mix-blend-mode: color, greying the SKU out. Figma layer GrayCast (node 474:10513). Rendered when bW=true (default). Turned off in the screenshots above so the coin art is visible in color.
  • SKU Slot Guide. Absolute-centered positioning frame that holds the art. Not visible: the geometric anchor. Figma layer SKU Slot Guide (node 474:10497 on 100%).
  • SKU Slot outline. Alternate slot layer with a stacked drop-shadow outline treatment. Rendered when outline=true. Figma layer SKU Slot outline (node 474:10499 on 100%).
  • checkmark (“Done 5”). Overlay badge marking a claimed SKU. Rendered when checkmark=true. Baked image with two large box-shadows for the green-glow halo.
  • UnityGuidelines. Hidden guide frame present only in the 100% variant metadata. 51 px corner brackets defining a Unity safe-area / bleed region. Not rendered; a Unity handoff artifact.

The component has no focus ring and is not a focus target (see Behavior and accessibility below).

The one structural axis is Size, with 6 values. Screenshots below show each size with the default Credits art in the slot, no checkmark, no outline, and the “240k” placeholder number. GrayCast (bW=true) is turned off for these renders so the coin art is visible in color. The size variant name carries its intended usage location (e.g. 70% - Big Reward Screen), which is a rare and useful convention.

The tiles in the grid below are not to scale (each tile is the same visual size for easier scanning). For the actual pixel dimensions rendered side by side, see the Sizing section.

SKU at 100% size, 336 x 336 px
100%
336 x 336 px
SKU at 70% size, Big Reward Screen
70% Big Reward Screen
235.2 x 235.2 px
SKU at 56% size, Small Reward Screen
56% SmallReward Screen
188.16 x 188.16 px
SKU at 48% size, Grand Prize
48% GrandPrize
161.28 x 161.28 px
SKU at 40% size, 2nd Hierarchy
40% 2ndHierarchy
134.4 x 134.4 px
SKU at 35% size, Min Running Text
35% MinRunningText
117.6 x 117.6 px

Reading each size variant’s name as its usage recommendation:

Size variant Use when Do not use when
100% Flagship, full-size SKU (a modal hero, a big reward reveal). Placing multiple SKUs side-by-side in a reward screen (use 70%).
70% - Big Reward Screen Rendering SKUs on a Big Reward screen where the SKU is the primary content. Use 56% on the Small Reward screen instead.
56% - SmallReward Screen Rendering SKUs on a Small Reward screen. Use 70% on the Big Reward screen.
48% - GrandPrize Rendering the SKU inside a Grand Prize component. For a 2nd-hierarchy reward (use 40%).
40% - 2ndHierarchy Second-hierarchy rewards on a screen where a 48% or larger owns the primary slot. Running text (use 35%).
35% - MinRunningText The SKU appears in a line of running text (a sentence, a description, a summary). Anywhere a number should appear on the SKU (the 35% variant does not render the number).

Anti-patterns:

  • Using two different sizes for the same visual weight on one screen. The size variants are hierarchical, not decorative.
  • Applying checkmark=true with bW=true unless the semantic is intentionally “claimed + still locked to further interaction” ([TODO: confirm with Ofek]).
  • Using the SKU as a tappable primary control without wrapping it in an accessible button / link. The SKU itself has no focus ring and no keyboard behavior.

The six sizes shown at their actual pixel dimensions, bottom-aligned so the size ladder reads at a glance.

SKU sizes at true pixel scale: 100% (336 x 336 px), 70% (235.2 x 235.2 px), 56% (188.16 x 188.16 px), 48% (161.28 x 161.28 px), 40% (134.4 x 134.4 px), 35% (117.6 x 117.6 px), bottom-aligned.

Measured dimensions per size variant. All values read from Figma on 2026-09-02.

Size variant Container (px) Slot / art frame (px) Number shown? Font size for number
100% 336 x 336 198 x 228 Yes 100px DEBT hardcoded
70% - Big Reward Screen 235.2 x 235.2 138.6 x 159.6 Yes 60px DEBT hardcoded
56% - SmallReward Screen 188.16 x 188.16 110.88 x 127.68 Yes 52px (--subtitle)
48% - GrandPrize 161.28 x 161.28 95.04 x 109.44 Yes 44px (--running-text)
40% - 2ndHierarchy 134.4 x 134.4 79.2 x 91.2 Yes 38px (--timer)
35% - MinRunningText 117.6 x 117.6 69.3 x 79.8 No (structurally omitted) n/a

The state model is expressed through four boolean props, not through named variants. The doc below proposes a semantic mapping. Confirm with Ofek before treating this as final.

Prop Default Layer toggled Proposed semantic
bW true GrayCast bW=true = locked / unavailable · bW=false = available
checkmark false Done 5 badge checkmark=true = claimed
outline false SKU Slot outline (stacked drop-shadow) outline=true = selected
showNumber true Number text showNumber=false = art-only (rarely used)

One prop combination catch: checkmark=true combined with bW=true is legal in Figma but the semantic (claimed + still greyed?) is undefined. Do not use that combination until Ofek confirms.

Motion for the SKU is not documented in Figma yet. This section lays out what the runtime probably has to animate, and what a proper motion spec would need to define. It is deliberately short. Fill it in when the specs arrive; do not invent them here.

State of motion for this component. No motion is defined in Figma on this component. The Figma MCP’s get_motion_context returned zero keyframe nodes for the whole subtree (recursive scan on 2026-09-02). There are no prototype connections on the component set either. The three state props (bW, checkmark, outline) all imply runtime transitions in the live product (locked to available should ungrey; claim should reveal the checkmark; select should turn the outline on) but Figma does not specify HOW those animate.

The three implied transitions:

Trigger Animated part(s) Property Duration Easing Reduced-motion behavior
bW: true to false (unlock, or reveal on claim) GrayCast opacity (fade out), possibly a scale / pop on the underlying art [TODO] [TODO] [TODO] swap instantly, no fade
checkmark: false to true (SKU is claimed) Done 5 badge opacity + transform: scale (pop-in), plus the green halo drawing [TODO] [TODO] [TODO] show instantly, no pop
outline: false to true (SKU is selected) SKU Slot outline (the stacked-shadow layer) filter or opacity (the four drop-shadows fade in) [TODO] [TODO] [TODO] swap instantly

Motion contract owed. To close this section out, the design system needs a motion.duration.state-change token, a motion.easing.state-change token, a single documented reduced-motion rule (instant state swap, no animation, when prefers-reduced-motion: reduce is set), and a short note per transition on what animates and what does NOT (never animate the container size, never animate the number’s font-size, both would be jank).

Number formatting, from Natalia Kornejewa, transcribed verbatim from the reference canvas (Figma section “SKUS Component, Published”, node 468:1037).

Range Format
1 to 999 1999
1,000 to 999,999 1k999k
1,000,000 to 999,000,000 1m999m

Prefix / template:

  • All SKUs except Album Pack: {0} (the number as-is).
  • Album Pack: x{0} (with a leading “x”, e.g. x3).

Other:

  • Language / RTL: [TODO: confirm.] The formatting is numeric so LTR / RTL should be transparent, but the k / m suffixes and the Album Pack x prefix need a localization rule.
  • Truncation / overflow: the number layer is whitespace: nowrap. Very long strings (unlikely given the formatting cap at 3 characters + suffix) will bleed outside the container.
  • Casing: suffixes are lowercase k / m per the reference. No other casing rules.

This is a decorative / presentational component. It does not respond to input on its own. Interaction (tap-to-claim, tap-to-select) is the responsibility of the parent container that composes SKUs into a reward screen, a collection, or a running-text callout.

  • Element / role: div with role="img" and an accessible name. [TODO: confirm with front-end team] whether a semantic figure + figcaption is preferred when the number is shown.
  • ARIA: aria-label composed from skuType name + num + state ("claimed" when checkmark=true, "locked" when bW=true). Concrete pattern [TODO: confirm].
  • Keyboard: not focusable in isolation. When wrapped in a claim / select control, the parent owns focus.
  • Target size: at size=35% the container is 117.6 x 117.6 px. Well above the WCAG 2.2 minimum (24 x 24 CSS px) and above the 44 x 48 px touch target, IF the SKU itself is tappable. When it is not tappable, the min-target rule does not apply.
  • Contrast: number text #f2f2f2 on #313131 grey-cast is approximately 12.6:1, well over 4.5:1. Contrast on unclamped color art is a property of the injected skuType, not of this component.
  • Reduced motion: [TODO: confirm.] See the Motion section for what has to be specified.
  • How to test: [TODO: confirm the test contract with the front-end team.]

The tokens Figma reports for this component at its top level. Untokenized values are flagged as DEBT with the token decision that is missing.

Property Value Token name (Figma) Notes
number text color #f2f2f2 Wireframe Palette/50 Primitive (wireframe). Same “Wireframe Palette” the rest of the WIP components use. Not a semantic token yet.
outline drop-shadow color #313131 Wireframe Palette/900 - Shadows & Meters Primitive (wireframe). Token name carries semantic hint (“Shadows & Meters”) but is still under the primitive palette namespace.
font size (56%) 52px Subtitle Primitive. Numeric token. Not scoped to SKU.
font size (48%) 44px Running text Primitive. Numeric token. Not scoped to SKU.
font size (40%) 38px Timer Primitive. Numeric token. Not scoped to SKU.
font size (100%) 100px none DEBT Hardcoded. Missing token: sku.number.100 (or a shared “display” type token).
font size (70%) 60px none DEBT Hardcoded. Missing token: sku.number.70.
font family Mikado Bold none (raw name) The wireframe font. Should map to a semantic type token, e.g. type.family.display.
checkmark halo shadows two large box-shadows, values scale per size none DEBT Uses raw shadows AND scales per size instead of a shared elevation token.
GrayCast fill #d9d9d9 none DEBT Should be a semantic state.locked or state.disabled color-token.
slot art inset -22.06% / -32.11% / -23.68% / -33.03% none Intentional bleed (the coin extends past the slot). Not a token, but consistent across sizes as a percentage.
  1. Wireframe fidelity. The component sits on the “Wireframe Palette” (#f2f2f2, #313131) and uses raw type names (Timer, Subtitle, Running text) instead of semantic tokens. The whole component needs a promotion pass into the real palette + type system.
  2. Two hardcoded font sizes. 100px (100%) and 60px (70%) are not tokenized. Missing tokens or a shared “display” type ramp.
  3. showNumber is inert on size=35%. The number layer is structurally absent from the 35% variant. Either move the omission to a prop rule (showNumber forced-false on 35%) or add the number layer to 35% and let the prop control it.
  4. State model is implicit. bW, checkmark, and outline are booleans without a documented semantic map. See the States section for the proposed mapping. Confirm with Ofek and give the states named tokens (state.locked, state.claimed, state.selected).
  5. bW=true as default. Ships greyed-out. Reasonable if “locked” is the most common state, worth documenting so consumers do not forget to disable it.
  6. Motion is undefined. No keyframes, no prototype connections, no motion tokens. The three state transitions (unlock, claim, select) all need a spec (duration, easing, reduced-motion behavior). See the Motion section.
  7. Checkmark halo uses raw box-shadows. The green-halo glow on Done 5 uses hardcoded rgba(0,0,0,0.35) / rgba(0,0,0,0.65) values that scale per size. Should be a single elevation token that scales via a formula, not per-size hardcodes.
  8. GrayCast uses #d9d9d9 raw. No semantic state token.
  9. UnityGuidelines layer is a handoff artifact. Hidden guide-brackets for Unity’s safe-area / bleed sit inside the 100% variant. Fine as an internal reference; document that the frame is not part of the component’s exported API.
  10. SKU Library is stale. Section SKU Library, Requires updating is the catalog the skuType slot draws from. Its current state is not a contract. Schedule a library-inventory trace as a follow-up build.
  11. Ownership gate. The reference canvas says “Component Variations (Specific component requirements, Ask Ofek Before Use)”. A soft governance rule that is not machine-readable. Formalize it (a status: gated-variants field, or a decision log).
  12. Playtika rendering language check. The outline state is implemented via 4 stacked tiny-blur drop-shadows to fake a stroke. That aligns with the house rule (hierarchy via outlines / containers, not web-style shadows): good. Worth documenting explicitly so future variants do not slip into single-shadow “web” outlines.
  13. Timer, Running text, Subtitle are numeric font-size tokens that carry a usage hint in their name. Same “naming carries usage” pattern the size variants use. Elevate the pattern to a documented convention (or refactor to type.size.* with a separate usage table).