GeneralCard
A fixed 224px tile with a centred title, an image and an optional badge, for grids and carousels.
General-cardOpen in Figma ↗Use for
A flexible media card for surfacing content, features, or navigational entry points where an image or icon is the primary visual and a short title supports it. Use in grid or carousel section layouts when the content doesn't have a price or service-bundle structure that would require a more specific card variant.
Not for
Plan or subscription content with pricing — use plans-mini or card-features-package. Product items with a product image and price — use card-features-product. Category entry points — use card-features-category. Content that requires body text, descriptions, or multiple CTAs — this card supports a title and badge only.
Choosing
Set surface=default when the card sits in a light section context. Set surface=inverse when it sits in a dark section context. Do not choose surface for visual variety — match it to the surrounding section surface. Both variants share the same internal structure; only the surface, text, and color tokens differ.
Set carousel=true for a scrollable horizontal layout; carousel=false for a fixed grid layout. In carousel mode the card width is narrower (147px); in grid mode it is wider (166px) to fill the grid slot — do not manually resize to compensate.
Set top-alignment=true to pin the title to the top of the card with the image centered below. Set top-alignment=false to pin the title to the top and allow the image to sit vertically centered in the remaining space. Use top-alignment when the title is the primary read and the image is supporting. Use bottom-alignment when the image carries more visual weight.
Set size to control the media slot: sm for supporting visuals where imagery is secondary, md for a balanced media-and-text read, lg when the image is the primary communication layer. Do not pick size for aesthetic reasons — match it to the content's role in the layout.
Set badge to show a badges-offers or badges-status tag when there is an active offer or status to surface. The badge sits at the bottom-left of the card inside the .badge-placement slot at 12px padding. Set badge off when there is no offer or status to show.
Set direction=rtl for Arabic. The title uses centered alignment and will reflow correctly; the badge placement mirrors to the bottom-right.
Rules
The title uses title/sm (16px semi-bold), centered, maximum 2 lines. Do not allow it to overflow beyond 2 lines — truncate or shorten copy to fit. The media slot is absolutely positioned and fills the card — do not move or reposition the image layer manually. All spacing and sizing values are tokenized — do not hard-code any measurements. Card height is fixed at 224px across all variants. Do not resize the instance height. For RTL, set direction=rtl at the component level — do not override text alignment or badge position manually.
import SwiftUI import EandTokens // General-card: a fixed 224pt (row-3) tile, r20, clipped; 166 wide in a two-up grid. // surface default = DARK (surface/raised/midnight, inverse ink); inverse = white. // Title title/sm, centred, 2 lines, padded 24/12/0. Image sm 64 (at y 104) · md 128 · // lg 224 full-bleed. Badge slot along the bottom (top with topAlignment). struct GeneralCard: View { let title: String; let image: Image var body: some View { ZStack(alignment: .top) { image.resizable().scaledToFit() .frame(width: EandScale.scale64, height: EandScale.scale64) .offset(y: 104) Text(title) .font(.system(size: EandTypography.titleSm.size, weight: EandTypography.titleSm.weight)) .multilineTextAlignment(.center).lineLimit(2) .padding(.top, EandSpacing.spacing2xl).padding(.horizontal, EandSpacing.md) VStack { Spacer(); HStack { OfferBadge(text: "20% off"); Spacer() } } .padding(EandSpacing.md) } .foregroundColor(EandColor.textDefaultInverse) .frame(width: EandSection.bodyWidthCol12, height: EandSection.bodyHeightRow3) .background(EandColor.surfaceRaisedMidnight) .clipShape(RoundedRectangle(cornerRadius: EandRadius.radius7)) } }
When to use
- Use for category or offer tiles inside a Section: a short title (two lines at most) and a visual, in a two-up grid or a carousel.
- Don't use for content with body copy and an action; that is Card. Not for plans or products with prices (PlanCard, ProductCard), and not for full-width campaign banners (Highlight).
surface follows the published Figma naming: default is the dark card and inverse is
the white one. That is the reverse of Card. The label is awaiting design
confirmation.
Options
| Prop | Values | Effect |
|---|---|---|
size | sm · md · lg | Image 64px, 128px, or 224px full-bleed |
topAlignment | false · true | true moves the title to the bottom and the image and badge to the top |
carousel | false · true | true fixes the width at 147px; otherwise the card fills its grid slot |
badgePlacement | start · center · end | Badge alignment; mirrors in RTL |
topAlignment follows the published symbol. The symbol's description text says the reverse,
which is logged as a design question.
Pass a Badge as badge. image takes a URL or your own node. imageAlt defaults to an empty
string, which marks the image as decorative; set it when the image carries meaning.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | — | |
badge? | ReactNode | — | A Badge (offer or status), placed 12px in from the corner. |
badgePlacement? | 'end' | 'start' | 'center' | start | Badge alignment (Figma .badge-placement Left | Center | Right, mirrored in RTL). |
carousel? | boolean | false | Carousel cell (147 wide) instead of a grid cell that fills its slot. |
image? | ReactNode | — | Image URL, or your own node (it is sized to the media box). |
imageAlt? | string | — | |
size? | 'lg' | 'md' | 'sm' | sm | Media size (Figma size): sm 64 · md 128 · lg 224 full-bleed. |
surface? | 'default' | 'inverse' | default | Figma surface: default is the DARK card, inverse the white one (as published). |
topAlignment? | boolean | false | Figma top-alignment: true = image and badge at the top, title at the bottom. |
Design
Figma source: General-card — etisalat Consumer App DSL V1.2
- 224px tall (
rem/14),border-radius/7(20px), clipped. In a two-up grid it is 166px wide (section/body/width/col-12); the 147px carousel width has no token. - Title
title/sm, centred, two lines at most. Padding 24/12/0 (spacing/2xl,spacing/md), or 0/12/24 withtopAlignment. - Fill:
defaultissurface/raised/midnightwithtext/default/inverse;inverseissurface/base/inversewithtext/default/default. - The badge sits in
.badge-placement: padding 12 (spacing/md), gap 4 (spacing/xs). - Images are placed by constraints:
sm104px from the top (56px withtopAlignment),mdflush to the edge opposite the title,lgfilling the card.