20 CSS Cards12 / 20

Pure CSSMIT licensed

SaaS Feature Highlight Card

Dark feature tile with 44×44 gradient icon tile, "NEW" chip, headline, one-line description, and a "Learn more →" link whose arrow slides on hover.

Published Updated

Live Demo
Try it

The code

<section class="card-12">
  <article class="card-12__card">
    <span class="card-12__icon" aria-hidden="true">
      <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="7" r="4"/><path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2"/><circle cx="17" cy="7" r="4" opacity="0.6"/><path d="M14 15h4a4 4 0 0 1 4 4v2" opacity="0.6"/></svg>
    </span>
    <span class="card-12__chip">New</span>
    <h3 class="card-12__title">Real-time collaboration</h3>
    <p class="card-12__desc">See teammates' cursors and edits as they happen — no refresh, no conflicts.</p>
    <a class="card-12__link" href="#learn">
      Learn more
      <span class="card-12__arrow" aria-hidden="true">
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
      </span>
    </a>
  </article>
</section>
.card-12 {
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(28px, 5vw, 56px) clamp(20px, 4vw, 40px);
  box-sizing: border-box;
  background: #f8f7f4;
  font-family: 'Inter', system-ui, sans-serif;
  --focus: #6366f1;
}

.card-12 *,
.card-12 *::before,
.card-12 *::after {
  box-sizing: border-box;
}

.card-12__card {
  position: relative;
  width: 100%;
  max-width: 340px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: 18px;
  padding: 22px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);
  transition: transform 0.25s, box-shadow 0.25s, border-color 0.25s;
}

.card-12__card:hover {
  transform: translateY(-2px);
  border-color: rgba(99, 102, 241, 0.2);
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.06), 0 0 0 4px rgba(99, 102, 241, 0.04);
}

.card-12__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: linear-gradient(135deg, #818cf8 0%, #6366f1 100%);
  box-shadow: 0 6px 16px rgba(99, 102, 241, 0.28), 0 0 0 1px rgba(255, 255, 255, 0.4) inset;
  margin-bottom: 16px;
}

.card-12__chip {
  position: absolute;
  top: 22px;
  right: 22px;
  font-family: ui-monospace, 'JetBrains Mono', Menlo, monospace;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: #6366f1;
  padding: 3px 9px;
  background: rgba(99, 102, 241, 0.10);
  border: 1px solid rgba(99, 102, 241, 0.2);
  border-radius: 999px;
  text-transform: uppercase;
}

.card-12__title {
  font-size: 17px;
  font-weight: 700;
  margin: 0 0 6px;
  color: #1a1a1a;
  letter-spacing: -0.01em;
}

.card-12__desc {
  font-size: 13px;
  color: #6b6b7c;
  margin: 0 0 16px;
  line-height: 1.55;
}

.card-12__link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-weight: 700;
  color: #4338ca;
  text-decoration: none;
  padding: 6px 0;
  min-height: 32px;
  border-radius: 6px;
  transition: color 0.18s;
}

.card-12__arrow {
  display: inline-flex;
  align-items: center;
  transition: transform 0.2s ease;
}

.card-12__link:hover .card-12__arrow,
.card-12__card:hover .card-12__arrow {
  transform: translateX(3px);
}

.card-12__link:hover {
  color: #312e81;
}

