Card
catnip-card is a surface wrapper. By default it is a non-interactive container for consumer-owned content. Set interactive to make the whole surface one native control: an anchor when href is set, or a button type="button" when it is not.
CatnipCard owns only the surface, interaction states, disabled treatment, and native control semantics. It does not supply a title, image, icon, status, action, width, or height. The consumer owns all slotted content, its hierarchy, its layout, and the wording announced by assistive technology.
Live example
Container card
Use the default non-interactive card when it groups content or contains several separate controls:
<catnip-card>
<article>
<h2>Identity methods</h2>
<p>Configure the methods available to your users.</p>
<catnip-button>Configure</catnip-button>
</article>
</catnip-card>The card itself is not focusable and has no implicit landmark or article role. Put meaningful HTML such as article, headings, lists, or regions in the slot when the content needs that structure.
Interactive card
Use one interactive card for one primary action. A URL produces a real link, so browser features such as copying the address, opening a context menu, opening in another tab, and native visited-link history remain available:
<catnip-card interactive href="/identity-methods" aria-label="Open identity methods">
<h2>Identity methods</h2>
<p>Configure electronic identity methods.</p>
</catnip-card>Without href, the component renders a native button type="button" for an in-page action. Listen for the ordinary click event:
<catnip-card interactive aria-label="Select Norwegian BankID">
<strong>Norwegian BankID</strong>
</catnip-card>
<script>
document.querySelector("catnip-card").addEventListener("click", selectMethod);
</script>One card, one action
Do not put any other interactive element inside an interactive card. This includes button, a, input, select, textarea, summary, media controls, or another custom element that users can focus or activate.
The HTML Standard’s anchor content model requires that “there must be no interactive content descendant.” WCAG’s Focus Order guidance warns against controls that appear to receive focus more than once or create a confusing order. Nested actions also make the card’s purpose and activation target ambiguous for screen-reader, keyboard, touch, switch, and voice-control users.
The entire card must perform the same single action. If the content needs a primary link plus Edit, Delete, More, a checkbox, a tooltip button, or any other independent action, omit interactive and place those controls inside the card as siblings.
New tabs and visited links
- The default target is the current browsing context. Use
target="_blank"only when the product genuinely requires a new tab or window, and tell users in the visible content or accessible name.noopener noreferreris added automatically unless already present. - Do not add a
visitedprop or announce a made-up visited state. A navigation card is a native anchor, so the browser and assistive technology own real history. Browser privacy rules restrict what:visitedmay style or reveal. - Use
ariaCurrent="page"(HTML:aria-current="page") for the card representing the current destination.aria-currentmeans current, not visited.
Styling slotted content from card state
The state selectors are implemented on the internal .catnip-card--interactive surface. Because that class is inside Shadow DOM, consumers should target the public catnip-card host. CSS custom properties inherit through the component and into slotted content, including nested Web Components:
.product-icon {
color: var(--product-icon-color, currentColor);
}
catnip-card:hover {
--product-icon-color: var(--catnip-color-content-primary-strong, #5f36bc);
--catnip-icon-color: var(--catnip-color-content-primary-strong, #5f36bc);
}
catnip-card:active {
--product-icon-color: var(--catnip-color-content-primary-strongest, #2a0062);
}The nested icon can consume the inherited property internally:
:host {
color: var(--catnip-icon-color, currentColor);
}Avoid expecting the library to reach arbitrary descendants with ::slotted(): it can match only a node assigned directly to the slot, not an icon nested inside a slotted div. The consumer owns these state-to-content relationships. The surface is also exposed as part="card" for narrowly scoped styling, but prefer inherited custom properties for reusable content behavior.
Accessibility defaults
- Native anchors and buttons supply their standard role, keyboard activation, focusability, context-menu behavior, and assistive-technology mappings.
- Slotted text becomes the accessible name by default. Use
ariaLabelonly when that computed name would be missing, overly verbose, or unclear. The WAI-ARIA APG says focusable interactive elements need an accessible name. - Disabled action cards use native
disabled. Disabled navigation cards have nohref, userole="link",aria-disabled="true", andtabindex="-1". Disabled container cards exposearia-disabled="true"without becoming focusable; consumers remain responsible for any controls inside them. - The visible two-layer focus ring follows the Figma focus treatment and remains present for keyboard focus.
- Ensure the card is not obscured when focused and keep its final size large enough for WCAG 2.2 Target Size (Minimum). The consumer controls width and height.