coderef

Walmart

Walmart's iOS app is an unapologetically bright, white-canvas retail environment built for speed and trust. The interface sits on pure white (#FFFFFF) with the faintest cool-blue tinted surfaces (#F2F8FD) to separate cards from the page.
DownloadRaw
Walmart

Build with the Walmart look.

Palette, type and components from this DESIGN.md, applied to a sample hero.

Palette

At a glance

Theme
light
Typefaces
Bogle
Sections
8
Length
3,457 words
Platforms
Android, Expo, SwiftUI, DESIGN.md

Add it to your project

Then prompt your agent

Design System · Walmart

Light and dark previews, tokens and a ready-to-use package, generated from this DESIGN.md. No AI involved: the same file always produces the same output.

Generating design system…
DownloadRaw

Walmart (iOS) — Jetpack Compose Implementation Guide

Companion to DESIGN.md — the framework-neutral spec. This file ports Walmart’s visual language to Android with Jetpack Compose (Material 3): a color token object, a Typography set, paste-ready @Composables, Modifiers, motion, and haptics.

Why a Compose guide for an iOS-referenced app? The DESIGN.md tokens are platform-neutral. This file keeps the visual identity (Walmart’s bright white canvas, single-blue action color, Spark Yellow savings chrome, Rollback tag) while making everything idiomatic Android — NavigationBar instead of a UITabBar, sp/dp instead of pt, Material 3 ripple instead of iOS press scale where appropriate.

Assumes Compose BOM 2024.09+, Kotlin 2.0+, minSdk 24, Material 3, and Coil 2.6+ for images.

1. Color Tokens

// ui/theme/WalmartColors.kt
import androidx.compose.ui.graphics.Color

object WalmartColors {
    // Canvas & Surfaces
    val Canvas      = Color(0xFFFFFFFF)
    val SurfaceTint = Color(0xFFF2F8FD)
    val SurfaceGray = Color(0xFFF5F5F5)
    val Divider     = Color(0xFFE2E8F0)
    val Border      = Color(0xFFC9D2DE)

    // Text
    val TextPrimary   = Color(0xFF2E2F32)
    val TextSecondary = Color(0xFF74767C)
    val TextTertiary  = Color(0xFF9BA0A8)

    // Brand
    val Blue         = Color(0xFF0071DC)
    val BluePressed  = Color(0xFF004F9A)
    val BlueTint     = Color(0xFFE6F1FC)
    val Spark        = Color(0xFFFFC220) // logo + savings chrome only
    val SparkPressed = Color(0xFFE5A800)

    // Semantic
    val Success  = Color(0xFF1A7F37)
    val Savings  = Color(0xFF2A8703)
    val Warning  = Color(0xFFB25E00)
    val Error    = Color(0xFFD03A2D)
    val StarGold = Color(0xFFFFB81C)
}

Wire it into a Material 3 lightColorScheme so ripples, dividers, and default component colors inherit the brand. Walmart is light-first; provide a dark scheme only if you target system dark mode.

// ui/theme/Theme.kt
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.MaterialTheme

private val WalmartScheme = lightColorScheme(
    primary          = WalmartColors.Blue,
    onPrimary        = Color.White,
    primaryContainer = WalmartColors.BlueTint,
    background        = WalmartColors.Canvas,
    onBackground      = WalmartColors.TextPrimary,
    surface           = WalmartColors.Canvas,
    onSurface         = WalmartColors.TextPrimary,
    surfaceVariant    = WalmartColors.SurfaceTint,
    outline           = WalmartColors.Border,
    outlineVariant    = WalmartColors.Divider,
    error             = WalmartColors.Error,
)

@Composable
fun WalmartTheme(content: @Composable () -> Unit) =
    MaterialTheme(colorScheme = WalmartScheme, typography = WalmartTypography, content = content)

2. Typography

Bogle is proprietary. Drop the TTFs in res/font/ (lowercase, snake_case) and build a FontFamily. Fall back to the system font (Roboto); Inter is the closest substitute if you bundle it.

// ui/theme/WalmartType.kt
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

