diff --git a/src/components/Header/Header.jsx b/src/components/Header/Header.jsx
index ad301e5..3068a72 100644
--- a/src/components/Header/Header.jsx
+++ b/src/components/Header/Header.jsx
@@ -1,13 +1,14 @@
-import React from 'react';
-import styled from 'styled-components';
-
-import { QUERIES, WEIGHTS } from '../../constants';
-import Logo from '../Logo';
-import Icon from '../Icon';
-import UnstyledButton from '../UnstyledButton';
-import SuperHeader from '../SuperHeader';
-import MobileMenu from '../MobileMenu';
-import VisuallyHidden from '../VisuallyHidden';
+import React from "react";
+import styled from "styled-components";
+
+import { QUERIES } from "../../constants";
+import Logo from "../Logo";
+import Icon from "../Icon";
+import UnstyledButton from "../UnstyledButton";
+import SuperHeader from "../SuperHeader";
+import MobileMenu from "../MobileMenu";
+import VisuallyHidden from "../VisuallyHidden";
+import NavLink from "../Navlink";
const Header = () => {
const [showMobileMenu, setShowMobileMenu] = React.useState(false);
@@ -81,6 +82,12 @@ const DesktopNav = styled.nav`
}
`;
+const HoverDesktopNav = styled.nav`
+ display: none;
+ &:hover:
+
+`;
+
const MobileActions = styled.div`
display: none;
@@ -114,16 +121,4 @@ const Filler = styled.div`
}
`;
-const NavLink = styled.a`
- font-size: 1.125rem;
- text-transform: uppercase;
- text-decoration: none;
- color: var(--color-gray-900);
- font-weight: ${WEIGHTS.medium};
-
- &:first-of-type {
- color: var(--color-secondary);
- }
-`;
-
export default Header;
diff --git a/src/components/MobileMenu/MobileMenu.jsx b/src/components/MobileMenu/MobileMenu.jsx
index f402df3..917f8b0 100644
--- a/src/components/MobileMenu/MobileMenu.jsx
+++ b/src/components/MobileMenu/MobileMenu.jsx
@@ -1,12 +1,12 @@
-import React from 'react';
-import styled from 'styled-components';
-import * as Dialog from '@radix-ui/react-dialog';
+import React from "react";
+import styled, { keyframes } from "styled-components";
+import * as Dialog from "@radix-ui/react-dialog";
-import { QUERIES, WEIGHTS } from '../../constants';
+import { QUERIES, WEIGHTS } from "../../constants";
-import UnstyledButton from '../UnstyledButton';
-import Icon from '../Icon';
-import VisuallyHidden from '../VisuallyHidden';
+import UnstyledButton from "../UnstyledButton";
+import Icon from "../Icon";
+import VisuallyHidden from "../VisuallyHidden";
const MobileMenu = ({ isOpen, onDismiss }) => {
return (
@@ -14,57 +14,92 @@ const MobileMenu = ({ isOpen, onDismiss }) => {
-
-
- Dismiss menu
-
-
- Mobile navigation
- Mobile navigation
-
-
-
-
+
+
+
+ Dismiss menu
+
+
+ Mobile navigation
+ Mobile navigation
+
+
+
+
+
);
};
+const fadeIn = keyframes`
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+`;
+const slideIn = keyframes`
+ from {
+ transform: translateX(100%);
+ }
+ to {
+ transform: translateX(0%);
+ }
+`;
+
const Overlay = styled(Dialog.Overlay)`
position: fixed;
inset: 0;
background: var(--color-backdrop);
+ animation: ${fadeIn} 750ms;
`;
const Content = styled(Dialog.Content)`
+ --overfill: 16px;
position: fixed;
top: 0;
right: 0;
bottom: 0;
- background: white;
- width: 300px;
+ display: flex;
+ flex-direction: column;
+ width: calc(300px + var(--overfill));
+ margin-right: calc(var(--overfill) * -1);
height: 100%;
padding: 24px 32px;
+ background: white;
+
+ @media (prefers-reduced-motion: no-preference) {
+ animation: ${slideIn} 500ms both cubic-bezier(0, 0.6, 0.32, 1.06);
+ animation-delay: 200ms;
+ }
+`;
+
+const Wrapper = styled.div`
display: flex;
flex-direction: column;
+ height: 100%;
+ animation: ${fadeIn} 600ms both;
+ animation-delay: 400ms;
`;
const CloseButton = styled(UnstyledButton)`
position: absolute;
top: 10px;
- right: 0;
+ right: var(--overfill);
padding: 16px;
`;
diff --git a/src/components/Navlink/Navlink.jsx b/src/components/Navlink/Navlink.jsx
new file mode 100644
index 0000000..5ab3a49
--- /dev/null
+++ b/src/components/Navlink/Navlink.jsx
@@ -0,0 +1,60 @@
+import React from "react";
+import styled from "styled-components";
+
+import { WEIGHTS } from "../../constants";
+
+const NavLink = ({ children, ...delegated }) => {
+ return (
+
+ {children}
+ {children}
+
+ );
+};
+
+const Wrapper = styled.a`
+ position: relative;
+ display: block;
+ font-size: 1.125rem;
+ text-transform: uppercase;
+ text-decoration: none;
+ color: var(--color-gray-900);
+ font-weight: ${WEIGHTS.medium};
+ /* Text slide-up effect */
+ overflow: hidden;
+
+ &:first-of-type {
+ color: var(--color-secondary);
+ }
+`;
+
+const Text = styled.span`
+ display: block;
+ transform: translateY(var(--translate-from));
+ transition: transform 500ms;
+
+ @media (prefers-reduced-motion: no-preference) {
+ ${Wrapper}:hover & {
+ transition: transform 250ms;
+ transform: translateY(var(--translate-to));
+ }
+ }
+`;
+
+const MainText = styled(Text)`
+ --translate-from: 0%;
+ --translate-to: -100%;
+`;
+
+const HoverText = styled(Text)`
+ --translate-from: 100%;
+ --translate-to: 0%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ font-weight: ${WEIGHTS.bold};
+`;
+
+export default NavLink;
diff --git a/src/components/Navlink/index.js b/src/components/Navlink/index.js
new file mode 100644
index 0000000..ef10bab
--- /dev/null
+++ b/src/components/Navlink/index.js
@@ -0,0 +1 @@
+export { default } from "./Navlink";
diff --git a/src/components/ShoeCard/ShoeCard.jsx b/src/components/ShoeCard/ShoeCard.jsx
index b33d371..00c6a13 100644
--- a/src/components/ShoeCard/ShoeCard.jsx
+++ b/src/components/ShoeCard/ShoeCard.jsx
@@ -1,9 +1,9 @@
-import React from 'react';
-import styled from 'styled-components';
+import React from "react";
+import styled from "styled-components";
-import { WEIGHTS } from '../../constants';
-import { formatPrice, pluralize, isNewShoe } from '../../utils';
-import Spacer from '../Spacer';
+import { WEIGHTS } from "../../constants";
+import { formatPrice, pluralize, isNewShoe } from "../../utils";
+import Spacer from "../Spacer";
const ShoeCard = ({
slug,
@@ -36,30 +36,26 @@ const ShoeCard = ({
- {variant === 'on-sale' && Sale}
- {variant === 'new-release' && (
- Just released!
- )}
+ {variant === "on-sale" && Sale}
+ {variant === "new-release" && Just released!}
{name}
{formatPrice(price)}
- {pluralize('Color', numOfColors)}
- {variant === 'on-sale' ? (
+ {pluralize("Color", numOfColors)}
+ {variant === "on-sale" ? (
{formatPrice(salePrice)}
) : undefined}
@@ -73,15 +69,29 @@ const Link = styled.a`
color: inherit;
`;
-const Wrapper = styled.article``;
+const Wrapper = styled.article`
+ position: relative;
+`;
const ImageWrapper = styled.div`
- position: relative;
+ border-radius: 16px 16px 4px 4px;
+ overflow: hidden;
`;
const Image = styled.img`
+ display: block;
width: 100%;
- border-radius: 16px 16px 4px 4px;
+ transform-origin: 50% 75%;
+ transition: transform 500ms;
+ will-change: transform;
+ @media (hover: hover) and (prefers-reduced-motion: no-preference) {
+ ${Link}:hover &,
+ ${Link}:focus & {
+ transform: scale(1.1);
+ transition: transform 200ms;
+ opacity: 75%;
+ }
+ }
`;
const Row = styled.div`
@@ -92,7 +102,7 @@ const Row = styled.div`
const Name = styled.h3`
font-weight: ${WEIGHTS.medium};
- color: var(--color-gray-900);
+ color: var(--color-gray-900);\
`;
const Price = styled.span`