Fixes and updates to the website. (#19350)
This PR implements a whole lot of fixes and updates to the website. It adds a release-notes page, terms and conditions page, privacy policy page, while fixing HomeStepper, ProductStepper, and WhyTwentyStepper. 3D models still need to be styled and their sizes need to be fixed.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import { theme } from '@/theme';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
const DividerOuter = styled.div`
|
||||
margin: ${theme.spacing(10)} 0;
|
||||
width: 100%;
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
`;
|
||||
|
||||
const DividerLine = styled.div`
|
||||
background-color: ${theme.colors.primary.border[20]};
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
margin-left: calc(132px + ${theme.spacing(10)});
|
||||
width: auto;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export function Divider() {
|
||||
return (
|
||||
<DividerOuter aria-hidden>
|
||||
<DividerLine />
|
||||
</DividerOuter>
|
||||
);
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { formatReleaseDisplayDate } from '@/lib/releases/format-release-display-date';
|
||||
import { theme } from '@/theme';
|
||||
import { styled } from '@linaria/react';
|
||||
import { ReleaseMarkdown } from '../ReleaseMarkdown/ReleaseMarkdown';
|
||||
|
||||
const ArticleRow = styled.article`
|
||||
column-gap: ${theme.spacing(6)};
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
min-width: 0;
|
||||
row-gap: ${theme.spacing(4)};
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
column-gap: ${theme.spacing(10)};
|
||||
grid-template-columns: minmax(96px, 132px) minmax(0, 1fr);
|
||||
row-gap: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const MetaColumn = styled.div`
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
min-width: 0;
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
flex-direction: column;
|
||||
padding-top: ${theme.spacing(2)};
|
||||
}
|
||||
`;
|
||||
|
||||
const Version = styled.span`
|
||||
color: ${theme.colors.primary.text[100]};
|
||||
font-size: ${theme.font.size(5)};
|
||||
font-weight: ${theme.font.weight.medium};
|
||||
line-height: 1.3;
|
||||
`;
|
||||
|
||||
const DateText = styled.span`
|
||||
color: ${theme.colors.primary.text[60]};
|
||||
font-size: ${theme.font.size(3)};
|
||||
white-space: nowrap;
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
margin-top: ${theme.spacing(2)};
|
||||
white-space: normal;
|
||||
}
|
||||
`;
|
||||
|
||||
const ContentColumn = styled.div`
|
||||
min-width: 0;
|
||||
`;
|
||||
|
||||
type ReleaseEntryProps = {
|
||||
content: string;
|
||||
date: string;
|
||||
release: string;
|
||||
};
|
||||
|
||||
export function ReleaseEntry({ content, date, release }: ReleaseEntryProps) {
|
||||
const displayDate = formatReleaseDisplayDate(date);
|
||||
|
||||
return (
|
||||
<ArticleRow>
|
||||
<MetaColumn>
|
||||
<Version>{release}</Version>
|
||||
<DateText>{displayDate || '—'}</DateText>
|
||||
</MetaColumn>
|
||||
<ContentColumn>
|
||||
<ReleaseMarkdown markdown={content} />
|
||||
</ContentColumn>
|
||||
</ArticleRow>
|
||||
);
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
'use client';
|
||||
|
||||
import { theme } from '@/theme';
|
||||
import { styled } from '@linaria/react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
const MARKETING_ORIGIN =
|
||||
process.env.NEXT_PUBLIC_MARKETING_ASSET_ORIGIN ?? 'https://twenty.com';
|
||||
|
||||
function resolveAssetUrl(url: string | undefined): string {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
if (url.startsWith('/')) {
|
||||
return `${MARKETING_ORIGIN}${url}`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
const Prose = styled.div`
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
|
||||
h1 {
|
||||
color: ${theme.colors.primary.text[100]};
|
||||
font-size: ${theme.font.size(8)};
|
||||
font-weight: ${theme.font.weight.medium};
|
||||
line-height: 1.25;
|
||||
margin: 0;
|
||||
margin-top: ${theme.spacing(8)};
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: ${theme.colors.primary.text[100]};
|
||||
font-size: ${theme.font.size(6)};
|
||||
font-weight: ${theme.font.weight.medium};
|
||||
line-height: 1.3;
|
||||
margin: 0;
|
||||
margin-top: ${theme.spacing(8)};
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: ${theme.colors.primary.text[100]};
|
||||
font-size: ${theme.font.size(5)};
|
||||
font-weight: ${theme.font.weight.medium};
|
||||
line-height: 1.35;
|
||||
margin: 0;
|
||||
margin-top: ${theme.spacing(6)};
|
||||
}
|
||||
|
||||
p {
|
||||
color: ${theme.colors.primary.text[80]};
|
||||
font-size: ${theme.font.size(4)};
|
||||
line-height: 1.65;
|
||||
margin: 0;
|
||||
margin-top: ${theme.spacing(4)};
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
color: ${theme.colors.primary.text[80]};
|
||||
font-size: ${theme.font.size(4)};
|
||||
line-height: 1.65;
|
||||
margin: ${theme.spacing(4)} 0 0;
|
||||
padding-left: ${theme.spacing(6)};
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: ${theme.spacing(2)};
|
||||
}
|
||||
|
||||
a {
|
||||
color: ${theme.colors.highlight[100]};
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: ${theme.colors.primary.border[10]};
|
||||
border-radius: ${theme.radius(1)};
|
||||
font-family: ${theme.font.family.mono};
|
||||
font-size: 0.9em;
|
||||
padding: 0.1em 0.35em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: ${theme.colors.primary.border[10]};
|
||||
border-radius: ${theme.radius(2)};
|
||||
margin-top: ${theme.spacing(4)};
|
||||
overflow: auto;
|
||||
padding: ${theme.spacing(4)};
|
||||
}
|
||||
|
||||
pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: ${theme.radius(2)};
|
||||
display: block;
|
||||
height: auto;
|
||||
margin-top: ${theme.spacing(6)};
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid ${theme.colors.primary.border[20]};
|
||||
margin: ${theme.spacing(8)} 0 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 2px solid ${theme.colors.primary.border[40]};
|
||||
color: ${theme.colors.primary.text[60]};
|
||||
margin: ${theme.spacing(4)} 0 0;
|
||||
padding-left: ${theme.spacing(4)};
|
||||
}
|
||||
`;
|
||||
|
||||
type ReleaseMarkdownProps = {
|
||||
markdown: string;
|
||||
};
|
||||
|
||||
export function ReleaseMarkdown({ markdown }: ReleaseMarkdownProps) {
|
||||
return (
|
||||
<Prose>
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
a: ({ children, href, ...props }) => (
|
||||
<a href={resolveAssetUrl(href)} rel="noopener noreferrer" {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
img: ({ alt, src, ...props }) => (
|
||||
<img alt={alt ?? ''} src={resolveAssetUrl(src)} {...props} />
|
||||
),
|
||||
}}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
urlTransform={(value) => value}
|
||||
>
|
||||
{markdown}
|
||||
</ReactMarkdown>
|
||||
</Prose>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Container } from '@/design-system/components';
|
||||
import { theme } from '@/theme';
|
||||
import { styled } from '@linaria/react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
const StyledSection = styled.section`
|
||||
background-color: ${theme.colors.primary.background[100]};
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledContainer = styled(Container)`
|
||||
padding-bottom: ${theme.spacing(20)};
|
||||
padding-left: ${theme.spacing(4)};
|
||||
padding-right: ${theme.spacing(4)};
|
||||
padding-top: ${theme.spacing(4)};
|
||||
|
||||
@media (min-width: ${theme.breakpoints.md}px) {
|
||||
padding-bottom: ${theme.spacing(28)};
|
||||
padding-left: ${theme.spacing(10)};
|
||||
padding-right: ${theme.spacing(10)};
|
||||
}
|
||||
`;
|
||||
|
||||
const EmptyMessage = styled.p`
|
||||
color: ${theme.colors.primary.text[60]};
|
||||
font-size: ${theme.font.size(4)};
|
||||
line-height: 1.65;
|
||||
margin: 0;
|
||||
max-width: 40rem;
|
||||
|
||||
strong {
|
||||
color: ${theme.colors.primary.text[80]};
|
||||
font-family: ${theme.font.family.mono};
|
||||
font-size: ${theme.font.size(3)};
|
||||
font-weight: ${theme.font.weight.medium};
|
||||
}
|
||||
`;
|
||||
|
||||
type RootProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export function Root({ children }: RootProps) {
|
||||
return (
|
||||
<StyledSection aria-label="Release notes">
|
||||
<StyledContainer>{children}</StyledContainer>
|
||||
</StyledSection>
|
||||
);
|
||||
}
|
||||
|
||||
export { EmptyMessage };
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Divider } from './Divider/Divider';
|
||||
import { EmptyMessage, Root } from './Root/Root';
|
||||
import { ReleaseEntry } from './ReleaseEntry/ReleaseEntry';
|
||||
|
||||
export const ReleaseNotes = {
|
||||
Divider,
|
||||
EmptyMessage,
|
||||
ReleaseEntry,
|
||||
Root,
|
||||
};
|
||||
Reference in New Issue
Block a user