Standards

    Blog pattern

    A shared post list and post-detail pattern that covers both tech/how-to guides and rescue-story content with real photos.

    Post list

    One post is marked featured and gets a hero card; the rest render in a filterable grid. Categories, colors, and the subscribe CTA are all site-supplied data.

    Featured

    🐶
    Guides

    Two Free Tools That Make Rescue Social Media Easier

    A practical, beginner-friendly setup for creating and scheduling adoption posts without a marketing background.

    2026-03-231 min readBy 4leggedIT
    Read article
    A rescued dog resting on a porch
    Rescue Stories

    Luna's Journey Home

    Found shivering under a porch in December, Luna spent four months in foster care before finding her forever family.

    2026-04-021 min readBy Foster Coordinator
    Read article
    🚗
    Volunteer Spotlight

    Volunteer Spotlight: Maria's Five Years of Transport Runs

    Maria has driven over 400 transport legs connecting shelters to foster homes across three counties.

    2026-02-101 min readBy Volunteer Team
    Read article

    More posts coming soon

    Occasional emails only — no noise, no spam.

    Subscribe for updates

    Post detail

    The full post, rendered with the same mini-markdown grammar used by every post's content: headings, lists, blockquotes, code blocks, links, and bold text.

    Back to blog
    Rescue Stories

    Luna's Journey Home

    2026-04-02Foster Coordinator · Example Rescue1 min read
    A rescued dog resting on a porch

    When a neighbor first spotted Luna, she was too scared to come near anyone.

    It took three weeks of quiet visits from our stray team before she trusted a hand reaching out.

    Foster Care Made the Difference

    Luna spent four months with a foster family learning that people could be safe.

    • Weekly vet check-ins
    • Slow, patient socialization
    • A foster home with another calm dog to learn from

    By month three, she was greeting visitors at the door instead of hiding.

    Luna was adopted in April by a family who had been waiting for exactly her kind of quiet, gentle energy.

    Want to talk to us?

    This closing CTA card is fully optional — omit the `cta` prop entirely to render nothing.

    Get in touch

    Homepage highlight

    Flag a post with highlightOnHome in its data, select it with getBlogHighlightItems() (exported from `BlogSection.tsx`), and render it via the same shared HomeHighlightSection component News/Events uses on the homepage — see the full write-up and a live Blog example on the Home Highlight pattern page.

    Standard

    - `category` is a plain, site-local string — never a fixed union. Pass a `categories` config with `{ value, label, colorClassName }` for badge/pill styling, or omit it entirely and the component auto-derives title-cased labels.

    - `category` is optional per post — no badge renders when it's absent.

    - `imageSrc`/`imageAlt` give a real photo on the card and detail header; `emoji` is a lighter-weight decorative fallback when there's no photo. Neither is required.

    - `readTime` is auto-computed from word count (~200wpm) via `estimateReadTime()` when omitted — set it explicitly only when a site wants an editorial override.

    - `showFeatured` renders a hero card for one post: an explicit `featuredPostId`, else the newest post flagged `featured: true`, else the newest post overall.

    - Cards only link out when `postBasePath` is set — omit it to render an unlinked teaser (e.g. a homepage preview with no dedicated blog section yet).

    - The content grammar (`## heading`, `> quote`, `- list`, fenced ``` code blocks, inline `code`/`[link](url)`/`**bold**`) lives entirely inside `BlogPostDetail.tsx` — same self-contained-renderer approach as `EventsNewsDetail.tsx`, not a shared `lib/` markdown utility.

    - `subscribeCta` (list) and `cta` (detail) are both fully optional call-outs — a site without a newsletter or a sales CTA simply omits the prop.

    - `getRelatedBlogPosts(posts, currentSlug, limit?)` picks the newest other posts, excluding the current slug — pass its result as `relatedPosts` rather than re-implementing the pick logic per site.

    - For a post detail page's Previous/Next navigation, compute both neighbors with `getAdjacentBlogPosts(posts, currentSlug, postBasePath)` (exported from `BlogSection.tsx`) and pass the result straight to `BlogPostDetail`'s `previous`/`next` props — do not reimplement chronological-adjacency logic per site. Either side renders nothing when there's no neighbor; distinct from `relatedPosts` (topical, same-category picks) rather than a replacement for it — both can render together.

    - `highlightOnHome?: boolean` on `BlogPostEntry` flags a post for the shared homepage highlight card — see the "Homepage highlight" example above and the Home Highlight pattern page for the full contract.

    - SEO (`SEOHead`/`StructuredData` — `CollectionPage` for the list, `BlogPosting` for a post) and prerender route generation stay page-level, same as every other pattern.

    - Component: `template/src/components/patterns/BlogSection.tsx` + `BlogPostDetail.tsx`