Blog: add quotes shortcode for testimonial cards - #3141
Merged
Conversation
Adds a quotes/quote shortcode pair that renders testimonial cards in a row with hairline dividers, matching the docs site's design language. When more quotes than fit one view are given, a scroll-snap carousel with arrow buttons and pagination dots takes over. A lone quote renders full width as a pull quote. Includes a draft test post with fictional companies and original SVG wordmarks so the component can be reviewed in the PR preview. Drop the test post before or at merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YWbYcGYDQESh3KWwpfoc4V
Contributor
PreviewPreview deployments for this PR have been cleaned up. |
Cards in a quotes row now adopt the track's explicit logo, quote, and attribution rows via CSS subgrid, so every attribution starts at the same line across a row and across carousel pages. Long quotes spill downward without clamping and short cards keep the open space. Attributions that wrap do so below the shared line. Browsers without subgrid fall back to the previous bottom-aligned flex layout. Subgrid is skipped below 600px where cards render one per view. Test post quotes now vary in length to show the behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YWbYcGYDQESh3KWwpfoc4V
The quotes row now bleeds horizontally past the post column, capped at 1200px and centered in the viewport, with a slice of the next card peeking at the right edge whenever more cards exist than fit one view. The bleed bound leaves a 32px gutter that absorbs the page scrollbar, so the page never scrolls horizontally. Carousel init now waits for DOMContentLoaded so every quotes block on a page gets its controls, and the arrow disabled state follows the actual scroll position. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YWbYcGYDQESh3KWwpfoc4V
PaperMod bolds figure > figcaption, which made the Name, Title line read too heavy. Override it to font-weight 400 so the attribution is a quiet secondary line, and drop the 600 weight on the no-logo company name fallback to match.
The figcaption now renders the name and title as separate block spans instead of one "Name, Title" line. The name is 700 in the body text color, the title stays 400 in the muted secondary color. A quote with no title renders the name alone. The test post gains a no-title variant to exercise that path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YWbYcGYDQESh3KWwpfoc4V
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YWbYcGYDQESh3KWwpfoc4V
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Den Delimarsky · Slack thread
Before / After
Before: customer quotes in blog posts render as plain markdown blockquotes, a gray bar with indented text.
After: quotes render as a horizontal strip of cards in the style of the claude.com blog. The strip breaks out of the post's text column and bleeds toward the viewport edges, up to 1200px wide, centered, three cards per view on desktop. Each card shows the company logo, the quote in body type wrapped in typographic quotation marks, and a two-line attribution with the person's name in bold and their title in muted regular type on the line below, with hairline dividers between cards. Quotes can run long: text spills downward without clamping, and every attribution in the set starts on the same line, sized by the longest quote, on every carousel page. Attributions that wrap do so below that shared line. When more quotes exist than fit one view, cards narrow slightly so a slice of the next card peeks at the right edge, and arrow buttons and pagination dots appear under the strip. A lone quote renders as a pull quote that stays in the text column. The strip shrinks back to the column width on narrow screens, with one card per view and a small peek on mobile, and everything follows the blog's light and dark themes.
Usage
Nothing to install. The two shortcodes and the stylesheet ship with the blog once this merges, and Hugo picks them up automatically.
To add quotes to a post:
index.mdinside, for exampleblog/content/posts/my-post/index.md.index.md. Bundle SVGs are inlined into the page, so wordmarks that usecurrentColoradapt to light and dark automatically.quotesblock where the cards should appear, with onequoteper card:{{< quotes >}} {{< quote name="Ada Person" title="CTO" company="Acme" logo="acme-logo.svg" >}} Quote text goes here. Markdown is supported. {{< /quote >}} {{< /quotes >}}Parameters on
quote:name: the person's name, shown in bold.title: the person's role, shown in muted type on its own line under the name. Optional. Without it the attribution is the name alone.company: the company name. It labels the logo for screen readers and renders as a text wordmark when nologois given.logo: filename of an SVG in the post's page bundle. A path outside the bundle still renders, but as a plainimgtag that keeps its hardcoded colors in both themes. Omit it to fall back to the text wordmark.Layout comes for free. Three cards per view on desktop, two at medium widths, one on mobile, with a slice of the next card peeking at the right edge when more quotes exist than fit. Arrow buttons and pagination dots appear only when the quotes overflow one view. A single quote renders as a full-width pull quote that stays in the text column.
How
Two Hugo shortcodes plus a stylesheet:
blog/layouts/shortcodes/quotes.htmlwraps the cards in a CSS scroll-snap track and carries a small vanilla JS snippet (emitted once per page, initialized after DOMContentLoaded so every quotes block on a page gets controls) that shows the arrows and dots only when the quotes overflow one view. No dependencies added.blog/layouts/shortcodes/quote.htmlrenders one card. SVG logos from the post's page bundle are inlined, socurrentColorwordmarks track the theme. Non-bundle paths fall back to animgtag, and omittinglogorenders the company name as a text wordmark.blog/assets/css/extended/quotes.cssstyles the cards with the blog's existing CSS variables. The breakout is symmetric negative margins on the strip, capped atmin((1200px - 100%) / 2, 50vw - 50% - 32px)per side. The 32px gutter absorbs the page scrollbar that100vwincludes, so the page never gains a horizontal scrollbar. The attribution start line is pure CSS: the track defines logo, quote, and attribution rows, and each card adopts them withgrid-template-rows: subgrid(Chrome and Edge 117+, Firefox 71+, Safari 16+). Browsers without subgrid fall back to the previous flex layout, where attributions align to the card bottom instead.Reviewers can see the component rendered in this PR's earlier preview deployments or by adding the shortcode block above to any post locally.
🤖 Generated with Claude Code
https://claude.ai/code/session_01YWbYcGYDQESh3KWwpfoc4V