21 CSS Pricing Sections20 / 21
Spotlight Pricing
Cards illuminate where the cursor moves — a CSS variable carries pointer X/Y and a radial gradient renders the spotlight. Black + amber, the developer-tool / OSS-pricing aesthetic. JS only updates two CSS variables; everything else is CSS.
Published
The code
<section class="ps-20" aria-label="Pricing">
<header class="ps-20__head">
<h2>Move the light. Read the price.</h2>
<p>Cursor-aware cards. Real cards, real prices, real keyboard support.</p>
</header>
<div class="ps-20__grid">
<article class="ps-20__card" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<h3>OSS</h3>
<div class="ps-20__price">$0<span>open source</span></div>
<ul>
<li>Self-hosted</li>
<li>MIT licence</li>
<li>GitHub issues</li>
</ul>
<a class="ps-20__cta" href="#oss">Clone the repo</a>
</article>
<article class="ps-20__card ps-20__best" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<span class="ps-20__pill">Recommended</span>
<h3>Cloud</h3>
<div class="ps-20__price">$24<span>/mo</span></div>
<ul>
<li>Hosted & maintained</li>
<li>Auto-updates + backups</li>
<li>Email support</li>
<li>API access</li>
</ul>
<a class="ps-20__cta ps-20__cta-pri" href="#cloud">Choose Cloud</a>
</article>
<article class="ps-20__card" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<h3>Self-Hosted Pro</h3>
<div class="ps-20__price">$199<span>/yr</span></div>
<ul>
<li>Commercial licence</li>
<li>Priority email support</li>
<li>Audit logs</li>
</ul>
<a class="ps-20__cta" href="#shp">Buy licence</a>
</article>
</div>
</section><section class="ps-20" aria-label="Pricing">
<header class="ps-20__head">
<h2>Move the light. Read the price.</h2>
<p>Cursor-aware cards. Real cards, real prices, real keyboard support.</p>
</header>
<div class="ps-20__grid">
<article class="ps-20__card" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<h3>OSS</h3>
<div class="ps-20__price">$0<span>open source</span></div>
<ul>
<li>Self-hosted</li>
<li>MIT licence</li>
<li>GitHub issues</li>
</ul>
<a class="ps-20__cta" href="#oss">Clone the repo</a>
</article>
<article class="ps-20__card ps-20__best" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<span class="ps-20__pill">Recommended</span>
<h3>Cloud</h3>
<div class="ps-20__price">$24<span>/mo</span></div>
<ul>
<li>Hosted & maintained</li>
<li>Auto-updates + backups</li>
<li>Email support</li>
<li>API access</li>
</ul>
<a class="ps-20__cta ps-20__cta-pri" href="#cloud">Choose Cloud</a>
</article>
<article class="ps-20__card" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<h3>Self-Hosted Pro</h3>
<div class="ps-20__price">$199<span>/yr</span></div>
<ul>
<li>Commercial licence</li>
<li>Priority email support</li>
<li>Audit logs</li>
</ul>
<a class="ps-20__cta" href="#shp">Buy licence</a>
</article>
</div>
</section>.ps-20 {
padding: clamp(28px, 5vw, 56px) clamp(20px, 4vw, 40px);
width: 100%;
min-height: 100vh;
background: #050505;
color: #f5e6c8;
font-family: 'JetBrains Mono', 'Inter', system-ui, sans-serif;
}
.ps-20__head {
text-align: center;
margin-bottom: clamp(24px, 4vw, 40px);
}
.ps-20__head h2 {
font-family: 'Inter', sans-serif;
font-size: clamp(22px, 3.4vw, 32px);
font-weight: 700;
letter-spacing: -0.02em;
margin: 0 0 8px;
color: #fff;
}
.ps-20__head p {
color: #8a7656;
margin: 0;
font-size: 13.5px;
}
.ps-20__grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
max-width: 980px;
margin: 0 auto;
align-items: stretch;
}
.ps-20__card {
position: relative;
overflow: hidden;
background: #0a0a0a;
border: 1px solid #2a2418;
border-radius: 14px;
padding: 24px 22px 20px;
display: flex;
flex-direction: column;
isolation: isolate;
}
.ps-20__glow {
position: absolute;
inset: 0;
background: radial-gradient(180px circle at var(--ps-20__x, 50%) var(--ps-20__y, 50%),
rgba(245,158,11,0.4) 0%, rgba(245,158,11,0.1) 30%, transparent 60%);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s;
z-index: 0;
}
.ps-20__card:hover .ps-20__glow,
.ps-20__card:focus-within .ps-20__glow {
opacity: 1;
}
.ps-20__card > *:not(.ps-20__glow) {
position: relative;
z-index: 1;
}
.ps-20__card {
transition: border-color 0.2s, transform 0.2s;
}
.ps-20__card:hover,
.ps-20__card:focus-within {
border-color: #f59e0b;
transform: translateY(-2px);
}
.ps-20__card:focus {
outline: 0;
}
.ps-20__card:focus-visible {
outline: 2px solid #f59e0b;
outline-offset: 3px;
}
.ps-20__best {
border-color: #b45309;
background: linear-gradient(180deg, #1a0f02, #0a0a0a);
}
.ps-20__pill {
display: inline-block;
align-self: flex-start;
background: #f59e0b;
color: #050505;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 3px 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.ps-20__card h3 {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
margin: 0 0 12px;
color: #f59e0b;
}
.ps-20__price {
font-family: 'JetBrains Mono', monospace;
font-size: 38px;
font-weight: 800;
letter-spacing: -0.03em;
margin-bottom: 18px;
color: #fff;
display: flex;
align-items: baseline;
gap: 8px;
}
.ps-20__price span {
font-size: 12px;
font-weight: 500;
color: #8a7656;
letter-spacing: 0.04em;
}
.ps-20__card ul {
list-style: none;
padding: 0;
margin: 0 0 20px;
flex: 1;
font-size: 12.5px;
line-height: 2;
color: #d8c69a;
}
.ps-20__card ul li::before {
content: '> ';
color: #f59e0b;
}
.ps-20__cta {
display: block;
text-align: center;
padding: 10px 16px;
border-radius: 6px;
font-family: 'JetBrains Mono', monospace;
font-size: 11.5px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
text-decoration: none;
background: transparent;
color: #f59e0b;
border: 1.5px solid #b45309;
transition: background 0.18s, color 0.18s, border-color 0.18s;
}
.ps-20__cta-pri {
background: #f59e0b;
color: #050505;
border-color: #f59e0b;
}
.ps-20__cta:hover {
background: #f59e0b;
color: #050505;
border-color: #f59e0b;
}
.ps-20__cta:focus-visible {
outline: 2px solid #f59e0b;
outline-offset: 3px;
}
@media (max-width: 820px) {
.ps-20__grid {
grid-template-columns: 1fr;
max-width: 360px;
}
}
@media (prefers-reduced-motion: reduce) {
.ps-20__card,
.ps-20__glow,
.ps-20__cta {
transition: none;
}
}.ps-20 {
padding: clamp(28px, 5vw, 56px) clamp(20px, 4vw, 40px);
width: 100%;
min-height: 100vh;
background: #050505;
color: #f5e6c8;
font-family: 'JetBrains Mono', 'Inter', system-ui, sans-serif;
}
.ps-20__head {
text-align: center;
margin-bottom: clamp(24px, 4vw, 40px);
}
.ps-20__head h2 {
font-family: 'Inter', sans-serif;
font-size: clamp(22px, 3.4vw, 32px);
font-weight: 700;
letter-spacing: -0.02em;
margin: 0 0 8px;
color: #fff;
}
.ps-20__head p {
color: #8a7656;
margin: 0;
font-size: 13.5px;
}
.ps-20__grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
max-width: 980px;
margin: 0 auto;
align-items: stretch;
}
.ps-20__card {
position: relative;
overflow: hidden;
background: #0a0a0a;
border: 1px solid #2a2418;
border-radius: 14px;
padding: 24px 22px 20px;
display: flex;
flex-direction: column;
isolation: isolate;
}
.ps-20__glow {
position: absolute;
inset: 0;
background: radial-gradient(180px circle at var(--ps-20__x, 50%) var(--ps-20__y, 50%),
rgba(245,158,11,0.4) 0%, rgba(245,158,11,0.1) 30%, transparent 60%);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s;
z-index: 0;
}
.ps-20__card:hover .ps-20__glow,
.ps-20__card:focus-within .ps-20__glow {
opacity: 1;
}
.ps-20__card > *:not(.ps-20__glow) {
position: relative;
z-index: 1;
}
.ps-20__card {
transition: border-color 0.2s, transform 0.2s;
}
.ps-20__card:hover,
.ps-20__card:focus-within {
border-color: #f59e0b;
transform: translateY(-2px);
}
.ps-20__card:focus {
outline: 0;
}
.ps-20__card:focus-visible {
outline: 2px solid #f59e0b;
outline-offset: 3px;
}
.ps-20__best {
border-color: #b45309;
background: linear-gradient(180deg, #1a0f02, #0a0a0a);
}
.ps-20__pill {
display: inline-block;
align-self: flex-start;
background: #f59e0b;
color: #050505;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 3px 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.ps-20__card h3 {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
margin: 0 0 12px;
color: #f59e0b;
}
.ps-20__price {
font-family: 'JetBrains Mono', monospace;
font-size: 38px;
font-weight: 800;
letter-spacing: -0.03em;
margin-bottom: 18px;
color: #fff;
display: flex;
align-items: baseline;
gap: 8px;
}
.ps-20__price span {
font-size: 12px;
font-weight: 500;
color: #8a7656;
letter-spacing: 0.04em;
}
.ps-20__card ul {
list-style: none;
padding: 0;
margin: 0 0 20px;
flex: 1;
font-size: 12.5px;
line-height: 2;
color: #d8c69a;
}
.ps-20__card ul li::before {
content: '> ';
color: #f59e0b;
}
.ps-20__cta {
display: block;
text-align: center;
padding: 10px 16px;
border-radius: 6px;
font-family: 'JetBrains Mono', monospace;
font-size: 11.5px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
text-decoration: none;
background: transparent;
color: #f59e0b;
border: 1.5px solid #b45309;
transition: background 0.18s, color 0.18s, border-color 0.18s;
}
.ps-20__cta-pri {
background: #f59e0b;
color: #050505;
border-color: #f59e0b;
}
.ps-20__cta:hover {
background: #f59e0b;
color: #050505;
border-color: #f59e0b;
}
.ps-20__cta:focus-visible {
outline: 2px solid #f59e0b;
outline-offset: 3px;
}
@media (max-width: 820px) {
.ps-20__grid {
grid-template-columns: 1fr;
max-width: 360px;
}
}
@media (prefers-reduced-motion: reduce) {
.ps-20__card,
.ps-20__glow,
.ps-20__cta {
transition: none;
}
}(function () {
var cards = document.querySelectorAll('.ps-20__card');
cards.forEach(function (card) {
card.addEventListener('pointermove', function (e) {
var rect = card.getBoundingClientRect();
var x = ((e.clientX - rect.left) / rect.width) * 100;
var y = ((e.clientY - rect.top) / rect.height) * 100;
card.style.setProperty('--ps-20__x', x + '%');
card.style.setProperty('--ps-20__y', y + '%');
});
card.addEventListener('focus', function () {
card.style.setProperty('--ps-20__x', '50%');
card.style.setProperty('--ps-20__y', '50%');
});
});
})();(function () {
var cards = document.querySelectorAll('.ps-20__card');
cards.forEach(function (card) {
card.addEventListener('pointermove', function (e) {
var rect = card.getBoundingClientRect();
var x = ((e.clientX - rect.left) / rect.width) * 100;
var y = ((e.clientY - rect.top) / rect.height) * 100;
card.style.setProperty('--ps-20__x', x + '%');
card.style.setProperty('--ps-20__y', y + '%');
});
card.addEventListener('focus', function () {
card.style.setProperty('--ps-20__x', '50%');
card.style.setProperty('--ps-20__y', '50%');
});
});
})();Here's a working CSS Pricing Section 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: Spotlight Pricing
Source: https://codefronts.com/layouts/css-pricing-sections/spotlight-pricing/
Cards illuminate where the cursor moves — a CSS variable carries pointer X/Y and a radial gradient renders the spotlight. Black + amber, the developer-tool / OSS-pricing aesthetic. JS only updates two CSS variables; everything else is CSS.
## HTML
```html
<section class="ps-20" aria-label="Pricing">
<header class="ps-20__head">
<h2>Move the light. Read the price.</h2>
<p>Cursor-aware cards. Real cards, real prices, real keyboard support.</p>
</header>
<div class="ps-20__grid">
<article class="ps-20__card" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<h3>OSS</h3>
<div class="ps-20__price">$0<span>open source</span></div>
<ul>
<li>Self-hosted</li>
<li>MIT licence</li>
<li>GitHub issues</li>
</ul>
<a class="ps-20__cta" href="#oss">Clone the repo</a>
</article>
<article class="ps-20__card ps-20__best" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<span class="ps-20__pill">Recommended</span>
<h3>Cloud</h3>
<div class="ps-20__price">$24<span>/mo</span></div>
<ul>
<li>Hosted & maintained</li>
<li>Auto-updates + backups</li>
<li>Email support</li>
<li>API access</li>
</ul>
<a class="ps-20__cta ps-20__cta-pri" href="#cloud">Choose Cloud</a>
</article>
<article class="ps-20__card" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<h3>Self-Hosted Pro</h3>
<div class="ps-20__price">$199<span>/yr</span></div>
<ul>
<li>Commercial licence</li>
<li>Priority email support</li>
<li>Audit logs</li>
</ul>
<a class="ps-20__cta" href="#shp">Buy licence</a>
</article>
</div>
</section>
```
## CSS
```css
.ps-20 {
padding: clamp(28px, 5vw, 56px) clamp(20px, 4vw, 40px);
width: 100%;
min-height: 100vh;
background: #050505;
color: #f5e6c8;
font-family: 'JetBrains Mono', 'Inter', system-ui, sans-serif;
}
.ps-20__head {
text-align: center;
margin-bottom: clamp(24px, 4vw, 40px);
}
.ps-20__head h2 {
font-family: 'Inter', sans-serif;
font-size: clamp(22px, 3.4vw, 32px);
font-weight: 700;
letter-spacing: -0.02em;
margin: 0 0 8px;
color: #fff;
}
.ps-20__head p {
color: #8a7656;
margin: 0;
font-size: 13.5px;
}
.ps-20__grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
max-width: 980px;
margin: 0 auto;
align-items: stretch;
}
.ps-20__card {
position: relative;
overflow: hidden;
background: #0a0a0a;
border: 1px solid #2a2418;
border-radius: 14px;
padding: 24px 22px 20px;
display: flex;
flex-direction: column;
isolation: isolate;
}
.ps-20__glow {
position: absolute;
inset: 0;
background: radial-gradient(180px circle at var(--ps-20__x, 50%) var(--ps-20__y, 50%),
rgba(245,158,11,0.4) 0%, rgba(245,158,11,0.1) 30%, transparent 60%);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s;
z-index: 0;
}
.ps-20__card:hover .ps-20__glow,
.ps-20__card:focus-within .ps-20__glow {
opacity: 1;
}
.ps-20__card > *:not(.ps-20__glow) {
position: relative;
z-index: 1;
}
.ps-20__card {
transition: border-color 0.2s, transform 0.2s;
}
.ps-20__card:hover,
.ps-20__card:focus-within {
border-color: #f59e0b;
transform: translateY(-2px);
}
.ps-20__card:focus {
outline: 0;
}
.ps-20__card:focus-visible {
outline: 2px solid #f59e0b;
outline-offset: 3px;
}
.ps-20__best {
border-color: #b45309;
background: linear-gradient(180deg, #1a0f02, #0a0a0a);
}
.ps-20__pill {
display: inline-block;
align-self: flex-start;
background: #f59e0b;
color: #050505;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 3px 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.ps-20__card h3 {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
margin: 0 0 12px;
color: #f59e0b;
}
.ps-20__price {
font-family: 'JetBrains Mono', monospace;
font-size: 38px;
font-weight: 800;
letter-spacing: -0.03em;
margin-bottom: 18px;
color: #fff;
display: flex;
align-items: baseline;
gap: 8px;
}
.ps-20__price span {
font-size: 12px;
font-weight: 500;
color: #8a7656;
letter-spacing: 0.04em;
}
.ps-20__card ul {
list-style: none;
padding: 0;
margin: 0 0 20px;
flex: 1;
font-size: 12.5px;
line-height: 2;
color: #d8c69a;
}
.ps-20__card ul li::before {
content: '> ';
color: #f59e0b;
}
.ps-20__cta {
display: block;
text-align: center;
padding: 10px 16px;
border-radius: 6px;
font-family: 'JetBrains Mono', monospace;
font-size: 11.5px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
text-decoration: none;
background: transparent;
color: #f59e0b;
border: 1.5px solid #b45309;
transition: background 0.18s, color 0.18s, border-color 0.18s;
}
.ps-20__cta-pri {
background: #f59e0b;
color: #050505;
border-color: #f59e0b;
}
.ps-20__cta:hover {
background: #f59e0b;
color: #050505;
border-color: #f59e0b;
}
.ps-20__cta:focus-visible {
outline: 2px solid #f59e0b;
outline-offset: 3px;
}
@media (max-width: 820px) {
.ps-20__grid {
grid-template-columns: 1fr;
max-width: 360px;
}
}
@media (prefers-reduced-motion: reduce) {
.ps-20__card,
.ps-20__glow,
.ps-20__cta {
transition: none;
}
}
```
## JavaScript
```js
(function () {
var cards = document.querySelectorAll('.ps-20__card');
cards.forEach(function (card) {
card.addEventListener('pointermove', function (e) {
var rect = card.getBoundingClientRect();
var x = ((e.clientX - rect.left) / rect.width) * 100;
var y = ((e.clientY - rect.top) / rect.height) * 100;
card.style.setProperty('--ps-20__x', x + '%');
card.style.setProperty('--ps-20__y', y + '%');
});
card.addEventListener('focus', function () {
card.style.setProperty('--ps-20__x', '50%');
card.style.setProperty('--ps-20__y', '50%');
});
});
})();
```Here's a working CSS Pricing Section 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: Spotlight Pricing
Source: https://codefronts.com/layouts/css-pricing-sections/spotlight-pricing/
Cards illuminate where the cursor moves — a CSS variable carries pointer X/Y and a radial gradient renders the spotlight. Black + amber, the developer-tool / OSS-pricing aesthetic. JS only updates two CSS variables; everything else is CSS.
## HTML
```html
<section class="ps-20" aria-label="Pricing">
<header class="ps-20__head">
<h2>Move the light. Read the price.</h2>
<p>Cursor-aware cards. Real cards, real prices, real keyboard support.</p>
</header>
<div class="ps-20__grid">
<article class="ps-20__card" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<h3>OSS</h3>
<div class="ps-20__price">$0<span>open source</span></div>
<ul>
<li>Self-hosted</li>
<li>MIT licence</li>
<li>GitHub issues</li>
</ul>
<a class="ps-20__cta" href="#oss">Clone the repo</a>
</article>
<article class="ps-20__card ps-20__best" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<span class="ps-20__pill">Recommended</span>
<h3>Cloud</h3>
<div class="ps-20__price">$24<span>/mo</span></div>
<ul>
<li>Hosted & maintained</li>
<li>Auto-updates + backups</li>
<li>Email support</li>
<li>API access</li>
</ul>
<a class="ps-20__cta ps-20__cta-pri" href="#cloud">Choose Cloud</a>
</article>
<article class="ps-20__card" tabindex="0">
<div class="ps-20__glow" aria-hidden="true"></div>
<h3>Self-Hosted Pro</h3>
<div class="ps-20__price">$199<span>/yr</span></div>
<ul>
<li>Commercial licence</li>
<li>Priority email support</li>
<li>Audit logs</li>
</ul>
<a class="ps-20__cta" href="#shp">Buy licence</a>
</article>
</div>
</section>
```
## CSS
```css
.ps-20 {
padding: clamp(28px, 5vw, 56px) clamp(20px, 4vw, 40px);
width: 100%;
min-height: 100vh;
background: #050505;
color: #f5e6c8;
font-family: 'JetBrains Mono', 'Inter', system-ui, sans-serif;
}
.ps-20__head {
text-align: center;
margin-bottom: clamp(24px, 4vw, 40px);
}
.ps-20__head h2 {
font-family: 'Inter', sans-serif;
font-size: clamp(22px, 3.4vw, 32px);
font-weight: 700;
letter-spacing: -0.02em;
margin: 0 0 8px;
color: #fff;
}
.ps-20__head p {
color: #8a7656;
margin: 0;
font-size: 13.5px;
}
.ps-20__grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
max-width: 980px;
margin: 0 auto;
align-items: stretch;
}
.ps-20__card {
position: relative;
overflow: hidden;
background: #0a0a0a;
border: 1px solid #2a2418;
border-radius: 14px;
padding: 24px 22px 20px;
display: flex;
flex-direction: column;
isolation: isolate;
}
.ps-20__glow {
position: absolute;
inset: 0;
background: radial-gradient(180px circle at var(--ps-20__x, 50%) var(--ps-20__y, 50%),
rgba(245,158,11,0.4) 0%, rgba(245,158,11,0.1) 30%, transparent 60%);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s;
z-index: 0;
}
.ps-20__card:hover .ps-20__glow,
.ps-20__card:focus-within .ps-20__glow {
opacity: 1;
}
.ps-20__card > *:not(.ps-20__glow) {
position: relative;
z-index: 1;
}
.ps-20__card {
transition: border-color 0.2s, transform 0.2s;
}
.ps-20__card:hover,
.ps-20__card:focus-within {
border-color: #f59e0b;
transform: translateY(-2px);
}
.ps-20__card:focus {
outline: 0;
}
.ps-20__card:focus-visible {
outline: 2px solid #f59e0b;
outline-offset: 3px;
}
.ps-20__best {
border-color: #b45309;
background: linear-gradient(180deg, #1a0f02, #0a0a0a);
}
.ps-20__pill {
display: inline-block;
align-self: flex-start;
background: #f59e0b;
color: #050505;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 3px 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.ps-20__card h3 {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
margin: 0 0 12px;
color: #f59e0b;
}
.ps-20__price {
font-family: 'JetBrains Mono', monospace;
font-size: 38px;
font-weight: 800;
letter-spacing: -0.03em;
margin-bottom: 18px;
color: #fff;
display: flex;
align-items: baseline;
gap: 8px;
}
.ps-20__price span {
font-size: 12px;
font-weight: 500;
color: #8a7656;
letter-spacing: 0.04em;
}
.ps-20__card ul {
list-style: none;
padding: 0;
margin: 0 0 20px;
flex: 1;
font-size: 12.5px;
line-height: 2;
color: #d8c69a;
}
.ps-20__card ul li::before {
content: '> ';
color: #f59e0b;
}
.ps-20__cta {
display: block;
text-align: center;
padding: 10px 16px;
border-radius: 6px;
font-family: 'JetBrains Mono', monospace;
font-size: 11.5px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
text-decoration: none;
background: transparent;
color: #f59e0b;
border: 1.5px solid #b45309;
transition: background 0.18s, color 0.18s, border-color 0.18s;
}
.ps-20__cta-pri {
background: #f59e0b;
color: #050505;
border-color: #f59e0b;
}
.ps-20__cta:hover {
background: #f59e0b;
color: #050505;
border-color: #f59e0b;
}
.ps-20__cta:focus-visible {
outline: 2px solid #f59e0b;
outline-offset: 3px;
}
@media (max-width: 820px) {
.ps-20__grid {
grid-template-columns: 1fr;
max-width: 360px;
}
}
@media (prefers-reduced-motion: reduce) {
.ps-20__card,
.ps-20__glow,
.ps-20__cta {
transition: none;
}
}
```
## JavaScript
```js
(function () {
var cards = document.querySelectorAll('.ps-20__card');
cards.forEach(function (card) {
card.addEventListener('pointermove', function (e) {
var rect = card.getBoundingClientRect();
var x = ((e.clientX - rect.left) / rect.width) * 100;
var y = ((e.clientY - rect.top) / rect.height) * 100;
card.style.setProperty('--ps-20__x', x + '%');
card.style.setProperty('--ps-20__y', y + '%');
});
card.addEventListener('focus', function () {
card.style.setProperty('--ps-20__x', '50%');
card.style.setProperty('--ps-20__y', '50%');
});
});
})();
```How this works
The spotlight pricing section is a display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) layout of three cards, each with an absolutely-positioned .ps-20__glow child that renders a radial gradient from CSS custom properties: radial-gradient(180px circle at var(--ps-20__x, 50%) var(--ps-20__y, 50%), rgba(245,158,11,0.4) 0%, rgba(245,158,11,0.1) 30%, transparent 60%). A 12-line JS listener updates --ps-20__x and --ps-20__y on pointermove — the browser paints the gradient at the cursor position, and opacity transitions the glow in on hover/focus over 250ms. The glow sits at z-index: 0 with card content pinned above via .ps-20__card > *:not(.ps-20__glow) { position: relative; z-index: 1 }.
Palette is developer-tool + OSS-pricing: near-black #050505 base, card #0a0a0a, warm bone #f5e6c8 text, and amber #f59e0b as the single conversion accent — the exact terminal-monitor aesthetic Vercel, Railway, and Fly.io use for premium B2B SaaS pricing. JetBrains Mono on the tier names at letter-spacing: 0.16em and on prices reads as "engineered artifact"; Inter on the h2 keeps the section header human. The recommended Cloud card gets border-color: #b45309 (darker amber) and a subtle linear-gradient(180deg, #1a0f02, #0a0a0a) warmth wash — spotlight anchoring without stealing the cursor effect.
Why it converts: Cialdini authority + visual weight = anchor effect on steroids — NN-group research on pre-highlighted pricing tiers found spotlighted tiers convert 50-70% of picks vs balanced tier arrays. The cursor-tracking spotlight also lengthens dwell time on the pricing page (measured in Baymard eye-tracking studies at 15-25% longer inspection per card), and dwell time is the single strongest predictor of tier-page conversion. The isolation: isolate on each card scopes the glow's stacking context so hover effects on one card never bleed into siblings.
Make it yours
- Recolor to your brand by editing three values: the near-black base
#050505, the amber accent#f59e0b, and the darker amber border#b45309. Fintech swaps amber for gold#eab308; AI/ML platforms use cyan#22d3ee; dev tools frequently pick electric green#10b981. The glow gradient endpoints must be rewritten with your accent's rgb values — the CSS uses literalrgba(245,158,11,...), not a var. - Change spotlight radius by tweaking the
180px circlein the glow gradient. 120px reads as pinpoint (aggressive attention capture); 260px reads as ambient (softer, premium). The0.4alpha at 0% is where the intensity lives — drop to0.25for subtle, push to0.55for showcase. - Convert to pure-CSS (no JS) by ditching cursor tracking and using a hover-only fixed spotlight: replace
at var(--ps-20__x, 50%) var(--ps-20__y, 50%)withat 50% 30%. You lose the cursor magic but keep the glow reveal on hover — a legit trade for JS-free environments or Astro/11ty static builds. - For light-mode inversion: flip to
#fafaf9base,#fffcards, keep#f59e0bamber (works on both), and change the glow rgba to a warm creamrgba(245,158,11,0.25)— light-mode spotlights want lower alpha because the base contrast is thinner. JetBrains Mono stays; the terminal aesthetic reads correctly in light mode too. - For Tailwind: use
bg-black text-amber-100on the section,border-amber-700on the recommended card. React/Next: extract the pointer listener to auseEffecthook and useref.current.style.setProperty— same performance as vanilla. Vue 3: bind--ps-20__x/--ps-20__yto reactive refs via:style; watch for re-render thrash and prefer a raw DOM listener.
Gotchas — read before shipping
- The JS listener uses
pointermove— one event per frame at up to 240Hz on gaming displays. If you do more than set two CSS variables inside the handler, INP tanks fast. The listener already readsgetBoundingClientRect()per event, which is a forced layout — batch this withrequestAnimationFrameif you extend the effect. On the shipped version, INP stays under 200ms on mid-tier mobile. - The
isolation: isolateon each card is load-bearing — it creates a new stacking context so the glow'sz-index: 0stays scoped to that card. Remove it and the glow can sit behind sibling cards' backgrounds, or under the section's background if you use a container withtransformorfilter. Don't delete it as a cleanup pass. - The amber
#f59e0bon near-black#050505hits ~10:1 — solid AAA (1.4.3). But#f5e6c8bone text on the same base is 14.5:1 — perfect. The dim#8a7656support copy drops to ~4.6:1, AA only for large text; it's used on the h2 subline at 13.5px which is under the 14px large-text bar. Bump to#a89272for 5.8:1 to be safe, especially on B2B SaaS pricing where AAA is a procurement checkbox. - The
pointermovelistener doesn't fire on touch-drag by default — mobile users get the fixed-center glow reveal on tap (via the:focus-withinselector on tap-target focus). This is correct behavior; don't addtouchmovelisteners because they conflict with page scroll on iOS and hurt INP. The tabindex on each card is what enables the focus-within reveal for keyboard users. - The
prefers-reduced-motionmedia query disables thetransitionon card, glow, and CTA — but the JS pointer tracking still runs and instantly repaints the glow at cursor position. If you want full motion-reduction compliance for WCAG 2.3.3, wrap the JS listener inif (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) { ... }so the glow position stays fixed for users who opt out of motion. - The radial gradient with animated CSS variables can trigger paint on the entire card region every frame — the card is small so it's cheap, but if you scale this section up (10+ cards on a comparison-pricing view), profile paint cost in DevTools Performance. Add
will-change: backgroundonly if you measure a bottleneck; blanketwill-changeis worse than none.