A kitchen for four
Light, quiet, two minutes from the metro.
A card with a picture (or any media) on top or at the side, then a meta line, title, text and actions.
Light, quiet, two minutes from the metro.
A desk, a view, and the best coffee on the street.
A talk from QuilloConf on building interfaces the browser already knows how to run.
The media slot takes any HTML: here, a chart drawn in SVG.
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.qweimport ui/theme.qwe use theme_cssuse ui/media_card.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>The code of the preview above.
// 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>// 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>}On MediaCard: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.