compoennt converted from tailwind to module scss

This commit is contained in:
Mandeep Tatla 2024-06-21 18:28:52 +09:30
parent b1b157fb88
commit 04971a5c53
12 changed files with 426 additions and 67 deletions

View File

@ -0,0 +1,16 @@
.megaMenu {
margin-top: 1rem;
display: none;
justify-content: center;
@media (min-width: 768px) {
display: flex;
}
.menuList {
font-size: 1rem;
display: flex;
gap: 1.5rem;
font-weight: 600;
}
}

View File

@ -0,0 +1,39 @@
.menuItem {
position: relative;
display: block;
}
.submenuItem {
padding: 0.5rem;
}
.menuLink {
padding-left: 0.5rem;
font-weight: bold;
text-decoration: none;
color: #757575;
text-decoration-thickness: 4px;
&:hover {
color: black;
text-decoration: underline;
}
}
.subMenu {
position: absolute;
left: 0;
top: 100%;
z-index: 10;
display: none;
width: 12rem;
background-color: white;
border-radius: 0.625rem;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
}
.dropdown {
.menuItem:hover & {
display: block;
}
}

View File

@ -1,4 +1,5 @@
import { MenuItem } from '../../lib/shopify/types';
import classes from './MegaMenu.module.scss';
import MenuItemComponent from './menu-item-component';
type MegaMenuProps = {
@ -7,8 +8,8 @@ type MegaMenuProps = {
const MegaMenu = ({ menu }: MegaMenuProps) => {
return (
<div className="mt-4 hidden justify-center md:flex">
<ul className="text-md flex gap-6 font-semibold">
<div className={classes.megaMenu}>
<ul className={classes.menuList}>
{menu.map((item) => (
<MenuItemComponent key={item.title} item={item} />
))}

View File

@ -1,5 +1,6 @@
import Link from 'next/link';
import { MenuItem } from '../../lib/shopify/types';
import styles from './MenuItemComponent.module.scss'; // Importing the SCSS module
type MenuItemProps = {
item: MenuItem;
@ -8,29 +9,17 @@ type MenuItemProps = {
const MenuItemComponent = ({ item, isSubMenu = false }: MenuItemProps) => {
return (
<li className={`group relative ${isSubMenu ? 'submenu-item p-2' : ''}`}>
<Link
href={item.path}
className="pl-2 font-semibold text-neutral-500 underline-offset-4 hover:text-black hover:underline dark:text-neutral-400 dark:hover:text-neutral-300"
>
<li className={`${styles.menuItem} ${isSubMenu ? styles.submenuItem : ''}`}>
<Link href={item.path} className={styles.menuLink}>
{item.title}
</Link>
{item.items.length > 0 && (
<ul className="absolute left-0 top-full z-10 hidden w-48 rounded-xl bg-white shadow-lg group-hover:block">
<ul className={`${styles.subMenu} ${styles.dropdown}`}>
{item.items.map((subItem) => (
<MenuItemComponent key={subItem.title} item={subItem} isSubMenu={true} />
))}
</ul>
)}
{isSubMenu && item.items.length > 0 && (
<ul className="absolute left-0 top-full z-10 hidden w-48 bg-white shadow-lg group-hover:block">
{item.items.map((subItem, index) =>
index === 2 ? (
<MenuItemComponent key={subItem.title} item={subItem} isSubMenu={true} />
) : null
)}
</ul>
)}
</li>
);
};

View File

@ -0,0 +1,128 @@
.footer {
font-size: 0.875rem;
color: #757575;
}
.footerContainer {
margin: 0 auto;
display: flex;
width: 100%;
max-width: 1536px;
flex-direction: column;
gap: 1.5rem;
border-top: 1px solid #e5e5e5;
padding: 3rem 1.5rem;
@media (min-width: 768px) {
flex-direction: row;
gap: 3rem;
padding: 3rem 1rem;
}
@media (min-width: 1320px) {
padding: 3rem 0;
}
}
.logoLink {
display: flex;
align-items: center;
gap: 0.5rem;
color: black;
@media (min-width: 768px) {
padding-top: 0.25rem;
}
}
.siteName {
text-transform: uppercase;
}
.skeletonContainer {
display: flex;
height: 11.75rem;
width: 12.5rem;
flex-direction: column;
gap: 0.5rem;
}
.skeleton {
width: 100%;
height: 1.5rem;
animation: pulse 2s infinite;
border-radius: 0.375rem;
background-color: #e5e5e5;
}
.deployContainer {
@media (min-width: 768px) {
margin-left: auto;
}
}
.deployButton {
display: flex;
height: 2rem;
align-items: center;
justify-content: center;
border-radius: 0.375rem;
border: 1px solid #e5e5e5;
background-color: white;
font-size: 0.75rem;
color: black;
}
.deployIcon {
padding: 0 0.75rem;
}
.deployDivider {
height: 100%;
border-right: 1px solid #e5e5e5;
}
.deployText {
padding: 0 0.75rem;
}
.footerBottom {
border-top: 1px solid #e5e5e5;
padding: 1.5rem 0;
}
.footerBottomContainer {
margin: 0 auto;
display: flex;
width: 100%;
max-width: 1536px;
flex-direction: column;
align-items: center;
gap: 0.25rem;
padding: 0 1rem;
@media (min-width: 768px) {
flex-direction: row;
gap: 0;
padding: 0 1rem;
}
@media (min-width: 1320px) {
padding: 0;
}
}
.divider {
margin: 0 1rem;
height: 1rem;
width: 1px;
border-left: 1px solid #a8a8a8;
@media (min-width: 768px) {
display: inline-block;
}
}
.craftedBy {
@media (min-width: 768px) {
margin-left: auto;
}
}
.craftedByLink {
color: black;
}

View File

@ -1,31 +1,32 @@
import Link from 'next/link';
import FooterMenu from 'components/layout/footer-menu';
import LogoSquare from 'components/logo-square';
import { getMenu } from 'lib/shopify';
import Link from 'next/link';
import { Suspense } from 'react';
import styles from './Footer.module.scss'; // Importing the SCSS module
const { COMPANY_NAME, SITE_NAME } = process.env;
export default async function Footer() {
const currentYear = new Date().getFullYear();
const copyrightDate = 2023 + (currentYear > 2023 ? `-${currentYear}` : '');
const skeleton = 'w-full h-6 animate-pulse rounded bg-neutral-200 dark:bg-neutral-700';
const skeleton = styles.skeleton;
const menu = await getMenu('next-js-frontend-footer-menu');
const copyrightName = COMPANY_NAME || SITE_NAME || '';
return (
<footer className="text-sm text-neutral-500 dark:text-neutral-400">
<div className="mx-auto flex w-full max-w-7xl flex-col gap-6 border-t border-neutral-200 px-6 py-12 text-sm md:flex-row md:gap-12 md:px-4 min-[1320px]:px-0 dark:border-neutral-700">
<footer className={styles.footer}>
<div className={styles.footerContainer}>
<div>
<Link className="flex items-center gap-2 text-black md:pt-1 dark:text-white" href="/">
<Link className={styles.logoLink} href="/">
<LogoSquare size="sm" />
<span className="uppercase">{SITE_NAME}</span>
<span className={styles.siteName}>{SITE_NAME}</span>
</Link>
</div>
<Suspense
fallback={
<div className="flex h-[188px] w-[200px] flex-col gap-2">
<div className={styles.skeletonContainer}>
<div className={skeleton} />
<div className={skeleton} />
<div className={skeleton} />
@ -37,28 +38,28 @@ export default async function Footer() {
>
<FooterMenu menu={menu} />
</Suspense>
<div className="md:ml-auto">
<div className={styles.deployContainer}>
<a
className="flex h-8 w-max flex-none items-center justify-center rounded-md border border-neutral-200 bg-white text-xs text-black dark:border-neutral-700 dark:bg-black dark:text-white"
className={styles.deployButton}
aria-label="Deploy on Vercel"
href="https://vercel.com/templates/next.js/nextjs-commerce"
>
<span className="px-3"></span>
<hr className="h-full border-r border-neutral-200 dark:border-neutral-700" />
<span className="px-3">Deploy</span>
<span className={styles.deployIcon}></span>
<hr className={styles.deployDivider} />
<span className={styles.deployText}>Deploy</span>
</a>
</div>
</div>
<div className="border-t border-neutral-200 py-6 text-sm dark:border-neutral-700">
<div className="mx-auto flex w-full max-w-7xl flex-col items-center gap-1 px-4 md:flex-row md:gap-0 md:px-4 min-[1320px]:px-0">
<div className={styles.footerBottom}>
<div className={styles.footerBottomContainer}>
<p>
&copy; {copyrightDate} {copyrightName}
{copyrightName.length && !copyrightName.endsWith('.') ? '.' : ''} All rights reserved.
</p>
<hr className="mx-4 hidden h-4 w-[1px] border-l border-neutral-400 md:inline-block" />
<hr className={styles.divider} />
<p>Designed in California</p>
<p className="md:ml-auto">
<a href="https://vercel.com" className="text-black dark:text-white">
<p className={styles.craftedBy}>
<a href="https://vercel.com" className={styles.craftedByLink}>
Crafted by Vercel
</a>
</p>

View File

@ -0,0 +1,53 @@
.openMenuButton {
display: flex;
height: 2.75rem;
width: 2.75rem;
align-items: center;
justify-content: center;
border-radius: 0.375rem;
border: 1px solid #e5e5e5;
color: black;
transition: color 0.3s;
@media (min-width: 768px) {
display: none;
}
}
.closeMenuButton {
margin-bottom: 1rem;
display: flex;
height: 2.75rem;
width: 2.75rem;
align-items: center;
justify-content: center;
border-radius: 0.375rem;
border: 1px solid #e5e5e5;
color: black;
transition: color 0.3s;
}
.dialogPanel {
position: fixed;
bottom: 0;
left: 0;
right: 0;
top: 0;
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
background-color: white;
padding-bottom: 1.5rem;
}
.menuItem {
padding: 0.5rem 0;
font-size: 1.25rem;
color: black;
transition: color 0.3s;
&:hover {
color: #737373;
}
}

View File

@ -0,0 +1,38 @@
.searchForm {
max-width: 550px;
position: relative;
width: 100%;
@media (min-width: 1024px) {
width: 20rem;
}
@media (min-width: 1280px) {
width: 100%;
}
}
.searchInput {
width: 100%;
border-radius: 0.5rem;
border: 1px solid #e5e5e5;
background-color: white;
padding: 0.5rem 1rem;
font-size: 0.875rem;
color: black;
&::placeholder {
color: #757575;
}
}
.searchIconContainer {
position: absolute;
right: 0;
top: 0;
margin-right: 0.75rem;
display: flex;
height: 100%;
align-items: center;
}
.searchIcon {
height: 1rem;
}

View File

@ -0,0 +1,100 @@
.navbar {
position: relative;
margin: 0 auto;
max-width: 1536px;
padding: 1rem;
@media (min-width: 1024px) {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.navbarContainer {
display: flex;
align-items: center;
justify-content: space-between;
.mobileMenu {
display: block;
flex: none;
@media (min-width: 768px) {
/* md */
display: none;
}
}
.navbarContent {
display: flex;
width: 100%;
align-items: center;
.logoContainer {
display: flex;
width: 100%;
@media (min-width: 768px) {
/* md */
width: 33.333333%; /* md:w-1/3 */
}
.logoLink {
margin-right: 0.5rem; /* mr-2 */
display: flex;
width: 100%;
align-items: center;
justify-content: center;
@media (min-width: 768px) {
/* md */
width: auto; /* md:w-auto */
}
@media (min-width: 1024px) {
/* lg */
margin-right: 1.5rem; /* lg:mr-6 */
}
.siteName {
margin-left: 0.5rem; /* ml-2 */
flex: none;
font-size: 0.875rem; /* text-sm */
font-weight: 500; /* font-medium */
text-transform: uppercase;
@media (min-width: 768px) {
/* md */
display: none;
}
@media (min-width: 1024px) {
/* lg */
display: block;
}
}
}
}
.searchContainer {
display: none;
justify-content: center;
@media (min-width: 768px) {
/* md */
display: flex;
width: 33.333333%; /* md:w-1/3 */
}
}
.cartContainer {
display: flex;
justify-content: flex-end;
@media (min-width: 768px) {
/* md */
width: 33.333333%; /* md:w-1/3 */
}
}
}
}
}

View File

@ -5,39 +5,36 @@ import LogoSquare from 'components/logo-square';
import { getMegaMenu } from 'lib/shopify';
import Link from 'next/link';
import { Suspense } from 'react';
import classes from './index.module.scss';
import MobileMenu from './mobile-menu';
import Search, { SearchSkeleton } from './search';
const { SITE_NAME } = process.env;
export default async function Navbar() {
const menu = await getMegaMenu('next-js-frontend-header-menu');
return (
<nav className="relative mx-auto max-w-screen-2xl p-4 lg:px-6">
<div className="flex items-center justify-between">
<div className="block flex-none md:hidden">
<nav className={classes.navbar}>
<div className={classes.navbarContainer}>
<div className={classes.mobileMenu}>
<Suspense fallback={null}>
<MobileMenu menu={menu} />
</Suspense>
</div>
<div className="flex w-full items-center">
<div className="flex w-full md:w-1/3">
<Link
href="/"
className="mr-2 flex w-full items-center justify-center md:w-auto lg:mr-6"
>
<div className={classes.navbarContent}>
<div className={classes.logoContainer}>
<Link href="/" className={classes.logoLink}>
<LogoSquare />
<div className="ml-2 flex-none text-sm font-medium uppercase md:hidden lg:block">
{SITE_NAME}
</div>
<div className={classes.siteName}>{SITE_NAME}</div>
</Link>
</div>
<div className="hidden justify-center md:flex md:w-1/3">
<div className={classes.searchContainer}>
<Suspense fallback={<SearchSkeleton />}>
<Search />
</Suspense>
</div>
<div className="flex justify-end md:w-1/3">
<div className={classes.cartContainer}>
<Suspense fallback={<OpenCart />}>
<Cart />
</Suspense>

View File

@ -9,6 +9,8 @@ import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline';
import { Menu } from 'lib/shopify/types';
import Search, { SearchSkeleton } from './search';
import styles from './MobileMenu.module.scss'; // Importing the SCSS module
export default function MobileMenu({ menu }: { menu: Menu[] }) {
const pathname = usePathname();
const searchParams = useSearchParams();
@ -35,7 +37,7 @@ export default function MobileMenu({ menu }: { menu: Menu[] }) {
<button
onClick={openMobileMenu}
aria-label="Open mobile menu"
className="flex h-11 w-11 items-center justify-center rounded-md border border-neutral-200 text-black transition-colors md:hidden dark:border-neutral-700 dark:text-white"
className={styles.openMenuButton}
>
<Bars3Icon className="h-4" />
</button>
@ -61,10 +63,10 @@ export default function MobileMenu({ menu }: { menu: Menu[] }) {
leaveFrom="translate-x-0"
leaveTo="translate-x-[-100%]"
>
<Dialog.Panel className="fixed bottom-0 left-0 right-0 top-0 flex h-full w-full flex-col bg-white pb-6 dark:bg-black">
<Dialog.Panel className={styles.dialogPanel}>
<div className="p-4">
<button
className="mb-4 flex h-11 w-11 items-center justify-center rounded-md border border-neutral-200 text-black transition-colors dark:border-neutral-700 dark:text-white"
className={styles.closeMenuButton}
onClick={closeMobileMenu}
aria-label="Close mobile menu"
>
@ -79,10 +81,7 @@ export default function MobileMenu({ menu }: { menu: Menu[] }) {
{menu.length ? (
<ul className="flex w-full flex-col">
{menu.map((item: Menu) => (
<li
className="py-2 text-xl text-black transition-colors hover:text-neutral-500 dark:text-white"
key={item.title}
>
<li className={styles.menuItem} key={item.title}>
<Link href={item.path} onClick={closeMobileMenu}>
{item.title}
</Link>

View File

@ -3,6 +3,7 @@
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { createUrl } from 'lib/utils';
import { useRouter, useSearchParams } from 'next/navigation';
import styles from './Search.module.scss'; // Importing the SCSS module
export default function Search() {
const router = useRouter();
@ -25,7 +26,7 @@ export default function Search() {
}
return (
<form onSubmit={onSubmit} className="w-max-[550px] relative w-full lg:w-80 xl:w-full">
<form onSubmit={onSubmit} className={styles.searchForm}>
<input
key={searchParams?.get('q')}
type="text"
@ -33,10 +34,10 @@ export default function Search() {
placeholder="Search for products..."
autoComplete="off"
defaultValue={searchParams?.get('q') || ''}
className="w-full rounded-lg border bg-white px-4 py-2 text-sm text-black placeholder:text-neutral-500 dark:border-neutral-800 dark:bg-transparent dark:text-white dark:placeholder:text-neutral-400"
className={styles.searchInput}
/>
<div className="absolute right-0 top-0 mr-3 flex h-full items-center">
<MagnifyingGlassIcon className="h-4" />
<div className={styles.searchIconContainer}>
<MagnifyingGlassIcon className={styles.searchIcon} />
</div>
</form>
);
@ -44,13 +45,10 @@ export default function Search() {
export function SearchSkeleton() {
return (
<form className="w-max-[550px] relative w-full lg:w-80 xl:w-full">
<input
placeholder="Search for products..."
className="w-full rounded-lg border bg-white px-4 py-2 text-sm text-black placeholder:text-neutral-500 dark:border-neutral-800 dark:bg-transparent dark:text-white dark:placeholder:text-neutral-400"
/>
<div className="absolute right-0 top-0 mr-3 flex h-full items-center">
<MagnifyingGlassIcon className="h-4" />
<form className={styles.searchForm}>
<input placeholder="Search for products..." className={styles.searchInput} />
<div className={styles.searchIconContainer}>
<MagnifyingGlassIcon className={styles.searchIcon} />
</div>
</form>
);