Cards

Cards contain content and actions about a single subject.


Page Summary


Specifications references

Accessibility

Please follow accessibility criteria for development

Images in cards are considered as decorative, so they are ignored by Voice Over.

Variants

Cards are a contained and independent element that can display content and actions on a single topic.

There are a few ways cards can be presented. Ranging from a single title on its own for a simple card view or with more information shown in a subtitle and supporting text and actions at the bottom of the card.

Vertical Image First Card

This is a full width card displayed with an image as first element.

This card is composed of two parts:

  • Media: (today an image)
  • Content: with a title, an optional subtitle an optional supporting text and optional buttons (zero up to two)

Vertical image first card light Vertical image first card dark

Implementation

Card is configured like this:

ODSCardVerticalImageFirst(
    title: Text("Title"),
    imageSource: .image(Image("placeholder", bundle: Bundle.ods)),
    subtitle: Text("Subtitle"),
    text: Text("A supporting text to describe something")
) {
   Button("Button 1") {
        // do something here
    }
} secondButton: {
    Button("Button 2") {
        // do something here 
    }
}

Vertical Header First Card

This is a full width card displaying with a title and a thumbnail on top as first element.

This card is composed of three parts:

  • Header: with a title, an optional subtitle and an optional thmubnail
  • Media: (today an image)
  • Content: with an optional supporting text and optional buttons (zero up to two)

Vertical header first card light Vertical header first card dark

Implementation

Card is configured like this:

    
ODSCardVerticalHeaderFirst(
    title: Text("Title"),
    imageSource: .image(Image("placeholder", bundle: Bundle.ods)),
    subtitle: Text("Subtitle"),
    thumbnailSource: .image(Image("placeholder", bundle: Bundle.ods)),
    text: Text("A supporting text to describe something")
) {
   Button("Button 1") {
        // do something here
    }
} secondButton: {
    Button("Button 2") {
        // do something here 
    }
}

Horizontal Card

This is a full width card displaying with image on left and content with texts on the right. Additonal action buttons can be added at the bottom of the card.

Thes content is composed by:

  • a title
  • an optional subtitle
  • an optional text for larger description

Horizontal card light Horizontal card dark

Implementation

Card is configured like this:

ODSCardHorizontal(
    title: Text("Title"),
    imageSource: .image(Image("placeholder", bundle: Bundle.ods)),
    imagePosition: .leading,
    subtitle: Text("Subtitle"),
    text: Text("A supporting text to describe something")
) {

    Button("Button 1") {
            // do something here
    }
} secondButton : {
    Button("Button 1") {
        // do something here
    }
}

Small Card

The small card if prefered for two-column portrait mobile screen display. As it is smaller than full-width cards, it contains only title and subtitle (optional) in one line (Truncated tail).

Small card light Small card dark

Implementation

Card is configured like this:

ODSCardSmall(
    title: Text("Title"),
    imageSource: .image(Image("placeholder", bundle: Bundle.ods)),
    subtitle: Text("Subtitle")
) 

How to add Small Card in Grid

class Model {
    let title: String
    let subtitle: String?
    let imageSource: ODSImage.Source
    
    init(title: String, imageSource: ODSImage.Source, subtitle: String? = nil) {
        self.title = title
        self.imageSource = imageSource
        self.subtitle = subtitle
    }
}


let models = [
    Model(
        title: "Title 1",
        imageSource: .image(Image("placeholder", bundle: Bundle.ods)),
        subtitle: "Subtitle 1"
    )
    Model(
        title: "Title 2",
        imageSource: .image(Image("placeholder", bundle: Bundle.ods)),
        subtitle: "Subtitle 2"
    )
    //...
]

/// /!\ Don't forget to put the grid into a scrollview
ScrollView {
    LazyVGrid(columns: columns, spacing: ODSSpacing.none) {
        ForEach(models, id:\.title) { model in
            ODSCardSmall(
                title: Text(model.title),
                imageSource: model.imageSource,
                subtitle: Text(model.subtitle)
            )
        }
    }
    .padding(.all, ODSSpacing.m)
}
 

However for accessibility edge cases, like when text sizes are accessibility sizes, the behaviour is different for such components. They won’t be displayed in one truncated line because the text will be too truncated and difficult to read. If this choice is too impacting for your UI, it is possible to define the limit number of lines to use if a11y size are used

ODSCardSmall(
    title: Text("Title"),
    imageSource: .image(Image("placeholder", bundle: Bundle.ods)),
    subtitle: Text("Subtitle"),
    // Here 3 is the number of lines you want for such edge cases
    titleAccessibleLineLimit: 3,
    subtitleAccessibleLineLimit: 3
)