Design System
Components

Recommendation

A centred 224px card that recommends one offer — logo, title, description and a single button — on glass or white.

Figma description · RecommendationOpen in Figma ↗

Use for

A self-contained card for surfacing a single personalized recommendation — a feature, service, or content item — with a branded icon, a short title, a supporting description, and a CTA button. Use in a horizontal carousel or as a standalone unit within a section when the goal is to prompt the user toward a specific action based on their context or usage.

Not for

Plan or product selling with pricing — use card-features variants for those. Category or service navigation — use service-card for that. Content that requires an image in the header — use card-info for that. Lists of items — use card-list for that. Any context where more than one CTA is needed.

Choosing

Set carousel=true when the card is part of a horizontally scrollable row — two cards are shown side by side with a 4px gap, each at 338px width, peeking the next item. Set carousel=false for a standalone full-width card at 353px.

Set inverse=true when the card sits on a dark or midnight surface — the background uses surface/glass/white/md (15% white glass), the title uses text/default/inverse (white), the description uses text/default/inverse-subtle (75% white), and the button uses scheme=inverse (white button, dark label). Set inverse=false on light surfaces — the background uses surface/base/default, text uses default dark tokens, and the button uses scheme=midnight (dark button, white label). Do not mix schemes — match inverse to the surrounding section surface.

Set direction=rtl for Arabic — the title and description use dir=auto and flip automatically. The button label also flips via dir=auto. Do not override text alignment manually.

Rules

The card is fixed at 224px height. In carousel mode each card is 338px wide; in standalone mode 353px wide. Do not resize the instance. The icon sits centered at the top of the card in a 32px container with border-radius/2 (8px) — do not replace it with a flat icon or image, and do not resize the container. The title uses title/md (20px semi-bold), centered, truncated with ellipsis — do not allow it to wrap beyond the available height. The description uses body/md (14px regular), centered, subtle color — it fills the remaining flex space and is also truncated. The CTA button is full-width, size=md (40px), primary style, centered at the bottom with 12px top padding. Do not remove the button or add a second one. All spacing values are tokenized — do not hard-code measurements.

smiles
Recommended for you
Handpicked offers based on how you use etisalat.
Props
import SwiftUI
import EandTokens

// Recommendation: 224pt (row-3), r20, padding 16, everything centred. inverse (default):
// white glass 15% + blur over imagery, white primary md button. 32pt r8 icon, title/md
// (2 lines), body/md description, CTA 12pt below. Carousel cards are 338 wide, gap 4.
struct Recommendation: View {
  var body: some View {
    VStack(spacing: 0) {
      Image("rec-offers").resizable()
        .frame(width: EandIcon.icon2xl, height: EandIcon.icon2xl)
        .clipShape(RoundedRectangle(cornerRadius: EandRadius.radius3))
      VStack(spacing: EandSpacing.xs) {
        Text("Recommended for you")
          .font(.system(size: EandTypography.titleMd.size, weight: EandTypography.titleMd.weight))
          .foregroundColor(EandColor.textDefaultInverse).lineLimit(2)
        Text("Handpicked offers based on how you use e&.")
          .font(.system(size: EandTypography.bodyMd.size))
          .foregroundColor(EandColor.textDefaultInverseSubtle)
        Spacer(minLength: 0)
      }
      .multilineTextAlignment(.center)
      .padding(.top, EandSpacing.md)
      InverseButton(title: "Explore", size: .md) {}        // white fill, midnight label
        .padding(.top, EandSpacing.md)
    }
    .padding(EandSpacing.lg)
    .frame(maxWidth: .infinity).frame(height: EandSection.bodyHeightRow3)
    .background(EandColor.surfaceGlassWhiteMd)
    .background(.ultraThinMaterial)
    .clipShape(RoundedRectangle(cornerRadius: EandRadius.radius7))
  }
}

When to use

  • Use to suggest one personalised offer or next step with a single clear action. The default glass card sits on a dark or image backdrop; inverse={false} gives a white card for light sections.
  • Don't use for a set of offers with prices or media (DealCard, ProductCard), or for a full-width campaign banner (Highlight).

Options

  • inverse (default true) — white glass with a backdrop blur, white text and an inverse primary button. false is a white card with a midnight primary button.
  • carousel — renders this card and each entry of items as 338px cards, 4px apart, scrolling sideways so the next card peeks. Figma's default variant is the carousel; the component defaults to a single card, because a carousel needs items.
  • Content — icon is a 32px logo (a Smiles placeholder until you pass one; null hides it). cta is the button label; null hides the button. title and description default to placeholder copy, so always pass your own.
import { Recommendation } from '@eand/react-design-system';

<Recommendation
  carousel
  title="Double data this weekend"
  description="Get twice the data on your plan until Sunday."
  cta="View offer"
  items={[
    { title: 'Add a second line', description: 'Share your plan with family.', cta: 'Explore' },
  ]}
/>

Props

PropTypeDefaultDescription
carousel?booleanfalseFigma carousel — this card and items as 338-wide peeking cards.
cta?ReactNodeExploreFigma button label. null hides the button.
description?ReactNodeHandpicked offers based on how you use etisalat.Figma description-string.
icon?ReactNode—Figma visual-asset — a 32px logo. Smiles placeholder by default; null hides it.
inverse?booleantrueFigma inverse — glass on a dark backdrop (default) vs white.
items?RecommendationItem[]—Further cards for carousel.
onCta?(() => void)—
title?ReactNodeRecommended for youFigma title-string — two lines max.
width?string | number—

Design

Figma source: Recommendation — etisalat Consumer App DSL V1.2

  • 224px tall (row-3), border-radius/7 (20px), padding 16 (spacing/lg), every element centred.
  • A 32px logo (icon/2xl) at border-radius/3; 12 below it (spacing/md), the title in title/md (two lines at most) and the description in body/md, 4 apart (spacing/xs); then a primary md button, 12 above it.
  • inverse: surface/glass/white/md with a 20px backdrop blur, title text/default/inverse, description text/default/inverse-subtle. White: surface/base/inverse, text/default/default and text/default/subtle.
  • Carousel cards are 338px wide in a 353px slot, gap spacing/xs. The width has no token.

On this page