Media

MediaCard

A card with a picture (or any media) on top or at the side, then a meta line, title, text and actions.

#card#media#image#layout#no-js

Demo

Preview

Porto · €70 a night

Studio by the river

A desk, a view, and the best coffee on the street.

Talk · 32 min

Why views come first

A talk from QuilloConf on building interfaces the browser already knows how to run.

Sales this quarter

The media slot takes any HTML: here, a chart drawn in SVG.

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/media_card.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/media_card.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/media_card.qwe
// A room drawn in SVG, so the demo needs no image files.fn media_card_demo_pic(wall: str, floor: str) -> str {    let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 360" width="640" height="360">`        + `<rect width="640" height="360" fill="${wall}"/><rect y="250" width="640" height="110" fill="${floor}"/>`        + `<rect x="80" y="70" width="160" height="120" rx="6" fill="#fff" opacity=".85"/>`        + `<rect x="330" y="150" width="230" height="100" rx="10" fill="#fff" opacity=".6"/></svg>`    `data:image/svg+xml,${url_encode(svg)}`}view MediaCardDemo() =    <div.demo-stack>        <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px" id="media-card-demo-grid">            <MediaCard src={media_card_demo_pic("#e8dcc8", "#8a6d4b")} alt="" meta="Lisbon · €95 a night" title="A kitchen for four"                       text="Light, quiet, two minutes from the metro." href="https://example.com/stays/12"/>            <MediaCard src={media_card_demo_pic("#d6e6f5", "#4b6a8a")} alt="" meta="Porto · €70 a night" title="Studio by the river"                       text="A desk, a view, and the best coffee on the street." ratio="4/3">                <button type="button" class="ui-btn ui-btn-solid ui-btn-sm">Book</button>                <button type="button" class="ui-btn ui-btn-ghost ui-btn-sm">Save</button>            </MediaCard>        </div>        <MediaCard layout="side" src={media_card_demo_pic("#f3e0e8", "#8a4b6a")} alt="" meta="Talk · 32 min" title="Why views come first"                   text="A talk from QuilloConf on building interfaces the browser already knows how to run." width="220px">            <button type="button" class="ui-btn ui-btn-outline ui-btn-sm">Watch</button>        </MediaCard>        <MediaCard layout="side-right" title="Sales this quarter" text="The media slot takes any HTML: here, a chart drawn in SVG." width="180px"                   media={<svg viewBox="0 0 180 120" style="display: block; width: 100%; height: 100%" aria-label="Sales by month">                       <rect x="12" y="60" width="28" height="50" fill="var(--ui-accent)"/><rect x="52" y="35" width="28" height="75" fill="var(--ui-accent)"/>                       <rect x="92" y="50" width="28" height="60" fill="var(--ui-accent)"/><rect x="132" y="15" width="28" height="95" fill="var(--ui-accent)"/>                   </svg>}/>    </div>

Source

ui/media_card.qwe
// MediaCard: a card with a picture (or a video, a map) on top or at the side,// then a title, a line of text and actions: a post, a course, a place to stay.////   <MediaCard src="/img/kitchen.jpg" alt="" title="A kitchen for four" text="Light, quiet, two minutes from the metro." href="/stays/12" meta="Lisbon · €95 a night"/>//   <MediaCard layout="side" src="/img/talk.jpg" alt="" title="Why views come first" text="A talk from QuilloConf.">//       <Button size="sm">Watch</Button>//       <Button size="sm" variant="ghost">Save</Button>//   </MediaCard>//   <MediaCard title="Sales" text="This quarter" media={<BarChart .../>}/>//// src, alt: the picture; or media, any HTML to show instead (a video, a chart).// title: the heading; href makes it a link (the picture too). text: a line or// two under it. meta: a small line above the title (a category, a date, a price).// layout: "top" (default), "side" (the media at the left) or "side-right".// ratio: the picture's shape, "16/9" (default), "4/3", "1". width: the media's// width at the side, as CSS ("200px"). children: actions, in a row at the bottom.import ui/theme.qwe use ui_clslet media_card_css = css`.ui-media-card {  display: grid; overflow: hidden; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface);  color: var(--ui-fg); font: 14px/1.5 var(--ui-font); box-shadow: var(--ui-shadow);}.ui-media-card-side { grid-template-columns: var(--w, 200px) minmax(0, 1fr); }.ui-media-card-side-right { grid-template-columns: minmax(0, 1fr) var(--w, 200px); }.ui-media-card-side-right .ui-media-card-media { order: 1; }.ui-media-card-media { display: block; position: relative; overflow: hidden; aspect-ratio: var(--r, 16/9); background: var(--ui-subtle); color: inherit; }.ui-media-card-side .ui-media-card-media, .ui-media-card-side-right .ui-media-card-media { aspect-ratio: auto; min-height: 100%; }.ui-media-card-media img { display: block; width: 100%; height: 100%; object-fit: cover; }.ui-media-card-side .ui-media-card-media img, .ui-media-card-side-right .ui-media-card-media img { position: absolute; inset: 0; }.ui-media-card-body { display: grid; align-content: start; gap: 6px; padding: 16px 18px; }.ui-media-card-meta { margin: 0; font-size: 12px; font-weight: 500; letter-spacing: .02em; color: var(--ui-muted); }.ui-media-card-title { margin: 0; font-size: 17px; font-weight: 600; line-height: 1.3; }.ui-media-card-title a { color: inherit; text-decoration: none; }.ui-media-card-title a:hover { color: var(--ui-accent); }.ui-media-card-title a:focus-visible { outline: none; box-shadow: var(--ui-focus); border-radius: 3px; }.ui-media-card-text { margin: 0; color: var(--ui-muted); }.ui-media-card-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }@media (max-width: 520px) { .ui-media-card-side, .ui-media-card-side-right { grid-template-columns: 1fr; } .ui-media-card-side-right .ui-media-card-media { order: 0; } .ui-media-card-side .ui-media-card-media, .ui-media-card-side-right .ui-media-card-media { aspect-ratio: var(--r, 16/9); min-height: 0; } }`view MediaCard(title: str, src: str? = null, alt: str = "", media = null, text: str? = null, meta: str? = null,               href: str? = null, layout: str = "top", ratio: str = "16/9", width: str? = null, class: str? = null, children) {    style global media_card_css    let pic = media ?? (src != null ? <img src={src} alt={alt} loading="lazy"/> : null)    <article class={ui_cls(["ui-media-card", layout != "top" && `ui-media-card-${layout}`, class])}             style={`--r: ${ratio}${width != null ? `; --w: ${width}` : ""}`}>        {pic != null && (href != null            ? <a.ui-media-card-media href={href} tabindex="-1" aria-hidden="true">{pic}</a>            : <div.ui-media-card-media>{pic}</div>)}        <div.ui-media-card-body>            {meta != null && <p.ui-media-card-meta>{meta}</p>}            <h3.ui-media-card-title>{href != null ? <a href={href}>{title}</a> : title}</h3>            {text != null && <p.ui-media-card-text>{text}</p>}            {len(children) > 0 && <div.ui-media-card-actions>{children}</div>}        </div>    </article>}
Thread
66Useful? Like it, or add to its thread below.

Thread 0 posts

On MediaCard: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.