Skip to main content

Card

Released in version 1.31.0

A container block that behaves like a Group, plus one switch: Link This Card. With it on, the card renders as a single <a> element wrapping all of its content, so every part of the card — the image, the padding, the gap between two lines — is the link.

Two cards on the front end, the left one with a focus ring around its whole edge

Keyboard focus on the left card. The ring traces the whole card, because the whole card is the link. Its hover treatment — the lift, the tint, the arrow sliding right — is the theme's, not the plugin's.

Which card do I want?

Yoko Core has two ways to make a card clickable, and they are not interchangeable.

Card blockGroup + stretched link
Markupone <a> around the whole carda <div> with an invisible overlay on one link inside it
Accessible namea block inside the card, or the label you writethe link's own text
Other links in the cardignored — see belowkeep working
Copy link addressworks anywhere on the cardonly on the link
Styling hookthe card element itself.yoko-link-container
Best fora card with exactly one destinationa card with a headline link and other links beside it

Rule of thumb: one destination, use a Card. More than one, use a Group with a stretched link.

For editors

Insert Card from the inserter, then build inside it exactly as you would in a Group — any blocks, any layout, all the same colour, border, spacing and layout controls.

The Card Link panel with Link This Card switched off
Off, which is the default. The card is a container and everything inside it behaves normally.

The toggle is in the Card Link panel in the block inspector. The chain-link button in the block toolbar does the same thing, and opens the link popover a Button block uses — search for a page, or paste a URL.

The Card Link panel switched on, showing URL, Open in new tab, Screen reader label, Link rel and Link title attribute
On, with a destination set. Every option a Button offers, plus the screen reader label that only a card needs.

Turning it on gives the card the options a Button or a Link already has:

OptionWhereWhat it does
Link topanelCustom URL, or The post for a Query Loop — see below.
URLpanel, toolbar popoverWhere the card goes, in Custom URL mode. Until this is set, the card is still a plain container.
Open in new tabpanel, toolbar popoverAdds target="_blank", plus rel="noreferrer noopener" and a warning for screen readers.
Mark as nofollowtoolbar popoverAdds nofollow to rel.
Screen reader labelpanelWhat a screen reader announces for the card — on its own, or as a prefix in front of the name the card takes from its own content. See below.
Link relpanelAnything else the rel attribute needs.
Link title attributepanelThe title attribute. Rarely useful; a tooltip is not an accessible label.

In a Query Loop

The Card Link panel with Link to set to The post, and no URL field
In this mode there is no URL to fill in, and the screen reader label warning is gone — each card is announced as its own post title.

Set Link to to The post and each card links to the post it is rendering. Nothing stores a URL: the permalink is read from the loop's block context when the page renders, the same way the Post Title, Read More and Featured Image blocks resolve theirs.

Two things follow from that, both of them the point:

  • The accessible name defaults to the post's own title. One label written by hand would be announced identically on every card in the loop, which is worse than no label. Anything you do type in Screen reader label goes in front of the title rather than replacing it, so "Read more about" gives you "Read more about Members only briefing" — different on every card, and still saying what the link does.
  • Outside a loop it renders as a plain container. There is no post in context on, say, a normal page, so there is nowhere to go — and the links inside the card keep working rather than being swallowed by an anchor that goes nowhere.
Why not Block Bindings?

Because a third-party block cannot use them. WP_Block::process_block_bindings() gates bindings on a hardcoded list of four core blocks — Paragraph, Heading, Image and Button — with no filter and no block.json opt-in, and neither of core's two binding sources (core/post-meta, core/pattern-overrides) yields a permalink in any case. Block context is the mechanism core itself uses for this, and it works today.

If the URL you need lives in a custom field, that is bindings territory, and the way to get it today is a Group with a stretched link on a Button: core/button is on that allowlist, so its URL can be bound to core/post-meta.

Naming the card after its own content

A card link needs a name, and typing one into the sidebar is fine for a card you write once. It stops being fine the moment the card is reused: in a pattern, the heading and the label are the same sentence stored twice, and whoever drops the pattern onto a page and rewrites the heading has no reason to know the second copy exists. The card then announces the old text.

So the name can come from the card's content instead. Select a block inside the card and switch on Name the card after this block, in the Yoko Card panel.

The Yoko Card panel on a heading, with Name the card after this block switched on
On the heading inside the card. The help text says what the card will be announced as.

From then on the card is announced with that block's text, and there is only one copy of it — rewrite the heading and the announcement changes with it. The card's own Card Link panel confirms what a visitor will hear.

The Card Link panel reporting that the card is announced as Membership benefits
The same card, from the card's side. The "no screen reader label" warning is gone, because the card has a name.

