121 Commits
1.0 ... 2.0

Author SHA1 Message Date
Kamran Ahmed
68950ab2c9 Add helmet to thanks page 2021-09-09 22:57:34 +02:00
Kamran Ahmed
305d0a41ac Add thanks page 2021-09-09 22:41:57 +02:00
Kamran Ahmed
e43c21a01d Fix responsiveness issues 2021-09-09 22:32:12 +02:00
Kamran Ahmed
b6205af02c Change youtube banner color 2021-09-09 22:09:20 +02:00
Kamran Ahmed
6de2867d8a Disable carbon ads 2021-09-09 22:00:43 +02:00
Kamran Ahmed
13612323d7 Disable carbon ads 2021-09-09 21:55:53 +02:00
Kamran Ahmed
edd10470a7 Right align the carbon ad 2021-09-09 21:50:02 +02:00
Kamran Ahmed
d5b8d761d5 Update sponsor ads and carbon ad styling 2021-09-09 21:48:31 +02:00
Kamran Ahmed
9f7119694b Revert custom ad changes 2021-09-09 21:36:06 +02:00
Kamran Ahmed
8fbde17c22 Add carbon ads 2021-09-09 21:13:20 +02:00
Kamran Ahmed
e16947bd78 Enable custom ad 2021-09-09 16:38:30 +02:00
Kamran Ahmed
33af054161 Enable carbon ads 2021-09-08 23:33:08 +02:00
Kamran Ahmed
8913d5c5e4 Add youtube logo 2021-09-07 17:41:06 +02:00
Kamran Ahmed
0040d568b1 Add sponsor 2021-09-06 14:28:39 +02:00
Kamran Ahmed
706070e42a Add resources page 2021-09-05 22:52:18 +02:00
Kamran Ahmed
f79fb62ff9 Add sli, slo and sla guide 2021-09-05 22:38:37 +02:00
Kamran Ahmed
5c428540dc Add web vitals png 2021-09-05 22:24:38 +02:00
Kamran Ahmed
35ae0a74b3 Add keywords to frontend roadmap 2021-09-05 22:19:09 +02:00
Kamran Ahmed
a1606521d4 Fix missing title and description on video page 2021-09-05 22:13:49 +02:00
Kamran Ahmed
6545c8de36 Add youtube link to video page 2021-09-05 22:09:22 +02:00
Kamran Ahmed
0f713cbfd8 Add sticky banner for youtube 2021-09-05 21:58:44 +02:00
Kamran Ahmed
333894b75b Fix style for sponsor button 2021-09-05 21:19:51 +02:00
Kamran Ahmed
c73a500ffd Add back to roadmaps button 2021-09-05 21:17:51 +02:00
Kamran Ahmed
a489bc0fde Update dimmed button 2021-09-05 21:08:28 +02:00
Kamran Ahmed
09ef6bfbb0 Header logo fix 2021-09-05 20:55:22 +02:00
Kamran Ahmed
67d2f5cb57 Add support for environment variables 2021-09-05 20:38:08 +02:00
Kamran Ahmed
b28deab192 Update build workflow 2021-09-05 20:28:35 +02:00
Kamran Ahmed
290a73c8b0 Update dependencies 2021-09-05 20:25:48 +02:00
Kamran Ahmed
579d39e104 Merge branch 'master' of github.com:kamranahmedse/roadmap-next 2021-09-05 20:22:27 +02:00
Kamran Ahmed
e7c32958c9 Remove initial version 2021-09-05 20:22:21 +02:00
Kamran Ahmed
4a31a757f1 Update build scripts 2021-09-05 20:06:41 +02:00
Kamran Ahmed
c7a141c024 Add logos 2021-09-05 20:04:41 +02:00
Kamran Ahmed
b4042cb183 Add readme and contribution docs 2021-09-05 20:03:30 +02:00
Kamran Ahmed
0d937384a4 Integrate husky 2021-09-05 20:01:22 +02:00
Kamran Ahmed
3ee4d49d05 Fix linting issues 2021-09-05 20:00:07 +02:00
Kamran Ahmed
bc9e58c155 Add custom ad 2021-09-05 18:46:38 +02:00
Kamran Ahmed
0a6d72f671 Add carbon integration 2021-09-05 18:45:32 +02:00
Kamran Ahmed
dd43969dfb Integrate google analytics 2021-09-05 18:40:02 +02:00
Kamran Ahmed
0067cfdbc6 Fix broken build UI issue 2021-09-05 18:27:34 +02:00
Kamran Ahmed
6bb1505db3 Fix build errors 2021-09-05 18:25:49 +02:00
Kamran Ahmed
7e0a392a87 Update gitignore 2021-09-05 17:30:30 +02:00
Kamran Ahmed
1c94fb8b95 Refactor page components 2021-09-05 17:28:37 +02:00
Kamran Ahmed
f03472127c Refactor page components 2021-09-05 17:26:54 +02:00
Kamran Ahmed
d5b091a5ed Fix linting issues 2021-09-05 16:12:54 +02:00
Kamran Ahmed
ba1b876e14 Add sitemap generation 2021-09-05 16:07:20 +02:00
Kamran Ahmed
af8a79c48d Add helmet to pages 2021-09-04 22:58:58 +02:00
Kamran Ahmed
6ba22799cd Remove podcasts link 2021-09-04 19:40:11 +02:00
Kamran Ahmed
63b297ca34 Integrate revue subscription 2021-09-04 19:25:07 +02:00
Kamran Ahmed
929e6c0a51 Add pdf links 2021-09-04 19:14:02 +02:00
Kamran Ahmed
9b97fce431 Refactor roadmap id 2021-09-04 18:51:49 +02:00
Kamran Ahmed
ad427eceee Add roadmaps listing 2021-09-04 18:50:38 +02:00
Kamran Ahmed
1b9bb03cde Add embed video links 2021-09-03 18:01:09 +02:00
Kamran Ahmed
68fc45104d Add youtube links to videos 2021-09-03 16:54:35 +02:00
Kamran Ahmed
96e02195a9 Refactor guide links and id 2021-09-03 16:27:30 +02:00
Kamran Ahmed
e800e3b6ce Normalize filename and url in videos 2021-09-03 16:21:20 +02:00
Kamran Ahmed
ac984346e8 Rename videos 2021-09-03 15:59:03 +02:00
Kamran Ahmed
afa0e032e7 Add video listing page 2021-09-03 13:07:13 +02:00
Kamran Ahmed
86f599b5b7 Add videos listing page 2021-09-03 12:59:44 +02:00
Kamran Ahmed
75c4f952dd Menu and guides listing changes 2021-09-03 10:52:43 +02:00
Kamran Ahmed
aa8aea9433 Guides listing to have all guides 2021-09-03 10:11:33 +02:00
Kamran Ahmed
93797cc5ee Make the guides page working 2021-09-03 09:45:46 +02:00
Kamran Ahmed
fc159ecb9b Guides listing 2021-09-02 18:08:02 +02:00
Kamran Ahmed
c097017520 Homepage links update 2021-09-01 14:45:26 +02:00
Kamran Ahmed
ec1725e312 Linking from homepage to roadmaps 2021-08-29 21:31:29 +02:00
Kamran Ahmed
67b8af3f6b Add roadmap rendering 2021-08-29 21:29:14 +02:00
Kamran Ahmed
74ef38cdb6 Add video and guides on homepage 2021-08-29 18:57:23 +02:00
Kamran Ahmed
0e90d53b8e Guides listing 2021-08-29 16:36:51 +02:00
Kamran Ahmed
ec0cd4692c Guides listing on homepage 2021-08-29 16:34:00 +02:00
Kamran Ahmed
964a87f605 Render roadmaps on homepage 2021-08-29 16:05:19 +02:00
Kamran Ahmed
7c3c552ff1 Add roadmap meta generation script 2021-08-29 14:05:59 +02:00
Kamran Ahmed
810cea40b3 Responsive components 2021-08-28 15:28:20 +02:00
Kamran Ahmed
ae463059dd Make opensource and updates banner responsive 2021-08-28 14:30:33 +02:00
Kamran Ahmed
3f2ef9ae48 Responsive homepage 2021-08-28 14:02:22 +02:00
Kamran Ahmed
8d05031e51 Make roadmaps page responsive 2021-08-28 13:50:44 +02:00
Kamran Ahmed
2c0d1ef77f Make homepage responsive 2021-08-28 13:38:29 +02:00
Kamran Ahmed
3a7099bebe Responsive homepage grid 2021-08-28 13:06:30 +02:00
Kamran Ahmed
8bb36c1451 Add responsive footer 2021-08-28 13:04:40 +02:00
Kamran Ahmed
d3ee830e26 Add responsive header 2021-08-28 12:48:48 +02:00
Kamran Ahmed
8013c64eb5 Add thanks page 2021-08-26 12:39:05 +02:00
Kamran Ahmed
3bdac7c7d9 Style signup page 2021-08-24 17:59:46 +02:00
Kamran Ahmed
101aed8257 Add sign up page 2021-08-24 16:35:23 +02:00
Kamran Ahmed
d1bdcfd1da Add terms and privacy pages 2021-08-22 22:32:50 +02:00
Kamran Ahmed
0a3fd7b894 Add about page 2021-08-22 22:21:34 +02:00
Kamran Ahmed
1c065f563d Add premium block 2021-08-22 16:01:55 +02:00
Kamran Ahmed
85ac2162bc Refactor content page header 2021-08-22 15:40:20 +02:00
Kamran Ahmed
fe34e7f8d7 Add video detail page 2021-08-22 15:36:03 +02:00
Kamran Ahmed
13817a4318 Jsx to Tsx conversion 2021-08-21 16:44:49 +02:00
Kamran Ahmed
197f1f6824 Add markdown rendering 2021-08-20 17:06:26 +02:00
Kamran Ahmed
c37297f822 Add support for markdown 2021-08-20 15:55:39 +02:00
Kamran Ahmed
a85497d7e7 Add guide header and guide page 2021-08-20 15:49:34 +02:00
Kamran Ahmed
4737dd8390 Disable powered by header 2021-08-19 15:01:18 +02:00
Kamran Ahmed
309731fe1b Add roadmap image page 2021-08-19 14:36:08 +02:00
Kamran Ahmed
8902100baa Add roadmap page 2021-08-16 18:15:54 +02:00
Kamran Ahmed
bdee14009b Add link to roadmaps page 2021-08-16 00:11:26 +02:00
Kamran Ahmed
55f7d00e83 Add roadmaps listing 2021-08-16 00:10:18 +02:00
Kamran Ahmed
4459b7d062 Change header button text to subcsribe 2021-08-15 18:31:53 +02:00
Kamran Ahmed
c188fc57ae Add video listing page 2021-08-15 18:28:05 +02:00
Kamran Ahmed
ef6709d2bf Refactor page header 2021-08-15 16:07:05 +02:00
Kamran Ahmed
51a5f570fe Refactor guides page 2021-08-15 15:57:21 +02:00
Kamran Ahmed
74791e1f7d Update UI 2021-08-15 10:42:56 +02:00
Kamran Ahmed
7d27cb0b9f Add guides listing page 2021-08-15 10:39:46 +02:00
Kamran Ahmed
6dbf170076 Design tweaks 2021-08-14 22:57:40 +02:00
Kamran Ahmed
461c9a03a7 Design tweaks 2021-08-14 22:55:17 +02:00
Kamran Ahmed
41ec8327c4 Refactor list and list items 2021-08-14 22:43:25 +02:00
Kamran Ahmed
c8395aa3df Refactor roadmap component 2021-08-14 20:43:23 +02:00
Kamran Ahmed
d80327c8e8 Fix badge alignment 2021-08-14 20:32:43 +02:00
Kamran Ahmed
b4ce36fb04 Add video and guide components 2021-08-14 20:29:05 +02:00
Kamran Ahmed
c84e7d41bb Divide home in components 2021-08-14 14:01:42 +02:00
Kamran Ahmed
14c03912f9 Add footer 2021-08-14 13:19:57 +02:00
Kamran Ahmed
0028d4f80c Add opensource section 2021-08-12 17:09:15 +02:00
Kamran Ahmed
46f1034163 Add videos section 2021-08-04 00:20:16 +02:00
Kamran Ahmed
c44fc62e8a Add videos section 2021-08-03 23:07:54 +02:00
Kamran Ahmed
7a6a8c94f9 Add visual guides navigation 2021-08-02 18:15:42 +02:00
Kamran Ahmed
cadf9ba4a7 Add visual guides navigation 2021-08-02 13:22:21 +02:00
Kamran Ahmed
4428e8d82b Guides listing 2021-08-02 12:49:35 +02:00
Kamran Ahmed
b080ed64ba Add visual guides navigation 2021-08-01 21:43:43 +02:00
Kamran Ahmed
3bfd06d704 Add guides listing design 2021-08-01 21:38:34 +02:00
Kamran Ahmed
0d21be3796 Add roadmap links 2021-08-01 19:09:04 +02:00
Kamran Ahmed
f15e7a18b8 Add homepage UI 2021-08-01 18:37:32 +02:00
Kamran Ahmed
9c81d818ed Add chakra and prettier 2021-08-01 13:19:54 +02:00
Kamran Ahmed
5cf17a3e0f Initial commit from Create Next App 2021-08-01 13:08:35 +02:00
228 changed files with 18524 additions and 52665 deletions

View File

@@ -1,13 +0,0 @@
{
"presets": [
"next/babel"
],
"plugins": [
[
"styled-components",
{
"ssr": true
}
]
]
}

View File

@@ -1,23 +0,0 @@
# EditorConfig for the node-soap library - head over to editorconfig.org to see if you editor supports this file.
# this is the topmost .editorconfig file
root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.xml]
insert_final_newline = false
[*.js]
indent_style = space
indent_size = 2
[*.json]
indent_style = space
indent_size = 2
[{package.json, .travis.yml}]
indent_style = space
indent_size = 2

