diff --git a/apps/web/src/views/home/index.tsx b/apps/web/src/views/home/index.tsx
index b813ee56..9eae251e 100644
--- a/apps/web/src/views/home/index.tsx
+++ b/apps/web/src/views/home/index.tsx
@@ -7,7 +7,6 @@ import { IoLogoGithub } from "react-icons/io";
import Button from "~/components/Button";
import { PageHead } from "~/components/PageHead";
-import Pricing from "../pricing/components/Pricing";
import Cta from "./components/Cta";
import FAQs from "./components/Faqs";
import Features from "./components/Features";
@@ -16,8 +15,6 @@ import Layout from "./components/Layout";
export default function HomeView() {
const { resolvedTheme } = useTheme();
- const isDarkMode = resolvedTheme === "dark";
-
return (
diff --git a/apps/web/src/views/pricing/components/FeatureComparisonTable.tsx b/apps/web/src/views/pricing/components/FeatureComparisonTable.tsx
new file mode 100644
index 00000000..f3d75ee5
--- /dev/null
+++ b/apps/web/src/views/pricing/components/FeatureComparisonTable.tsx
@@ -0,0 +1,297 @@
+import { t } from "@lingui/core/macro";
+import { HiCheckCircle, HiXCircle } from "react-icons/hi2";
+
+interface Feature {
+ key: string;
+ label: string;
+ kan: boolean | string;
+ trello: boolean | string;
+}
+
+interface Section {
+ name: string;
+ features: Feature[];
+}
+
+type FrequencyValue = "monthly" | "annually";
+
+const CellValue = ({ value }: { value: boolean | string }) => {
+ if (typeof value === "string") {
+ return (
+ {value}
+ );
+ }
+ return value ? (
+
+ ) : (
+
+ );
+};
+
+const FeatureComparisonTable = ({
+ frequencyValue,
+}: {
+ frequencyValue: FrequencyValue;
+}) => {
+ const sections: Section[] = [
+ {
+ name: t`Core features`,
+ features: [
+ {
+ key: "ulimited-workspaces",
+ label: t`Unlimited workspaces`,
+ kan: true,
+ trello: false,
+ },
+ {
+ key: "unlimited-boards",
+ label: t`Unlimited boards`,
+ kan: true,
+ trello: false,
+ },
+ {
+ key: "unlimited-lists",
+ label: t`Unlimited lists`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "unlimited-cards",
+ label: t`Unlimited cards`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "activity-log",
+ label: t`Activity log`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "templates",
+ label: t`Custom templates`,
+ kan: true,
+ trello: false,
+ },
+ { key: "labels", label: t`Labels & filters`, kan: true, trello: false },
+ { key: "checklists", label: t`Checklists`, kan: true, trello: true },
+ {
+ key: "import",
+ label: t`Import from Trello`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "visibility",
+ label: t`Board visibility`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "search",
+ label: t`Intelligent search`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "workspace-url",
+ label: t`Custom workspace link`,
+ kan: true,
+ trello: false,
+ },
+ {
+ key: "this-is-a-joke",
+ label: t`Owned by Atlassian`,
+ kan: false,
+ trello: true,
+ },
+ ],
+ },
+ {
+ name: t`Teams`,
+ features: [
+ {
+ key: "pricing",
+ label: t`Price per user/month`,
+ kan: frequencyValue === "monthly" ? `$10.00` : `$8.00`,
+ trello: frequencyValue === "monthly" ? `$12.00` : `$10.00`,
+ },
+ {
+ key: "comments",
+ label: t`Comments & mentions`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "assignments",
+ label: t`Assignees`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "sharing",
+ label: t`Board sharing & invites`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "invite-links",
+ label: t`Invite links`,
+ kan: true,
+ trello: true,
+ },
+ ],
+ },
+ {
+ name: t`Platform`,
+ features: [
+ {
+ key: "api",
+ label: t`REST API`,
+ kan: true,
+ trello: true,
+ },
+ {
+ key: "open-source",
+ label: t`Open source`,
+ kan: true,
+ trello: false,
+ },
+ {
+ key: "self-hostable",
+ label: t`Self-hostable`,
+ kan: true,
+ trello: false,
+ },
+ {
+ key: "white-label",
+ label: t`White label`,
+ kan: true,
+ trello: false,
+ },
+ {
+ key: "performance",
+ label: t`Fast & lightweight`,
+ kan: true,
+ trello: false,
+ },
+ ],
+ },
+ ];
+
+ const products = [
+ {
+ id: "kan",
+ name: t`Kan`,
+ featured: true,
+ },
+ {
+ id: "trello",
+ name: t`Trello`,
+ featured: false,
+ },
+ ];
+
+ return (
+
+
+
+ {products.map((product) => (
+
+ ))}
+
+
+
+ {sections.map((section) => (
+
+
+ {section.name}
+
+
+
+ {products.map((product) => (
+
+ ))}
+
+
+
+
+
+ |
+ {t`Feature`}
+ |
+ {products.map((p) => (
+
+ {p.name}
+ |
+ ))}
+
+
+
+ {section.features.map((feature, featureIdx) => (
+
+ |
+ {feature.label}
+ {featureIdx !== section.features.length - 1 ? (
+
+ ) : null}
+ |
+ {products.map((p) => (
+
+
+
+
+ |
+ ))}
+
+ ))}
+
+
+
+
+ {products.map((product) => (
+
+ ))}
+
+
+
+ ))}
+
+
+
+ );
+};
+
+export default FeatureComparisonTable;
diff --git a/apps/web/src/views/pricing/components/PricingTiers.tsx b/apps/web/src/views/pricing/components/PricingTiers.tsx
index 6539222f..d0563c50 100644
--- a/apps/web/src/views/pricing/components/PricingTiers.tsx
+++ b/apps/web/src/views/pricing/components/PricingTiers.tsx
@@ -1,28 +1,26 @@
import Link from "next/link";
import { Radio, RadioGroup } from "@headlessui/react";
import { t } from "@lingui/core/macro";
-import { useState } from "react";
import { HiBolt, HiCheckCircle } from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
-type Frequency = "monthly" | "annually";
+type FrequencyValue = "monthly" | "annually";
-const Pricing = () => {
- const frequencies = [
- {
- value: "monthly" as Frequency,
- label: t`Monthly`,
- priceSuffix: t`per user/month`,
- },
- {
- value: "annually" as Frequency,
- label: t`Yearly`,
- priceSuffix: t`per user/month`,
- },
- ];
-
- const [frequency, setFrequency] = useState(frequencies[1]);
+interface Frequency {
+ value: FrequencyValue;
+ label: string;
+ priceSuffix: string;
+}
+const Pricing = ({
+ frequency,
+ frequencies,
+ setFrequency,
+}: {
+ frequency: Frequency | undefined;
+ frequencies: Frequency[];
+ setFrequency: (frequency: Frequency) => void;
+}) => {
const tiers = [
{
name: t`Individuals`,
@@ -35,10 +33,9 @@ const Pricing = () => {
features: [
t`1 user`,
t`Unlimited boards`,
- t`Unlimited lists`,
t`Unlimited cards`,
- t`Unlimited comments`,
- t`Unlimited activity log`,
+ t`Custom board templates`,
+ t`Checklists`,
],
showPrice: true,
},
@@ -48,7 +45,7 @@ const Pricing = () => {
href: "signup",
buttonText: t`Get Started`,
price: { monthly: "$10.00", annually: "$8.00" },
- description: t`Kanban is better with a team. Perfect for small and growing teams looking to collaborate.`,
+ description: t`Kan is better with a team. Perfect for small and growing teams looking to collaborate.`,
featureHeader: t`Everything in the free plan, plus:`,
features: [
t`Workspace members`,
diff --git a/apps/web/src/views/pricing/index.tsx b/apps/web/src/views/pricing/index.tsx
index ca12789f..e6c7e8b3 100644
--- a/apps/web/src/views/pricing/index.tsx
+++ b/apps/web/src/views/pricing/index.tsx
@@ -1,10 +1,33 @@
import { t } from "@lingui/core/macro";
+import { useTheme } from "next-themes";
+import { useState } from "react";
+import Button from "~/components/Button";
import { PageHead } from "~/components/PageHead";
+import Cta from "../home/components/Cta";
import Layout from "../home/components/Layout";
+import FeatureComparisonTable from "./components/FeatureComparisonTable";
import PricingTiers from "./components/PricingTiers";
+type FrequencyValue = "monthly" | "annually";
+
export default function PricingView() {
+ const { resolvedTheme } = useTheme();
+ const frequencies = [
+ {
+ value: "monthly" as FrequencyValue,
+ label: t`Monthly`,
+ priceSuffix: t`per user/month`,
+ },
+ {
+ value: "annually" as FrequencyValue,
+ label: t`Yearly`,
+ priceSuffix: t`per user/month`,
+ },
+ ];
+
+ const [frequency, setFrequency] = useState(frequencies[1]);
+
return (
@@ -23,7 +46,38 @@ export default function PricingView() {
{t`Get started for free, with no usage limits. For collaboration, upgrade to a plan that fits the size of your team.`}
-
+
+
+
+
+
+
+
+ {t`Feature breakdown`}
+
+
+ {t`Compare our features to see why Kan is the best choice.`}
+
+
+
+
+
+
+
+
+
+
+