The blocks that can be marked are Heading, Paragraph, Post Title, Button and Site Title. Three rules, all of them visible in the editor:

  • The label field becomes a prefix. Mark the heading and type "Read more about" and the card is announced "Read more about Membership benefits". Leave the field empty and it is announced as the heading alone.
  • The first marked block wins, in the order the blocks appear. Marking a second one is not an error, but the panel on that block says it is not the one being used.
  • A mark inside a nested card belongs to that card, not to the one around it.

In a Query Loop this is how you name each card after something other than the post title — mark a Post Title block and it resolves per post exactly as the link does, or mark a Heading holding fixed text if that is what the design wants.

Building a pattern

Mark the heading, leave Screen reader label empty, and the pattern needs no accessibility instructions in its description. Whoever uses it writes the heading, which is the one thing they were always going to do.

An HTML link cannot contain another link — the browser closes the first one as soon as it meets the second, which would cut the card link short. So while Link This Card is on:

  • Every link inside the card is rendered as plain text on the site. A button keeps its button styling; it just stops being clickable.
  • The link options on the blocks inside are switched off in the editor, and an Image inside the card shows as unlinked, matching what the visitor will get.
  • Nothing is deleted. Turn Link This Card off and every inner link is back.

The Image block's toolbar, inside a linked card and outside one — the link control is the only thing that changes:

An image block toolbar with no link button

Inside a linked card. The chain-link control is gone.

The same toolbar with a link button between the duotone and caption controls

The same image in a card that is not a link. Everything else is identical.

A Yoko Card panel on the image block explaining that its link options are switched off
Selecting a block inside a linked card says why its link options are missing, and what to change to get them back.

The blocks whose link options are switched off are Image, Button, Media & Text, Gallery, Post Title, Featured Image, Excerpt, Read More, Site Logo and Site Title. Whatever that list says, every link inside a linked card is ignored on the site — the list only decides which controls the editor hides.

Warnings the panel shows

The Card Link panel showing three warnings beneath the options
All three at once: a link inside the card, no screen reader label, and an image carrying alt text that would be read as part of the link.
WarningWhereMeaning
No URL yetthe cardThe toggle is on but there is nowhere to go, so the card is still a container and its inner links still work.
N links inside this card are ignoredthe cardExpected, and worth a second look: if you need those links, use a Group with a stretched link instead.
No screen reader labelthe cardThe card link will be announced as every word inside it. Write a short label, or mark a block inside the card as its name.
Announced as "…"the cardNot a warning: the card has a name, taken from the marked block, and this is what a visitor will hear.
N images in this card have alt textthe cardAlt text is read out as part of the card link. Clear it when the image is decorative.
N blocks in this card are marked as its namea marked blockTwo or more blocks are marked. The first one wins; this one is not it.
Its own link options are switched offa block insideThe card is the link, so this block's own link is ignored.

A linked card is outlined with a dashed border in the editor canvas, so the click area is visible without selecting it.

A card in the editor canvas with a dashed outline around its whole edge

The dashed outline is editor-only and never renders on the site. It is hidden on the card you have selected, so click elsewhere to see it.

Accessibility

Three things make the difference between a card link and a card link a screen reader user can stand.

  1. Give the card a name. Without one, the accessible name of the link is every word in the card, read as one run-on link. "Read the annual report" is a name; the whole card is not. Either write it in Screen reader label or mark a block inside the card as the card's name — the second is what a pattern or a Query Loop wants, because it cannot go stale.
  2. Empty the alt text on decorative images. An image inside the anchor contributes its alt text to the link name. A card image is almost always decorative — the heading already says what the card is.
  3. Do not put anything else interactive in a linked card. Buttons, form fields and other links are invalid inside a link, which is why they are neutralized. If the card needs them, it needs a stretched link instead.

Keyboard focus lands on the card as a whole, so the focus ring traces the whole card. That is correct: the ring should match what a click will do.

Styling the card

Yoko Core ships the mechanic and no design. Two classes, both on the card element:

ClassOn
wp-block-yoko-cardevery card
yoko-card--is-linka card that rendered as a link

The only rules the plugin applies to a linked card are mechanical: display: block (an anchor is inline by default), and color: inherit with text-decoration: none, without which the theme's link colour and underline would repaint every word in the card.

Because the card is the link, one transition on the card element covers everything inside it — no overlay, no JavaScript:

.wp-block-yoko-card {
transition:
background-color 250ms ease,
box-shadow 250ms ease,
transform 250ms ease;
}

.yoko-card--is-link:hover,
.yoko-card--is-link:focus-visible {
box-shadow: 0 14px 28px rgba( 15, 23, 42, 0.18 );
transform: translateY( -6px );
}

.yoko-card--is-link:active {
transform: translateY( -1px );
transition-duration: 80ms;
}

