toggleMenu(false)}
11 | />
12 | );
13 | }
14 |
15 | Overlay.propTypes = {
16 | showOverlay: PropTypes.bool,
17 | isOverview: PropTypes.bool,
18 | toggleMenu: PropTypes.func,
19 | };
20 |
21 | Overlay.defaultProps = {
22 | showOverlay: false,
23 | isOverview: false,
24 | };
25 |
--------------------------------------------------------------------------------
/src/components/Overlay/Overlay.module.css:
--------------------------------------------------------------------------------
1 | .mobile_menu_overlay {
2 | content: "";
3 | position: fixed;
4 | top: 63px;
5 | bottom: 0;
6 | left: 0;
7 | right: 0;
8 | background-color: var(--neural-950-color);
9 | opacity: 0.8;
10 | z-index: 2;
11 | display: block;
12 | }
13 | .mobile_menu_overlay.overview {
14 | top: 0;
15 | }
16 |
17 | @media (min-width: 1024px) {
18 | .mobile_menu_overlay {
19 | display: none;
20 | }
21 |
22 | .mobile_menu_overlay.overview {
23 | top: 0;
24 | display: block;
25 | }
26 | }
27 |
28 |
29 | @media (min-width: 1280px) {
30 | .mobile_menu_overlay.overview {
31 | display: none;
32 | }
33 | }
--------------------------------------------------------------------------------
/src/components/Overlay/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './Overlay';
2 |
--------------------------------------------------------------------------------
/src/components/PagePagination/PagePagination.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import PagePaginationLink from '../PagePaginationLink';
4 | import clsx from 'clsx';
5 | import styles from './PagePagination.module.css';
6 |
7 | export default function PagePagination({ text, link, hasPadding }) {
8 | return (
9 |
12 | );
13 | }
14 |
15 | PagePagination.propTypes = {
16 | text: PropTypes.string.isRequired,
17 | link: PropTypes.string.isRequired,
18 | hasPadding: PropTypes.bool,
19 | };
20 |
21 | PagePagination.defaultProps = {
22 | hasPadding: true,
23 | };
24 |
--------------------------------------------------------------------------------
/src/components/PagePagination/PagePagination.module.css:
--------------------------------------------------------------------------------
1 | .pagePagination {
2 | display: flex;
3 | justify-content: flex-end;
4 | align-items: center;
5 | margin: 5rem auto;
6 | max-width: var(--xl);
7 | }
8 |
9 | .pagePagination.padding {
10 | padding-inline: 2rem;
11 | }
--------------------------------------------------------------------------------
/src/components/PagePagination/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './PagePagination';
2 |
--------------------------------------------------------------------------------
/src/components/PagePaginationLink/PagePaginationLink.jsx:
--------------------------------------------------------------------------------
1 | import styles from './PagePaginationLink.module.css';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import Svg from '../Svg';
5 | import Link from 'next/link';
6 | import React from 'react';
7 | import { useRouter } from 'next/router';
8 | import navbarLinks from '@/data/navbarLinks.yaml';
9 |
10 | export default function PagePaginationLink({ link, navDirection, text, ...props }) {
11 | const [isLastLink, setIsLastLink] = React.useState(false);
12 | const router = useRouter();
13 |
14 | const pagePagination = clsx(styles.pagePagination, {
15 | [styles.prevButton]: navDirection === 'previous',
16 | });
17 |
18 | const direction = clsx(styles.direction, {
19 | [styles.prevDirection]: navDirection === 'previous',
20 | });
21 |
22 | React.useEffect(() => {
23 | const items = navbarLinks.items;
24 | setIsLastLink(router.pathname.match(new RegExp(items[items.length - 1].href, 'gi')) ? true : false);
25 | }, [link]);
26 |
27 | if (isLastLink) return null;
28 |
29 | return (
30 |
31 |
32 |
33 | {navDirection}
34 |
40 |
41 | {`Go to "${text}"`}
42 |
43 |
44 | );
45 | }
46 | PagePaginationLink.propTypes = {
47 | link: PropTypes.string.isRequired,
48 | navDirection: PropTypes.oneOf(['previous', 'next']),
49 | text: PropTypes.string.isRequired,
50 | };
51 |
52 | PagePaginationLink.defaultProps = {
53 | navDirection: 'next',
54 | };
55 |
--------------------------------------------------------------------------------
/src/components/PagePaginationLink/PagePaginationLink.module.css:
--------------------------------------------------------------------------------
1 | .pagePagination {
2 | display: flex;
3 | justify-content: center;
4 | flex-direction: column;
5 | align-items: flex-end;
6 | background-color: var(--light-04);
7 | width: 100%;
8 | border: 1px solid var(--primary-700);
9 | border-radius: 16px;
10 | padding: 1rem 1.5rem 1.5rem;
11 | gap: 1rem;
12 | transition: all 0.2s ease-in-out;
13 | }
14 |
15 | .prevButton{
16 | align-items: flex-start;
17 | }
18 |
19 | .pagePagination span {
20 | color: var(--primary-400);
21 | }
22 |
23 | .direction {
24 | display: flex;
25 | align-items: center;
26 | justify-content: center;
27 | flex-direction: row;
28 | gap: 0.5rem;
29 | }
30 |
31 | .direction span {
32 | color: var(--green-200);
33 | text-transform: uppercase;
34 | font-size: 0.75rem;
35 | font-weight: 800;
36 | }
37 |
38 | .prevDirection {
39 | flex-direction: row-reverse;
40 | }
41 |
42 | .prevDirection img{
43 | transform: rotate(180deg);
44 | }
45 |
46 | .pagePagination:hover {
47 | border: 1px solid var(--primary-500);
48 | background-color: var(--white-120);
49 | }
50 | .text {
51 | white-space: nowrap;
52 | overflow: hidden;
53 | text-overflow: ellipsis;
54 | max-width: 300px;
55 | }
--------------------------------------------------------------------------------
/src/components/PagePaginationLink/PagePaginationLink.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PagePagination from './PagePaginationLink';
3 |
4 | export default {
5 | title: 'component/Page Pagination',
6 | component: PagePagination,
7 | argTypes: {
8 | navDirection: {
9 | control: 'select',
10 | options: ['next', 'previous'],
11 | require: true,
12 | },
13 | },
14 | };
15 |
16 | const Template = (args) =>
;
17 |
18 | export const Default = Template.bind({});
19 | Default.args = {
20 | link: '/',
21 | text: 'Blockchain 101',
22 | };
23 | export const Previous = Template.bind({});
24 | Previous.args = {
25 | link: 'https://thischain.org',
26 | navDirection: 'previous',
27 | text: 'Map',
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/PagePaginationLink/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './PagePaginationLink';
2 |
--------------------------------------------------------------------------------
/src/components/QuoteCard/QuoteCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import styles from './QuoteCard.module.css';
5 | import CardWithArrow from '@/components/CardWithArrow';
6 | import padNumber from '@/helpers/padNumber';
7 |
8 | const Quotecard = ({ textDirection, cards, children }) => {
9 | const bodyClasses = clsx(styles.body, {
10 | [styles.reverse_body]: textDirection === 'left',
11 | });
12 | const textClasses = clsx({
13 | [styles.left_content]: textDirection === 'left',
14 | [styles.right_content]: textDirection === 'right',
15 | });
16 |
17 | return (
18 |
19 |
20 | {cards.map((card, index) => (
21 |
28 | ))}
29 |
30 |
{children}
31 |
32 | );
33 | };
34 |
35 | Quotecard.propTypes = {
36 | textDirection: PropTypes.oneOf(['left', 'right']),
37 | cards: PropTypes.array.isRequired,
38 | children: PropTypes.node.isRequired,
39 | };
40 |
41 | Quotecard.defaultProps = {
42 | textDirection: 'right',
43 | };
44 |
45 | export default Quotecard;
46 |
--------------------------------------------------------------------------------
/src/components/QuoteCard/QuoteCard.module.css:
--------------------------------------------------------------------------------
1 | .body, .cards {
2 | display: flex;
3 | align-items: center;
4 | justify-content: center;
5 | }
6 |
7 | .body {
8 | flex-wrap: wrap;
9 | }
10 |
11 | .right_content,
12 | .left_content {
13 | margin-top: 2rem;
14 | flex: 1 0 100%;
15 | }
16 |
17 |
18 | .reverse_body {
19 | flex-direction: row-reverse;
20 | }
21 |
22 |
23 | .cards {
24 | flex: 1 0 100%;
25 | gap: 1.5rem;
26 | justify-content: start;
27 | }
28 |
29 | @media (min-width: 1024px) {
30 | .body {
31 | flex-wrap: nowrap;
32 | max-width: var(--xl);
33 | margin: auto;
34 | }
35 |
36 |
37 | .body > .right_content {
38 | margin-left: 7.1875rem;
39 | }
40 |
41 | .body > .left_content {
42 | margin-right: 7.1875rem;
43 | max-width: 20.25rem;
44 | }
45 |
46 | .right_content,
47 | .left_content {
48 | margin-top: unset;
49 | flex: 1;
50 | }
51 |
52 | .cards {
53 | flex: 1;
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/src/components/QuoteCard/QuoteCard.stories.js:
--------------------------------------------------------------------------------
1 | import Quotecard from './QuoteCard';
2 |
3 | export default {
4 | title: 'component/Quote Card',
5 | component: Quotecard,
6 | layout: 'fullscreen',
7 | };
8 |
9 | const Template = (args) => (
10 |
11 | Demand for Smart Contract development skills is projected to grow 122 % over the next 5 years.
12 |
13 | );
14 |
15 | export const Default = Template.bind({});
16 |
17 | Default.args = {
18 | cards: [
19 | {
20 | title: 'Intorduction to Blockchain',
21 | href: '#',
22 | },
23 | {
24 | title: 'Intorduction to Smart Contracts',
25 | href: '#',
26 | },
27 | ],
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/QuoteCard/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './QuoteCard';
2 |
--------------------------------------------------------------------------------
/src/components/ShareModal/ShareModal.stories.js:
--------------------------------------------------------------------------------
1 | import ShareModal from './ShareModal';
2 |
3 | export default {
4 | title: 'component/ Share Modal',
5 | component: ShareModal,
6 | };
7 |
8 | const Template = (args) =>
;
9 |
10 | export const Default = Template.bind({});
11 |
12 | Default.args = {
13 | onClose: null,
14 | url: 'https://www.google.com',
15 | };
16 |
--------------------------------------------------------------------------------
/src/components/ShareModal/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './ShareModal';
2 |
--------------------------------------------------------------------------------
/src/components/Svg/Svg.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | function Svg({ href, width, height, title, ...props }) {
4 | return

;
5 | }
6 |
7 | Svg.propTypes = {
8 | href: PropTypes.string.isRequired,
9 | width: PropTypes.string.isRequired,
10 | height: PropTypes.string.isRequired,
11 | title: PropTypes.string,
12 | };
13 |
14 | export default Svg;
15 |
--------------------------------------------------------------------------------
/src/components/Svg/Svg.stories.js:
--------------------------------------------------------------------------------
1 | import Svg from './Svg';
2 |
3 | export default {
4 | title: 'component/Svg',
5 | component: Svg,
6 | };
7 |
8 | const Template = (args) =>
;
9 |
10 | export const Default = Template.bind({});
11 |
12 | Default.args = {
13 | href: '/icons/arrow-right.svg',
14 | width: '100',
15 | height: '100',
16 | };
17 |
--------------------------------------------------------------------------------
/src/components/Svg/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './Svg';
2 |
--------------------------------------------------------------------------------
/src/components/Testimonial/Testimonial.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import Svg from '@/components/Svg';
3 | import clsx from 'clsx';
4 | import styles from './Testimonial.module.css';
5 |
6 | function Testimonial({ title, source, icon }) {
7 | return (
8 |
9 |
10 |
11 | {title}
12 |
13 | Blockchain development skills are rated as the fastest-growing in-demand skills, with an{' '}
14 | increase of 60x in 2020.
15 |
16 |
17 | source: {source}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | }
27 |
28 | Testimonial.propTypes = {
29 | title: PropTypes.string.isRequired,
30 | content: PropTypes.string,
31 | source: PropTypes.string.isRequired,
32 | icon: PropTypes.string.isRequired,
33 | };
34 |
35 | export default Testimonial;
36 |
--------------------------------------------------------------------------------
/src/components/Testimonial/Testimonial.module.css:
--------------------------------------------------------------------------------
1 | .card {
2 | flex: 1;
3 | width: 100%;
4 | min-height: 11.75rem;
5 | }
6 |
7 | .card__inner {
8 | position: relative;
9 | background-color: var(--white-40);
10 | color: var(--primary-50);
11 | border-radius: 1rem;
12 | transition: all 0.2s ease-in-out;
13 |
14 | display: flex;
15 | flex-direction: column-reverse;
16 | align-items: flex-start;
17 | padding: 1.5rem;
18 | gap: 2rem;
19 | }
20 |
21 | @media (min-width: 762px) {
22 | .card__inner {
23 | flex-direction: row;
24 | align-items: center;
25 | padding: 2rem 4rem;
26 | gap: 4rem;
27 | }
28 | }
29 |
30 | .card__info {
31 | display: flex;
32 | flex-direction: column;
33 | justify-content: center;
34 | align-items: flex-start;
35 | padding: 0px;
36 | gap: 1rem;
37 | }
38 |
39 | .title {
40 | text-transform: uppercase;
41 | color: var(--primary-500);
42 | }
43 |
44 | .content {
45 | font-weight: 700;
46 | width: 100%;
47 | color: var(--primary-50);
48 | background: var(--pink-gradient-200);
49 | -webkit-background-clip: text;
50 | background-clip: text;
51 | -webkit-text-fill-color: transparent;
52 | }
53 |
54 | .content span {
55 | font-weight: 400;
56 | background: var(--primary-50);
57 | -webkit-background-clip: text;
58 | background-clip: text;
59 | -webkit-text-fill-color: transparent;
60 | }
61 |
62 | .pink-gradient-200 {
63 | color: var(--pink-gradient-200);
64 | }
65 |
66 | .source {
67 | font-weight: 400;
68 | color: var(--primary-500);
69 | }
--------------------------------------------------------------------------------
/src/components/Testimonial/Testimonial.stories.js:
--------------------------------------------------------------------------------
1 | import Testimnoial from './Testimonial';
2 |
3 | export default {
4 | title: 'component/Testimnoial',
5 | component: Testimnoial,
6 | argTypes: {
7 | variant: {
8 | control: 'select',
9 | },
10 | },
11 | };
12 |
13 | const Template = (args) =>
;
14 |
15 | export const TestimonialSample = Template.bind({});
16 |
17 | TestimonialSample.args = {
18 | title: 'Did you know that...',
19 | content:
20 | 'Blockchain development skills are rated as the fastest-growing in-demand skills, with an increase of 60x in 2020.',
21 | source: 'blockchainjobs.com',
22 | icon: '/icons/chart.svg',
23 | };
24 |
--------------------------------------------------------------------------------
/src/components/Testimonial/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './Testimonial';
2 |
--------------------------------------------------------------------------------
/src/components/TutorialCard/TutorialCard.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import clsx from 'clsx';
3 | import styles from './TutorialCard.module.css';
4 |
5 | export default function TutorialCard({ heading, author, description, variant, href }) {
6 | const classes = clsx(styles.tutorial_card, { [styles.tutorial_card_gray]: variant === 'gray' });
7 | const heading_classes = clsx(styles.tutorial_card_content, 'text-md--short-semi', styles.tutorial_card_heading);
8 | const author_date_classes = clsx('caption--semi-bold', styles.tutorial_card_author_date);
9 | const description_classes = clsx(styles.tutorial_card_content, 'body-long-02', styles.tutorial_card_description);
10 |
11 | return (
12 |
13 |
14 |
{author}
15 |
{heading}
16 | {description &&
{description}
}
17 |
18 |
19 | );
20 | }
21 |
22 | TutorialCard.propTypes = {
23 | heading: PropTypes.string.isRequired,
24 | author: PropTypes.string.isRequired,
25 | href: PropTypes.string.isRequired,
26 | description: PropTypes.string,
27 | variant: PropTypes.oneOf(['default', 'gray']),
28 | };
29 |
30 | TutorialCard.defaultProps = {
31 | variant: 'default',
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/TutorialCard/TutorialCard.module.css:
--------------------------------------------------------------------------------
1 | .tutorial_card {
2 | width: 100%;
3 | min-height: 70px;
4 | background-color: var(--white-40);
5 | border: 1px solid var(--primary-700);
6 | padding: 1rem;
7 | border-radius: 1rem;
8 | cursor: pointer;
9 | transition: all 0.2s ease-in-out;
10 | display: flex;
11 | flex-direction: column;
12 | justify-content: center;
13 | }
14 |
15 | .tutorial_card_heading {
16 | margin-top: 0.375rem;
17 | color: var(--primary-50);
18 | transition: all 0.2s ease-in-out;
19 | }
20 |
21 | .tutorial_card_author_date {
22 | color: var(--primary-500);
23 | }
24 |
25 | .tutorial_card_author_date > span {
26 | margin-inline: 0.2rem;
27 | }
28 |
29 | .tutorial_card_description {
30 | line-height: 142%;
31 | color: var(--neural-900-color);
32 | }
33 |
34 | .tutorial_card_gray {
35 | background-color: var(--neural-100-color);
36 | border: 1px solid var(--neural-100-color);
37 | }
38 |
39 | .tutorial_card:hover {
40 | border: 1px solid var(--primary-500);
41 | background-color: var(--light-12);
42 | }
--------------------------------------------------------------------------------
/src/components/TutorialCard/TutorialCard.stories.js:
--------------------------------------------------------------------------------
1 | import TutorialCard from './TutorialCard';
2 |
3 | export default {
4 | title: 'component/Tutorial Card',
5 | component: TutorialCard,
6 | argTypes: {
7 | variant: {
8 | control: 'select',
9 | options: ['default', 'gray'],
10 | },
11 | },
12 | };
13 |
14 | const Template = (args) =>
;
15 |
16 | export const Default = Template.bind({});
17 | Default.args = {
18 | heading: 'XRPL Hackathon: New Year, New NFTs',
19 | author: 'Patrick Collins ',
20 | date: 'Feb 12, 2022',
21 | variant: 'default',
22 | description: '',
23 | };
24 |
--------------------------------------------------------------------------------
/src/components/TutorialCard/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './TutorialCard';
2 |
--------------------------------------------------------------------------------
/src/helpers/__tests__/getRandomItemsFromArray.test.js:
--------------------------------------------------------------------------------
1 | import { getRandomItemsFromArray } from '../getRandomItemsFromArray';
2 |
3 | describe('getRandomItemsFromArray function', () => {
4 | const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
5 | const result = getRandomItemsFromArray(array, 4);
6 | test('The output number is correct', () => {
7 | expect(result.length).toBe(4);
8 | });
9 |
10 | test('The outputs are from the array', () => {
11 | for (let i = 0; i < result.length; i++) {
12 | expect(array.includes(result[i])).toBeTruthy();
13 | }
14 | });
15 |
16 | test('The outputs are unique', () => {
17 | for (let i = 0; i < result.length; i++) {
18 | expect(array.filter((r) => r === result[i]).length).toBe(1);
19 | }
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/helpers/__tests__/subscribeTo.test.js:
--------------------------------------------------------------------------------
1 | import 'whatwg-fetch';
2 | import { rest } from 'msw';
3 | import { setupServer } from 'msw/node';
4 |
5 | const server = setupServer(
6 | rest.post('https://api.hubapi.com/contacts/v1/contact', async (req, res, ctx) => {
7 | const body = await req.json();
8 | if (body.email) {
9 | return res(
10 | ctx.status(200),
11 | ctx.json({
12 | message: 'Subscribed',
13 | })
14 | );
15 | } else {
16 | return res(
17 | ctx.status(404),
18 | ctx.json({
19 | message: 'Please, email most be provided',
20 | })
21 | );
22 | }
23 | })
24 | );
25 |
26 | describe('subscribe to function', () => {
27 | beforeAll(() => server.listen());
28 | afterAll(() => server.close());
29 | afterEach(() => server.resetHandlers());
30 |
31 | it('should subscribe', async () => {
32 | const res = await fetch('https://api.hubapi.com/contacts/v1/contact', {
33 | method: 'POST',
34 | body: JSON.stringify({ email: 'damilolajerryhd@gmail.com' }),
35 | });
36 |
37 | const { message } = await res.json();
38 | const status = await res.status;
39 | expect(status).toBe(200);
40 | expect(message).toBe('Subscribed');
41 | });
42 |
43 | it('should fail if email is missing', async () => {
44 | const res = await fetch('https://api.hubapi.com/contacts/v1/contact', {
45 | method: 'POST',
46 | body: JSON.stringify({}),
47 | });
48 |
49 | const status = await res.status;
50 | expect(status).toBe(404);
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/src/helpers/expiredDate.js:
--------------------------------------------------------------------------------
1 | export default (end_date) => {
2 | const todays_date = new Date().getTime();
3 | const event_end_date = new Date(end_date).getTime();
4 |
5 | return todays_date >= event_end_date;
6 | };
7 |
--------------------------------------------------------------------------------
/src/helpers/getInternalCaseStudies.js:
--------------------------------------------------------------------------------
1 | import getPagesInfo from '@/helpers/getPagesInfo';
2 | import internal_case_studies_data from '@/data/internal-case-studies-data';
3 |
4 | const getInternalCaseStudies = async () => {
5 | const infos = await getPagesInfo('case-studies');
6 | const response = infos.map((res) => res.data).filter((res) => res);
7 |
8 | const data = {
9 | ...internal_case_studies_data,
10 | items: response,
11 | };
12 |
13 | return data;
14 | };
15 |
16 | export default getInternalCaseStudies;
17 |
--------------------------------------------------------------------------------
/src/helpers/getPage.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import matter from 'gray-matter';
4 |
5 | const getPost = (pageDir) => {
6 | const fileContents = fs.readFileSync(path.join(`data/pages/${pageDir}`), 'utf8');
7 | const { data, content } = matter(fileContents);
8 | return {
9 | data,
10 | content,
11 | };
12 | };
13 |
14 | export default getPost;
15 |
--------------------------------------------------------------------------------
/src/helpers/getPagesInfo.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import matter from 'gray-matter';
4 |
5 | function getAllPostsData(dir) {
6 | const files = fs.readdirSync(path.join(`data/pages/${dir}`));
7 | const allPostsData = files.map((fileName) => {
8 | const fileContents = fs.readFileSync(path.join(`data/pages/${dir}/${fileName}`), 'utf8');
9 | const { data } = matter(fileContents);
10 | return {
11 | slug: fileName.split('.')[0],
12 | data,
13 | };
14 | });
15 |
16 | return allPostsData;
17 | }
18 |
19 | export default getAllPostsData;
20 |
--------------------------------------------------------------------------------
/src/helpers/getPaths.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 |
4 | const getPaths = (dir) => {
5 | const slugs = fs.readdirSync(path.join(`data/pages/${dir}`));
6 | return slugs.map((slug) => slug.split('.')[0]);
7 | };
8 |
9 | export default getPaths;
10 |
--------------------------------------------------------------------------------
/src/helpers/getRandomItemsFromArray.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | /**
3 | @param {Array} array - The array to get random items from
4 | @param {Number} numItemsToTake - The number of items to get
5 | @returns {Array} - An array of random items from the given array
6 | */
7 | export function getRandomItemsFromArray(array, numItemsToTake) {
8 | /** @type {Array} */
9 | let result = new Array(numItemsToTake);
10 |
11 | /** @type {number} */
12 | let arrayLength = array.length;
13 |
14 | /** @type {Array} */
15 | let taken = new Array(arrayLength);
16 |
17 | if (numItemsToTake > arrayLength) return array;
18 | while (numItemsToTake--) {
19 | /** @type {number} */
20 | const x = Math.floor(Math.random() * arrayLength);
21 |
22 | result[numItemsToTake] = array[x in taken ? taken[x] : x];
23 | taken[x] = --arrayLength in taken ? taken[arrayLength] : arrayLength;
24 | }
25 | return result;
26 | }
27 |
--------------------------------------------------------------------------------
/src/helpers/getTags.js:
--------------------------------------------------------------------------------
1 | const getTags = (str) => {
2 | return str ? str.split(',').map((res) => res && res.trim()) : [];
3 | };
4 |
5 | export default getTags;
6 |
--------------------------------------------------------------------------------
/src/helpers/isElementVisable.js:
--------------------------------------------------------------------------------
1 | function isElementVisable(el) {
2 | let top = el.offsetTop;
3 | let left = el.offsetLeft;
4 | let width = el.offsetWidth;
5 | let height = el.offsetHeight;
6 |
7 | while (el.offsetParent) {
8 | el = el.offsetParent;
9 | top += el.offsetTop;
10 | left += el.offsetLeft;
11 | }
12 |
13 | return (
14 | top >= window.pageYOffset &&
15 | left >= window.pageXOffset &&
16 | top + height <= window.pageYOffset + window.innerHeight &&
17 | left + width <= window.pageXOffset + window.innerWidth
18 | );
19 | }
20 |
21 | export default isElementVisable;
22 |
--------------------------------------------------------------------------------
/src/helpers/padNumber.js:
--------------------------------------------------------------------------------
1 | export default (num, size) => {
2 | return num > size ? num : `0${num + 1}`;
3 | };
4 |
--------------------------------------------------------------------------------
/src/helpers/removePastEvent.js:
--------------------------------------------------------------------------------
1 | import expiredDate from '@/helpers/expiredDate.js';
2 |
3 | export default (item) => {
4 | const result = item.filter((res) => !expiredDate(res.end_date));
5 | return result;
6 | };
7 |
--------------------------------------------------------------------------------
/src/helpers/subscribeTo.js:
--------------------------------------------------------------------------------
1 | const subscribeTo = async (data) => {
2 | const res = fetch('api/newsletter/subscribe', {
3 | method: 'POST',
4 | headers: {
5 | 'Content-Type': 'application/json',
6 | },
7 | body: JSON.stringify(data),
8 | });
9 |
10 | return res;
11 | };
12 |
13 | export default subscribeTo;
14 |
--------------------------------------------------------------------------------
/src/hooks/useGoogleTagManager.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import TagManager from 'react-gtm-module';
3 |
4 | const useGoogleTagManager = (trackingCode) => {
5 | useEffect(() => {
6 | if (trackingCode) {
7 | TagManager.initialize({ gtmId: trackingCode });
8 | }
9 | }, []);
10 | };
11 |
12 | export default useGoogleTagManager;
13 |
--------------------------------------------------------------------------------
/src/hooks/useSubscription.js:
--------------------------------------------------------------------------------
1 | const { default: subscribeTo } = require('@/helpers/subscribeTo');
2 | const { useState } = require('react');
3 |
4 | const useSubscription = ({ email, setEmail }) => {
5 | const [sending, setSending] = useState(false);
6 | const [message, setMessage] = useState('');
7 | const [status, setStatus] = useState('success');
8 | const [showNotification, setShowNotification] = useState(false);
9 |
10 | const resetNotification = () => {
11 | setShowNotification(false);
12 | setMessage('');
13 | setStatus('');
14 | };
15 |
16 | const sendSubscription = () => {
17 | setSending(true);
18 | subscribeTo({
19 | email: email,
20 | })
21 | .then((data) => data.json())
22 | .then((data) => {
23 | const { message, status: subStatus } = data;
24 | setMessage(message);
25 | setStatus(subStatus === 200 ? 'success' : 'fail');
26 | setShowNotification(true);
27 | setSending(false);
28 | if (subStatus === 200) {
29 | setEmail('');
30 | }
31 | });
32 | setTimeout(() => resetNotification(), 3000);
33 | };
34 |
35 | return [sendSubscription, sending, message, status, showNotification];
36 | };
37 |
38 | export default useSubscription;
39 |
--------------------------------------------------------------------------------
/src/layouts/BlogLayout/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './BlogLayout';
2 |
--------------------------------------------------------------------------------
/src/layouts/DefaultLayout/DefaultLayout.jsx:
--------------------------------------------------------------------------------
1 | import NavBar from '@/components/NavBar';
2 | import Head from 'next/head';
3 | import PropTypes from 'prop-types';
4 | import { useRouter } from 'next/router';
5 | import { useState, useEffect } from 'react';
6 | import Footer from '@/components/Footer';
7 | import HomeFooter from '@/components/Footer/HomeFooter';
8 |
9 | export default function DefaultLayout({ children }) {
10 | const { pathname } = useRouter();
11 | const [isSticky, setIsSticky] = useState(false);
12 | const [showHomeFooter, setShowHomeFooter] = useState(false);
13 |
14 | useEffect(() => {
15 | const defaultNavbarPathNames = {
16 | home: '/',
17 | };
18 | if (pathname === defaultNavbarPathNames.home) {
19 | setIsSticky(false);
20 | setShowHomeFooter(true);
21 | } else if (!isSticky) {
22 | setIsSticky(true);
23 | setShowHomeFooter(false);
24 | }
25 |
26 | return () => setIsSticky(true);
27 | }, [pathname, isSticky, showHomeFooter]);
28 |
29 | return (
30 | <>
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
{children}
40 | {showHomeFooter ?
:
}
41 | >
42 | );
43 | }
44 |
45 | DefaultLayout.propTypes = {
46 | children: PropTypes.node,
47 | };
48 |
--------------------------------------------------------------------------------
/src/layouts/DefaultLayout/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './DefaultLayout';
2 |
--------------------------------------------------------------------------------
/src/next-seo.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | title: 'Blockchain Developer Hub',
3 | description: 'Dive into blockchain and smart contract development.',
4 | openGraph: {
5 | type: 'website',
6 | locale: 'en_US',
7 | url: 'https://smartcontract.com/',
8 | site_name: 'Blockchain Developer Hub',
9 | images: [
10 | {
11 | url: 'https://blockchain.education/images/og-image.png',
12 | width: 800,
13 | height: 600,
14 | alt: 'Blockchain Developer Hub',
15 | },
16 | ],
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/src/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css';
2 | import { DefaultSeo } from 'next-seo';
3 | import SEO from '../next-seo.config';
4 | import PropTypes from 'prop-types';
5 | import DefaultLayout from '@/layouts/DefaultLayout';
6 | import useGoogleTagManager from '@/hooks/useGoogleTagManager';
7 |
8 | function MyApp({ Component, pageProps }) {
9 | useGoogleTagManager(process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_TRACKING);
10 |
11 | return (
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | MyApp.propTypes = {
20 | Component: PropTypes.elementType.isRequired,
21 | pageProps: PropTypes.object.isRequired,
22 | };
23 |
24 | export default MyApp;
25 |
--------------------------------------------------------------------------------
/src/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default function handler(req, res) {
4 | res.status(200).json({ name: 'John Doe' });
5 | }
6 |
--------------------------------------------------------------------------------
/src/pages/build.js:
--------------------------------------------------------------------------------
1 | import PagePagination from '@/components/PagePagination';
2 | import data from '@/data/build.yaml';
3 | import removePastEvent from '@/helpers/removePastEvent';
4 | import BuildPageSection from '@/sections/BuildPageSection';
5 | import styles from '@/styles/pages/BuildPage.module.css';
6 | function Build() {
7 | const { categories } = data;
8 | return (
9 |
10 | {categories.map(({ name, overview, items, href }, index) => (
11 |
12 | ))}
13 |
14 |
15 | );
16 | }
17 |
18 | export default Build;
19 |
--------------------------------------------------------------------------------
/src/pages/case-studies/index.js:
--------------------------------------------------------------------------------
1 | import PagePagination from '@/components/PagePagination';
2 | import external_case_studies from '@/data/case-studies.yaml';
3 | import getInternalCaseStudies from '@/helpers/getInternalCaseStudies';
4 | import removePastEvent from '@/helpers/removePastEvent';
5 | import BuildPageSection from '@/sections/BuildPageSection';
6 | import styles from '@/styles/pages/BuildPage.module.css';
7 | import PropTypes from 'prop-types';
8 |
9 | function CaseStudies({ internal_case_studies }) {
10 | const case_studies = {
11 | ...internal_case_studies,
12 | };
13 | case_studies.items = [...case_studies.items, ...external_case_studies.items];
14 | const { name, items, overview, href } = case_studies;
15 |
16 | return (
17 |
21 | );
22 | }
23 |
24 | CaseStudies.propTypes = {
25 | internal_case_studies: PropTypes.object,
26 | };
27 | export default CaseStudies;
28 |
29 | export const getStaticProps = async () => {
30 | const internal_case_studies = await getInternalCaseStudies();
31 | return {
32 | props: {
33 | internal_case_studies,
34 | },
35 | };
36 | };
37 |
--------------------------------------------------------------------------------
/src/pages/ecosystem-map-with-accordion.js:
--------------------------------------------------------------------------------
1 | import AccordionContent from '@/components/AccordionContent';
2 | import EcosystemAccordion from '@/components/EcosystemAccordion';
3 | import ExploreMapBanner from '@/components/ExploreMapBanner';
4 | import styles from '@/styles/pages/EcosystemMap.module.css';
5 | import { useState } from 'react';
6 | import accordionData from '@/data/ecosystem-accordion.yaml';
7 |
8 | function EcosystemMap() {
9 | const [expanded, setExpanded] = useState('');
10 |
11 | const toggleAccordion = (id) => {
12 | setExpanded(expanded && expanded === id ? null : id);
13 | };
14 |
15 | return (
16 |
17 |
18 |
23 |
24 | {accordionData.map((data, index) => (
25 |
34 |
35 |
36 | ))}
37 |
38 | );
39 | }
40 |
41 | export default EcosystemMap;
42 |
--------------------------------------------------------------------------------
/src/pages/ecosystem-map.js:
--------------------------------------------------------------------------------
1 | import ExploreMapBanner from '@/components/ExploreMapBanner';
2 | import styles from '@/styles/pages/EcosystemMap.module.css';
3 |
4 | import clsx from 'clsx';
5 |
6 | function EcosystemMap() {
7 | const titleClasses = clsx('h-600', styles.title);
8 | return (
9 |
10 |
11 |
15 |
16 | The smart contract Blockchain Ecosystem
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
24 | export default EcosystemMap;
25 |
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import getInternalCaseStudies from '@/helpers/getInternalCaseStudies';
2 | import CaseStudyBanner from '@/sections/CaseStudyBanner';
3 | import EcosystemBanner from '@/sections/EcosystemBanner';
4 | import HomeExplore from '@/sections/HomeExplore';
5 | import HomePageBanner from '@/sections/HomePageBanner';
6 | import HomepageBlockchain from '@/sections/HomePageBlockchain';
7 | import HomePageImproveSkillsStories from '@/sections/HomePageImproveSkills';
8 | import PropTypes from 'prop-types';
9 |
10 | import styles from '@/styles/pages/HomePage.module.css';
11 | import HomepageNewsLetter from '@/sections/HomepageNewsLetter';
12 |
13 | export default function Home({ internal_case_studies }) {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
27 | Home.propTypes = {
28 | internal_case_studies: PropTypes.object,
29 | };
30 |
31 | export const getStaticProps = async () => {
32 | const internal_case_studies = await getInternalCaseStudies();
33 | return {
34 | props: {
35 | internal_case_studies,
36 | },
37 | };
38 | };
39 |
--------------------------------------------------------------------------------
/src/pages/learn.js:
--------------------------------------------------------------------------------
1 | import LearnHero from '@/sections/LearnHero';
2 | import data from '@/data/learn.yaml';
3 | import LearnCrypto from '@/sections/LearnCrypto';
4 | import styles from '@/styles/pages/LearnPage.module.css';
5 | import PagePagination from '@/components/PagePagination';
6 |
7 | function Learn() {
8 | const { resources } = data;
9 |
10 | return (
11 |
12 |
13 |
14 | {resources.map(({ id, name, logo, courses, tutorials }) => (
15 |
24 | ))}
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | export default Learn;
32 |
--------------------------------------------------------------------------------
/src/pages/ship.js:
--------------------------------------------------------------------------------
1 | import { MDXRemote } from 'next-mdx-remote';
2 | import { serialize } from 'next-mdx-remote/serialize';
3 | import getPage from '@/helpers/getPage';
4 | import PropTypes from 'prop-types';
5 | import BlogLayout from '@/layouts/BlogLayout';
6 | import ArticleHeader from '@/components/ArticleHeader';
7 | import remarkGfm from 'remark-gfm';
8 | import rehypeSlug from 'rehype-slug';
9 | import styles from '@/styles/MDX.module.css';
10 | import BlogCustomComponents from '@/components/BlogCustomComponents';
11 |
12 | function Ship({ data, content }) {
13 | return (
14 |
15 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 |
29 | Ship.propTypes = {
30 | data: PropTypes.object.isRequired,
31 | content: PropTypes.object.isRequired,
32 | };
33 | export default Ship;
34 |
35 | export const getStaticProps = async () => {
36 | const page = await getPage('ship.md');
37 | const mdxSource = await serialize(page.content, {
38 | mdxOptions: {
39 | remarkPlugins: [remarkGfm],
40 | rehypePlugins: [rehypeSlug],
41 | },
42 | });
43 | return {
44 | props: {
45 | data: page.data,
46 | content: mdxSource,
47 | },
48 | };
49 | };
50 |
--------------------------------------------------------------------------------
/src/sections/BuildPageSection/BuildPageSection.module.css:
--------------------------------------------------------------------------------
1 | .headings {
2 | display: inline-flex;
3 | align-items: center;
4 | font-weight: bold;
5 | color: var(--primary-100);
6 | position: relative;
7 | cursor: pointer;
8 | }
9 |
10 | .headings:hover a {
11 | color: var(--green-500);
12 | }
13 |
14 | .headings:hover > a:last-child {
15 | color: var(--primary-100);
16 | }
17 |
18 | .overview {
19 | color: var(--primary-400);
20 | margin: 0.5rem 0 0;
21 | max-width: 36.8125rem;
22 | }
23 |
24 | .mainContent {
25 | margin: 3.5rem auto;
26 | }
27 |
28 | .cards {
29 | display: grid;
30 | grid-template-columns: 1fr;
31 | gap: 1.5rem;
32 | margin: 3rem 0;
33 | }
34 |
35 | @media (min-width: 768px) {
36 | .cards {
37 | grid-template-columns: repeat(2, 1fr);
38 | }
39 |
40 | }
41 |
42 | @media (min-width: 1024px) {
43 | .mainContent {
44 | max-width: var(--xl);
45 | }
46 |
47 | .cards {
48 | grid-template-columns: repeat(3, 1fr);
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/src/sections/BuildPageSection/BuildPageSection.stories.js:
--------------------------------------------------------------------------------
1 | import BuildPageSection from './BuildPageSection';
2 | import data from '@/data/build.yaml';
3 | const { categories } = data;
4 | const { name, overview, items } = categories[0];
5 |
6 | export default {
7 | title: 'section/Build Page section',
8 | component: BuildPageSection,
9 | layout: 'fullscreen',
10 | };
11 |
12 | const Template = (args) =>
;
13 |
14 | export const Default = Template.bind({});
15 |
16 | Default.args = {
17 | name,
18 | overview,
19 | items,
20 | };
21 |
--------------------------------------------------------------------------------
/src/sections/BuildPageSection/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './BuildPageSection';
2 |
--------------------------------------------------------------------------------
/src/sections/CaseStudyBanner/CaseStudyBanner.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 7.5rem 2rem;
3 | background: var(--bg-gradient-2);
4 | background-position: center;
5 | background-repeat: no-repeat;
6 | background-size: 100% 100%;
7 | }
8 |
9 | .content_wrapper {
10 | max-width: var(--xl);
11 | margin: auto;
12 | }
13 |
14 | .content {
15 | display: flex;
16 | align-items: center;
17 | justify-content: center;
18 | flex-flow: row wrap;
19 | background: var(--white-40);
20 | border-radius: 16px;
21 | padding: 1rem;
22 | gap: 2rem;
23 | }
24 |
25 | .cards {
26 | display: flex;
27 | flex-flow: row wrap;
28 | gap: 1.5rem;
29 | flex: 1;
30 | padding-left: 0;
31 | }
32 |
33 | .info {
34 | color: var(--white);
35 | width: 100%;
36 | }
37 |
38 | .cases_btn {
39 | color: var(--dark-green-700);
40 | background-color: var(--green-300);
41 | border-radius: 40px;
42 | text-transform: uppercase;
43 | }
44 |
45 | .cases_btn:hover {
46 | background-color: var(--green-500);
47 | box-shadow: var(--green-box-shadow);
48 | }
49 |
50 | .description {
51 | margin: 1rem auto 2rem;
52 | letter-spacing: -0.01em;
53 | color: var(--primary-300);
54 | }
55 |
56 | @media (min-width: 1280px) {
57 | .content {
58 | padding: 1.5rem;
59 | }
60 | .info {
61 | width: 240px;
62 | margin-left: 4rem;
63 | }
64 | }
--------------------------------------------------------------------------------
/src/sections/CaseStudyBanner/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './CaseStudyBanner';
2 |
--------------------------------------------------------------------------------
/src/sections/EcosystemBanner/EcosystemBanner.jsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import styles from './EcosystemBanner.module.css';
3 | import ExploreMapBanner from '@/components/ExploreMapBanner';
4 |
5 | export default function CaseStudyBanner() {
6 | return (
7 |
8 |
9 |
10 |

11 |
12 |
13 |
14 | smart contracts ecosystem
15 |
19 | A growing landscape of tools and technologies
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/sections/EcosystemBanner/EcosystemBanner.module.css:
--------------------------------------------------------------------------------
1 | .container,
2 | .content_wrapper, .actions {
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | flex-wrap: wrap;
7 | }
8 |
9 | .container {
10 | background-color: var(--primary-950);
11 | backdrop-filter: blur(64px);
12 | padding: 3rem 0 7.5rem;
13 | }
14 |
15 | .content_wrapper {
16 | background-color: var(--white-40);
17 | padding: 2rem;
18 | border-radius: 1rem;
19 | }
20 |
21 | .image, .subtitle {
22 | box-sizing: border-box;
23 | }
24 |
25 | .image {
26 | border-radius: 1rem;
27 | height: 21.0625rem;
28 | width: 100%;
29 | margin-bottom: 2rem;
30 | border: 1px solid var(--primary-700);
31 | overflow: hidden;
32 | }
33 |
34 | .image > img {
35 | width: 100%;
36 | height: 100%;
37 | object-fit: fill;
38 | }
39 |
40 | .title {
41 | color: var(--primary-50);
42 | margin: 0.75rem auto;
43 | }
44 |
45 | .subtitle {
46 | color: var(--pink-400);
47 | text-transform: uppercase
48 | }
49 |
50 | @media (min-width: 1024px) {
51 | .content_wrapper {
52 | max-width: var(--xl);
53 | flex-wrap: nowrap;
54 | margin: auto;
55 | justify-content: space-between;
56 | }
57 |
58 | .image {
59 | width: 32.625rem;
60 | flex: 1;
61 | margin-right: 7.1875rem;
62 | margin-bottom: 0;
63 | }
64 |
65 | .info {
66 | flex: 1;
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/src/sections/EcosystemBanner/EcosystemBanner.stories.js:
--------------------------------------------------------------------------------
1 | import CaseStudyBanner from './EcosystemBanner';
2 |
3 | export default {
4 | title: 'section/HomePage Banner',
5 | component: CaseStudyBanner,
6 | };
7 |
8 | const Template = (args) =>
;
9 |
10 | export const Default = Template.bind({});
11 |
--------------------------------------------------------------------------------
/src/sections/EcosystemBanner/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './EcosystemBanner';
2 |
--------------------------------------------------------------------------------
/src/sections/HomeExplore/HomeExplore.stories.js:
--------------------------------------------------------------------------------
1 | import HomeExplore from './HomeExplore';
2 |
3 | export default {
4 | title: 'section/HomePage Explore',
5 | component: HomeExplore,
6 | };
7 |
8 | const Template = (args) =>
;
9 |
10 | export const Default = Template.bind({});
11 |
--------------------------------------------------------------------------------
/src/sections/HomeExplore/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './HomeExplore';
2 |
--------------------------------------------------------------------------------
/src/sections/HomePageBanner/HomePageBanner.jsx:
--------------------------------------------------------------------------------
1 | import NavLink from '@/components/NavLink';
2 | import styles from './HomePageBanner.module.css';
3 | import clsx from 'clsx';
4 |
5 | function HomePageBanner() {
6 | const headingClasses = clsx('h-600', styles.heading);
7 | const descriptionClasses = clsx('text-lg--long', styles.description);
8 |
9 | return (
10 |
11 |
12 |
13 | Jump into smart contract development
14 |
15 |
16 | Learn the essentials, become familiar with popular dev stacks, and define your journey to becoming a
17 | blockchain developer.
18 |
19 |
27 |
28 |
29 | );
30 | }
31 |
32 | export default HomePageBanner;
33 |
--------------------------------------------------------------------------------
/src/sections/HomePageBanner/HomePageBanner.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | background-image: url(/images/hero-bg.png);
3 | background-position: center;
4 | background-size: cover;
5 | background-repeat: no-repeat;
6 | background-origin: content-box;
7 | }
8 | .contents {
9 | display: flex;
10 | justify-content: center;
11 | align-items: center;
12 | flex-direction: column;
13 | text-align: center;
14 | padding: 11rem 2rem;
15 | }
16 |
17 | .heading {
18 | background: var(--green-gradient-100);
19 | -webkit-background-clip: text;
20 | -webkit-text-fill-color: transparent;
21 | width: 100%;
22 | max-width: 42rem;
23 | }
24 |
25 | .heading > span{
26 | background: var(--primary-50);
27 | -webkit-background-clip: text;
28 | -webkit-text-fill-color: transparent;
29 | }
30 |
31 | .description {
32 | color: var(--primary-300);
33 | margin: 1.5rem 0 3rem;
34 | width: 100%;
35 | max-width: 32.625rem;
36 | }
37 |
38 | .contents a,
39 | .contents a:hover {
40 | color: var(--dark-green-700);
41 | }
42 | .contents a {
43 | background: var(--green-300);
44 | border-radius: 2.5rem;
45 | width: 100%;
46 | max-width: 11.5625rem;
47 | height: 4rem;
48 | display: flex;
49 | justify-content: center;
50 | transition: all 0.2s ease-in-out;
51 | }
52 |
53 | .contents a:hover {
54 | background: var(--green-500);
55 | box-shadow: var(--green-box-shadow);
56 | }
--------------------------------------------------------------------------------
/src/sections/HomePageBanner/OldHomePageBanner.jsx:
--------------------------------------------------------------------------------
1 | import NavLink from '@/components/NavLink';
2 | import PropTypes from 'prop-types';
3 | import Image from 'next/image';
4 | import styles from './OldHomePageBanner.module.css';
5 | import clsx from 'clsx';
6 |
7 | function Homepagebanner({ label, title, overview, link, linkText, image, imageAlt }) {
8 | const labelClasses = clsx('overline', styles.label);
9 | const titleClasses = clsx('h-500', styles.title);
10 | const overviewClasses = clsx('body-long-01', styles.overview);
11 |
12 | return (
13 |
14 |
15 |
16 |
{label}
17 |
{title}
18 |
{overview}
19 |
20 |
21 |
22 | {image && }
23 |
24 |
25 |
26 | );
27 | }
28 |
29 | Homepagebanner.propTypes = {
30 | title: PropTypes.string,
31 | overview: PropTypes.string,
32 | label: PropTypes.string,
33 | image: PropTypes.string,
34 | link: PropTypes.string,
35 | linkText: PropTypes.string,
36 | imageAlt: PropTypes.string,
37 | };
38 |
39 | export default Homepagebanner;
40 |
--------------------------------------------------------------------------------
/src/sections/HomePageBanner/OldHomePageBanner.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | background-color: var(--secondary-200-color);
3 | padding: 2rem;
4 | }
5 |
6 | .banner {
7 | margin: 2rem auto;
8 | display: flex;
9 | align-items: center;
10 | flex-flow: row wrap;
11 | justify-content: space-between;
12 | max-width: var(--xl);
13 | }
14 |
15 | .title_a {
16 | color: var(--neural-variant-600-color)
17 | }
18 |
19 | .banner_left {
20 | width: 100%;
21 | margin-bottom: 2rem;
22 | }
23 |
24 | .label {
25 | color: var(--neural-700-color);
26 | margin-top: 0;
27 | }
28 |
29 | .title {
30 | color: var(--neural-950-color);
31 | margin: 0.75rem 0;
32 | }
33 |
34 | .overview {
35 | color: var(--neural-800-color);
36 | margin-bottom: 1rem;
37 | }
38 |
39 | .imageContainer {
40 | background-color: var(--secondary-400-color);
41 | flex: 1;
42 | max-width: 32.625rem;
43 | height: 19.5rem;
44 | border-radius: 347.1875rem;
45 | overflow: hidden;
46 | position: relative;
47 | display: none;
48 | }
49 |
50 | @media (min-width: 1024px) {
51 | .container {
52 | height: 32.375rem;
53 | }
54 |
55 | .title, .overview {
56 | width: 28.1875rem;
57 | }
58 |
59 | .banner_left {
60 | flex: 1;
61 | margin-right: 5.9375rem;
62 | margin-bottom: 0;
63 | }
64 | .overview {
65 | margin-bottom: 3rem;
66 | }
67 |
68 | .imageContainer {
69 | display: block;
70 | }
71 | }
--------------------------------------------------------------------------------
/src/sections/HomePageBanner/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './HomePageBanner';
2 |
--------------------------------------------------------------------------------
/src/sections/HomePageBlockchain/HomepageBlockchain.jsx:
--------------------------------------------------------------------------------
1 | // import QuoteCard from '@/components/QuoteCard';
2 | import styles from './HomepageBlockchain.module.css';
3 | import data from '@/data/home.yaml';
4 | import CardWithArrow from '@/components/CardWithArrow';
5 |
6 | function HomepageBlockchain() {
7 | const cardItems = data.hero.featured_articles;
8 |
9 | return (
10 |
11 |
12 |
13 | {cardItems.map((card, index) => (
14 |
21 |
22 |
23 | ))}
24 |
25 |
26 |
27 | );
28 | }
29 |
30 | export default HomepageBlockchain;
31 |
--------------------------------------------------------------------------------
/src/sections/HomePageBlockchain/HomepageBlockchain.module.css:
--------------------------------------------------------------------------------
1 | .container, .cards {
2 | display: flex;
3 | align-items: center;
4 | justify-content: center;
5 | }
6 |
7 | .inner_container {
8 | max-width: var(--xl);
9 | margin: 0 auto;
10 | }
11 |
12 | .container {
13 | padding: 2rem 2rem 4rem;
14 | }
15 |
16 | .cards {
17 | flex-wrap: wrap;
18 | width: 100%;
19 | gap: 1.5rem;
20 | }
--------------------------------------------------------------------------------
/src/sections/HomePageBlockchain/HomepageBlockchain.stories.js:
--------------------------------------------------------------------------------
1 | import HomepageBlockchain from './HomepageBlockchain';
2 |
3 | export default {
4 | title: 'section/Homepage Blockchain',
5 | component: HomepageBlockchain,
6 | };
7 |
8 | const Template = (args) =>
;
9 |
10 | export const Default = Template.bind({});
11 |
--------------------------------------------------------------------------------
/src/sections/HomePageBlockchain/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './HomepageBlockchain';
2 |
--------------------------------------------------------------------------------
/src/sections/HomePageImproveSkills/HomePageImproveSkills.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CardWithArrow from '@/components/CardWithArrow';
3 | import buildData from '@/data/build.yaml';
4 | import styles from './HomePageImproveSkills.module.css';
5 | import clsx from 'clsx';
6 |
7 | function HomePageImproveSkills() {
8 | const data = ['Hackathons', 'Workshops'];
9 |
10 | return (
11 |
12 |
13 |
14 |
15 | Want to test your skills, and ask questions later?
16 |
17 |
18 | Improve your skills by gathering with blockchain community members.
19 |
20 |
21 |
22 | {buildData.categories.map((category, index) => (
23 |
24 | {data.indexOf(category.name) > -1 && (
25 |
32 |
33 |
34 | )}
35 |
36 | ))}
37 |
38 |
39 |
40 | );
41 | }
42 |
43 | export default HomePageImproveSkills;
44 |
--------------------------------------------------------------------------------
/src/sections/HomePageImproveSkills/HomePageImproveSkills.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding:7.5rem 2rem;
3 | }
4 | .content_wrapper {
5 | max-width: var(--xl);
6 | margin: auto;
7 | }
8 |
9 | .title {
10 | width: 100%;
11 | color: var(--primary-50);
12 | max-width: 26.875rem;
13 | background: var(--green-gradient-100);
14 | -webkit-background-clip: text;
15 | background-clip: text;
16 | -webkit-text-fill-color: transparent;
17 | padding-right: 1.8rem;
18 | margin-bottom: 2rem;
19 | }
20 |
21 | .title span {
22 | background: var(--primary-50);
23 | -webkit-background-clip: text;
24 | background-clip: text;
25 | -webkit-text-fill-color: transparent;
26 | }
27 |
28 | .description {
29 | width: 100%;
30 | max-width: 21.0625rem;
31 | min-width: 12rem;
32 | color: var(--primary-300);
33 | padding-right: 1rem;
34 | flex: 1;
35 | }
36 |
37 | .headings, .categories {
38 | display: flex;
39 | flex-flow: row wrap;
40 | }
41 |
42 | .headings {
43 | justify-content: space-between;
44 | margin-bottom: 2.25rem;
45 | }
46 |
47 | .categories {
48 | align-items: center;
49 | justify-content: center;
50 | width: 100%;
51 | gap: 1.5rem;
52 | }
53 |
--------------------------------------------------------------------------------
/src/sections/HomePageImproveSkills/HomePageImproveSkills.stories.js:
--------------------------------------------------------------------------------
1 | import HomepageImproveSkills from './HomePageImproveSkills';
2 |
3 | export default {
4 | title: 'section/Homepage Improve Skills',
5 | component: HomepageImproveSkills,
6 | };
7 |
8 | const Template = (args) =>
;
9 |
10 | export const Default = Template.bind({});
11 |
--------------------------------------------------------------------------------
/src/sections/HomePageImproveSkills/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './HomePageImproveSkills';
2 |
--------------------------------------------------------------------------------
/src/sections/HomePageLifecycleBanner/HomePageLifecycleBanner.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import styles from './HomePageLifecycleBanner.module.css';
3 | import ExploreMapBanner from '@/components/ExploreMapBanner';
4 |
5 | function HomePageLifecycleBanner({ title, overview }) {
6 | return (
7 |
12 | );
13 | }
14 |
15 | HomePageLifecycleBanner.propTypes = {
16 | title: PropTypes.string,
17 | overview: PropTypes.string,
18 | };
19 |
20 | export default HomePageLifecycleBanner;
21 |
--------------------------------------------------------------------------------
/src/sections/HomePageLifecycleBanner/HomePageLifecycleBanner.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | background-color: var(--white-color);
3 | display: flex;
4 | align-items: center;
5 | justify-content: center;
6 | padding: 2rem;
7 | }
8 |
9 |
10 | @media (min-width: 1024px) {
11 | .container {
12 | height: 22.6875rem;
13 | }
14 | .content {
15 | max-width: var(--lg)
16 | }
17 | }
--------------------------------------------------------------------------------
/src/sections/HomePageLifecycleBanner/HomePageLifecycleBanner.stories.js:
--------------------------------------------------------------------------------
1 | import HomePageLifecycleBanner from './HomePageLifecycleBanner';
2 |
3 | export default {
4 | title: 'section/HomePage lifecycle Banner',
5 | component: HomePageLifecycleBanner,
6 | };
7 |
8 | const Template = (args) =>
;
9 |
10 | export const Default = Template.bind({});
11 |
12 | Default.args = {
13 | title: 'Get to know the development cycle',
14 | overview:
15 | 'The ecosystem tools map outlines the entire smart contract developer ecosystem, and all of the tools and products smart contract developers may need to rely on.',
16 | link: '/',
17 | linkText: 'Explore Map',
18 | };
19 | Default.parameters = {
20 | backgrounds: {
21 | default: 'gray',
22 | },
23 | layout: 'fullscreen',
24 | };
25 |
--------------------------------------------------------------------------------
/src/sections/HomePageLifecycleBanner/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './HomePageLifecycleBanner';
2 |
--------------------------------------------------------------------------------
/src/sections/HomepageNewsLetter/HomepageNewsLetter.jsx:
--------------------------------------------------------------------------------
1 | import NewsLetterTextContent from '@/components/NewsLetterTextContent';
2 | import React, { useState } from 'react';
3 | import styles from './HomepageNewsLetter.module.css';
4 | import clsx from 'clsx';
5 | import Notification from '@/components/Notification';
6 | import useSubscription from '@/hooks/useSubscription';
7 |
8 | export default function HomepageNewsLetter() {
9 | const [email, setEmail] = useState('');
10 | const [sendSubscription, sending, message, status, showNotification] = useSubscription({ email, setEmail });
11 |
12 | const handleInput = (e) => setEmail(e.target.value);
13 |
14 | const handleKeyPress = (e) => e.key === 'Enter' && email && sendSubscription();
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
29 |
36 |
37 |
No spam. Unsubscribe any time
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/src/sections/HomepageNewsLetter/HomepageNewsLetter.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | background-color: var(--pink-900);
3 | padding: 4rem 1.5rem;
4 | }
5 |
6 | .inner_container {
7 | max-width: var(--xl);
8 | margin: 0 auto;
9 | display: flex;
10 | justify-content: center;
11 | align-items: center;
12 | flex-direction: column;
13 | gap: 4rem;
14 | }
15 | .sub_container {
16 | display: flex;
17 | justify-content: space-between;
18 | align-items: center;
19 | padding: 0 0.5rem 1rem;
20 | border-bottom: 1px solid var(--pink-800);
21 | }
22 |
23 | .sub {
24 | width: 100%;
25 | }
26 |
27 | .sub_input {
28 | border: none;
29 | outline: none;
30 | background: transparent;
31 | color: var(--white);
32 | font-size: 1.125rem;
33 | width: 100%;
34 | padding-right: 2rem;
35 | }
36 |
37 | .sub_btn {
38 | background-color: var(--pink-400);
39 | color: var(--pink-900);
40 | border-radius: 2.5rem;
41 | font-weight: 800;
42 | text-transform: uppercase;
43 | border: none;
44 | padding: 0.8125rem 1.25rem;
45 | cursor: pointer;
46 | margin-top: 0;
47 | }
48 |
49 | .sub_btn:hover {
50 | background-color: var(--pink-500);
51 | box-shadow: var(--pink-box-shadow);
52 | }
53 | .sub_btn:disabled {
54 | cursor: not-allowed;
55 | background-color: var(--pink-400);
56 | }
57 |
58 | .no_spam {
59 | margin-top: 1rem;
60 | color: var(--pink-400);
61 | padding-inline: 0.5rem;
62 | }
63 |
64 | @media (min-width: 768px) {
65 | .sub {
66 | width: auto;
67 | }
68 | .inner_container {
69 | flex-direction: row;
70 | justify-content: space-between;
71 | }
72 | }
73 | @media (min-width: 1024px) {
74 | .sub_input {
75 | width: 288px;
76 | }
77 | }
--------------------------------------------------------------------------------
/src/sections/HomepageNewsLetter/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './HomepageNewsLetter';
2 |
--------------------------------------------------------------------------------
/src/sections/LearnCrypto/LearnCrypto.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | margin: 4rem auto;
3 | min-height: 100vh;
4 | max-width: var(--xl);
5 | }
6 |
7 | .headings {
8 | color: var(--primary-100);
9 | position: relative;
10 | display: inline-flex;
11 | align-items: center;
12 | cursor: pointer;
13 | }
14 |
15 | .headings:hover a {
16 | color: var(--green-500);
17 | }
18 |
19 | .overview {
20 | color: var(--primary-300);
21 | margin: 0.5rem 0 3rem;
22 | max-width: 36.8125rem;
23 | }
24 |
25 | .mainContent {
26 | margin: 2rem auto;
27 | padding-inline: 2rem;
28 | }
29 |
30 | .cards {
31 | display: grid;
32 | grid-template-columns: 1fr;
33 | gap: 1.5rem;
34 | margin-bottom: 3rem;
35 | }
36 |
37 | .sectionHeading {
38 | width: 100%;
39 | background-color: var(--white-40);
40 | display: flex;
41 | align-items: center;
42 | padding: 1rem 2.4rem;
43 | border-radius: 1rem;
44 | margin: 0 auto 4rem;
45 | color: var(--primary-300);
46 | flex-direction: row-reverse;
47 | justify-content: flex-end;
48 | }
49 |
50 | .logo {
51 | width: 1.1rem;
52 | height: 2rem;
53 | object-fit: contain;
54 | transform: matrix(-1, 0, 0, 1, 0, 0);
55 | margin-right: 1.3rem;
56 | }
57 |
58 | @media (min-width: 768px) {
59 | .cards {
60 | grid-template-columns: repeat(2, 1fr);
61 | }
62 | }
63 |
64 | @media (min-width: 1024px) {
65 |
66 | .cards {
67 | grid-template-columns: repeat(3, 1fr);
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/src/sections/LearnCrypto/LearnCrypto.stories.js:
--------------------------------------------------------------------------------
1 | import LearnCrypto from './LearnCrypto';
2 | import data from '@/data/learn.yaml';
3 | const { resources } = data;
4 | const { id, name, logo, courses, tutorials } = resources[0];
5 |
6 | export default {
7 | title: 'section/Learn Crypto',
8 | component: LearnCrypto,
9 | };
10 |
11 | const Template = (args) =>
;
12 |
13 | export const Default = Template.bind({});
14 |
15 | Default.args = {
16 | id,
17 | name,
18 | logo,
19 | logoAlt: `${name} Logo`,
20 | courses,
21 | tutorials,
22 | };
23 |
24 | Default.parameters = {
25 | layout: 'fullscreen',
26 | };
27 |
--------------------------------------------------------------------------------
/src/sections/LearnCrypto/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './LearnCrypto';
2 |
--------------------------------------------------------------------------------
/src/sections/LearnHero/LearnHero.jsx:
--------------------------------------------------------------------------------
1 | import styles from './LearnHero.module.css';
2 | import clsx from 'clsx';
3 | import data from '@/data/learn.yaml';
4 | import CardWithLogo from '@/components/CardWithLogo';
5 |
6 | function LearnHero() {
7 | const titleClasses = clsx('h-300', styles.title);
8 | const descriptionClasses = clsx('text-lg--long', styles.description);
9 | const dividerClasses = clsx('overline--extra-bold', styles.divider);
10 | const blockchains = data.resources.map(({ id, name, logo }) => ({ id, name, logo }));
11 |
12 | return (
13 |
14 |
15 |
16 |
17 | Learn how to code smart contracts.
18 |
19 |
20 |
21 | Smart contracts are digital contracts stored on a blockchain that are automatically executed when
22 | predetermined conditions are met
23 |
24 |
25 |
26 |
Learn tracks
27 |
28 | {blockchains.map(({ id, name, logo }) => (
29 |
30 | ))}
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | export default LearnHero;
38 |
--------------------------------------------------------------------------------
/src/sections/LearnHero/LearnHero.module.css:
--------------------------------------------------------------------------------
1 | .container, .header, .wrapper {
2 | display: flex;
3 | width: 100%;
4 | }
5 | .container {
6 | background: radial-gradient(57.03% 57.03% at 50% 0%, rgba(202, 255, 130, 0.15) 0%, rgba(55, 166, 103, 0.024) 100%);
7 | padding-bottom: 7.5rem;
8 | }
9 |
10 | .wrapper {
11 | flex-direction: column;
12 | align-items: center;
13 | justify-content: center;
14 | margin: 4.5rem auto 0;
15 | padding-inline: 2rem;
16 | max-width: var(--xl);
17 | }
18 |
19 | .header {
20 | justify-content: space-between;
21 | margin-bottom: 4.25rem;
22 | flex-flow: row wrap;
23 | }
24 |
25 | .title {
26 | width: 100%;
27 | color: var(--primary-50);
28 | max-width: 32.5625rem;
29 | background: var(--green-gradient-100);
30 | -webkit-background-clip: text;
31 | background-clip: text;
32 | -webkit-text-fill-color: transparent;
33 | padding-right: 1.6rem;
34 | margin-bottom: 2rem;
35 | }
36 |
37 | .title span {
38 | background: var(--primary-50);
39 | -webkit-background-clip: text;
40 | background-clip: text;
41 | -webkit-text-fill-color: transparent;
42 | }
43 |
44 | .description {
45 | width: 100%;
46 | max-width: 21.25rem;
47 | min-width: 12rem;
48 | color: var(--primary-300);
49 | padding-right: 1rem;
50 | flex: 1;
51 | }
52 |
53 | .divider {
54 | color: var(--neural-800-color);
55 | width: 100%;
56 | border-top: 1px solid var(--primary-700);
57 | padding: 1rem 0;
58 | text-transform: uppercase;
59 | }
60 |
61 | .blockchainsContainer {
62 | display: flex;
63 | flex-wrap: wrap;
64 | width: 100%;
65 | justify-content: center;
66 | gap: 1.5rem;
67 | max-width: var(--xl);
68 | }
69 | @media (min-width: 768px) {
70 | .blockchainsContainer {
71 | justify-content: center;
72 | flex-wrap: nowrap;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/sections/LearnHero/LearnHero.stories.js:
--------------------------------------------------------------------------------
1 | import LearnHero from './LearnHero';
2 |
3 | export default {
4 | title: 'section/Learn Hero',
5 | component: LearnHero,
6 | };
7 |
8 | const Template = (args) =>
;
9 |
10 | export const Default = Template.bind({});
11 |
--------------------------------------------------------------------------------
/src/sections/LearnHero/index.js:
--------------------------------------------------------------------------------
1 | export { default as default } from './LearnHero';
2 |
--------------------------------------------------------------------------------
/src/stories/MarkdownStyle/MarkdownStyles.stories.js:
--------------------------------------------------------------------------------
1 | import styles from '@/styles/MDX.module.css';
2 | import MdxContent from './MarkdownContent.md';
3 |
4 | export default {
5 | title: 'Markdown/Default',
6 | };
7 |
8 | const Template = () => (
9 |
10 |
11 |
12 | );
13 |
14 | export const Default = Template.bind({});
15 |
--------------------------------------------------------------------------------
/src/styles/breakpoints.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --sm: 640px;
3 | --md: 768px;
4 | --lg: 1024px;
5 | --xl: 1280px;
6 | }
--------------------------------------------------------------------------------
/src/styles/globals.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');
2 | @import url('https://fonts.googleapis.com/css2?family=Manrope:wght@200;400;600;700;800&display=swap');
3 | @import './colors.css';
4 | @import './breakpoints.css';
5 | @import './typography.css';
6 |
7 | :root {
8 | --font-family-inter: 'Inter',
9 | -apple-system,
10 | BlinkMacSystemFont,
11 | Segoe UI,
12 | Roboto,
13 | Oxygen,
14 | Ubuntu,
15 | Cantarell,
16 | Fira Sans,
17 | Droid Sans,
18 | Helvetica Neue,
19 | sans-serif;
20 | --font-family-manrope: 'Manrope', sans-serif;
21 | }
22 |
23 | html,
24 | body {
25 | padding: 0;
26 | margin: 0;
27 | font-family: var(--font-family-manrope);
28 | scroll-behavior: smooth;
29 | }
30 |
31 | body {
32 | background: var(--primary-950);
33 | }
34 |
35 | .home {
36 | background: var(--bg-gradient);
37 | }
38 |
39 | a {
40 | color: inherit;
41 | text-decoration: none;
42 | }
43 |
44 | *{
45 | box-sizing: border-box;
46 | margin: 0;
47 | }
48 |
49 |
50 | ul.contains-task-list {
51 | padding: 0;
52 | list-style: none;
53 | }
54 |
55 | div.table-wrapper {
56 | width: 85vw;
57 | overflow-x: scroll;
58 | }
59 |
60 |
61 | @media (min-width: 526px) {
62 | div.table-wrapper {
63 | width: 100%;
64 | overflow-x: hidden;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/styles/pages/BuildPage.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding-inline: 2rem;
3 | }
--------------------------------------------------------------------------------
/src/styles/pages/EcosystemMap.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | width: 100%;
3 | padding-inline: 1rem;
4 | margin-inline: auto;
5 | background: radial-gradient(57.04% 57.03% at 50% 0%, rgba(255, 130, 242, 0.2) 0%, rgba(150, 55, 166, 0.032) 100%),
6 | linear-gradient(180deg, #0E1212 0%, rgba(14, 18, 18, 0.32) 100%),
7 | linear-gradient(180deg, #0E1212 0%, rgba(14, 18, 18, 0.32) 100%),
8 | url(/images/bg.png);
9 | background-position: bottom;
10 | background-size: auto;
11 | background-repeat: no-repeat;
12 | background-blend-mode: lighten;
13 | display: flex;
14 | }
15 |
16 | .content {
17 | width: 100%;
18 | max-width: 44rem;
19 | height: 100%;
20 | margin: 6.5rem auto;
21 | }
22 | .content > * {
23 | margin-inline: auto;
24 | }
25 |
26 | .content p {
27 | width: 100%;
28 | max-width: 37.75rem;
29 | }
30 |
31 | .title {
32 | background: var(--pink-gradient-100);
33 | -webkit-background-clip: text;
34 | -webkit-text-fill-color: transparent;
35 | }
36 |
37 | .title > span{
38 | background: var(--primary-50);
39 | -webkit-background-clip: text;
40 | -webkit-text-fill-color: transparent;
41 | }
42 |
43 |
44 | @media (min-width: 768px) {
45 | .container {
46 | height: 100vh;
47 | }
48 | }
49 | @media (min-width: 1280px) {
50 | .container {
51 | background-size: contain;
52 | }
53 | }
--------------------------------------------------------------------------------
/src/styles/pages/HomePage.module.css:
--------------------------------------------------------------------------------
1 | .sectionholder {
2 | background-color: var(--neural-100-color);
3 | height: 16rem;
4 | }
5 |
6 | .container {
7 | background: var(--bg-gradient);
8 | }
--------------------------------------------------------------------------------
/src/styles/pages/LearnPage.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | margin-inline: auto;
3 | }
4 |
5 | .contents {
6 | padding: 7.5rem 0 0;
7 | }
--------------------------------------------------------------------------------