Pricing comparison
A polished three-plan comparison with a highlighted recommendation and usable action callbacks.
Live preview
Adjust a setting to update the preview and usage code together.
Usage & source
"use client";
import type { CSSProperties } from "react";
import { PricingComparison } from "./components/stepkit/PricingComparison";
import "./components/stepkit/styles.css";
const themeStyle = {"--stepkit-bg":"#f7f7f8","--stepkit-surface":"#ffffff","--stepkit-surface-muted":"#f0f0f3","--stepkit-fg":"#17181c","--stepkit-muted":"#626572","--stepkit-border":"#e3e4e9","--stepkit-border-strong":"#c8cad3","--stepkit-code":"#f1f1f4","--stepkit-shadow":"0 20px 60px rgba(31, 26, 64, .10)","--stepkit-accent":"#6950df","--stepkit-accent-strong":"#5137c7","--stepkit-accent-soft":"#eeeafd","--stepkit-accent-on-soft":"#5137c7","--stepkit-accent-text":"#ffffff","--stepkit-error":"#b43145","--stepkit-placeholder":"#626572","--stepkit-radius":"16px","--stepkit-space":"16px","--stepkit-control-height":"44px","--stepkit-font-sans":"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif"} as CSSProperties;
export default function Example() {
return (
<div className="stepkit-theme" data-stepkit-preset="modern" style={themeStyle}>
<PricingComparison
sample={true}
/>
</div>
);
}Dependencies
[email protected] [email protected]
/*
* Stepkit 0.2.0
* Copyright (c) 2026 Vladislav Stepanov
*
* Original Stepkit portions in this release are licensed under the MIT License. Adapted portions identify their upstream licence and copyright in the copied source and in `NOTICE-MAGICUI.md` where applicable.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
"use client";
import { Button } from "./Button";
import "./styles.css";
export interface PricingPlan { name: string; price: string; interval?: string; description: string; features: string[]; ctaLabel?: string; recommended?: boolean; }
export interface PricingComparisonProps { plans?: PricingPlan[]; sample?: boolean; onSelect?: (plan: PricingPlan) => void; className?: string; }
const defaults: PricingPlan[] = [
{ name: "Starter", price: "$0", interval: "forever", description: "For exploring the system.", features: ["All foundations", "Local theme tokens", "MIT source"], ctaLabel: "Try starter" },
{ name: "Studio", price: "$24", interval: "/ month", description: "For a small product team.", features: ["Every component", "Export-ready blocks", "Priority patterns"], ctaLabel: "Choose studio", recommended: true },
{ name: "Scale", price: "$68", interval: "/ month", description: "For teams shaping a suite.", features: ["Shared conventions", "Advanced composition", "Team documentation"], ctaLabel: "Talk to us" }
];
export function PricingComparison({ plans = defaults, sample = true, onSelect, className = "" }: PricingComparisonProps) { return <section className={`stepkit stepkit-pricing-comparison${className ? ` ${className}` : ""}`} aria-label={sample ? "Sample pricing comparison" : "Pricing comparison"}>{sample ? <p className="stepkit-card__eyebrow">Sample pricing</p> : null}{plans.slice(0, 3).map((plan) => <article className="stepkit-pricing-comparison__plan" data-recommended={plan.recommended} key={plan.name}>{plan.recommended ? <span className="stepkit-pricing-comparison__recommended">Recommended</span> : null}<span className="stepkit-pricing-comparison__name">{plan.name}</span><p className="stepkit-pricing-comparison__description">{plan.description}</p><div><strong className="stepkit-pricing-comparison__price">{plan.price}</strong><span className="stepkit-pricing-comparison__interval">{plan.interval}</span></div><ul className="stepkit-pricing-comparison__features">{plan.features.map((feature) => <li className="stepkit-pricing-comparison__feature" key={feature}>{feature}</li>)}</ul><Button className="stepkit-pricing-comparison__action" label={plan.ctaLabel ?? "Select"} variant={plan.recommended ? "solid" : "outline"} onClick={() => onSelect?.(plan)} /></article>)}</section>; }
export default PricingComparison;
components/PricingComparison.tsxcomponents/Button.tsxcomponents/styles.cssInstall
Use the released registry entry or download the complete archive.
npx [email protected] add https://vstepanov.com/stepkit/r/pricing-comparison.jsonIntegration prompt
Install Stepkit Pricing comparison 0.2.0 from the registry and preserve the included MIT notice. npx [email protected] add https://vstepanov.com/stepkit/v0.2.0/r/pricing-comparison.json Install the tested runtime dependencies exactly: npm install [email protected] [email protected] Keep the component source, shared styles, NOTICE.md, and provenance files together. Apply the theme tokens on a wrapper rather than passing theme keys as component props. Usage: ```tsx "use client"; import type { CSSProperties } from "react"; import { PricingComparison } from "./components/stepkit/PricingComparison"; import "./components/stepkit/styles.css"; const themeStyle = {"--stepkit-bg":"#f7f7f8","--stepkit-surface":"#ffffff","--stepkit-surface-muted":"#f0f0f3","--stepkit-fg":"#17181c","--stepkit-muted":"#626572","--stepkit-border":"#e3e4e9","--stepkit-border-strong":"#c8cad3","--stepkit-code":"#f1f1f4","--stepkit-shadow":"0 20px 60px rgba(31, 26, 64, .10)","--stepkit-accent":"#6950df","--stepkit-accent-strong":"#5137c7","--stepkit-accent-soft":"#eeeafd","--stepkit-accent-on-soft":"#5137c7","--stepkit-accent-text":"#ffffff","--stepkit-error":"#b43145","--stepkit-placeholder":"#626572","--stepkit-radius":"16px","--stepkit-space":"16px","--stepkit-control-height":"44px","--stepkit-font-sans":"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif"} as CSSProperties; export default function Example() { return ( <div className="stepkit-theme" data-stepkit-preset="modern" style={themeStyle}> <PricingComparison sample={true} /> </div> ); } ``` Selected bounded settings: {"sample":true}. Selected theme: {"mode":"light","accent":"violet","radius":"soft","density":"comfortable","preset":"modern"}.
Props
| Name | Type | Default | Description |
|---|---|---|---|
plans | PricingPlan[] | default plans | Up to three plans. |
sample | boolean | true | Labels the built-in invented content as sample content. |
onSelect | (plan: PricingPlan) => void | undefined | Called after selecting a plan. |
Accessibility & limits
- Plans use articles, headings, lists, and native buttons.
- Recommended state is paired with text.
Limitations
- Built-in prices and plan names are explicitly sample content.
Provenance
Original source under the MIT licence.
Original Stepkit implementation.
Required notices: NOTICE.md, PROVENANCE-stepkit-pricing-comparison.md
- 3 source files in the release
- Version 0.2.0