Testimonials
Quotes from people with their picture, role and company, in a row that scrolls or a grid that wraps.
Demo
We replaced three services and a build pipeline with one file. The team shipped the next week.
Ada LovelaceCTO, Analytical The first web framework my team did not fight. Views are functions; that is the whole trick.
Grace HopperEngineering lead, Compiler Co. Live queries did in an afternoon what our socket layer took a quarter to get wrong.
Linus BenedictFounder
Loved by small teams
We replaced three services and a build pipeline with one file. The team shipped the next week.
Ada LovelaceCTO, Analytical The first web framework my team did not fight. Views are functions; that is the whole trick.
Grace HopperEngineering lead, Compiler Co. Live queries did in an afternoon what our socket layer took a quarter to get wrong.
Linus BenedictFounder Small enough to read, fast enough not to think about.
Margaret HamiltonApollo Labs
A row scrolls sideways when it runs out of room; a grid wraps.
Get it
It needs theme.qwe, as every component does, and avatar.qwe; 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/avatar.qwe$ curl -O https://quillolang.org/components/ui/testimonials.qweimport ui/theme.qwe use theme_cssuse ui/testimonials.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
let testimonials_demo_items = [ { quote: "We replaced three services and a build pipeline with one file. The team shipped the next week.", name: "Ada Lovelace", title: "CTO", company: "Analytical", href: "#testimonials", rating: 5 }, { quote: "The first web framework my team did not fight. Views are functions; that is the whole trick.", name: "Grace Hopper", title: "Engineering lead", company: "Compiler Co.", rating: 5 }, { quote: "Live queries did in an afternoon what our socket layer took a quarter to get wrong.", name: "Linus Benedict", title: "Founder", rating: 4 }, { quote: "Small enough to read, fast enough not to think about.", name: "Margaret Hamilton", company: "Apollo Labs" },]view TestimonialsDemo() = <div.demo-stack> <Testimonials id="testimonials-demo-row" items={take(testimonials_demo_items, 3)}/> <Testimonials title="Loved by small teams" layout="grid" variant="plain" items={testimonials_demo_items}/> <p.demo-note>A row scrolls sideways when it runs out of room; a grid wraps.</p> </div>Source
// Testimonials: what people say, as quotes with who said them, in a row or a// grid. Each is a <figure> with the quote in a <blockquote> and the person in// its caption. It draws the person with Avatar, so the page needs ui/avatar.qwe too.//// <Testimonials items={[// { quote: "We replaced three services with one file.", name: "Ada Lovelace", title: "CTO", company: "Analytical", src: "/img/ada.jpg" },// { quote: "The first framework my team did not fight.", name: "Grace Hopper", title: "Engineering lead", rating: 5 },// ]}/>// <Testimonials title="Loved by small teams" layout="grid" items={quotes}/>//// items: a list of { quote, name, title?, company?, src?, rating?, href? }. src:// the person's picture (their initials without one). rating: 1 to 5, drawn as// stars and said to screen readers. href: where the company's name links.// layout: "row" (default: side by side, scrolling sideways when they do not fit)// or "grid" (as many columns as fit, wrapping). title: a centred heading above.// variant: "cards" (default) or "plain", without the boxes.import ui/avatar.qwe use Avatarimport ui/theme.qwe use ui_clslet testimonials_css = css`.ui-testimonials { display: grid; gap: 24px; font: 15px/1.55 var(--ui-font); color: var(--ui-fg); }.ui-testimonials-title { margin: 0; font-size: clamp(24px, 3.4vw, 32px); font-weight: 700; line-height: 1.2; letter-spacing: -.015em; text-align: center; }.ui-testimonials-items { display: grid; gap: 20px; margin: 0; padding: 0; list-style: none; }.ui-testimonials-row .ui-testimonials-items { grid-auto-flow: column; grid-auto-columns: minmax(280px, 1fr); overflow-x: auto; scroll-snap-type: x proximity; padding-bottom: 6px; scrollbar-width: thin; }.ui-testimonials-row .ui-testimonial { scroll-snap-align: start; }.ui-testimonials-grid .ui-testimonials-items { grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); }.ui-testimonial { display: flex; flex-direction: column; gap: 16px; min-width: 0; margin: 0; }.ui-testimonials-cards .ui-testimonial { padding: 22px 24px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); }.ui-testimonial-stars { display: inline-flex; gap: 2px; color: var(--ui-warning); }.ui-testimonial-stars svg { width: 16px; height: 16px; }.ui-testimonial-quote { flex: 1; margin: 0; font-size: 16px; }.ui-testimonial-quote p { margin: 0; }.ui-testimonial-quote p::before { content: "“"; }.ui-testimonial-quote p::after { content: "”"; }.ui-testimonial-who { display: flex; align-items: center; gap: 12px; }.ui-testimonial-name { display: block; font-weight: 600; }.ui-testimonial-role { display: block; color: var(--ui-muted); font-size: 13px; }.ui-testimonial-role a { color: inherit; }`view Testimonials(items: list, title: str? = null, layout: str = "row", variant: str = "cards", id: str? = null, class: str? = null) { style global testimonials_css <section id={id} class={ui_cls(["ui-testimonials", `ui-testimonials-${layout}`, variant == "cards" && "ui-testimonials-cards", class])}> {title != null && <h2.ui-testimonials-title>{title}</h2>} <ul.ui-testimonials-items> {for t in items => <li><figure.ui-testimonial> {t.rating != null && <span.ui-testimonial-stars role="img" aria-label={`${t.rating} out of 5 stars`}> {for i in range(5) => <svg viewBox="0 0 24 24" fill={i < t.rating ? "currentColor" : "none"} stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" aria-hidden="true"> <path d="M12 2.8l2.8 6 6.5.8-4.8 4.5 1.3 6.5L12 17.4l-5.8 3.2 1.3-6.5L2.7 9.6l6.5-.8z"/> </svg>} </span>} <blockquote.ui-testimonial-quote><p>{t.quote}</p></blockquote> <figcaption.ui-testimonial-who> <Avatar name={t.name ?? ""} src={t.src}/> <span> <span.ui-testimonial-name>{t.name}</span> {(t.title != null || t.company != null) && <span.ui-testimonial-role> {t.title}{t.title != null && t.company != null && ", "} {t.company != null && (t.href != null ? <a href={t.href}>{t.company}</a> : t.company)} </span>} </span> </figcaption> </figure></li>} </ul> </section>}Related
Thread 0 posts
On Testimonials: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.