feat: init card and nav

This commit is contained in:
2021-01-14 17:12:40 +00:00
parent c5e8c04ddc
commit e129093113
19 changed files with 417 additions and 112 deletions

View File

@@ -5,7 +5,42 @@ module.exports = {
title: `Abc Project Design`, title: `Abc Project Design`,
description: `Easy as ABC`, description: `Easy as ABC`,
keywords: `Foo bar baz`, keywords: `Foo bar baz`,
navData: [], navData: [
{
title: "Home",
to: "/"
},
{
title: "Services",
to: "/services",
subNav: [
{
title: "Bathrooms",
to: "/services/bathrooms"
},
{
title: "Tiling",
to: "/services/tiling"
},
{
title: "Electrics",
to: "/services/electrics"
},
{
title: "Carpentry",
to: "/services/carpentry"
}
]
},
{
title: "About",
to: "/about"
},
{
title: "Contact",
to: "/contact"
}
],
reviews: [ reviews: [
{ {
name: "Rated People", name: "Rated People",

View File

@@ -1,11 +1,11 @@
import React from "react"; import React from "react";
import { Link as GatsbyLink } from "gatsby";
import Img from "gatsby-image"; import Img from "gatsby-image";
import styled from "styled-components"; import styled from "styled-components";
const Container = styled.div` const Container = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
overflow: hidden;
margin: 0 calc(var(--spacing-x-xs) * -1) var(--spacing-y-sm); margin: 0 calc(var(--spacing-x-xs) * -1) var(--spacing-y-sm);
@media (max-width: 30em) { @media (max-width: 30em) {
@@ -15,15 +15,34 @@ const Container = styled.div`
} }
`; `;
const Media = styled.div` const Media = styled.span`
padding: 0 var(--spacing-x-xs); padding: 0 var(--spacing-x-xs);
transition: transform 0.15s ease-in, filter 0.15s ease-in;
`; `;
const Content = styled.div`
const Content = styled.span`
padding: 0 var(--spacing-x-xs); padding: 0 var(--spacing-x-xs);
`; `;
export default function Card({ image, children }) { const Link = styled(GatsbyLink)`
return ( outline: 0;
text-decoration: none;
color: var(--color-black);
&:hover ${Media} {
transform: scale(1.1);
filter: brightness(110%) contrast(110%);
}
@media (max-width: 30em) {
&:nth-child(even) ${Container} {
flex-direction: row-reverse;
}
}
`;
export default function Card({ image, children, to }) {
const render = () => (
<Container> <Container>
{image && ( {image && (
<Media> <Media>
@@ -39,4 +58,5 @@ export default function Card({ image, children }) {
<Content>{children}</Content> <Content>{children}</Content>
</Container> </Container>
); );
return to ? <Link to={to}>{render()}</Link> : render();
} }

View File

@@ -10,6 +10,7 @@ const Content = styled.div`
max-width: var(--theme-max-width); max-width: var(--theme-max-width);
margin: 0 auto; margin: 0 auto;
padding: var(--spacing-y-lg) var(--spacing-x-sm); padding: var(--spacing-y-lg) var(--spacing-x-sm);
font-size: var(--font-size-sm);
text-align: center; text-align: center;
`; `;

View File

@@ -79,7 +79,7 @@ const Slogan = styled.p`
export default function HeaderLogo({ transition }) { export default function HeaderLogo({ transition }) {
const [visible, setVisible] = useState(!transition); const [visible, setVisible] = useState(!transition);
useEffect(() => transition && setVisible(true)); useEffect(() => transition && setVisible(true), [transition]);
return ( return (
<Container> <Container>
<Top visible={visible}> <Top visible={visible}>

View File

@@ -9,6 +9,15 @@ const Container = styled.button`
background-color: transparent; background-color: transparent;
padding: 4px 8px; padding: 4px 8px;
border: 0; border: 0;
cursor: pointer;
&:focus {
outline: 0;
}
@media (min-width: 30em) {
display: none;
}
`; `;
const Bar = styled.span` const Bar = styled.span`

View File

@@ -0,0 +1,47 @@
import React from "react";
import styled, { css } from "styled-components";
import NavLink from "./nav-link";
const SubList = styled.ul`
margin: 0 0 0 var(--spacing-x-md);
padding: 0;
list-style: none;
@media (min-width: 30em) {
display: none;
flex-direction: column;
position: absolute;
margin: 0;
padding: var(--spacing-y-xs) var(--spacing-x-xs);
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(2px);
}
`;
const ListItem = styled.li`
position: relative;
&:focus-within ${SubList}, &:hover ${SubList} {
@media (min-width: 30em) {
display: flex;
}
}
`;
export default function NavItem({ children, subNav, ...other }) {
const hasChildren = subNav && subNav.length > 0;
return (
<ListItem>
<NavLink {...other}>{children}</NavLink>
{hasChildren && (
<SubList>
{subNav.map((link, i) => (
<ListItem key={i}>
<NavLink to={link.to}>{link.title}</NavLink>
</ListItem>
))}
</SubList>
)}
</ListItem>
);
}

View File

@@ -1,56 +1,30 @@
import React from "react"; import React from "react";
import styled, { css } from "styled-components";
import { Link as GatsbyLink } from "gatsby"; import { Link as GatsbyLink } from "gatsby";
import styled from "styled-components";
const SubList = styled.ul` const Link = styled(GatsbyLink)`
position: absolute; display: inline-block;
display: none;
flex-direction: column;
min-width: 120px;
margin: 0;
padding: 0; padding: 0;
list-style: none; margin-bottom: var(--spacing-y-sm);
`; color: var(--color-white);
font-size: var(--font-size-lg);
text-decoration: none;
cursor: pointer;
const ListItem = styled.li` &:focus {
position: relative; outline: none;
margin: 0 var(--spacing-y-xs); }
&:hover ${SubList} { @media (min-width: 30em) {
@media (min-width: 40rem) { font-size: var(--font-size-sm);
display: flex; margin: 0 var(--spacing-x-xs);
} padding: 9px 0;
border-bottom: 2px solid
${props => (props.active ? `var(--color-white)` : `transparent`)};
} }
`; `;
const Link = styled(GatsbyLink)` export default function NavLink({ location, children, to }) {
display: block; console.log(location);
color: white; return <Link to={to}>{children}</Link>;
text-decoration: none;
padding: 5px;
margin-top: var(--spacing-y-xs);
border-bottom: 2px solid transparent;
${props =>
props.active &&
css`
border-color: var(--color-white);
`}
`;
export default function NavLink({ children, subLinks, ...other }) {
return (
<ListItem>
<Link {...other}>{children}</Link>
{subLinks && subLinks.length > 0 && (
<SubList>
{subLinks.map((link, i) => (
<Link to={link.to} key={i}>
{link.title}
</Link>
))}
</SubList>
)}
</ListItem>
);
} }

View File

@@ -1,60 +1,86 @@
import React from "react"; import React from "react";
import { Link } from "gatsby";
import styled from "styled-components"; import styled from "styled-components";
import NavLink from "./nav-link"; import NavItem from "./nav-item";
import MenuButton from "./menu-button"; import MenuButton from "./menu-button";
import LogoArrows from "../icons/logo-arrows.svg";
const Container = styled.div` const Container = styled.div`
position: ${props => (props.open ? "fixed" : "absolute")}; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0;
display: flex;
flex-direction: column-reverse;
z-index: 6; z-index: 6;
background-color: ${props =>
props.open ? `rgba(0, 0, 0, 0.8)` : `rgba(0, 0, 0, 0.4)`};
backdrop-filter: blur(2px);
transform: ${props => transform: ${props =>
props.open ? `translate3d(0, 0, 0)` : `translate3d(100%, 0, 0)`}; props.open
transition: transform 0.15s; ? `translate3d(0, 0, 0)`
: `translate3d(0, calc(-100% + 40px), 0)`};
transition: transform 0.3s ease-in-out, background-color 0.3s ease-in-out;
@media (min-width: 30em) { @media (min-width: 30em) {
bottom: auto; position: absolute;
left: 0;
display: flex; display: flex;
background-color: rgba(0, 0, 0, 0.4); flex-direction: row;
backdrop-filter: blur(2px); bottom: auto;
background-color: rgba(0, 0, 0, 0.6);
transform: none; transform: none;
transition: none; transition: none;
} }
`; `;
const Menu = styled.div` const Menu = styled.div`
position: absolute; display: flex;
top: 0; justify-content: space-between;
left: 0; align-items: stretch;
padding: var(--spacing-y-xs) var(--spacing-x-md) 0 0; color: white;
transform: translateX(-100%); margin: var(--spacing-x-xs) var(--spacing-y-sm);
`;
@media (min-width: 30em) { const Heading = styled(Link)`
display: none; display: flex;
flex-direction: column;
justify-content: space-around;
opacity: ${props => (props.visible ? 1 : 0)};
transform: opacity 0.3s ease-in;
svg {
width: 24px;
height: 12px;
&:first-of-type {
path:first-child {
fill: var(--theme-color-primary);
}
path:last-child {
fill: var(--theme-color-secondary);
}
}
&:last-of-type {
transform: rotate(180deg);
path:first-child {
fill: var(--theme-color-dark);
}
path:last-child {
fill: var(--theme-color-secondary);
}
}
} }
`; `;
const Content = styled.nav` const Content = styled.nav`
margin: 0 var(--spacing-x-sm); flex-grow: 1;
margin: var(--spacing-y-lg) var(--spacing-x-md);
@media (max-width: 30em) { @media (min-width: 30em) {
background-color: rgba(0, 0, 0, 0.4); display: flex;
} margin: 0;
`;
const Overlay = styled.div`
@media (max-width: 30em) {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 5;
display: ${props => (props.visible ? `block` : `none`)};
background-color: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(2px);
} }
`; `;
@@ -70,25 +96,25 @@ const List = styled.ul`
`; `;
export default function Nav({ data, open, setOpen }) { export default function Nav({ data, open, setOpen }) {
const handleOverlayClick = () => setOpen(false);
const handleButtonClick = () => setOpen(!open); const handleButtonClick = () => setOpen(!open);
return ( return (
<> <Container open={open}>
<Container open={open}> <Menu>
<Menu> <Heading to="/" visible={!open}>
<MenuButton open={open} onClick={handleButtonClick} /> <LogoArrows />
</Menu> <LogoArrows />
<Content> </Heading>
<List> <MenuButton open={open} onClick={handleButtonClick} />
{data.map((link, i) => ( </Menu>
<NavLink key={i} subNav={link.subNav} to={link.to}> <Content>
{link.title} <List>
</NavLink> {data.map((link, i) => (
))} <NavItem key={i} subNav={link.subNav} to={link.to}>
</List> {link.title}
</Content> </NavItem>
</Container> ))}
<Overlay visible={open} onClick={handleOverlayClick} /> </List>
</> </Content>
</Container>
); );
} }

BIN
src/images/bathroom2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

View File

@@ -1,6 +1,7 @@
import React from "react"; import React, { useState } from "react";
import { useStaticQuery, graphql } from "gatsby"; import { useStaticQuery, graphql } from "gatsby";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
import Nav from "./components/nav";
import Footer from "./components/footer"; import Footer from "./components/footer";
import { createGlobalStyle } from "styled-components"; import { createGlobalStyle } from "styled-components";
import "@fontsource/cooper-hewitt/400.css"; import "@fontsource/cooper-hewitt/400.css";
@@ -65,6 +66,7 @@ const GlobalStyle = createGlobalStyle`
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
font-family: 'Cooper Hewitt', sans-serif; font-family: 'Cooper Hewitt', sans-serif;
overflow-x: hidden;
overflow-y: ${props => (props.navOpen ? "hidden" : "auto")}; overflow-y: ${props => (props.navOpen ? "hidden" : "auto")};
} }
@@ -83,7 +85,8 @@ const GlobalStyle = createGlobalStyle`
} }
`; `;
export default function Layout({ children }) { export default function Layout({ location, children }) {
console.log(location);
const data = useStaticQuery(graphql` const data = useStaticQuery(graphql`
query HeaderQuery { query HeaderQuery {
site { site {
@@ -91,11 +94,20 @@ export default function Layout({ children }) {
title title
description description
keywords keywords
navData {
title
to
subNav {
title
to
}
}
} }
} }
} }
`); `);
const { title, description, keywords } = data.site.siteMetadata; const { title, description, keywords, navData } = data.site.siteMetadata;
const [navOpen, setNavOpen] = useState();
return ( return (
<> <>
<main> <main>
@@ -106,10 +118,11 @@ export default function Layout({ children }) {
{ name: "keywords", content: keywords } { name: "keywords", content: keywords }
]} ]}
/> />
<GlobalStyle /> <GlobalStyle navOpen={navOpen} />
{children} {children}
<Footer /> <Footer />
</main> </main>
<Nav data={navData} open={navOpen} setOpen={setNavOpen} />
</> </>
); );
} }

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { graphql } from "gatsby"; import { Link, graphql } from "gatsby";
import Layout from "../layout"; import Layout from "../layout";
import Header from "../components/header"; import Header from "../components/header";
import HeaderLogo from "../components/header-logo"; import HeaderLogo from "../components/header-logo";
@@ -9,10 +9,12 @@ export default function Index({ data }) {
return ( return (
<Layout> <Layout>
<Header image={data.headerImage.childImageSharp.fluid}> <Header image={data.headerImage.childImageSharp.fluid}>
<HeaderLogo visible={true} />
<h2>404</h2> <h2>404</h2>
</Header> </Header>
<Section variant="primary">oops.</Section> <Section>
<p>Page not found</p>
<Link to="/">Back Home</Link>
</Section>
</Layout> </Layout>
); );
} }

70
src/pages/colours.js Normal file
View File

@@ -0,0 +1,70 @@
import React from "react";
import { graphql } from "gatsby";
import Layout from "../layout";
import Header from "../components/header";
import HeaderLogo from "../components/header-logo";
import Section from "../components/section";
import Text from "../components/text";
export default function Index({ data }) {
return (
<Layout>
<Header image={data.headerImage.childImageSharp.fluid}>
<HeaderLogo>
<h1>ABC Project Design</h1>
<p>Easy as ABC</p>
</HeaderLogo>
</Header>
<Section>
<Text
style={{
"background-color": "var(--theme-color-primary)"
}}
>
--theme-color-primary: #9c0905;
</Text>
<Text
style={{
"background-color": "var(--theme-color-secondary)"
}}
>
--theme-color-secondary: #9590a8;
</Text>
<Text
style={{
"background-color": "var(--theme-color-tertiary)"
}}
>
--theme-color-tertiary: #bbcbcb;
</Text>
<Text
style={{
"background-color": "var(--theme-color-light)"
}}
>
--theme-color-light: #e5ffde;
</Text>
<Text
style={{
"background-color": "var(--theme-color-dark)",
color: "white"
}}
>
--theme-color-dark: #000a61;
</Text>
</Section>
</Layout>
);
}
export const query = graphql`
query {
headerImage: file(relativePath: { eq: "bathroom.png" }) {
childImageSharp {
fluid(maxWidth: 1920, quality: 100) {
...GatsbyImageSharpFluid_withWebp
}
}
}
}
`;

View File

@@ -4,6 +4,7 @@ import Layout from "../layout";
import Header from "../components/header"; import Header from "../components/header";
import HeaderLogo from "../components/header-logo"; import HeaderLogo from "../components/header-logo";
import Section from "../components/section"; import Section from "../components/section";
import Card from "../components/card";
import ReviewSummary from "../components/review-summary"; import ReviewSummary from "../components/review-summary";
import CustomSlick from "../components/custom-slick"; import CustomSlick from "../components/custom-slick";
import Text from "../components/text"; import Text from "../components/text";
@@ -12,7 +13,7 @@ export default function Index({ data }) {
return ( return (
<Layout> <Layout>
<Header image={data.headerImage.childImageSharp.fluid}> <Header image={data.headerImage.childImageSharp.fluid}>
<HeaderLogo /> <HeaderLogo transition={true} />
</Header> </Header>
<Section> <Section>
<Text> <Text>
@@ -25,6 +26,32 @@ export default function Index({ data }) {
operate in Essex, Hertfordshire, Kent and London. operate in Essex, Hertfordshire, Kent and London.
</Text> </Text>
</Section> </Section>
<Section grid={2}>
<Card
to="/services/bathrooms"
image={data.bathroomImage.childImageSharp.fixed}
>
Bathrooms
</Card>
<Card
to="/services/tiling"
image={data.tilingImage.childImageSharp.fixed}
>
Tiling
</Card>
<Card
to="/services/electrics"
image={data.electricsImage.childImageSharp.fixed}
>
Electrics
</Card>
<Card
to="/services/carpentry"
image={data.carpentryImage.childImageSharp.fixed}
>
Carpentry
</Card>
</Section>
<Section variant="primary"> <Section variant="primary">
<CustomSlick> <CustomSlick>
{data.site.siteMetadata.reviews.map((review, i) => ( {data.site.siteMetadata.reviews.map((review, i) => (
@@ -45,6 +72,34 @@ export const query = graphql`
} }
} }
} }
bathroomImage: file(relativePath: { eq: "service_bathroom.png" }) {
childImageSharp {
fixed(width: 96, height: 54, quality: 100) {
...GatsbyImageSharpFixed_withWebp
}
}
}
tilingImage: file(relativePath: { eq: "service_tiling.png" }) {
childImageSharp {
fixed(width: 96, height: 54, quality: 100) {
...GatsbyImageSharpFixed_withWebp
}
}
}
electricsImage: file(relativePath: { eq: "service_electrics.png" }) {
childImageSharp {
fixed(width: 96, height: 54, quality: 100) {
...GatsbyImageSharpFixed_withWebp
}
}
}
carpentryImage: file(relativePath: { eq: "service_carpentry.png" }) {
childImageSharp {
fixed(width: 96, height: 54, quality: 100) {
...GatsbyImageSharpFixed_withWebp
}
}
}
site { site {
siteMetadata { siteMetadata {
reviews { reviews {

View File

@@ -0,0 +1,26 @@
import React from "react";
import { graphql } from "gatsby";
import Layout from "../../layout";
import Header from "../../components/header";
export default function Index({ data }) {
return (
<Layout>
<Header image={data.headerImage.childImageSharp.fluid}>
<h1>Bathrooms</h1>
</Header>
</Layout>
);
}
export const query = graphql`
query {
headerImage: file(relativePath: { eq: "bathroom2.png" }) {
childImageSharp {
fluid(maxWidth: 1920, quality: 100) {
...GatsbyImageSharpFluid_withWebp
}
}
}
}
`;

View File

@@ -0,0 +1,27 @@
import React from "react";
import { graphql } from "gatsby";
import Layout from "../../layout";
import Header from "../../components/header";
export default function Index({ location, data }) {
console.log(location);
return (
<Layout>
<Header image={data.headerImage.childImageSharp.fluid}>
<h1>Services</h1>
</Header>
</Layout>
);
}
export const query = graphql`
query {
headerImage: file(relativePath: { eq: "bathroom2.png" }) {
childImageSharp {
fluid(maxWidth: 1920, quality: 100) {
...GatsbyImageSharpFluid_withWebp
}
}
}
}
`;