val Bogle = FontFamily(
    Font(R.font.bogle_regular,  FontWeight.Normal),   // 400
    Font(R.font.bogle_medium,   FontWeight.Medium),   // 500
    Font(R.font.bogle_semibold, FontWeight.SemiBold), // 600
    Font(R.font.bogle_bold,     FontWeight.Bold),     // 700
)

// Named ramp — mirrors DESIGN.md §3 exactly (pt → sp 1:1)
object WalmartText {
    val TitleLarge   = TextStyle(Bogle, fontWeight = FontWeight.Bold,     fontSize = 28.sp, lineHeight = 34.sp, letterSpacing = (-0.3).sp)
    val Section      = TextStyle(Bogle, fontWeight = FontWeight.Bold,     fontSize = 22.sp, lineHeight = 28.sp, letterSpacing = (-0.2).sp)
    val PriceLarge   = TextStyle(Bogle, fontWeight = FontWeight.Bold,     fontSize = 22.sp, lineHeight = 24.sp, letterSpacing = (-0.2).sp)
    val Subsection   = TextStyle(Bogle, fontWeight = FontWeight.SemiBold, fontSize = 18.sp, lineHeight = 23.sp, letterSpacing = (-0.1).sp)
    val ProductTitle = TextStyle(Bogle, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, lineHeight = 21.sp)
    val Body         = TextStyle(Bogle, fontWeight = FontWeight.Normal,   fontSize = 15.sp, lineHeight = 22.sp)
    val Button       = TextStyle(Bogle, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, lineHeight = 20.sp)
    val PriceInline  = TextStyle(Bogle, fontWeight = FontWeight.Bold,     fontSize = 15.sp, lineHeight = 18.sp)
    val Struck       = TextStyle(Bogle, fontWeight = FontWeight.Normal,   fontSize = 13.sp, lineHeight = 16.sp)
    val Meta         = TextStyle(Bogle, fontWeight = FontWeight.Normal,   fontSize = 13.sp, lineHeight = 17.sp)
    val Tag          = TextStyle(Bogle, fontWeight = FontWeight.Bold,     fontSize = 12.sp, lineHeight = 14.sp, letterSpacing = 0.2.sp)
    val Tab          = TextStyle(Bogle, fontWeight = FontWeight.SemiBold, fontSize = 11.sp, lineHeight = 13.sp, letterSpacing = 0.1.sp)
    val Caption      = TextStyle(Bogle, fontWeight = FontWeight.Normal,   fontSize = 11.sp, lineHeight = 14.sp)
}

// Map onto Material 3 slots so stock components inherit the brand
val WalmartTypography = Typography(
    headlineLarge = WalmartText.TitleLarge,
    headlineSmall = WalmartText.Section,
    titleMedium   = WalmartText.ProductTitle,
    bodyMedium    = WalmartText.Body,
    labelSmall    = WalmartText.Tab,
)

For tabular price digits, apply a TextStyle with fontFeatureSettings = "tnum":

val PriceTabular = WalmartText.PriceLarge.copy(fontFeatureSettings = "tnum")

3. Signature Components

Primary CTA — Add to Cart

import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.draw.scale

enum class CtaVariant { Primary, Secondary, Deal }

@Composable
fun WalmartCtaButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    variant: CtaVariant = CtaVariant.Primary,
) {
    val interaction = remember { MutableInteractionSource() }
    val pressed by interaction.collectIsPressedAsState()
    val scale by animateFloatAsState(if (pressed) 0.98f else 1f, label = "ctaScale")
    val haptics = LocalHapticFeedback.current

    val bg = when {
        variant == CtaVariant.Primary && pressed -> WalmartColors.BluePressed
        variant == CtaVariant.Primary            -> WalmartColors.Blue
        variant == CtaVariant.Deal               -> WalmartColors.Spark
        else                                     -> Color.Transparent
    }
    val fg = when (variant) {
        CtaVariant.Primary   -> Color.White
        CtaVariant.Deal      -> WalmartColors.TextPrimary
        CtaVariant.Secondary -> WalmartColors.Blue
    }

    Box(
        modifier = modifier
            .scale(scale)
            .heightIn(min = 48.dp)
            .clip(RoundedCornerShape(24.dp))
            .background(bg)
            .then(if (variant == CtaVariant.Secondary) Modifier.border(1.5.dp, WalmartColors.Blue, RoundedCornerShape(24.dp)) else Modifier)
            .clickable(interaction, indication = null) {
                haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ~iOS light impact
                onClick()
            }
            .padding(horizontal = 24.dp),
        contentAlignment = Alignment.Center,
    ) {
        Text(text, style = WalmartText.Button, color = fg)
    }
}

