Website last fixes (#19895)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
203
README.md
|
|
@ -1,126 +1,170 @@
|
|||
<p align="center">
|
||||
<a href="https://www.producthunt.com/products/twenty-crm?launch=twenty-2-0">
|
||||
<img src="./packages/twenty-website/public/images/readme/product-hunt-banner.png" alt="We're live on Product Hunt — Support us" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.twenty.com">
|
||||
<img src="./packages/twenty-website/public/images/core/logo.svg" width="100px" alt="Twenty logo" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2 align="center" >The #1 Open-Source CRM </h2>
|
||||
|
||||
<p align="center"><a href="https://twenty.com">🌐 Website</a> · <a href="https://docs.twenty.com">📚 Documentation</a> · <a href="https://github.com/orgs/twentyhq/projects/1"><img src="./packages/twenty-website/public/images/readme/planner-icon.svg" width="12" height="12"/> Roadmap </a> · <a href="https://discord.gg/cx5n4Jzs57"><img src="./packages/twenty-website/public/images/readme/discord-icon.svg" width="12" height="12"/> Discord</a> · <a href="https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty"><img src="./packages/twenty-website/public/images/readme/figma-icon.png" width="12" height="12"/> Figma</a></p>
|
||||
<br />
|
||||
<h2 align="center" >The #1 Open-Source CRM</h2>
|
||||
|
||||
<p align="center"><a href="https://twenty.com"><img src="./packages/twenty-website/public/images/readme/globe-icon.svg" width="12" height="12"/> Website</a> · <a href="https://docs.twenty.com"><img src="./packages/twenty-website/public/images/readme/book-icon.svg" width="12" height="12"/> Documentation</a> · <a href="https://github.com/orgs/twentyhq/projects/1"><img src="./packages/twenty-website/public/images/readme/map-icon.svg" width="12" height="12"/> Roadmap </a> · <a href="https://discord.gg/cx5n4Jzs57"><img src="./packages/twenty-website/public/images/readme/discord-icon.svg" width="12" height="12"/> Discord</a> · <a href="https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty"><img src="./packages/twenty-website/public/images/readme/figma-icon.png" width="12" height="12"/> Figma</a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.twenty.com">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/github-cover-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/github-cover-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/github-cover-light.png" alt="Cover" />
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/github-cover-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/github-cover-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/github-cover-light.png" alt="Twenty banner" />
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
# Installation
|
||||
|
||||
See:
|
||||
🚀 [Self-hosting](https://docs.twenty.com/developers/self-host/capabilities/docker-compose)
|
||||
🖥️ [Local Setup](https://docs.twenty.com/developers/contribute/capabilities/local-setup)
|
||||
|
||||
# Why Twenty
|
||||
|
||||
We built Twenty for three reasons:
|
||||
Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves. Twenty is the CRM you build, ship, and version like the rest of your stack.
|
||||
|
||||
**CRMs are too expensive, and users are trapped.** Companies use locked-in customer data to hike prices. It shouldn't be that way.
|
||||
|
||||
**A fresh start is required to build a better experience.** We can learn from past mistakes and craft a cohesive experience inspired by new UX patterns from tools like Notion, Airtable or Linear.
|
||||
|
||||
**We believe in open-source and community.** Hundreds of developers are already building Twenty together. Once we have plugin capabilities, a whole ecosystem will grow around it.
|
||||
<a href="https://twenty.com/why-twenty"><img src="./packages/twenty-website/public/images/readme/star-icon.svg" width="14" height="14"/> Learn more about why we built Twenty</a>
|
||||
|
||||
<br />
|
||||
|
||||
# What You Can Do With Twenty
|
||||
# Installation
|
||||
|
||||
Please feel free to flag any specific needs you have by creating an issue.
|
||||
### <img src="./packages/twenty-website/public/images/readme/globe-icon.svg" width="14" height="14"/> Cloud
|
||||
|
||||
Below are a few features we have implemented to date:
|
||||
The fastest way to get started. Sign up at [twenty.com](https://twenty.com) and spin up a workspace in under a minute, with no infrastructure to manage and always up to date.
|
||||
|
||||
+ [Personalize layouts with filters, sort, group by, kanban and table views](#personalize-layouts-with-filters-sort-group-by-kanban-and-table-views)
|
||||
+ [Customize your objects and fields](#customize-your-objects-and-fields)
|
||||
+ [Create and manage permissions with custom roles](#create-and-manage-permissions-with-custom-roles)
|
||||
+ [Automate workflow with triggers and actions](#automate-workflow-with-triggers-and-actions)
|
||||
+ [Emails, calendar events, files, and more](#emails-calendar-events-files-and-more)
|
||||
### <img src="./packages/twenty-website/public/images/readme/book-icon.svg" width="14" height="14"/> Build an app
|
||||
|
||||
Scaffold a new app with the Twenty CLI:
|
||||
|
||||
## Personalize layouts with filters, sort, group by, kanban and table views
|
||||
```bash
|
||||
npx create-twenty-app my-app
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/views-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/views-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/views-light.png" alt="Companies Kanban Views" />
|
||||
</picture>
|
||||
</p>
|
||||
Define objects, fields, and views as code:
|
||||
|
||||
## Customize your objects and fields
|
||||
```ts
|
||||
import { defineObject, FieldType } from 'twenty-sdk/define';
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/data-model-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/data-model-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/data-model-light.png" alt="Setting Custom Objects" />
|
||||
</picture>
|
||||
</p>
|
||||
export default defineObject({
|
||||
nameSingular: 'deal',
|
||||
namePlural: 'deals',
|
||||
labelSingular: 'Deal',
|
||||
labelPlural: 'Deals',
|
||||
fields: [
|
||||
{ name: 'name', label: 'Name', type: FieldType.TEXT },
|
||||
{ name: 'amount', label: 'Amount', type: FieldType.CURRENCY },
|
||||
{ name: 'closeDate', label: 'Close Date', type: FieldType.DATE_TIME },
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Create and manage permissions with custom roles
|
||||
Then ship it to your workspace:
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/permissions-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/permissions-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/permissions-light.png" alt="Permissions" />
|
||||
</picture>
|
||||
</p>
|
||||
```bash
|
||||
npx twenty deploy
|
||||
```
|
||||
|
||||
## Automate workflow with triggers and actions
|
||||
See the [app development guide](https://docs.twenty.com/developers/extend/apps/getting-started) for objects, views, agents, and logic functions.
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/workflows-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/workflows-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/workflows-light.png" alt="Workflows" />
|
||||
</picture>
|
||||
</p>
|
||||
### <img src="./packages/twenty-website/public/images/readme/rocket-icon.svg" width="14" height="14"/> Self-hosting
|
||||
|
||||
## Emails, calendar events, files, and more
|
||||
Run Twenty on your own infrastructure with [Docker Compose](https://docs.twenty.com/developers/self-host/capabilities/docker-compose), or contribute locally via the [local setup guide](https://docs.twenty.com/developers/contribute/capabilities/local-setup).
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/plus-other-features-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/plus-other-features-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/plus-other-features-light.png" alt="Other Features" />
|
||||
</picture>
|
||||
</p>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
# Everything you need
|
||||
|
||||
Twenty gives you the building blocks of a modern CRM (objects, views, workflows, and agents) and lets you extend them as code. Here's a tour of what's in the box.
|
||||
|
||||
Want to go deeper? Read the <a href="https://docs.twenty.com/user-guide/introduction"><img src="./packages/twenty-website/public/images/readme/planner-icon.svg" width="14" height="14"/> User Guide</a> for product walkthroughs, or the <a href="https://docs.twenty.com"><img src="./packages/twenty-website/public/images/readme/book-icon.svg" width="14" height="14"/> Documentation</a> for developer reference.
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-build-apps-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-build-apps-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/v2-build-apps-light.png" alt="Create your apps" />
|
||||
</picture>
|
||||
<p align="center"><a href="https://docs.twenty.com/developers/extend/apps/getting-started"><img src="./packages/twenty-website/public/images/readme/code-icon.svg" width="16" height="16"/> Learn more about apps in doc</a></p>
|
||||
</td>
|
||||
<td width="50%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-version-control-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-version-control-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/v2-version-control-light.png" alt="Stay on top with version control" />
|
||||
</picture>
|
||||
<p align="center"><a href="https://docs.twenty.com/developers/extend/apps/publishing"><img src="./packages/twenty-website/public/images/readme/monitor-icon.svg" width="16" height="16"/> Learn more about version control in doc</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-all-tools-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-all-tools-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/v2-all-tools-light.png" alt="All the tools you need to build anything" />
|
||||
</picture>
|
||||
<p align="center"><a href="https://docs.twenty.com/developers/extend/apps/building"><img src="./packages/twenty-website/public/images/readme/rocket-icon.svg" width="16" height="16"/> Learn more about primitives in doc</a></p>
|
||||
</td>
|
||||
<td width="50%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-tools-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-tools-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/v2-tools-light.png" alt="Customize your layouts" />
|
||||
</picture>
|
||||
<p align="center"><a href="https://docs.twenty.com/user-guide/views-pipelines/overview"><img src="./packages/twenty-website/public/images/readme/planner-icon.svg" width="16" height="16"/> Learn more about layouts in doc</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-ai-agents-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-ai-agents-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/v2-ai-agents-light.png" alt="AI agents and chats" />
|
||||
</picture>
|
||||
<p align="center"><a href="https://docs.twenty.com/user-guide/ai/overview"><img src="./packages/twenty-website/public/images/readme/message-icon.svg" width="16" height="16"/> Learn more about AI in doc</a></p>
|
||||
</td>
|
||||
<td width="50%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-crm-tools-dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-crm-tools-light.png" />
|
||||
<img src="./packages/twenty-website/public/images/readme/v2-crm-tools-light.png" alt="Plus all the tools of a good CRM" />
|
||||
</picture>
|
||||
<p align="center"><a href="https://docs.twenty.com/user-guide/introduction"><img src="./packages/twenty-website/public/images/readme/star-icon.svg" width="16" height="16"/> Learn more about CRM features in doc</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
# Stack
|
||||
- [TypeScript](https://www.typescriptlang.org/)
|
||||
- [Nx](https://nx.dev/)
|
||||
- [NestJS](https://nestjs.com/), with [BullMQ](https://bullmq.io/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/)
|
||||
- [React](https://reactjs.org/), with [Jotai](https://jotai.org/), [Linaria](https://linaria.dev/) and [Lingui](https://lingui.dev/)
|
||||
|
||||
- <a href="https://www.typescriptlang.org/"><img src="./packages/twenty-website/public/images/readme/stack-typescript.svg" width="14" height="14"/> TypeScript</a>
|
||||
- <a href="https://nx.dev/"><img src="./packages/twenty-website/public/images/readme/stack-nx.svg" width="14" height="14"/> Nx</a>
|
||||
- <a href="https://nestjs.com/"><img src="./packages/twenty-website/public/images/readme/stack-nestjs.svg" width="14" height="14"/> NestJS</a>, with <a href="https://bullmq.io/">BullMQ</a>, <a href="https://www.postgresql.org/"><img src="./packages/twenty-website/public/images/readme/stack-postgresql.svg" width="14" height="14"/> PostgreSQL</a>, <a href="https://redis.io/"><img src="./packages/twenty-website/public/images/readme/stack-redis.svg" width="14" height="14"/> Redis</a>
|
||||
- <a href="https://reactjs.org/"><img src="./packages/twenty-website/public/images/readme/stack-react.svg" width="14" height="14"/> React</a>, with <a href="https://jotai.org/">Jotai</a>, <a href="https://linaria.dev/">Linaria</a> and <a href="https://lingui.dev/">Lingui</a>
|
||||
|
||||
|
||||
|
||||
# Thanks
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.chromatic.com/"><img src="./packages/twenty-website/public/images/readme/chromatic.png" height="30" alt="Chromatic" /></a>
|
||||
<a href="https://greptile.com"><img src="./packages/twenty-website/public/images/readme/greptile.png" height="30" alt="Greptile" /></a>
|
||||
<a href="https://sentry.io/"><img src="./packages/twenty-website/public/images/readme/sentry.png" height="30" alt="Sentry" /></a>
|
||||
<a href="https://crowdin.com/"><img src="./packages/twenty-website/public/images/readme/crowdin.png" height="30" alt="Crowdin" /></a>
|
||||
<a href="https://e2b.dev/"><img src="./packages/twenty-website/public/images/readme/e2b.svg" height="30" alt="E2B" /></a>
|
||||
<a href="https://www.chromatic.com/"><img src="./packages/twenty-website/public/images/readme/chromatic.png" height="28" alt="Chromatic" /></a>
|
||||
|
||||
<a href="https://greptile.com"><img src="./packages/twenty-website/public/images/readme/greptile.png" height="28" alt="Greptile" /></a>
|
||||
|
||||
<a href="https://sentry.io/"><img src="./packages/twenty-website/public/images/readme/sentry.png" height="28" alt="Sentry" /></a>
|
||||
|
||||
<a href="https://crowdin.com/"><img src="./packages/twenty-website/public/images/readme/crowdin.png" height="28" alt="Crowdin" /></a>
|
||||
</p>
|
||||
|
||||
Thanks to these amazing services that we use and recommend for UI testing (Chromatic), code review (Greptile), catching bugs (Sentry) and translating (Crowdin).
|
||||
|
|
@ -128,9 +172,4 @@ Below are a few features we have implemented to date:
|
|||
|
||||
# Join the Community
|
||||
|
||||
- Star the repo
|
||||
- Subscribe to releases (watch -> custom -> releases)
|
||||
- Follow us on [Twitter](https://twitter.com/twentycrm) or [LinkedIn](https://www.linkedin.com/company/twenty/)
|
||||
- Join our [Discord](https://discord.gg/cx5n4Jzs57)
|
||||
- Improve translations on [Crowdin](https://twenty.crowdin.com/twenty)
|
||||
- [Contributions](https://github.com/twentyhq/twenty/contribute) are, of course, most welcome!
|
||||
<p><a href="https://github.com/twentyhq/twenty"><img src="./packages/twenty-website/public/images/readme/star-icon.svg" width="12" height="12"/> Star the repo</a> · <a href="https://discord.gg/cx5n4Jzs57"><img src="./packages/twenty-website/public/images/readme/discord-icon.svg" width="12" height="12"/> Discord</a> · <a href="https://github.com/twentyhq/twenty/discussions"><img src="./packages/twenty-website/public/images/readme/message-icon.svg" width="12" height="12"/> Feature requests</a> · <a href="https://github.com/orgs/twentyhq/projects/1/views/35"><img src="./packages/twenty-website/public/images/readme/rocket-icon.svg" width="12" height="12"/> Releases</a> · <a href="https://twitter.com/twentycrm"><img src="./packages/twenty-website/public/images/readme/x-icon.svg" width="12" height="12"/> X</a> · <a href="https://www.linkedin.com/company/twenty/"><img src="./packages/twenty-website/public/images/readme/linkedin-icon.svg" width="12" height="12"/> LinkedIn</a> · <a href="https://twenty.crowdin.com/twenty"><img src="./packages/twenty-website/public/images/readme/language-icon.svg" width="12" height="12"/> Crowdin</a> · <a href="https://github.com/twentyhq/twenty/contribute"><img src="./packages/twenty-website/public/images/readme/code-icon.svg" width="12" height="12"/> Contribute</a></p>
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
|
@ -11,6 +11,7 @@ import { TalkToUsButton } from '@/app/components/ContactCalModal';
|
|||
import { FAQ_DATA, MENU_DATA, TRUSTED_BY_DATA } from '@/app/_constants';
|
||||
import { Body, Eyebrow, Heading, LinkButton } from '@/design-system/components';
|
||||
import { Pages } from '@/enums/pages';
|
||||
import { ArrowRightUpIcon } from '@/icons';
|
||||
import { fetchCommunityStats } from '@/lib/community/fetch-community-stats';
|
||||
import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels';
|
||||
import { Faq } from '@/sections/Faq/components';
|
||||
|
|
@ -28,11 +29,15 @@ import { styled } from '@linaria/react';
|
|||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Twenty | Open Source CRM',
|
||||
description: 'Modular, scalable open source CRM for modern teams.',
|
||||
title: 'Twenty | #1 open source CRM',
|
||||
description:
|
||||
'The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business.',
|
||||
};
|
||||
|
||||
const HOME_TOP_BACKGROUND_COLOR = '#F4F4F4';
|
||||
const PRODUCT_HUNT_LAUNCH_URL =
|
||||
'https://www.producthunt.com/products/twenty-crm?launch=twenty-2-0';
|
||||
const PRODUCT_HUNT_BRAND_COLOR = '#DA552F';
|
||||
|
||||
const HeroHeadingGroup = styled.div`
|
||||
align-items: center;
|
||||
|
|
@ -41,11 +46,63 @@ const HeroHeadingGroup = styled.div`
|
|||
gap: ${theme.spacing(3)};
|
||||
width: 100%;
|
||||
|
||||
> *:nth-child(2) {
|
||||
> *:last-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const HeroLaunchChip = styled.a`
|
||||
align-items: center;
|
||||
background: ${theme.colors.primary.background[100]};
|
||||
border: 1px solid ${theme.colors.primary.border[10]};
|
||||
border-radius: 999px;
|
||||
color: ${theme.colors.primary.text[100]};
|
||||
display: inline-flex;
|
||||
font-family: ${theme.font.family.mono};
|
||||
font-size: ${theme.font.size(2.5)};
|
||||
font-weight: ${theme.font.weight.medium};
|
||||
gap: ${theme.spacing(2)};
|
||||
line-height: ${theme.lineHeight(3)};
|
||||
padding: ${theme.spacing(2)} ${theme.spacing(3)};
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
transition:
|
||||
border-color 180ms ease,
|
||||
color 180ms ease,
|
||||
transform 180ms ease;
|
||||
white-space: nowrap;
|
||||
|
||||
&:is(:hover, :focus-visible) {
|
||||
border-color: ${PRODUCT_HUNT_BRAND_COLOR};
|
||||
color: ${PRODUCT_HUNT_BRAND_COLOR};
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 1px solid ${theme.colors.highlight[100]};
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const HeroLaunchChipDot = styled.span`
|
||||
background: ${PRODUCT_HUNT_BRAND_COLOR};
|
||||
border-radius: 999px;
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
height: ${theme.spacing(2)};
|
||||
width: ${theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const HeroLaunchChipLabel = styled.span`
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
gap: ${theme.spacing(1.5)};
|
||||
`;
|
||||
|
||||
const HeroIntroGroup = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
|
@ -109,6 +166,17 @@ export default async function HomePage() {
|
|||
<Hero.Root backgroundColor={HOME_TOP_BACKGROUND_COLOR} showHomeBackground>
|
||||
<HeroIntroGroup data-halftone-exclude>
|
||||
<HeroHeadingGroup>
|
||||
<HeroLaunchChip
|
||||
href={PRODUCT_HUNT_LAUNCH_URL}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<HeroLaunchChipDot />
|
||||
<HeroLaunchChipLabel>
|
||||
Live on Product Hunt
|
||||
<ArrowRightUpIcon size={8} strokeColor="currentColor" />
|
||||
</HeroLaunchChipLabel>
|
||||
</HeroLaunchChip>
|
||||
<Hero.Heading page={Pages.Home} segments={HERO_DATA.heading} />
|
||||
<Hero.Body page={Pages.Home} body={HERO_DATA.body} size="sm" />
|
||||
</HeroHeadingGroup>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const FAQ_DATA: FaqDataType = {
|
|||
fontFamily: 'sans',
|
||||
},
|
||||
answer: {
|
||||
text: "Yes. Twenty is the #1 open-source CRM on GitHub. Most teams run it on our managed cloud for zero-ops setup; self-hosting is always available if you'd rather own the infrastructure.",
|
||||
text: "Yes. Twenty is the #1 open source CRM on GitHub. Most teams run it on our managed cloud for zero-ops setup; self-hosting is always available if you'd rather own the infrastructure.",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export const MENU_DATA: MenuDataType = {
|
|||
image: '/images/shared/menu/developers-preview.png',
|
||||
imageAlt: 'Blue developer illustration with branching arrows',
|
||||
imagePosition: 'center',
|
||||
imageScale: 1.4,
|
||||
imageScale: 1.6,
|
||||
title: 'Build on an open platform',
|
||||
description:
|
||||
'APIs, SDKs and webhooks to extend Twenty and ship apps on top of your CRM data.',
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export const CASE_STUDY_CATALOG_ENTRIES: CaseStudyCatalogEntry[] = [
|
|||
'NetZero uses Twenty as a modular CRM across product lines and countries, with a roadmap into AI-assisted workflows.',
|
||||
date: '2025',
|
||||
coverImageSrc:
|
||||
'https://images.unsplash.com/photo-1510524474345-1c4bac68d1d0?w=1600&q=80',
|
||||
'https://images.unsplash.com/photo-1744830343976-ce690ba2a67c?w=1600&q=80',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { theme } from '@/theme';
|
|||
import type { Metadata } from 'next';
|
||||
|
||||
const PLACEHOLDER_HERO =
|
||||
'https://images.unsplash.com/photo-1510524474345-1c4bac68d1d0?w=1600&q=80';
|
||||
'https://images.unsplash.com/photo-1744830343976-ce690ba2a67c?w=1600&q=80';
|
||||
|
||||
const CASE_STUDY: CaseStudyData = {
|
||||
meta: {
|
||||
|
|
|
|||
|
|
@ -66,8 +66,9 @@ const StyledMain = styled.main`
|
|||
`;
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Twenty | Open Source CRM',
|
||||
description: 'Modular, scalable open source CRM for modern teams.',
|
||||
title: 'Twenty | #1 open source CRM',
|
||||
description:
|
||||
'The #1 open source CRM for modern teams. Modular, scalable, and built to fit your business.',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@ export const HERO_DATA = {
|
|||
{ text: 'our partner', fontFamily: 'sans', newLine: true },
|
||||
],
|
||||
body: {
|
||||
text: "We're building the #1 open-source CRM, but we can't do it alone. Join our partner ecosystem and grow with us.",
|
||||
text: "We're building the #1 open source CRM, but we can't do it alone. Join our partner ecosystem and grow with us.",
|
||||
},
|
||||
} satisfies HeroBaseDataType;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export const THREE_CARDS_ILLUSTRATION_DATA: ThreeCardsIllustrationDataType = {
|
|||
{
|
||||
heading: { text: 'Content & Community Partners', fontFamily: 'sans' },
|
||||
body: {
|
||||
text: "Share Twenty with your audience and help shape the future of open-source CRM. We're looking for creators, educators, and community builders who want to showcase great software.",
|
||||
text: "Share Twenty with your audience and help shape the future of the #1 open source CRM. We're looking for creators, educators, and community builders who want to showcase great software.",
|
||||
},
|
||||
benefits: [
|
||||
{ text: 'Revenue share for referred customers', icon: 'tag' },
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { ThreeCards } from '@/sections/ThreeCards/components';
|
|||
import { TrustedBy } from '@/sections/TrustedBy/components';
|
||||
import type { ThreeCardsScrollLayoutOptions } from '@/sections/ThreeCards/utils/three-cards-scroll-layout';
|
||||
import { theme } from '@/theme';
|
||||
import { styled } from '@linaria/react';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
const PARTNER_ILLUSTRATION_CARDS_SCROLL_LAYOUT_OPTIONS: ThreeCardsScrollLayoutOptions =
|
||||
|
|
@ -37,10 +38,18 @@ const PARTNER_ILLUSTRATION_CARDS_SCROLL_LAYOUT_OPTIONS: ThreeCardsScrollLayoutOp
|
|||
stagger: 0.16,
|
||||
};
|
||||
|
||||
const PromoSpacing = styled.div`
|
||||
margin-bottom: ${theme.spacing(8)};
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
margin-bottom: ${theme.spacing(12)};
|
||||
}
|
||||
`;
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Partners | Twenty',
|
||||
description:
|
||||
'Join our partner ecosystem and grow with us as we build the #1 open-source CRM.',
|
||||
'Join our partner ecosystem and grow with us as we build the #1 open source CRM.',
|
||||
};
|
||||
|
||||
export default async function PartnerPage() {
|
||||
|
|
@ -72,13 +81,16 @@ export default async function PartnerPage() {
|
|||
|
||||
<TrustedBy.Root
|
||||
backgroundColor={theme.colors.primary.background[100]}
|
||||
compactBottom
|
||||
>
|
||||
<TrustedBy.Separator separator={TRUSTED_BY_DATA.separator} />
|
||||
<TrustedBy.Logos logos={TRUSTED_BY_DATA.logos} />
|
||||
<TrustedBy.ClientCount label={TRUSTED_BY_DATA.clientCountLabel.text} />
|
||||
</TrustedBy.Root>
|
||||
|
||||
<CaseStudyCatalog.Promo entries={CASE_STUDY_CATALOG_ENTRIES} />
|
||||
<PromoSpacing>
|
||||
<CaseStudyCatalog.Promo compactTop entries={CASE_STUDY_CATALOG_ENTRIES} />
|
||||
</PromoSpacing>
|
||||
|
||||
<ThreeCards.Root backgroundColor={theme.colors.secondary.background[5]}>
|
||||
<ThreeCards.Intro page={Pages.Partners} align="left">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const PricingBannerContainer = styled.div`
|
|||
export const metadata: Metadata = {
|
||||
title: 'Pricing | Twenty',
|
||||
description:
|
||||
'Plans that scale with your team. Compare tiers of the #1 open-source CRM.',
|
||||
'Plans that scale with your team. Compare tiers of the #1 open source CRM.',
|
||||
};
|
||||
|
||||
export default async function PricingPage() {
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ export const RELEASE_NOTES_HERO_HEADING: HeadingType[] = [
|
|||
];
|
||||
|
||||
export const RELEASE_NOTES_HERO_BODY: BodyType = {
|
||||
text: 'Discover the newest features and improvements in Twenty,\nthe #1 open-source CRM.',
|
||||
text: 'Discover the newest features and improvements in Twenty,\nthe #1 open source CRM.',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { Fragment } from 'react';
|
|||
export const metadata: Metadata = {
|
||||
title: 'Releases | Twenty',
|
||||
description:
|
||||
'Discover the newest features and improvements in Twenty, the open-source CRM.',
|
||||
'Discover the newest features and improvements in Twenty, the #1 open source CRM.',
|
||||
};
|
||||
|
||||
export default async function ReleasesPage() {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { LinkButton } from '@/design-system/components';
|
|||
import { Pages } from '@/enums/pages';
|
||||
import { fetchCommunityStats } from '@/lib/community/fetch-community-stats';
|
||||
import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels';
|
||||
import { OverflowProbe } from '@/lib/debug/overflow-probe';
|
||||
import { Editorial } from '@/sections/Editorial/components';
|
||||
import { Hero } from '@/sections/Hero/components';
|
||||
import { Marquee } from '@/sections/Marquee/components';
|
||||
|
|
@ -72,7 +71,6 @@ export default async function WhyTwentyPage() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<OverflowProbe />
|
||||
<Menu.Root
|
||||
backgroundColor={theme.colors.secondary.background[100]}
|
||||
scheme="secondary"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const PLUS_PATH = "M.5 7.25H14M7.25 14V.5";
|
||||
const PLUS_PATH = 'M1.5 7.5H13.5M7.5 13.5V1.5';
|
||||
|
||||
interface PlusIconProps {
|
||||
size: number;
|
||||
|
|
@ -13,10 +13,13 @@ export function PlusIcon({ size, strokeColor }: PlusIconProps) {
|
|||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
overflow="visible"
|
||||
style={{ display: 'block' }}
|
||||
>
|
||||
<path
|
||||
d={PLUS_PATH}
|
||||
stroke={strokeColor}
|
||||
strokeWidth={1.25}
|
||||
strokeMiterlimit={10}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ const StyledContainer = styled(Container)<{ compactTop: boolean }>`
|
|||
}
|
||||
`;
|
||||
|
||||
const CORNER_OFFSET = '-6px';
|
||||
const CORNER_SIZE = 14;
|
||||
const CORNER_OFFSET = '-7px';
|
||||
|
||||
const FramedGrid = styled.div`
|
||||
position: relative;
|
||||
|
|
@ -89,11 +90,12 @@ const FrameRailBottom = styled.span`
|
|||
const FrameCorner = styled.span`
|
||||
align-items: center;
|
||||
display: none;
|
||||
height: 12px;
|
||||
height: ${CORNER_SIZE}px;
|
||||
justify-content: center;
|
||||
line-height: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
width: ${CORNER_SIZE}px;
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
display: flex;
|
||||
|
|
@ -157,18 +159,30 @@ export function Grid({
|
|||
{!compactTop && (
|
||||
<>
|
||||
<FrameCornerTopLeft aria-hidden>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</FrameCornerTopLeft>
|
||||
<FrameCornerTopRight aria-hidden>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</FrameCornerTopRight>
|
||||
</>
|
||||
)}
|
||||
<FrameCornerBottomLeft aria-hidden>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</FrameCornerBottomLeft>
|
||||
<FrameCornerBottomRight aria-hidden>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</FrameCornerBottomRight>
|
||||
<CardGrid>
|
||||
{entries.map((entry, index) => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ import { PromoMic } from '@/illustrations/CaseStudyCatalog/PromoMic';
|
|||
import { theme } from '@/theme';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
const CORNER_OFFSET = '-6px';
|
||||
const CORNER_SIZE = 14;
|
||||
const CORNER_OFFSET = '-7px';
|
||||
const CONNECTED_TOP_OFFSET = '6px';
|
||||
const LINE_INSET = '20px';
|
||||
const TRUSTED_BY_BOTTOM_PADDING = 12;
|
||||
const TRUSTED_BY_BOTTOM_PADDING_DESKTOP = 16;
|
||||
|
|
@ -59,12 +61,13 @@ const FrameBoard = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
const Frame = styled.div`
|
||||
const Frame = styled.div<{ compactTop: boolean }>`
|
||||
bottom: ${theme.spacing(12)};
|
||||
left: ${theme.spacing(10)};
|
||||
position: absolute;
|
||||
right: ${theme.spacing(10)};
|
||||
top: ${theme.spacing(12)};
|
||||
top: ${({ compactTop }) =>
|
||||
compactTop ? CONNECTED_TOP_OFFSET : theme.spacing(12)};
|
||||
`;
|
||||
|
||||
const FrameLine = styled.span`
|
||||
|
|
@ -86,28 +89,29 @@ const FrameLineBottom = styled(FrameLine)`
|
|||
right: ${LINE_INSET};
|
||||
`;
|
||||
|
||||
const FrameLineLeft = styled(FrameLine)`
|
||||
const FrameLineLeft = styled(FrameLine)<{ compactTop: boolean }>`
|
||||
bottom: ${LINE_INSET};
|
||||
left: 0;
|
||||
top: ${LINE_INSET};
|
||||
top: ${({ compactTop }) => (compactTop ? '0' : LINE_INSET)};
|
||||
width: 1px;
|
||||
`;
|
||||
|
||||
const FrameLineRight = styled(FrameLine)`
|
||||
const FrameLineRight = styled(FrameLine)<{ compactTop: boolean }>`
|
||||
bottom: ${LINE_INSET};
|
||||
right: 0;
|
||||
top: ${LINE_INSET};
|
||||
top: ${({ compactTop }) => (compactTop ? '0' : LINE_INSET)};
|
||||
width: 1px;
|
||||
`;
|
||||
|
||||
const FrameCorner = styled.span`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 12px;
|
||||
height: ${CORNER_SIZE}px;
|
||||
justify-content: center;
|
||||
line-height: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
width: ${CORNER_SIZE}px;
|
||||
`;
|
||||
|
||||
const FrameCornerTopLeft = styled(FrameCorner)`
|
||||
|
|
@ -130,16 +134,20 @@ const FrameCornerBottomRight = styled(FrameCorner)`
|
|||
right: ${CORNER_OFFSET};
|
||||
`;
|
||||
|
||||
const StyledContainer = styled(Container)`
|
||||
const StyledContainer = styled(Container)<{ compactTop: boolean }>`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
gap: ${theme.spacing(10)};
|
||||
grid-template-columns: 1fr;
|
||||
min-height: 520px;
|
||||
padding-bottom: ${theme.spacing(24 + TRUSTED_BY_BOTTOM_PADDING)};
|
||||
padding-bottom: ${({ compactTop }) =>
|
||||
compactTop
|
||||
? theme.spacing(16)
|
||||
: theme.spacing(24 + TRUSTED_BY_BOTTOM_PADDING)};
|
||||
padding-left: ${theme.spacing(4)};
|
||||
padding-right: ${theme.spacing(4)};
|
||||
padding-top: ${theme.spacing(20)};
|
||||
padding-top: ${({ compactTop }) =>
|
||||
compactTop ? theme.spacing(16) : theme.spacing(20)};
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
|
|
@ -147,12 +155,14 @@ const StyledContainer = styled(Container)`
|
|||
column-gap: ${theme.spacing(16)};
|
||||
grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
|
||||
min-height: 500px;
|
||||
padding-bottom: ${theme.spacing(
|
||||
40 + TRUSTED_BY_BOTTOM_PADDING_DESKTOP,
|
||||
)};
|
||||
padding-bottom: ${({ compactTop }) =>
|
||||
compactTop
|
||||
? `calc(${theme.spacing(24)} + ${theme.spacing(12)} - ${CONNECTED_TOP_OFFSET})`
|
||||
: theme.spacing(40 + TRUSTED_BY_BOTTOM_PADDING_DESKTOP)};
|
||||
padding-left: ${theme.spacing(10)};
|
||||
padding-right: ${theme.spacing(10)};
|
||||
padding-top: ${theme.spacing(32)};
|
||||
padding-top: ${({ compactTop }) =>
|
||||
compactTop ? theme.spacing(24) : theme.spacing(32)};
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
@ -250,41 +260,57 @@ type PromoProps = {
|
|||
entries: readonly CaseStudyCatalogEntry[];
|
||||
ctaHref?: string;
|
||||
ctaLabel?: string;
|
||||
compactTop?: boolean;
|
||||
};
|
||||
|
||||
export function Promo({
|
||||
entries,
|
||||
ctaHref = '/customers',
|
||||
ctaLabel = 'Explore customer stories',
|
||||
compactTop = false,
|
||||
}: PromoProps) {
|
||||
const featured = entries[0];
|
||||
|
||||
return (
|
||||
<Section aria-label="Customer stories preview">
|
||||
<BackgroundLayer aria-hidden>
|
||||
<FrameBoard>
|
||||
<Frame>
|
||||
<FrameLineTop />
|
||||
<Frame compactTop={compactTop}>
|
||||
{!compactTop && <FrameLineTop />}
|
||||
<FrameLineBottom />
|
||||
<FrameLineLeft />
|
||||
<FrameLineRight />
|
||||
<FrameCornerTopLeft>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
</FrameCornerTopLeft>
|
||||
<FrameCornerTopRight>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
</FrameCornerTopRight>
|
||||
<FrameLineLeft compactTop={compactTop} />
|
||||
<FrameLineRight compactTop={compactTop} />
|
||||
{!compactTop && (
|
||||
<>
|
||||
<FrameCornerTopLeft>
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</FrameCornerTopLeft>
|
||||
<FrameCornerTopRight>
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</FrameCornerTopRight>
|
||||
</>
|
||||
)}
|
||||
<FrameCornerBottomLeft>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</FrameCornerBottomLeft>
|
||||
<FrameCornerBottomRight>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</FrameCornerBottomRight>
|
||||
</Frame>
|
||||
</FrameBoard>
|
||||
</BackgroundLayer>
|
||||
|
||||
<StyledContainer>
|
||||
<StyledContainer compactTop={compactTop}>
|
||||
<VisualColumn>
|
||||
<VisualStage>
|
||||
<MicFrame aria-hidden>
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ import {
|
|||
} from '@/icons';
|
||||
import type { FaqQuestionType } from '@/sections/Faq/types/FaqQuestion';
|
||||
import { theme } from '@/theme';
|
||||
import {
|
||||
Accordion as BaseAccordion,
|
||||
type AccordionTriggerState,
|
||||
} from '@base-ui/react/accordion';
|
||||
import { Accordion as BaseAccordion } from '@base-ui/react/accordion';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
const QuestionText = styled.span`
|
||||
|
|
@ -55,12 +52,29 @@ const ToggleVisual = styled.span`
|
|||
display: inline-flex;
|
||||
height: 36px;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
transform 0.2s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
width: 36px;
|
||||
`;
|
||||
|
||||
const ToggleIconLayer = styled.span`
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
inset: 0;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
|
||||
&[data-icon='minus'] {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
`;
|
||||
|
||||
const RowTrigger = styled.button`
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
|
@ -123,6 +137,16 @@ export const ItemRow = styled.div`
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
&[data-open] ${ToggleIconLayer}[data-icon='plus'] {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
&[data-open] ${ToggleIconLayer}[data-icon='minus'] {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
grid-template-columns: 14px 60px 1fr 80px 36px;
|
||||
row-gap: ${theme.spacing(4)};
|
||||
|
|
@ -186,42 +210,41 @@ export function Item({ question, value }: ItemProps) {
|
|||
return (
|
||||
<BaseAccordion.Item key={value} value={value} render={<ItemRow />}>
|
||||
<BaseAccordion.Header render={<Header />}>
|
||||
<BaseAccordion.Trigger
|
||||
render={(props, state: AccordionTriggerState) => {
|
||||
const isOpen = state.open;
|
||||
const ToggleIcon = isOpen ? MinusIcon : PlusIcon;
|
||||
return (
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading -- Accordion.Trigger host props
|
||||
<RowTrigger {...props} type="button">
|
||||
<QuestionIconContainer aria-hidden>
|
||||
<QuestionIconLayer data-layer="outline">
|
||||
<RectangleOutlineIcon
|
||||
size={14}
|
||||
strokeColor={theme.colors.secondary.text[100]}
|
||||
/>
|
||||
</QuestionIconLayer>
|
||||
<QuestionIconLayer data-layer="fill">
|
||||
<RectangleFillIcon
|
||||
size={14}
|
||||
fillColor={theme.colors.secondary.text[100]}
|
||||
/>
|
||||
</QuestionIconLayer>
|
||||
</QuestionIconContainer>
|
||||
<BaseAccordion.Trigger render={<RowTrigger type="button" />}>
|
||||
<QuestionIconContainer aria-hidden>
|
||||
<QuestionIconLayer data-layer="outline">
|
||||
<RectangleOutlineIcon
|
||||
size={14}
|
||||
strokeColor={theme.colors.secondary.text[100]}
|
||||
/>
|
||||
</QuestionIconLayer>
|
||||
<QuestionIconLayer data-layer="fill">
|
||||
<RectangleFillIcon
|
||||
size={14}
|
||||
fillColor={theme.colors.secondary.text[100]}
|
||||
/>
|
||||
</QuestionIconLayer>
|
||||
</QuestionIconContainer>
|
||||
|
||||
<QuestionText>{question.question.text}</QuestionText>
|
||||
<QuestionText>{question.question.text}</QuestionText>
|
||||
|
||||
<ToggleContainer>
|
||||
<ToggleVisual aria-hidden>
|
||||
<ToggleIcon
|
||||
size={12}
|
||||
strokeColor={theme.colors.secondary.border[80]}
|
||||
/>
|
||||
</ToggleVisual>
|
||||
</ToggleContainer>
|
||||
</RowTrigger>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<ToggleContainer>
|
||||
<ToggleVisual aria-hidden>
|
||||
<ToggleIconLayer data-icon="plus">
|
||||
<PlusIcon
|
||||
size={12}
|
||||
strokeColor={theme.colors.secondary.border[80]}
|
||||
/>
|
||||
</ToggleIconLayer>
|
||||
<ToggleIconLayer data-icon="minus">
|
||||
<MinusIcon
|
||||
size={12}
|
||||
strokeColor={theme.colors.secondary.border[80]}
|
||||
/>
|
||||
</ToggleIconLayer>
|
||||
</ToggleVisual>
|
||||
</ToggleContainer>
|
||||
</BaseAccordion.Trigger>
|
||||
</BaseAccordion.Header>
|
||||
|
||||
<BaseAccordion.Panel render={<AnswerWrapper />} keepMounted>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { theme } from '@/theme';
|
|||
import { styled } from '@linaria/react';
|
||||
import { Children, type ReactNode } from 'react';
|
||||
|
||||
const CORNER_OFFSET = '-6px';
|
||||
const CORNER_SIZE = 14;
|
||||
const CORNER_OFFSET = '-7px';
|
||||
|
||||
const StyledSection = styled.section<{
|
||||
compactTop: boolean;
|
||||
|
|
@ -18,6 +19,7 @@ const StyledSection = styled.section<{
|
|||
compactTop ? theme.spacing(4) : theme.spacing(12)};
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: ${({ compactBottom }) => (compactBottom ? 2 : 'auto')};
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
padding-bottom: ${({ compactBottom }) =>
|
||||
|
|
@ -97,11 +99,12 @@ const StyledCountCell = styled(StyledCell)`
|
|||
const CornerMarker = styled.span`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 12px;
|
||||
height: ${CORNER_SIZE}px;
|
||||
justify-content: center;
|
||||
line-height: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
width: ${CORNER_SIZE}px;
|
||||
`;
|
||||
|
||||
const CornerTopLeft = styled(CornerMarker)`
|
||||
|
|
@ -150,16 +153,28 @@ export function Root({
|
|||
<StyledContainer>
|
||||
<StyledCard backgroundColor={cardBackgroundColor}>
|
||||
<CornerTopLeft aria-hidden>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</CornerTopLeft>
|
||||
<CornerTopRight aria-hidden>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</CornerTopRight>
|
||||
<CornerBottomLeft aria-hidden>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</CornerBottomLeft>
|
||||
<CornerBottomRight aria-hidden>
|
||||
<PlusIcon size={12} strokeColor={theme.colors.highlight[100]} />
|
||||
<PlusIcon
|
||||
size={CORNER_SIZE}
|
||||
strokeColor={theme.colors.highlight[100]}
|
||||
/>
|
||||
</CornerBottomRight>
|
||||
<StyledLabelCell>{label}</StyledLabelCell>
|
||||
<StyledLogosCell>{logos}</StyledLogosCell>
|
||||
|
|
|
|||
BIN
packages/twenty-website/public/images/readme/book-icon.svg
Normal file
|
After Width: | Height: | Size: 368 B |
BIN
packages/twenty-website/public/images/readme/code-icon.svg
Normal file
|
After Width: | Height: | Size: 288 B |
|
Before Width: | Height: | Size: 827 KiB After Width: | Height: | Size: 341 KiB |
|
Before Width: | Height: | Size: 699 KiB After Width: | Height: | Size: 302 KiB |
BIN
packages/twenty-website/public/images/readme/globe-icon.svg
Normal file
|
After Width: | Height: | Size: 370 B |
BIN
packages/twenty-website/public/images/readme/language-icon.svg
Normal file
|
After Width: | Height: | Size: 372 B |
BIN
packages/twenty-website/public/images/readme/linkedin-icon.svg
Normal file
|
After Width: | Height: | Size: 413 B |
BIN
packages/twenty-website/public/images/readme/map-icon.svg
Normal file
|
After Width: | Height: | Size: 306 B |
BIN
packages/twenty-website/public/images/readme/message-icon.svg
Normal file
|
After Width: | Height: | Size: 339 B |
BIN
packages/twenty-website/public/images/readme/monitor-icon.svg
Normal file
|
After Width: | Height: | Size: 325 B |
|
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 344 B |
|
After Width: | Height: | Size: 18 KiB |
BIN
packages/twenty-website/public/images/readme/rocket-icon.svg
Normal file
|
After Width: | Height: | Size: 398 B |
BIN
packages/twenty-website/public/images/readme/stack-nestjs.svg
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
packages/twenty-website/public/images/readme/stack-nx.svg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
BIN
packages/twenty-website/public/images/readme/stack-react.svg
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
packages/twenty-website/public/images/readme/stack-redis.svg
Normal file
|
After Width: | Height: | Size: 776 B |
|
After Width: | Height: | Size: 1.3 KiB |
BIN
packages/twenty-website/public/images/readme/star-icon.svg
Normal file
|
After Width: | Height: | Size: 333 B |
|
After Width: | Height: | Size: 377 KiB |
|
After Width: | Height: | Size: 251 KiB |
BIN
packages/twenty-website/public/images/readme/v2-ai-agents.png
Normal file
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 465 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 438 KiB |
|
After Width: | Height: | Size: 294 KiB |
BIN
packages/twenty-website/public/images/readme/v2-build-apps.png
Normal file
|
After Width: | Height: | Size: 294 KiB |
|
After Width: | Height: | Size: 363 KiB |
|
After Width: | Height: | Size: 166 KiB |
BIN
packages/twenty-website/public/images/readme/v2-crm-tools.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
packages/twenty-website/public/images/readme/v2-tools-dark.png
Normal file
|
After Width: | Height: | Size: 402 KiB |
BIN
packages/twenty-website/public/images/readme/v2-tools-light.png
Normal file
|
After Width: | Height: | Size: 345 KiB |
BIN
packages/twenty-website/public/images/readme/v2-tools.png
Normal file
|
After Width: | Height: | Size: 465 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 285 KiB |
BIN
packages/twenty-website/public/images/readme/x-icon.svg
Normal file
|
After Width: | Height: | Size: 310 B |