Skip to content

Commit

Permalink
Implement DocsPage and BlogPost template components
Browse files Browse the repository at this point in the history
This includes styled components like `PaperSheet` for a uniform docs
design. The `MDXProvider` has been configured to map custom HTML
components to default Markdown elements and a wrapper component for doc
pages has been implemented to define specific styles like "Inter UI"
font family.

GH-129
  • Loading branch information
arcticicestudio committed Mar 3, 2019
1 parent 8dc497e commit 7344faa
Show file tree
Hide file tree
Showing 7 changed files with 500 additions and 0 deletions.
103 changes: 103 additions & 0 deletions src/components/templates/blog/BlogPost.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]>
* Copyright (C) 2018-present Sven Greb <[email protected]>
*
* Project: Nord Docs
* Repository: https://github.com/arcticicestudio/nord-docs
* License: MIT
*/

import React from "react";
import MDXRenderer from "gatsby-mdx/mdx-renderer";
import { MDXProvider } from "@mdx-js/tag";
import { graphql } from "gatsby";
import camelCase from "camelcase";

import BaseLayout from "layouts/core/BaseLayout";
import Section, { Content } from "containers/core/Section";

import PageTypoHead from "../shared/PageTypoHead";
import mappedHtmlElementComponents from "../shared/mappedHtmlElementComponents";
import { blogPostTemplatePropTypes } from "../shared/propTypes";

/**
* A template for blog posts with custom HTML components and injected props that can be used in the MDX document.
*
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.10.0
* @see https://mdxjs.com
* @see https://github.com/ChristopherBiscardi/gatsby-mdx
*
*/
const BlogPost = ({ data: { mdx, images }, location: { pathname }, ...passProps }) => {
/*
* Make each image available as image prop in camelcase format consisting of the name and extension of the file.
* Example: "snow-mountain.png" -> "snowMountainPng"
*/
const blogPostImages = {};
images?.edges.forEach(({ node }) => {
const { extension, name } = node;
const imageName = camelCase(`${name}.${extension}`);
blogPostImages[imageName] = node.childImageSharp;
});

/*
* Shorten the object key path for content image props by deconstructing the "childImageSharp" property.
* The images can then be used by their index: `props.contentImages[0].fluid`
*/
const blogPostContentImages = [];
if (mdx.frontmatter?.contentImages) {
mdx.frontmatter.contentImages.forEach(({ childImageSharp }) => blogPostContentImages.push(childImageSharp));
}

return (
<BaseLayout pathName={pathname}>
<Section>
<Content as="article" centered>
<PageTypoHead fontScale={9} headline={mdx.frontmatter?.title} subline={mdx.frontmatter?.introduction} />
<MDXProvider components={mappedHtmlElementComponents}>
<MDXRenderer
contentImages={blogPostContentImages}
heroImage={mdx.frontmatter?.heroImage?.childImageSharp}
images={blogPostImages}
{...passProps}
>
{mdx.code.body}
</MDXRenderer>
</MDXProvider>
</Content>
</Section>
</BaseLayout>
);
};

BlogPost.propTypes = blogPostTemplatePropTypes;

export const pageQuery = graphql`
query($id: String!, $relativeDirectory: String!) {
images: allFile(
filter: { relativeDirectory: { eq: $relativeDirectory }, extension: { regex: "/(png|jpe?g)/" } }
sort: { fields: [name], order: ASC }
) {
edges {
node {
childImageSharp {
...contentMdxDocumentImageFluid
}
extension
name
}
}
}
mdx(id: { eq: $id }) {
code {
body
}
id
...contentBlogPostFrontmatter
}
}
`;

export default BlogPost;
100 changes: 100 additions & 0 deletions src/components/templates/docs/DocsPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]>
* Copyright (C) 2018-present Sven Greb <[email protected]>
*
* Project: Nord Docs
* Repository: https://github.com/arcticicestudio/nord-docs
* License: MIT
*/

