FeatureGrid
A grid of a product's features, each an icon, a title and a sentence, from a list or written by hand (Feature).
Demo
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.qwemain.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>}Related
TeamGridThe team as a grid of people: picture or initials, name, role, a line about them and links.6 views · 8 downloadsTestimonialsQuotes from people with their picture, role and company, in a row that scrolls or a grid that wraps.9 views · 7 downloadsBentoGridA grid of tiles spanning columns and rows, with titles, tints and links, packed to leave no holes.9 views · 6 downloadsBlogListPosts as cards in a grid or rows down the page: picture, tags, title, excerpt, author and date.8 views · 7 downloadsCtaSectionA call-to-action band: a title, a line and the buttons, filled, tinted or outlined, centred or split.7 views · 6 downloadsFeatureListFeatures in alternating rows of words and a picture (or code), with ticked points and a link each.7 views · 6 downloads
Thread
Thread 0 posts
On FeatureGrid: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.