# Cards, icons, images and badges

## A card with an icon and image

A card is a content container with background, border and corner controls. It does not need every element at once. `blocks/media-card.html` includes an optional image, icon selection and size, title and description. Add it to the package and allow `media-card` in the parent schema.

## 1. Card surface

```html
<article data-canvas-card="[[ block.field_path ]].card" style="background:[[ block.settings.background ]];border:[[ block.settings.border_width ]]px solid [[ block.settings.border_color ]];border-radius:[[ block.settings.radius ]]px">
  <h2 data-text-field="[[ block.settings_path ]].title" gs-text="block.settings.title"></h2>
</article>
```

`data-canvas-card` connects the surface editor. Schema additionally exposes explicit color, border width and radius controls with defaults. Never reuse a fixed identifier for every card; `block.field_path` gives each instance its own path.

Preview appearance overrides are stored in `content.canvas.elements`. If a schema change appears ineffective, inspect that element's override and reset its style in the editor. These are two configuration layers for the same element.

## 2. Icon

```html
<span class="launch-card-icon" style="--icon-size:[[ block.settings.icon_size ]]px">
  <gs-icon name="[[ block.settings.icon ]]" field="[[ block.field_path ]].icon"></gs-icon>
</span>
```

Use a schema select with real icon names. The catalog contains `spark`, `star`, `check`, `arrow`, `heart`, `plus`, `globe`, `shield`, `users`, `bolt`, `wallet`, `cards` and `insights`. `none` means no icon.

Declare size as a range, for example 16–96 px with a 32 px default. CSS reads it through `.launch-card-icon svg { width:var(--icon-size,32px); height:var(--icon-size,32px); }`. A slider does nothing unless rendering uses its value. The `field` binding also connects shared icon and dimension editing.

The example omits the icon when set to none. Restore it through schema controls. Do not display a fictitious default icon for an icon-free element.

## 3. Images and alternative text

```html
<template gs-if="block.settings.image_url != blank">
<img src="[[ safe_url(block.settings.image_url) ]]" alt="[[ block.settings.image_alt ]]" loading="lazy" width="640" height="400">
</template>
```

`image_picker` currently accepts an image URL. Use HTTPS or a media-library address. `image_alt` is a separate setting describing the image's meaning; decorative images may use an empty value. A filename is not an informative description.

Set dimensions matching the intended area to reduce layout shifts. The example uses aspect-ratio and object-fit cover. This is CSS cropping, not a saved interactive crop. Shared `gs-image` works with separate content fields (`image_url`, adjacent `image_alt` and `image_frame`); do not switch these models without explicitly mapping their data.

## 4. Badges

A badge is a short label, not automatically a link. Declare text with ID `badge`, a color and radius, then render:

```html
<span class="theme-badge" data-text-field="[[ block.settings_path ]].badge" gs-text="block.settings.badge"></span>
```

Example styling: inline-flex, 6px/12px padding, a one-pixel currentColor border and a 999px radius. For an icon, use `gs-icon` with a distinct field path and a real none state. If the badge navigates, use an accessible link as described in [buttons](/en/buttons/).

## 5. Responsive card lists

The parent renders children through `gs-slot="blocks"`; CSS defines the grid. Cards retain individual settings when reordered. Do not use a screen-sized fixed width for each card. Use minmax and test long content and absent images on mobile.

## Final checks

Add differently colored cards, reorder and save them. Hide and restore an icon, resize it, set radius and border width to zero. Check empty and valid image URLs, alt text, surface editing and public rendering. Appearance overrides must not move between instances.

## Complete block file

### blocks/media-card.html

```html
<article class="launch-card" data-canvas-card="[[ block.field_path ]].card" style="background:[[ block.settings.background ]];border:[[ block.settings.border_width ]]px solid [[ block.settings.border_color ]];border-radius:[[ block.settings.radius ]]px">
  <template gs-if="block.settings.icon != &#x27;none&#x27;"><span class="launch-card-icon" style="color:[[ block.settings.icon_color ]];--icon-size:[[ block.settings.icon_size ]]px"><gs-icon name="[[ block.settings.icon ]]" field="[[ block.field_path ]].icon"></gs-icon></span></template>
  <template gs-if="block.settings.image_url != blank"><img class="launch-card-image" src="[[ safe_url(block.settings.image_url) ]]" alt="[[ block.settings.image_alt ]]" loading="lazy" width="640" height="400"></template>
  <h2 data-text-field="[[ block.settings_path ]].title" gs-text="block.settings.title"></h2>
  <p data-text-field="[[ block.settings_path ]].text" gs-text="block.settings.text"></p>
</article>
```
