@contentrain/astro-kit 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/catalog.json +4600 -1584
  2. package/components/_shared/Button.astro +2 -1
  3. package/components/_shared/KitImage.astro +9 -4
  4. package/components/_shared/Section.astro +3 -1
  5. package/components/_shared/SectionHeader.astro +27 -0
  6. package/components/_shared/measured.ts +23 -0
  7. package/components/_shared/safe-url.ts +67 -0
  8. package/components/_shared/social.ts +73 -0
  9. package/components/announcement/Announcement.astro +2 -1
  10. package/components/announcement/meta.json +1 -1
  11. package/components/band/Band.astro +3 -2
  12. package/components/band/meta.json +1 -0
  13. package/components/breadcrumb/Breadcrumb.astro +2 -1
  14. package/components/breadcrumb/meta.json +1 -1
  15. package/components/card-grid/CardGrid.astro +2 -1
  16. package/components/card-grid/meta.json +1 -0
  17. package/components/consent/Consent.astro +2 -1
  18. package/components/consent/meta.json +1 -1
  19. package/components/contact-details/ContactDetails.astro +3 -2
  20. package/components/contact-details/meta.json +1 -0
  21. package/components/contact-form/ContactForm.astro +51 -14
  22. package/components/contact-form/fixtures.json +169 -9
  23. package/components/contact-form/meta.json +284 -15
  24. package/components/cta/Cta.astro +52 -12
  25. package/components/cta/fixtures.json +5 -1
  26. package/components/cta/meta.json +11 -2
  27. package/components/dialog/Dialog.astro +113 -0
  28. package/components/dialog/fixtures.json +84 -0
  29. package/components/dialog/meta.json +252 -0
  30. package/components/embed/meta.json +1 -1
  31. package/components/faq/Faq.astro +60 -17
  32. package/components/faq/fixtures.json +79 -0
  33. package/components/faq/meta.json +9 -3
  34. package/components/feature-list/FeatureList.astro +28 -4
  35. package/components/feature-list/meta.json +16 -3
  36. package/components/footer/Footer.astro +5 -4
  37. package/components/footer/meta.json +1 -0
  38. package/components/gallery/Gallery.astro +49 -14
  39. package/components/gallery/fixtures.json +275 -0
  40. package/components/gallery/meta.json +267 -14
  41. package/components/header/Header.astro +17 -5
  42. package/components/header/fixtures.json +90 -1
  43. package/components/header/meta.json +5 -3
  44. package/components/hero/meta.json +1 -0
  45. package/components/language-switcher/LanguageSwitcher.astro +68 -0
  46. package/components/language-switcher/fixtures.json +51 -0
  47. package/components/language-switcher/meta.json +73 -0
  48. package/components/logo-cloud/LogoCloud.astro +86 -21
  49. package/components/logo-cloud/fixtures.json +239 -0
  50. package/components/logo-cloud/meta.json +145 -5
  51. package/components/nav/Nav.astro +77 -24
  52. package/components/nav/NavTree.astro +40 -0
  53. package/components/nav/fixtures.json +165 -0
  54. package/components/nav/meta.json +11 -2
  55. package/components/newsletter/Newsletter.astro +2 -1
  56. package/components/newsletter/meta.json +1 -0
  57. package/components/pagination/Pagination.astro +4 -3
  58. package/components/pagination/meta.json +1 -1
  59. package/components/post-card/PostCard.astro +8 -5
  60. package/components/post-card/meta.json +3 -2
  61. package/components/post-list/PostList.astro +115 -0
  62. package/components/post-list/fixtures.json +387 -0
  63. package/components/post-list/meta.json +309 -0
  64. package/components/pricing/Pricing.astro +128 -24
  65. package/components/pricing/fixtures.json +288 -4
  66. package/components/pricing/meta.json +188 -1
  67. package/components/search-box/SearchBox.astro +51 -0
  68. package/components/search-box/fixtures.json +31 -0
  69. package/components/search-box/meta.json +75 -0
  70. package/components/slider/Slider.astro +19 -115
  71. package/components/slider/SliderTrack.astro +178 -0
  72. package/components/slider/fixtures.json +2 -1
  73. package/components/slider/meta.json +11 -5
  74. package/components/social-links/SocialLinks.astro +74 -0
  75. package/components/social-links/fixtures.json +67 -0
  76. package/components/social-links/meta.json +94 -0
  77. package/components/split/Split.astro +76 -14
  78. package/components/split/fixtures.json +8 -1
  79. package/components/split/meta.json +226 -4
  80. package/components/stats/Stats.astro +54 -11
  81. package/components/stats/fixtures.json +260 -0
  82. package/components/stats/meta.json +151 -4
  83. package/components/steps/Steps.astro +36 -8
  84. package/components/steps/fixtures.json +217 -5
  85. package/components/steps/meta.json +157 -3
  86. package/components/tabs/Tabs.astro +10 -3
  87. package/components/tabs/fixtures.json +4 -0
  88. package/components/tabs/meta.json +5 -2
  89. package/components/team/Team.astro +54 -9
  90. package/components/team/fixtures.json +217 -4
  91. package/components/team/meta.json +169 -3
  92. package/components/team/team.ts +4 -0
  93. package/components/testimonial/Testimonial.astro +71 -21
  94. package/components/testimonial/fixtures.json +73 -0
  95. package/components/testimonial/meta.json +168 -6
  96. package/dist/index.mjs +1 -1
  97. package/dist/index.mjs.map +1 -1
  98. package/mapping/classic.json +124 -3
  99. package/mapping/divi.json +26 -8
  100. package/mapping/elementor.json +133 -14
  101. package/mapping/gutenberg.json +38 -10
  102. package/package.json +2 -2