import React from "react";
import MDXRenderer from "gatsby-mdx/mdx-renderer";
import { MDXProvider } from "@mdx-js/tag";
import { graphql } from "gatsby";
import camelCase from "camelcase";

import { WaveFooter } from "atoms/core/vectors/divider";
import BaseLayout from "layouts/core/BaseLayout";
import Section, { Content } from "containers/core/Section";

import mappedHtmlElementComponents from "../shared/mappedHtmlElementComponents";
import { docsPageTemplatePropTypes } from "../shared/propTypes";
import PaperSheet from "./styled/PaperSheet";
import PageTypoHead from "../shared/PageTypoHead";

/**
* A template for docs pages with custom HTML components and injected props that can be used in the MDX document.
*
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.10.0
* @see https://mdxjs.com
* @see https://github.com/ChristopherBiscardi/gatsby-mdx
*
*/
const DocsPage = ({ data: { mdx, images }, location: { pathname }, ...passProps }) => {
/*
* Make each image available as image prop in camelcase format consisting of the name and extension of the file.
* Example: "snow-mountain.png" -> "snowMountainPng"
*/
const blogPostImages = {};
images?.edges.forEach(({ node }) => {
const { extension, name } = node;
const imageName = camelCase(`${name}.${extension}`);
blogPostImages[imageName] = node.childImageSharp;
});

/* Shorten the object key path for content image props by deconstructing the "childImageSharp" property. */
const blogPostContentImages = [];
if (mdx.frontmatter?.contentImages) {
mdx.frontmatter.contentImages.forEach(({ childImageSharp }) => blogPostContentImages.push(childImageSharp));
}

return (
<BaseLayout headerVariant="tertiary" pathName={pathname}>
<Section variant="tertiary">
<Content as="article" centered>
<MDXProvider components={mappedHtmlElementComponents}>
<PageTypoHead fontScale={6.5} headline={mdx.frontmatter?.title} subline={mdx.frontmatter?.subline} />
<PaperSheet>
<MDXRenderer contentImages={blogPostContentImages} images={blogPostImages} {...passProps}>
{mdx.code.body}
</MDXRenderer>
</PaperSheet>
</MDXProvider>
</Content>
<WaveFooter />
</Section>
</BaseLayout>
);
};

DocsPage.propTypes = docsPageTemplatePropTypes;

export const pageQuery = graphql`
query($id: String!, $relativeDirectory: String!) {
images: allFile(
filter: { relativeDirectory: { eq: $relativeDirectory }, extension: { regex: "/(png|jpe?g)/" } }
sort: { fields: [name], order: ASC }
) {
edges {
node {
childImageSharp {
...contentMdxDocumentImageFluid
}
extension
name
}
}
}
mdx(id: { eq: $id }) {
code {
body
}
id
...contentDocsPageFrontmatter
}
}
`;

export default DocsPage;
50 changes: 50 additions & 0 deletions src/components/templates/docs/styled/PaperSheet.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]>
* Copyright (C) 2018-present Sven Greb <[email protected]>
*
* Project: Nord Docs
* Repository: https://github.com/arcticicestudio/nord-docs
* License: MIT
*/

import styled from "styled-components";
import { rgba } from "polished";

import { colors, media, motion, themedMode, MODE_BRIGHT_SNOW_FLURRY, MODE_DARK_NIGHT_FROST } from "styles/theme";
import { baseBackgroundColor } from "containers/core/shared/styles";

const dropShadowColorAmbientLight = themedMode({
[MODE_BRIGHT_SNOW_FLURRY]: rgba(colors.shadow.base[MODE_BRIGHT_SNOW_FLURRY], 0.1),
[MODE_DARK_NIGHT_FROST]: rgba(colors.shadow.base[MODE_DARK_NIGHT_FROST], 0.1)
});