18
.eslintrc Normal file
View File

@@ -0,0 +1,18 @@
{
"extends": [
"next",
"next/core-web-vitals",
"prettier"
],
"rules": {
"@next/next/no-img-element": [
"off"
],
"react/display-name": [
"off"
],
"react/jsx-no-target-blank": [
"off"
]
}
}

1
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,2 @@
# These are supported funding model platforms
github: kamranahmedse

View File

@@ -14,15 +14,14 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
node-version: 14
- name: Setup Environment
run: |
npm install -g yarn
yarn install
npm install
- name: Generate meta and builld
run: |
yarn meta
yarn build
npm run meta
npm run build
- name: Deploy to GitHub Pages
run: |
git config user.email "kamranahmed.se@gmail.com"

48
.gitignore vendored
View File

@@ -1,14 +1,36 @@
_*
Thumbs.db
.DS_Store
.idea
.next
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
out
.env
build
node_modules
yarn-error.log
*.sublime-project
*.sublime-workspace
config/*.json
!config/dev.json
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.idea
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel

5
.prettierrc Normal file
View File

@@ -0,0 +1,5 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2
}

View File

@@ -1,76 +0,0 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <kamranahmed.se@gmail.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

20
LICENSE
View File

@@ -1,20 +0,0 @@
The codebase in this repository is covered under BSD 4-Clause license and
the content with CC BY-NC-ND 3.0 with additional clauses
Everything including text and images in this project are protected by the copyright laws.
You are allowed to use this material for personal use but are not allowed to use it for
any other purpose including publishing the images, the project files or the content in the
images in any form either digital, non-digital, textual, graphical or written formats.
You are allowed to share the links to the repository or the website roadmap.sh but not
the content for any sort of usage that involves the content of this repository taken out
of the repository and be shared from any other medium including but not limited to blog
posts, articles, newsletters, you must get prior consent from the understated.
Copyright © 2021 Kamran Ahmed <kamranahmed.se@gmail.com>
Please note that I am really flexible with allowing the usage of the content in this
repository. If you reach out to me with a brief detail of why and how you would like
to use this content, there is a good chance that I will allow you to use it. The reason
behind this strictness in the license is to stop the people who have been using these
roadmaps in ill manners e.g. ripping people off with suggesting random affiliate links,
redistributing these roadmaps just for the sake of monetizing the traffic.

View File

@@ -15,7 +15,7 @@
</p>
</p>
[roadmap.sh](https://roadmap.sh) is the community effort to create knowledge that is approachable for the developers.
[roadmap.sh](https://roadmap.sh) is the community effort to create knowledge that is approachable for the developers.
The website is built with Next.js, contains roadmaps which are the step by step guides for developers, guides which are the easier to understand explanations on the complex topics. Anyone can contribute to the website by suggesting changes to existing paths, adding learning resources, becoming an author by adding new guides, updating the existing guides.

View File

@@ -1,22 +0,0 @@
import { AboutHeaderWrap } from './style';
import siteConfig from "content/site";
const AboutHeader = () => (
<AboutHeaderWrap>
<div className="container container-small">
<div className="author-info">
<img className='author-img d-none d-sm-none d-md-block d-lg-block d-xl-block' src="/kamran.jpeg" />
<div className="author-msg">
<h1>Hello, I'm Kamran Ahmed.</h1>
<p>I created <span className='flow-black'>roadmap.sh</span> to help developers find their path if they are confused and help them grow in their career.</p>
<div className="author-links">
<a href={`https://twitter.com/${siteConfig.twitter}`} target="_blank">@kamranahmedse</a>
<a href="mailto:kamran@roadmap.sh">kamran@roadmap.sh</a>
</div>
</div>
</div>
</div>
</AboutHeaderWrap>
);
export default AboutHeader;

View File

@@ -1,49 +0,0 @@
import styled from 'styled-components';
export const AboutHeaderWrap = styled.div`
text-align: left;
padding: 70px 0;
margin: 0 auto;
.author-info {
display: flex;
align-items: center;
flex-direction: row;
}
h1 {
font-weight: 700;
}
p {
font-size: 20px;
color: #333;
font-weight: 400;
margin-bottom: 0;
line-height: 35px;
}
a {
font-weight: 700;
color: #000;
}
.author-img {
height: 160px;
margin-right: 25px;
margin-top: 8px;
border-radius: 9px;
}
.author-links a {
display: inline-block;
margin-right: 5px;
background: #2d2d2d;
color: white;
padding: 5px 10px;
font-weight: 500;
margin-top: 12px;
border-radius: 5px;
text-decoration: none;
}
`;

View File

@@ -1,54 +0,0 @@
import styled from 'styled-components';
export const BadgesList = styled.p`
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 0;
`;
export const PrimaryBadge = styled.span`
font-size: 11px;
font-weight: 600;
background: #2929ff;
color: white;
padding: 2px 8px;
border-radius: 4px;
text-transform: uppercase;
display: inline-flex;
align-items: center;
justify-content: center;
margin-left: 10px;
svg {
height: 11px !important;
margin-right: 5px;
&.fa-clock {
height: 10px !important;
}
}
`;
export const SecondaryBadge = styled(PrimaryBadge)`
background: #696969;
color: white;
`;
export const InfoBadge = styled(PrimaryBadge)`
background: #039640;
color: white;
`;
export const DarkBadge = styled(PrimaryBadge)`
background: #101010;
color: white;
`;
export const BadgeLink = styled.a`
text-decoration: none;
&:hover {
text-decoration:none;
}
`;

View File

@@ -0,0 +1,60 @@
import { Box, Container, Flex, Heading, Image, Link, Text } from '@chakra-ui/react';
import React from 'react';
type ContentPageHeaderProps = {
formattedDate: string;
title: string;
subtitle: string;
author?: {
name: string;
twitter: string;
picture: string;
},
subLink?: {
text: string;
url: string;
}
};
export function ContentPageHeader(props: ContentPageHeaderProps) {
const { title, subtitle, author = null, formattedDate, subLink = null } = props;
return (
<Box pt={['35px', '35px', '70px']} pb={['35px', '35px', '55px']} borderBottomWidth={1} mb='30px'>
<Container maxW='container.md' position='relative' textAlign={['left', 'left', 'center']}>
<Flex alignItems='center' justifyContent={['flex-start', 'flex-start', 'center']}
fontSize={['12px', '12px', '14px']}>
{author?.name && (
<>
<Link
d={['none', 'flex', 'flex']}
target='_blank'
href={`https://twitter.com/${author.twitter}`}
alignItems='center'
fontWeight={600}
color='gray.500'
>
<Image alt={''} rounded={'full'} mr='7px' w='22px' src={author.picture} />
{author.name}
</Link>
<Text d={['none', 'inline', 'inline']} mx='7px' color='gray.500' as='span'>&middot;</Text>
</>
)}
<Text color='gray.500' as='span'>{formattedDate}</Text>
{subLink?.text && (
<>
<Text d={['none', 'none', 'inline']} mx='7px' color='gray.500' as='span'>&middot;</Text>
<Link d={['none', 'none', 'inline']} color='blue.500' fontWeight={500}
href={subLink.url} target={'_blank'}>{subLink.text}</Link>
</>
)}
</Flex>
<Heading as='h1' color='black' fontSize={['30px', '30px', '45px']} lineHeight={['40px', '40px', '53px']}
fontWeight={700} my={['5px', '5px', '10px']}>{title}</Heading>
<Text fontSize={['14px', '14px', '16px']} color='gray.700'>{subtitle}</Text>
</Container>
</Box>
);
}

37
components/custom-ad.tsx Normal file
View File

@@ -0,0 +1,37 @@
export const CustomAd = () => {
return (
<div id='carbonads'>
<span>
<span className='carbon-wrap'>
<a
href='https://freemote.com/strategy?sl=roadmap'
className='carbon-img'
target='_blank'
>
<img
src='/fm-img.png'
alt='FM Logo'
height='100'
width='130'
style={{ maxWidth: '130px', border: 'none' }}
/>
</a>
<a
href='https://freemote.com/strategy?sl=roadmap'
className='carbon-text'
target='_blank'
>
He Went from ZERO TO $74,000 as a Full Time Developer in 7 Weeks
</a>
</span>
<a
href='https://github.com/sponsors/kamranahmedse'
className='carbon-poweredby'
target='_blank'
>
Sponsored by
</a>
</span>
</div>
);
};

View File

@@ -1,133 +0,0 @@
import { useState } from 'react';
import classNames from 'classnames';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFacebookSquare, faTwitterSquare, faRedditSquare, faGithubSquare } from '@fortawesome/free-brands-svg-icons'
import {
PageHeader,
RoadmapMeta,
ShareRoadmap,
Sidebar,
Summary,
SummaryContainer,
MobileNavHeader,
SidebarButton,
MobileSidebar,
MobileSidebarWrap,
DesktopSidebarWrap,
PageTitle,
PageDetail
} from './style';
import { faBars } from '@fortawesome/free-solid-svg-icons'
import { getFacebookShareUrl } from 'lib/url';
import { ShareIcon } from 'components/share-icon';
import { getRedditShareUrl, getTwitterShareUrl } from 'lib/url';
import siteConfig from "content/site";
import MdRenderer from 'components/md-renderer';
const DetailedRoadmap = ({ roadmap }) => {
const [menuActive, setMenuState] = useState(false);
const {
sidebar = {},
page: currentPage = {},
author = {}
} = roadmap;
const roadmapPages = Object.keys(sidebar || {}).map((groupTitle, groupCounter) => {
if (groupTitle.startsWith('_')) {
return;
}
// @todo remove it after completing the frontend roadmap
const isInProgress = groupCounter !== 0;
return (
<div className={`links-group ${isInProgress ? 'in-progress' : ''}`} key={groupTitle}>
<h3>
{ groupTitle }
{ isInProgress && <span className='badge badge-warning progress-badge'>In Progress</span> }
</h3>
<ul>
{ sidebar[groupTitle].map(page => {
const isActivePage = page.url === currentPage.url;
// e.g. /frontend should mark `/frontend/landscape` as active
const isSummaryPage = page.url === `${currentPage.url}/summary`;
return (
<li className={classNames({ active: isActivePage || isSummaryPage })} key={page.url}>
<a href={ page.url }>
<span className="bullet"></span>
{ page.title }
</a>
</li>
);
}) }
</ul>
</div>
);
});
const filePath = currentPage.path.replace(/^\//, '');
const RoadmapContent = require(`../../content/${filePath}`).default;
return (
<SummaryContainer>
<PageHeader className="border-top border-bottom text-center text-md-left">
<div className="container d-flex align-items-center flex-column flex-md-row">
<RoadmapMeta>
<h3>{ roadmap.title }</h3>
<p>
Roadmap contributed by <a href={ author.url } target="_blank">{ author.name }</a>
{ roadmap.contributorsCount > 1 && ` and <a href="${roadmap.contributorsUrl}">${roadmap.contributorsCount} others</a>`}
</p>
</RoadmapMeta>
<ShareRoadmap className="mt-2 mt-md-0">
<ShareIcon href={ siteConfig.url.repo } target="_blank">
<FontAwesomeIcon icon={ faGithubSquare } />
</ShareIcon>
<ShareIcon href={ getFacebookShareUrl({ text: roadmap.title, url: roadmap.url }) } target="_blank">
<FontAwesomeIcon icon={ faFacebookSquare } />
</ShareIcon>
<ShareIcon href={ getTwitterShareUrl({ text: roadmap.title, url: roadmap.url }) } target="_blank">
<FontAwesomeIcon icon={ faTwitterSquare } />
</ShareIcon>
<ShareIcon href={ getRedditShareUrl({ text: roadmap.title, url: roadmap.url }) } target="_blank">
<FontAwesomeIcon icon={ faRedditSquare } />
</ShareIcon>
</ShareRoadmap>
</div>
</PageHeader>
<MobileNavHeader className="border-bottom d-block d-md-none">
<div className="container">
<SidebarButton onClick={() => setMenuState((prevMenuActive) => !prevMenuActive)}>
<FontAwesomeIcon icon={ faBars } />
{ currentPage.title }
</SidebarButton>
</div>
<MobileSidebarWrap className={classNames({ visible: menuActive })}>
<div className="container">
<MobileSidebar>
{ roadmapPages }
</MobileSidebar>
</div>
</MobileSidebarWrap>
</MobileNavHeader>
<Summary className="container">
<DesktopSidebarWrap className="d-none d-md-block">
<Sidebar>
{ roadmapPages }
</Sidebar>
</DesktopSidebarWrap>
<PageDetail>
<PageTitle>{ currentPage.title }</PageTitle>
<MdRenderer>
<RoadmapContent />
</MdRenderer>
</PageDetail>
</Summary>
</SummaryContainer>
)
};
export default DetailedRoadmap;

View File

@@ -1,199 +0,0 @@
import styled from 'styled-components';
export const SummaryContainer = styled.div``;
export const Summary = styled.div`
text-align: left;
min-height: 400px;
display: flex;
.container {
position: relative;
}
`;
export const PageHeader = styled.div`
padding-top: 20px;
padding-bottom: 20px;
h3 {
margin-bottom: 4px;
font-weight: 600;
font-size: 21px;
}
p {
margin-bottom: 0;
font-size: 14px;
color: #696969;
a {
color: #101010;
}
}
`;
export const RoadmapMeta = styled.div`
flex: 1;
`;
export const ShareRoadmap = styled.div`
display: flex;
align-items: center;
a {
margin-bottom: 0;
& + a {
margin-left: 5px;
}
}
`;
export const MobileNavHeader = styled.div`
padding: 10px 0;
display: flex;
align-items: center;
position: relative;
`;
export const SidebarButton = styled.button`
background: transparent;
border: none !important;
box-shadow: none !important;
outline: none !important;
-webkit-appearance: none;
display: flex;
align-items: center;
svg {
margin-right: 10px;
}
`;
export const PageDetail = styled.div`
padding: 25px 0 100px;
`;
export const PageTitle = styled.h1`
font-size: 40px;
font-weight: 700;
`;
export const Sidebar = styled.div`
padding-bottom: 150px;
ul {
list-style: none;
margin: 0;
padding: 0;
}
.bullet {
display: inline-block;
margin-right: 7px;
width: 7px;
height: 7px;
min-width: 7px;
border-radius: 100%;
background: #efefef;
transform: translateX(-4px);
transition: background 0.5s ease;
}
.links-group {
padding: 30px 0 10px;
width: 260px;
h3 {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
padding-left: 20px;
}
}
.progress-badge {
position: relative;
top: -2px;
margin-left: 5px;
}
.links-group.in-progress {
opacity: 0.3;
}
.links-group li {
list-style: none;
margin: 7px 0;
a {
font-size: 14px;
font-weight: normal;
color: #696969;
}
.bullet {
display: inline-block;
margin-right: 12px;
width: 7px;
height: 7px;
min-width: 7px;
border-radius: 100%;
background: #efefef;
transform: translateX(-4px);
transition: background 0.5s ease;
}
&.active a {
color: #101010;
}
&.active .bullet {
background: #101010;
}
}
`;
export const DesktopSidebarWrap = styled.div`
border-left: 1px solid #efefef;
${Sidebar} {
position: sticky;
top: 0;
background: white;
}
`;
export const MobileSidebarWrap = styled.div`
position: absolute;
z-index: 999;
background: white;
width: 100%;
left: 0;
bottom: 100%;
overflow-y: scroll;
box-shadow: rgba(0, 0, 0, 0.1) 0 10px 20px;
top: calc(100% + 1px);
transition: bottom 0.5s ease 0s;
&.visible {
bottom: -50vh;
}
`;
export const MobileSidebar = styled(Sidebar)`
border-left: 1px solid #efefef;
margin-left: 12px;
padding-bottom: 20px;
.links-group {
width: auto;
}
.progress-badge {
position: relative;
top: -2px;
margin-left: 5px;
}
`;

View File

@@ -0,0 +1,46 @@
import { Box, Link, Text } from '@chakra-ui/react';
type DimmedMoreProps = {
text: string;
href: string;
};
export function DimmedMore(props: DimmedMoreProps) {
const { text, href } = props;
return (
<Box position='relative' textAlign='center' bottom='20px'>
<Box
opacity={1}
pointerEvents='none'
position='absolute'
bottom={0}
height='200px'
width='100%'
background='linear-gradient(180deg, rgb(255 255 255 / 40%), white)'
/>
<Link
rounded='20px'
display='inline'
bg='green.600'
color='white'
p='7px 20px'
href={href}
fontWeight={800}
fontSize='11px'
textTransform='uppercase'
my='25px'
position='relative'
_hover={{
textDecoration: 'none',
'& .forward-arrow': {
transform: 'translateX(3px)'
}
}}>
{text}
<Text d='inline-block' as='span' transition='200ms' ml='4px' className='forward-arrow'>&rarr;</Text>
</Link>
</Box>
);
}

View File

@@ -1,16 +0,0 @@
import { FaqContainer } from './style';
import MdRenderer from 'components/md-renderer';
const AboutPage = require(`../../content/pages/about.md`).default;
const FaqList = () => (
<FaqContainer className='border-top bg-light'>
<div className="container container-small">
<MdRenderer>
<AboutPage />
</MdRenderer>
</div>
</FaqContainer>
);
export default FaqList;

View File

@@ -1,25 +0,0 @@
import styled from 'styled-components';
export const FaqContainer = styled.div`
padding: 40px 0;
h4 {
margin-top: 30px;
font-weight: 700;
}
h4:first-child {
margin-top: 0;
}
p {
color: #333;
}
p:last-child {
margin-bottom: 0;
}
.container {
}
`;

View File

@@ -1,25 +0,0 @@
import Link from 'next/link';
import { FeaturedContentWrap } from './style';
import FeaturedGuide from 'components/featured-guide';
import { getFeaturedGuides } from 'lib/guide';
const FeaturedGuides = () => (
<FeaturedContentWrap className="featured-content-wrap">
<div className="container">
<p className='border-through featured-separator'>
<span>
Guides mostly visited by the community&nbsp;
<a href="/guides" className="dark-link d-none d-sm-none d-md-inline d-xl-inline">View all Guides &rarr;</a>
</span>
</p>
<div className="swim-lane row">
{ getFeaturedGuides()
.map(guide => (
<FeaturedGuide guide={ guide } key={ guide.url } />
)) }
</div>
</div>
</FeaturedContentWrap>
);
export default FeaturedGuides;

View File

@@ -1,16 +0,0 @@
import { FeaturedWrap } from './style';
import FeaturedGuides from './guides';
import FeaturedRoadmaps from './roadmaps';
const FeaturedContent = (props) => (
<FeaturedWrap className="border-top bg-light">
<FeaturedRoadmaps />
<FeaturedGuides />
</FeaturedWrap>
);
FeaturedContent.defaultProps = {
className: '',
};
export default FeaturedContent;

View File

@@ -1,27 +0,0 @@
import { FeaturedContentWrap } from './style';
import roadmaps from 'content/roadmaps';
import FeaturedRoadmap from 'components/featured-roadmap';
const FeaturedRoadmaps = () => (
<FeaturedContentWrap className="featured-content-wrap">
<div className="container">
<div className="featured-head">
<p className="border-through featured-separator">
<span>
Roadmaps mostly visited by the community&nbsp;
<a href='/roadmaps' className="dark-link d-none d-sm-none d-md-inline d-xl-inline">View all Roadmaps &rarr;</a>
</span>
</p>
</div>
<div className="swim-lane row">
{ roadmaps
.filter(({ featured }) => featured)
.map(roadmap => (
<FeaturedRoadmap roadmap={ roadmap } key={ roadmap.url } />
)) }
</div>
</div>
</FeaturedContentWrap>
);
export default FeaturedRoadmaps;

View File

@@ -1,110 +0,0 @@
import styled from 'styled-components';
export const FeaturedWrap = styled.div`
padding: 30px 0 50px;
`;
export const FeaturedContentWrap = styled.div`
.featured-head {
text-align: center;
display: block;
h3 {
font-weight: 700;
font-size: 35px;
margin-bottom: 30px;
}
}
.featured-separator {
font-size: 16px;
margin-bottom: 40px;
margin-top: 25px;
color: #999;
span {
background: #f8f9fa;
}
a {
background: black;
color: white;
padding: 3px 8px;
border-radius: 6px;
font-size: 14px;
text-decoration: none;
margin-left: 5px;
}
}
.swim-lane {
.featured-block {
}
}
.guide-item {
border-bottom: 1px solid #dee2e6;
padding: 15px 10px;
p {
margin-bottom: 0;
font-size: 15px;
color: #999;
}
&:last-child {
border-bottom: none;
}
}
.journey-block {
a {
border: 1px solid #f7f7f7;
display: block;
text-decoration: none;
color: #000000;
background: #ffffff;
box-shadow: rgba(0, 0, 0, 0.12) 0 5px 10px;
transition: box-shadow 0.2s ease 0s;
cursor: pointer;
margin-bottom: 32px;
border-radius: 0 0 10px 10px;
&:hover {
box-shadow: rgba(0, 0, 0, 0.12) 0 30px 60px;
img {
filter: grayscale(0);
}
}
}
.journey-meta {
padding: 18px 25px 20px;
h4 {
line-height: 27px;
font-weight: 600;
margin-bottom: 0;
font-size: 18px;
white-space: nowrap;
text-overflow: ellipsis;
flex: 0 0 auto;
overflow: hidden;
}
p {
font-size: 15px;
line-height: 25px;
color: #999999;
margin-bottom: 0;
}
}
img {
width: 100%;
filter: grayscale(1);
border-radius: 10px 10px 0 0;
}
}
`;

View File

@@ -1,25 +0,0 @@
import formatDate from 'date-fns/format';
import { Author, AuthorImage, AuthorName, BlockLink, BlockMeta, BlockSubtitle, BlockTitle, PublishDate } from './style';
import { findByUsername } from 'lib/author';
const FeaturedGuide = ({ guide }) => {
const author = findByUsername(guide.author) || {};
return (
<div className='col-xl-4 col-lg-6 col-md-6 col-sm-12 col-12 grid-item-container'>
<BlockLink href={guide.url}>
<BlockTitle>{guide.title}</BlockTitle>
<BlockSubtitle>{guide.featuredDescription || guide.description}</BlockSubtitle>
<BlockMeta>
<Author>
<AuthorImage src={author.picture} />
<AuthorName>{author.name}</AuthorName>
</Author>
<PublishDate>{formatDate(new Date(guide.createdAt), 'MMMM d, yyyy')}</PublishDate>
</BlockMeta>
</BlockLink>
</div>
);
};
export default FeaturedGuide;

View File

@@ -1,65 +0,0 @@
import styled from 'styled-components';
export const BlockLink = styled.a`
border: 1px solid #f7f7f7;
display: block;
text-decoration: none;
color: #000000;
background: #ffffff;
padding: 25px 25px 22px;
border-radius: 10px;
box-shadow: rgba(0, 0, 0, 0.12) 0 5px 10px;
transition: box-shadow 0.2s ease 0s;
cursor: pointer;
margin-bottom: 32px;
&:hover {
text-decoration: none;
color: #000000;
box-shadow: rgba(0, 0, 0, 0.12) 0 30px 60px;
}
`;
export const BlockTitle = styled.h4`
line-height: 27px;
font-weight: 600;
font-size: 22px;
white-space: nowrap;
text-overflow: ellipsis;
flex: 0 0 auto;
overflow: hidden;
`;
export const BlockSubtitle = styled.p`
font-size: 15px;
line-height: 25px;
color: #999999;
margin-bottom: 0;
`;
export const BlockMeta = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 19px;
`;
export const PublishDate = styled.time`
font-size: 13px;
color: #999;
`;
export const Author = styled.div`
display: flex;
align-items: center;
font-size: 15px;
color: #999;
`;
export const AuthorImage = styled.img`
height: 22px;
width: 22px;
border-radius: 100%;
margin-right: 10px;
`;
export const AuthorName = styled.div``;

View File

@@ -1,12 +0,0 @@
import { BlockLink, BlockSubtitle, BlockTitle } from './style';
const FeaturedRoadmap = ({ roadmap }) => (
<div className='col-xl-4 col-lg-4 col-md-6 col-sm-12 col-12 grid-item-container'>
<BlockLink href={roadmap.url}>
<BlockTitle>{roadmap.title}</BlockTitle>
<BlockSubtitle>{roadmap.featuredDescription || roadmap.description}</BlockSubtitle>
</BlockLink>
</div>
);
export default FeaturedRoadmap;

View File

@@ -1,38 +0,0 @@
import styled from 'styled-components';
export const BlockLink = styled.a`
border: 1px solid #f7f7f7;
display: block;
text-decoration: none;
color: #000000;
background: #ffffff;
padding: 26px 25px 25px;
border-radius: 10px;
box-shadow: rgba(0, 0, 0, 0.12) 0 5px 10px;
transition: box-shadow 0.2s ease 0s;
cursor: pointer;
margin-bottom: 32px;
&:hover {
text-decoration: none;
color: #000000;
box-shadow: rgba(0, 0, 0, 0.12) 0 30px 60px;
}
`;
export const BlockTitle = styled.h4`
line-height: 27px;
font-weight: 600;
font-size: 22px;
white-space: nowrap;
text-overflow: ellipsis;
flex: 0 0 auto;
overflow: hidden;
`;
export const BlockSubtitle = styled.p`
font-size: 15px;
line-height: 25px;
color: #999999;
margin-bottom: 0;
`;

79
components/footer.tsx Normal file
View File

@@ -0,0 +1,79 @@
import { Box, Container, Flex, Image, Link, Stack, Text } from '@chakra-ui/react';
import siteConfig from '../content/site.json';
import { CustomAd } from './custom-ad';
function NavigationLinks() {
return (
<>
<Stack isInline d={['none', 'none', 'flex']} color='gray.400' fontWeight={600} spacing='30px'>
<Link _hover={{ color: 'white' }} href='/roadmaps'>Roadmaps</Link>
<Link _hover={{ color: 'white' }} href='/guides'>Guides</Link>
<Link _hover={{ color: 'white' }} href='/watch'>Videos</Link>
<Link _hover={{ color: 'white' }} href='/about'>About</Link>
<Link _hover={{ color: 'white' }} href={siteConfig.url.youtube} target='_blank'>YouTube</Link>
</Stack>
<Stack d={['flex', 'flex', 'none']} color='gray.400' fontWeight={600} spacing={0}>
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
href='/roadmaps'>Roadmaps</Link>
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
href='/guides'>Guides</Link>
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
href='/watch'>Videos</Link>
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
href='/thanks'>Thanks</Link>
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
href='/about'>About</Link>
<Link py='7px' _hover={{ color: 'white' }} target='_blank'
href={siteConfig.url.youtube}>YouTube</Link>
</Stack>
</>
);
}
export function Footer() {
return (
<Box bg='gray.900' p={['25px 0', '25px 0', '40px 0']}>
<Container maxW='container.md'>
<NavigationLinks />
<Box mt={['40px', '40px', '50px']} mb='40px' maxW='500px'>
<Flex spacing={0} alignItems='center' color='gray.400'>
<Link d='flex' alignItems='center' fontWeight={600} _hover={{ textDecoration: 'none', color: 'white' }}
href='/'>
<Image alt='' h='25px' w='25px' src='/logo.svg' mr='6px' />
roadmap.sh
</Link>
<Text as='span' mx='7px'>by</Text>
<Link bg='blue.500' px='6px' py='2px' rounded='4px' color='white' fontWeight={600} fontSize='13px'
_hover={{ textDecoration: 'none', bg: 'blue.600' }} href={siteConfig.url.twitter}
target='_blank'>@kamranahmedse</Link>
</Flex>
<Text my='15px' fontSize='14px' color='gray.500'>Community created roadmaps, articles, resources and
journeys to help you choose your path and grow in your career.</Text>
<Text fontSize='14px' color='gray.500'>
<Text as='span' mr='10px'>&copy; roadmap.sh</Text>&middot;
<Link href='/about' _hover={{ textDecoration: 'none', color: 'white' }} color='gray.400'
mx='10px'>FAQs</Link>&middot;
<Link href='/terms' _hover={{ textDecoration: 'none', color: 'white' }} color='gray.400'
mx='10px'>Terms</Link>&middot;
<Link href='/privacy' _hover={{ textDecoration: 'none', color: 'white' }} color='gray.400'
mx='10px'>Privacy</Link>
</Text>
</Box>
</Container>
<CustomAd />
{process.env.GA_SECRET && false && (
<script
async
type='text/javascript'
src='//cdn.carbonads.com/carbon.js?serve=CE7DLK3Y&placement=roadmapsh'
id='_carbonads_js'
/>
)}
</Box>
);
}

View File

@@ -0,0 +1,113 @@
import { useState } from 'react';
import { HamburgerIcon } from '@chakra-ui/icons';
import { Box, CloseButton, Container, Flex, IconButton, Image, Link, Stack, Text } from '@chakra-ui/react';
import RoadmapLogo from '../components/icons/roadmap.svg';
import siteConfig from '../content/site.json';
type MenuLinkProps = {
text: string;
link: string;
};
function MenuLink(props: MenuLinkProps) {
const { text, link } = props;
return <Link
borderBottomWidth={0}
borderBottomColor='gray.500'
_hover={{ textDecoration: 'none', borderBottomColor: 'white' }}
fontWeight={500}
href={link}
>
{text}
</Link>;
}
function DesktopMenuLinks() {
return (
<Stack d={['none', 'flex', 'flex']} shouldWrapChildren isInline spacing='15px' alignItems='center' color='gray.50'
fontSize='15px'>
<MenuLink text={'Roadmaps'} link={'/roadmaps'} />
<MenuLink text={'Guides'} link={'/guides'} />
<MenuLink text={'Videos'} link={'/watch'} />
<MenuLink text={'Thanks'} link={'/thanks'} />
<Link ml='10px' bgGradient='linear(to-l, yellow.700, red.600)' p='7px 10px' rounded='4px'
_hover={{ textDecoration: 'none', bgGradient: 'linear(to-l, red.800, yellow.700)' }}
fontWeight={500} href={'/signup'}>Subscribe</Link>
</Stack>
);
}
function MobileMenuLinks() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<IconButton
rounded='5px'
padding={0}
aria-label={'Menu'}
d={['block', 'none', 'none']}
icon={<HamburgerIcon color='white' w='25px' height='25px' />}
color='white'
cursor='pointer'
h='auto'
bg='transparent'
_hover={{ bg: 'transparent' }}
_active={{ bg: 'transparent' }}
_focus={{ bg: 'transparent' }}
onClick={() => setIsOpen(true)}
/>
{isOpen && (
<Stack color='gray.100'
fontSize={['22px', '22px', '22px', '32px']}
alignItems='center'
justifyContent='center'
pos='fixed'
left={0}
right={0}
bottom={0}
top={0}
bg='gray.900'
spacing='12px'
zIndex={1}
>
<Link href='/roadmaps'>Roadmaps</Link>
<Link href='/guides'>Guides</Link>
<Link href='/watch'>Videos</Link>
<Link href='/thanks'>Thanks</Link>
<Link href='/signup'>Subscribe</Link>
<CloseButton onClick={() => setIsOpen(false)} pos='fixed' top='40px' right='15px' size='lg' />
</Stack>
)}
</>
);
}
export function GlobalHeader() {
return (
<Box bg='gray.900' p='58px 0 20px'>
<Container maxW='container.md'>
<Flex justifyContent='space-between' alignItems='center'>
<Box>
<Link w='100%'
d='flex'
href='/'
alignItems='center'
color='white'
fontWeight={600}
_hover={{ textDecoration: 'none' }}
fontSize='18px'>
<RoadmapLogo style={{ height: '30px', width: '30px', marginRight: '10px' }} />
<Text d={['block', 'none', 'block']} as='span'>roadmap.sh</Text>
</Link>
</Box>
<DesktopMenuLinks />
<MobileMenuLinks />
</Flex>
</Container>
</Box>
);
}

View File

@@ -1,23 +0,0 @@
import MdRenderer from 'components/md-renderer'
import SharePage from 'components/share-page';
import { GuideBodyWrap } from './style';
const GuideBody = ({ guide }) => {
const GuideContent = require(`../../content/guides/${guide.fileName}.md`).default;
return (
<GuideBodyWrap>
<MdRenderer>
<GuideContent />
{
guide.author && <SharePage
title={ guide.title }
url={ guide.url }
twitterUsername={ guide.author.twitter }
/>
}
</MdRenderer>
</GuideBodyWrap>
);
};
export default GuideBody;

View File

@@ -1,13 +0,0 @@
import styled from 'styled-components';
export const GuideBodyWrap = styled.div`
position: relative;
max-width: 750px;
margin: 0 auto;
padding: 0 20px;
min-height: 300px;
p:first-child, h1, h2, h3, h4, h5, h6, img, blockquote {
margin-top: 0;
}
`;

View File

@@ -1,83 +0,0 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFacebookSquare, faGithub, faHackerNewsSquare, faRedditSquare, faTwitter, faTwitterSquare } from '@fortawesome/free-brands-svg-icons'
import { getContributionUrl } from "lib/guide";
import {
getTwitterUrl,
getTwitterShareUrl,
getFacebookShareUrl,
getRedditShareUrl,
getHnShareUrl
} from "lib/url";
import {
AuthorBio,
AuthorImg,
AuthorInfoWrap,
AuthorMeta,
ContributeIcon,
FooterBg,
FooterContainer,
FooterWrap,
ShareIcons,
ShareWrap
} from './style';
const GuideFooter = ({
guide,
guide: {
author = {}
} = {}
}) => (
<FooterWrap>
<FooterBg className="border-top">
<FooterContainer>
<ShareWrap>
<ContributeIcon>
<a href={ getContributionUrl(guide) } target="_blank">
<span className="d-none d-sm-none d-md-inline d-lg-inline d-xl-inline">Improve this Guide </span>
<span className="d-inline d-sm-inline d-md-none d-lg-none d-xl-none">Contribute </span>
<FontAwesomeIcon icon={faGithub}/>
</a>
</ContributeIcon>
<ContributeIcon hasMargins>
<a href={ getTwitterUrl(author.twitter) } target="_blank">
<span className="d-none d-sm-none d-md-inline d-lg-inline d-xl-inline">Follow the author </span>
<span className="d-inline d-sm-inline d-md-none d-lg-none d-xl-none">Author </span>
<FontAwesomeIcon icon={faTwitter}/>
</a>
</ContributeIcon>
<ShareIcons>
<span className="d-none d-sm-none d-md-none d-lg-inline d-xl-inline">Help spread the word</span>
<span className="d-inline d-sm-inline d-md-inline d-lg-none d-xl-none">Share</span>
<a href={ getTwitterShareUrl({ text: `${guide.title} by @${author.twitter}`, url: guide.url })} target="_blank">
<FontAwesomeIcon icon={faTwitterSquare}/>
</a>
<a href={ getFacebookShareUrl({ text: guide.title, url: guide.url }) } target="_blank">
<FontAwesomeIcon icon={faFacebookSquare}/>
</a>
<a href={ getRedditShareUrl({ text: guide.title, url: guide.url })} target="_blank">
<FontAwesomeIcon icon={faRedditSquare}/>
</a>
<a href={ getHnShareUrl({ text: guide.title, url: guide.url })} target="_blank">
<FontAwesomeIcon icon={faHackerNewsSquare}/>
</a>
</ShareIcons>
</ShareWrap>
</FooterContainer>
</FooterBg>
<FooterBg className="border-top">
<FooterContainer>
<AuthorInfoWrap>
<AuthorImg src={ author.picture } alt={ author.name }/>
<AuthorMeta>
<h4><a href={ getTwitterUrl(author.twitter) } target="_blank">{ author.name }</a></h4>
<AuthorBio>{ author.bio }</AuthorBio>
</AuthorMeta>
</AuthorInfoWrap>
</FooterContainer>
</FooterBg>
</FooterWrap>
);
export default GuideFooter;

View File

@@ -1,123 +0,0 @@
import styled from 'styled-components';
export const FooterWrap = styled.div`
display: block;
margin-top: 50px;
`;
export const FooterContainer = styled.div`
max-width: 750px;
margin: 0 auto;
padding: 0 20px;
`;
export const FooterBg = styled.div`
`;
export const ShareWrap = styled.div`
padding: 17px 0px;
align-items: center;
justify-content: center;
display: flex;
a {
display: flex;
align-items: center;
color: #101010;
svg {
height: 18px !important;
width: 18px !important;
color: #757575;
margin-left: 7px;
transition: all 0.2s ease;
}
&:hover {
svg {
color: #101010;
}
}
}
`;
export const ContributeIcon = styled.div`
display: flex;
align-items: center;
justify-content: center;
margin: ${props => props.hasMargins ? '0 30px' : '0'};
span {
margin-right: 4px;
}
a {
color: #757575;
font-size: 14px;
transition: all 0.2s ease;
&:hover {
color: #101010;
}
}
`;
export const ShareIcons = styled.div`
display: flex;
align-items: center;
color: #757575;
font-size: 14px;
span {
margin-right: 4px;
}
`;
export const AuthorInfoWrap = styled.div`
display: flex;
align-items: center;
padding: 40px 0;
h4 {
position: relative;
font-size: 22px;
margin: 10px 0;
line-height: 17px;
a {
font-weight: 700;
color: #101010;
}
}
.author-description {
margin-bottom: 14px;
}
`;
export const AuthorBio = styled.p`
font-size: 15px;
line-height: 24px;
color: #757575;
margin-bottom: 0;
a {
font-weight: 500;
}
`;
export const AuthorImg = styled.img`
border-radius: 100%;
height: 100px !important;
width: 100px !important;
margin-right: 22px;
display: block;
`;
export const AuthorMeta = styled.div`
display: flex;
justify-content: center;
flex-direction: column;
flex: 1;
`;

View File

@@ -1,57 +0,0 @@
import formatDate from 'date-fns/format'
import {
ActionItems,
AuthorImage,
EditGuide,
GuideAuthor,
GuideDate,
GuideMeta,
GuideSubtitle,
GuideTitle,
HeaderWrap,
} from './style';
import { getContributionUrl } from "lib/guide";
import { getTwitterUrl } from "lib/url";
import { faClock, faEnvelope, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { BadgeLink, BadgesList, PrimaryBadge, SecondaryBadge, DarkBadge } from 'components/badges';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const GuideHeader = ({
guide,
guide: {
author = {}
} = {}
}) => (
<HeaderWrap className="border-bottom">
<GuideMeta>
<GuideAuthor href={ getTwitterUrl(author.twitter) } target="_blank">
<AuthorImage src={ author.picture } />
{ author.name }
</GuideAuthor>
&middot;
<GuideDate>{ formatDate(new Date(guide.createdAt), 'EEEE, MMMM d yyyy') }</GuideDate>
&middot;
<EditGuide href={ getContributionUrl(guide) } target="_blank">Improve this Guide</EditGuide>
</GuideMeta>
<GuideTitle>{ guide.title }</GuideTitle>
<GuideSubtitle>{ guide.description }</GuideSubtitle>
<ActionItems>
<BadgesList className="mt-4">
<BadgeLink href="/guides">
<SecondaryBadge>
<FontAwesomeIcon icon={faArrowLeft}/>
Other Guides
</SecondaryBadge>
</BadgeLink>
<BadgeLink href="/signup">
<PrimaryBadge>
<FontAwesomeIcon icon={faEnvelope}/>
Send me Updates
</PrimaryBadge>
</BadgeLink>
</BadgesList>
</ActionItems>
</HeaderWrap>
);
export default GuideHeader;

View File

@@ -1,54 +0,0 @@
import styled from 'styled-components';
export const HeaderWrap = styled.div`
padding: 80px 15px 45px;
text-align: center;
margin-bottom: 35px;
`;
export const GuideTitle = styled.h1`
font-weight: 700;
font-size: 46px;
margin: 12px 0;
`;
export const GuideSubtitle = styled.p`
margin-bottom: 0;
font-size: 16px;
color: #444;
`;
export const GuideMeta = styled.p`
margin-bottom: 0;
color: #757575;
font-size: 13px;
font-weight: 400;
`;
export const GuideDate = styled.span`
margin-left: 7px;
margin-right: 7px;
`;
export const GuideAuthor = styled.a`
margin-right: 7px;
font-weight: 500;
color: #101010;
&:hover {
color: #101010;
}
`;
export const AuthorImage = styled.img`
width: 22px;
height: 22px;
border-radius: 100%;
margin-right: 10px;
`;
export const EditGuide = styled.a`
margin-left: 7px;
`;
export const ActionItems = styled.div``;

View File

@@ -0,0 +1,31 @@
import { Badge, Box, Heading, Link, Text } from '@chakra-ui/react';
type GuideGridItemProps = {
title: string;
href: string;
subtitle: string;
date: string;
isNew?: boolean;
colorIndex?: number;
};
const bgColorList = [
'gray.700',
'purple.800'
];
export function GuideGridItem(props: GuideGridItemProps) {
const { title, subtitle, date, isNew = false, colorIndex = 0, href } = props;
return (
<Box _hover={{ textDecoration: 'none', transform: 'scale(1.02)' }} as={Link} href={href} shadow='xl' p='20px'
rounded='10px' bg={bgColorList[colorIndex] ?? bgColorList[0]} flex={1}>
<Text mb='10px' fontSize='13px' color='gray.400'>
{isNew && <Badge colorScheme={'yellow'} mr='10px'>New</Badge>}
{date}
</Text>
<Heading color='white' mb={'6px'} fontSize='20px'>{title}</Heading>
<Text color='gray.300' fontSize='14px'>{subtitle}</Text>
</Box>
);
}

73
components/helmet.tsx Normal file
View File

@@ -0,0 +1,73 @@
import NextHead from 'next/head';
import siteConfig from '../content/site.json';
type HelmetProps = {
title?: string;
keywords?: string[];
canonical?: string;
description?: string;
};
const Helmet = (props: HelmetProps) => (
<NextHead>
<meta charSet='UTF-8' />
<title>{props.title || siteConfig.title}</title>
<meta name='description' content={props.description || siteConfig.description} />
<meta name='author' content={siteConfig.author} />
<meta name='keywords' content={props.keywords ? props.keywords.join(',') : siteConfig.keywords.join(',')} />
<meta name='viewport'
content='width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=3.0, minimum-scale=1.0' />
{props.canonical && <link rel='canonical' href={props.canonical} />}
<meta httpEquiv='Content-Language' content='en' />
<meta property='og:title' content={props.title || siteConfig.title} />
<meta property='og:description' content={props.description || siteConfig.description} />
<meta property='og:image' content={`${siteConfig.url.web}${siteConfig.logoSquare}`} />
<meta property='og:url' content={siteConfig.url.web} />
<meta property='og:type' content='website' />
<meta property='article:publisher' content={`https://facebook.com/${siteConfig.facebook}`} />
<meta property='og:site_name' content={siteConfig.name} />
<meta property='article:author' content={siteConfig.author} />
<meta name='twitter:card' content='summary' />
<meta name='twitter:site' content={`@${siteConfig.twitter}`} />
<meta name='twitter:title' content={props.title || siteConfig.title} />
<meta name='twitter:description' content={props.description || siteConfig.description} />
<meta name='twitter:image' content={`${siteConfig.url.web}${siteConfig.logoSquare}`} />
<meta name='twitter:image:alt' content='roadmap.sh' />
<meta name='mobile-web-app-capable' content='yes' />
<meta name='apple-mobile-web-app-capable' content='yes' />
<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />
<link rel='apple-touch-icon' sizes='180x180' href='/manifest/apple-touch-icon.png' />
<meta name='msapplication-TileColor' content='#101010' />
<meta name='theme-color' content='#848a9a' />
<link rel='manifest' href='/manifest/manifest.json' />
<link rel='icon' type='image/png' sizes='32x32' href='/manifest/icon32.png' />
<link rel='icon' type='image/png' sizes='16x16' href='/manifest/icon16.png' />
<link rel='shortcut icon' href='/manifest/favicon.ico' type='image/x-icon' />
<link rel='icon' href='/manifest/favicon.ico' type='image/x-icon' />
{ /* Global Site Tag (gtag.js) - Google Analytics */}
{process.env.GA_SECRET && (
<>
<script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GA_SECRET}`} />
<script dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.GA_SECRET}');
`
}} />
</>
)}
</NextHead>
);
export default Helmet;

