From 08c4451d49e878f864b9eae2e6a03dfe583f2142 Mon Sep 17 00:00:00 2001
From: Hirotaka Mizutani <52546+hirotaka@users.noreply.github.com>
Date: Sat, 27 Jun 2026 19:15:11 +0900
Subject: [PATCH] feat(registry): Add testimonials block
---
app/components/demo/TestimonialsDemo.vue | 53 +++++++
.../demo/TestimonialsFallbackDemo.vue | 37 +++++
.../blocks/testimonials/Testimonials.vue | 129 ++++++++++++++++++
app/registry/blocks/testimonials/index.ts | 1 +
content/docs/2.components/13.testimonials.md | 116 ++++++++++++++++
scripts/registry-verify.ts | 4 +
6 files changed, 340 insertions(+)
create mode 100644 app/components/demo/TestimonialsDemo.vue
create mode 100644 app/components/demo/TestimonialsFallbackDemo.vue
create mode 100644 app/registry/blocks/testimonials/Testimonials.vue
create mode 100644 app/registry/blocks/testimonials/index.ts
create mode 100644 content/docs/2.components/13.testimonials.md
diff --git a/app/components/demo/TestimonialsDemo.vue b/app/components/demo/TestimonialsDemo.vue
new file mode 100644
index 0000000..7c6a220
--- /dev/null
+++ b/app/components/demo/TestimonialsDemo.vue
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
diff --git a/app/components/demo/TestimonialsFallbackDemo.vue b/app/components/demo/TestimonialsFallbackDemo.vue
new file mode 100644
index 0000000..d4f0f3b
--- /dev/null
+++ b/app/components/demo/TestimonialsFallbackDemo.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
diff --git a/app/registry/blocks/testimonials/Testimonials.vue b/app/registry/blocks/testimonials/Testimonials.vue
new file mode 100644
index 0000000..4d32e7d
--- /dev/null
+++ b/app/registry/blocks/testimonials/Testimonials.vue
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+ {{ title }}
+
+
+ {{ description }}
+
+
+
+
+
+
+
+ {{ item.quote }}
+
+
+
+
+
+
+ {{ initials(item.author.name) }}
+
+
+
+ {{ item.author.name }}
+
+
+ {{ item.author.role || item.author.description }}
+
+
+
+
+
+
+
+
diff --git a/app/registry/blocks/testimonials/index.ts b/app/registry/blocks/testimonials/index.ts
new file mode 100644
index 0000000..0ae312d
--- /dev/null
+++ b/app/registry/blocks/testimonials/index.ts
@@ -0,0 +1 @@
+export { default as Testimonials, type TestimonialAuthor, type TestimonialAvatar, type TestimonialItem, type TestimonialsProps } from "./Testimonials.vue";
diff --git a/content/docs/2.components/13.testimonials.md b/content/docs/2.components/13.testimonials.md
new file mode 100644
index 0000000..38e038f
--- /dev/null
+++ b/content/docs/2.components/13.testimonials.md
@@ -0,0 +1,116 @@
+---
+title: Testimonials
+description: Render a responsive social-proof section from app-owned testimonial data.
+category: marketing
+---
+
+::component-preview
+---
+name: TestimonialsDemo
+---
+::
+
+## Installation
+
+```bash
+npx shadcn-vue@latest add "https://ui.stackhacker.io/r/testimonials.json"
+```
+
+## Usage
+
+```vue
+
+
+
+
+
+```
+
+## App-Owned Testimonials
+
+`Testimonials` owns the section layout, quote cards, accessible author rendering, and simple avatar fallback. Your app owns testimonial collection, moderation, ordering, source mapping, avatar storage, image optimization, carousel behavior, analytics, and any CMS or API integration.
+
+Pass already-approved, display-ready testimonial items to the component. Keep remote fetching, caching, personalization, and rotation logic in app code so the registry item remains portable.
+
+## Examples
+
+### Default
+
+::component-preview
+---
+name: TestimonialsDemo
+---
+::
+
+### Without Avatars
+
+::component-preview
+---
+name: TestimonialsFallbackDemo
+---
+::
+
+```vue
+
+```
+
+## API Reference
+
+### Props
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `title` | `string` | `"What our customers say"` | Section heading shown above the testimonials. |
+| `description` | `string` | `"Real feedback from teams using this product to move faster."` | Optional supporting copy shown below the title. |
+| `items` | `TestimonialItem[]` | `[]` | Testimonials supplied by your app. |
+| `class` | `HTMLAttributes["class"]` | — | Additional CSS classes for the section. |
+
+### Item Type
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | `string` | Stable item id. Falls back to author name plus index when omitted. |
+| `quote` | `string` | Testimonial quote text. |
+| `author` | `TestimonialAuthor` | Person or organization credited for the quote. |
+
+### Author Type
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `name` | `string` | Author display name. |
+| `role` | `string` | Optional role, company, or byline. |
+| `description` | `string` | Optional legacy-friendly byline. Used when `role` is omitted. |
+| `avatar` | `TestimonialAvatar` | Optional avatar image supplied by your app. |
+
+### Avatar Type
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `src` | `string` | Avatar image source. |
+| `alt` | `string` | Optional alt text. Falls back to author name. |
+| `srcset` | `string` | Optional responsive source set. |
diff --git a/scripts/registry-verify.ts b/scripts/registry-verify.ts
index 7c60338..a6529d1 100644
--- a/scripts/registry-verify.ts
+++ b/scripts/registry-verify.ts
@@ -131,6 +131,10 @@ const expectedItems: Record = {
dependencies: [],
registryDependencies: [],
},
+ "testimonials": {
+ dependencies: [],
+ registryDependencies: [],
+ },
};
if (registryIndex) {