const dropShadowColorDirectLight = themedMode({
[MODE_BRIGHT_SNOW_FLURRY]: rgba(colors.shadow.base[MODE_BRIGHT_SNOW_FLURRY], 0.25),
[MODE_DARK_NIGHT_FROST]: rgba(colors.shadow.base[MODE_DARK_NIGHT_FROST], 0.25)
});

/**
* A styled paper sheet component.
*
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.10.0
*/
const PaperSheet = styled.div`
background-color: ${baseBackgroundColor};
overflow: hidden;
width: 100%;
margin: 0 auto;
padding: 0 1em;
border-radius: 8px;
box-shadow: 0 4px 6px ${dropShadowColorDirectLight}, 0 5px 7px ${dropShadowColorAmbientLight};
transition: box-shadow ${motion.speed.duration.transition.base.themeModeSwitch}ms ease-in-out,
background-color ${motion.speed.duration.transition.base.themeModeSwitch}ms ease-in-out;
${media.phoneLandscape`
max-width: 95%;
padding: 2em 4em;
`};
`;

export default PaperSheet;
84 changes: 84 additions & 0 deletions src/components/templates/shared/PageTypoHead.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]>
* Copyright (C) 2018-present Sven Greb <[email protected]>
*
* Project: Nord Docs
* Repository: https://github.com/arcticicestudio/nord-docs
* License: MIT
*/

import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";

import { H1, P } from "atoms/core/html-elements";
import { colors, media, motion, ms, themedMode, MODE_BRIGHT_SNOW_FLURRY, MODE_DARK_NIGHT_FROST } from "styles/theme";

const fontColor = themedMode({
[MODE_BRIGHT_SNOW_FLURRY]: colors.nord2,
[MODE_DARK_NIGHT_FROST]: colors.font.base[MODE_DARK_NIGHT_FROST]
});

const Headline = styled(H1)`
font-size: ${({ fontScale }) => ms(fontScale)};
color: ${fontColor};
margin-bottom: 0.8em;
font-weight: 500;
transition: color ${motion.speed.duration.transition.base.themeModeSwitch}ms ease-in-out;
${media.tabletLandscape`
font-size: ${({ fontScale }) => ms(fontScale + 1)};
`};
${media.desktop`
font-size: ${({ fontScale }) => ms(fontScale + 2)};
`};
`;

const Subline = styled(P)`
max-width: 80%;
margin: 1em auto;
font-size: ${({ fontScale }) => ms(fontScale / 3.2)};
${media.tabletLandscape`
max-width: 70%;
`};
${media.desktop`
max-width: 60%;
`};
`;

const Wrapper = styled.div`
max-width: 90%;
text-align: center;
margin: 0 auto 4em auto;
`;

/**
* A typography header consisting of a large headline and a subline.
*
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.10.0
*/
const PageTypoHead = ({ headline, fontScale, subline, ...passProps }) =>
headline || subline ? (
<Wrapper {...passProps}>
{headline && <Headline fontScale={fontScale}>{headline}</Headline>}
{subline && <Subline fontScale={fontScale}>{subline}</Subline>}
</Wrapper>
) : null;

PageTypoHead.propTypes = {
fontScale: PropTypes.number.isRequired,
headline: PropTypes.string,
subline: PropTypes.string
};

PageTypoHead.defaultProps = {
headline: "",
subline: ""
};

export default PageTypoHead;
26 changes: 26 additions & 0 deletions src/components/templates/shared/Wrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]>
* Copyright (C) 2018-present Sven Greb <[email protected]>
*
* Project: Nord Docs
* Repository: https://github.com/arcticicestudio/nord-docs
* License: MIT
*/

import styled from "styled-components";

import { typography } from "styles/theme";

/**
* A styled component for the `wrapper` mapping of the supported `MDXProvider` tags.
* It is used to override global/inherited styles for docs pages and blog posts.
*
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.10.0
*/
const Wrapper = styled.div`
font-family: ${typography.typefaces.straight};
`;

export default Wrapper;
Loading

0 comments on commit 7344faa

Please sign in to comment.