Pricing
02 - Choose a pricing card
Three pricing cards with a plan name, short description, price, purchase button, and feature list. The middle plan has a Popular badge at the top right, and the cards stack on narrow screens.
---
import { CheckIcon } from '@lism-css/icons/astro';
import DemoLayout from '@/layouts/DemoLayout.astro';
import { AutoColumns, Divider, Container, Flex, Grid, Heading, Icon, Inline, Stack, Text } from 'lism-css/astro';
import { Badge } from '@lism-css/ui/astro/Badge';
import { Button } from '@lism-css/ui/astro/Button';
import { Tabs } from '@lism-css/ui/astro/Tabs';
import './_style.css';
const plans = [
{
name: 'Plus',
description: 'For getting started',
price: '$9.80',
yearlyPrice: '$98',
button: 'Choose this plan',
recommended: false,
features: ['1 member', 'Up to 3 projects', '2GB storage', 'All core features'],
},
{
name: 'Pro',
description: 'For growing teams',
price: '$19.80',
yearlyPrice: '$198',
button: 'Choose this plan',
recommended: true,
features: ['Up to 10 members', 'Up to 50 projects', '100GB storage', 'External integrations', 'Chat support'],
},
{
name: 'Premium',
description: 'For larger teams',
price: '$99.80',
yearlyPrice: '$998',
button: 'Choose this plan',
recommended: false,
features: ['Unlimited members', 'Unlimited projects', '1TB storage', 'External integrations', 'Advanced permissions', 'Priority support'],
},
];
const billingPeriods = [
{ label: 'Monthly', isYearly: false, unit: '/mo' },
{ label: 'Yearly', isYearly: true, unit: '/yr' },
];
---
<DemoLayout title="02 - Choose a pricing card">
<Container as="section" lang="en" aria-label="Pricing plans" isWrapper="l" hasGutter bgc="base-2" c="text" py="50">
<Tabs.Root
class="c--pricingBilling"
variant="pill"
g="40"
listProps={{ jslf: 'center', p: '3px', g: '2px', bgc: 'base', bdrs: '99', bd: true, 'aria-label': 'Billing period' }}
>
{
billingPeriods.map((period) => (
<Tabs.Item>
<Tabs.Tab c="var(--_isSelected, var(--base))" bgc="var(--_isSelected, var(--text))" px="35" py="10" bdrs="99" fw="bold" whs="nowrap">
{period.label}
</Tabs.Tab>
<Tabs.Panel>
<AutoColumns cols="18rem" g="25" autoFit>
{plans.map((plan) => (
<Grid as="div" gtr="subgrid" gr="span 5" g="30" p="30" bgc="base" bdrs="30" bxsh="20">
<Stack g="20">
<Flex class="u--trim" ai="center" jc="between" g="10">
<Heading level="2" fz="xl">
{plan.name}
</Heading>
{plan.recommended && (
<Badge
class="c--pricingRibbon"
bgc="transparent"
c="white"
fw="bold"
lts="l"
whs="nowrap"
bd="none"
bdrs="0"
bdrs-se="10"
pos="relative"
iso="isolate"
px="25"
py="10"
me="calc(-1 * (var(--s30) + var(--s10)))"
>
Popular
</Badge>
)}
</Flex>
<Text class="u--trim" fz="s" c="text-2">
{plan.description}
</Text>
</Stack>
<Flex class="u--trim" ai="baseline" g="10">
<Inline fz={['3xl', null, null, '4xl']} fw="bold" lh="xs" whs="nowrap">
{period.isYearly ? plan.yearlyPrice : plan.price}
</Inline>
<Inline fz="s" c="text-2">
{period.unit}
</Inline>
</Flex>
<Button href="#" bgc="text" c="base" py="10" bdrs="20" fw="500" aria-label={`${plan.name}: ${plan.button}`}>
{plan.button}
</Button>
<Divider />
<Stack as="ul" class="u--trim" hl="s" g="15">
{plan.features.map((feature) => (
<Flex as="li" ai="center" g="10">
<Icon icon={CheckIcon} c="brand" />
<Inline fz="s" c="text-2">
{feature}
</Inline>
</Flex>
))}
</Stack>
</Grid>
))}
</AutoColumns>
</Tabs.Panel>
</Tabs.Item>
))
}
</Tabs.Root>
</Container>
</DemoLayout> @layer lism-custom {
.c--pricingRibbon {
filter: drop-shadow(0 2px 2px var(--shadow));
}
.c--pricingRibbon::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
background-color: var(--brand);
border-radius: inherit;
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, var(--s10) 50%);
}
.c--pricingRibbon::after {
content: '';
position: absolute;
inset-block-start: 100%;
inset-inline-end: 0;
width: var(--s10);
height: var(--s10);
background-color: var(--brand);
filter: brightness(var(--o--p));
clip-path: polygon(0 0, 100% 0, 0 100%);
}
}