Rollback Price Tag (signature)

enum class TagKind { Rollback, Clearance, BestSeller }

@Composable
fun RollbackTag(kind: TagKind = TagKind.Rollback, modifier: Modifier = Modifier) {
    val (bg, fg, label) = when (kind) {
        TagKind.Rollback   -> Triple(WalmartColors.Spark,    WalmartColors.TextPrimary, "ROLLBACK")
        TagKind.Clearance  -> Triple(WalmartColors.Error,    Color.White,               "CLEARANCE")
        TagKind.BestSeller -> Triple(WalmartColors.BlueTint, WalmartColors.Blue,        "BEST SELLER")
    }
    Box(
        modifier
            .clip(RoundedCornerShape(4.dp))
            .background(bg)
            .padding(horizontal = 8.dp, vertical = 3.dp),
    ) {
        Text(label, style = WalmartText.Tag, color = fg)
    }
}

Price Block

@Composable
fun PriceBlock(
    price: String,
    wasPrice: String? = null,
    savings: String? = null,
    rollback: Boolean = false,
    modifier: Modifier = Modifier,
) {
    Column(modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
        if (rollback) RollbackTag(TagKind.Rollback)
        Row(verticalAlignment = Alignment.Bottom, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
            Text(price, style = WalmartText.PriceLarge.copy(fontFeatureSettings = "tnum"), color = WalmartColors.TextPrimary)
            if (wasPrice != null) {
                Text(
                    wasPrice,
                    style = WalmartText.Struck.copy(textDecoration = TextDecoration.LineThrough),
                    color = WalmartColors.TextSecondary,
                )
            }
        }
        if (savings != null) {
            Text(savings, style = WalmartText.Meta.copy(fontWeight = FontWeight.SemiBold), color = WalmartColors.Savings)
        }
    }
}

Product Card

@Composable
fun ProductCard(
    title: String,
    price: String,
    wasPrice: String? = null,
    rollback: Boolean = false,
    rating: Int,
    onAdd: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Column(
        modifier
            .clip(RoundedCornerShape(12.dp))
            .background(WalmartColors.Canvas)
            .border(1.dp, WalmartColors.Divider, RoundedCornerShape(12.dp))
            .padding(12.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        Box(
            Modifier
                .fillMaxWidth()
                .aspectRatio(1f)
                .clip(RoundedCornerShape(8.dp))
                .background(WalmartColors.SurfaceGray),
        )
        PriceBlock(price = price, wasPrice = wasPrice, rollback = rollback)
        Text(title, style = WalmartText.ProductTitle, color = WalmartColors.TextPrimary, maxLines = 2, overflow = TextOverflow.Ellipsis)
        Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
            repeat(5) { i ->
                Icon(
                    if (i < rating) Icons.Filled.Star else Icons.Outlined.StarBorder,
                    contentDescription = null,
                    tint = WalmartColors.StarGold,
                    modifier = Modifier.size(11.dp),
                )
            }
        }
        WalmartCtaButton("Add to cart", onAdd, Modifier.fillMaxWidth().height(40.dp))
    }
}

Pickup / Delivery Toggle (signature)

import androidx.compose.animation.core.animateDpAsState

