Marketing

Testimonials

Quotes from people with their picture, role and company, in a row that scrolls or a grid that wraps.

#marketing#section#card#social#no-js

Demo

Preview
  • 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.qwe
main.qwe
import 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.

demos/testimonials.qwe
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

ui/testimonials.qwe
// 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>}
Thread
97Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.