Simple left aligned
A square photo of the person beside a rated quote.
Last updated
---
/**
* Simple left aligned: A square photo of the person beside a rated quote.
*
* @block testimonials
* @order 3
*/
import Section from '@/components/apogee/Section/Section.astro'
import Image from '@/components/apogee/Image/Image.astro'
import Rating from '@/components/apogee/Rating/Rating.astro'
import type { TestimonialsContent } from '@/components/apogee/sections/_shared/testimonials.ts'
/** @composable */
interface Props extends TestimonialsContent {}
const {
items = [{ quote: 'Every page we build now looks like it belongs together. Our designers finally trust what ships.', name: 'Daniel Reyes', role: 'Engineering Lead, Quanta', avatar: 'https://picsum.photos/id/91/656/656', rating: 5 }],
} = Astro.props
---
<Section spacing="large">
{items.slice(0, 1).map((t) => (
<figure class="grid items-center gap-8 lg:grid-cols-[auto_1fr] lg:gap-24">
{t.avatar && <Image src={t.avatar} alt="" width={656} height={656} fit="cover" radius="xl" class="size-50 md:size-82" />}
<div class="flex flex-col gap-6 md:gap-8">
{t.rating != null && <Rating value={t.rating} readonly size="small" color="warning" aria-label={`Rated ${t.rating} out of 5`} />}
<blockquote class="text-2xl font-medium tracking-tight text-balance md:text-4xl"><p>“{t.quote}”</p></blockquote>
<figcaption class="flex flex-col gap-1"><span class="flex flex-col"><span class="flex items-center gap-1 font-semibold">— {t.name}</span>{t.role && <span class="text-sm text-neutral-faded">{t.role}</span>}</span></figcaption>
</div>
</figure>
))}
</Section>
Show codeHide code
---
/**
* Simple left aligned: A square photo of the person beside a rated quote.
*
* @block testimonials
* @order 3
*/
import Section from '@/components/apogee/Section/Section.astro'
import Image from '@/components/apogee/Image/Image.astro'
import Rating from '@/components/apogee/Rating/Rating.astro'
import type { TestimonialsContent } from '@/components/apogee/sections/_shared/testimonials.ts'
/** @composable */
interface Props extends TestimonialsContent {}
const {
items = [{ quote: 'Every page we build now looks like it belongs together. Our designers finally trust what ships.', name: 'Daniel Reyes', role: 'Engineering Lead, Quanta', avatar: 'https://picsum.photos/id/91/656/656', rating: 5 }],
} = Astro.props
---
<Section spacing="large">
{items.slice(0, 1).map((t) => (
<figure class="grid items-center gap-8 lg:grid-cols-[auto_1fr] lg:gap-24">
{t.avatar && <Image src={t.avatar} alt="" width={656} height={656} fit="cover" radius="xl" class="size-50 md:size-82" />}
<div class="flex flex-col gap-6 md:gap-8">
{t.rating != null && <Rating value={t.rating} readonly size="small" color="warning" aria-label={`Rated ${t.rating} out of 5`} />}
<blockquote class="text-2xl font-medium tracking-tight text-balance md:text-4xl"><p>“{t.quote}”</p></blockquote>
<figcaption class="flex flex-col gap-1"><span class="flex flex-col"><span class="flex items-center gap-1 font-semibold">— {t.name}</span>{t.role && <span class="text-sm text-neutral-faded">{t.role}</span>}</span></figcaption>
</div>
</figure>
))}
</Section>Add
npx @ava-ui/apogee add testimonials/simple-left-aligned
This copies the block to src/components/apogee/blocks/testimonials/ with everything it uses: Image, Rating, Section. Every piece of copy is a prop, with the sample copy as its default. Pass yours, or null to hide a part; see copy is props.
Accessibility
- The stars are read as one rating (“Rated 5 out of 5”).
- Photos that only decorate have an empty
alt; give yours one when it carries meaning.
Related
TestimonialsOne featured quote, or a grid of quote cards.
CarouselA horizontal scroll-snap track for browsing slides. Native scroll and swipe work with zero JS; arrows, dots, and autoplay are progressive enhancement.
RatingA star rating on native radios — pick a value on a scale, or display one read-only.