---
name: app_card
kind: component
lang: cpp
domain: core
version: "1.1.0"
purity: pure
signature: "bool fn_ui::app_card(const fn_ui::AppCardData& data, ImVec2 size)"
description: "Clickable card widget with accent stripe, optional left-side icon (ImTextureID), title and wrapped description. Inner ClipRect prevents description overflow when text exceeds card height. Does NOT inherit ImGui::Button colors — uses InvisibleButton + ImDrawList so visuals are fully driven by accent."
tags: [imgui, ui, card, button, launcher, hub]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [imgui]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/core/app_card.cpp"
framework: imgui
params:
  - name: data
    desc: "AppCardData {id, title, description, accent}. id must be unique within the parent window. description may be nullptr or empty."
  - name: size
    desc: "Card size in pixels (width x height). Typical: ImVec2(280, 96)."
output: "true the frame the user clicks the card, false otherwise."
notes: "Consumed by apps/app_hub_launcher. Pattern: caller loops over a vector of cards in a grid using SameLine + Dummy for row spacing."
---

# app_card

Clickable card widget for app/launcher grids. Visual identity driven by an accent color (semi-transparent fill + solid stripe + accent border) so it does NOT look like a default `ImGui::Button` (no blue). Uses `InvisibleButton` for hit-testing and `ImDrawList` for the visuals — no `ImGui::Button` is involved.

## Ejemplo

```cpp
#include "core/app_card.h"

fn_ui::AppCardData card;
card.id          = "graph_explorer";
card.title       = "Graph Explorer";
card.description = "Visor de grafos GPU-accelerated";
card.accent      = IM_COL32(0x08, 0x91, 0xc2, 255);  // #0891c2

if (fn_ui::app_card(card, ImVec2(280, 96))) {
    // launch the app
}
```

Grid layout (N cards per row):

```cpp
float card_w = 280.0f, card_h = 96.0f, spacing = 12.0f;
int cols = std::max(1, (int)((ImGui::GetContentRegionAvail().x + spacing) / (card_w + spacing)));
int shown = 0;
for (auto& app : apps) {
    if (shown % cols != 0) ImGui::SameLine(0.0f, spacing);
    fn_ui::AppCardData c{app.id.c_str(), app.title.c_str(), app.desc.c_str(), app.accent};
    if (fn_ui::app_card(c, ImVec2(card_w, card_h))) launch(app);
    ++shown;
    if (shown % cols == 0) ImGui::Dummy(ImVec2(0.0f, spacing - 4.0f));
}
```

## Cuando usarla

Cuando necesites un grid/lista de "cards" clicables (launchers, dashboards de modulos, selectores de proyecto) y quieras que la identidad visual venga de un accent color por card en vez de los azules por defecto de `ImGui::Button`. Si solo quieres un boton simple con texto, usa `ImGui::Button` normal.

## Gotchas

- `data.id` debe ser unico dentro del padre — colision = un solo card recibe el click.
- La descripcion se renderiza con wrap automatico; si `size.y` es muy pequeno el texto se sale por debajo (no clipea). Ajusta `size.y` a 90-110 px para 2-3 lineas de wrap.
- Pinta sobre `ImGui::GetWindowDrawList()` — debe llamarse dentro de una ventana ImGui activa.
- El color `data.accent` debe llevar alpha 255; las transparencias para fondo/border/hover las calcula el componente internamente.

## Capability growth log

v1.1.0 (2026-05-17) — Optional `icon` (ImTextureID) param renders left-side thumbnail; PushClipRect contains description overflow.
v1.0.0 (2026-05-17) — Initial. Extracted from inline rendering in `apps/app_hub_launcher/main.cpp`.