@Composable
fun FulfillmentToggle(
    isDelivery: Boolean,
    onSelect: (Boolean) -> Unit,
    modifier: Modifier = Modifier,
) {
    var width by remember { mutableStateOf(0.dp) }
    val density = LocalDensity.current
    val offset by animateDpAsState(if (isDelivery) width / 2 else 0.dp, tween(220), label = "thumb")

    Box(
        modifier
            .clip(RoundedCornerShape(20.dp))
            .background(WalmartColors.SurfaceTint)
            .onSizeChanged { width = with(density) { it.width.toDp() } }
            .padding(4.dp),
    ) {
        Box(
            Modifier
                .offset(x = offset)
                .fillMaxWidth(0.5f)
                .height(32.dp)
                .clip(RoundedCornerShape(16.dp))
                .background(Color.White),
        )
        Row(Modifier.fillMaxWidth()) {
            listOf("Pickup" to false, "Delivery" to true).forEach { (label, value) ->
                val selected = value == isDelivery
                Box(
                    Modifier.weight(1f).height(32.dp).clickable { onSelect(value) },
                    contentAlignment = Alignment.Center,
                ) {
                    Text(
                        label,
                        style = WalmartText.Button,
                        color = if (selected) WalmartColors.Blue else WalmartColors.TextSecondary,
                    )
                }
            }
        }
    }
}

Quantity Stepper

@Composable
fun QuantityStepper(qty: Int, onChange: (Int) -> Unit, modifier: Modifier = Modifier) {
    Row(
        modifier
            .border(1.dp, WalmartColors.Border, RoundedCornerShape(18.dp))
            .height(36.dp)
            .padding(horizontal = 14.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(16.dp),
    ) {
        Icon(
            if (qty <= 1) Icons.Outlined.Delete else Icons.Filled.Remove,
            contentDescription = if (qty <= 1) "Remove" else "Decrease",
            tint = WalmartColors.Blue,
            modifier = Modifier.size(16.dp).clickable { onChange(maxOf(0, qty - 1)) },
        )
        Text("$qty", style = WalmartText.Button.copy(fontFeatureSettings = "tnum"), color = WalmartColors.TextPrimary)
        Icon(
            Icons.Filled.Add,
            contentDescription = "Increase",
            tint = WalmartColors.Blue,
            modifier = Modifier.size(16.dp).clickable { onChange(qty + 1) },
        )
    }
}

4. Spark Logo Mark

@Composable
fun SparkMark(size: Dp = 28.dp) {
    Box(Modifier.size(size), contentAlignment = Alignment.Center) {
        repeat(6) { i ->
            Box(
                Modifier
                    .graphicsLayer {
                        rotationZ = i * 60f
                        translationY = with(this) { -size.toPx() * 0.30f }
                    }
                    .size(width = size * 0.16f, height = size * 0.42f)
                    .clip(RoundedCornerShape(50))
                    .background(WalmartColors.Spark),
            )
        }
    }
}

5. Bottom Navigation (Tab Bar)

Use Material 3 NavigationBar. Walmart’s iOS tab bar is opaque white with a hairline top border (no live blur — readability first). Active tint is Walmart Blue.

@Composable
fun WalmartBottomBar(selected: Int, cartCount: Int, onSelect: (Int) -> Unit) {
    NavigationBar(
        containerColor = WalmartColors.Canvas,
        tonalElevation = 0.dp,
    ) {
        val items = listOf(
            "Shop"     to Icons.Filled.Home,
            "Services" to Icons.Filled.Build,
            "Search"   to Icons.Filled.Search,
            "Cart"     to Icons.Filled.ShoppingCart,
            "Account"  to Icons.Filled.AccountCircle,
        )
        items.forEachIndexed { i, (label, icon) ->
            NavigationBarItem(
                selected = selected == i,
                onClick = { onSelect(i) },
                icon = {
                    if (label == "Cart" && cartCount > 0) {
                        BadgedBox(badge = { Badge(containerColor = WalmartColors.Blue) { Text("$cartCount") } }) {
                            Icon(icon, contentDescription = label, modifier = Modifier.size(24.dp))
                        }
                    } else {
                        Icon(icon, contentDescription = label, modifier = Modifier.size(24.dp))
                    }
                },
                label = { Text(label, style = WalmartText.Tab) },
                colors = NavigationBarItemDefaults.colors(
                    selectedIconColor   = WalmartColors.Blue,
                    selectedTextColor   = WalmartColors.Blue,
                    unselectedIconColor = WalmartColors.TextSecondary,
                    unselectedTextColor = WalmartColors.TextSecondary,
                    indicatorColor      = WalmartColors.BlueTint,
                ),
            )
        }
    }
}

Place a hairline Divider(color = WalmartColors.Divider, thickness = 0.5.dp) directly above the bar; on PDP, render a sticky white “Add to cart” bar in the Scaffold bottomBar slot stacked above it.

6. Motion

Moment Compose recipe
Add-to-cart bump Animatable keyframes 1 → 0.96 → 1.04 → 1 over ~260ms; HapticFeedbackType.LongPress; pop the cart badge
Pickup/Delivery toggle animateDpAsState thumb offset, tween(220) ease-in-out; label color crossfade
Card press animateFloatAsState 1 → 0.98, tween(150); border darkens to Border
Subtotal roll AnimatedContent with slideInVertically/slideOutVertically on the subtotal Text, 200ms
Spark launch stagger 6 rays’ alpha/scale over 200ms with animateFloatAsState + per-ray delay
// Add-to-cart bump
val bump = remember { Animatable(1f) }
LaunchedEffect(cartTrigger) {
    if (cartTrigger > 0) {
        bump.animateTo(0.96f, tween(100))
        bump.animateTo(1.04f, tween(80))
        bump.animateTo(1f, spring(dampingRatio = 0.6f))
    }
}
Modifier.scale(bump.value)

Haptics: prefer LocalHapticFeedback. For a softer iOS-like “bump” use LocalView.current.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) (API 30+) or a Vibrator VibrationEffect.createOneShot(10, …).