@@ -2,10 +2,14 @@
2
2
  // Price plans side by side: a name, a price and what it includes, with a
3
3
  // button each. The featured plan is raised and carries a badge, so it stands
4
4
  // out by more than its colour. Each plan's features are a markdown list,
5
- // rendered at build time.
5
+ // rendered at build time. Variants: `layout="table"` is a comparison table
6
+ // (plan columns, feature rows); `billingToggle="radio"` swaps monthly and yearly
7
+ // prices with two radio inputs and CSS `:has()` — no script.
6
8
  import { tv } from 'tailwind-variants'
7
9
  import Button from '../_shared/Button.astro'
8
10
  import Section, { type Tone } from '../_shared/Section.astro'
11
+ import { measuredGrid } from '../_shared/measured'
12
+ import type { MeasuredStyle } from '../_shared/types'
9
13
  import { renderMarkdown } from '../../../lib/markdown'
10
14
 
11
15
  const pricing = tv({
@@ -14,6 +18,12 @@ const pricing = tv({
14
18
  heading: 'text-3xl [font-weight:var(--font-weight-heading,700)] tracking-tight text-balance md:text-4xl',
15
19
  intro: 'text-lg opacity-80',
16
20
  list: 'grid items-stretch gap-6 sm:grid-cols-2',
21
+ toggle: 'mx-auto mb-8 flex w-fit rounded-full border border-line p-1',
22
+ toggleOption: 'rounded-full px-4 py-1.5 text-sm font-medium has-[:checked]:bg-accent has-[:checked]:text-accent-ink has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-accent',
23
+ tableWrap: 'overflow-x-auto',
24
+ table: 'w-full min-w-[32rem] border-collapse text-start',
25
+ cell: 'border-b border-line px-4 py-3 text-center align-top',
26
+ rowHead: 'border-b border-line px-4 py-3 text-start font-normal',
17
27
  plan: 'relative flex flex-col gap-5 rounded-card border border-line bg-surface p-6 text-ink md:p-8',
18
28
  badge: 'absolute -top-3 start-6 rounded-full bg-accent px-3 py-1 text-xs font-semibold text-accent-ink',
19
29
  name: 'text-lg [font-weight:var(--font-weight-heading,600)]',
@@ -30,9 +40,16 @@ const pricing = tv({
30
40
  '3': { list: 'lg:grid-cols-3' },
31
41
  '4': { list: 'lg:grid-cols-4' },
32
42
  },
43
+ // Which mark leads each feature in the cards: a check, a dot or none (the markdown list's own bullets).
44
+ featureStyle: {
45
+ check: { features: '[&_ul]:list-none [&_ul]:ps-0 [&_li]:flex [&_li]:gap-2 [&_li]:before:shrink-0 [&_li]:before:font-semibold [&_li]:before:text-accent-text [&_li]:before:content-["✓"]' },
46
+ dot: { features: '[&_ul]:list-none [&_ul]:ps-0 [&_li]:flex [&_li]:gap-2 [&_li]:before:mt-2 [&_li]:before:size-1.5 [&_li]:before:shrink-0 [&_li]:before:rounded-full [&_li]:before:bg-accent [&_li]:before:content-[""]' },
47
+ plain: {},
48
+ },
49
+ compact: { true: { list: 'gap-4', plan: 'gap-3 p-4 md:p-5', amount: 'text-3xl' }, false: {} },
33
50
  tone: { default: {}, muted: { plan: 'border-transparent' }, inverse: { plan: 'border-transparent' }, accent: { intro: 'opacity-100', plan: 'border-transparent', badge: 'bg-ink text-surface' } },
34
51
  },
35
- defaultVariants: { columns: '3', tone: 'default' },
52
+ defaultVariants: { columns: '3', featureStyle: 'plain', compact: false, tone: 'default' },
36
53
  })
37
54
 
38
55
  interface Plan {
@@ -45,6 +62,11 @@ interface Plan {
45
62
  ctaLabel?: string | undefined
46
63
  ctaHref?: string | undefined
47
64
  featured?: boolean | undefined
65
+ /** A free-text badge ("Best value"); a featured plan without one reads `featuredLabel`. */
66
+ badge?: string | undefined
67
+ /** The other billing period's price and period (`billingToggle="radio"`). */
68
+ priceAlt?: string | undefined
69
+ periodAlt?: string | undefined
48
70
  }
49
71
 
50
72
  interface Props {
@@ -54,43 +76,125 @@ interface Props {
54
76
  /** Badge on the featured plan. */
55
77
  featuredLabel?: string | undefined
56
78
  columns?: '2' | '3' | '4' | undefined
79
+ /** `cards` side by side, or one comparison `table` (plans as columns, features as rows). */
80
+ layout?: 'cards' | 'table' | undefined
81
+ featureStyle?: 'check' | 'dot' | 'plain' | undefined
82
+ compact?: boolean | undefined
83
+ /** `radio`: two labelled radios swap each plan's price for its `priceAlt` — no script. */
84
+ billingToggle?: 'none' | 'radio' | undefined
85
+ /** Texts of the toggle: its two options and the legend read to screen readers. */
86
+ billingMainLabel?: string | undefined
87
+ billingAltLabel?: string | undefined
88
+ billingLegend?: string | undefined
89
+ /** Table cell texts for screen readers. */
90
+ includedLabel?: string | undefined
91
+ notIncludedLabel?: string | undefined
57
92
  tone?: Tone | undefined
93
+ /** The source section's measured style (`MeasuredStyle`): frame, padding, fill, background image, columns per width, gap. Absent, the site's tokens; present, it wins over tone and columns. */
94
+ measured?: MeasuredStyle | undefined
58
95
  id?: string | undefined
59
96
  }
60
97
 
61
- const { heading, intro, plans, featuredLabel = 'Most popular', columns, tone, id } = Astro.props
62
- const s = pricing({ columns, tone })
98
+ const { heading, intro, plans, featuredLabel = 'Most popular', columns, layout = 'cards', featureStyle, compact, billingToggle = 'none', billingMainLabel = 'Monthly', billingAltLabel = 'Yearly', billingLegend = 'Billing period', includedLabel = 'Included', notIncludedLabel = 'Not included', tone, measured, id } = Astro.props
99
+ const s = pricing({ columns, featureStyle, compact: compact ?? false, tone })
100
+ // The radio group's name must be unique on the page: the section's id, or a hash of its plans when it has none.
101
+ const group = id ?? `pricing-${[...JSON.stringify(plans)].reduce((h, c) => (h * 31 + c.charCodeAt(0)) >>> 0, 7).toString(36)}`
102
+ const toggled = billingToggle === 'radio' && plans.some(plan => plan.priceAlt)
103
+ // The cards' list items, as plain strings, for the table's feature rows (the markdown list's `- ` / `* ` lines).
104
+ const itemsOf = (md?: string) => (md ?? '').split('\n').flatMap((line) => { const m = /^\s*[-*]\s+(.*\S)/.exec(line); return m ? [m[1]!.replace(/[*_`]/g, '')] : [] })
105
+ const rows = layout === 'table' ? [...new Set(plans.flatMap(plan => itemsOf(plan.features)))] : []
106
+ const included = plans.map(plan => new Set(itemsOf(plan.features)))
63
107
  const headingId = heading && id ? `${id}-title` : undefined
64
108
  // One level under the section heading, or the section's heading when there is none.
65
109
  const PlanHeading = heading ? 'h3' : 'h2'
66
110
  const features = await Promise.all(plans.map(plan => renderMarkdown(plan.features)))
67
111
  ---
68
112
  {plans.length > 0 && (
69
- <Section tone={tone} id={id} aria-labelledby={headingId}>
113
+ <Section measured={measured} tone={tone} id={id} aria-labelledby={headingId}>
70
114
  {(heading || intro) && (
71
115
  <header class={s.header()}>
72
116
  {heading && <h2 id={headingId} class={s.heading()}>{heading}</h2>}
73
117
  {intro && <p class={s.intro()} data-kit-text>{intro}</p>}
74
118
  </header>
75
119
  )}
76
- <ul class={s.list()}>
77
- {plans.map((plan, i) => (
78
- <li class={s.plan({ class: plan.featured ? 'ring-2 ring-accent shadow-lg md:-my-2 md:py-10' : undefined })}>
79
- {plan.featured && <p class={s.badge()}>{featuredLabel}</p>}
80
- <PlanHeading class={s.name()}>{plan.name}</PlanHeading>
81
- <p class={s.price()}>
82
- <span class={s.amount()}>{plan.price}</span>
83
- {plan.period && <span class={s.period()}>{plan.period}</span>}
84
- </p>
85
- {plan.description && <p class={s.description()} data-kit-text>{plan.description}</p>}
86
- {features[i] && <div class={s.features()} set:html={features[i]} />}
87
- {plan.ctaLabel && plan.ctaHref && (
88
- <div class={s.action()}>
89
- <Button action={{ label: plan.ctaLabel, href: plan.ctaHref, style: plan.featured ? 'primary' : 'ghost' }} class="w-full" />
90
- </div>
91
- )}
92
- </li>
93
- ))}
94
- </ul>
120
+ {layout === 'table' ? (
121
+ <div class={s.tableWrap()} tabindex="0" role="region" aria-labelledby={headingId} aria-label={headingId ? undefined : plans.map(plan => plan.name).join(', ')}>
122
+ <table class={s.table()}>
123
+ <caption class="sr-only">{heading ?? plans.map(plan => plan.name).join(', ')}</caption>
124
+ <thead>
125
+ <tr>
126
+ <td class={s.cell()} />
127
+ {plans.map(plan => (
128
+ <th scope="col" class={s.cell({ class: plan.featured ? 'bg-surface-muted' : undefined })}>
129
+ <span class="block text-lg [font-weight:var(--font-weight-heading,600)]">{plan.name}</span>
130
+ <span class="block text-2xl [font-weight:var(--font-weight-heading,700)]">{plan.price}</span>
131
+ {plan.period && <span class="block text-sm opacity-75">{plan.period}</span>}
132
+ {(plan.badge ?? (plan.featured ? featuredLabel : undefined)) && <span class="mt-1 inline-block rounded-full bg-accent px-3 py-0.5 text-xs font-semibold text-accent-ink">{plan.badge ?? featuredLabel}</span>}
133
+ </th>
134
+ ))}
135
+ </tr>
136
+ </thead>
137
+ <tbody>
138
+ {rows.map(row => (
139
+ <tr>
140
+ <th scope="row" class={s.rowHead()}>{row}</th>
141
+ {plans.map((plan, i) => (
142
+ <td class={s.cell({ class: plan.featured ? 'bg-surface-muted' : undefined })}>
143
+ {included[i]!.has(row)
144
+ ? <><span aria-hidden="true">✓</span><span class="sr-only">{includedLabel}</span></>
145
+ : <><span aria-hidden="true" class="opacity-50">–</span><span class="sr-only">{notIncludedLabel}</span></>}
146
+ </td>
147
+ ))}
148
+ </tr>
149
+ ))}
150
+ {plans.some(plan => plan.ctaLabel && plan.ctaHref) && (
151
+ <tr>
152
+ <td class={s.cell()} />
153
+ {plans.map(plan => (
154
+ <td class={s.cell({ class: plan.featured ? 'bg-surface-muted' : undefined })}>
155
+ {plan.ctaLabel && plan.ctaHref && <Button action={{ label: plan.ctaLabel, href: plan.ctaHref, style: plan.featured ? 'primary' : 'ghost' }} />}
156
+ </td>
157
+ ))}
158
+ </tr>
159
+ )}
160
+ </tbody>
161
+ </table>
162
+ </div>
163
+ ) : (
164
+ <div class={toggled ? '[&:has(input[value=alt]:checked)_[data-price=main]]:hidden [&:not(:has(input[value=alt]:checked))_[data-price=alt]]:hidden' : undefined}>
165
+ {toggled && (
166
+ <fieldset class={s.toggle()}>
167
+ <legend class="sr-only">{billingLegend}</legend>
168
+ <label class={s.toggleOption()}><input type="radio" name={`${group}-billing`} value="main" checked class="sr-only" />{billingMainLabel}</label>
169
+ <label class={s.toggleOption()}><input type="radio" name={`${group}-billing`} value="alt" class="sr-only" />{billingAltLabel}</label>
170
+ </fieldset>
171
+ )}
172
+ <ul class={s.list({ class: measuredGrid(measured) })}>
173
+ {plans.map((plan, i) => (
174
+ <li class={s.plan({ class: plan.featured ? 'ring-2 ring-accent shadow-lg md:-my-2 md:py-10' : undefined })}>
175
+ {(plan.badge ?? (plan.featured ? featuredLabel : undefined)) && <p class={s.badge()}>{plan.badge ?? featuredLabel}</p>}
176
+ <PlanHeading class={s.name()}>{plan.name}</PlanHeading>
177
+ <p class={s.price()} data-price="main">
178
+ <span class={s.amount()}>{plan.price}</span>
179
+ {plan.period && <span class={s.period()}>{plan.period}</span>}
180
+ </p>
181
+ {toggled && plan.priceAlt && (
182
+ <p class={s.price()} data-price="alt">
183
+ <span class={s.amount()}>{plan.priceAlt}</span>
184
+ {plan.periodAlt && <span class={s.period()}>{plan.periodAlt}</span>}
185
+ </p>
186
+ )}
187
+ {plan.description && <p class={s.description()} data-kit-text>{plan.description}</p>}
188
+ {features[i] && <div class={s.features()} set:html={features[i]} />}
189
+ {plan.ctaLabel && plan.ctaHref && (
190
+ <div class={s.action()}>
191
+ <Button action={{ label: plan.ctaLabel, href: plan.ctaHref, style: plan.featured ? 'primary' : 'ghost' }} class="w-full" />
192
+ </div>
193
+ )}
194
+ </li>
195
+ ))}
196
+ </ul>
197
+ </div>
198
+ )}
95
199
  </Section>
96
200
  )}
@@ -1,6 +1,290 @@
1
1
  [
2
- {"name": "three-featured", "props": {"id": "pricing", "heading": "Pricing", "intro": "Start free, upgrade when the team grows.", "plans": [{"name": "Free", "price": "$0", "period": "/month", "description": "For one person and one site.", "features": "- 1 project\n- Git-backed content\n- Community support", "ctaLabel": "Start free", "ctaHref": "/signup/"}, {"name": "Pro", "price": "$12", "period": "/month", "description": "For freelancers.", "features": "- 5 projects\n- Media library\n- Email support", "ctaLabel": "Choose Pro", "ctaHref": "/signup/?plan=pro", "featured": true}, {"name": "Team", "price": "$29", "period": "/month", "description": "For studios and teams.", "features": "- Unlimited projects\n- Reviews and roles\n- Priority support", "ctaLabel": "Choose Team", "ctaHref": "/signup/?plan=team"}]}},
3
- {"name": "two-muted", "props": {"columns": "2", "tone": "muted", "featuredLabel": "Best value", "plans": [{"name": "Monthly", "price": "€15", "period": "per month", "features": "- Cancel anytime", "ctaLabel": "Subscribe", "ctaHref": "/buy/monthly/"}, {"name": "Yearly", "price": "€150", "period": "per year", "features": "- Two months free\n- **Priority** support", "ctaLabel": "Subscribe", "ctaHref": "/buy/yearly/", "featured": true}]}},
4
- {"name": "four-inverse", "props": {"columns": "4", "tone": "inverse", "heading": "Plans", "plans": [{"name": "Solo", "price": "$0", "features": "- 1 site"}, {"name": "Pro", "price": "$12", "period": "/mo", "features": "- 5 sites", "featured": true, "ctaLabel": "Buy", "ctaHref": "/buy/"}, {"name": "Team", "price": "$29", "period": "/mo", "features": "- 20 sites"}, {"name": "Enterprise", "price": "On request", "description": "Custom terms.", "ctaLabel": "Contact us", "ctaHref": "/contact/"}]}},
5
- {"name": "accent", "props": {"tone": "accent", "heading": "One price", "plans": [{"name": "Studio", "price": "$49", "period": "once", "features": "- Everything", "ctaLabel": "Buy now", "ctaHref": "/buy/", "featured": true}, {"name": "Support", "price": "$9", "period": "/month", "features": "- Updates and help", "ctaLabel": "Add support", "ctaHref": "/buy/support/"}]}}
2
+ {
3
+ "name": "three-featured",
4
+ "props": {
5
+ "id": "pricing",
6
+ "heading": "Pricing",
7
+ "intro": "Start free, upgrade when the team grows.",
8
+ "plans": [
9
+ {
10
+ "name": "Free",
11
+ "price": "$0",
12
+ "period": "/month",
13
+ "description": "For one person and one site.",
14
+ "features": "- 1 project\n- Git-backed content\n- Community support",
15
+ "ctaLabel": "Start free",
16
+ "ctaHref": "/signup/"
17
+ },
18
+ {
19
+ "name": "Pro",
20
+ "price": "$12",
21
+ "period": "/month",
22
+ "description": "For freelancers.",
23
+ "features": "- 5 projects\n- Media library\n- Email support",
24
+ "ctaLabel": "Choose Pro",
25
+ "ctaHref": "/signup/?plan=pro",
26
+ "featured": true
27
+ },
28
+ {
29
+ "name": "Team",
30
+ "price": "$29",
31
+ "period": "/month",
32
+ "description": "For studios and teams.",
33
+ "features": "- Unlimited projects\n- Reviews and roles\n- Priority support",
34
+ "ctaLabel": "Choose Team",
35
+ "ctaHref": "/signup/?plan=team"
36
+ }
37
+ ]
38
+ }
39
+ },
40
+ {
41
+ "name": "two-muted",
42
+ "props": {
43
+ "columns": "2",
44
+ "tone": "muted",
45
+ "featuredLabel": "Best value",
46
+ "plans": [
47
+ {
48
+ "name": "Monthly",
49
+ "price": "€15",
50
+ "period": "per month",
51
+ "features": "- Cancel anytime",
52
+ "ctaLabel": "Subscribe",
53
+ "ctaHref": "/buy/monthly/"
54
+ },
55
+ {
56
+ "name": "Yearly",
57
+ "price": "€150",
58
+ "period": "per year",
59
+ "features": "- Two months free\n- **Priority** support",
60
+ "ctaLabel": "Subscribe",
61
+ "ctaHref": "/buy/yearly/",
62
+ "featured": true
63
+ }
64
+ ]
65
+ }
66
+ },
67
+ {
68
+ "name": "four-inverse",
69
+ "props": {
70
+ "columns": "4",
71
+ "tone": "inverse",
72
+ "heading": "Plans",
73
+ "plans": [
74
+ {
75
+ "name": "Solo",
76
+ "price": "$0",
77
+ "features": "- 1 site"
78
+ },
79
+ {
80
+ "name": "Pro",
81
+ "price": "$12",
82
+ "period": "/mo",
83
+ "features": "- 5 sites",
84
+ "featured": true,
85
+ "ctaLabel": "Buy",
86
+ "ctaHref": "/buy/"
87
+ },
88
+ {
89
+ "name": "Team",
90
+ "price": "$29",
91
+ "period": "/mo",
92
+ "features": "- 20 sites"
93
+ },
94
+ {
95
+ "name": "Enterprise",
96
+ "price": "On request",
97
+ "description": "Custom terms.",
98
+ "ctaLabel": "Contact us",
99
+ "ctaHref": "/contact/"
100
+ }
101
+ ]
102
+ }
103
+ },
104
+ {
105
+ "name": "accent",
106
+ "props": {
107
+ "tone": "accent",
108
+ "heading": "One price",
109
+ "plans": [
110
+ {
111
+ "name": "Studio",
112
+ "price": "$49",
113
+ "period": "once",
114
+ "features": "- Everything",
115
+ "ctaLabel": "Buy now",
116
+ "ctaHref": "/buy/",
117
+ "featured": true
118
+ },
119
+ {
120
+ "name": "Support",
121
+ "price": "$9",
122
+ "period": "/month",
123
+ "features": "- Updates and help",
124
+ "ctaLabel": "Add support",
125
+ "ctaHref": "/buy/support/"
126
+ }
127
+ ]
128
+ }
129
+ },
130
+ {
131
+ "name": "table",
132
+ "props": {
133
+ "id": "compare",
134
+ "heading": "Compare plans",
135
+ "layout": "table",
136
+ "plans": [
137
+ {
138
+ "name": "Free",
139
+ "price": "$0",
140
+ "features": "- 1 project\n- Community support",
141
+ "ctaLabel": "Start",
142
+ "ctaHref": "/signup/"
143
+ },
144
+ {
145
+ "name": "Pro",
146
+ "price": "$12",
147
+ "period": "/month",
148
+ "features": "- 1 project\n- 5 projects\n- Media library\n- Email support",
149
+ "ctaLabel": "Choose",
150
+ "ctaHref": "/signup/?plan=pro",
151
+ "featured": true
152
+ },
153
+ {
154
+ "name": "Team",
155
+ "price": "$29",
156
+ "period": "/month",
157
+ "features": "- 1 project\n- 5 projects\n- Media library\n- Email support\n- Roles and reviews\n- Priority support\n- Audit log\n- SSO",
158
+ "ctaLabel": "Choose",
159
+ "ctaHref": "/signup/?plan=team"
160
+ }
161
+ ]
162
+ }
163
+ },
164
+ {
165
+ "name": "check-compact",
166
+ "props": {
167
+ "featureStyle": "check",
168
+ "compact": true,
169
+ "plans": [
170
+ {
171
+ "name": "Free",
172
+ "price": "$0",
173
+ "period": "/month",
174
+ "description": "For one person and one site.",
175
+ "features": "- 1 project\n- Git-backed content\n- Community support",
176
+ "ctaLabel": "Start free",
177
+ "ctaHref": "/signup/"
178
+ },
179
+ {
180
+ "name": "Pro",
181
+ "price": "$12",
182
+ "period": "/month",
183
+ "description": "For freelancers.",
184
+ "features": "- 5 projects\n- Media library\n- Email support",
185
+ "ctaLabel": "Choose Pro",
186
+ "ctaHref": "/signup/?plan=pro",
187
+ "featured": true
188
+ },
189
+ {
190
+ "name": "Team",
191
+ "price": "$29",
192
+ "period": "/month",
193
+ "description": "For studios and teams.",
194
+ "features": "- Unlimited projects\n- Reviews and roles\n- Priority support",
195
+ "ctaLabel": "Choose Team",
196
+ "ctaHref": "/signup/?plan=team"
197
+ }
198
+ ]
199
+ }
200
+ },
201
+ {
202
+ "name": "dot-features",
203
+ "props": {
204
+ "featureStyle": "dot",
205
+ "plans": [
206
+ {
207
+ "name": "Free",
208
+ "price": "$0",
209
+ "period": "/month",
210
+ "description": "For one person and one site.",
211
+ "features": "- 1 project\n- Git-backed content\n- Community support",
212
+ "ctaLabel": "Start free",
213
+ "ctaHref": "/signup/"
214
+ },
215
+ {
216
+ "name": "Pro",
217
+ "price": "$12",
218
+ "period": "/month",
219
+ "description": "For freelancers.",
220
+ "features": "- 5 projects\n- Media library\n- Email support",
221
+ "ctaLabel": "Choose Pro",
222
+ "ctaHref": "/signup/?plan=pro",
223
+ "featured": true
224
+ },
225
+ {
226
+ "name": "Team",
227
+ "price": "$29",
228
+ "period": "/month",
229
+ "description": "For studios and teams.",
230
+ "features": "- Unlimited projects\n- Reviews and roles\n- Priority support",
231
+ "ctaLabel": "Choose Team",
232
+ "ctaHref": "/signup/?plan=team"
233
+ }
234
+ ]
235
+ }
236
+ },
237
+ {
238
+ "name": "billing-toggle",
239
+ "props": {
240
+ "id": "billing",
241
+ "heading": "Billing",
242
+ "billingToggle": "radio",
243
+ "columns": "2",
244
+ "plans": [
245
+ {
246
+ "name": "Basic",
247
+ "price": "€15",
248
+ "period": "/month",
249
+ "priceAlt": "€150",
250
+ "periodAlt": "/year",
251
+ "features": "- 1 site",
252
+ "ctaLabel": "Subscribe",
253
+ "ctaHref": "/buy/basic/"
254
+ },
255
+ {
256
+ "name": "Plus",
257
+ "price": "€29",
258
+ "period": "/month",
259
+ "priceAlt": "€290",
260
+ "periodAlt": "/year",
261
+ "features": "- 5 sites\n- Priority support",
262
+ "ctaLabel": "Subscribe",
263
+ "ctaHref": "/buy/plus/",
264
+ "badge": "Best value"
265
+ }
266
+ ],
267
+ "billingMainLabel": "Monthly",
268
+ "billingAltLabel": "Yearly",
269
+ "billingLegend": "Billing period"
270
+ }
271
+ },
272
+ {
273
+ "name": "missing-period-description",
274
+ "props": {
275
+ "plans": [
276
+ {
277
+ "name": "Custom",
278
+ "price": "On request",
279
+ "ctaLabel": "Talk to us",
280
+ "ctaHref": "/contact/"
281
+ },
282
+ {
283
+ "name": "Open",
284
+ "price": "Free",
285
+ "features": "- Everything"
286
+ }
287
+ ]
288
+ }
289
+ }
6
290
  ]