View File

@@ -1,74 +0,0 @@
import NextHead from 'next/head';
import siteConfig from 'content/site';
const prepareTitle = (givenTitle) => {
return givenTitle || siteConfig.title;
};
const prepareDescription = (givenDescription) => {
return givenDescription || siteConfig.description;
};
// noinspection JSUnresolvedLibraryURL
const Helmet = (props) => (
<NextHead>
<meta charSet='UTF-8' />
<title>{ prepareTitle(props.title) }</title>
<meta name='description' content={ prepareDescription(props.description) } />
<meta name="author" content={ siteConfig.author } />
<meta name="keywords" content={ props.keywords ? props.keywords.join(',') : siteConfig.keywords.join(',') } />
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=3.0, minimum-scale=1.0" />
{ props.canonical && <link rel="canonical" href={ props.canonical } /> }
<meta httpEquiv="Content-Language" content="en" />
<meta property="og:title" content={ prepareTitle(props.title) } />
<meta property="og:description" content={ prepareDescription(props.description) } />
<meta property="og:image" content={ `${siteConfig.url.web}${siteConfig.logoSquare}` } />
<meta property="og:url" content={ siteConfig.url.web } />
<meta property="og:type" content="website" />
<meta property="article:publisher" content={ `https://facebook.com/${siteConfig.facebook}` } />
<meta property="og:site_name" content={ siteConfig.name } />
<meta property="article:author" content={ siteConfig.author } />
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content={ `@${siteConfig.twitter}` } />
<meta name="twitter:title" content={ prepareTitle(props.title) } />
<meta name="twitter:description" content={ prepareDescription(props.description) } />
<meta name="twitter:image" content={ `${siteConfig.url.web}${siteConfig.logoSquare}` } />
<meta name="twitter:image:alt" content="roadmap.sh" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="apple-touch-icon" sizes="180x180" href="/manifest/apple-touch-icon.png" />
<meta name="msapplication-TileColor" content="#101010" />
<meta name="theme-color" content="#848a9a" />
<link rel="manifest" href="/manifest/manifest.json" />
<link rel="icon" type="image/png" sizes="32x32" href="/manifest/icon32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/manifest/icon16.png" />
<link rel="shortcut icon" href="/manifest/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/manifest/favicon.ico" type="image/x-icon" />
{ /* Global Site Tag (gtag.js) - Google Analytics */ }
{ process.env.GA_SECRET && (
<>
<script async src={ `https://www.googletagmanager.com/gtag/js?id=${process.env.GA_SECRET}` } />
<script dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.GA_SECRET}');
`,
}} />
</>
)}
</NextHead>
);
export default Helmet;

View File

@@ -1,13 +0,0 @@
import Link from 'next/link';
import { HeroSectionWrap } from './style';
const HeroSection = () => (
<HeroSectionWrap>
<div className="container">
<h1>Developer Roadmaps</h1>
<p>Community driven roadmaps, articles and resources for developers. <Link href="/signup"><a>Sign up</a></Link> for occasional updates on new roadmaps, updates and guides</p>
</div>
</HeroSectionWrap>
);
export default HeroSection;

View File

@@ -1,30 +0,0 @@
import styled from 'styled-components';
export const HeroSectionWrap = styled.div`
text-align: center;
padding: 70px 20px;
margin: 0 auto;
.container {
max-width: 800px;
}
h1 {
font-size: 70px;
font-weight: 700;
margin-bottom: 25px;
}
p {
font-size: 20px;
color: #333;
font-weight: 400;
margin-bottom: 0;
line-height: 35px;
}
a {
font-weight: 700;
color: #000;
}
`;

View File

@@ -1,15 +0,0 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import RowBlock from 'components/row-block';
import { SubmitText, SubmitWrap } from './style';
const IconRowBlock = ({ url, icon, text, openExternal=false }) => (
<RowBlock url={ url } openExternal={openExternal}>
<SubmitWrap>
<FontAwesomeIcon icon={ icon } />
<SubmitText>{ text }</SubmitText>
</SubmitWrap>
</RowBlock>
);
export default IconRowBlock;

View File

@@ -1,20 +0,0 @@
import styled from 'styled-components';
export const SubmitWrap = styled.div`
display: flex;
flex-direction: column;
height: 100%;
align-items: center;
justify-content: center;
svg {
height: 25px;
color: #d6d6d6;
}
`;
export const SubmitText = styled.p`
margin-bottom: 0;
color: dimgrey;
margin-top: 8px;
`;

View File

@@ -1,4 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-label="GitHub" role="img" viewBox="0 0 512 512" width="22px" height="20px">
<rect width="512" height="512" rx="15%"/>
<path fill="#fff" d="M335 499c14 0 12 17 12 17H165s-2-17 12-17c13 0 16-6 16-12l-1-50c-71 16-86-28-86-28-12-30-28-37-28-37-24-16 1-16 1-16 26 2 40 26 40 26 22 39 59 28 74 22 2-17 9-28 16-35-57-6-116-28-116-126 0-28 10-51 26-69-3-6-11-32 3-67 0 0 21-7 70 26 42-12 86-12 128 0 49-33 70-26 70-26 14 35 6 61 3 67 16 18 26 41 26 69 0 98-60 120-117 126 10 8 18 24 18 48l-1 70c0 6 3 12 16 12z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>

Before

Width:  |  Height:  |  Size: 565 B

After

Width:  |  Height:  |  Size: 841 B

View File

@@ -0,0 +1,4 @@
<svg width="30" height="30" viewBox="0 0 283 283" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 39C0 17.4609 17.4609 0 39 0H244C265.539 0 283 17.4609 283 39V244C283 265.539 265.539 283 244 283H39C17.4609 283 0 265.539 0 244V39Z" fill="black"></path>
<path d="M121.215 210.72C119.348 211.28 116.361 211.84 112.255 212.4C108.335 212.96 104.228 213.24 99.9347 213.24C95.828 213.24 92.0947 212.96 88.7347 212.4C85.5614 211.84 82.8547 210.72 80.6147 209.04C78.3747 207.36 76.6014 205.12 75.2947 202.32C74.1747 199.333 73.6147 195.507 73.6147 190.84V106.84C73.6147 102.547 74.3614 98.9067 75.8547 95.92C77.5347 92.7467 79.868 89.9467 82.8547 87.52C85.8414 85.0933 89.4814 82.9467 93.7747 81.08C98.2547 79.0267 103.015 77.2533 108.055 75.76C113.095 74.2667 118.321 73.1467 123.735 72.4C129.148 71.4667 134.561 71 139.975 71C148.935 71 156.028 72.7733 161.255 76.32C166.481 79.68 169.095 85.28 169.095 93.12C169.095 95.7333 168.721 98.3467 167.975 100.96C167.228 103.387 166.295 105.627 165.175 107.68C161.255 107.68 157.241 107.867 153.135 108.24C149.028 108.613 145.015 109.173 141.095 109.92C137.175 110.667 133.441 111.507 129.895 112.44C126.535 113.187 123.641 114.12 121.215 115.24V210.72ZM166.387 188.32C166.387 180.48 168.813 173.947 173.667 168.72C178.52 163.493 185.147 160.88 193.547 160.88C201.947 160.88 208.573 163.493 213.427 168.72C218.28 173.947 220.707 180.48 220.707 188.32C220.707 196.16 218.28 202.693 213.427 207.92C208.573 213.147 201.947 215.76 193.547 215.76C185.147 215.76 178.52 213.147 173.667 207.92C168.813 202.693 166.387 196.16 166.387 188.32Z" fill="white"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,3 +1,3 @@
<svg width="29" height="29">
<svg width="29" height="29" fill="currentColor">
<path d="M22.05 7.54a4.47 4.47 0 0 0-3.3-1.46 4.53 4.53 0 0 0-4.53 4.53c0 .35.04.7.08 1.05A12.9 12.9 0 0 1 5 6.89a5.1 5.1 0 0 0-.65 2.26c.03 1.6.83 2.99 2.02 3.79a4.3 4.3 0 0 1-2.02-.57v.08a4.55 4.55 0 0 0 3.63 4.44c-.4.08-.8.13-1.21.16l-.81-.08a4.54 4.54 0 0 0 4.2 3.15 9.56 9.56 0 0 1-5.66 1.94l-1.05-.08c2 1.27 4.38 2.02 6.94 2.02 8.3 0 12.86-6.9 12.84-12.85.02-.24 0-.43 0-.65a8.68 8.68 0 0 0 2.26-2.34c-.82.38-1.7.62-2.6.72a4.37 4.37 0 0 0 1.95-2.51c-.84.53-1.81.9-2.83 1.13z"></path>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 529 B

After

Width:  |  Height:  |  Size: 550 B

View File

@@ -0,0 +1,21 @@
export function VideoIcon(props: any) {
return (
<svg
stroke='currentColor'
fill='currentColor'
strokeWidth='0'
viewBox='0 0 24 24'
height='1em'
width='1em'
xmlns='http://www.w3.org/2000/svg'
{...props}
>
<g>
<path fill='none' d='M0 0h24v24H0z' />
<path
d='M3 3.993C3 3.445 3.445 3 3.993 3h16.014c.548 0 .993.445.993.993v16.014a.994.994 0 0 1-.993.993H3.993A.994.994 0 0 1 3 20.007V3.993zm7.622 4.422a.4.4 0 0 0-.622.332v6.506a.4.4 0 0 0 .622.332l4.879-3.252a.4.4 0 0 0 0-.666l-4.88-3.252z'
/>
</g>
</svg>
);
}

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill='currentColor'>
<path d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"/>
</svg>

After

Width:  |  Height:  |  Size: 369 B

View File

@@ -1,9 +0,0 @@
export function BadgeLink({ target='_blank', variant ='success', badgeText, href, children }) {
return (
<p className='mb-0'>
<a href={href} target={ target }>
<span style={{ position: 'relative', top: '-2px'}} className={`badge badge-${variant}`}>{badgeText}</span> { children }
</a>
</p>
);
}

View File

@@ -1,13 +0,0 @@
import styled from 'styled-components';
export const StrongLink = styled.a`
border-bottom: 2px solid currentColor;
position: relative;
transition: background-color 120ms;
text-decoration: none;
font-weight: 600;
&:hover {
text-decoration: none;
}
`;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import { Badge, Flex, Link, Text } from '@chakra-ui/react';
type LinksListItemProps = {
href: string;
title: string;
subtitle: string;
badgeText?: string;
icon?: React.ReactChild;
hideSubtitleOnMobile?: boolean;
};
export function LinksListItem(props: LinksListItemProps) {
const { title, subtitle, badgeText, icon, hideSubtitleOnMobile = false, href } = props;
return (
<Link
href={href}
fontSize={['14px', '14px', '15px']}
py='9px'
d='flex'
flexDirection={['column', 'row', 'row']}
fontWeight={500}
color='gray.600'
alignItems={['flex-start', 'center']}
justifyContent={'space-between'}
_hover={{
textDecoration: 'none',
color: 'blue.400',
'& .list-item-title': {
transform: 'translateX(10px)'
}
}}
isTruncated
maxWidth='100%'
>
<Flex alignItems='center' className='list-item-title' transition={'200ms'}>
{icon}
<Text maxWidth={'345px'} isTruncated as='span'>{title}</Text>
{badgeText &&
<Badge pos='relative' top='1px' variant='subtle' colorScheme='purple' ml='10px'>{badgeText}</Badge>}
</Flex>
<Text d={[hideSubtitleOnMobile ? 'none' : 'inline', 'inline']} mt={['3px', 0]} as='span'
fontSize={['11px', '11px', '12px']} color='gray.500'>{subtitle}</Text>
</Link>
);
}

21
components/links-list.tsx Normal file
View File

@@ -0,0 +1,21 @@
import React from 'react';
import { StackDivider, VStack } from '@chakra-ui/react';
type LinksListProps = {
children: React.ReactNode
};
export function LinksList(props: LinksListProps) {
const { children } = props;
return (
<VStack
rounded='5px'
divider={<StackDivider borderColor='gray.200' />}
spacing={0}
align='stretch'
>
{children}
</VStack>
);
}

View File

@@ -1,11 +0,0 @@
import styled from 'styled-components';
export const Mark = styled.span`
background: #1b1e21;
color: #fff;
display: inline-block;
font-size: 18px;
font-weight: 600;
margin: 0 4px;
padding: 0 6px 0 7px;
`;

View File

@@ -1,10 +0,0 @@
import { MDXProvider } from '@mdx-js/react';
import MdxComponents from './mdx-components';
const MdRenderer = (props) => (
<MDXProvider components={ MdxComponents }>
{ props.children }
</MDXProvider>
);
export default MdRenderer;

View File

@@ -0,0 +1,20 @@
import React from 'react';
// @ts-ignore
import { MDXProvider } from '@mdx-js/react';
import { ChakraProvider } from '@chakra-ui/react';
import MdxComponents from './mdx-components';
import { roadmapTheme } from '../../styles/theme';
type MdRendererType = {
children: React.ReactNode
};
export default function MdRenderer(props: MdRendererType) {
return (
<ChakraProvider theme={roadmapTheme} resetCSS>
<MDXProvider components={MdxComponents}>
{props.children}
</MDXProvider>
</ChakraProvider>
);
};

View File

@@ -1,18 +0,0 @@
import styled from 'styled-components';
const Link = styled.a`
font-weight: 600;
`;
const EnrichedLink = props => {
// Is external URL or is a media URL
const isExternalUrl = /(^http(s)?:\/\/)|(\.(png|svg|jpeg|jpg)$)/.test(props.href);
return (
<Link href={ props.href } target={ isExternalUrl ? '_blank' : '_self' }>
{ props.children }
</Link>
);
};
export default EnrichedLink;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import styled from 'styled-components';
type EnrichedLinkProps = {
href: string;
children: React.ReactNode
}
const Link = styled.a`
font-weight: 600;
text-decoration: underline;
`;
const EnrichedLink = (props: EnrichedLinkProps) => {
// Is external URL or is a media URL
const isExternalUrl = /(^http(s)?:\/\/)|(\.(png|svg|jpeg|jpg)$)/.test(props.href);
return (
<Link href={props.href} target={isExternalUrl ? '_blank' : '_self'}>
{props.children}
</Link>
);
};
export default EnrichedLink;

View File

@@ -0,0 +1,21 @@
import React from 'react';
import { Link, Text, Badge } from '@chakra-ui/react';
type BadgeLinkType = {
target: string;
badgeText: string;
href: string;
children: React.ReactNode
};
export function BadgeLink(props: BadgeLinkType) {
const { target = '_blank', badgeText, href, children } = props;
return (
<Text mb={0}>
<Link fontWeight={500} textDecoration='underline' href={href} target={target}>
<Badge colorScheme={'purple'} style={{ position: 'relative', top: '-2px' }}>{badgeText}</Badge> {children}
</Link>
</Text>
);
}

View File

@@ -5,20 +5,21 @@ const BlockQuote = styled.blockquote`
position: relative;
background: #e8e8e8;
border-radius: 5px;
margin-bottom: 18px;
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
}
p + h4 {
margin-top: 15px;
}
p {
margin: 0;
& + p {
margin-top: 10px;
margin-top: 10px;
}
}
`;

View File

@@ -0,0 +1,10 @@
import React from 'react';
import { Code as ChakraCode } from '@chakra-ui/react';
type CodeType = {
children: React.ReactNode;
}
export default function Code(props: CodeType) {
return <ChakraCode bg='blue.500'>{props.children}</ChakraCode>;
}

View File

@@ -1,8 +1,9 @@
import React from 'react';
import styled from 'styled-components';
import Link from 'components/icons/link.svg';
import LinkIcon from 'components/icons/link.svg';
const linkify = (Component) => {
return (props) => {
const linkify = (Component: React.FunctionComponent<any>) => {
return function EnrichedHeading(props: { children: string }): React.ReactNode {
const text = props.children;
const id = text.toLowerCase && text
.toLowerCase()
@@ -11,11 +12,11 @@ const linkify = (Component) => {
.replace(/[?!]/g, '');
return (
<Component id={ id }>
<HeaderLink href={ `#${id}` }>
<Link />
<Component id={id}>
<HeaderLink href={`#${id}`}>
<LinkIcon />
</HeaderLink>
{ props.children }
{props.children}
</Component>
);
};
@@ -35,9 +36,10 @@ const HeaderLink = styled.a`
const H1 = styled.h1`
position: relative;
font-size: 42px;
line-height: 40px;
font-weight: 700;
margin: 32px 0 10px !important;
&:hover ${HeaderLink} {
display: flex;
}
@@ -67,11 +69,13 @@ const H6 = styled(H1).attrs({ as: 'h6' })`
font-size: 18px;
`;
export const Headings = {
const Headings = {
h1: linkify(H1),
h2: linkify(H2),
h3: linkify(H3),
h4: linkify(H4),
h5: linkify(H5),
h6: linkify(H6),
h6: linkify(H6)
};
export default Headings;

View File

@@ -1,8 +0,0 @@
import styled from 'styled-components';
export const IFrame = styled.iframe`
display: block;
width: 100%;
border: none;
margin: 30px auto;
`;

View File

@@ -0,0 +1,47 @@
import styled from 'styled-components';
type IFrameProps = {
title: string;
src: string;
};
const AspectRatioBox = styled.div`
position: relative;
max-width: 100%;
margin-bottom: 18px;
&:before {
height: 0;
content: "";
display: block;
padding-bottom: 50%;
}
& > iframe {
overflow: hidden;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
`;
export default function IFrame(props: IFrameProps) {
return (
<AspectRatioBox>
<iframe
frameBorder={0}
title={props.title}
src={props.src}
allow={'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'}
allowFullScreen
/>
</AspectRatioBox>
);
}

View File

@@ -1,12 +1,15 @@
import P from './p';
import { Headings } from './heading';
import { Code } from '@chakra-ui/react';
import { P } from './p';
import Headings from './heading';
import { Pre } from './pre';
import BlockQuote from './blockquote';
import { Table } from './table';
import { IFrame } from './iframe';
import IFrame from './iframe';
import { Img } from './img';
import EnrichedLink from './a';
import { BadgeLink } from '../../link/badge-link';
import { BadgeLink } from './badge-link';
import { Li, Ul } from './ul';
import PremiumBlock from './premium-block';
const MdxComponents = {
p: P,
@@ -17,7 +20,11 @@ const MdxComponents = {
table: Table,
iframe: IFrame,
img: Img,
BadgeLink: BadgeLink
code: Code,
BadgeLink: BadgeLink,
PremiumBlock: PremiumBlock,
ul: Ul,
li: Li
};
export default MdxComponents;

View File

@@ -1,20 +0,0 @@
import styled from 'styled-components';
const P = styled.p`
color: inherit;
font-size: 16px;
font-weight: 400;
line-height: 26px;
margin: 0 0 12px;
img + em {
text-align: center;
color: #666666;
font-style: normal;
font-size: 14px;
margin: 5px 0 10px;
display: block;
}
`;
export default P;

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { Text } from '@chakra-ui/react';
import styled from 'styled-components';
type EnrichedTextType = {
children: React.ReactNode;
}
export const P = styled.p`
line-height: 27px;
font-size: 16px;
color: black;
margin-bottom: 18px;
`;

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { Box, Button, Heading, Text } from '@chakra-ui/react';
import { LockIcon } from '@chakra-ui/icons';
type PremiumBlockProps = {
title: string;
description: string;
};
export default function PremiumBlock(props: PremiumBlockProps) {
return (
<Box p='40px' textAlign='center' rounded='5px' mb='18px' bg='gray.50' borderWidth={1}>
<LockIcon color='gray.300' height='45px' w='45px' mb='18px' />
<Heading as='h3' fontSize='30px' mb='10px'>{props.title}</Heading>
<Text mb='18px'>{props.description}</Text>
<Button colorScheme='green'>Become a Member</Button>
</Box>
);
}

View File

@@ -5,7 +5,7 @@ export const Table = styled.table`
width: 100%;
border-spacing: 0;
margin: 20px 0;
th {
color: #666;
font-size: 12px;
@@ -16,10 +16,10 @@ export const Table = styled.table`
vertical-align: middle;
padding: 5px 10px;
}
td {
font-size: 14px;
padding: 10px;
border-bottom: 1px solid #EAEAEA;
}
`;
`;

View File

@@ -0,0 +1,12 @@
import React from 'react';
import { UnorderedList } from '@chakra-ui/react';
import styled from 'styled-components';
export const Ul = styled.ul`
margin-left: 40px;
margin-bottom: 18px;
`;
export const Li = styled.li`
margin-bottom: 7px;
`;

View File

@@ -0,0 +1,46 @@
import { Box, Container, Heading, Link, Text } from '@chakra-ui/react';
export function OpensourceBanner() {
return (
<Box borderTopWidth={1} pt={['45px', '45px', '70px']} pb={['20px', '20px', '30px']} textAlign='center'>
<Container maxW='container.md'>
<Heading fontSize={['25px', '25px', '35px']} mb={['10px', '10px', '20px']}>Open Source</Heading>
<Text lineHeight='26px' fontSize={['15px', '15px', '16px']} mb='20px'>The project is OpenSource,&nbsp;
<Link
_hover={{ textDecoration: 'none' }}
href='https://github.com/search?o=desc&q=stars%3A%3E100000&s=stars&type=Repositories'
target='_blank'
borderBottomWidth={1}
fontWeight={600}
>7th most starred project on GitHub</Link> and is visited by hundreds of thousands of
developers every month.</Text>
<iframe
src='https://ghbtns.com/github-btn.html?user=kamranahmedse&repo=developer-roadmap&type=star&count=true&size=large'
frameBorder='0'
scrolling='0'
width='170'
height='30'
style={{ margin: 'auto', marginBottom: '30px' }}
title='GitHub'
/>
<Text lineHeight={['25px', '25px', '26px']} fontSize={['15px', '15px', '16px']} mb='15px'>A considerable amount of my time is spent doing unpaid
community work on things that I hope will help humanity in some way. Your sponsorship helps me continue to
produce more open-source and free educational material consumed by hundreds of thousands of developers every
month.</Text>
<Box>
<iframe
src='https://ghbtns.com/github-btn.html?user=kamranahmedse&type=sponsor&size=large'
frameBorder='0'
scrolling='0'
width='260'
height='30'
title='GitHub'
style={{ margin: 'auto' }}
/>
</Box>
</Container>
</Box>
);
}

View File

@@ -1,41 +0,0 @@
export const CustomAd = () => {
return (
<div id="carbonads">
<span>
<span className="carbon-wrap">
<a
href="https://freemote.com/strategy?sl=roadmap"
className="carbon-img"
target="_blank"
rel="noopener sponsored"
>
<img
src="/fm-img.png"
alt="FM Logo"
border="0"
height="100"
width="130"
style={{ maxWidth: "130px" }}
/>
</a>
<a
href="https://freemote.com/strategy?sl=roadmap"
className="carbon-text"
target="_blank"
rel="noopener sponsored"
>
He Went from ZERO TO $74,000 as a Full Time Developer in 7 Weeks
</a>
</span>
<a
href="https://github.com/sponsors/kamranahmedse"
className="carbon-poweredby"
target="_blank"
rel="noopener sponsored"
>
Sponsored by
</a>
</span>
</div>
);
};

View File

@@ -1,71 +0,0 @@
import siteConfig from 'content/site';
import { FooterWrap } from './style.js';
import './carbon.scss';
import { CustomAd } from "./custom-ad";
const PageFooter = () => (
<FooterWrap className="border-top">
<div className="container">
<div className="foot-cols-wrap row">
<div className="site-meta-wrap col-12 col-sm-12 col-lg col-xl col-md-12">
<div className="site-meta">
<div className="brand-detail">
<a href="/" className='brand'><img src="/brand.png" alt="" /> roadmap.sh</a>
<span className="preposition">by</span>
<a href="https://twitter.com/kamranahmedse" target="_blank" className='follow-author'>@kamranahmedse</a>
</div>
<div className="brand-explanation">
<p>Community created roadmaps, articles, resources and journeys to help you choose your path and grow in your career.</p>
</div>
<p className='meta-links'>
&copy; roadmap.sh &middot;
&nbsp; <a href="/about">FAQ</a> &middot;
&nbsp; <a href="/terms">Terms</a> &middot;
&nbsp; <a href="/privacy">Privacy</a>
</p>
</div>
</div>
<div className="site-contribute foot-col col-12 col-sm-4 col-lg-2">
<ul>
<li className='foot-header'>Contribute</li>
<li><a href='https://gum.co/roadmap-sh' target="_blank">Sponsor us</a></li>
<li><a href={ siteConfig.url.addGuide } target="_blank">Write a Guide</a></li>
<li><a href={ siteConfig.url.addRoadmap } target="_blank">Submit a Roadmap</a></li>
<li><a href='/about'>About this Site</a></li>
</ul>
</div>
<div className="site-learn foot-col col-12 col-sm-4 col-lg-2">
<ul>
<li className="foot-header">Learn</li>
<li><a href="/guides">Read Guides</a></li>
<li><a href="/watch">Watch Videos</a></li>
<li><a href="/podcasts">Podcasts</a></li>
<li><a href="https://www.youtube.com/channel/UCA0H2KIWgWTwpTFjSxp0now?sub_confirmation=1" target="_blank">YouTube</a></li>
</ul>
</div>
<div className="site-learn foot-col col-12 col-sm-4 col-lg-2">
<ul>
<li className="foot-header">Most Visited</li>
<li><a href="/frontend">Frontend Roadmap</a></li>
<li><a href="/backend">Backend Roadmap</a></li>
<li><a href="/devops">DevOps Roadmap</a></li>
<li><a href="/roadmaps">Upcoming</a></li>
</ul>
</div>
</div>
</div>
<CustomAd />
{/* Do not show on local */}
{ process.env.GA_SECRET && false && (
<>
<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CE7DLK3Y&placement=roadmapsh" id="_carbonads_js"></script>
{/*<div id="codefund"></div>*/}
{/*<script src="https://app.codefund.io/properties/681/funder.js" async></script>*/}
</>
) }
</FooterWrap>
);
export default PageFooter;

View File

@@ -1,94 +0,0 @@
import styled from 'styled-components';
export const FooterWrap = styled.div`
padding: 65px 0;
.site-meta {
margin-bottom: 30px;
width: 350px;
.brand-detail {
display: flex;
align-items: center;
}
.brand {
display: flex;
align-items: center;
font-weight: 600;
color: #000;
text-decoration: none;
img {
height: 25px;
width: 25px;
margin-right: 8px;
}
}
.preposition {
margin: 0 7px;
}
.follow-author {
background-color: #1e99e6;
border-radius: 3px;
color: #fff;
cursor: pointer;
font-size: 12px;
font-weight: 600;
line-height: 20px;
padding: 0 6px;
white-space: nowrap;
text-decoration: none;
&:hover {
background: #43aaea;
}
}
.brand-explanation {
color: #999;
p {
font-size: 15px;
line-height: 24px;
margin: 15px 0;
}
}
.meta-links {
color: #a3a3a3;
font-size: 15px;
a {
color: #000;
text-decoration: none;
}
}
}
.foot-col {
margin-bottom: 20px;
a {
color: #999;
}
.foot-header {
font-weight: 500;
margin-bottom: 7px;
}
ul {
margin: 0;
padding: 0;
li {
margin-bottom: 2px;
font-size: 15px;
list-style: none;
margin-left: 0;
}
}
}
`;

View File

@@ -0,0 +1,35 @@
import { Box, Container, Heading, Text } from '@chakra-ui/react';
import React from 'react';
type PageHeaderProps = {
title: string;
subtitle: string;
children?: React.ReactNode;
};
export function PageHeader(props: PageHeaderProps) {
const { title, subtitle, children } = props;
return (
<Box pt={['25px', '20px', '45px']} pb={['20px', '15px', '30px']} borderBottomWidth={1} mb='30px'>
<Container maxW='container.md' position='relative'>
<Heading
as='h1'
color='black'
fontSize={['28px', '33px', '40px']}
fontWeight={700}
mb={['2px', '2px', '5px']}
>
{title}
</Heading>
<Text fontSize={['13px', '14px', '15px']}>{subtitle}</Text>
</Container>
{children && (
<Container maxW='container.md'>
{children}
</Container>
)}
</Box>
);
}

View File

@@ -1,16 +0,0 @@
import { HeaderWrap, Subtitle, Title } from './style';
const PageHeader = ({
title,
subtitle,
children,
}) => (
<HeaderWrap>
<Title>{ title }</Title>
<Subtitle dangerouslySetInnerHTML={{ __html: subtitle }} />
{ children }
</HeaderWrap>
);
export default PageHeader;

View File

@@ -1,22 +0,0 @@
import styled from 'styled-components';
export const HeaderWrap = styled.div`
text-align: center;
padding: 45px 30px;
`;
export const Title = styled.h1`
font-size: 48px;
font-weight: 700;
margin-bottom: 12px;
`;
export const Subtitle = styled.p`
font-size: 16px;
color: #444;
a {
color: inherit;
font-weight: 700;
}
`;

View File

@@ -1,13 +0,0 @@
import { HeaderWrap, Subtitle, Title, Logo } from './style';
const PageLogoHeader = ({ title, subtitle, children, }) => (
<HeaderWrap>
<Logo src='/brand.png' alt='' />
<Title>{ title }</Title>
<Subtitle dangerouslySetInnerHTML={{ __html: subtitle }} />
{ children }
</HeaderWrap>
);
export default PageLogoHeader;

View File

@@ -1,30 +0,0 @@
import styled from 'styled-components';
export const HeaderWrap = styled.div`
text-align: center;
padding: 45px 30px;
`;
export const Title = styled.h1`
font-size: 40px;
font-weight: 700;
margin-bottom: 12px;
`;
export const Subtitle = styled.p`
font-size: 16px;
color: #444;
margin-bottom: 0;
a {
color: inherit;
font-weight: 700;
}
`;
export const Logo = styled.img`
width: 75px;
height: 75px;
margin-bottom: 26px;
`;

View File

@@ -1,62 +0,0 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft, faClock, faEnvelope, faHandshake } from '@fortawesome/free-solid-svg-icons';
import { BadgeLink, BadgesList, DarkBadge, PrimaryBadge, SecondaryBadge } from 'components/badges';
import siteConfig from "content/site";
import { Description, Header, Title, MenuItemLink, MenuItems } from './style';
import Link from 'next/link';
import classNames from 'classnames';
const RoadmapHeader = ({ roadmap, page = 'landscape' }) => (
<Header>
<Title>{ roadmap.title }</Title>
<Description>{ roadmap.description }</Description>
<BadgesList className="mt-4">
<BadgeLink href="/roadmaps">
<DarkBadge>
<FontAwesomeIcon className='d-none d-md-block' icon={ faArrowLeft } />
Other Roadmaps
</DarkBadge>
</BadgeLink>
{ roadmap.upcoming && (
<BadgeLink href="/signup">
<SecondaryBadge>
<FontAwesomeIcon className='d-none d-md-block' icon={ faClock } />
Upcoming Roadmap
</SecondaryBadge>
</BadgeLink>
) }
{ !roadmap.upcoming && (
<BadgeLink href={ `${siteConfig.url.issue}?title=[${roadmap.title}] - Title Here` } target="_blank" className='d-none d-md-block' >
<SecondaryBadge>
<FontAwesomeIcon icon={ faHandshake } />
Suggest Changes
</SecondaryBadge>
</BadgeLink>
) }
<BadgeLink href="/signup">
<PrimaryBadge>
<FontAwesomeIcon className='d-none d-md-block' icon={ faEnvelope } />
Send me Updates
</PrimaryBadge>
</BadgeLink>
</BadgesList>
<MenuItems className="border-bottom">
<div className={ classNames({ 'd-none': roadmap.title.toLowerCase() !== 'frontend developer' })}>
<Link href={ `${roadmap.url}` } passHref>
<MenuItemLink className={ classNames({ active: page === 'landscape', }) }>Landscape</MenuItemLink>
</Link>
<Link href={ `${roadmap.url}/resources` } passHref>
<MenuItemLink className={ classNames({ active: page === 'resources', }) }>Resources</MenuItemLink>
</Link>
{/*<Link href={ `${roadmap.url}/resources` } passHref>*/}
{/* <MenuItemLink className={ classNames({ active: false, }) }>Project Ideas</MenuItemLink>*/}
{/*</Link>*/}
</div>
</MenuItems>
</Header>
);
export default RoadmapHeader;

