Skip to content

feat(ui): add CTA card side column to blog posts - #79

Merged
matej-marcisovsky merged 1 commit into
strapi:mainfrom
PaulBratslavsky:feat/blog-sidebar-cta
Jul 31, 2026
Merged

feat(ui): add CTA card side column to blog posts#79
matej-marcisovsky merged 1 commit into
strapi:mainfrom
PaulBratslavsky:feat/blog-sidebar-cta

Conversation

@PaulBratslavsky

@PaulBratslavsky PaulBratslavsky commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What & why

Blog posts render as a single centred article, so the only promo surface is an inline section at the bottom — which readers reach only after finishing the piece. This adds a side column beside the article for marketing CTAs.

CleanShot 2026-07-30 at 16 09 09@2x

Content model

cards.cta-card — a generic, reusable component: title, optional image, description, one link, and a primary | secondary button style.

It is deliberately named for its structure, not a use case. An editor can make it a demo promo, a sales prompt, a webinar invite or a docs link with no schema change, and it can be dropped into any dynamic zone — not just the blog.

The link reuses the existing utilities.link, so a CTA points at either an internal page relation or an external URL with newTab — the same choice the hero section already offers, with the same conditional fields (href shows for external, the page picker for page).

Two-tier placement. sidebar is a repeatable cards.cta-card on both:

Location Role
api::blog.blog (Blog single type) blog-wide default shown on every post
api::blog-post.blog-post optional per-post override; empty = inherit

The single type is the primary home because the cards are evergreen. Re-adding them to every post by hand would not survive contact with a real content team, and new posts would otherwise publish with an empty column. A post that sets its own column replaces the default outright rather than merging — merge order would be ambiguous and invisible to the editor.

Layout

  • Article and column are centred as one group inside the standard page container, so the column's right edge lands on the same grid as the hero and navbar.
  • The share rail is now anchored to the article (left-full) instead of a viewport offset (left-[calc(50%+27rem+1rem)]) that only cleared above 1536px.
  • The column shows from xl and sticks on scroll, like the share rail.
  • Below xl the cards flow into the article width rather than vanishing or keeping the rail's narrow column.

Tradeoff: with a column present the article narrows from 864px to 616px — that is what lets four tracks fit the page grid. Posts without a sidebar are completely unchanged.

Testing

Verified locally against Strapi + Next: both link modes resolve correctly (type: page → the page's fullPath, type: external → the URL with target="_blank" rel="noopener noreferrer"), cards render beside the article and not below it, the image sits between heading and button, the column sticks through a long scroll, cards fall inline below xl, and there is no horizontal overflow at any breakpoint.

tsc --noEmit and ESLint are clean on all changed files.

Note for reviewers

apps/strapi/types/generated/* was hand-written in typegen's format because pnpm generate:types currently fails on main with e.charAt is not a function — it fails on a clean checkout too, so it predates this branch. Those files should be regenerated once that is fixed.

🤖 Generated with Claude Code

Blog posts rendered as a single centred article, so the only promo surface
was an inline section at the bottom that readers reach only after finishing
the piece. This adds a side column beside the article for marketing CTAs.

Content model:

- `cards.cta-card` is a generic, reusable component (title, optional image,
  description, one link, primary/secondary button). It is named for its
  structure rather than a use case, so an editor can make it a demo promo, a
  sales prompt or a docs link without a schema change, and it can be dropped
  into any dynamic zone.
- The link is the existing `utilities.link`, so a CTA points at either an
  internal page relation or an external URL with `newTab` — the same choice
  the hero section already offers, with the same conditional fields.
- `sidebar` is a repeatable `cards.cta-card` on both the Blog single type and
  Blog Post. The single type holds the blog-wide default; a post overrides it
  outright when it sets its own. Evergreen cards would not survive being
  re-added to every post by hand, and new posts would otherwise publish with
  an empty column.

Layout:

- Article and column are centred as one group inside the standard page
  container, so the column's right edge lands on the same grid as the hero
  and navbar. The share rail is now anchored to the article rather than a
  viewport offset that only cleared above 1536px.
- The column shows from xl and sticks on scroll like the share rail. Below
  xl the cards flow into the article width instead of vanishing or keeping
  the rail's narrow column.
- With a column present the article narrows from 864px to 616px, which is
  what lets four tracks fit the grid. Posts without a sidebar are unchanged.

Generated types were hand-written in typegen's format because
`strapi ts:generate-types` currently fails on main with "e.charAt is not a
function"; they should be regenerated once that is fixed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

@PaulBratslavsky is attempting to deploy a commit to the Strapi-Website Team on Vercel.

A member of the Team first needs to authorize it.

@PaulBratslavsky

Copy link
Copy Markdown
Contributor Author

@matej-marcisovsky thanks for the review. I don't have auth to merge or deploy, so if it looks good, can you push it. 🙂

@matej-marcisovsky
matej-marcisovsky merged commit b15b930 into strapi:main Jul 31, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants