OsirionPricingTable and OsirionFeatureComparison
OsirionPricingTable shows plans side by side, each with a price, a short description, its features and a call to
action, with the recommended plan set apart. OsirionFeatureComparison follows it with a table that compares the
plans feature by feature. Both render without script.
Available from 4.1.
Usage
The plans and rows are data types from Osirion.Blazor.Core.Models:
@using Osirion.Blazor.Components
@using Osirion.Blazor.Core.Models
<OsirionPricingTable Plans="@plans" />
<OsirionFeatureComparison PlanNames="@planNames" Rows="@rows" />
@code {
private readonly PricingPlan[] plans =
[
new()
{
Name = "Starter",
Price = "Free",
Description = "For a personal site.",
Features = ["One site", "Community support"],
CallToActionText = "Start",
CallToActionUrl = "/signup"
},
new()
{
Name = "Team",
Price = "$29",
Period = "per month",
Description = "For teams publishing every week.",
Features = ["Five sites", "Email support", "Scheduled posts"],
CallToActionText = "Start a trial",
CallToActionUrl = "/signup?plan=team",
Highlighted = true,
Badge = "Most popular"
},
new()
{
Name = "Enterprise",
Price = "Custom",
Description = "For organizations with many sites.",
Features = ["Unlimited sites", "Support agreement"],
CallToActionText = "Contact sales",
CallToActionUrl = "mailto:sales@example.org"
},
];
private readonly string[] planNames = ["Starter", "Team", "Enterprise"];
private readonly FeatureComparisonRow[] rows =
[
new("Sites", ["1", "5", "Unlimited"]),
new("Scheduled posts", [false, true, true]),
new("Single sign-on", [false, "Add-on", true]),
];
}
A cell is a ComparisonCell. A bool converts to a check mark (true) or a dash (false), and a string to text,
so rows can mix both. ComparisonCell.Yes and ComparisonCell.No are the same marks written out.
Parameters
OsirionPricingTable
| Parameter | Type | Default | Description |
|---|---|---|---|
Plans |
IReadOnlyList<PricingPlan> |
[] |
The plans, in the order shown. Required. With no plans, nothing is rendered. |
Class |
string? |
null |
Extra CSS classes on the list. |
PricingPlan is a record:
| Property | Type | Description |
|---|---|---|
Name |
string |
The plan's name. Required. |
Price |
string |
The price as shown, with its currency, such as $29 or Free. Required. |
Period |
string? |
What the price covers, such as per month. |
Description |
string? |
One line on who the plan is for. |
Features |
IReadOnlyList<string> |
Features listed under the price. Empty by default. |
CallToActionText |
string? |
Label of the plan's button. |
CallToActionUrl |
string? |
Where the button goes. |
Highlighted |
bool |
Marks the recommended plan: a colored border and the primary button. |
Badge |
string? |
A short label on the plan's top border, such as Most popular. |
OsirionFeatureComparison
| Parameter | Type | Default | Description |
|---|---|---|---|
PlanNames |
IReadOnlyList<string> |
[] |
One column per plan. Required. |
Rows |
IReadOnlyList<FeatureComparisonRow> |
[] |
One row per feature. Required. With no plans or no rows, nothing is rendered. |
Caption |
string |
"Compare plans" |
The table's caption, which also names the scrolling region. |
ShowCaption |
bool |
false |
Shows the caption; otherwise only screen readers announce it. |
FeatureColumnText |
string |
"Feature" |
Header of the feature column, read by screen readers only. |
IncludedText |
string |
"Included" |
What screen readers say for a check mark. |
NotIncludedText |
string |
"Not included" |
What screen readers say for a dash. |
Class |
string? |
null |
Extra CSS classes on the wrapper. |
FeatureComparisonRow(string Feature, IReadOnlyList<ComparisonCell> Values) holds one value per plan, in the order
of PlanNames; missing values leave the cell empty.
Both components write unmatched attributes to their root element.
Behavior
- Both render plain HTML and need no script.
- The pricing table is a
<ul>withrole="list", so screen readers still announce it as a list of plans after its bullets are removed. - A plan's button is rendered only when it has
CallToActionTextand a safeCallToActionUrl: a path starting with/, or anhttp,httpsormailtoaddress. Anything else, such as ajavascript:URL or a relative path without the leading slash, leaves the button out. - Each button's accessible name includes the plan, for example "Start a trial: Team", so a list of buttons with the same text stays distinguishable. The highlighted plan gets the CSS framework's primary button class, the others the secondary one.
- The comparison is a real
<table>with a caption, column headers for the plans and row headers for the features. Check marks and dashes are drawn in CSS and hidden from screen readers, which readIncludedTextorNotIncludedTextinstead. - The table keeps a minimum width of 32rem and scrolls sideways inside its wrapper on narrow screens. The wrapper is a focusable region named by the caption, so keyboard users can scroll it.
- All text is HTML-encoded.
Styling
The pricing table is a grid of columns at least 15rem wide that wraps on narrow screens. Classes:
osirion-pricing-table, osirion-pricing-plan (and osirion-pricing-plan-highlighted), osirion-pricing-badge,
osirion-pricing-name, osirion-pricing-price with osirion-pricing-amount and osirion-pricing-period,
osirion-pricing-description, osirion-pricing-features and osirion-pricing-action.
The comparison uses osirion-feature-comparison (the scrolling wrapper), osirion-feature-comparison-table,
osirion-feature-comparison-caption, osirion-feature-comparison-yes and osirion-feature-comparison-no.
Both take their colors from the theme tokens: --osirion-action-primary for the highlighted border, badge and check
marks, --osirion-action-primary-text for the badge text, --osirion-border-color, --osirion-background-primary,
--osirion-text-primary, --osirion-text-secondary and --osirion-text-tertiary for the dashes.
Related
Related items

