Marketing

FeatureGrid

A grid of a product's features, each an icon, a title and a sentence, from a list or written by hand (Feature).

#marketing#section#grid#card#no-js

Demo

Preview

Everything in one file

What you would otherwise assemble from a dozen packages.

No build step

Save the file and the page is there. Nothing to install but one binary.

Safe by default

Escaped HTML, parameterised SQL, records checked at the door.

Live data

A query the page follows as rows change, with nothing to wire up.

Components

Views on the server, components in the browser, one language for both.

A database inside

SQLite built in, Postgres and MySQL a connection string away.

One file to deploy

Copy the binary and the program; it serves, it watches, it reloads.

Fast

Pages in well under a millisecond, components resumed, not rebuilt.

Small

A 10 KB runtime, and only the components a page shows.

Checked

Types, effects and SQL looked over before the program runs.

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/feature_grid.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/feature_grid.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/feature_grid.qwe
let feature_grid_demo_items = [    { icon: "⚡", title: "No build step", text: "Save the file and the page is there. Nothing to install but one binary." },    { icon: "🔒", title: "Safe by default", text: "Escaped HTML, parameterised SQL, records checked at the door." },    { icon: "📡", title: "Live data", text: "A query the page follows as rows change, with nothing to wire up.", href: "#feature_grid" },    { icon: "🧩", title: "Components", text: "Views on the server, components in the browser, one language for both." },    { icon: "🗄️", title: "A database inside", text: "SQLite built in, Postgres and MySQL a connection string away." },    { icon: "🚀", title: "One file to deploy", text: "Copy the binary and the program; it serves, it watches, it reloads." },]view FeatureGridDemo() =    <div.demo-stack>        <FeatureGrid id="feature-grid-demo" title="Everything in one file" text="What you would otherwise assemble from a dozen packages."                     features={feature_grid_demo_items}/>        <FeatureGrid columns={3} variant="cards" align="center">            <Feature title="Fast" icon={<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/></svg>}>                Pages in well under a millisecond, components resumed, not rebuilt.            </Feature>            <Feature title="Small" icon={<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M9 12h6"/></svg>}>                A 10 KB runtime, and only the components a page shows.            </Feature>            <Feature title="Checked" icon={<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>}>                Types, effects and SQL looked over before the program runs.            </Feature>        </FeatureGrid>    </div>

Source

ui/feature_grid.qwe
// FeatureGrid: what a product does, as a grid of short items, each with an icon,// a title and a sentence or two. Feature is one item, for a grid written by hand.////   <FeatureGrid title="Everything in one file" features={[//       { icon: "⚡", title: "No build step", text: "Save the file and the page is there." },//       { icon: "🔒", title: "Safe by default", text: "Escaped HTML, parameterised SQL, checked records." },//       { icon: "📡", title: "Live data", text: "A query the page follows as rows change.", href: "/docs/live" },//   ]}/>//   <FeatureGrid columns={2}>//       <Feature icon={<svg>...</svg>} title="Fast">Pages in under a millisecond.</Feature>//   </FeatureGrid>//// FeatureGrid: features, a list of { icon?, title, text, href? }, or Feature// children. title and text: a heading and a line above the grid, centred.// columns: a fixed number (three or more become two under 900px, one under 560px);// without it, as many as fit at min pixels wide (260). variant: "plain" (default)// or "cards", each item in a bordered box. align: "start" (default) or "center",// the items' text.// Feature: icon (HTML, or a short text such as an emoji; none for no icon), title,// text or children, href (the title becomes a link, with an arrow).import ui/theme.qwe use ui_clslet feature_grid_css = css`.ui-feature-grid { display: grid; gap: 28px; font: 15px/1.55 var(--ui-font); color: var(--ui-fg); }.ui-feature-grid-head { display: grid; gap: 8px; max-width: 640px; margin: 0 auto 8px; text-align: center; }.ui-feature-grid-title { margin: 0; font-size: clamp(24px, 3.4vw, 32px); font-weight: 700; line-height: 1.2; letter-spacing: -.015em; }.ui-feature-grid-text { margin: 0; color: var(--ui-muted); font-size: 17px; }.ui-feature-grid-items { display: grid; gap: 28px 24px; grid-template-columns: repeat(auto-fill, minmax(min(var(--ui-feature-min, 260px), 100%), 1fr)); }.ui-feature-grid-cols .ui-feature-grid-items { grid-template-columns: repeat(var(--ui-feature-cols), minmax(0, 1fr)); }.ui-feature { display: grid; gap: 8px; align-content: start; min-width: 0; }.ui-feature-grid-center .ui-feature { justify-items: center; text-align: center; }.ui-feature-grid-cards .ui-feature { padding: 22px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); }.ui-feature-icon { display: grid; place-items: center; width: 44px; height: 44px; margin-bottom: 4px; border-radius: var(--ui-radius-sm);  background: var(--ui-accent-soft); color: var(--ui-accent); font-size: 22px; line-height: 1; }.ui-feature-icon svg { width: 22px; height: 22px; }.ui-feature-title { margin: 0; font-size: 17px; font-weight: 600; line-height: 1.3; }.ui-feature-title a { color: inherit; text-decoration: none; border-radius: 3px; }.ui-feature-title a:hover { color: var(--ui-accent); }.ui-feature-title a:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-feature-title a::after { content: " →"; color: var(--ui-accent); }.ui-feature-text { margin: 0; color: var(--ui-muted); }.ui-feature-text > :first-child { margin-top: 0; }.ui-feature-text > :last-child { margin-bottom: 0; }@media (max-width: 899px) { .ui-feature-grid-many .ui-feature-grid-items { grid-template-columns: repeat(2, minmax(0, 1fr)); } }@media (max-width: 559px) { .ui-feature-grid-cols .ui-feature-grid-items { grid-template-columns: minmax(0, 1fr); } }`view Feature(title: str, icon = null, text: str? = null, href: str? = null, class: str? = null, children) {    style global feature_grid_css    <div class={ui_cls(["ui-feature", class])}>        {icon != null && <div.ui-feature-icon aria-hidden="true">{icon}</div>}        <h3.ui-feature-title>{href != null ? <a href={href}>{title}</a> : title}</h3>        {(text != null || children != null) && <div.ui-feature-text>{text}{children}</div>}    </div>}view FeatureGrid(features: list = [], title: str? = null, text: str? = null, columns: int? = null, min: int = 260,                 variant: str = "plain", align: str = "start", id: str? = null, class: str? = null, children) {    style global feature_grid_css    <section id={id} class={ui_cls(["ui-feature-grid", columns != null && "ui-feature-grid-cols", columns != null && columns > 2 && "ui-feature-grid-many",                                    variant == "cards" && "ui-feature-grid-cards", align == "center" && "ui-feature-grid-center", class])}             style={columns != null ? `--ui-feature-cols: ${columns}` : `--ui-feature-min: ${min}px`}>        {(title != null || text != null) && <div.ui-feature-grid-head>            {title != null && <h2.ui-feature-grid-title>{title}</h2>}            {text != null && <p.ui-feature-grid-text>{text}</p>}        </div>}        <div.ui-feature-grid-items>            {for f in features => <Feature icon={f.icon} title={f.title} text={f.text} href={f.href}/>}            {children}        </div>    </section>}
Thread
74Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.