A surface for one self-contained thing — a product tile, a summary, a search result. What it adds over a .card class of your own is what a class cannot do: it takes its accessible name from the projected heading, it folds in loading and disabled, and gogCardLink makes the whole surface activate a real link — your <a>, with your routerLink still on it.
Overview
Import the component and the slots you use. The heading is what names the card — a card without one is an unnamed group, so it deliberately gets no role at all.
The heading names the card: it takes that element's id and points its own aria-labelledby at it. Without a header a card is an unnamed group, so it gets neither.
<gog-card><h3gogCardHeader>Quarterly summary</h3><p>
The heading names the card: it takes that element's id and points its own
<code>aria-labelledby</code> at it. Without a header a card is an unnamed group, so it gets
neither.
</p></gog-card>
Card or panel?
Both paint a surface, and the library ships them as two components because they answer different questions. The quickest test is about links:
Is the whole surface a link? Then it is a card, with a gogCardLink. A gog-panel is never a link — there is no gogPanelLink, because a region that is a link cannot hold the controls a panel exists to hold.
Otherwise: a card is one self-contained thing, usually one of many in a grid, and announces as role="group" so twenty of them do not bury the landmark list. A panel is a titled region of a page — a real landmark, one of a handful, and it can collapse.
Examples
Variants and sizes
The same three variants as gog-panel, from the shared GogSurfaceVariant type — but outlined is the default here, because a grid of many cards wants a border rather than twenty shadows. size drives padding and the row gap on the same five-tier scale as every control.
outlined
Outlined is the default — a border and no shadow, which is what a grid of many wants.
elevated
Outlined is the default — a border and no shadow, which is what a grid of many wants.
filled
Outlined is the default — a border and no shadow, which is what a grid of many wants.
xsm
Same copy, every tier.
sm
Same copy, every tier.
md
Same copy, every tier.
lg
Same copy, every tier.
slg
Same copy, every tier.
<divclass="variants">
@for (variant of variants; track variant) {
<gog-card [variant]="variant"><h4gogCardHeader>{{ variant }}</h4><p>Outlined is the default — a border and no shadow, which is what a grid of many wants.</p></gog-card>
}
</div><divclass="sizes">
@for (size of sizes; track size) {
<gog-card [size]="size"><h4gogCardHeader>{{ size }}</h4><p>Same copy, every tier.</p></gog-card>
}
</div>
Interactive — the whole surface activates one real link
There is no interactive input and no gogClick output. A card becomes interactive by containing a gogCardLink, which stretches that link's hit area over the whole surface. The link stays yours, so routerLink, href, target, middle-click, "open in new tab" and Enter all behave normally, and the focus ring is drawn around the card.
Anything else focusable in the card still receives its own clicks — press the footer button and watch the tally, then click anywhere else on the card and follow the link instead.
<divclass="grid">
@for (person of people; track person.id) {
<gog-card><h4gogCardHeader><agogCardLinkrouterLink="/components/card" [fragment]="person.id">{{ person.name }}</a></h4><p>{{ person.role }}</p><divgogCardFooter><gog-tagvariant="info"size="xsm">Profile</gog-tag><gog-buttonsize="xsm"variant="ghost" (gogClick)="shortlist()">Shortlist</gog-button></div></gog-card>
}
</div><pclass="tally">
Footer clicks that reached their own button: <strong>{{ footerClicks() }}</strong></p>
Two costs, inherent to the pattern rather than to this implementation: text in the card cannot be selected by dragging, and a second link is reachable by keyboard but not by clicking the surface around it. If either matters, do not stretch the link — put an ordinary link in the heading and leave the surface inert.
gogCardLink applies to <a> and <button> only. On a <div> it does nothing, on purpose: a div with a click handler is the thing this directive exists to stop you writing.
Media
gogCardMedia runs full-bleed to the card's edges and rounds into its top corners. The rendered order of the slots is fixed by the component — media, heading, body, footer — and does not follow the order you write them in: the two cards below are authored in opposite orders and render identically, which is what keeps a grid aligned when one card happens to be written differently from its neighbours.
Media written first
Full-bleed to the card's edges, rounded into its top corners.
Media written last
Authored after the heading and the body — and rendered identically to the card beside it. The component fixes the slot order, so the write order never leaks into the layout.
<divclass="grid"><gog-card><img [src]="mediaSrc"gogCardMediaalt=""height="120" /><h4gogCardHeader>Media written first</h4><p>Full-bleed to the card's edges, rounded into its top corners.</p></gog-card><gog-card><h4gogCardHeader>Media written last</h4><p>
Authored after the heading and the body — and rendered identically to the card beside it. The
component fixes the slot order, so the write order never leaks into the layout.
</p><img [src]="mediaSrc"gogCardMediaalt=""height="120" /></gog-card></div>
The bleed is inline-size: calc(100% + 2 * padding-x) plus a negative inline margin, so do not give the media a width of your own — a width: 100% overrides that calculation and the picture ends one padding short of the card's right edge while still starting flush at its left.
Loading and disabled
loading swaps the content for a title bar plus skeletonLines body lines and marks the host aria-busy. disabled dims the card, sets aria-disabled, and takes its link out of the tab order. Both make the card's link non-clickable.
loading is the first-paint treatment. For a refresh of a card that already has content, project a gog-spinner-overlay instead — replacing real content with placeholders is a worse answer once the reader has something to look at.
Quarterly summary
Revenue is up 12% on the quarter, driven mostly by renewals rather than new business.
Tab through the pair: only the enabled card's link stops.
<divclass="grid"><divclass="cell"><gog-buttonsize="xsm"variant="outline" (gogClick)="toggleLoading()">
{{ loading() ? 'Finish loading' : 'Load again' }}
</gog-button><gog-card [loading]="loading()" [skeletonLines]="3"><h4gogCardHeader>Quarterly summary</h4><p>Revenue is up 12% on the quarter, driven mostly by renewals rather than new business.</p></gog-card></div><divclass="cell"><gog-buttonsize="xsm"variant="outline" (gogClick)="toggleDisabled()">
{{ disabled() ? 'Enable' : 'Disable' }}
</gog-button><gog-card [disabled]="disabled()"><h4gogCardHeader><agogCardLinkrouterLink="/components/card">Archived report</a></h4><p>Tab through the pair: only the enabled card's link stops.</p></gog-card></div></div>
Theming
Every value comes from a --gog-card-* token, and the instance tier (--gog-card-bg, --gog-card-border-color, --gog-card-shadow, --gog-card-padding-y, --gog-card-padding-x, --gog-card-gap) is left undeclared on purpose, so setting one on an element beats the variant and size classes without a specificity fight.
Overridden per instance
Three instance tokens from one class on the element. No ::ng-deep.
<gog-cardclass="themed"><h4gogCardHeader>Overridden per instance</h4><p>Three instance tokens from one class on the element. No <code>::ng-deep</code>.</p></gog-card>
API Reference
Inputs
Name
Type
Default
Description
variant21.6.1
GogSurfaceVariant
'outlined'
'outlined' | 'elevated' | 'filled'. Outlined is the default — a border and no shadow, which is what a grid of many wants.
size21.6.1
GogSize
'md'
Drives padding and the gap between the card’s rows, on the five-tier scale.
disabled21.6.1
boolean
false
Dims the card, sets aria-disabled, and takes the card link out of the tab order. A bare attribute works.
loading21.6.1
boolean
false
Replaces the content with a title bar plus skeletonLines text lines and sets aria-busy. The first-paint treatment, not a refresh.
skeletonLines21.6.1
number
2
How many body lines the loading placeholder draws.
Slots
All four are attribute directives on your own elements, not <ng-template>s.
Name
Description
gogCardHeader21.6.1
Your own heading. The card takes its id (minting one if needed), points aria-labelledby at it, and announces as role="group". Without it the card gets neither.
gogCardMedia21.6.1
Runs full-bleed to the card’s edges and rounds into its top corners. Rendered above the heading whatever order you write it in.
gogCardFooter21.6.1
Rendered last, below the body. Its controls keep their own clicks.
gogCardLink21.6.1
On your own <a> or <button>: stretches that link’s hit area over the whole card. Ignored on any other element, deliberately.
No outputs. A card that needs to report something reports it from the control inside it.
The instance tier — left undeclared by the library on purpose, so setting one on an element beats the variant and size classes without a specificity fight.