Refactor code and enhance comments in multiple files
The changes made include code refactoring and reformatting in several files for better readability, and to adhere to best practices. Detailed and intuitive comments were added to the code, improving clarity and understanding of the codebase. There were also some class adjustments and minor changes in several files ranging from reordering class listings to updating element selections. Import statements were also modified for clarity. Accordion functionality was removed, potentially indicating a pending replacement or enhancement.
This commit is contained in:
parent
c2dda2165b
commit
03f0d73c45
18 changed files with 230 additions and 335 deletions
161
public/scripts/vendor/preline/preline.js
vendored
161
public/scripts/vendor/preline/preline.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
// import necessary dependencies
|
||||
// Import the necessary dependencies
|
||||
import { Image } from "astro:assets";
|
||||
import heroImage from "../images/hero-image.avif";
|
||||
import PrimaryCTA from "./ui/buttons/PrimaryCTA.astro";
|
||||
|
@ -8,41 +8,29 @@ import Avatar from "./ui/avatars/Avatar.astro";
|
|||
import FullStar from "./ui/stars/FullStar.astro";
|
||||
import HalfStar from "./ui/stars/HalfStar.astro";
|
||||
|
||||
// Variables for customization of the HeroSection Component
|
||||
// Main heading
|
||||
const title:string = `Equip Your Projects with <span
|
||||
// Define the variables that will be used in this component
|
||||
const title: string = `Equip Your Projects with <span
|
||||
class="text-yellow-500 dark:text-yellow-400">ScrewFast</span>`;
|
||||
|
||||
// Main heading
|
||||
const subTitle:string =
|
||||
const subTitle: string =
|
||||
"Top-quality hardware tools and expert construction services for every project need.";
|
||||
|
||||
// Sub-heading text
|
||||
const primaryBtn:string = "Start Exploring";
|
||||
const primaryBtnURL:string = "/products";
|
||||
|
||||
/* `secondaryBtn` and `secondaryBtnURL` variables used to customise the text and target link of the secondary button. */
|
||||
const secondaryBtn:string = "Contact Sales Team";
|
||||
const secondaryBtnURL:string = "/contact";
|
||||
|
||||
/* `rating` variable used to customise the rating. */
|
||||
const rating:number = 4.8;
|
||||
|
||||
/* `starCount` variable used to customise the star rating with the full stars. */
|
||||
const starCount:number = 4;
|
||||
|
||||
/* `reviews` variable used to customise the number of reviews. */
|
||||
const reviews:string = "12.8k";
|
||||
|
||||
const primaryBtn: string = "Start Exploring";
|
||||
const primaryBtnURL: string = "/products";
|
||||
const secondaryBtn: string = "Contact Sales Team";
|
||||
const secondaryBtnURL: string = "/contact";
|
||||
const rating: number = 4.8;
|
||||
const starCount: number = 4;
|
||||
const reviews: string = "12.8k";
|
||||
---
|
||||
|
||||
<!-- Defining a grid container that holds all the content -->
|
||||
<div
|
||||
class="mx-auto grid max-w-[85rem] gap-4 px-4 py-14 sm:px-6 md:grid-cols-2 md:items-center md:gap-8 lg:px-8 2xl:max-w-full"
|
||||
>
|
||||
<!-- Title and description -->
|
||||
<div>
|
||||
<!-- Each h1 and p tag renders a portion of the title and subTitle defined above -->
|
||||
<h1
|
||||
class="text-balance block text-3xl font-bold tracking-tight text-neutral-800 dark:text-neutral-200 sm:text-4xl lg:text-6xl lg:leading-tight"
|
||||
class="block text-balance text-3xl font-bold tracking-tight text-neutral-800 dark:text-neutral-200 sm:text-4xl lg:text-6xl lg:leading-tight"
|
||||
>
|
||||
<!-- About Fragment: https://docs.astro.build/en/basics/astro-syntax/#fragments -->
|
||||
<Fragment set:html={title} />
|
||||
|
@ -53,13 +41,13 @@ const reviews:string = "12.8k";
|
|||
{subTitle}
|
||||
</p>
|
||||
|
||||
<!-- Action buttons -->
|
||||
<!-- Action Button Section: This section includes two CTAs with their own styles and URL -->
|
||||
<div class="mt-7 grid w-full gap-3 sm:inline-flex">
|
||||
<PrimaryCTA title={primaryBtn} url={primaryBtnURL} />
|
||||
<SecondaryCTA title={secondaryBtn} url={secondaryBtnURL} />
|
||||
</div>
|
||||
|
||||
<!-- Review Section -->
|
||||
<!-- Review Section: This section presents avatars, review ratings and the number of reviews -->
|
||||
<div class="mt-6 lg:mt-10">
|
||||
<div class="py-5">
|
||||
<div class="text-center sm:flex sm:items-center sm:text-start">
|
||||
|
@ -67,16 +55,20 @@ const reviews:string = "12.8k";
|
|||
<!-- Avatar Group -->
|
||||
<div class="flex justify-center -space-x-3">
|
||||
<Avatar
|
||||
src="https://images.unsplash.com/photo-1568602471122-7832951cc4c5?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=2&w=300&h=300&q=80" alt="Image Description"
|
||||
src="https://images.unsplash.com/photo-1568602471122-7832951cc4c5?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=2&w=300&h=300&q=80"
|
||||
alt="Image Description"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://images.unsplash.com/photo-1531927557220-a9e23c1e4794?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=2&w=300&h=300&q=80" alt="Image Description"
|
||||
src="https://images.unsplash.com/photo-1531927557220-a9e23c1e4794?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=2&w=300&h=300&q=80"
|
||||
alt="Image Description"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://images.unsplash.com/photo-1541101767792-f9b2b1c4f127?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&&auto=format&fit=facearea&facepad=3&w=300&h=300&q=80" alt="Image Description"
|
||||
src="https://images.unsplash.com/photo-1541101767792-f9b2b1c4f127?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&&auto=format&fit=facearea&facepad=3&w=300&h=300&q=80"
|
||||
alt="Image Description"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://images.unsplash.com/photo-1492562080023-ab3db95bfbce?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=2&w=300&h=300&q=80" alt="Image Description"
|
||||
src="https://images.unsplash.com/photo-1492562080023-ab3db95bfbce?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=2&w=300&h=300&q=80"
|
||||
alt="Image Description"
|
||||
/>
|
||||
<span
|
||||
class="inline-flex h-8 w-8 items-center justify-center rounded-full bg-zinc-800 ring-2 ring-white dark:bg-zinc-900 dark:ring-zinc-800"
|
||||
|
@ -119,7 +111,7 @@ const reviews:string = "12.8k";
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hero Image -->
|
||||
<!-- Hero Image Section -->
|
||||
<div class="flex w-full">
|
||||
<div class="top-12 overflow-hidden">
|
||||
<Image
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
---
|
||||
// Import the necessary dependencies from individual component files
|
||||
// Import the necessary dependencies
|
||||
import GithubBtn from "./ui/buttons/GithubBtn.astro";
|
||||
|
||||
// Variables for customization of the HeroSection2 Component
|
||||
// Main heading
|
||||
// Define the variables that will be used in this component
|
||||
const title: string = "Let's Build Together";
|
||||
|
||||
// Sub-heading text
|
||||
const subTitle: string =
|
||||
"ScrewFast is an open-source template, meticulously crafted with Astro, Tailwind CSS, and Preline UI frameworks.";
|
||||
---
|
||||
|
||||
<div class="relative mx-auto max-w-[85rem] px-4 pb-24 pt-10 sm:px-6 lg:px-8">
|
||||
<!-- Decorating SVG elements -->
|
||||
<div
|
||||
class="absolute left-0 top-[55%] scale-90 md:top-[20%] xl:left-[10%] xl:top-[25%]"
|
||||
>
|
||||
|
@ -112,21 +110,21 @@ const subTitle: string =
|
|||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Hero Section Heading -->
|
||||
<div class="mx-auto mt-5 max-w-xl text-center">
|
||||
<h1
|
||||
<h2
|
||||
class="block text-balance text-4xl font-bold leading-tight tracking-tight text-neutral-800 dark:text-neutral-200 md:text-5xl lg:text-6xl"
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- Hero Section Sub-heading -->
|
||||
<div class="mx-auto mt-5 max-w-3xl text-center">
|
||||
<p class="text-pretty text-lg text-neutral-600 dark:text-neutral-400">
|
||||
{subTitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Github Button -->
|
||||
<div class="mt-8 flex justify-center gap-3">
|
||||
<GithubBtn url="https://github.com/mearashadowfax/ScrewFast" />
|
||||
</div>
|
|
@ -1,72 +1,73 @@
|
|||
---
|
||||
// Assign a description using the `meta` prop passed from the parent component,
|
||||
// `meta` is used as a variable on each individual page by passing it as a prop to this component.
|
||||
// For example, you can pass a meta description to the MainLayout component like this: <MainLayout title="Page title" meta="Page description"/>
|
||||
// For example, you can pass a meta description to the MainLayout component like this: <MainLayout meta="Page description"/>
|
||||
const { meta: description } = Astro.props;
|
||||
|
||||
interface Props {
|
||||
meta?: string;
|
||||
meta?: string;
|
||||
}
|
||||
|
||||
// Customize the following metadata for your landing page
|
||||
|
||||
const title: string = "ScrewFast"; // Replace with your website title
|
||||
const ogTitle: string = "ScrewFast: Hardware Tools & Construction Services"; // Replace with your Open Graph title
|
||||
const author:string = "Emil Gulamov" // Replace with the author's name
|
||||
const ogDescription: string = "Equip your projects with ScrewFast's top-quality hardware tools and expert construction services. Trusted by industry leaders, ScrewFast offers simplicity, affordability, and reliability. Experience the difference with user-centric design and cutting-edge tools. Start exploring now!"; // Replace with your site description for social media
|
||||
const URL:string = "https://screw-fast.vercel.app"; // Replace with your website URL
|
||||
const socialImage:string = "https://screw-fast.vercel.app/social.png"; // Replace with the URL to your social media image
|
||||
// Define the metadata for your website and individual pages
|
||||
const title: string = "ScrewFast"; // Set the website title
|
||||
const ogTitle: string = "ScrewFast: Hardware Tools & Construction Services"; // Set the Open Graph title
|
||||
const author: string = "Emil Gulamov"; // Set the author's name
|
||||
const ogDescription: string =
|
||||
"Equip your projects with ScrewFast's top-quality hardware tools and expert construction services. Trusted by industry leaders, ScrewFast offers simplicity, affordability, and reliability. Experience the difference with user-centric design and cutting-edge tools. Start exploring now!"; // Set the Open Graph description
|
||||
const URL: string = "https://screw-fast.vercel.app"; // Set the website URL
|
||||
const socialImage: string = "https://screw-fast.vercel.app/social.png"; // Set the URL for the social media image
|
||||
---
|
||||
|
||||
<!-- JSON-LD structured data script for the website -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "ScrewFast",
|
||||
"url": "https://screw-fast.vercel.app",
|
||||
"description": "ScrewFast offers top-tier hardware tools and expert construction services to meet all your project needs. Start exploring and contact our sales team for superior quality and reliability."
|
||||
}
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "ScrewFast",
|
||||
"url": "https://screw-fast.vercel.app",
|
||||
"description": "ScrewFast offers top-tier hardware tools and expert construction services to meet all your project needs. Start exploring and contact our sales team for superior quality and reliability."
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Define the character set, description, author, and viewport settings -->
|
||||
<meta charset="utf-8" />
|
||||
<meta content={description} name="description" />
|
||||
<meta name="web_author" content={author} />
|
||||
<meta
|
||||
content={description}
|
||||
name="description"
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0"
|
||||
/>
|
||||
<meta name="web_author" content={author}/>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
|
||||
<!-- Facebook Meta Tags -->
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content={URL}>
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content={ogTitle}>
|
||||
<meta property="og:site_name" content={title}/>
|
||||
<meta property="og:description" content={ogDescription}>
|
||||
<meta property="og:image" content={socialImage}>
|
||||
<meta content="1200" property="og:image:width"/>
|
||||
<meta content="600" property="og:image:height"/>
|
||||
<meta content="image/png" property="og:image:type"/>
|
||||
<meta property="og:url" content={URL} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content={ogTitle} />
|
||||
<meta property="og:site_name" content={title} />
|
||||
<meta property="og:description" content={ogDescription} />
|
||||
<meta property="og:image" content={socialImage} />
|
||||
<meta content="1200" property="og:image:width" />
|
||||
<meta content="600" property="og:image:height" />
|
||||
<meta content="image/png" property="og:image:type" />
|
||||
|
||||
<!-- Twitter Meta Tags -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:domain" content={URL}>
|
||||
<meta property="twitter:url" content={URL}>
|
||||
<meta name="twitter:title" content={ogTitle}>
|
||||
<meta name="twitter:description" content={ogDescription}>
|
||||
<meta name="twitter:image" content={socialImage}>
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:domain" content={URL} />
|
||||
<meta property="twitter:url" content={URL} />
|
||||
<meta name="twitter:title" content={ogTitle} />
|
||||
<meta name="twitter:description" content={ogDescription} />
|
||||
<meta name="twitter:image" content={socialImage} />
|
||||
|
||||
<!-- Links to the webmanifest and sitemap -->
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
|
||||
<!-- https://docs.astro.build/en/guides/integrations-guide/sitemap/ -->
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
|
||||
<!-- Add your favicon and apple touch icon links -->
|
||||
<link href="/favicon.ico" rel="icon" sizes="any" type="image/x-icon"/>
|
||||
<link href="/icon.svg" rel="icon" type="image/svg+xml" sizes="any"/>
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<link href="/apple-touch-icon.png" rel="apple-touch-icon"/>
|
||||
<link href="/apple-touch-icon.png" rel="shortcut icon"/>
|
||||
<meta name="theme-color" content="#facc15">
|
||||
<!-- Links for favicons -->
|
||||
<link href="/favicon.ico" rel="icon" sizes="any" type="image/x-icon" />
|
||||
<link href="/icon.svg" rel="icon" type="image/svg+xml" sizes="any" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<link href="/apple-touch-icon.png" rel="apple-touch-icon" />
|
||||
<link href="/apple-touch-icon.png" rel="shortcut icon" />
|
||||
<!-- Set theme color -->
|
||||
<meta name="theme-color" content="#facc15" />
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
---
|
||||
// Import the necessary dependencies from individual component files
|
||||
//Import relevant dependencies
|
||||
import ThemeIcon from "./ThemeIcon.astro";
|
||||
import NavLink from "./ui/links/NavLink.astro";
|
||||
import Authentication from "./Authentication.astro";
|
||||
---
|
||||
|
||||
<!-- Main header component -->
|
||||
<header
|
||||
class="sticky inset-x-0 top-4 z-50 flex w-full flex-wrap text-sm md:flex-nowrap md:justify-start"
|
||||
>
|
||||
<!-- Navigation container -->
|
||||
<nav
|
||||
class="relative mx-2 w-full rounded-[36px] border border-yellow-100/40 bg-yellow-50/60 px-4 py-3 backdrop-blur-md dark:border-neutral-700/40 dark:bg-neutral-800/80 dark:backdrop-blur-md md:flex md:items-center md:justify-between md:px-6 md:py-0 lg:px-8 xl:mx-auto"
|
||||
aria-label="Global"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- Brand logo -->
|
||||
<a
|
||||
class="flex-none text-xl outline-none rounded-lg font-bold dark:focus:outline-none ring-zinc-500 focus-visible:ring dark:ring-zinc-200"
|
||||
class="flex-none rounded-lg text-xl font-bold outline-none ring-zinc-500 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
||||
href="/"
|
||||
aria-label="Brand"
|
||||
>
|
||||
<!-- Brand Logo -->
|
||||
<svg class="h-auto w-24" viewBox="0 0 521 226" fill="none">
|
||||
<rect
|
||||
width="78.937"
|
||||
|
@ -73,6 +75,7 @@ import Authentication from "./Authentication.astro";
|
|||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
<!-- Collapse toggle for smaller screens -->
|
||||
<div class="ml-auto mr-5 md:hidden">
|
||||
<button
|
||||
type="button"
|
||||
|
@ -112,17 +115,21 @@ import Authentication from "./Authentication.astro";
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- ThemeIcon component specifically for smaller screens -->
|
||||
<span class="inline-block md:hidden">
|
||||
<ThemeIcon />
|
||||
</span>
|
||||
</div>
|
||||
<!-- Contains navigation links -->
|
||||
<div
|
||||
id="navbar-collapse-with-animation"
|
||||
class="hs-collapse hidden grow basis-full overflow-hidden transition-all duration-300 md:block"
|
||||
>
|
||||
<!-- Navigation links container -->
|
||||
<div
|
||||
class="mt-5 flex flex-col gap-x-0 gap-y-4 md:mt-0 md:flex-row md:items-center md:justify-end md:gap-x-7 md:gap-y-0 md:ps-7"
|
||||
>
|
||||
<!-- Navigation links and Authentication component -->
|
||||
<NavLink url="/" name="Home" />
|
||||
<NavLink url="/products" name="Products" />
|
||||
<NavLink url="/services" name="Services" />
|
||||
|
@ -130,6 +137,7 @@ import Authentication from "./Authentication.astro";
|
|||
<NavLink url="/contact" name="Contact" />
|
||||
|
||||
<Authentication />
|
||||
<!-- ThemeIcon component specifically for larger screens -->
|
||||
<span class="hidden md:inline-block">
|
||||
<ThemeIcon />
|
||||
</span>
|
||||
|
@ -137,7 +145,7 @@ import Authentication from "./Authentication.astro";
|
|||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Theme Appearance script to manage light/dark modes -->
|
||||
<script is:inline>
|
||||
const HSThemeAppearance = {
|
||||
init() {
|
||||
|
@ -242,5 +250,8 @@ import Authentication from "./Authentication.astro";
|
|||
});
|
||||
});
|
||||
</script>
|
||||
<!--Import the necessary Collapse and Overlay plugins-->
|
||||
<!--https://preline.co/plugins/html/collapse.html-->
|
||||
<!--https://preline.co/plugins/html/overlay.html-->
|
||||
<script is:inline src="/scripts/vendor/preline/collapse/index.js"></script>
|
||||
<script is:inline src="/scripts/vendor/preline/overlay/index.js"></script>
|
|
@ -1,23 +1,28 @@
|
|||
---
|
||||
// Get heading and content from Astro props
|
||||
const { heading, content } = Astro.props;
|
||||
|
||||
// Define TypeScript interface for props
|
||||
interface Props {
|
||||
heading?: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
// Define classes for heading and content
|
||||
const headingClasses =
|
||||
"text-balance text-lg font-bold text-gray-800 dark:text-neutral-200";
|
||||
const contentClasses =
|
||||
"mt-1 text-pretty text-neutral-700 dark:text-neutral-300";
|
||||
---
|
||||
|
||||
<!-- The root container that arranges your slot and the heading/content -->
|
||||
<div class="flex gap-x-5">
|
||||
<!-- Slot to allow for extensibility of the component -->
|
||||
<slot />
|
||||
<div class="grow">
|
||||
<!-- Heading of the section -->
|
||||
<h3 class={headingClasses}>
|
||||
{heading}
|
||||
</h3>
|
||||
<!-- Content text of the section -->
|
||||
<p class={contentClasses}>{content}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
// Import the necessary modules
|
||||
import { Image } from "astro:assets";
|
||||
import PrimaryCTA from "../buttons/PrimaryCTA.astro";
|
||||
|
||||
// Destructure the props passed to the Astro component
|
||||
const { title, subTitle, btnExists, btnTitle, btnURL, img, imgAlt } =
|
||||
Astro.props;
|
||||
|
||||
// Define TypeScript interface for props
|
||||
interface Props {
|
||||
title: string;
|
||||
subTitle: string;
|
||||
|
@ -16,9 +17,11 @@ interface Props {
|
|||
}
|
||||
---
|
||||
|
||||
<!-- The root section of the component -->
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] items-center gap-8 px-4 py-10 sm:px-6 sm:py-16 md:grid md:grid-cols-2 lg:grid lg:grid-cols-2 lg:px-8 lg:py-14 xl:gap-16 2xl:max-w-full"
|
||||
>
|
||||
<!-- The Image component which renders the image -->
|
||||
<Image
|
||||
class="w-full rounded-xl"
|
||||
src={img}
|
||||
|
@ -26,17 +29,21 @@ interface Props {
|
|||
draggable={"false"}
|
||||
format={"avif"}
|
||||
/>
|
||||
<!-- The container for title, subtitle, and optional CTA button -->
|
||||
<div class="mt-4 md:mt-0">
|
||||
<!-- The title of the section -->
|
||||
<h2
|
||||
class="mb-4 text-balance text-4xl font-extrabold tracking-tight text-neutral-800 dark:text-neutral-200"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
<!-- The subtitle of the section -->
|
||||
<p
|
||||
class="mb-4 max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-lg"
|
||||
>
|
||||
{subTitle}
|
||||
</p>
|
||||
<!-- Conditionally render the Primary CTA button if btnExists is true -->
|
||||
{btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null}
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
---
|
||||
// Import PrimaryCTA component
|
||||
import PrimaryCTA from "../buttons/PrimaryCTA.astro";
|
||||
|
||||
// Destructure the props passed to the Astro component
|
||||
const { title, subTitle, btnExists, btnTitle, btnURL } = Astro.props;
|
||||
|
||||
// Define TypeScript interface for props
|
||||
interface Props {
|
||||
title: string;
|
||||
subTitle: string;
|
||||
|
@ -12,20 +14,24 @@ interface Props {
|
|||
}
|
||||
---
|
||||
|
||||
<!-- Root section of the component -->
|
||||
<section
|
||||
class="mx-auto mt-10 max-w-[85rem] px-4 py-10 sm:px-6 sm:py-16 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div class="max-w-screen-md">
|
||||
<!-- Section title -->
|
||||
<h1
|
||||
class="mb-4 text-balance text-4xl font-extrabold tracking-tight text-neutral-800 dark:text-neutral-200"
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
<!-- Section subtitle -->
|
||||
<p
|
||||
class="mb-8 max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-xl"
|
||||
>
|
||||
{subTitle}
|
||||
</p>
|
||||
<!-- Conditional rendering of PrimaryCTA component if 'btnExists' property is truthy -->
|
||||
{
|
||||
btnExists ? (
|
||||
<div class="flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0">
|
||||
|
|
|
@ -6,10 +6,11 @@ interface Props {
|
|||
url?: string;
|
||||
}
|
||||
|
||||
const baseClasses = "group inline-flex items-center justify-center gap-x-3 rounded-full px-4 py-3 text-center text-sm font-medium text-neutral-700 ring-zinc-500 focus-visible:ring transition duration-300 outline-none";
|
||||
const baseClasses =
|
||||
"group inline-flex items-center justify-center gap-x-3 rounded-full px-4 py-3 text-center text-sm font-medium text-neutral-700 ring-zinc-500 focus-visible:ring transition duration-300 outline-none";
|
||||
const borderClasses = "border border-transparent";
|
||||
const bgColorClasses = "bg-yellow-400 dark:focus:outline-none";
|
||||
const hoverClasses = "hover:shadow-2xl hover:shadow-yellow-500"
|
||||
const hoverClasses = "hover:shadow-2xl hover:shadow-yellow-500";
|
||||
const fontSizeClasses = "2xl:text-base";
|
||||
const ringClasses = "dark:ring-zinc-200";
|
||||
const githubSVG = `<svg
|
||||
|
@ -27,11 +28,13 @@ const githubSVG = `<svg
|
|||
---
|
||||
|
||||
<a
|
||||
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${hoverClasses} ${fontSizeClasses} ${ringClasses}`}
|
||||
href={url}
|
||||
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${hoverClasses} ${fontSizeClasses} ${ringClasses}`}
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<!-- About Fragment: https://docs.astro.build/en/basics/astro-syntax/#fragments -->
|
||||
|
||||
<Fragment set:html={githubSVG} />
|
||||
{title}
|
||||
</a>
|
||||
|
|
|
@ -5,9 +5,11 @@ interface Props {
|
|||
title: string;
|
||||
}
|
||||
|
||||
const baseClasses = "inline-flex w-full items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-sm dark:text-neutral-400 font-medium text-neutral-600 shadow-sm transition duration-300 focus-visible:ring outline-none";
|
||||
const baseClasses =
|
||||
"inline-flex w-full items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-sm dark:text-neutral-400 font-medium text-neutral-600 shadow-sm transition duration-300 focus-visible:ring outline-none";
|
||||
const borderClasses = "border border-neutral-200 dark:border-neutral-700";
|
||||
const bgColorClasses = "bg-neutral-50 dark:bg-neutral-800 hover:bg-neutral-200 dark:hover:bg-neutral-900";
|
||||
const bgColorClasses =
|
||||
"bg-neutral-50 dark:bg-neutral-800 hover:bg-neutral-200 dark:hover:bg-neutral-900";
|
||||
const disableClasses = "disabled:pointer-events-none disabled:opacity-50";
|
||||
const ringClasses = "ring-zinc-500 dark:ring-zinc-200";
|
||||
const googleSVG = `<svg
|
||||
|
@ -36,6 +38,8 @@ const googleSVG = `<svg
|
|||
type="button"
|
||||
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${disableClasses} ${ringClasses}`}
|
||||
>
|
||||
<!-- About Fragment: https://docs.astro.build/en/basics/astro-syntax/#fragments -->
|
||||
|
||||
<Fragment set:html={googleSVG} />
|
||||
{title}
|
||||
</button>
|
||||
|
|
|
@ -5,9 +5,11 @@ interface Props {
|
|||
title?: string;
|
||||
}
|
||||
|
||||
const baseClasses = "flex items-center gap-x-2 text-base md:text-sm font-medium text-neutral-600 ring-zinc-500 transition duration-300 focus-visible:ring outline-none";
|
||||
const baseClasses =
|
||||
"flex items-center gap-x-2 text-base md:text-sm font-medium text-neutral-600 ring-zinc-500 transition duration-300 focus-visible:ring outline-none";
|
||||
const hoverClasses = "hover:text-[#fa5a15] dark:hover:text-[#fb713b]";
|
||||
const darkClasses = "dark:border-neutral-700 dark:text-neutral-400 dark:ring-zinc-200 dark:focus:outline-none";
|
||||
const darkClasses =
|
||||
"dark:border-neutral-700 dark:text-neutral-400 dark:ring-zinc-200 dark:focus:outline-none";
|
||||
const mdClasses = "md:my-6 md:border-s md:border-neutral-300 md:ps-6";
|
||||
const txtSizeClasses = "2xl:text-base";
|
||||
const userSVG = `<svg
|
||||
|
@ -27,10 +29,12 @@ const userSVG = `<svg
|
|||
---
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class={`${baseClasses} ${hoverClasses} ${darkClasses} ${mdClasses} ${txtSizeClasses}`}
|
||||
data-hs-overlay="#hs-toggle-between-modals-login-modal"
|
||||
type="button"
|
||||
class={`${baseClasses} ${hoverClasses} ${darkClasses} ${mdClasses} ${txtSizeClasses}`}
|
||||
data-hs-overlay="#hs-toggle-between-modals-login-modal"
|
||||
>
|
||||
<!-- About Fragment: https://docs.astro.build/en/basics/astro-syntax/#fragments -->
|
||||
|
||||
<Fragment set:html={userSVG} />
|
||||
{title}
|
||||
</button>
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
// Import the necessary dependencies from individual component files
|
||||
// Import necessary components from their individual files
|
||||
import EmailInput from "./input/EmailInput.astro";
|
||||
import PasswordInput from "./input/PasswordInput.astro";
|
||||
import Checkbox from "./input/Checkbox.astro";
|
||||
|
@ -7,13 +7,13 @@ import AuthBtn from "../buttons/AuthBtn.astro";
|
|||
import GoogleBtn from "../buttons/GoogleBtn.astro";
|
||||
|
||||
// Variables for customization of the LoginModal Component
|
||||
// Modal identifier, Main heading, Sub-heading text, Text for registration button, Target link for registration button
|
||||
|
||||
const config = {
|
||||
id: "hs-toggle-between-modals-login-modal",
|
||||
title: "Sign in",
|
||||
subTitle: "Don't have an account yet?",
|
||||
registerBtn: "Sign up here",
|
||||
registerBtnDataHS: "#hs-toggle-between-modals-register-modal"
|
||||
id: "hs-toggle-between-modals-login-modal", // Modal IDENTIFIER
|
||||
title: "Sign in", // Main HEADING
|
||||
subTitle: "Don't have an account yet?", // Sub-Heading TEXT
|
||||
registerBtn: "Sign up here", // Text for REGISTRATION BUTTON
|
||||
registerBtnDataHS: "#hs-toggle-between-modals-register-modal", // TARGET LINK for registration button
|
||||
};
|
||||
---
|
||||
|
||||
|
@ -30,11 +30,11 @@ const config = {
|
|||
>
|
||||
<div class="p-4 sm:p-7">
|
||||
<div class="text-center">
|
||||
<h1
|
||||
<h2
|
||||
class="block text-2xl font-bold text-neutral-800 dark:text-neutral-200"
|
||||
>
|
||||
{config.title}
|
||||
</h1>
|
||||
</h2>
|
||||
<p class="mt-2 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{config.subTitle}
|
||||
<button
|
||||
|
@ -53,21 +53,23 @@ const config = {
|
|||
>
|
||||
Or
|
||||
</div>
|
||||
|
||||
<!-- The container for the form -->
|
||||
<form>
|
||||
<!-- A grid layout for the form fields -->
|
||||
<div class="grid gap-y-4">
|
||||
<!-- The email input field -->
|
||||
<EmailInput id="login-email" />
|
||||
|
||||
<!-- The password input field -->
|
||||
<PasswordInput
|
||||
forgot={true}
|
||||
id="password"
|
||||
;
|
||||
errorId="password-error"
|
||||
errorId="login-password-error"
|
||||
content="8+ characters required"
|
||||
/>
|
||||
|
||||
<!-- The remember-me checkbox -->
|
||||
<Checkbox id="remember-me" />
|
||||
|
||||
<!-- The sign-in button -->
|
||||
<AuthBtn title="Sign in" />
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -28,7 +28,7 @@ interface Props {
|
|||
{
|
||||
forgot ? (
|
||||
<button
|
||||
class="text-sm font-medium text-[#fa5a15] decoration-2 rounded-lg outline-none ring-zinc-500 hover:underline focus-visible:ring dark:text-[#fa5a15] dark:ring-zinc-200 dark:focus:outline-none dark:focus:ring-1"
|
||||
class="rounded-lg text-sm font-medium text-[#fa5a15] decoration-2 outline-none ring-zinc-500 hover:underline focus-visible:ring dark:text-[#fa5a15] dark:ring-zinc-200 dark:focus:outline-none dark:focus:ring-1"
|
||||
data-hs-overlay="#hs-toggle-between-modals-recover-modal"
|
||||
>
|
||||
Forgot password?
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
// Destructure the properties from Astro.props
|
||||
const { url, name } = Astro.props;
|
||||
|
||||
// Define TypeScript interface for the properties
|
||||
interface Props {
|
||||
url: string;
|
||||
name: string;
|
||||
|
@ -23,20 +25,20 @@ If URL is '/' (home page), assign ID as 'home'
|
|||
</a>
|
||||
|
||||
<script>
|
||||
window.onload = function () {
|
||||
let url = window.location.pathname;
|
||||
let firstPathSegment = url.split('/')[1];
|
||||
let navId = firstPathSegment ? firstPathSegment : 'home';
|
||||
let nav = document.getElementById(navId);
|
||||
if (nav) {
|
||||
nav.classList.remove(
|
||||
'text-neutral-600',
|
||||
'dark:text-neutral-400',
|
||||
'hover:text-neutral-500',
|
||||
'dark:hover:text-neutral-500',
|
||||
);
|
||||
nav.classList.add('text-[#fa5a15]', 'dark:text-[#fb713b]');
|
||||
nav.setAttribute('aria-current', 'page');
|
||||
}
|
||||
};
|
||||
window.onload = function () {
|
||||
let url = window.location.pathname;
|
||||
let firstPathSegment = url.split("/")[1];
|
||||
let navId = firstPathSegment ? firstPathSegment : "home";
|
||||
let nav = document.getElementById(navId);
|
||||
if (nav) {
|
||||
nav.classList.remove(
|
||||
"text-neutral-600",
|
||||
"dark:text-neutral-400",
|
||||
"hover:text-neutral-500",
|
||||
"dark:hover:text-neutral-500",
|
||||
);
|
||||
nav.classList.add("text-[#fa5a15]", "dark:text-[#fb713b]");
|
||||
nav.setAttribute("aria-current", "page");
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -1,22 +1,31 @@
|
|||
---
|
||||
// Importing necessary components
|
||||
import Meta from "../components/Meta.astro";
|
||||
import Navbar from "../components/Navbar.astro";
|
||||
import FooterSection from "../components/FooterSection.astro";
|
||||
|
||||
// Setting expected props - expecting 'title' and 'meta' as options with a default value for title
|
||||
const { title = "ScrewFast", meta } = Astro.props;
|
||||
|
||||
// Interface to type-check the properties
|
||||
interface Props {
|
||||
title?: string;
|
||||
meta?: string;
|
||||
}
|
||||
---
|
||||
|
||||
<!--
|
||||
This is the main structure for the page.
|
||||
We set the language of the page to English and add classes for scrollbar and scroll behavior.
|
||||
-->
|
||||
<html lang="en" class="scrollbar-hide scroll-pt-16">
|
||||
<head>
|
||||
<!-- Adding metadata to the HTML document -->
|
||||
<Meta meta={meta} />
|
||||
<!-- Define the title of the page -->
|
||||
<title>{title}</title>
|
||||
<script is:inline>
|
||||
// This script is to handle theme according to user preference
|
||||
// Script to handle dark mode. It will check if the theme is stored in localStorage or if dark theme is preferred by system settings
|
||||
if (
|
||||
localStorage.getItem("hs_theme") === "dark" ||
|
||||
(!("hs_theme" in localStorage) &&
|
||||
|
@ -29,7 +38,7 @@ interface Props {
|
|||
</script>
|
||||
<script is:inline src="/scripts/vendor/lenis/lenis.js"></script>
|
||||
<script is:inline>
|
||||
// This script is to handle lenis library settings and behaviors, like the smooth scrolling
|
||||
// Script to handle Lenis library settings for smooth scrolling
|
||||
const lenis = new Lenis({ smooth: true, smoothTouch: false });
|
||||
|
||||
function raf(time) {
|
||||
|
@ -43,13 +52,17 @@ interface Props {
|
|||
<body
|
||||
class="bg-neutral-200 selection:bg-yellow-400 selection:text-neutral-700 dark:bg-neutral-800"
|
||||
>
|
||||
<!--
|
||||
Setting up the main structure of the page.
|
||||
The Navbar is placed at the top, with a slot for the main content and FooterSection at the bottom.
|
||||
-->
|
||||
<div class="mx-auto max-w-screen-2xl px-4 sm:px-6 lg:px-8">
|
||||
<Navbar />
|
||||
<slot />
|
||||
</div>
|
||||
<FooterSection />
|
||||
<style>
|
||||
// These css rules are for the page scrollbar and scrolling experience with lenis library
|
||||
// CSS rules for the page scrollbar and scrolling experience with lenis library
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
---
|
||||
// Import section components
|
||||
// Import necessary components, modules and types
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
import CardBlog from "../../components/ui/cards/CardBlog.astro";
|
||||
import CardBlogRecent from "../../components/ui/cards/CardBlogRecent.astro";
|
||||
import CardInsight from "../../components/ui/cards/CardInsight.astro";
|
||||
import { Image } from "astro:assets";
|
||||
|
||||
import { getCollection } from "astro:content";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
// Get all blogs post and sort them based on publish date
|
||||
const blogPosts: CollectionEntry<"blog">[] = (await getCollection("blog")).sort(
|
||||
(a: CollectionEntry<"blog">, b: CollectionEntry<"blog">) =>
|
||||
b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||
);
|
||||
|
||||
// Get all insights posts
|
||||
const insightPosts: CollectionEntry<"insights">[] =
|
||||
await getCollection("insights");
|
||||
|
||||
// Separate the most recent post from others
|
||||
const mostRecentPost: CollectionEntry<"blog"> = blogPosts[0];
|
||||
const otherPosts: CollectionEntry<"blog">[] = blogPosts.slice(1);
|
||||
|
||||
// Define variables for page content
|
||||
const title: string = "Your Gateway to Construction Excellence";
|
||||
|
||||
const subTitle: string =
|
||||
"Explore the latest news, tips, and insights from ScrewFast to enhance your construction projects. From product spotlights to project management strategies, our blog is your go-to resource for all things hardware and construction.";
|
||||
|
||||
const secondTitle: string = "Insights";
|
||||
|
||||
const secondSubTitle: string =
|
||||
"Stay up-to-date with the latest trends and developments in the construction industry with insights from ScrewFast's team of industry experts. ";
|
||||
---
|
||||
|
@ -38,6 +36,7 @@ const secondSubTitle: string =
|
|||
<div
|
||||
class="mx-auto max-w-[85rem] space-y-8 px-4 pt-16 sm:px-6 lg:px-8 2xl:max-w-full"
|
||||
>
|
||||
<!--Page header-->
|
||||
<div class="mx-auto max-w-3xl text-center">
|
||||
<h1
|
||||
class="block text-balance text-4xl font-bold tracking-tight text-neutral-800 dark:text-neutral-200 md:text-5xl lg:text-6xl"
|
||||
|
@ -56,17 +55,18 @@ const secondSubTitle: string =
|
|||
<div
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<!--Blog posts grid-->
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
{otherPosts.map((blogEntry) => <CardBlog blogEntry={blogEntry} />)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Most recent blog post-->
|
||||
<div
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<CardBlogRecent blogEntry={mostRecentPost} />
|
||||
</div>
|
||||
|
||||
<!--Insights section-->
|
||||
<div
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
|
@ -76,9 +76,10 @@ const secondSubTitle: string =
|
|||
>
|
||||
{secondTitle}
|
||||
</h2>
|
||||
<p class="mt-1 text-neutral-600 dark:text-neutral-400 text-pretty">{secondSubTitle}</p>
|
||||
<p class="mt-1 text-pretty text-neutral-600 dark:text-neutral-400">
|
||||
{secondSubTitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{
|
||||
insightPosts.map((insightEntry) => (
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
// Import section components
|
||||
// Import the necessary components
|
||||
import MainLayout from "../layouts/MainLayout.astro";
|
||||
import HeroSection from "../components/HeroSection.astro";
|
||||
import HeroSection2 from "../components/HeroSection2.astro";
|
||||
import HeroSectionAlt from "../components/HeroSectionAlt.astro";
|
||||
import ClientsSection from "../components/ClientsSection.astro";
|
||||
import FeaturesGeneral from "../components/FeaturesGeneral.astro";
|
||||
import FeaturesNavs from "../components/FeaturesNavs.astro";
|
||||
|
@ -11,8 +11,10 @@ import PricingSection from "../components/PricingSection.astro";
|
|||
import FAQ from "../components/FAQ.astro";
|
||||
---
|
||||
|
||||
<MainLayout meta="ScrewFast offers top-tier hardware tools and expert construction services to meet all your project needs. Start exploring and contact our sales team for superior quality and reliability.">
|
||||
|
||||
<!--Utilizing MainLayout for the outer layout of the page, and defining meta for SEO purposes-->
|
||||
<MainLayout
|
||||
meta="ScrewFast offers top-tier hardware tools and expert construction services to meet all your project needs. Start exploring and contact our sales team for superior quality and reliability."
|
||||
>
|
||||
<HeroSection />
|
||||
<ClientsSection />
|
||||
<FeaturesGeneral />
|
||||
|
@ -20,5 +22,5 @@ import FAQ from "../components/FAQ.astro";
|
|||
<TestimonialsSection />
|
||||
<PricingSection />
|
||||
<FAQ />
|
||||
<HeroSection2 />
|
||||
<HeroSectionAlt />
|
||||
</MainLayout>
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
---
|
||||
// Import section components
|
||||
// Importing necessary components
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
import PrimaryCTA from "../../components/ui/buttons/PrimaryCTA.astro";
|
||||
import CardSmall from "../../components/ui/cards/CardSmall.astro";
|
||||
import CardWide from "../../components/ui/cards/CardWide.astro";
|
||||
import FeaturesStats2 from "../../components/FeaturesStats2.astro";
|
||||
import TestimonialsSection2 from "../../components/TestimonialsSection2.astro";
|
||||
import FeaturesStatsAlt from "../../components/FeaturesStatsAlt.astro";
|
||||
import TestimonialsSectionAlt from "../../components/TestimonialsSectionAlt.astro";
|
||||
|
||||
// Importing necessary functions from Astro
|
||||
import { getCollection } from "astro:content";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
// Fetching all the product related content and sorting it by main.id
|
||||
const product: CollectionEntry<"products">[] = (
|
||||
await getCollection("products")
|
||||
).sort(
|
||||
(a: CollectionEntry<"products">, b: CollectionEntry<"products">) =>
|
||||
a.data.main.id - b.data.main.id
|
||||
a.data.main.id - b.data.main.id,
|
||||
);
|
||||
|
||||
// Define variables for page content
|
||||
const title: string = "Products";
|
||||
|
||||
const subTitle: string =
|
||||
"Explore the durability and precision of ScrewFast tools, designed for both professionals and enthusiasts. Each of our products is crafted with precision and built to last, ensuring you have the right tool for every job.";
|
||||
---
|
||||
|
||||
<!--Utilizing MainLayout for the outer layout of the page, and defining meta for SEO purposes-->
|
||||
<MainLayout
|
||||
title="Products | ScrewFast"
|
||||
meta="ScrewFast offers top-tier hardware tools and expert construction services to meet all your project needs. Start exploring and contact our sales team for superior quality and reliability."
|
||||
|
@ -46,7 +49,8 @@ const subTitle: string =
|
|||
</div>
|
||||
<PrimaryCTA title="Customer Stories" url="#testimonials" noArrow={true} />
|
||||
</div>
|
||||
|
||||
<!--Displaying products in alternating styles. Alternative product gets different card styling.-->
|
||||
<!--Maps through all product entries and displays them with either CardSmall or CardWide based on their position.-->
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3 md:gap-6 xl:gap-8">
|
||||
{
|
||||
product.map((product, index) => {
|
||||
|
@ -60,7 +64,8 @@ const subTitle: string =
|
|||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FeaturesStats2 />
|
||||
<TestimonialsSection2 />
|
||||
<!--Features statistics section-->
|
||||
<FeaturesStatsAlt />
|
||||
<!--Testimonials section-->
|
||||
<TestimonialsSectionAlt />
|
||||
</MainLayout>
|
||||
|
|
Loading…
Add table
Reference in a new issue