The Cards demo page showing linked cards and a stretched-link card

The Cards page on the demo site: the Card block and a Group with a stretched link, set up the same way so the two can be compared.

:hover, :focus-visible and :active all apply to the card itself, and descendant selectors reach anything inside it — .yoko-card--is-link:hover img works. The stretched link docs show the same three states on a container that is not itself a link.

A neutralized inner link gets yoko-card__link-ignored alongside the classes it already had, if a theme needs to style it differently from a real link.

tip

The Cards page on the Playground demo site shows these three states side by side with the stretched-link version of the same card. The transitions there come from the demo site, not from the plugin.

Block editor, classic editor and page builders

SurfaceBlock themes / GutenbergClassic editor / Beaver Builder
The blockIn the inserter under DesignNot available — blocks are not registered in the classic editor, and Beaver Builder has no Card module
The equivalentCard block, or Group + stretched linkA Beaver Builder row or column with its own link, or the theme's card markup
Front-end classeswp-block-yoko-card, yoko-card--is-linknone — the block does not render
warning

This is a block. A post type a page builder has taken over cannot use it. For those layouts, link the row or module in Beaver Builder and style .fl-row a / .fl-col a in the theme.

For developers

Markup

An unlinked card:

<div class="wp-block-yoko-card">…inner blocks…</div>

A linked card:

<a class="wp-block-yoko-card yoko-card--is-link"
href="/reports/annual/"
aria-label="Read the annual report">…inner blocks…</a>

The block declares usesContext: [ "postId", "postType" ], which is what makes The post mode possible: a block only receives the context it asks for.

The block is server rendered, so the wrapper is built in PHP and only the inner blocks are stored in the post. Switching between a div and an a is therefore not a markup change the editor has to be taught about, and the anchor's escaping lives next to the pass that neutralizes the links inside it.

Attributes

AttributeTypeDefaultRenders as
isLinkbooleanfalsean <a> element, when url is also set
linkTostringcustomcustom, or post for the permalink from block context
urlstringhref, in custom mode
linkTargetstringtarget, and rel="noreferrer noopener"
relstringrel
linkTitlestringtitle
linkLabelstringaria-label, or its first half — see below
tagNamestringdivthe element an unlinked card renders as
anchorstringid, on either shape of card

tagName accepts div, section, article, aside, header and footer; anything else falls back to div. A linked card is always an <a>.

The aria-label is composed rather than stored: linkLabel is an optional prefix, followed by the first name source that yields anything.

OrderSourceRead from
1a descendant block with yokoCardLabel: truethe parsed tree — attrs.content, attrs.text, or the block's innerHTML
2the post, in post modeget_the_title() on the postId in block context
3nothingno aria-label is rendered

yokoCardLabel is a boolean added by this plugin to the blocks YokoCo\Blocks\CardLabel::get_label_blocks() names, so it is stored in those blocks' own comment attributes:

<!-- wp:heading {"level":3,"yokoCardLabel":true} -->
<h3 class="wp-block-heading">Membership benefits</h3>
<!-- /wp:heading -->

The walk is depth first, takes the first match, and does not descend into a nested yoko/card. Core's rich text blocks declare content with "source": "html", which means the server never receives it as a block attribute — so the text is read out of innerHTML when the attributes do not carry it. core/post-title and core/site-title have no stored text at all and are resolved the same way their own blocks resolve theirs.

Filters

// Which descendant blocks have their link options switched off in the editor
// while the card around them is a link.
add_filter( 'yoko_core_card_child_link_blocks', function ( $blocks ) {
$blocks[] = 'my-plugin/promo-tile';
return $blocks;
} );

This one only affects the editor. Every anchor inside a linked card is neutralized at render time regardless, because the HTML parser leaves no choice.

// Which descendant blocks can be marked as the card's accessible name.
add_filter( 'yoko_core_card_label_blocks', function ( $blocks ) {
$blocks[] = 'my-plugin/promo-tile';
return $blocks;
} );

This one drives both halves — the toggle in the editor and the walk at render time — so a block added here has to keep its text somewhere CardLabel can find it: a content or text attribute, or its own innerHTML. Anything else needs a case in CardLabel::block_text() as well, or editors get a toggle that does nothing.

Debugging

With WP_DEBUG on, the block logs to wp-content/debug.log under [yoko-core] card:

LineMeaning
card link enabled with no destinationThe toggle is on but there is nowhere to go — no URL, or The post outside a loop. The card rendered as a container.
linked to the post, but no postId in block contextThe post mode on a card that is not inside a Query Loop or a singular template.
nested links left alone: parser saw …The inner markup could not be rewritten safely, so it was left untouched. The card link will stop at the first inner link. View source and look for a stray </a>.
block metadata missing from build/yoko-cardRun npm run build.