feat(client): shinny new landing page 🎉 (#39400)

Co-authored-by: Mrugesh Mohapatra <1884376+raisedadead@users.noreply.github.com>
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Ahmad Abdolsaheb
2020-08-24 21:06:40 +03:00
committed by GitHub
parent 99585403f9
commit d9dad10f43
36 changed files with 714 additions and 252 deletions

View File

@ -7310,6 +7310,11 @@
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="
},
"eventlistener": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz",
"integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg="
},
"events": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
@ -17398,6 +17403,17 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-lazy-load": {
"version": "3.1.13",
"resolved": "https://registry.npmjs.org/react-lazy-load/-/react-lazy-load-3.1.13.tgz",
"integrity": "sha512-eAVNUn3vhNj79Iv04NOCwy/sCLyqDEhL3j9aJKV7VJuRBDg6rCiB+BIWHuG7VXJGCgb//6nX/soR8PTyWRhFvQ==",
"requires": {
"eventlistener": "0.0.1",
"lodash.debounce": "^4.0.0",
"lodash.throttle": "^4.0.0",
"prop-types": "^15.5.8"
}
},
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",

View File

@ -56,6 +56,7 @@
"react-hotkeys": "^2.0.0",
"react-identicons": "^1.1.7",
"react-instantsearch-dom": "^6.0.0-beta.0",
"react-lazy-load": "^3.1.13",
"react-monaco-editor": "^0.31.0",
"react-redux": "^5.0.7",
"react-reflex": "^3.0.18",

View File

@ -0,0 +1,34 @@
import React from 'react';
export default function LinkButton(props) {
return (
<svg
aria-hidden='true'
height='20px'
version='1.1'
viewBox='0 0 16 20'
width='18px'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
{...props}
fill='inherit'
>
<g>
<polygon
points={
'-2.68014473e-15 -1.06357708e-13 2.01917516 ' +
'-1.06357708e-13 8.99824941 9.00746464 2.01917516 ' +
'18.0149293 -2.66453526e-15 18.0149293 7.00955027 9'
}
/>
<polygon
points={
'7.99971435 -1.06357708e-13 10.0188895 ' +
'-1.06357708e-13 16.9979638 9.00746464 ' +
'10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9'
}
/>
</g>
</svg>
);
}

View File

