Website last fixes (#19895)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thomas des Francs
2026-04-21 07:43:06 +02:00
committed by GitHub
parent 6ef15713b1
commit 3ee1b528a1
60 changed files with 456 additions and 239 deletions
+121 -82
View File
@@ -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>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://greptile.com"><img src="./packages/twenty-website/public/images/readme/greptile.png" height="28" alt="Greptile" /></a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://sentry.io/"><img src="./packages/twenty-website/public/images/readme/sentry.png" height="28" alt="Sentry" /></a>
&nbsp;&nbsp;&nbsp;&nbsp;
<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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

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>
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M3 19a9 9 0 0 1 9 0a9 9 0 0 1 9 0"/>
<path d="M3 6a9 9 0 0 1 9 0a9 9 0 0 1 9 0"/>
<path d="M3 6v13"/>
<path d="M12 6v13"/>
<path d="M21 6v13"/>
</svg>

After

Width:  |  Height:  |  Size: 368 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M7 8l-4 4l4 4"/>
<path d="M17 8l4 4l-4 4"/>
<path d="M14 4l-4 16"/>
</svg>

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 827 KiB

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 KiB

After

Width:  |  Height:  |  Size: 302 KiB

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="9"/>
<path d="M3.6 9h16.8"/>
<path d="M3.6 15h16.8"/>
<path d="M11.5 3a17 17 0 0 0 0 18"/>
<path d="M12.5 3a17 17 0 0 1 0 18"/>
</svg>

After

Width:  |  Height:  |  Size: 370 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M4 5h7"/>
<path d="M9 3v2c0 4.418 -2.239 8 -5 8"/>
<path d="M5 9c0 2.144 2.952 3.908 6.7 4"/>
<path d="M12 20l4 -9l4 9"/>
<path d="M19.1 18h-6.2"/>
</svg>

After

Width:  |  Height:  |  Size: 372 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"/>
<path d="M8 11l0 5"/>
<path d="M8 8l0 .01"/>
<path d="M12 16l0 -5"/>
<path d="M16 16v-3a2 2 0 0 0 -4 0"/>
</svg>

After

Width:  |  Height:  |  Size: 413 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M3 7l6 -3l6 3l6 -3v13l-6 3l-6 -3l-6 3v-13"/>
<path d="M9 4v13"/>
<path d="M15 7v13"/>
</svg>

After

Width:  |  Height:  |  Size: 306 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M3 20l1.3 -3.9a9 8 0 1 1 3.4 2.9l-4.7 1"/>
<path d="M12 12l0 .01"/>
<path d="M8 12l0 .01"/>
<path d="M16 12l0 .01"/>
</svg>

After

Width:  |  Height:  |  Size: 339 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="3" y="4" width="18" height="12" rx="1"/>
<path d="M7 20h10"/>
<path d="M9 16v4"/>
<path d="M15 16v4"/>
</svg>

After

Width:  |  Height:  |  Size: 325 B

@@ -1,55 +1,5 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg version="1.1" id="_x36_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<g>
<g>
<g>
<path style="fill:#504F4F;" d="M512,28.891v308.708c0,0.986-0.071,1.973-0.141,2.959c-0.07,0.916-0.212,1.832-0.423,2.678
c-2.819,13.177-14.939,23.112-29.454,23.112H30.018c-1.762,0-3.523-0.141-5.214-0.423c-12.754-2.184-22.76-12.049-24.522-24.522
c-0.071-0.493-0.141-1.057-0.141-1.55C0,339.078,0,338.374,0,337.599V28.891c0-1.903,0.212-3.805,0.564-5.638
C3.312,10.006,15.502,0,30.018,0h451.964c1.057,0,2.184,0.071,3.241,0.141c1.057,0.141,2.044,0.282,3.1,0.493
c11.838,2.466,21.069,11.697,23.183,23.183c0.141,0.704,0.282,1.409,0.352,2.114C511.929,26.918,512,27.904,512,28.891z"/>
<path style="fill:#C2C1C1;" d="M80.119,0v366.419H28.82c-1.268,0-2.537-0.071-3.805-0.282
c-12.895-1.691-23.042-11.909-24.733-24.733c-0.071-0.493-0.141-1.057-0.141-1.55C0,339.078,0,338.374,0,337.599V28.891
c0-1.903,0.212-3.805,0.564-5.638C3.171,10.006,14.798,0,28.82,0H80.119z"/>
<path style="fill:#C2C1C1;" d="M512,28.891v308.708c0,0.986-0.071,1.973-0.141,2.959c-1.48,14.445-13.741,25.861-28.679,25.861
h-51.298V0h51.298c1.691,0,3.382,0.141,4.933,0.423c11.979,2.114,21.351,11.556,23.465,23.465
c0.141,0.705,0.211,1.339,0.282,2.043C511.929,26.918,512,27.904,512,28.891z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M246.925,28.905v313.198H62.734c-5.9,0-11.303-2.148-15.463-5.674
c-5.176-4.385-8.477-10.941-8.477-18.265V52.844c0-13.224,10.715-23.939,23.94-23.939H246.925z"/>
<g>
<path style="fill:#FFFFFF;" d="M254.348,28.905v313.198h184.191c5.9,0,11.303-2.148,15.462-5.674
c5.176-4.385,8.477-10.941,8.477-18.265V52.844c0-13.224-10.715-23.939-23.94-23.939H254.348z"/>
</g>
</g>
<polygon style="fill:#E14D4B;" points="406.02,0 406.02,91.182 373.818,73.707 341.545,91.182 341.545,0 "/>
<path style="fill:#FFFFFF;" d="M387.656,36.571h-11.063v-0.07l-3.03-9.372l-0.423-1.409h-0.141l-0.423,1.409l-3.1,9.442h-11.063
v0.141l8.949,6.624l-1.127,3.523l-2.396,7.328l0.141,0.071l9.09-6.765l9.02,6.765l0.141-0.071l-3.523-10.851l8.949-6.624V36.571z"
/>
<rect x="73.178" y="75.654" style="fill:#D8D8D8;" width="145.522" height="72.479"/>
<rect x="282.812" y="102.763" style="fill:#D8D8D8;" width="42.42" height="41.549"/>
<rect x="335.706" y="102.763" style="fill:#D8D8D8;" width="42.42" height="41.549"/>
<rect x="388.6" y="103.253" style="fill:#D8D8D8;" width="42.42" height="41.549"/>
<rect x="73.178" y="170.951" style="fill:#D8D8D8;" width="145.522" height="6.782"/>
<rect x="73.178" y="207.12" style="fill:#D8D8D8;" width="145.522" height="6.782"/>
<rect x="73.178" y="250.071" style="fill:#D8D8D8;" width="145.522" height="6.782"/>
<rect x="73.178" y="283.98" style="fill:#D8D8D8;" width="145.522" height="6.782"/>
<g>
<rect x="283.411" y="170.951" style="fill:#D8D8D8;" width="145.522" height="6.782"/>
<rect x="283.411" y="207.12" style="fill:#D8D8D8;" width="145.522" height="6.782"/>
<rect x="283.411" y="250.071" style="fill:#D8D8D8;" width="145.522" height="6.782"/>
<rect x="283.411" y="283.98" style="fill:#D8D8D8;" width="145.522" height="6.782"/>
</g>
</g>
<path style="opacity:0.03;fill:#040000;" d="M507.842,28.82v308.708c0,0.423-0.141,0.704-0.141,1.127
c-0.563,14.868-12.402,26.988-27.199,27.693c-0.493,0.071-0.987,0.071-1.48,0.071h-51.369v-0.071H49.255l30.864-30.864
l44.746-44.745l6.764-6.765l27.129-27.129l6.764-6.765l36.149-36.149l6.835-6.835l45.873-45.873l30.934-30.934l27.552-27.552
l54.117-54.117l10.358-10.288v-0.071l9.372-9.372L406.02,9.583L415.603,0h63.419c3.805,0,7.399,0.775,10.781,2.114
c7.117,2.889,12.825,8.597,15.714,15.714c1.268,3.03,2.044,6.342,2.184,9.794C507.701,28.045,507.842,28.468,507.842,28.82z"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"/>
<path d="M4 12h16"/>
<path d="M12 4v8"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"/>
<path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"/>
<circle cx="15" cy="9" r="1"/>
</svg>

After

Width:  |  Height:  |  Size: 398 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

@@ -0,0 +1 @@
<svg width="24" height="24" fill="#143055" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Nx</title><path d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -0,0 +1 @@
<svg width="24" height="24" fill="#61DAFB" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>React</title><path d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1 @@
<svg width="24" height="24" fill="#FF4438" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Redis</title><path d="M22.71 13.145c-1.66 2.092-3.452 4.483-7.038 4.483-3.203 0-4.397-2.825-4.48-5.12.701 1.484 2.073 2.685 4.214 2.63 4.117-.133 6.94-3.852 6.94-7.239 0-4.05-3.022-6.972-8.268-6.972-3.752 0-8.4 1.428-11.455 3.685C2.59 6.937 3.885 9.958 4.35 9.626c2.648-1.904 4.748-3.13 6.784-3.744C8.12 9.244.886 17.05 0 18.425c.1 1.261 1.66 4.648 2.424 4.648.232 0 .431-.133.664-.365a100.49 100.49 0 0 0 5.54-6.765c.222 3.104 1.748 6.898 6.014 6.898 3.819 0 7.604-2.756 9.33-8.965.2-.764-.73-1.361-1.261-.73zm-4.349-5.013c0 1.959-1.926 2.922-3.685 2.922-.941 0-1.664-.247-2.235-.568 1.051-1.592 2.092-3.225 3.21-4.973 1.972.334 2.71 1.43 2.71 2.619z"/></svg>

After

Width:  |  Height:  |  Size: 776 B

@@ -0,0 +1 @@
<svg width="24" height="24" fill="#3178C6" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>TypeScript</title><path d="M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111a6.38 6.38 0 0 1 1.306.34v2.458a3.95 3.95 0 0 0-.643-.361 5.093 5.093 0 0 0-.717-.26 5.453 5.453 0 0 0-1.426-.2c-.3 0-.573.028-.819.086a2.1 2.1 0 0 0-.623.242c-.17.104-.3.229-.393.374a.888.888 0 0 0-.14.49c0 .196.053.373.156.529.104.156.252.304.443.444s.423.276.696.41c.273.135.582.274.926.416.47.197.892.407 1.266.628.374.222.695.473.963.753.268.279.472.598.614.957.142.359.214.776.214 1.253 0 .657-.125 1.21-.373 1.656a3.033 3.033 0 0 1-1.012 1.085 4.38 4.38 0 0 1-1.487.596c-.566.12-1.163.18-1.79.18a9.916 9.916 0 0 1-1.84-.164 5.544 5.544 0 0 1-1.512-.493v-2.63a5.033 5.033 0 0 0 3.237 1.2c.333 0 .624-.03.872-.09.249-.06.456-.144.623-.25.166-.108.29-.234.373-.38a1.023 1.023 0 0 0-.074-1.089 2.12 2.12 0 0 0-.537-.5 5.597 5.597 0 0 0-.807-.444 27.72 27.72 0 0 0-1.007-.436c-.918-.383-1.602-.852-2.053-1.405-.45-.553-.676-1.222-.676-2.005 0-.614.123-1.141.369-1.582.246-.441.58-.804 1.004-1.089a4.494 4.494 0 0 1 1.47-.629 7.536 7.536 0 0 1 1.77-.201zm-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 17.75l-6.172 3.245l1.179 -6.873l-5 -4.867l6.9 -1.002l3.086 -6.253l3.086 6.253l6.9 1.002l-5 4.867l1.179 6.873z"/>
</svg>

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4A38F5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M4 4l11.733 16h4.267l-11.733 -16z"/>
<path d="M4 20l6.768 -6.768m2.46 -2.46l6.772 -6.772"/>
</svg>

After

Width:  |  Height:  |  Size: 310 B