JavaScript Card

@lemonadejs/card · ✓ 24 contract checks · framework-agnostic · zero dependencies

<Card /> — a content surface block (LemonadeJS v6)

The classic card sections (header / media / content / actions) collapsed into one contract-driven block. Every section is a branch: it only exists in the DOM when its props are set — the media image when image is set, the header when any of avatar/title/subtitle is set, the action row when actions has entries. Children always render in the content area, after the content text.

Two variants through data-variant: ” (elevated — subtle shadow, the default) and ‘outlined’ (1px border, no shadow). clickable makes the whole card an interactive surface (hover lift + onclick); action buttons stop propagation so their clicks never double-fire the card.

Example

live
import { html } from 'lemonadejs';
import Card from '@lemonadejs/card';

const App = (props, { state }) => {
    const status = state('Not saved');
    const actions = [
        { label: 'Share', onclick: () => (status.value = 'Link copied') },
        { label: 'Save', onclick: () => (status.value = 'Saved to your trips') },
    ];

    return html`<div style="display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start;">
        <div style="width: 300px;">
            <${Card} image="https://picsum.photos/seed/lisbon/600/340"
                title="Lisbon weekend" subtitle="18 to 20 April 2026"
                content="Two nights in Alfama, a day trip to Sintra and pastel de nata on every corner."
                actions="${actions}" />
        </div>
        <div style="width: 300px;">
            <${Card} variant="outlined" clickable title="Porto extension" subtitle="Optional, 1 night"
                content="Click anywhere on this card." onclick="${() => (status.value = 'Porto added to the plan')}" />
            <p>Status: <b>${status}</b></p>
        </div>
    </div>`;
};

Installation

npm install @lemonadejs/card
import Card from '@lemonadejs/card';
import '@lemonadejs/card/style.css';

Three deployment forms, one component:

html`<${Card} />`                       // by value (no registration)
setComponents({ Card });               // then <Card /> by name anywhere
createWebComponent(Card);              // <lm-card> in plain HTML/any framework

Props

Every declared prop arrives as a live state — pass a value for a snapshot or a state for a two-way live wire. Attribute strings are coerced to the declared type.

PropTypeDefaultDescription
titlestring''header title line
subtitlestring''muted line under the title
imagestring''media url, top image (object-fit: cover)
imageheightnumber180media height in px
imagealtstring''media image alt text (” = decorative)
avatarstring''small round img beside the header titles
avataraltstring''avatar alt text (” = decorative)
contentstring''body text (children render after it)
actionsarrayCardAction[] — footer buttons, right-aligned
variantstring''” = elevated
clickablebooleanfalsewhole card hover lift + onclick

Events

All event names are lowercase (the platform convention — LJS-305 warns otherwise).

  • onclick — fires when a clickable card is clicked

Styling

All classes follow the lm-card-* convention; visual variants are data-* attributes on the root. Override freely — there is no styling engine to fight.

Contract

The machine-readable schema ships with the package:

import contract from '@lemonadejs/card/contract.json';

verify.json carries the conformance proof produced by verify(Card).

Looking for the v5 plugin? See the archived v5 documentation.