diff --git a/www/gatsby-node.js b/www/gatsby-node.js index c9645b5bf9041..a274256641e24 100644 --- a/www/gatsby-node.js +++ b/www/gatsby-node.js @@ -620,7 +620,7 @@ exports.onCreateNode = ({ node, actions, getNode, reporter }) => { exports.onCreatePage = ({ page, actions }) => { // add lists of featured items to Ecosystem page - if (page.path === `/ecosystem/`) { + if (page.path === `/ecosystem/` || page.path === `/`) { const { createPage, deletePage } = actions const oldPage = Object.assign({}, page) diff --git a/www/src/assets/ecosystem.svg b/www/src/assets/ecosystem.svg index d3ff6c233c86a..b62de54f963ff 100644 --- a/www/src/assets/ecosystem.svg +++ b/www/src/assets/ecosystem.svg @@ -1,4 +1,4 @@ - + diff --git a/www/src/components/button.js b/www/src/components/button.js index 2779adf82a25c..7cdc5983fafd9 100644 --- a/www/src/components/button.js +++ b/www/src/components/button.js @@ -18,6 +18,7 @@ const Button = ({ small, tiny, secondary, + ondark, ...rest }) => { const Tag = components[tag || `link`] @@ -36,6 +37,7 @@ const Button = ({ ...(large && buttonStyles.large), ...(small && buttonStyles.small), ...(tiny && buttonStyles.tiny), + ...(ondark && buttonStyles.ondark), }, } diff --git a/www/src/components/cards.js b/www/src/components/cards.js index cc906fb7aa4de..a76cb36a3f0c5 100644 --- a/www/src/components/cards.js +++ b/www/src/components/cards.js @@ -11,6 +11,7 @@ const Cards = ({ children }) => ( borderRadius: presets.radiusLg, boxShadow: `0 5px 20px rgba(25, 17, 34, 0.1)`, transform: `translateZ(0)`, + width: `100%`, }} > {children} diff --git a/www/src/components/ecosystem/ecosystem-board.js b/www/src/components/ecosystem/ecosystem-board.js index 3f9f528ee530d..0e0b6c0b8d6c7 100644 --- a/www/src/components/ecosystem/ecosystem-board.js +++ b/www/src/components/ecosystem/ecosystem-board.js @@ -5,6 +5,10 @@ import styled from "react-emotion" import EcosystemSection from "./ecosystem-section" import presets from "../../utils/presets" +import { + setupScrollersObserver, + unobserveScrollers, +} from "../../utils/scrollers-observer" const EcosystemBoardRoot = styled(`div`)` display: flex; @@ -21,68 +25,12 @@ const EcosystemBoardRoot = styled(`div`)` ` class EcosystemBoard extends Component { - observer - observerTargets = [] - componentDidMount() { - if (typeof window.IntersectionObserver !== `undefined`) { - this.setupObserver() - } + setupScrollersObserver() } componentWillUnmount() { - if (typeof window.IntersectionObserver !== `undefined`) { - this.observerTargets.forEach(target => this.observer.unobserve(target)) - } - } - - setupObserver = () => { - const options = { rootMargin: `0px`, threshold: [1] } - this.observer = new IntersectionObserver(this.handleIntersect, options) - this.observerTargets = Array.from( - document.querySelectorAll(`.featuredItems`) - ) - - this.observerTargets.forEach(target => this.observer.observe(target)) - } - - handleIntersect = (entries, observer) => { - entries.forEach(entry => { - const target = entry.target - - if (entry.intersectionRatio > 0) { - setTimeout( - () => this.turnOnLeadScroll({ target, duration: 1000, distance: 20 }), - 250 - ) - this.observer.unobserve(target) - } - }) - } - - turnOnLeadScroll = ({ target, duration, distance }) => { - let startTime = null - - function animation(currentTime) { - if (startTime === null) { - startTime = currentTime - } - - const timeElapsed = currentTime - startTime - const getDistanceToScroll = ease(timeElapsed, 0, distance, duration) - - target.scroll({ top: 0, left: getDistanceToScroll }) - - if (timeElapsed < duration) { - requestAnimationFrame(animation) - } - } - - function ease(t, b, c, d) { - return -c * (t /= d) * (t - 2) + b - } - - requestAnimationFrame(animation) + unobserveScrollers() } render() { @@ -102,11 +50,11 @@ class EcosystemBoard extends Component { links={[ { label: `Browse Plugins`, to: `/plugins/` }, { - label: `Plugin Authoring`, + label: `Creating Plugins`, to: `/docs/plugin-authoring/`, secondary: true, }, - { label: `Plugin Docs`, to: `/docs/plugins/`, secondary: true }, + { label: `Using Plugins`, to: `/docs/plugins/`, secondary: true }, ]} featuredItems={plugins} /> @@ -117,7 +65,7 @@ class EcosystemBoard extends Component { icon={StartersIcon} links={[ { label: `Browse Starters`, to: `/starters/` }, - { label: `Starter Docs`, to: `/docs/starters/`, secondary: true }, + { label: `Using Starters`, to: `/docs/starters/`, secondary: true }, ]} featuredItems={starters} /> diff --git a/www/src/components/ecosystem/ecosystem-featured-item.js b/www/src/components/ecosystem/ecosystem-featured-item.js index bff9030585493..535837e77b2fb 100644 --- a/www/src/components/ecosystem/ecosystem-featured-item.js +++ b/www/src/components/ecosystem/ecosystem-featured-item.js @@ -13,20 +13,26 @@ import presets, { colors } from "../../utils/presets" const MAX_DESCRIPTION_LENGTH = 100 const EcosystemFeaturedItemRoot = styled(`li`)` - flex-basis: ${props => `calc(100% / ${props.numberOfItems})`}; - float: left; + width: 85vw; margin: 0 2px 0 0; padding: 5px; + :last-child { + margin-right: 0; + } + ${presets.Tablet} { - padding: 0; border-bottom: 1px solid ${colors.gray.superLight}; + margin: 0; + padding: 0; + width: auto; } ` -const BlockLink = styled(Link)` +export const BlockLink = styled(Link)` + background: #fff; border-radius: ${presets.radiusLg}px; - box-shadow: 0 1px 8px rgba(0, 0, 0, 0.2); + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; height: 100%; @@ -37,7 +43,9 @@ const BlockLink = styled(Link)` box-shadow: none; transition: all ${presets.animation.speedDefault} ${presets.animation.curveDefault}; + } + ${presets.Desktop} { :hover { background: ${colors.ui.whisper}; } @@ -45,8 +53,10 @@ const BlockLink = styled(Link)` ` const Header = styled(`header`)` + align-items: flex-start; display: flex; justify-content: space-between; + h3 { color: ${colors.gatsbyDark}; font-size: 1rem; @@ -54,11 +64,12 @@ const Header = styled(`header`)` } span { + align-items: center; color: ${colors.lilac}; display: flex; - align-items: center; font-size: 0.8125rem; font-family: ${options.systemFontFamily.join(`,`)}; + padding-left: 5px; svg { fill: ${colors.gray.light}; @@ -72,16 +83,15 @@ const Header = styled(`header`)` const Digest = styled(`div`)` display: flex; flex-grow: 1; - justify-content: space-between; - height: 100%; font-family: ${options.systemFontFamily.join(`,`)}; + justify-content: space-between; padding: ${rhythm(0.5)} 0 0; ` const Thumbnail = styled(`div`)` + height: 64px; padding-right: ${rhythm(2 / 3)}; margin-top: ${rhythm(1 / 12)}; - height: 64px; img { border: 1px solid ${colors.gray.superLight}; @@ -95,7 +105,7 @@ const Description = styled(`p`)` margin: 0; ` -const EcosystemFeaturedItem = ({ item, numberOfItems }) => { +const EcosystemFeaturedItem = ({ item, className }) => { const { slug, name, @@ -114,7 +124,7 @@ const EcosystemFeaturedItem = ({ item, numberOfItems }) => { } return ( - +

{name}

@@ -145,7 +155,7 @@ const EcosystemFeaturedItem = ({ item, numberOfItems }) => { EcosystemFeaturedItem.propTypes = { item: PropTypes.object.isRequired, - numberOfItems: PropTypes.number.isRequired, + className: PropTypes.string, } export default EcosystemFeaturedItem diff --git a/www/src/components/ecosystem/ecosystem-featured-items.js b/www/src/components/ecosystem/ecosystem-featured-items.js index e8198d7468503..aa8c822090737 100644 --- a/www/src/components/ecosystem/ecosystem-featured-items.js +++ b/www/src/components/ecosystem/ecosystem-featured-items.js @@ -2,17 +2,18 @@ import React from "react" import PropTypes from "prop-types" import styled from "react-emotion" -import EcosystemFeaturedItem from "./ecosystem-featured-item" - import presets, { colors } from "../../utils/presets" import { rhythm, options } from "../../utils/typography" import { scrollbarStyles } from "../../utils/styles" +import { SCROLLER_CLASSNAME } from "../../utils/scrollers-observer" -const EcosystemFeaturedItemsRoot = styled(`div`)` +export const EcosystemFeaturedItemsRootBase = styled(`div`)` overflow-x: scroll; margin: ${rhythm(0.1)} -${rhythm(options.blockMarginBottom)}; -webkit-overflow-scrolling: touch; +` +const EcosystemFeaturedItemsRoot = styled(EcosystemFeaturedItemsRootBase)` ${presets.Tablet} { border-top: 1px solid ${colors.gray.superLight}; margin-top: ${rhythm(0.4)}; @@ -23,13 +24,14 @@ const EcosystemFeaturedItemsRoot = styled(`div`)` } ` -const List = styled(`ul`)` - display: flex; +export const ListBase = styled(`ul`)` + display: inline-flex; list-style: none; margin: 0; padding: 0 calc(${rhythm(options.blockMarginBottom)} - 5px) 4px; - width: ${props => `calc(80vw * ${props.numberOfItems})`}; +` +const List = styled(ListBase)` ${presets.Tablet} { flex-direction: column; padding: 0; @@ -37,25 +39,25 @@ const List = styled(`ul`)` } ` -const EcosystemFeaturedItems = ({ items }) => ( - - +const EcosystemFeaturedItems = ({ + items, + itemComponent: Item, + className = ``, +}) => ( + + {items.map(item => { const { slug } = item - return ( - - ) + return })} ) EcosystemFeaturedItems.propTypes = { - items: PropTypes.array, + items: PropTypes.array.isRequired, + itemComponent: PropTypes.func.isRequired, + className: PropTypes.string, } export default EcosystemFeaturedItems diff --git a/www/src/components/ecosystem/ecosystem-section.js b/www/src/components/ecosystem/ecosystem-section.js index 6caad8f910444..147212014581e 100644 --- a/www/src/components/ecosystem/ecosystem-section.js +++ b/www/src/components/ecosystem/ecosystem-section.js @@ -4,6 +4,7 @@ import styled from "react-emotion" import Button from "../button" import EcosystemFeaturedItems from "./ecosystem-featured-items" +import EcosystemFeaturedItem from "./ecosystem-featured-item" import { rhythm, options } from "../../utils/typography" import presets, { colors } from "../../utils/presets" @@ -17,13 +18,13 @@ const EcosystemSectionRoot = styled(`section`)` box-shadow: 0 1px 8px rgba(0, 0, 0, 0.2); border-radius: ${presets.radiusLg}px; display: flex; + flex-basis: calc(50% - 20px); flex-direction: column; flex-grow: 0; margin: 0 10px 20px; + max-height: 60vh; padding: ${rhythm(options.blockMarginBottom)}; padding-bottom: 0; - flex-basis: calc(50% - 20px); - max-height: 60vh; :last-child { flex-grow: 1; @@ -36,6 +37,7 @@ const EcosystemSectionRoot = styled(`section`)` :last-child { align-self: flex-start; + padding-bottom: ${rhythm(options.blockMarginBottom)}; } } @@ -43,51 +45,52 @@ const EcosystemSectionRoot = styled(`section`)` text-decoration: none; } ` -const Header = styled(`header`)` - align-items: flex-start; - ${presets.Tablet}: { - padding: ${rhythm(1)}; - padding: 10px; - background: yellow; - }; +export const Header = styled(`header`)` + align-items: flex-start; ` const Title = styled(`h1`)` + align-items: center; color: ${colors.gatsby}; display: flex; font-size: 1.25rem; + line-height: 1; margin: 0; - padding-bottom: ${rhythm(0.5)}; + margin-bottom: ${rhythm(0.25)}; + min-height: 32px; span { margin: 0 0.3rem 0 -0.1rem; } ` + const Icon = styled(`span`)` display: block; - width: 32px; height: 32px; + width: 32px; ` + const SubTitle = styled(`h2`)` color: ${colors.lilac}; font-size: 0.875rem; font-weight: 300; letter-spacing: 0.05em; margin: 0; + margin-top: ${rhythm(1)}; text-transform: uppercase; ` const Description = styled(`p`)` + color: ${colors.gray.lightCopy}; font-family: ${options.systemFontFamily.join(`,`)}; font-size: 0.8125rem; - color: ${colors.gray.lightCopy}; ` const Actions = styled(`div`)` display: flex; flex-wrap: wrap; - margin: -4px 0 ${rhythm(1)}; + margin-top: -${rhythm(1 / 4)}; > a { margin: 4px 8px 4px 0; @@ -101,33 +104,35 @@ const EcosysteSection = ({ icon, links, featuredItems, + className, }) => ( - +
- - - {icon && <Icon dangerouslySetInnerHTML={{ __html: icon }} />} - <span>{title}</span> - - {description} - - {links.map((item, idx) => { - const { to, label, secondary } = item - - return ( - - ) - })} - - + + {icon && <Icon dangerouslySetInnerHTML={{ __html: icon }} />} + <span>{title}</span> + + {description} + + {links.map((item, idx) => { + const { to, label, secondary } = item + + return ( + + ) + })} + {subTitle && {subTitle}}
{featuredItems && featuredItems.length > 0 && ( - + )}
) @@ -135,6 +140,7 @@ const EcosysteSection = ({ EcosysteSection.propTypes = { title: PropTypes.string.isRequired, description: PropTypes.string.isRequired, + className: PropTypes.string, subTitle: PropTypes.string, icon: PropTypes.string, links: PropTypes.array, diff --git a/www/src/components/homepage/homepage-ecosystem.js b/www/src/components/homepage/homepage-ecosystem.js new file mode 100644 index 0000000000000..981ade872c3f4 --- /dev/null +++ b/www/src/components/homepage/homepage-ecosystem.js @@ -0,0 +1,221 @@ +import React from "react" +import PropTypes from "prop-types" +import styled from "react-emotion" + +import ArrowForwardIcon from "react-icons/lib/md/arrow-forward" + +import HomepageSection from "./homepage-section" +import EcosystemSection from "../ecosystem/ecosystem-section" +import { + EcosystemFeaturedItemsRootBase, + ListBase as EcosystemFeaturedItemsListBase, +} from "../ecosystem/ecosystem-featured-items" +import EcosystemFeaturedItem, { + BlockLink as FeaturedItemBlockLink, +} from "../ecosystem/ecosystem-featured-item" + +import { EcosystemIcon } from "../../assets/mobile-nav-icons" +import { PluginsIcon, StartersIcon } from "../../assets/ecosystem-icons" + +import { rhythm, options } from "../../utils/typography" +import presets, { colors } from "../../utils/presets" + +import { SCROLLER_CLASSNAME } from "../../utils/scrollers-observer" + +const Sections = styled(`div`)` + display: flex; + flex-direction: column; + + ${presets.Tablet} { + flex-direction: row; + margin: 0 -8px; + } + + ${presets.Desktop} { + margin: 0 1.5rem 0 2.5rem; + } +` + +const Section = styled(EcosystemSection)` + box-shadow: 0 1px 8px rgba(0, 0, 0, 0.2); + border-radius: ${presets.radiusLg}px; + margin-bottom: ${rhythm(presets.gutters.default / 2)}; + padding: ${rhythm(options.blockMarginBottom)}; + + ${presets.Tablet} { + margin: 0 8px 0px; + padding: ${rhythm(options.blockMarginBottom)}; + + :last-child { + align-self: stretch; + } + } +` + +const SubTitle = styled(`h3`)` + color: ${colors.lemon}; + font-size: 1.2rem; + margin-top: 2rem; + + ${presets.Tablet} { + margin-left: 3rem; + } + + ${presets.Desktop} { + margin-left: 6rem; + } +` + +const FeaturedItems = styled(EcosystemFeaturedItemsRootBase)` + margin: 0 -${rhythm(presets.gutters.default / 2)}; + + ${presets.Desktop} { + margin: 0; + margin-left: calc(3rem - (${rhythm(options.blockMarginBottom)})); + margin-right: 1rem; + overflow-x: auto; + } + + ${presets.Hd} { + margin-right: 3rem; + } +` + +const FeaturedItemsList = styled(EcosystemFeaturedItemsListBase)` + padding: 0 calc(${rhythm(options.blockMarginBottom)} - 7px) 0; + + ${presets.Desktop} { + flex-wrap: wrap; + margin: 0; + width: 100%; + } +` + +const FeaturedItem = styled(EcosystemFeaturedItem)` + margin: 0 6px 6px 0; + + ${presets.Tablet} { + border-bottom: none; + margin: 0 6px 6px 0; + padding: 5px; + width: 320px; + } + + ${presets.Desktop} { + flex-basis: 30%; + + :nth-child(4) { + margin-left: 8%; + } + } + + ${FeaturedItemBlockLink} { + padding-left: calc(${rhythm(3 / 4)} + 1.1rem); + position: relative; + + /* this ovveride the .main-body a style*/ + box-shadow: none; + font-weight: normal; + + ${presets.Tablet} { + border-radius: ${presets.radiusLg}px; + } + + ${presets.Desktop} { + :hover { + background: ${colors.ui.whisper}; + } + } + + :before { + background: ${props => + props.item.type === `Starter` ? colors.skyLight : colors.accentLight}; + border-radius: ${presets.radiusLg}px 0 0 ${presets.radiusLg}px; + bottom: 0; + content: ""; + left: 0; + position: absolute; + top: 0; + width: 1.1rem; + } + + :after { + bottom: 0; + content: "${props => props.item.type}"; + color: ${props => + props.item.type === `Starter` ? colors.skyDark : colors.accentDark}; + font-family: ${options.headerFontFamily.join(`,`)}; + font-size: 0.8rem; + left: 0; + letter-spacing: 0.05em; + position: absolute; + transform: rotate(-90deg) translate(-0.5em, -0); + transform-origin: top left; + } + } +` + +const HomepageEcosystem = ({ featuredItems }) => ( + + +
+ +
+
+ + Some of our recent favorites + + + {featuredItems.map(item => { + const { slug } = item + return + })} + + + +) + +HomepageEcosystem.propTypes = { + featuredItems: PropTypes.array.isRequired, +} + +export default HomepageEcosystem diff --git a/www/src/components/homepage/homepage-section.js b/www/src/components/homepage/homepage-section.js new file mode 100644 index 0000000000000..04f02b86be6e2 --- /dev/null +++ b/www/src/components/homepage/homepage-section.js @@ -0,0 +1,145 @@ +import React from "react" +import PropTypes from "prop-types" +import styled from "react-emotion" + +import Button from "../button" + +import { rhythm, options } from "../../utils/typography" +import { vP } from "../gutters" +import presets, { colors } from "../../utils/presets" + +const ICON_SIZE = `32px` + +const HomepageSectionRoot = styled(`section`)` + background: ${props => (props.inverse ? colors.gatsby : `#fff`)}; + color: ${props => (props.inverse ? colors.ui.light : colors.gatsbyDark)}; + margin: 0 -${rhythm(presets.gutters.default / 2)}; + padding: ${rhythm(2)} ${rhythm(presets.gutters.default / 2)}; + width: calc(100% + ${rhythm(presets.gutters.default)}); + + ${presets.Hd} { + margin: 0 -${vP}; + width: calc(100% + (${vP} * 2)); + } + + ${presets.VHd} { + padding: ${rhythm(2)} 5%; + } +` +const Header = styled(`header`)` + ${presets.Tablet} { + margin-left: 3rem; + max-width: 30rem; + } + + ${presets.Desktop} { + margin-left: 6rem; + } +` + +const Name = styled(`h3`)` + align-items: center; + color: ${props => (props.inverse ? colors.ui.light : colors.lilac)}; + display: flex; + font-size: 1rem; + font-weight: normal; + margin: 0; + margin-left: calc(${ICON_SIZE} * -0.2); + margin-bottom: 0.5em; + + ${presets.Tablet} { + margin-left: calc(${ICON_SIZE} * -1.2); + } +` + +const Icon = styled(`span`)` + display: block; + + ${presets.Tablet} { + margin-right: calc(${ICON_SIZE} / 5); + } + + svg { + height: ${ICON_SIZE}; + stroke: ${props => (props.inverse ? colors.ui.light : colors.lilac)}; + width: ${ICON_SIZE}; + } +` + +const Title = styled(`h1`)` + color: ${props => (props.inverse ? colors.lemon : colors.gatsby)}; + font-size: 1.75rem; + margin: 0; + margin-bottom: 0.5em; +` + +const Introduction = styled(`p`)` + color: ${props => (props.inverse ? colors.ui.light : colors.gatsbyDark)}; + font-size: 1.125rem; + font-family: ${options.headerFontFamily.join(`,`)}; + margin-bottom: 0; +` + +const Actions = styled(`div`)` + display: flex; + flex-wrap: wrap; + margin-top: -${rhythm(1 / 4)}; + + > a { + margin: ${rhythm(1.2)} 0 ${rhythm(1.5)}; + } +` + +const HomepageSection = ({ + children, + sectionName, + sectionIcon, + title, + introduction, + inverseStyle, + links, +}) => ( + +
+ {sectionName && ( + + {sectionIcon && ( + + )} + {sectionName} + + )} + {title && {title}} + {introduction && ( + {introduction} + )} + + {links.map((item, idx) => { + const { to, label, icon: Icon } = item + + return ( + + ) + })} + +
+ {children} +
+) + +HomepageSection.propTypes = { + children: PropTypes.node.isRequired, + sectionName: PropTypes.string, + sectionIcon: PropTypes.string, + title: PropTypes.string, + introduction: PropTypes.string, + links: PropTypes.array, + inverseStyle: PropTypes.bool, +} + +export default HomepageSection diff --git a/www/src/data/ecosystem/featured-items.yaml b/www/src/data/ecosystem/featured-items.yaml index 77bdd0084b273..4e72292b07302 100644 --- a/www/src/data/ecosystem/featured-items.yaml +++ b/www/src/data/ecosystem/featured-items.yaml @@ -2,9 +2,9 @@ starters: - "/gatsby-starter-blog/" - "/gatsby-starter-netlify-cms/" - - "/gatsby-advanced-blog/" - "/gatsby-advanced-starter/" - - "/gatsby-starter-lumen/" + - "/gatsby-starter-default/" + - "/gatsby-material-starter/" # use name to pick a plugin plugins: - gatsby-plugin-react-helmet diff --git a/www/src/pages/index.js b/www/src/pages/index.js index 9c7d63c64edb6..620134ea78c2d 100644 --- a/www/src/pages/index.js +++ b/www/src/pages/index.js @@ -19,10 +19,73 @@ import FuturaParagraph from "../components/futura-paragraph" import Button from "../components/button" import TechWithIcon from "../components/tech-with-icon" import EmailCaptureForm from "../components/email-capture-form" +import HomepageEcosystem from "../components/homepage/homepage-ecosystem" +import { + setupScrollersObserver, + unobserveScrollers, +} from "../utils/scrollers-observer" class IndexRoute extends React.Component { + componentDidMount() { + setupScrollersObserver() + } + + componentWillUnmount() { + unobserveScrollers() + } + + combineEcosystemFeaturedItems = ({ starters, plugins, numFeatured = 3 }) => + new Array(numFeatured) + .fill(undefined) + .reduce( + (merged, _, index) => merged.concat([starters[index], plugins[index]]), + [] + ) + render() { - const blogPosts = this.props.data.allMarkdownRemark + const { + data: { + allMarkdownRemark: blogPosts, + allStartersYaml: { edges: startersData }, + allNpmPackage: { edges: pluginsData }, + }, + } = this.props + + const starters = startersData.map(item => { + const { + node: { + fields: { + starterShowcase: { slug, name, description, stars }, + }, + childScreenshot: { + screenshotFile: { + childImageSharp: { fixed: thumbnail }, + }, + }, + }, + } = item + + return { + slug: `/starters${slug}`, + name, + description, + stars, + thumbnail, + type: `Starter`, + } + }) + + const plugins = pluginsData.map(item => { + item.node.type = `Plugin` + + return item.node + }) + + const ecosystemFeaturedItems = this.combineEcosystemFeaturedItems({ + plugins, + starters, + }) + return ( @@ -48,10 +111,7 @@ class IndexRoute extends React.Component { padding: rhythm(presets.gutters.default / 2), flex: `0 0 100%`, maxWidth: `100%`, - [presets.Hd]: { - padding: vP, - paddingTop: 0, - }, + [presets.Hd]: { padding: vP, paddingTop: 0 }, }} >
- -

Curious yet?

@@ -161,14 +219,16 @@ class IndexRoute extends React.Component {
+ + + +
Latest from the Gatsby blog @@ -229,7 +287,10 @@ class IndexRoute extends React.Component { export default IndexRoute export const pageQuery = graphql` - query { + query IndexRouteQuery( + $featuredStarters: [String]! + $featuredPlugins: [String]! + ) { file(relativePath: { eq: "gatsby-explanation.png" }) { childImageSharp { fluid(maxWidth: 870) { @@ -254,5 +315,42 @@ export const pageQuery = graphql` } } } + allStartersYaml( + filter: { + fields: { starterShowcase: { slug: { in: $featuredStarters } } } + } + ) { + edges { + node { + fields { + starterShowcase { + slug + description + stars + name + } + } + childScreenshot { + screenshotFile { + childImageSharp { + fixed(width: 64, height: 64) { + ...GatsbyImageSharpFixed_noBase64 + } + } + } + } + } + } + } + allNpmPackage(filter: { name: { in: $featuredPlugins } }) { + edges { + node { + slug + name + description + humanDownloadsLast30Days + } + } + } } ` diff --git a/www/src/utils/colors.js b/www/src/utils/colors.js index a017c93397289..e814a01573a84 100644 --- a/www/src/utils/colors.js +++ b/www/src/utils/colors.js @@ -5,6 +5,7 @@ const colors = { // @see https://www.figma.com/file/J6IYJEtdRmwJQOrcZ2DfvxDD/Gatsby gatsby: `#663399`, // was #744c9e gatsbyDark: `#442266`, + lemon: `#ffdf37`, lilac: `#8c65b3`, lavender: `#b190d5`, wisteria: `#ccb2e5`, @@ -13,6 +14,10 @@ const colors = { accent: `#ffb238`, // "Mustard", success: `#37b635`, warning: `#ec1818`, + accentLight: `#ffeccd`, + accentDark: `#9e6100`, + skyLight: `#dcfffd`, + skyDark: `#0a75c2`, ui: { border: `#ede7f3`, bright: `#e0d6eb`, diff --git a/www/src/utils/scrollers-observer.js b/www/src/utils/scrollers-observer.js new file mode 100644 index 0000000000000..8be840f568e68 --- /dev/null +++ b/www/src/utils/scrollers-observer.js @@ -0,0 +1,60 @@ +let observer +let scrollers = [] + +export const SCROLLER_CLASSNAME = `scrollerWithLead` + +export const setupScrollersObserver = () => { + if (typeof window.IntersectionObserver !== `undefined`) { + const options = { rootMargin: `0px`, threshold: [1] } + observer = new IntersectionObserver(handleIntersect, options) + + scrollers = Array.from(document.querySelectorAll(`.${SCROLLER_CLASSNAME}`)) + + scrollers.forEach(scroller => observer.observe(scroller)) + } +} + +export const unobserveScrollers = () => { + if (typeof window.IntersectionObserver !== `undefined`) { + scrollers.forEach(scroller => observer.unobserve(scroller)) + } +} + +const handleIntersect = (entries, observer) => { + entries.forEach(entry => { + const target = entry.target + + if (entry.intersectionRatio > 0.5) { + setTimeout( + () => turnOnLeadScroll({ target, duration: 1000, distance: 20 }), + 250 + ) + observer.unobserve(target) + } + }) +} + +const turnOnLeadScroll = ({ target, duration, distance }) => { + let startTime = null + + function animation(currentTime) { + if (startTime === null) { + startTime = currentTime + } + + const timeElapsed = currentTime - startTime + const getDistanceToScroll = ease(timeElapsed, 0, distance, duration) + + target.scroll({ top: 0, left: getDistanceToScroll }) + + if (timeElapsed < duration) { + requestAnimationFrame(animation) + } + } + + function ease(t, b, c, d) { + return -c * (t /= d) * (t - 2) + b + } + + requestAnimationFrame(animation) +} diff --git a/www/src/utils/styles.js b/www/src/utils/styles.js index 6464761c265b0..8b63f90ae5ee2 100644 --- a/www/src/utils/styles.js +++ b/www/src/utils/styles.js @@ -107,6 +107,7 @@ export const buttonStyles = { padding: `${rhythm(1 / 5)} ${rhythm(1 / 3)}`, }, }, + ondark: { border: `1px solid ${colors.ui.light}` }, } export const svgStyles = {