View File

@@ -1,50 +0,0 @@
import styled from 'styled-components';
export const Header = styled.div`
text-align: center;
padding: 45px 30px 10px;
`;
export const Title = styled.h1`
font-weight: 700;
margin-bottom: 12px;
font-size: 48px;
`;
export const Description = styled.p`
font-size: 16px;
color: #444444;
`;
export const MenuItems = styled.div`
margin: 35px 0 15px;
`;
export const MenuItemLink = styled.a`
display: inline-block;
position: relative;
padding: 5px 10px 8px;
text-decoration: none;
color: rgb(102, 102, 102);
font-size: 14px;
font-weight: 400;
&.active, &.active:hover {
color: #2d2d2d;
&:after {
position: absolute;
content: "";
display: block;
height: 0;
left: 9px;
right: 9px;
bottom: -1px;
border-bottom: 2px solid currentColor;
}
}
&:hover {
text-decoration: none;
color: #111111;
}
`;

View File

@@ -1,35 +0,0 @@
import { Summary, SummaryContainer, UpcomingContainer } from './style';
import GuideBody from 'components/guide-body';
import RoadmapHeader from 'components/roadmap-header';
import SharePage from 'components/share-page';
import MdRenderer from 'components/md-renderer';
const RoadmapResources = ({ roadmap }) => {
if (roadmap.upcoming) {
return (
<>
<RoadmapHeader roadmap={ roadmap } />
<UpcomingContainer>
<GuideBody guide={{ fileName: 'upcoming' }} />
</UpcomingContainer>
</>
);
}
const filePath = roadmap.resources.replace(/^\//, '');
const ResourcesContent = require(`../../content/${filePath}`).default;
return (
<SummaryContainer>
<RoadmapHeader roadmap={ roadmap } page='resources' />
<Summary className="container">
<MdRenderer>
<ResourcesContent />
</MdRenderer>
<SharePage title={ roadmap.description } url={ roadmap.url } />
</Summary>
</SummaryContainer>
)
};
export default RoadmapResources;

View File

@@ -1,21 +0,0 @@
import styled from 'styled-components';
export const SummaryContainer = styled.div``;
export const UpcomingContainer = styled.div`
text-align: center;
padding: 40px 0 50px;
.container {
position: relative;
}
`;
export const Summary = styled.div`
margin-top: 35px;
min-height: 400px;
max-width: 1000px;
display: block;
position: relative;
text-align: left;
`;

View File

@@ -1,36 +0,0 @@
import { Summary, SummaryContainer, UpcomingContainer } from './style';
import classNames from 'classnames';
import GuideBody from 'components/guide-body';
import RoadmapHeader from 'components/roadmap-header';
import SharePage from 'components/share-page';
import MdRenderer from 'components/md-renderer';
const RoadmapSummary = ({ roadmap }) => {
if (roadmap.upcoming) {
return (
<>
<RoadmapHeader roadmap={ roadmap } />
<UpcomingContainer>
<GuideBody guide={{ fileName: 'upcoming' }} />
</UpcomingContainer>
</>
);
}
const filePath = roadmap.path.replace(/^\//, '');
const RoadmapContent = require(`../../content/${filePath}`).default;
return (
<SummaryContainer>
<RoadmapHeader roadmap={ roadmap } />
<Summary className={classNames("container", { "container-small": roadmap.isTextHeavy })}>
<MdRenderer>
<RoadmapContent />
</MdRenderer>
<SharePage title={ roadmap.description } url={ roadmap.url } />
</Summary>
</SummaryContainer>
)
};
export default RoadmapSummary;

View File

@@ -1,21 +0,0 @@
import styled from 'styled-components';
export const SummaryContainer = styled.div``;
export const UpcomingContainer = styled.div`
text-align: center;
padding: 40px 0 50px;
.container {
position: relative;
}
`;
export const Summary = styled.div`
margin-top: 35px;
min-height: 400px;
max-width: 1000px;
display: block;
position: relative;
text-align: left;
`;

View File

@@ -0,0 +1,47 @@
import { Box, Heading, Link, Text, Tooltip } from '@chakra-ui/react';
import { InfoIcon } from '@chakra-ui/icons';
type RoadmapGridItemProps = {
title: string;
subtitle: string;
isCommunity?: boolean;
colorIndex?: number;
url: string;
};
const bgColorList = [
'blue.900',
'red.800',
'green.800',
'teal.800',
'gray.800',
'red.900'
];
export function HomeRoadmapItem(props: RoadmapGridItemProps) {
const { title, subtitle, isCommunity, colorIndex = 0, url } = props;
return (
<Box
as={Link}
href={url}
_hover={{ textDecoration: 'none', transform: 'scale(1.02)' }}
flex={1}
shadow='2xl'
bg={bgColorList[colorIndex] ?? bgColorList[0]}
color='white'
p='15px'
rounded='10px'
pos='relative'
>
{isCommunity && (
<Tooltip label={'Community contribution'} hasArrow placement='top'>
<InfoIcon opacity={0.5} position='absolute' top='10px' right='10px' />
</Tooltip>
)}
<Heading fontSize={['17px', '17px', '22px']} mb='5px'>{title}</Heading>
<Text color='gray.200' fontSize={['13px']}>{subtitle}</Text>
</Box>
);
}

View File

@@ -0,0 +1,89 @@
import { Badge, Box, Flex, Heading, Link, Text, Tooltip } from '@chakra-ui/react';
import { InfoIcon } from '@chakra-ui/icons';
type RoadmapGridItemProps = {
title: string;
subtitle: string;
href: string;
isCommunity?: boolean;
isUpcoming?: boolean;
colorIndex?: number;
};
const bgColorList = [
'gray.900',
'purple.900',
'blue.900',
'red.900',
'green.900',
'teal.900',
'yellow.900',
'cyan.900',
'pink.900',
'gray.800',
'purple.800',
'blue.800',
'red.800',
'green.800',
'teal.800',
'yellow.800',
'cyan.800',
'pink.800',
'gray.700',
'purple.700',
'blue.700',
'red.700',
'green.700',
'teal.700',
'yellow.700',
'cyan.700',
'pink.700',
'gray.600',
'purple.600',
'blue.600',
'red.600',
'green.600',
'teal.600',
'yellow.600',
'cyan.600',
'pink.600'
];
export function RoadmapGridItem(props: RoadmapGridItemProps) {
const { title, subtitle, isCommunity = false, isUpcoming = false, colorIndex = 0, href = '/' } = props;
return (
<Box _hover={{ textDecoration: 'none', transform: 'scale(1.02)' }} as={Link} href={href} shadow='xl' p='20px'
rounded='10px' bg={bgColorList[colorIndex] ?? bgColorList[0]} flex={1} pos='relative'>
{isCommunity && (
<Tooltip label={'Community contribution'} hasArrow placement='top'>
<InfoIcon opacity={0.5} color='gray.100' position='absolute' top='10px' right='10px' />
</Tooltip>
)}
<Heading color='white' mb={'6px'} fontSize='20px'>{title}</Heading>
<Text color='gray.300' fontSize='14px'>{subtitle}</Text>
{isUpcoming && (
<Flex
alignItems='center'
justifyContent='center'
pos='absolute'
left={0}
right={0}
top={0}
bottom={0}
rounded='10px'
>
<Text color='white' bg='yellow.900' zIndex={1} fontWeight={600} p={'5px 10px'}
rounded='10px'>Upcoming</Text>
<Box bg={'black'} pos='absolute' top={0} left={0} right={0} bottom={0} rounded={'10px'} opacity={0.5} />
</Flex>
)}
</Box>
);
}

View File

@@ -1,28 +0,0 @@
import { Badge, BlockItem, ItemSubtitle, ItemTitle, ItemWrap } from './style';
const RowBlock = ({
title,
subtitle,
url,
badge,
openExternal = false,
disabled = false,
children = null
}) => (
<ItemWrap className="col-md-6 col-lg-4 col-xl-4">
<BlockItem href={ url } disabled={ disabled } target={openExternal ? '_blank' : '_self'}>
{ !children && (
<>
<ItemTitle>
{ title }
{ badge && <Badge>{ badge }</Badge>}
</ItemTitle>
<ItemSubtitle>{ subtitle }</ItemSubtitle>
</>
) }
{ children }
</BlockItem>
</ItemWrap>
);
export default RowBlock;

View File

@@ -1,62 +0,0 @@
import styled from 'styled-components';
export const ItemWrap = styled.div`
padding: 0 10px 20px;
`;
export const BlockItem = styled.a`
min-height: 114px;
box-shadow: rgba(0, 0, 0, 0.12) 0 5px 10px;
transition: box-shadow 0.2s ease 0s;
align-items: stretch;
flex: 1;
display: flex;
flex-direction: column;
min-width: 1px;
max-width: 100%;
background: white;
padding: 20px;
border-radius: 10px;
justify-content: center;
text-decoration:none;
opacity: ${ props => props.disabled ? 0.5 : 1 };
&:hover {
text-decoration:none;
box-shadow: rgba(0, 0, 0, 0.12) 0 30px 60px;
z-index: 1;
}
`;
export const ItemTitle = styled.h3`
font-weight: 600;
font-size: 18px;
line-height: 24px;
color: #101010;
display: flex;
align-items: center;
justify-content: flex-start;
`;
export const Badge = styled.span`
font-size: 10px;
font-weight: 600;
background: #8a8a8a;
color: white;
padding: 0 8px;
border-radius: 4px;
text-transform: uppercase;
display: flex;
align-items: center;
justify-content: center;
margin-left: 10px;
height: 18px;
`;
export const ItemSubtitle = styled.p`
font-size: 14px;
color: rgb(102, 102, 102);
white-space: normal;
line-height: 1.5;
margin: 0;
`;

View File

@@ -1,21 +0,0 @@
import styled from 'styled-components';
export const ShareIcon = styled.a`
display: block;
width: 22px;
height: 22px;
margin-bottom: 8px;
text-align: center;
svg {
height: 22px !important;
width: 22px !important;
color: #757575;
transition: all 0.2s;
vertical-align: top;
}
&:hover svg {
color: #000000
}
`;

View File

@@ -1,37 +0,0 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFacebookSquare, faHackerNewsSquare, faRedditSquare, faTwitterSquare } from '@fortawesome/free-brands-svg-icons';
import { getFacebookShareUrl, getHnShareUrl, getRedditShareUrl, getTwitterShareUrl } from 'lib/url';
import { ShareIconsList, ShareWrap } from './style';
import { ShareIcon } from 'components/share-icon';
const SharePage = ({
url,
title,
twitterUsername,
}) => (
<ShareWrap>
<ShareIconsList className="d-none d-sm-flex">
<ShareIcon
href={ getTwitterShareUrl({
text: `${title} ${twitterUsername ? `by @${twitterUsername}` : ''}`,
url: url
})}
target="_blank"
>
<FontAwesomeIcon icon={ faTwitterSquare } />
</ShareIcon>
<ShareIcon href={ getFacebookShareUrl({ text: title, url: url }) } target="_blank">
<FontAwesomeIcon icon={ faFacebookSquare } />
</ShareIcon>
<ShareIcon href={ getHnShareUrl({ text: title, url: url })} target="_blank">
<FontAwesomeIcon icon={faHackerNewsSquare}/>
</ShareIcon>
<ShareIcon href={ getRedditShareUrl({ text: title, url: url })} target="_blank">
<FontAwesomeIcon icon={ faRedditSquare } />
</ShareIcon>
</ShareIconsList>
</ShareWrap>
);
export default SharePage;

View File

@@ -1,21 +0,0 @@
import styled from 'styled-components';
export const ShareIconsList = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: sticky;
top: 65px;
`;
export const ShareWrap = styled.div`
display: flex;
align-items: center;
flex-direction: column;
position: absolute;
padding: 0 0;
top: 0;
left: -40px;
min-height: 100%;
`;

View File

@@ -1,17 +0,0 @@
import { SignUpWrap, Title, Subtitle, Textbox, Button } from './style';
const SignUpForm = () => (
<SignUpWrap>
<Title>Subscribe</Title>
<Subtitle>Enter your email below to get notified about the new roadmaps, guides and updates</Subtitle>
<form action="https://kamranahmed.us9.list-manage.com/subscribe/post?u=6f57b741a6a939744f1f203a0&amp;id=f9ca4d6aee" target="_blank" method="post">
<Textbox type="email" name="EMAIL" required placeholder="Your email" />
<div style={{position: 'absolute', left: '-5000px'}}>
<input type="text" name="b_6f57b741a6a939744f1f203a0_f9ca4d6aee" tabIndex="-1" />
</div>
<Button type="submit">Subscribe</Button>
</form>
</SignUpWrap>
);
export default SignUpForm;

View File

@@ -1,57 +0,0 @@
import styled from 'styled-components';
export const SignUpWrap = styled.div`
max-width: 400px;
margin: 200px auto;
text-align: center;
`;
export const Title = styled.h1`
font-size: 48px;
font-weight: 700;
margin-bottom: 17px;
`;
export const Subtitle = styled.p`
font-size: 16px;
`;
export const Textbox = styled.input`
display: block;
width: 100%;
padding: 10px 15px;
font-size: 16px;
font-weight: 400;
line-height: 1.5;
color: #495057;
height: auto;
outline: none;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 5px;
&:focus {
border: 1px solid #101010;
}
`;
export const Button = styled.button`
-webkit-appearance: none;
border: none;
outline: none;
box-shadow: none;
display: block;
text-align: center;
width: 100%;
padding: 10px 15px;
margin-top: 10px;
background: #333;
color: white;
border-radius: 5px;
&:hover, &:active, &:focus {
background: #000000;
box-shadow: none;
outline: none;
}
`;

View File

@@ -1,26 +0,0 @@
import { HeaderWrap } from './style';
const SiteNav = () => (
<HeaderWrap>
<div className='top-row container'>
<div className='flex-grow-1 brand'>
<a href='/'>
<img src='/brand.png' alt='' />
</a>
</div>
<div className='nav-links'>
<a href='/guides'>Read</a>
<a href='/watch'>
Watch
<span className='new-item' />
</a>
</div>
<div className='ml-3 align-items-center d-none d-md-flex'>
<iframe src="https://ghbtns.com/github-btn.html?user=kamranahmedse&repo=developer-roadmap&type=star&count=true&size=large" frameBorder="0" scrolling="0" width="190px" height="30px"></iframe>
</div>
</div>
</HeaderWrap>
);
export default SiteNav;

View File

@@ -1,61 +0,0 @@
import styled from 'styled-components';
export const HeaderWrap = styled.div`
padding: 15px 0;
font-size: 18px;
.top-row {
display: flex;
align-items: center;
> span {
display: flex;
margin-left: 12px;
}
.github-button {
font-size: 16px;
color: #666;
padding-left: 10px;
}
}
.brand img {
padding: 5px 0;
height: 50px;
text-decoration: none;
border-radius: 2px;
}
.nav-links {
a {
position: relative;
padding: 0 10px;
text-decoration: none;
font-size: 16px;
color: #666;
}
.signup {
background: #101010;
border-radius: 5px;
color: #ffffff;
padding: 7px 10px;
margin-left: 15px;
&:hover {
background: #2d2d2d;
}
}
.new-item {
position: absolute;
top: 0;
right: 0;
display: inline-block;
padding: 3px;
background: #e25712;
border-radius: 100%;
}
}
`;

View File

@@ -1,26 +0,0 @@
import React from 'react';
import { BannerWrap, CloseSponsor, SponsorLogo } from './style';
export function SponsorBanner({ onCloseBanner = () => null }) {
return (
<div className='row'>
<div className='col p-0'>
<BannerWrap
href={`https://www.youtube.com/channel/UCA0H2KIWgWTwpTFjSxp0now?sub_confirmation=1`}
target='_blank'
className='alert alert-info'
>
<SponsorLogo src='/sponsors/youtube.svg' />
We now have a youtube channel. <span className='d-none d-sm-inline-block'>Subscribe for the video content.</span>
<CloseSponsor
onClick={(e) => {
e.preventDefault();
onCloseBanner();
}}
className='close'>&times;</CloseSponsor>
</BannerWrap>
</div>
</div>
);
}

