Card v1.5.0
Group content such as images, text, and related links, offering a compact and organized way to display information.
-
Tristique senectus et netus
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Tristique et netus
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Fames ac turpis egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></h2>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Fames ac turpis egestas</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}Best Practices for Creating Accessible Card Interfaces Anchor (Best Practices for Creating Accessible Card Interfaces)
When designing and developing card interfaces, it's essential to prioritize accessibility and usability. Below are key best practices and common pitfalls to avoid when structuring cards, especially for assistive technology users.
- Use distinct and descriptive CTA text - Instead of generic phrases like "Learn More," opt for distinct and contextually relevant links (e.g., "Learn More about Accessibility"). This improves clarity and usability, especially for screen reader users.
- Consider linking only the card heading - This approach, combined with affordances such as arrows, provides a familiar interaction pattern while reducing the risk of incorrectly programmed "Learn More" hyperlinks.
- Utilize visually hidden text where appropriate - Hidden text techniques can enhance accessibility, but they should not be used in CMS-authored content, due to the high risk of breaking accessibility.
- Use a "Faux CTA" if necessary - If "Learn More" must be included for design purposes, it can be visually present but not marked up as a hyperlink. It can even be hidden from assistive technology using ARIA, as it is often redundant. However, this technique should not be used in CMS-authored content.
- Do not overload hyperlinks with excessive content - Wrapping too much content, including alternative text, within a link can overwhelm assistive technology users. If the entire card needs to be clickable, use the pseudo-element trick to maintain a single link structure.
- Provide appropriate alt text for images - Only use alt text if the image requires a description. The card title should never be repeated as alt text.
- Use proper heading levels - Card headings should be logical and follow the surrounding content structure. Typically these are
h3orh2headings. Heading levels must also never be skipped. - Apply a class to card headings - This allows flexibility if the heading level needs to be changed later or replaced with a non-heading element.
- Avoid headings in cards without summaries - If a card does not contain a summary, a heading should not be used.
- Ensure proper focus states - The
:focuspseudo-class does not work on non-interactive element for keyboard users. Instead, use:focus-withinif you need to highlight parent element when a link within the card receives focus. - Be mindful of duplicate links - If both the heading and CTA link to the same URL, ensure they are correctly marked up to prevent confusion.
- Provide semantic structure - Proper markup, like a list of cards with nested headings and links, may not requre you to add visually hidden content (though it is never a bad idea to include it anyway).
The pseudo-element Trick Anchor (The pseudo-element Trick)
The pseudo-element trick provides a simple CSS solution for making entire card regions clickable, eliminating the necessity for multiple hyperlinks or links wrapping various content regions. Additionally, it improves accessibility by enabling us to prioritize linking the title, the most distinct and unique aspect of a card.
.card__item {
position: relative;
}
.card__trigger::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}-
Tristique senectus et netus
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.
- Topic
- Topic
- Topic
-
Tristique et netus
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
- Topic
- Topic
- Topic
-
Fames ac turpis egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
- Topic
- Topic
- Topic
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></h2>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
<ul class="card__category" role="list">
<li>Topic</li>
<li>Topic</li>
<li>Topic</li>
</ul>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
<ul class="card__category" role="list">
<li>Topic</li>
<li>Topic</li>
<li>Topic</li>
</ul>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Fames ac turpis egestas</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
<ul class="card__category" role="list">
<li>Topic</li>
<li>Topic</li>
<li>Topic</li>
</ul>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Eyebrow
Tristique senectus et netus
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Eyebrow
Tristique et netus
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Eyebrow
Fames ac turpis egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
Note: Examine this markup closely and you will notice that we are using an element called hgroup to encapsulate the eyebrow and heading. This should be done instead of using multiple headings to markup alternative titles. See Subheadings, subtitles, alternative titles and taglines in HTML for more details about this useful element and best practice.
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<hgroup class="card__group">
<p class="card__eyebrow">Eyebrow</p>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></h2>
</hgroup>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<hgroup class="card__group">
<p class="card__eyebrow">Eyebrow</p>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></h2>
</hgroup>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<hgroup class="card__group">
<p class="card__eyebrow">Eyebrow</p>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Fames ac turpis egestas</a></h2>
</hgroup>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus et netus
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Tristique et netus
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Fames ac turpis egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></h2>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
<ul class="card__metadata" role="list">
<li>Content Type</li>
<li>Job Category</li>
<li><time datetime="2024-05-15T19:00">May 15, 2024</time></li>
<li>Read Time</li>
</ul>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
<ul class="card__metadata" role="list">
<li>Content Type</li>
<li>Job Category</li>
<li><time datetime="2024-05-15T19:00">May 15, 2024</time></li>
<li>Read Time</li>
</ul>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Fames ac turpis egestas</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
<ul class="card__metadata" role="list">
<li>Content Type</li>
<li>Job Category</li>
<li><time datetime="2024-05-15T19:00">May 15, 2024</time></li>
<li>Read Time</li>
</ul>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}Warning: Examine this markup closely and you will notice the absence of heading levels. This in intentional. The omission stems from the lack of a clear summary. Headings should exclusively introduce content; otherwise, they should not be used at all.
<ul class="card" data-grid-row-2 role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<div class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<div class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<div class="card__title"><a class="card__trigger card__click" href="#!">Pellentesque habitant morbi tristique</a></div>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus et netus
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Fames ac turpis egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
Note: When incorporating a video, ensure that the title explicitly includes "(Video)" to aid assistive technology users, signaling their engagement with video content. If this content is not visually desired, you may use various techniques to hide it, but still make it accessible to assistive technology. Additionally, always provide captions for videos, and when needed, ensure transcripts are available. In the design of the module, we are also offsetting the play button, which often obstructs the selected images in undesirable ways.
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></h2>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
<svg aria-hidden="true" class="card__play" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><path d="M24 48a24 24 0 1 1 24-24 24 24 0 0 1-24 24zm0-46a22 22 0 1 0 22 22A22 22 0 0 0 24 2z"/><path d="M18 37a1 1 0 0 1-.46-.11A1 1 0 0 1 17 36V12a1 1 0 0 1 .54-.89 1 1 0 0 1 1 .07l17 12a1 1 0 0 1 0 1.64l-17 12A1 1 0 0 1 18 37zm1-23.07v20.14L33.27 24z"/></svg></span>
</div>
<h2 class="card__title"><button class="card__trigger card__click">Tristique et netus (Video)</button></h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Fames ac turpis egestas</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus et netus
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Tristique et netus
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Fames ac turpis egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
<ul class="card" role="list">
<li class="card__item">
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></h2>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Fames ac turpis egestas</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Malesuada fames ac egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant tristique senectus et netus. Unde omnis iste error sit voluptatem accusantium. Pellentesque habitant tristique senectus et fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde iste natus doloremque.
-
Omnis iste natus error
Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Tristique et netus
Perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Habitant tristique senectus et malesuada
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Perspiciatis unde iste doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
<ul class="card" data-horizontal role="list">
<li class="card__item">
<div class="card__media">
<picture>
<source media="(min-width: 60em)" srcset="https://radancy.dev/placeholder-225x225.png">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</picture>
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Malesuada fames ac egestas</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant tristique senectus et netus. Unde omnis iste error sit voluptatem accusantium. Pellentesque habitant tristique senectus et fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde iste natus doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<picture>
<source media="(min-width: 60em)" srcset="https://radancy.dev/placeholder-225x225.png">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</picture>
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Omnis iste natus error</a></h2>
<div class="card__summary">
<p>Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<picture>
<source media="(min-width: 60em)" srcset="https://radancy.dev/placeholder-225x225.png">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</picture>
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></h2>
<div class="card__summary">
<p>Perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<picture>
<source media="(min-width: 60em)" srcset="https://radancy.dev/placeholder-225x225.png">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</picture>
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Habitant tristique senectus et malesuada</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque. Perspiciatis unde iste doloremque. Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus et netus
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Tristique et netus
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Fames ac turpis egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
Best Practice: This strategy can be used by developers and CMS authors.
Note: In this example, the heading serves as the primary link, with an arrow affordance to draw attention to it instead of using a text-based call to action like Learn More. A CSS pseudo-element trick makes the entire card clickable, ensuring broad usability while maintaining a unique link in each card for better accessibility.
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></h2>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Fames ac turpis egestas</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus
Learn More about Tristique senectusHabitant morbi tristique senectus et netus et malesuada. Sed ut perspiciatis unde omnis iste natus error sit voluptatem.
-
Tristique netus
Learn More about Tristique netusPellentesque habitant tristique senectus et fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
-
Fames egestas
Learn More about Fames egestasTristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
Best Practice: This strategy can be used by developers and CMS authors. While designers may not embrace this strategy due to the length of the CTA, this may be the most universal, straightforward, and accessible way to handle CTAs across a wide range of situations.
Note: In this example, the heading does not need to be the primary link because each call to action (CTA) provides distinct and unique hypertext.
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Tristique senectus</h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus et malesuada. Sed ut perspiciatis unde omnis iste natus error sit voluptatem.</p>
</div>
<a class="card__cta card__click" href="#!">Learn More about Tristique senectus</a>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Tristique netus</h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
<a class="card__cta card__click" href="#!">Learn More about Tristique netus</a>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Fames egestas</h2>
<div class="card__summary">
<p>Tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
<a class="card__cta card__click" href="#!">Learn More about Fames egestas</a>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus et netus
Habitant morbi tristique senectus et netus et malesuada. Sed ut perspiciatis unde omnis iste natus error sit voluptatem.
-
Tristique et netus
Pellentesque habitant tristique senectus et fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
-
Fames ac turpis egestas
Tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
Warning: This strategy can be used by developers but it is not recommended for CMS-authored content.
Note: In this example, the heading serves as the primary link, but the arrow affordance has been replaced with a text-based call to action (Learn More). This is often called a "Faux CTA" because it is not clickable and it is intentionally hidden from assistive technology. Like an arrow affordance, it is meant purely as a visual call to action.
<ul class="card" data-remove-arrow role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique senectus et netus</a></h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus et malesuada. Sed ut perspiciatis unde omnis iste natus error sit voluptatem.</p>
</div>
<div aria-hidden="true" class="card__cta">Learn More</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Tristique et netus</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
<div aria-hidden="true" class="card__cta">Learn More</div>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger card__click" href="#!">Fames ac turpis egestas</a></h2>
<div class="card__summary">
<p>Tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
<div aria-hidden="true" class="card__cta">Learn More</div>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus et netus
Habitant morbi tristique senectus et netus et malesuada. Sed ut perspiciatis unde omnis iste natus error sit voluptatem.
-
Tristique et netus
Pellentesque habitant tristique senectus et fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
-
Fames ac turpis egestas
Tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
Warning: This strategy can be used by developers but it is not recommended for CMS-authored content.
Note: In this example, both the heading and the call to action point to the same URL, with the call to action effectively hidden from assistive technology and keyboard users via aria-hidden="true" and tabindex="-1". While this strategy is uncommon, it is considered a more materially honest technique compared to many commonly used methods of linking a card. If nothing else, it can serve as a practical solution for addressing adjacent duplicate links, which can be a problem for accessibility.
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger" href="#!">Tristique senectus et netus</a></h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus et malesuada. Sed ut perspiciatis unde omnis iste natus error sit voluptatem.</p>
</div>
<a aria-hidden="true" class="card__cta" href="#!" tabindex="-1">Learn More</a>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger" href="#!">Tristique et netus</a></h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
<a aria-hidden="true" class="card__cta" href="#!" tabindex="-1">Learn More</a>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title"><a class="card__trigger" href="#!">Fames ac turpis egestas</a></h2>
<div class="card__summary">
<p>Tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
<a aria-hidden="true" class="card__cta" href="#!" tabindex="-1">Learn More</a>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus et netus
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Tristique et netus
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
-
Fames ac turpis egestas
Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.
Warning: This strategy should be avoided unless the content output is known to not be excessive. Wrapping excessive amounts of content (including alternative text) within a hyperlink can overwhelm assistive technology users. This strategy, when deemed appropriate, can be used by developers but it is not recommended for CMS-authored content.
Note: In this example, a hyperlink is placed around the image, heading, and content, making the entire region clickable.
<ul class="card" role="list">
<li class="card__item">
<a class="card__block" href="#!">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Tristique senectus et netus</h2>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</a>
</li>
<li class="card__item">
<a class="card__block" href="#!">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Tristique et netus</h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
</a>
</li>
<li class="card__item">
<a class="card__block" href="#!">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Fames ac turpis egestas</h2>
<div class="card__summary">
<p>Pellentesque habitant tristique senectus et malesuada fames ac egestas. Sed ut perspiciatis unde omnis iste error sit voluptatem doloremque.</p>
</div>
</a>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}-
Tristique senectus et netus
Habitant morbi tristique senectus et netus et malesuada. Sed ut perspiciatis unde omnis iste natus error sit voluptatem.
-
Tristique et netus
Pellentesque habitant morbi tristique senectus ames ac turpis sit voluptatem accusantium doloremque.
-
Fames ac turpis egestas
Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.
Best Practice: This strategy can be used by developers, with visually hidden text as recommended, and CMS authors if visually hidden text is not included.
Note: In this example, the card interface uses list markup with nested images, headings and hyperlinks, ensuring clarity without the need for visually hidden text. This applies to cards with single or multiple links. While including visually hidden text is always recommended, content structure alone may provide sufficient context when hidden text is not possible or advisable. See WCAG Technique H77 and Technique H81 for more information.
<ul class="card" role="list">
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Tristique senectus et netus</h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus et malesuada. Sed ut perspiciatis unde omnis iste natus error sit voluptatem.</p>
</div>
<ul class="card__links" role="list">
<li><a href="#!">Read More</a></li>
<li><a href="#!">View Jobs</a></li>
</ul>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Tristique et netus</h2>
<div class="card__summary">
<p>Pellentesque habitant morbi tristique senectus ames ac turpis sit voluptatem accusantium doloremque.</p>
</div>
<ul class="card__links" role="list">
<li><a href="#!">Read More</a></li>
<li><a href="#!">View Jobs</a></li>
</ul>
</li>
<li class="card__item">
<div class="card__media">
<img alt="" src="https://radancy.dev/placeholder-450x225.png">
</div>
<h2 class="card__title">Fames ac turpis egestas</h2>
<div class="card__summary">
<p>Habitant morbi tristique senectus et netus. Unde omnis iste natus error sit voluptatem accusantium doloremque.</p>
</div>
<ul class="card__links" role="list">
<li><a href="#!">Read More</a></li>
<li><a href="#!">View Jobs</a></li>
</ul>
</li>
</ul>/*!
Radancy Component Library: Card
Contributor(s):
Michael "Spell" Spellacy
Dependencies: Sass
*/
// Card (.card)
.card {
// Component Variables
--card-base-color: #6F00EF;
--card-columns: 3;
--card-horizontal-rows: 2;
--card-secondary-color: #767676;
--card-transition: 300ms;
$card-breakpoint: 60em;
display: grid;
gap: 1rem;
list-style: none;
margin: 0;
overflow-wrap: break-word;
padding: 0;
text-wrap: balance;
&:not([data-horizontal]) {
text-wrap: pretty;
@media (min-width: 60em) {
grid-template-columns: repeat(var(--card-columns), 1fr);
}
}
// Item (.card__item)
&__item {
border: 1px var(--card-secondary-color) solid;
display: grid;
grid-template-rows: subgrid;
padding: 1rem;
position: relative; // Needed for pseudo-element trick.
transition: var(--card-transition);
&:has(> :nth-child(2):last-child) {
grid-row: span 2;
}
&:has(> :nth-child(3):last-child), &:has(.card__block) {
grid-row: span 3;
}
&:has(> :nth-child(4):last-child) {
grid-row: span 4;
}
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
gap: inherit;
grid-auto-flow: column;
grid-template-rows: min-content repeat(calc(var(--card-horizontal-rows) - 1), 1fr);
}
}
&:focus-within, &:hover {
background-color: #eee;
.card__trigger {
&::after {
transform: translateX(calc(5em/16));
}
}
.card__play {
transform: scale(1.2);
}
}
}
// Block (.card__block)
&__block {
display: grid;
grid-row: inherit;
grid-template-rows: subgrid;
}
// Media (.card__media)
&__media {
display: grid;
[data-horizontal] & {
@media (min-width: $card-breakpoint) {
grid-row: span var(--card-horizontal-rows);
}
}
picture {
display: contents;
}
img, video {
border-radius: 5px;
border: calc(1rem / 16) var(--card-secondary-color) solid;
display: block;
grid-area: 1 / 1;
inline-size: 100%;
}
}
// Play Button (.card__play)
&__play {
grid-area: 1 / 1;
inline-size: calc(40em / 16);
margin-block-end: 1rem;
margin-inline-end: 1rem;
place-self: end;
transition: var(--card-transition);
path {
fill: #fff;
}
}
// Eyebrow (.card__eyebrow)
&__eyebrow {
color: var(--card-base-color);
margin: 0;
}
// Title (.card__title)
&__title {
color: var(--card-base-color);
font-size: calc(22.4rem / 16);
line-height: normal;
margin: 0;
text-wrap: auto;
width: fit-content;
// If text is not a heading, then center it.
&:not(h2):not(h3):not(h4):not(h5):not(h6) {
justify-self: center;
text-align: center;
}
}
// Trigger (.card__trigger)
&__trigger {
background-color: transparent;
border: 0;
color: inherit;
cursor: pointer;
display: grid;
font: inherit;
gap: 0.3em;
grid-auto-flow: column;
margin: 0;
padding: 0;
text-decoration: none;
&:visited {
color: #000;
}
[data-remove-arrow] & {
text-decoration: underline;
&:focus, &:hover {
text-decoration: none;
}
}
// CTA
// NOTE: HTML Entity being used, but you can use Font Awesome or inline SVG, too.
&:not(button) {
&::after {
align-self: center;
content: "\2794";
display: block;
line-height: 0;
transition: var(--card-transition);
[data-remove-arrow] & {
display: none;
}
}
}
}
// Summary (.card__summary)
&__summary {
p {
margin: 0;
&:not(:last-child) {
margin-block-end: 1rem;
}
&:last-child {
margin-block-end: 0;
}
}
}
// CTA (.card__cta)
&__cta {
color: var(--card-base-color);
font-weight: bold;
}
// Shared: Category (.card__category) & Metadata (.card__metadata)
// TODO: Make part of grid; may need 12 column grid instead of 3.
&__category, &__metadata, &__links {
display: flex;
flex-wrap: wrap;
font-size: 0.9rem;
gap: 0.5rem;
line-height: normal;
list-style: none;
padding: 0;
li:not(:last-child)::after {
padding: 0 0.5rem;
}
}
// Category (.card__category)
&__category {
li {
background-color: #ccc;
border-radius: calc(5rem / 16);
padding: 0.5rem 1rem 0.7rem;
}
}
// Metadata (.card__metadata)
&__metadata {
li:not(:last-child)::after {
content: "|";
}
}
// Links (.card__links)
&__links {
a {
background-color: var(--card-base-color);
color: #fff;
display: block;
padding: 0.8rem;
text-decoration: none;
&:focus-visible, &:hover {
text-decoration: underline;
}
}
}
// Pseudo-element Trick (.card__click)
&__click {
&::before {
content: "";
display: block;
inset: 0;
position: absolute;
z-index: 1;
}
}
}Properties Anchor (Properties)
Use the following properties to configure this component.
| Attribute | Applies To | Note | Description |
|---|---|---|---|
data-horizontal |
.card |
Boolean, Optional | Used to change the card layout to horizontal |
data-remove-arrow |
.card |
Boolean, Optional | Used to remove the arrow affordance from all .card__trigger hyperlinks. |
| Class | Applies To | Note | Description |
|---|---|---|---|
.card |
article, div, ol, ul |
Mandatory | Used on the parent element that wraps your card content. |
.card__item |
div, li |
Mandatory | Used on your card item. Must be a child of .card. |
.card__block |
a |
Optional | Used to define the link used to wrap entire content regions. Should be used sparingly and when content output is minimal. |
.card__category |
div |
Optional | Used to define a card category list. |
.card__click |
a, button |
Optional | Used to determine which link or button in your card should contain the pseudo-element trick to expand the clickable region. Should only be used on one link or button per card. |
.card__cta |
div |
Optional | Used to add distinct styles to your card's faux CTA. |
.card__eyebrow |
p |
Optional | Used to define an additional card title. May only be used in hgroup. |
.card__group |
hgroup |
Optional | Used to define multiple card titles, specifically when .card__eyebrow is used. |
.card__media |
div |
Optional | Used to define your media region. |
.card__metadata |
div |
Optional | Used to define a card metadata list. |
.card__summary |
div |
Optional | Used to define a card summary. |
.card__title |
h2, h3, h4, h5, h6, div |
Optional | Used to define primary card heading and may be applied to any logical heading or div if .card__summary is not present. |
.card__trigger |
a, button |
Mandatory | Used to define a card interaction trigger. |
Report Issues
Find a bug? Want a new feature? Report it on JIRA. You're so awesome!