.card-12__link:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .card-12__card,
    .card-12__link,
    .card-12__arrow {
    transition: none;
  }

  .card-12__card:hover {
    transform: none;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Card from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: SaaS Feature Highlight Card
Source: https://codefronts.com/components/css-cards/saas-feature-highlight-card/

Dark feature tile with 44×44 gradient icon tile, "NEW" chip, headline, one-line description, and a "Learn more →" link whose arrow slides on hover.
## HTML
```html
<section class="card-12">
  <article class="card-12__card">
    <span class="card-12__icon" aria-hidden="true">
      <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="7" r="4"/><path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2"/><circle cx="17" cy="7" r="4" opacity="0.6"/><path d="M14 15h4a4 4 0 0 1 4 4v2" opacity="0.6"/></svg>
    </span>
    <span class="card-12__chip">New</span>
    <h3 class="card-12__title">Real-time collaboration</h3>
    <p class="card-12__desc">See teammates' cursors and edits as they happen — no refresh, no conflicts.</p>
    <a class="card-12__link" href="#learn">
      Learn more
      <span class="card-12__arrow" aria-hidden="true">
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
      </span>
    </a>
  </article>
</section>
```
## CSS
```css
.card-12 {
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(28px, 5vw, 56px) clamp(20px, 4vw, 40px);
  box-sizing: border-box;
  background: #f8f7f4;
  font-family: 'Inter', system-ui, sans-serif;
  --focus: #6366f1;
}

.card-12 *,
.card-12 *::before,
.card-12 *::after {
  box-sizing: border-box;
}

.card-12__card {
  position: relative;
  width: 100%;
  max-width: 340px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: 18px;
  padding: 22px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);
  transition: transform 0.25s, box-shadow 0.25s, border-color 0.25s;
}

.card-12__card:hover {
  transform: translateY(-2px);
  border-color: rgba(99, 102, 241, 0.2);
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.06), 0 0 0 4px rgba(99, 102, 241, 0.04);
}

.card-12__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: linear-gradient(135deg, #818cf8 0%, #6366f1 100%);
  box-shadow: 0 6px 16px rgba(99, 102, 241, 0.28), 0 0 0 1px rgba(255, 255, 255, 0.4) inset;
  margin-bottom: 16px;
}

.card-12__chip {
  position: absolute;
  top: 22px;
  right: 22px;
  font-family: ui-monospace, 'JetBrains Mono', Menlo, monospace;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: #6366f1;
  padding: 3px 9px;
  background: rgba(99, 102, 241, 0.10);
  border: 1px solid rgba(99, 102, 241, 0.2);
  border-radius: 999px;
  text-transform: uppercase;
}

.card-12__title {
  font-size: 17px;
  font-weight: 700;
  margin: 0 0 6px;
  color: #1a1a1a;
  letter-spacing: -0.01em;
}

.card-12__desc {
  font-size: 13px;
  color: #6b6b7c;
  margin: 0 0 16px;
  line-height: 1.55;
}

.card-12__link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-weight: 700;
  color: #4338ca;
  text-decoration: none;
  padding: 6px 0;
  min-height: 32px;
  border-radius: 6px;
  transition: color 0.18s;
}

.card-12__arrow {
  display: inline-flex;
  align-items: center;
  transition: transform 0.2s ease;
}

.card-12__link:hover .card-12__arrow,
.card-12__card:hover .card-12__arrow {
  transform: translateX(3px);
}

.card-12__link:hover {
  color: #312e81;
}

.card-12__link:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .card-12__card,
    .card-12__link,
    .card-12__arrow {
    transition: none;
  }

  .card-12__card:hover {
    transform: none;
  }
}
```

How this works

This SaaS feature highlight card is a copy-paste unit sized for Notion, Linear, and Stripe docs grids — one 340px article, 22px inner padding, a 44px indigo icon tile at top-left, a monospaced NEW chip pinned top-right, a 17px title, a 13px description, and an arrow-slide learn-more link. The whole card sits on background: #fff with border: 1px solid rgba(0, 0, 0, 0.06) and an ultra-soft box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03), so it reads as flat until you interact.

Hover promotes the card by translateY(-2px), shifts the border to rgba(99, 102, 241, 0.2), and layers a 4px indigo halo via box-shadow: 0 14px 32px rgba(0, 0, 0, 0.06), 0 0 0 4px rgba(99, 102, 241, 0.04). The icon tile paints a linear-gradient(135deg, #818cf8 0%, #6366f1 100%) with an inset white sheen — enough depth that stroked SVG icons at stroke="#fff" read cleanly at 22px. The NEW chip pairs a ui-monospace, JetBrains Mono, Menlo, monospace stack with letter-spacing: 0.14em, matching the tracking Linear ships on their labels.

The Learn more link uses two selectors on the same transform target — .card-12__link:hover .card-12__arrow and .card-12__card:hover .card-12__arrow — so the arrow slides translateX(3px) whether the pointer lands on the link itself or anywhere inside the card. That single detail is what makes the whole tile feel clickable, and the anchor keeps its 32px min-height so keyboard and touch users hit WCAG 2.5.5. Reduced-motion nulls the transition and hover transform in one block.

Make it yours

  • Retint the icon tile and chip by editing the linear-gradient(135deg, #818cf8, #6366f1) and the rgba(99, 102, 241, ...) tokens together; keep the inset 0 0 0 1px rgba(255, 255, 255, 0.4) sheen so the tile stays legible at 44px on any hue.
  • Swap the NEW chip for BETA, ALPHA, or NEXT by editing the text node and widening the padding to 3px 11px for six-letter labels; the ui-monospace font stack keeps every variant vertically centered without adjusting line-height.
  • Reflow the card into a two-column grid feature by removing the max-width: 340px cap and applying grid-template-columns: 44px 1fr to the article; the icon becomes a left rail and title, description, and link stack on the right.
  • Drop the arrow-slide by removing the .card-12__arrow transition or replace it with a 3px underline-offset hover by adding text-decoration: underline; text-underline-offset: 3px to the link:hover rule.
  • Compose a docs grid by wrapping three or four card-12 articles in a parent set to grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); the 340px max-width caps each column at the same visual footprint Linear uses on their feature pages.

Gotchas — read before shipping

  • The Learn more anchor pushes color to #4338ca on hover but keeps text-decoration: none — passes WCAG 1.4.3 at 8.2:1, but WCAG 1.4.1 says color must not be the only distinguisher. The arrow slide is a valid non-color affordance, so never remove it without also adding underline on hover.
  • The card has no clickable outer wrapper, only the link is focusable — assistive users tab straight to the anchor and skip the title, which is correct WCAG 4.1.2 semantics. Do not wrap the whole card in an anchor to fake full-tile click; keep it link-only or add a role="link" with aria-labelledby on the article and manage keyboard yourself.
  • The chip is positioned absolute top: 22px; right: 22px, which will collide with a two-line title on narrow viewports below 300px. Add padding-right: 60px to the title, or move the chip inside the flex flow above the title when max-width drops under 320px.
  • The linear-gradient icon tile costs one paint layer per card — at grids of 40+ features the LCP element sometimes becomes the first icon, not the title. Preload the hero card's title font weight 700 in your document head, and the icon layer stops competing with the LCP candidate.
  • React apps that hot-swap the chip text between NEW and BETA in a useEffect cause a hydration-mismatch warning if the initial SSR value differs from client value — the chip should read from a stable feature flag inside getServerSideProps or generateStaticParams, not from window state.
  • The hover halo uses two shadows stacked — Safari repaints both on every hover, and if you compose this card inside a virtualized list on iOS the halo flashes on scroll momentum bounce. Toggle the second shadow onto a @media (hover: hover) block so touch devices skip the halo cost entirely.

Techniques used in this demo

Search CodeFronts

Loading…