View File

@@ -1,49 +0,0 @@
import styled from 'styled-components';
export const BannerWrap = styled.a`
margin-bottom: 0;
//background: #101010;
//color: white;
background: #ffe0b2;
color: #d8362a;
font-weight: 600;
border: 0;
border-radius: 0;
padding: 10px 15px;
display: block;
text-align: center;
transition: all 200ms;
&:hover {
background: #ffd698;
color: #bd2015;
text-decoration: none;
}
`;
export const SponsorLogo = styled.img`
height: 20px;
margin-right: 10px;
position: relative;
top: -2px;
`;
export const EmojiWrap = styled.img`
height: 18px;
position: relative;
top: -1px;
margin: 0 6px 0 6px;
`;
export const CloseSponsor = styled.span`
text-shadow: none;
margin-right: 15px;
position: relative;
top: -2px;
color: #101010;
&:hover {
opacity: 1;
color: #101010;
}
`;

View File

@@ -0,0 +1,31 @@
import { Flex, Link, Text } from '@chakra-ui/react';
import YouTubeLogo from '../components/icons/youtube.svg';
import siteConfig from '../content/site.json';
export function StickyBanner() {
return (
<Flex as={Link}
href={siteConfig.url.youtube}
bg={'yellow.300'}
color='gray.900'
// bg={'teal.900'}
// color='gray.300'
alignItems='center'
position='fixed'
left={0}
right={0}
zIndex={999}
justifyContent='center'
py='8px'
_hover={{ textDecoration: 'none', bg: 'yellow.400' }}
// _hover={{ textDecoration: 'none', bg: 'teal.800', color: 'gray.100' }}
target='_blank'
>
<YouTubeLogo style={{ height: '20px', display: 'inline-block', marginRight: '7px' }} />
<Text as='span' fontWeight={500} fontSize='14px'>
<Text as='span'>We now have a YouTube Channel. <Text as='span' d={['none', 'inline']}>Subscribe for the video
content.</Text></Text>
</Text>
</Flex>
);
}

View File

@@ -1,22 +0,0 @@
import styled from 'styled-components';
export const TosPage = styled.div`
h2 {
font-weight: 700;
margin-bottom: 16px;
}
h4 {
margin-top: 25px;
margin-bottom: 10px;
font-weight: 600;
}
p {
color: #999;
}
a {
color: #333;
}
`;

Some files were not shown because too many files have changed in this diff Show More