Three-Tier Pricing
The canonical Starter / Pro / Enterprise grid, with the middle tier promoted.
6 फ्रेमवर्कशुरुआती
A seat slider that recomputes the price live, built on a native range input.
<!--
A native <input type="range">, not a div with a drag handler. It arrives with
arrow keys, Home/End, Page Up/Down, the correct touch target, and the
platform's own high-contrast rendering - none of which a custom slider gets
without hundreds of lines, and most of which it never gets at all.
The price goes in an <output>, which maps to role="status" and is therefore
already a polite live region: the recomputed total announces itself without an
explicit aria-live. It is spelled out anyway because a few screen-reader and
browser pairings still do not map <output> to a live region, and a silent
price is the one failure this component cannot afford.
Note aria-live on the OUTPUT, not on a wrapper - a live region announces its
own subtree, so wrapping it would re-announce the label on every drag.
-->
<form class="usage" aria-labelledby="usage-heading">
<h3 class="usage__heading" id="usage-heading">Team</h3>
<p class="usage__blurb">Pay for the seats you use. Every feature is included at every size.</p>
<div class="usage__field">
<label class="usage__label" for="usage-seats">Team members</label>
<!--
The range's own value is announced as a bare number ("12"), so
aria-valuetext gives it a unit. Without it the control says "12" and the
user has to guess what of.
-->
<input
class="usage__range"
id="usage-seats"
type="range"
min="1"
max="50"
step="1"
value="5"
aria-valuetext="5 seats"
/>
<div class="usage__scale" aria-hidden="true">
<span>1</span>
<span>50</span>
</div>
</div>
<p class="usage__result">
<output class="usage__output" for="usage-seats" aria-live="polite">
<span class="usage__total">$60</span>
<span class="usage__period">/month</span>
<span class="usage__breakdown">5 seats × $12</span>
</output>
</p>
<a class="usage__cta" href="#">Start free trial</a>
</form>
<script>
document.querySelectorAll('.usage').forEach(function (root) {
var range = root.querySelector('.usage__range');
var total = root.querySelector('.usage__total');
var breakdown = root.querySelector('.usage__breakdown');
var PER_SEAT = 12;
function render() {
var seats = Number(range.value);
total.textContent = '$' + seats * PER_SEAT;
breakdown.textContent = seats + (seats === 1 ? ' seat' : ' seats') + ' × $' + PER_SEAT;
range.setAttribute('aria-valuetext', seats + (seats === 1 ? ' seat' : ' seats'));
}
// 'input' rather than 'change': change only fires when the drag ends, so the
// price would freeze mid-gesture - exactly when the user is watching it.
range.addEventListener('input', render);
render();
});
</script>| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
name | string | 'Team' | हेडिंग के रूप में दिखने वाला प्लान का नाम। |
ctaLabel | string | 'Start free trial' | कॉल-टू-एक्शन बटन का टेक्स्ट। |
ctaHref | string | '#' | कॉल-टू-एक्शन लिंक का डेस्टिनेशन। |
className | string | - | रूट एलिमेंट पर मर्ज होने वाली अतिरिक्त क्लासेज़। |
It is a native `<input type="range">`, not a div with a drag handler: arrow keys, Home/End, Page Up/Down, the correct touch target and the platform’s forced-colours rendering all arrive for free, and a custom slider gets most of them only after hundreds of lines - some of them never. Three things make the price trustworthy. The listener is `input`, not `change`, or the total would freeze mid-drag, exactly when the user is watching it. The price sits in an `<output>`, which maps to `role="status"` and is already a polite live region; `aria-live` is spelled out anyway because a few screen-reader and browser pairings still do not map `<output>`, and a silent price is the one failure this component cannot afford - note it goes on the output itself, not a wrapper, or every drag re-announces the label. And `aria-valuetext` gives the value a unit, since the raw control announces a bare "12" and leaves the user to guess what of. Change `PER_SEAT` to reprice; move to integer cents the moment a price gains a decimal, because `12.99 * 3` is already `38.97000000000001`.