@ -7,7 +7,7 @@ function SpotifyLogo(props) {
return (
<svg
id='amazon-logo'
viewBox='93.907 230.751 1400 572'
viewBox='93.907 250 1350 472'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
{...props}

View File

@ -7,7 +7,7 @@ function AppleLogo(props) {
return (
<svg
id='apple-logo'
viewBox='170 0 1000 572'
viewBox='450 0 500 650'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
{...props}

View File

@ -1,17 +1,19 @@
/* eslint-disable max-len */
import React from 'react';
import PropTypes from 'prop-types';
const propTypes = {};
const propTypes = { fill: PropTypes.string };
function AsSeenLogo(props) {
const fill = props.fill === 'dark' ? 'var(--gray-75)' : 'var(--gray-15)';
return (
<svg
id='featured-logos'
viewBox='0 0 1700 340'
viewBox='0 40 1700 200'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
{...props}
fill='var(--quaternary-color)'
fill={fill}
>
<defs>
<path

View File

@ -7,7 +7,7 @@ function GoogleLogo(props) {
return (
<svg
id='google-logo'
viewBox='1635 200 1400 600'
viewBox='1635 200 1185 600'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
{...props}

View File

@ -7,7 +7,7 @@ function MicrosoftLogo(props) {
return (
<svg
id='microsoft-logo'
viewBox='939.813 1145 1400 572'
viewBox='939.813 1185 1400 472'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
{...props}

View File

@ -7,7 +7,7 @@ function SpotifyLogo(props) {
return (
<svg
id='spotify-logo'
viewBox='3018 235.379 1579 572'
viewBox='3018 235.379 1525 572'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
{...props}

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

View File

@ -23,8 +23,6 @@ import {
} from '../redux';
import { Spacer, Loader, FullWidthRow } from '../components/helpers';
import './showuser.css';
const propTypes = {
email: PropTypes.string,
isSignedIn: PropTypes.bool,

View File

@ -1,4 +0,0 @@
/* remove bootstrap margin*/
.row {
margin: 0;
}

View File

@ -43,7 +43,7 @@ function Intro({
<Row>
<Col sm={10} smOffset={1} xs={12}>
<Spacer />
<h1 className='text-center big-heading'>
<h1 className='text-center '>
{name ? `Welcome back, ${name}.` : `Welcome to freeCodeCamp.org`}
</h1>
<Spacer />
@ -101,9 +101,7 @@ function Intro({
<Row>
<Col sm={8} smOffset={2} xs={12}>
<Spacer />
<h1 className='big-heading'>
Welcome to freeCodeCamp's curriculum.
</h1>
<h1>Welcome to freeCodeCamp's curriculum.</h1>
<Spacer size={1} />
</Col>
<IntroDescription />

View File

@ -9,14 +9,6 @@
white-space: pre-line;
}
.logo-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-content: center;
}
.logo-row h2 {
height: 35px;
padding: 0 10px 0 10px;
@ -27,11 +19,6 @@
padding-left: 0px;
}
#learn-app-wrapper h2.medium-heading {
margin-bottom: 50px;
text-align: center;
}
.quote-partial .blockquote {
font-size: 1.3rem;
border: none;

View File

@ -15,7 +15,7 @@ function CurrentChallengeLink({ children, isLargeBtn }) {
if (isLargeBtn) {
classNames = 'btn btn-lg btn-primary btn-block';
} else {
classNames = 'btn btn-cta-big btn-primary btn-block';
classNames = 'btn btn-primary btn-block';
}
return (
<a className={classNames} href={`${apiLocation}${currentChallengeApi}`}>

View File

@ -0,0 +1,65 @@
import React from 'react';
import './image-loader.css';
import LazyLoad from 'react-lazy-load';
import PropTypes from 'prop-types';
const propTypes = {
alt: PropTypes.string,
className: PropTypes.string,
height: PropTypes.number,
loadedClassName: PropTypes.string,
loadingClassName: PropTypes.string,
src: PropTypes.string,
width: PropTypes.number
};
class ImageLoader extends React.Component {
// initial state: image loaded stage
state = {
loaded: false
};
// define our loading and loaded image classes
static defaultProps = {
className: '',
loadingClassName: 'img-loading',
loadedClassName: 'img-loaded'
};
// image onLoad handler to update state to loaded
onLoad = () => {
this.setState(() => ({ loaded: true }));
};
render() {
let {
className,
loadedClassName,
loadingClassName,
alt,
src,
width,
height
} = this.props;
className = `${className} ${
this.state.loaded ? loadedClassName : loadingClassName
}`;
return (
<LazyLoad
debounce={false}
height={height}
offsetVertical={100}
width={width}
>
{/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */}
<img alt={alt} className={className} onLoad={this.onLoad} src={src} />
{/* eslint-enable jsx-a11y/no-noninteractive-element-interactions */}
</LazyLoad>
);
}
}
ImageLoader.propTypes = propTypes;
export default ImageLoader;

View File

@ -0,0 +1,23 @@
@keyframes fadeInImg {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.img-loading {
opacity: 0;
width: 100%;
height: auto;
}
.img-loaded {
animation: fadeInImg cubic-bezier(0.23, 1, 0.32, 1) 1;
position: relative;
opacity: 0;
animation-fill-mode: forwards;
animation-duration: 0.7s;
animation-delay: 0.1s;
}

View File

@ -6,3 +6,4 @@ export { default as SkeletonSprite } from './SkeletonSprite';
export { default as Spacer } from './Spacer';
export { default as Link } from './Link';
export { default as CurrentChallengeLink } from './CurrentChallengeLink';
export { default as ImageLoader } from './ImageLoader';

View File

@ -0,0 +1,18 @@
import React from 'react';
import { Col, Row } from '@freecodecamp/react-bootstrap';
import { AsFeatureLogo } from '../../../assets/images/components';
const AsSeenIn = () => (
<Row className='as-seen-in'>
<Col sm={8} smOffset={2} xs={12}>
<div className='text-center'>
<h1 className='big-heading'>As seen in:</h1>
<AsFeatureLogo fill='light' />
</div>
</Col>
</Row>
);
AsSeenIn.displayName = 'AsSeenIn';
export default AsSeenIn;

View File

@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import Login from '../../Header/components/Login';
const propTypes = {
page: PropTypes.string
};
const BigCallToAction = ({ page }) => (
<Login block={true} data-test-label={`${page}-big-cta`}>
{page === 'landing'
? "Get started (it's free)"
: "Sign in to save your progress (it's free)"}
</Login>
);
BigCallToAction.displayName = 'BigCallToAction';
BigCallToAction.propTypes = propTypes;
export default BigCallToAction;

View File

@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Col, Row } from '@freecodecamp/react-bootstrap';
import { uniq } from 'lodash';
import { Spacer, Link } from '../../helpers';
import LinkButton from '../../../assets/icons/LinkButton';
import BigCallToAction from './BigCallToAction';
const propTypes = {
nodes: PropTypes.array,
page: PropTypes.string
};
const Certifications = ({ nodes, page }) => {
const superBlocks = uniq(nodes.map(node => node.superBlock)).filter(
cert => cert !== 'Coding Interview Prep'
);
return (
<Row className='certification-section'>
<Col md={8} mdOffset={2} sm={10} smOffset={1} xs={12}>
<h1 className='big-heading'>Earn free verified certifications in:</h1>
<ul data-test-label='certifications'>
{superBlocks.map((superBlock, i) => (
<li key={i}>
<Link
className='btn link-btn btn-lg'
state={{ superBlock: superBlock }}
to={`/learn`}
>
{superBlock}
<LinkButton />
</Link>
</li>
))}
</ul>
<Spacer />
<BigCallToAction page={page} />
<Spacer />
</Col>
</Row>
);
};
Certifications.displayName = 'Certifications';
Certifications.propTypes = propTypes;
export default Certifications;

View File

@ -1,26 +0,0 @@
import React from 'react';
import {
AmazonLogo,
AppleLogo,
MicrosoftLogo,
SpotifyLogo,
GoogleLogo
} from '../../../assets/images';
import './companyLogos.css';
export const CompanyLogos = () => {
return (
<div className='logo-row'>
<GoogleLogo />
<MicrosoftLogo />
<AppleLogo />
<SpotifyLogo />
<AmazonLogo />
</div>
);
};
CompanyLogos.displayName = 'CompanyLogos';
export default CompanyLogos;

View File

@ -0,0 +1,78 @@
import React from 'react';
import PropTypes from 'prop-types';
import Media from 'react-responsive';
import { Col, Row } from '@freecodecamp/react-bootstrap';
import { Spacer, ImageLoader } from '../../helpers';
import wideImg from '../../../assets/images/landing/wide-image.png';
import Login from '../../Header/components/Login';
import {
AmazonLogo,
AppleLogo,
MicrosoftLogo,
SpotifyLogo,
GoogleLogo
} from '../../../assets/images/components';
const propTypes = {
page: PropTypes.string
};
const LARGE_SCREEN_SIZE = 1200;
function landingTop({ page }) {
const landingImageSection = (
<Media minWidth={LARGE_SCREEN_SIZE}>
<Spacer size={2} />
<ImageLoader
alt='Freecodecamp students at a local study'
className='landing-page-image'
height={442}
src={wideImg}
width={750}
/>
<p className='text-center caption'>
freeCodeCamp students at a local study group in South Korea.
</p>
</Media>
);
const BigCallToAction = (
<Login block={true} data-test-label={`${page}-big-cta`}>
{page === 'landing'
? "Get started (it's free)"
: "Sign in to save your progress (it's free)"}
</Login>
);
return (
<div className='landing-top'>
<Row>
<Spacer />
<Col lg={8} lgOffset={2} sm={10} smOffset={1} xs={12}>
<h1 className='big-heading' data-test-label={`${page}-header`}>
Learn to code at home.
</h1>
<h1 className='big-heading '>Build projects.</h1>
<h1 className='big-heading'>Earn certifications.</h1>
<h2>
Since 2014, more than 40,000 freeCodeCamp.org graduates have gotten
jobs at tech companies including:
</h2>
<div className='logo-row'>
<AppleLogo />
<GoogleLogo />
<MicrosoftLogo />
<AmazonLogo />
<SpotifyLogo />
</div>
<Spacer />
{BigCallToAction}
{landingImageSection}
<Spacer />
</Col>
</Row>
</div>
);
}
landingTop.displayName = 'LandingTop';
landingTop.propTypes = propTypes;
export default landingTop;

View File

@ -0,0 +1,61 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ImageLoader } from '../../helpers';
import testimonialsMeta from '../landingMeta';
const propTypes = {
page: PropTypes.string
};
const Testimonials = () => {
const campers = Object.keys(testimonialsMeta);
return (
<div className='testimonials'>
<h1 className='big-heading text-center'>
Here is what our alumini say about freeCodeCamp:
</h1>
<div className='testimonials-row' data-test-label='testimonial-cards'>
{campers.map((camper, i) => {
let {
name,
country,
position,
company,
testimony
} = testimonialsMeta[camper];
let imageSource = require(`../../../assets/images/landing/${camper}.png`);
return (
<div className='testimonial-card' key={i}>
<div className='testimonial-card-header'>
<ImageLoader
alt={`${name}`}
className='testimonial-image'
src={imageSource}
/>
</div>
<div className='testimonials-footer'>
<div className='testimonial-meta'>
<p>
{' '}
<strong>{name}</strong> in {country}
</p>
<p>
{position} at <strong>{company}</strong>
</p>
</div>
<div className='testimony'>{testimony}</div>
</div>
</div>
);
})}
</div>
</div>
);
};
Testimonials.displayName = 'Testimonals';
Testimonials.propTypes = propTypes;
export default Testimonials;

View File

@ -1,32 +0,0 @@
.logo-row svg {
height: 25px;
margin: 10px;
}
@media (min-width: 343px) {
.logo-row svg {
height: 35px;
}
}
@media (min-width: 380px) {
.logo-row svg {
height: 40px;
}
}
@media (min-width: 480px) {
.logo-row svg {
height: 25px;
}
}
@media (min-width: 600px) {
.logo-row svg {
height: 40px;
margin: 10px;
}
.logo-row #apple-logo {
height: 35px;
}
}

View File

@ -1,108 +1,49 @@
import React, { Fragment } from 'react';
import { Grid, Row, Col } from '@freecodecamp/react-bootstrap';
import { Grid } from '@freecodecamp/react-bootstrap';
import Helmet from 'react-helmet';
import PropTypes from 'prop-types';
import { Link } from 'gatsby';
import { uniq, partition } from 'lodash';
import { Spacer } from '../helpers';
import Login from '../Header/components/Login';
import CompanyLogos from './components/CompanyLogos';
import { AsFeatureLogo } from '../../assets/images';
import { graphql, useStaticQuery } from 'gatsby';
import Testimonials from './components/Testimonials';
import LandingTop from './components/LandingTop';
import Certifications from './components/Certifications';
import AsSeenIn from './components/AsSeenIn';
import './landing.css';
import '../Map/map.css';
const propTypes = {
nodes: PropTypes.array
page: PropTypes.string
};
const BigCallToAction = () => (
<Row>
<Col md={6} mdOffset={3} sm={8} smOffset={2} xs={12}>
<Login block={true} data-test-label='landing-big-cta'>
Get started (it's free)
</Login>
</Col>
</Row>
);
export const Landing = ({ page = 'landing' }) => {
const data = useStaticQuery(graphql`
query certifications {
challenges: allChallengeNode(
filter: { isHidden: { eq: false } }
sort: { fields: [superOrder, order, challengeOrder] }
) {
nodes {
superBlock
}
}
}
`);
const AsFeaturedSection = () => (
<Row>
<Col sm={8} smOffset={2} xs={12}>
<div className='text-center'>
<h2 className='medium-heading'>As Featured In:</h2>
<AsFeatureLogo />
</div>
</Col>
</Row>
);
export const Landing = ({ nodes }) => {
const [superBlocks, rest] = partition(
uniq(nodes.map(node => node.superBlock)),
name => name !== 'Coding Interview Prep'
);
const interviewPrep = rest[0];
return (
<Fragment>
<Helmet>
<title>Learn to code at home | freeCodeCamp.org</title>
</Helmet>
<main className='landing-page'>
<Spacer />
<Grid>
<Row>
<Col sm={10} smOffset={1} xs={12}>
<h1
className='big-heading text-center'
data-test-label='landing-header'
>
Welcome to freeCodeCamp.org
</h1>
<Spacer />
<h2 className='medium-heading text-center'>
Learn to code at home.
</h2>
<h2 className='medium-heading text-center'>Build projects.</h2>
<h2 className='medium-heading text-center'>
Earn certifications.
</h2>
<h2 className='medium-heading text-center'>
Since 2014, more than 40,000 freeCodeCamp.org graduates have
gotten jobs at tech companies including:
</h2>
<CompanyLogos />
</Col>
</Row>
<Spacer />
<BigCallToAction />
<Spacer />
<AsFeaturedSection />
<Row>
<Col sm={10} smOffset={1} xs={12}>
<h2 className='medium-heading'>Certifications:</h2>
<ul>
{superBlocks.map((superBlock, i) => (
<li className={'superblock'} key={i}>
<Link state={{ superBlock: superBlock }} to={`/learn`}>
<h2 className='medium-heading'>{superBlock}</h2>
</Link>
</li>
))}
</ul>
<Spacer />
<h2 className='medium-heading'>Additional Learning:</h2>
<ul>
<li>
<Link state={{ superBlock: interviewPrep }} to={`/learn`}>
<h2 className='medium-heading'>{interviewPrep}</h2>
</Link>
</li>
</ul>
</Col>
</Row>
<Spacer />
<LandingTop page={page} />
</Grid>
<Grid fluid={true}>
<AsSeenIn />
</Grid>
<Grid>
<Testimonials />
<Certifications nodes={data.challenges.nodes} page={page} />
</Grid>
</main>
</Fragment>

View File

@ -9,21 +9,203 @@
white-space: pre-line;
}
.logo-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-content: center;
margin-bottom: 10px;
}
.landing-page ul {
list-style: none;
padding-left: 0px;
}
#featured-logos {
margin-top: 10px;
max-width: 600px;
}
.landing-top h1:first-child {
margin-top: 0px;
}
.landing-page h1,
.landing-page h2,
.landing-page p {
font-family: 'Lato', sans-serif;
}
p.caption {
margin: 10px 0 0 0;
font-size: 0.8rem;
color: var(--quaternary-color);
}
.as-seen-in {
background-color: var(--gray-75);
color: var(--gray-15);
}
.as-seen-in h1 {
color: var(--gray-15);
}
.landing-page h1 {
margin-bottom: 40px;
}
.campers-images {
display: none !important;
}
@media (min-width: 1200px) {
.campers-images {
display: inline-block !important;
width: 100%;
height: 100%;
}
}
.logo-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-content: center;
margin: 10px 0;
}
.logo-row svg {
height: 40px;
}
@media (min-width: 370px) {
.logo-row svg {
margin: 5px 15px;
}
}
@media (min-width: 480px) {
.logo-row svg {
height: 25px;
margin: 5px;
}
}
@media (min-width: 550px) {
.logo-row {
justify-content: space-between;
}
.logo-row svg {
height: 40px;
}
}
.cta-landing-section h2 {
font-size: 1.3rem;
font-weight: 400;
}
/* testimonials */
.testimonials-row {
display: flex;
flex-direction: column;
justify-content: space-around;
flex-wrap: wrap;
align-items: center;
}
.testimonials p,
.testimonials strong {
font-family: 'Lato', sans-serif;
font-size: 1.1rem;
margin: 0;
color: var(--gray-75);
}
.testimonial-meta p:last-child {
margin-top: 15px;
}
.testimonial-card {
max-width: 350px;
display: flex;
flex-direction: column;
margin: 10px 10px 50px 10px;
background: var(--gray-00);
-webkit-box-shadow: 0 3px 13px 1px rgba(0, 0, 0, 0.09);
box-shadow: 0 3px 13px 1px rgba(0, 0, 0, 0.09);
}
.testimonial-image,
.landing-page-image {
width: 100%;
}
.testimonial-meta {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 10px 0px 30px 0px;
}
.testimonial-meta p,
.testimonial-meta strong {
font-size: 1.2rem;
}
.testimonial-card-header {
height: 100%;
padding-bottom: 100%;
position: relative;
}
.testimonial-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: auto;
}
.testimonials-footer {
display: flex;
flex-direction: column;
padding: 25px;
text-align: justify;
justify-content: center;
}
.landing-top,
.as-seen-in,
.certification-section,
.testimonials {
padding: 4vw 0;
}
@media (min-width: 500px) {
.cta-landing-section h2 {
font-size: 1.5rem;
}
}
@media (min-width: 992px) {
.testimonial-card {
flex-direction: row;
width: auto;
max-width: none;
}
.testimonial-image {
width: auto;
height: 100%;
}
.testimonial-meta {
padding: 0px 0px 30px;
}
.testimonial-meta p,
.testimonial-meta strong {
font-size: 1.3rem;
}
.testimonials-footer {
padding: 40px;
}
.testimonial-card-header {
height: 350px;
min-width: 350px;
padding-bottom: 0;
}
}

View File

@ -0,0 +1,48 @@
import React from 'react';
export default {
Shawn: {
name: 'Shawn Wang',
country: 'Singapore',
position: 'Software Engineer',
company: 'Amazon',
testimony: (
<p>
"It's scary to change careers. I only gained confidence that I could
code by working through the hundreds of hours of free lessons on
freeCodeCamp. Within a year I had a six-figure job as a Software
Engineer.
<strong> freeCodeCamp changed my life.</strong>"
</p>
)
},
Sarah: {
name: 'Sarah Chima',
country: 'Nigeria',
position: 'Software Engineer',
company: 'ChatDesk',
testimony: (
<p>
<strong>freeCodeCamp was the gateway to my career </strong>
as a software developer. The well-structured curriculum took my coding
knowledge from a total beginner level to a very confident level. It was
everything I needed to land my first dev job at an amazing company."
</p>
)
},
Emma: {
name: 'Emma Bostian',
country: 'Sweden',
position: 'Software Engineer',
company: 'Spotify',
testimony: (
<p>
"I've always struggled with learning JavaScript. I've taken many courses
but freeCodeCamp's course was the one which stuck. Studying JavaScript
as well as data structures and algorithms on{' '}
<strong>freeCodeCamp gave me the skills</strong> and confidence I needed
to land my dream job as a software engineer at Spotify."
</p>
)
}
};

View File

@ -11,7 +11,7 @@ body {
.btn-cta-big {
max-height: 100%;
font-size: 1.3rem;
font-size: 1.5rem;
white-space: normal;
width: 100%;
}
@ -64,25 +64,13 @@ p {
}
.big-heading {
font-size: 1.5rem !important;
font-size: 2.5rem !important;
overflow-wrap: break-word;
}
.medium-heading {
font-size: 1.25rem !important;
font-weight: normal;
}
.medium-heading.text-center {
margin-bottom: 50px;
}
@media (max-width: 500px) {
.big-heading {
font-size: 1.2rem !important;
}
.medium-heading {
font-size: 1.1rem !important;
font-size: 1.5rem !important;
}
h1 {
font-size: 1.3rem;
@ -99,6 +87,12 @@ p {
}
}
@media (max-width: 1199px) {
.btn-cta-big {
font-size: 1.3rem;
}
}
.text-center {
text-align: center !important;
}
@ -160,6 +154,7 @@ input[type='submit'],
color: var(--secondary-color);
border-radius: 0px;
text-decoration: none;
white-space: pre-line;
}
button:hover,
@ -257,6 +252,33 @@ fieldset[disabled] .btn-primary.focus {
color: inherit;
}
.link-btn {
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
font-size: 1rem;
text-align: left;
}
.link-btn.btn-lg svg {
height: 100%;
min-height: 20px;
min-width: 16px;
margin-left: 5px;
}
.link-btn:hover svg {
fill: var(--quaternary-background);
}
@media (min-width: 700px) {
.link-btn {
font-size: 1.1rem;
}
}
.button-group .btn:not(:last-child) {
margin-bottom: 10px;
}
@ -494,27 +516,3 @@ blockquote .small {
#search::placeholder {
color: var(--secondary-color);
}
/* checkbox */
.checkbox-inline input[type='checkbox'] {
margin-left: -30px;
}
.checkbox-inline {
padding-left: 30px;
}
input[type='checkbox'] {
height: 21px;
width: 21px;
}
/* general page styling */
.default-page-wrapper {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
padding: 20px;
}

View File

@ -1,16 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { graphql } from 'gatsby';
import Landing from '../components/landing';
import { AllChallengeNode } from '../redux/propTypes';
export const IndexPage = ({
data: {
allChallengeNode: { nodes }
}
}) => {
return <Landing nodes={nodes} />;
export const IndexPage = () => {
return <Landing />;
};
const propTypes = {
@ -23,16 +18,3 @@ IndexPage.propTypes = propTypes;
IndexPage.displayName = 'IndexPage';
export default IndexPage;
export const query = graphql`
query MyQuery {
allChallengeNode(
filter: { isHidden: { eq: false } }
sort: { fields: [superOrder, order, challengeOrder] }
) {
nodes {
superBlock
}
}
}
`;

View File

@ -32,7 +32,7 @@ export default function layoutSelector({ element, props }) {
</DefaultLayout>
);
}
if (/^\/donation(\/.*)*|^\/donate(\/.*)*/.test(pathname)) {
if (/^\/donation(\/.*)*|^\/$|^\/donate(\/.*)*/.test(pathname)) {
return (
<DefaultLayout pathname={pathname} useTheme={false}>
{element}

View File

@ -1,16 +1,41 @@
/* global cy */
const selectors = {
heading: "[data-test-label='landing-header']",
callToAction: "[data-test-label='landing-big-cta']"
callToAction: "[data-test-label='landing-big-cta']",
certifications: "[data-test-label='certifications']",
testimonials: "[data-test-label='testimonial-cards']",
landingPageImage: '.landing-page-image'
};
describe('Landing page', function() {
it('renders', function() {
cy.visit('/');
cy.title().should('eq', 'Learn to code at home | freeCodeCamp.org');
cy.contains(selectors.heading, 'Welcome to freeCodeCamp.org');
cy.contains(selectors.heading, 'Learn to code at home.');
cy.contains(selectors.callToAction, "Get started (it's free)");
cy.get(selectors.callToAction).should('have.length', 2);
});
it('has a visible large image on large viewports', function() {
cy.viewport(1200, 660)
.get(selectors.landingPageImage)
.should('be.visible');
cy.viewport(1199, 660)
.get(selectors.landingPageImage)
.should('not.be.visible');
});
it('has 10 certifications', function() {
cy.get(selectors.certifications)
.children()
.its('length')
.should('eq', 10);
});
it('has 3 testimonial cards', function() {
cy.get(selectors.testimonials)
.children()
.its('length')
.should('eq', 3);
});
});