7. Icons

Closest first-party set is androidx.compose.material:material-icons-extended. For exact parity, export Walmart glyphs as vector drawables and load via ImageVector.vectorResource(...).

Purpose SF Symbol (iOS) Material Icon (Compose)
Shop (tab) house.fill Icons.Filled.Home
Services (tab) wrench.and.screwdriver.fill Icons.Filled.Build
Search magnifyingglass Icons.Filled.Search
Cart (tab) cart.fill Icons.Filled.ShoppingCart
Account (tab) person.crop.circle.fill Icons.Filled.AccountCircle
Barcode scan barcode.viewfinder Icons.Filled.QrCodeScanner
Heart / list heart / heart.fill Icons.Outlined.FavoriteBorder / Icons.Filled.Favorite
Rating star star / star.fill Icons.Outlined.StarBorder / Icons.Filled.Star
Quantity minus minus Icons.Filled.Remove
Quantity remove trash Icons.Outlined.Delete
Quantity plus plus Icons.Filled.Add
Location chevron chevron.down Icons.Filled.ExpandMore
Fulfillment ETA clock Icons.Filled.Schedule

8. Minimum SDK & Accessibility Notes

  • minSdk 24 (Compose floor is 21; modern motion is comfortable at 24). compileSdk 34+, Compose BOM 2024.09+, Kotlin 2.0+.
  • Edge-to-edge: call enableEdgeToEdge(); the bright canvas wants dark-content system bars (WindowCompat). Apply Scaffold insets so the sticky “Add to cart” bar clears gesture nav.
  • Font scaling: sp honors the user’s scale — keep it on titles, prices, body. Pin layout-sensitive text (Rollback tag, tab labels, stepper digit) with a fixed Density wrapper or derive from dp.
  • TalkBack: set a product-specific contentDescription on Add to cart; merge card text with Modifier.semantics(mergeDescendants = true) and expose the CTA as a separate button; announce price + savings together.
  • Touch targets: Material minimum is 48.dp. The 36.dp stepper buttons and 24.dp tab icons need Modifier.size(48.dp) hit padding even though the glyphs are smaller.
  • Contrast: dark #2E2F32 on Spark Yellow #FFC220 passes WCAG AA — never white on yellow. #74767C on white passes AA at 14sp+; bump 11sp captions toward #5E6068 for strict compliance.
  • Dynamic color: do not enable Material You dynamicLightColorScheme() — Walmart’s brand requires the fixed #0071DC blue and #FFFFFF canvas regardless of wallpaper.
All mobile designs →

Brand names describe the visual style only; coderef is not affiliated with or endorsed by any brand listed. Licenses