Blog Post Content (#37)

This commit is contained in:
Manuel Garcia Genta 2022-04-13 09:35:37 -03:00 committed by GitHub
parent 72c7938c6d
commit 81094d001c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1096 additions and 63 deletions

View File

@ -1,16 +1,139 @@
@import '~/css/helpers';
.card {
cursor: pointer;
display: flex;
flex-direction: column;
cursor: pointer;
padding-bottom: tovw(2px, 'default', 2px);
white-space: normal;
width: 100%;
&-blog {
border-bottom: none;
&:hover,
&:focus {
.read span {
&::after {
opacity: 0;
}
&::before {
opacity: 1;
background-image: repeating-linear-gradient(
0deg,
currentcolor,
currentcolor tovw(4px, 'default', 3px),
transparent tovw(4px, 'default', 3px),
transparent tovw(8px, 'default', 6px),
currentcolor tovw(8px, 'default', 6px)
),
repeating-linear-gradient(
90deg,
currentcolor,
currentcolor tovw(4px, 'default', 3px),
transparent tovw(4px, 'default', 3px),
transparent tovw(8px, 'default', 6px),
currentcolor tovw(8px, 'default', 6px)
),
repeating-linear-gradient(
currentcolor,
currentcolor tovw(4px, 'default', 3px),
transparent tovw(4px, 'default', 3px),
transparent tovw(8px, 'default', 6px),
currentcolor tovw(8px, 'default', 6px)
),
repeating-linear-gradient(
270deg,
currentcolor,
currentcolor tovw(4px, 'default', 3px),
transparent tovw(4px, 'default', 3px),
transparent tovw(8px, 'default', 6px),
currentcolor tovw(8px, 'default', 6px)
);
}
}
}
&::after,
&::before {
content: none;
}
.content {
height: auto;
}
.read {
span {
position: relative;
&::before {
position: absolute;
top: 100%;
left: 0;
opacity: 0;
background-image: repeating-linear-gradient(
0deg,
currentcolor,
currentcolor 100%,
transparent 100%,
transparent 100%,
currentcolor 100%
),
repeating-linear-gradient(
90deg,
currentcolor,
currentcolor 100%,
transparent 100%,
transparent 100%,
currentcolor 100%
),
repeating-linear-gradient(
currentcolor,
currentcolor 100%,
transparent 100%,
transparent 100%,
currentcolor 100%
),
repeating-linear-gradient(
270deg,
currentcolor,
currentcolor 100%,
transparent 100%,
transparent 100%,
currentcolor 100%
);
background-position: 0 0, 0 0, 100% 0, 0 100%;
background-repeat: no-repeat;
background-size: 0 100%, 100% 0, 0 100%, 100% 2px;
width: 100%;
height: tovw(1px, 'default', 1px);
animation: border var(--duration-normal) linear infinite;
content: '';
pointer-events: none;
}
&::after {
position: absolute;
top: 100%;
left: 0;
background: currentcolor;
width: 100%;
height: tovw(1px);
content: '';
}
}
svg {
height: 10px;
width: 10px;
}
}
> svg {
display: none;
}
}
&__header {
@ -170,3 +293,15 @@
font-size: tovw(18px, 'default', 15px);
}
}
@keyframes border {
from {
background-position: 0 0, tovw(8px, 'default', 6px) 0,
100% tovw(8px, 'default', 6px), 0 100%;
}
to {
background-position: 0 tovw(8px, 'default', 6px), 0 0, 100% 0,
tovw(8px, 'default', 6px) 100%;
}
}

View File

@ -1,6 +1,7 @@
import clsx from 'clsx'
import NextLink from 'next/link'
import { ArrowLink } from '~/components/icons/arrow'
import { Calendar, Clock } from '~/components/icons/events'
import Heading from '~/components/primitives/heading'
import Link from '~/components/primitives/link'
@ -74,7 +75,8 @@ export const BlogCard = ({
horizontal = false
}: BlogCardProps) => {
return (
<div
<Link
href={`/blog/${data?.slug}`}
className={clsx(
s['card'],
s['card-blog'],
@ -114,9 +116,11 @@ export const BlogCard = ({
{data?.title}
</Heading>
{horizontal && <p>{data && getDescription(data)}</p>}
<Link href={`/blog/${data?.slug}`}>READ ARTICLE</Link>
<div className={s['read']}>
<span>READ ARTICLE</span> <ArrowLink />
</div>
</div>
</div>
</div>
</Link>
)
}

View File

@ -10,6 +10,7 @@ $img-height-mobile: 200px;
@media screen and (max-width: 800px) {
transform: none;
padding-bottom: tovw(56px, 'default', 56px);
}
.image__container {

View File

@ -5,11 +5,13 @@ import s from './posts-grid.module.scss'
interface PostsGridProps {
children: React.ReactNode
title?: string
}
const PostsGrid = ({ children }: PostsGridProps) => {
const PostsGrid = ({ children, title }: PostsGridProps) => {
return (
<Section>
{title && <h2 className={s.title}>{title}</h2>}
<Container className={s['container']}>{children}</Container>
</Section>
)

View File

@ -9,6 +9,22 @@
row-gap: tovw(104px, 'default', 81px);
@media screen and (max-width: 800px) {
grid-template-columns: 1fr;
display: flex;
flex-direction: column;
gap: tovw(80px, 'default', 80px);
}
}
.title {
font-family: var(--font-arthemys);
font-size: tovw(76px, 'default', 42px);
font-weight: 400;
line-height: tovw(84px, 'default', 48px);
margin-top: 0;
margin-bottom: tovw(88px, 'default', 44px);
text-align: center;
@media screen and (max-width: 800px) {
letter-spacing: -0.02em;
}
}

View File

@ -26,6 +26,7 @@
text-decoration: none;
white-space: nowrap;
font-size: tovw(12px, 'default', 12px);
line-height: tovw(16px, 'default', 16px);
}
}
}

View File

@ -0,0 +1,16 @@
const Discord = (props) => (
<svg
width={33}
height={26}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M27.936 2.155A26.571 26.571 0 0 0 21.219.002a.1.1 0 0 0-.108.053c-.29.533-.611 1.229-.837 1.775a24.314 24.314 0 0 0-7.544 0 18.232 18.232 0 0 0-.85-1.775.105.105 0 0 0-.107-.053 26.497 26.497 0 0 0-6.717 2.153.097.097 0 0 0-.044.04C.733 8.8-.438 15.245.136 21.61c.003.03.02.06.043.08 2.823 2.142 5.557 3.443 8.24 4.305a.105.105 0 0 0 .116-.039c.635-.896 1.2-1.84 1.686-2.834a.11.11 0 0 0-.057-.15 17.773 17.773 0 0 1-2.574-1.269.111.111 0 0 1-.01-.181c.172-.134.345-.274.51-.414a.1.1 0 0 1 .107-.015c5.4 2.549 11.248 2.549 16.585 0a.099.099 0 0 1 .108.013c.165.141.338.282.512.416a.111.111 0 0 1-.01.181c-.821.497-1.676.916-2.575 1.267a.11.11 0 0 0-.055.152 22.854 22.854 0 0 0 1.684 2.833.103.103 0 0 0 .116.04c2.696-.862 5.43-2.163 8.253-4.305a.11.11 0 0 0 .043-.079c.688-7.358-1.153-13.75-4.88-19.415a.085.085 0 0 0-.042-.04Zm-16.909 15.58c-1.625 0-2.965-1.543-2.965-3.438 0-1.895 1.314-3.438 2.966-3.438 1.665 0 2.991 1.556 2.965 3.438 0 1.895-1.314 3.438-2.966 3.438Zm10.966 0c-1.626 0-2.966-1.543-2.966-3.438 0-1.895 1.314-3.438 2.966-3.438 1.665 0 2.992 1.556 2.966 3.438 0 1.895-1.301 3.438-2.966 3.438Z"
fill="currentColor"
/>
</svg>
)
export default Discord

View File

@ -0,0 +1,16 @@
const Facebook = (props) => (
<svg
width={28}
height={28}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M28 14c0-7.732-6.268-14-14-14S0 6.268 0 14c0 6.988 5.12 12.78 11.813 13.83v-9.783H8.258V14h3.555v-3.084c0-3.51 2.09-5.447 5.287-5.447 1.532 0 3.134.273 3.134.273v3.446H18.47c-1.739 0-2.282 1.079-2.282 2.187V14h3.883l-.62 4.047h-3.262v9.783C22.88 26.78 28 20.988 28 14Z"
fill="currentColor"
/>
</svg>
)
export default Facebook

View File

@ -0,0 +1,20 @@
const Instagram = (props) => (
<svg
width={26}
height={26}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M13 2.341c3.473 0 3.885.015 5.25.076 1.27.056 1.956.27 2.413.447a4.02 4.02 0 0 1 1.493.97 4 4 0 0 1 .97 1.493c.177.457.39 1.148.447 2.412.06 1.371.076 1.782.076 5.25 0 3.474-.015 3.886-.076 5.252-.056 1.27-.27 1.955-.447 2.412a4.02 4.02 0 0 1-.97 1.493 4 4 0 0 1-1.493.97c-.457.177-1.148.39-2.412.447-1.371.06-1.783.076-5.251.076-3.473 0-3.885-.016-5.25-.076-1.27-.056-1.956-.27-2.413-.447a4.02 4.02 0 0 1-1.493-.97 3.998 3.998 0 0 1-.97-1.493c-.178-.457-.39-1.148-.447-2.412-.06-1.372-.076-1.783-.076-5.251 0-3.474.015-3.885.076-5.25.056-1.27.27-1.956.447-2.413a4.02 4.02 0 0 1 .97-1.493 3.999 3.999 0 0 1 1.493-.97c.457-.178 1.148-.39 2.412-.447 1.366-.06 1.778-.076 5.251-.076ZM13 0C9.47 0 9.029.015 7.643.076 6.26.137 5.312.361 4.489.68a6.345 6.345 0 0 0-2.305 1.504 6.37 6.37 0 0 0-1.504 2.3C.36 5.312.137 6.256.076 7.638.016 9.027 0 9.47 0 13c0 3.53.015 3.971.076 5.357.061 1.382.285 2.331.604 3.154a6.345 6.345 0 0 0 1.504 2.305 6.355 6.355 0 0 0 2.3 1.498c.828.32 1.772.544 3.154.605 1.386.06 1.828.076 5.357.076 3.53 0 3.971-.015 5.357-.076 1.382-.061 2.331-.285 3.154-.605a6.355 6.355 0 0 0 2.3-1.498 6.356 6.356 0 0 0 1.498-2.3c.32-.828.544-1.772.605-3.153.06-1.387.076-1.829.076-5.358 0-3.53-.015-3.971-.076-5.357-.061-1.382-.285-2.331-.605-3.154a6.088 6.088 0 0 0-1.488-2.31 6.355 6.355 0 0 0-2.3-1.498c-.828-.32-1.772-.544-3.153-.605C16.97.015 16.529 0 13 0Z"
fill="currentColor"
/>
<path
d="M13 6.322A6.68 6.68 0 0 0 6.322 13 6.68 6.68 0 0 0 13 19.678 6.68 6.68 0 0 0 19.678 13 6.68 6.68 0 0 0 13 6.322Zm0 11.01a4.332 4.332 0 1 1 .001-8.665A4.332 4.332 0 0 1 13 17.332ZM21.5 6.058a1.56 1.56 0 1 1-3.118 0 1.56 1.56 0 0 1 3.119 0Z"
fill="currentColor"
/>
</svg>
)
export default Instagram

View File

@ -0,0 +1,18 @@
const Reddit = (props) => (
<svg
width={28}
height={28}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M28 14c0 7.732-6.268 14-14 14S0 21.732 0 14 6.268 0 14 0s14 6.268 14 14Zm-6.713-2.047c1.13 0 2.046.917 2.046 2.047 0 .835-.507 1.556-1.179 1.883.033.197.05.393.05.606 0 3.144-3.652 5.682-8.171 5.682-4.52 0-8.171-2.538-8.171-5.682 0-.213.016-.426.05-.622A2.04 2.04 0 0 1 4.698 14c0-1.13.917-2.047 2.047-2.047.54 0 1.048.23 1.408.573 1.409-1.031 3.357-1.67 5.535-1.735l1.031-4.88a.398.398 0 0 1 .164-.23.408.408 0 0 1 .279-.048l3.39.72c.228-.491.72-.819 1.293-.819a1.46 1.46 0 0 1 1.457 1.458 1.46 1.46 0 0 1-2.915.065L15.36 6.42l-.933 4.372c2.128.082 4.06.736 5.452 1.735.36-.36.852-.573 1.409-.573ZM10.79 14a1.46 1.46 0 0 0-1.458 1.457 1.46 1.46 0 0 0 1.458 1.458 1.46 1.46 0 0 0 1.457-1.458A1.46 1.46 0 0 0 10.791 14Zm3.225 6.37c.557 0 2.457-.066 3.455-1.065a.421.421 0 0 0 .033-.54.385.385 0 0 0-.54 0c-.639.622-1.965.851-2.931.851s-2.309-.229-2.931-.851a.385.385 0 0 0-.54 0 .385.385 0 0 0 0 .54c.982.983 2.898 1.065 3.454 1.065Zm1.736-4.913a1.46 1.46 0 0 0 1.457 1.458 1.46 1.46 0 0 0 1.458-1.458A1.46 1.46 0 0 0 17.209 14a1.46 1.46 0 0 0-1.457 1.457Z"
fill="currentColor"
/>
</svg>
)
export default Reddit

View File

@ -0,0 +1,18 @@
const Telegram = (props) => (
<svg
width={28}
height={28}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M28 14c0 7.732-6.268 14-14 14S0 21.732 0 14 6.268 0 14 0s14 6.268 14 14Zm-13.498-3.665c-1.362.567-4.084 1.74-8.165 3.517-.663.264-1.01.522-1.041.774-.054.426.48.594 1.206.822l.307.097c.714.233 1.676.505 2.176.515.453.01.96-.177 1.518-.56 3.813-2.575 5.782-3.876 5.905-3.904.088-.02.208-.044.29.028.082.073.074.211.065.248-.053.225-2.147 2.172-3.23 3.18-.339.314-.578.537-.627.588-.11.114-.222.222-.33.325-.663.64-1.161 1.12.028 1.905.572.376 1.03.688 1.486.999.498.34.995.678 1.639 1.1.163.107.32.218.472.327.58.413 1.101.785 1.745.726.374-.035.76-.386.957-1.436.464-2.479 1.376-7.851 1.586-10.065a2.469 2.469 0 0 0-.023-.551.59.59 0 0 0-.2-.38c-.167-.135-.426-.164-.542-.162-.526.01-1.334.29-5.222 1.907Z"
fill="currentColor"
/>
</svg>
)
export default Telegram

View File

@ -0,0 +1,16 @@
const Twitter = (props) => (
<svg
width={31}
height={25}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M9.752 25c11.696 0 18.094-9.62 18.094-17.958 0-.27-.006-.547-.018-.818 1.245-.893 2.319-2 3.172-3.268-1.16.512-2.39.847-3.65.992A6.355 6.355 0 0 0 30.146.457a12.843 12.843 0 0 1-4.037 1.532A6.383 6.383 0 0 0 22.5.083a6.41 6.41 0 0 0-4.032.664 6.34 6.34 0 0 0-2.795 2.96 6.277 6.277 0 0 0-.406 4.038 18.17 18.17 0 0 1-7.259-1.913 18.039 18.039 0 0 1-5.847-4.678 6.279 6.279 0 0 0-.695 4.598A6.32 6.32 0 0 0 4.13 9.577a6.404 6.404 0 0 1-2.882-.787v.078a6.28 6.28 0 0 0 1.437 4 6.367 6.367 0 0 0 3.666 2.19 6.368 6.368 0 0 1-2.87.108 6.327 6.327 0 0 0 2.262 3.137 6.404 6.404 0 0 0 3.677 1.25A12.817 12.817 0 0 1 0 22.165 18.142 18.142 0 0 0 9.752 25Z"
fill="currentColor"
/>
</svg>
)
export default Twitter

View File

@ -0,0 +1,64 @@
import Link from 'next/link'
import { Container } from '~/components/layout/container'
import Section from '~/components/layout/section'
import Discord from './icons/discord'
import Facebook from './icons/facebook'
import Instagram from './icons/instagram'
import Reddit from './icons/reddit'
import Telegram from './icons/telegram'
import Twitter from './icons/twitter'
import s from './shares.module.scss'
interface SharesProps {
url: string
}
const Shares = ({ url }: SharesProps) => {
const encodedUrl = encodeURIComponent(url)
return (
<Section>
<Container className={s['container']}>
<p>SHARE THIS ARTICLE</p>
<div className={s['links']}>
<Link href={`https://twitter.com/intent/tweet?url=${encodedUrl}`}>
<a target="_blank">
<Twitter />
</a>
</Link>
<Link href={`https://reddit.com/submit?url=${encodedUrl}`}>
<a target="_blank">
<Reddit />
</a>
</Link>
<Link
href={`https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`}
>
<a target="_blank">
<Facebook />
</a>
</Link>
<Link href="https://discord.com/invite/ukhbBemyxY">
<a target="_blank">
<Discord />
</a>
</Link>
<Link href={`https://t.me/share/url?url=${encodedUrl}`}>
<a target="_blank">
<Instagram />
</a>
</Link>
<Link href={`https://t.me/share/url?url=${encodedUrl}`}>
<a target="_blank">
<Telegram />
</a>
</Link>
</div>
</Container>
</Section>
)
}
export default Shares

View File

@ -0,0 +1,47 @@
@import '~/css/helpers';
.container {
border-top: 1px solid var(--color-white);
padding-top: tovw(96px, 'default', 48px);
padding-bottom: tovw(243px, 'default', 145px);
text-align: center;
max-width: tovw(856px, 'default', 856px);
> p {
font-size: tovw(18px, 'default', 12px);
line-height: tovw(23px, 'default', 16px);
margin-bottom: tovw(56px, 'default', 32px);
@media screen and (max-width: 800px) {
letter-spacing: -0.02em;
}
}
> div {
display: flex;
gap: tovw(32px, 'default', 16px);
justify-content: center;
flex-wrap: wrap;
a {
align-items: center;
background-color: var(--color-black);
border: 1px solid var(--color-white);
border-radius: 30px;
color: var(--color-white);
display: flex;
height: tovw(56px, 'default', 48px);
justify-content: center;
transition: filter 0.2s, filter 0.2s;
width: tovw(72px, 'default', 64px);
svg {
height: 28px;
}
&:hover {
filter: invert(1);
}
}
}
}

View File

@ -43,13 +43,6 @@ export const getBlogPosts = async ({
export const getBlogPostsCategories = async () => {
const { allCategories } = await cms().GetBlogPostsCategories()
// Push dummy category
allCategories.push({
id: '0',
slug: 'none',
title: 'No Category'
})
return allCategories
}

View File

@ -14,6 +14,23 @@ fragment BlogPost on BlogPostRecord {
slug
content {
value
blocks {
__typename
... on ImageRecord {
id
title
asset {
url
width
height
}
}
... on CtaRecord {
id
label
url
}
}
}
image {
...Image

View File

@ -99,9 +99,11 @@ export type AuthorRecord_SeoMetaTagsArgs = {
locale?: InputMaybe<SiteLocale>;
};
export type BlogPostModelContentBlocksField = CtaRecord | ImageRecord;
export type BlogPostModelContentField = {
__typename?: 'BlogPostModelContentField';
blocks: Array<Scalars['String']>;
blocks: Array<BlogPostModelContentBlocksField>;
links: Array<Scalars['String']>;
value: Scalars['JsonField'];
};
@ -317,6 +319,33 @@ export type CreatedAtFilter = {
neq?: InputMaybe<Scalars['DateTime']>;
};
/** Record of type Cta (cta) */
export type CtaRecord = {
__typename?: 'CtaRecord';
_createdAt: Scalars['DateTime'];
_firstPublishedAt?: Maybe<Scalars['DateTime']>;
_isValid: Scalars['BooleanType'];
_modelApiKey: Scalars['String'];
_publicationScheduledAt?: Maybe<Scalars['DateTime']>;
_publishedAt?: Maybe<Scalars['DateTime']>;
/** SEO meta tags */
_seoMetaTags: Array<Tag>;
_status: ItemStatus;
_unpublishingScheduledAt?: Maybe<Scalars['DateTime']>;
_updatedAt: Scalars['DateTime'];
createdAt: Scalars['DateTime'];
id: Scalars['ItemId'];
label?: Maybe<Scalars['String']>;
updatedAt: Scalars['DateTime'];
url?: Maybe<Scalars['String']>;
};
/** Record of type Cta (cta) */
export type CtaRecord_SeoMetaTagsArgs = {
locale?: InputMaybe<SiteLocale>;
};
/** Specifies how to filter Date fields */
export type DateFilter = {
/** Search for records with an exact match */
@ -440,6 +469,33 @@ export type GlobalSeoField = {
twitterAccount?: Maybe<Scalars['String']>;
};
/** Record of type Image (image) */
export type ImageRecord = {
__typename?: 'ImageRecord';
_createdAt: Scalars['DateTime'];
_firstPublishedAt?: Maybe<Scalars['DateTime']>;
_isValid: Scalars['BooleanType'];
_modelApiKey: Scalars['String'];
_publicationScheduledAt?: Maybe<Scalars['DateTime']>;
_publishedAt?: Maybe<Scalars['DateTime']>;
/** SEO meta tags */
_seoMetaTags: Array<Tag>;
_status: ItemStatus;
_unpublishingScheduledAt?: Maybe<Scalars['DateTime']>;
_updatedAt: Scalars['DateTime'];
asset?: Maybe<FileField>;
createdAt: Scalars['DateTime'];
id: Scalars['ItemId'];
title?: Maybe<Scalars['String']>;
updatedAt: Scalars['DateTime'];
};
/** Record of type Image (image) */
export type ImageRecord_SeoMetaTagsArgs = {
locale?: InputMaybe<SiteLocale>;
};
export type ImgixParams = {
/**
* Aspect Ratio
@ -2512,7 +2568,7 @@ export type FocalPoint = {
export type AuthorFragment = { __typename?: 'AuthorRecord', name?: string | null };
export type BlogPostFragment = { __typename?: 'BlogPostRecord', title?: string | null, date?: any | null, slug?: string | null, _seoMetaTags: Array<{ __typename?: 'Tag', content?: string | null, tag: string, attributes?: any | null }>, category: Array<{ __typename?: 'CategoryRecord', title?: string | null, slug?: string | null }>, author?: { __typename?: 'AuthorRecord', name?: string | null } | null, content?: { __typename?: 'BlogPostModelContentField', value: any } | null, image?: { __typename?: 'FileField', url: string, alt?: string | null, height?: any | null, width?: any | null, title?: string | null } | null };
export type BlogPostFragment = { __typename?: 'BlogPostRecord', title?: string | null, date?: any | null, slug?: string | null, _seoMetaTags: Array<{ __typename?: 'Tag', content?: string | null, tag: string, attributes?: any | null }>, category: Array<{ __typename?: 'CategoryRecord', title?: string | null, slug?: string | null }>, author?: { __typename?: 'AuthorRecord', name?: string | null } | null, content?: { __typename?: 'BlogPostModelContentField', value: any, blocks: Array<{ __typename: 'CtaRecord', id: any, label?: string | null, url?: string | null } | { __typename: 'ImageRecord', id: any, title?: string | null, asset?: { __typename?: 'FileField', url: string, width?: any | null, height?: any | null } | null }> } | null, image?: { __typename?: 'FileField', url: string, alt?: string | null, height?: any | null, width?: any | null, title?: string | null } | null };
export type CategoryFragment = { __typename?: 'CategoryRecord', id: any, title?: string | null, slug?: string | null };
@ -2535,7 +2591,7 @@ export type GetBlogPostsQueryVariables = Exact<{
}>;
export type GetBlogPostsQuery = { __typename?: 'Query', _allBlogPostsMeta: { __typename?: 'CollectionMetadata', count: any }, allBlogPosts: Array<{ __typename?: 'BlogPostRecord', title?: string | null, date?: any | null, slug?: string | null, _seoMetaTags: Array<{ __typename?: 'Tag', content?: string | null, tag: string, attributes?: any | null }>, category: Array<{ __typename?: 'CategoryRecord', title?: string | null, slug?: string | null }>, author?: { __typename?: 'AuthorRecord', name?: string | null } | null, content?: { __typename?: 'BlogPostModelContentField', value: any } | null, image?: { __typename?: 'FileField', url: string, alt?: string | null, height?: any | null, width?: any | null, title?: string | null } | null }> };
export type GetBlogPostsQuery = { __typename?: 'Query', _allBlogPostsMeta: { __typename?: 'CollectionMetadata', count: any }, allBlogPosts: Array<{ __typename?: 'BlogPostRecord', title?: string | null, date?: any | null, slug?: string | null, _seoMetaTags: Array<{ __typename?: 'Tag', content?: string | null, tag: string, attributes?: any | null }>, category: Array<{ __typename?: 'CategoryRecord', title?: string | null, slug?: string | null }>, author?: { __typename?: 'AuthorRecord', name?: string | null } | null, content?: { __typename?: 'BlogPostModelContentField', value: any, blocks: Array<{ __typename: 'CtaRecord', id: any, label?: string | null, url?: string | null } | { __typename: 'ImageRecord', id: any, title?: string | null, asset?: { __typename?: 'FileField', url: string, width?: any | null, height?: any | null } | null }> } | null, image?: { __typename?: 'FileField', url: string, alt?: string | null, height?: any | null, width?: any | null, title?: string | null } | null }> };
export type BlogPostsSlugQueryVariables = Exact<{
skip: Scalars['IntType'];
@ -2550,7 +2606,7 @@ export type SingleBlogPostQueryVariables = Exact<{
}>;
export type SingleBlogPostQuery = { __typename?: 'Query', blogPost?: { __typename?: 'BlogPostRecord', title?: string | null, date?: any | null, slug?: string | null, _seoMetaTags: Array<{ __typename?: 'Tag', content?: string | null, tag: string, attributes?: any | null }>, category: Array<{ __typename?: 'CategoryRecord', title?: string | null, slug?: string | null }>, author?: { __typename?: 'AuthorRecord', name?: string | null } | null, content?: { __typename?: 'BlogPostModelContentField', value: any } | null, image?: { __typename?: 'FileField', url: string, alt?: string | null, height?: any | null, width?: any | null, title?: string | null } | null } | null };
export type SingleBlogPostQuery = { __typename?: 'Query', blogPost?: { __typename?: 'BlogPostRecord', title?: string | null, date?: any | null, slug?: string | null, _seoMetaTags: Array<{ __typename?: 'Tag', content?: string | null, tag: string, attributes?: any | null }>, category: Array<{ __typename?: 'CategoryRecord', title?: string | null, slug?: string | null }>, author?: { __typename?: 'AuthorRecord', name?: string | null } | null, content?: { __typename?: 'BlogPostModelContentField', value: any, blocks: Array<{ __typename: 'CtaRecord', id: any, label?: string | null, url?: string | null } | { __typename: 'ImageRecord', id: any, title?: string | null, asset?: { __typename?: 'FileField', url: string, width?: any | null, height?: any | null } | null }> } | null, image?: { __typename?: 'FileField', url: string, alt?: string | null, height?: any | null, width?: any | null, title?: string | null } | null } | null };
export const SeoTagsFragmentDoc = gql`
fragment SEOTags on Tag {
@ -2590,6 +2646,23 @@ export const BlogPostFragmentDoc = gql`
slug
content {
value
blocks {
__typename
... on ImageRecord {
id
title
asset {
url
width
height
}
}
... on CtaRecord {
id
label
url
}
}
}
image {
...Image

View File

@ -568,6 +568,27 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "UNION",
"name": "BlogPostModelContentBlocksField",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": [
{
"kind": "OBJECT",
"name": "CtaRecord",
"ofType": null
},
{
"kind": "OBJECT",
"name": "ImageRecord",
"ofType": null
}
]
},
{
"kind": "OBJECT",
"name": "BlogPostModelContentField",
@ -587,8 +608,8 @@
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"kind": "UNION",
"name": "BlogPostModelContentBlocksField",
"ofType": null
}
}
@ -2302,6 +2323,254 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CtaRecord",
"description": "Record of type Cta (cta)",
"fields": [
{
"name": "_createdAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_firstPublishedAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_isValid",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "BooleanType",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_modelApiKey",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_publicationScheduledAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_publishedAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_seoMetaTags",
"description": "SEO meta tags",
"args": [
{
"name": "locale",
"description": "The locale to use to fetch the field's content",
"type": {
"kind": "ENUM",
"name": "SiteLocale",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Tag",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_status",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "ItemStatus",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_unpublishingScheduledAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_updatedAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "createdAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ItemId",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "label",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updatedAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "url",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "CustomData",
@ -3276,6 +3545,254 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ImageRecord",
"description": "Record of type Image (image)",
"fields": [
{
"name": "_createdAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_firstPublishedAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_isValid",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "BooleanType",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_modelApiKey",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_publicationScheduledAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_publishedAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_seoMetaTags",
"description": "SEO meta tags",
"args": [
{
"name": "locale",
"description": "The locale to use to fetch the field's content",
"type": {
"kind": "ENUM",
"name": "SiteLocale",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Tag",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_status",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "ItemStatus",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_unpublishingScheduledAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "_updatedAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "asset",
"description": null,
"args": [],
"type": {
"kind": "OBJECT",
"name": "FileField",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "createdAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ItemId",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "title",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updatedAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "ImgixParams",

View File

@ -1,8 +1,33 @@
@import '~/css/helpers';
.structured {
font-family: var(--font-tt-hoves);
max-width: tovw(856px, 'default', 856px);
h1 {
margin-top: tovw(124px, 'default', 72px);
font-family: var(--font-arthemys);
font-size: tovw(58px, 'default', 30px);
line-height: 1;
@media screen and (max-width: 800px) {
line-height: 124%;
}
}
h2 {
font-family: var(--font-tt-hoves);
font-size: tovw(40px, 'default', 24px);
font-weight: 400;
line-height: 1;
margin-top: tovw(44px, 'default', 40px);
margin-bottom: tovw(44px, 'default', 24px);
@media screen and (max-width: 800px) {
line-height: 160%;
}
}
p {
font-family: var(--font-tt-hoves);
font-size: tovw(22px, 'default', 15px);
@ -10,4 +35,60 @@
letter-spacing: -0.01em;
margin: tovw(44px, 'default', 40px) 0;
}
blockquote {
margin: tovw(76px, 'default', 40px) 0;
border-left: 4px solid var(--color-accent);
padding-left: tovw(87px, 'default', 22px);
p {
font-size: tovw(28px, 'default', 18px);
line-height: tovw(39px, 'default', 29px);
}
footer {
font-size: tovw(20px, 'default', 15px);
line-height: tovw(32px, 'default', 24px);
}
}
ul {
margin: tovw(76px, 'default', 40px) 0;
padding-left: tovw(72px, 'default', 12px);
list-style: none;
position: relative;
li {
&::before {
content: '';
position: absolute;
border-radius: 50%;
height: 8px;
width: 8px;
margin-top: 9px;
background: var(--color-accent);
left: 0;
}
}
}
code {
border: tovw(1px, 'default', 1px) solid var(--color-grey-light);
border-radius: tovw(8px, 'default', 4px);
background: var(--color-black);
display: block;
font-family: var(--font-dm-mono), sans-serif;
padding: tovw(40px, 'default', 22px);
word-break: break-all;
white-space: break-spaces;
}
}
.img {
margin: tovw(136px, 'default', 72px) auto;
> img {
object-fit: cover;
width: 100%;
}
}

View File

@ -2,60 +2,31 @@ import { StructuredText } from 'react-datocms'
import Marked from '~/components/common/marked'
import { Container } from '~/components/layout/container'
import { ButtonLink } from '~/components/primitives/button'
import s from './blog.module.scss'
export const renderBlock = ({ record }) => {
switch (record.__typename) {
case 'CalloutRecord':
return <Marked>{record.content ?? ''}</Marked>
case 'ImageRecord':
return (
<div className="my-12">
<div className={s.img}>
<img
src={record.image?.url ?? '/4040404'}
width={record.image?.width ?? 0}
height={record.image?.height ?? 0}
alt={record.image?.alt ?? record.image?.title ?? 'blog image'}
src={record.asset?.url}
width={record.asset?.width ?? 0}
height={record.asset?.height ?? 0}
alt={record.asset?.alt ?? record.asset?.title ?? 'blog image'}
/>
</div>
)
case 'CalloutRecord':
return <Marked>{record.content ?? ''}</Marked>
// case 'ShareLinkRecord':
// return (
// <div className="flex justify-center mt-20 space-x-4">
// <SocialLink variant="logo-only" tag="/" type="twitter" />
// <SocialLink variant="logo-only" tag="/" type="telegram" />
// <SocialLink variant="logo-only" tag="/" type="facebook" />
// <SocialLink variant="logo-only" type="copy-link" />
// </div>
// )
// case 'CtaRecord':
// return (
// <SectionCTAs
// className="flex flex-col items-center justify-center mt-20 space-y-2 sm:space-y-0 sm:space-x-2 sm:flex-row"
// ctas={
// record?.links?.map((link, i) => {
// return {
// href: link?.href ?? '',
// as: 'a',
// children: link?.label ?? '',
// variant: i === 0 ? 'primary' : 'tertiary',
// size: 'lg'
// }
// }) ?? []
// }
// />
// )
// case 'TableRecord':
// return (
// <ViewportWidthBox>
// <Container size="sm">
// <div className={s.table}>
// <Marked>{record.markdownTable ?? ''}</Marked>
// </div>
// </Container>
// </ViewportWidthBox>
// )
case 'CtaRecord':
return (
<ButtonLink variant="primary" size="large" href={record.url}>
{record.label}
</ButtonLink>
)
default:
return null
}

View File

@ -11,6 +11,7 @@ import {
InferGetStaticPropsType
} from 'next'
import Error from 'next/error'
import { useRouter } from 'next/router'
import { Key } from 'react'
import { BlogCard } from '~/components/common/card'
@ -18,6 +19,7 @@ import { Meta } from '~/components/common/meta'
import Content from '~/components/sections/blog/post-content'
import Hero from '~/components/sections/blog/post-hero'
import PostsGrid from '~/components/sections/blog/posts-grid'
import Shares from '~/components/sections/blog/shares'
import { BlogPostFragment } from '~/lib/cms/generated'
const BlogPost = ({
@ -28,12 +30,17 @@ const BlogPost = ({
return <Error statusCode={404} />
}
const router = useRouter()
// `${location.origin}${router.asPath}`
const completeUrl = `https://laconic.com${router.asPath}`
return (
<PageLayout>
<Meta />
<Hero data={post} />
<Content data={post} />
<PostsGrid>
<Shares url={completeUrl} />
<PostsGrid title="Related articles">
{latestPosts.map((relatedPost: BlogPostFragment, index: Key) => {
return (
<div key={index}>