diff --git a/landing/competitor-comparison.css b/landing/competitor-comparison.css new file mode 100644 --- /dev/null +++ b/landing/competitor-comparison.css @@ -0,0 +1,63 @@ +.competitorComparisonSection { + display: flex; + flex-direction: column; + align-items: center; + padding: 0 16px; +} + +.headerText { + color: var(--white-80); + text-align: center; + margin-bottom: 72px; +} + +.competitorsContainer { + overflow: hidden; + display: flex; + justify-content: space-between; + column-gap: 16px; + padding: 16px 32px; + background-color: var(--comparison-cards); + border-radius: 16px; + width: 560px; + max-width: 90vw; +} + +.logoContainer { + display: flex; + flex-direction: column; + align-items: center; + position: relative; +} + +.competitorLogo { + opacity: 0.25; +} + +.competitorLogo:hover { + opacity: 1; + cursor: pointer; +} + +.activeCompetitorLogo { + opacity: 1; + transform: translateY(-2px); +} + +.bump { + width: 80%; + height: 4px; + border-top-left-radius: 16px; + border-top-right-radius: 16px; + background-color: var(--violet-dark-100); + position: absolute; + bottom: -16px; +} + +.featureCardsContainer { + display: flex; + justify-content: center; + gap: 40px; + flex-wrap: wrap; + margin-top: 48px; +} diff --git a/landing/competitor-comparison.react.js b/landing/competitor-comparison.react.js new file mode 100644 --- /dev/null +++ b/landing/competitor-comparison.react.js @@ -0,0 +1,94 @@ +// @flow + +import classNames from 'classnames'; +import * as React from 'react'; + +import { useModalContext } from 'lib/components/modal-provider.react.js'; + +import css from './competitor-comparison.css'; +import { + competitorData, + type Competitor, + type FeatureComparison, +} from './competitor-data.js'; +import CompetitorFeatureCard from './competitor-feature-card.react.js'; +import CompetitorLogo from './competitor-logo.react.js'; +import FeatureModal from './feature-modal.react.js'; +import typography from './typography.css'; + +const competitors = [ + 'signal', + 'keybase', + 'telegram', + 'discord', + 'slack', + 'matrix', +]; + +function CompetitorComparison(): React.Node { + const { pushModal } = useModalContext(); + + const [selectedCompetitorID, setSelectedCompetitorID] = + React.useState('signal'); + + const onFeatureCardClick = React.useCallback( + (competitor: Competitor, feature: FeatureComparison) => { + pushModal(); + }, + [pushModal], + ); + + const competitorSelector = React.useMemo( + () => + competitors.map(competitor => { + const competitorLogoClassName = classNames({ + [css.competitorLogo]: true, + [css.activeCompetitorLogo]: selectedCompetitorID === competitor, + }); + const bumpClassName = classNames({ + [css.bump]: selectedCompetitorID === competitor, + }); + + return ( +
+
setSelectedCompetitorID(competitor)} + > + +
+
+
+ ); + }), + [selectedCompetitorID], + ); + + const featureCards = React.useMemo(() => { + const selectedCompetitor = competitorData[selectedCompetitorID]; + + return selectedCompetitor.featureComparison.map(feature => ( + onFeatureCardClick(selectedCompetitor, feature)} + /> + )); + }, [onFeatureCardClick, selectedCompetitorID]); + + const headerClassName = classNames([typography.heading1, css.headerText]); + + return ( +
+

See how Comm is different

+
{competitorSelector}
+
{featureCards}
+
+ ); +} + +export default CompetitorComparison;