├── public ├── CNAME ├── favicon.ico ├── apple-touch-icon.png ├── mstile-150x150.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon-precomposed.png ├── robots.txt ├── browserconfig.xml ├── sitemap.xml ├── site.webmanifest ├── 404.html └── safari-pinned-tab.svg ├── bun.lockb ├── src ├── assets │ └── media │ │ ├── header-bg.webp │ │ ├── services-bg.webp │ │ ├── header-bg-mobile.webp │ │ ├── reviewers │ │ ├── Mercy_Ares.webp │ │ ├── usman_shah.webp │ │ ├── Lex_Scooter.webp │ │ ├── xavier_marin.webp │ │ ├── Danielle_Marie.webp │ │ ├── Eddrina-Delmont.webp │ │ ├── Africans_In_Orlando.webp │ │ └── Virag_Anna_Szolgyemy.webp │ │ ├── services-bg-mobile.webp │ │ ├── services │ │ ├── radio_stereo.webp │ │ ├── hid_led_lights.webp │ │ └── lift_kit_services.webp │ │ └── cc-logo.svg ├── scss │ ├── bs-dynamic.scss │ ├── style.scss │ ├── _whyus.scss │ ├── _testimonials.scss │ ├── _variables.scss │ ├── _hero.scss │ ├── _components.scss │ ├── _deals.scss │ ├── _base.scss │ ├── _contactus.scss │ └── _navigation.scss ├── js │ ├── footer.js │ ├── hidepreloader.js │ ├── glide.js │ ├── header.js │ ├── whyus.js │ ├── aboutus.js │ ├── contact.js │ ├── deals.js │ └── testimonials.js ├── index.js └── css │ ├── preloader.min.css │ └── preloader.css ├── .gitignore ├── LICENSE ├── package.json ├── .github └── workflows │ └── static.yml ├── vite.config.js ├── postcss.config.cjs ├── README.md └── index.html /public/CNAME: -------------------------------------------------------------------------------- 1 | carclubtire.com -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/bun.lockb -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/public/mstile-150x150.png -------------------------------------------------------------------------------- /src/assets/media/header-bg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/header-bg.webp -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/assets/media/services-bg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/services-bg.webp -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /src/assets/media/header-bg-mobile.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/header-bg-mobile.webp -------------------------------------------------------------------------------- /src/assets/media/reviewers/Mercy_Ares.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/reviewers/Mercy_Ares.webp -------------------------------------------------------------------------------- /src/assets/media/reviewers/usman_shah.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/reviewers/usman_shah.webp -------------------------------------------------------------------------------- /src/assets/media/services-bg-mobile.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/services-bg-mobile.webp -------------------------------------------------------------------------------- /src/assets/media/reviewers/Lex_Scooter.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/reviewers/Lex_Scooter.webp -------------------------------------------------------------------------------- /src/assets/media/reviewers/xavier_marin.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/reviewers/xavier_marin.webp -------------------------------------------------------------------------------- /src/assets/media/services/radio_stereo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/services/radio_stereo.webp -------------------------------------------------------------------------------- /src/assets/media/reviewers/Danielle_Marie.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/reviewers/Danielle_Marie.webp -------------------------------------------------------------------------------- /src/assets/media/reviewers/Eddrina-Delmont.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/reviewers/Eddrina-Delmont.webp -------------------------------------------------------------------------------- /src/assets/media/services/hid_led_lights.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/services/hid_led_lights.webp -------------------------------------------------------------------------------- /src/assets/media/services/lift_kit_services.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/services/lift_kit_services.webp -------------------------------------------------------------------------------- /src/assets/media/reviewers/Africans_In_Orlando.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/reviewers/Africans_In_Orlando.webp -------------------------------------------------------------------------------- /src/assets/media/reviewers/Virag_Anna_Szolgyemy.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saaqi/car-mechanic-shop/HEAD/src/assets/media/reviewers/Virag_Anna_Szolgyemy.webp -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | 3 | Disallow: 4 | Disallow: /cgi-bin/ 5 | Disallow: /src/ 6 | Disallow: /purgecss.config.js 7 | 8 | Sitemap: https://carclubtire.com/sitemap.xml -------------------------------------------------------------------------------- /src/scss/bs-dynamic.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | // override bootstrap variables 4 | @import "./variables"; 5 | // import bootstrap scss 6 | @import "../../node_modules/bootstrap/scss/bootstrap.scss"; 7 | -------------------------------------------------------------------------------- /src/scss/style.scss: -------------------------------------------------------------------------------- 1 | @use "base"; 2 | @use "navigation"; 3 | @use "components"; 4 | // sections 5 | @use "hero"; 6 | @use "deals"; 7 | @use "whyus"; 8 | @use "testimonials"; 9 | @use "contactus"; 10 | -------------------------------------------------------------------------------- /src/scss/_whyus.scss: -------------------------------------------------------------------------------- 1 | @media only screen and ( max-width: 300px) { 2 | .counter-box { 3 | word-break: break-all; 4 | flex-direction: column; 5 | } 6 | .counter-box > div { 7 | width: 100% 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #374162 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://carclubtire.com/ 5 | 2024-03-13 6 | monthly 7 | 1.0 8 | 9 | -------------------------------------------------------------------------------- /src/js/footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file footer.js 3 | * @description This file contains a script to display the current year in an HTML element with the ID "year". 4 | */ 5 | 6 | /** 7 | * @description Sets the innerHTML of the element with ID "year" to the current year. 8 | */ 9 | document.getElementById("year").innerHTML = new Date().getFullYear(); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Car Club Tire and Auto Repair Service", 3 | "short_name": "Car Club", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#374162", 17 | "background_color": "#374162", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // BOOTSTRAP 2 | // import "./scss/bs-dynamic.scss" 3 | import "./css/bs-compiled.css" 4 | // STYLES 5 | import "./scss/style.scss" 6 | // BOX Icons 7 | import "boxicons/css/boxicons.css" 8 | 9 | 10 | // Sections 11 | import "./js/header.js" 12 | import "./js/deals.js" 13 | import "./js/whyus.js" 14 | import "./js/aboutus.js" 15 | import "./js/testimonials.js" 16 | import "./js/contact.js" 17 | import "./js/footer.js" 18 | 19 | // GlideJs 20 | import "./js/glide.js" 21 | 22 | // hide preloader 23 | import "./js/hidepreloader.js" -------------------------------------------------------------------------------- /src/scss/_testimonials.scss: -------------------------------------------------------------------------------- 1 | // import partials --- 2 | @use "./variables" as v; 3 | 4 | .testimonial .blockquote { 5 | font-size: .875rem; 6 | } 7 | 8 | .star-ratings { 9 | font-size: 1.5rem; 10 | color: #ffcc33; 11 | } 12 | 13 | .star-ratings>i { 14 | text-shadow: v.$text-shadow; 15 | } 16 | 17 | .glide-deals .glide__arrow--left, 18 | .glide-services .glide__arrow--left, 19 | .glide-reviews .glide__arrow--left { 20 | left: 0; 21 | } 22 | 23 | .glide-deals .glide__arrow--right, 24 | .glide-services .glide__arrow--right, 25 | .glide-reviews .glide__arrow--right { 26 | right: 0; 27 | } -------------------------------------------------------------------------------- /src/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | // Bootstrap over rides --- 2 | 3 | // Colors ---------- 4 | $primary: #374162; 5 | $secondary: #f9d949; 6 | $info: #f45050; 7 | $danger: #9E1A1A; 8 | $success: #105B38; 9 | $dark: #212529; 10 | $light: #fefefe; 11 | // $warning: $warning; 12 | 13 | // Typography ------ 14 | // $font-size-base: 0.9375rem;; 15 | $font-family-sans-serif: Montserrat, sans-serif; 16 | 17 | 18 | // custom variables ----- 19 | $even-bg: #f0f0f0; 20 | $odd-bg: #f9f9f9; 21 | 22 | $box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.3); 23 | $text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1); 24 | $transition: all ease-in-out 0.2s; -------------------------------------------------------------------------------- /src/scss/_hero.scss: -------------------------------------------------------------------------------- 1 | .hero { 2 | padding: calc(2rem + 66px) 0 2rem; 3 | background-image: url("/src/assets/media/header-bg.webp"); 4 | background-position: center center; 5 | background-repeat: no-repeat; 6 | background-size: cover; 7 | position: relative; 8 | min-height: 100svh; 9 | 10 | a.home-phone > i.bx:hover { 11 | transform: rotate(30deg); 12 | } 13 | 14 | .container * { 15 | z-index: 2; 16 | } 17 | 18 | .background-overlay { 19 | height: 100%; 20 | width: 100%; 21 | min-height: 100svh; 22 | top: 0; 23 | left: 0; 24 | position: absolute; 25 | mix-blend-mode: multiply; 26 | background-color: var(--bs-primary); 27 | opacity: 1; 28 | z-index: 0; 29 | } 30 | } 31 | 32 | @media (max-width: 1024px) { 33 | .hero { 34 | background-image: url("/src/assets/media/header-bg-mobile.webp"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/scss/_components.scss: -------------------------------------------------------------------------------- 1 | // BUTTONS --------------------------- 2 | .btn { 3 | box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.3), 1px 1px 2px 0 rgba(0, 0, 0, 0.3); 4 | font-weight: 500; 5 | padding: 1rem 2rem; 6 | text-transform: uppercase; 7 | transition: all ease-in-out 0.1s; 8 | letter-spacing: 1px; 9 | 10 | &:hover { 11 | box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.3); 12 | transform: translateY(0.5px); 13 | } 14 | 15 | &:active { 16 | box-shadow: none; 17 | text-shadow: none; 18 | transform: translateY(2px); 19 | } 20 | } 21 | 22 | [class*="btn-"]:not([class*="btn-outline-"]) { 23 | border: 0; 24 | } 25 | 26 | [class*="btn-outline-"] { 27 | box-shadow: none; 28 | 29 | &:hover { 30 | box-shadow: none; 31 | } 32 | 33 | &:active { 34 | box-shadow: none; 35 | } 36 | } 37 | 38 | .social-button { 39 | letter-spacing: 0; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/js/hidepreloader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/js/hidepreloader.js 3 | * @description This file contains functions to for the hiding the preloader after the page is loaded. 4 | */ 5 | 6 | // Hide Preloader ------------ 7 | const preloader = document.getElementById("loader-wrapper"); 8 | 9 | /** 10 | * Hides the preloader element after a delay of 2 seconds once the window has loaded. 11 | * This function sets the `hidden` attribute and `display` style of the preloader element to `none` 12 | * after a delay of 2 seconds. 13 | */ 14 | window.onload = setTimeout(function () { 15 | if (preloader) preloader.hidden = true; 16 | if (preloader) preloader.style.display = "none"; 17 | }, 2000); 18 | 19 | /** 20 | * Adds the 'hide-preloader' class to the preloader element once the window has loaded. 21 | * This function ensures that the preloader element is hidden by adding the 'hide-preloader' class 22 | * to it when the window has finished loading. 23 | */ 24 | window.onload = function () { 25 | preloader.classList.add("hide-preloader"); 26 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Saqib Islam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "car-mechanic-shop-html5-bootstrap-template", 3 | "description": "This is a fully responsive and animated HTML 5 template for a car mechanic shop. The template has been designed to provide a professional and modern look to the website. It includes a responsive navigation bar that makes it easy for users to navigate through the different sections of the website.", 4 | "private": true, 5 | "version": "2.1.6", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "vite build --emptyOutDir", 10 | "test": "vite preview", 11 | "build-bootstrap": "sass --no-source-map ./src/scss/bs-dynamic.scss ./src/css/bs-compiled.css" 12 | }, 13 | "devDependencies": { 14 | "@fullhuman/postcss-purgecss": "^5.0.0", 15 | "autoprefixer": "^10.4.21", 16 | "caniuse-lite": "^1.0.30001717", 17 | "cssnano": "^6.1.2", 18 | "postcss": "^8.5.3", 19 | "sass": "^1.87.0", 20 | "vite": "^5.4.19", 21 | "vite-plugin-minify": "^1.5.2", 22 | "vite-plugin-webfont-dl": "^3.10.4" 23 | }, 24 | "dependencies": { 25 | "@glidejs/glide": "^3.7.1", 26 | "bootstrap": "^5.3.5", 27 | "boxicons": "^2.1.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ['main'] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: 'pages' 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Single deploy job since we're just deploying 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | - name: Set up Bun 34 | uses: oven-sh/setup-bun@v1 35 | with: 36 | bun-version: latest 37 | - name: Install dependencies 38 | run: bun install 39 | - name: Build 40 | run: bun run build 41 | - name: Setup Pages 42 | uses: actions/configure-pages@v4 43 | - name: Upload artifact 44 | uses: actions/upload-pages-artifact@v3 45 | with: 46 | # Upload dist folder 47 | path: './dist' 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@v4 51 | -------------------------------------------------------------------------------- /src/scss/_deals.scss: -------------------------------------------------------------------------------- 1 | // import partials --- 2 | @use "./variables" as v; 3 | 4 | .ag-courses_item { 5 | overflow: hidden; 6 | position: relative; 7 | 8 | &:hover { 9 | box-shadow: v.$box-shadow; 10 | } 11 | 12 | &:hover .ag-courses-item_bg { 13 | transform: scale(10); 14 | } 15 | } 16 | 17 | .ag-courses-item_title { 18 | position: relative; 19 | z-index: 2; 20 | overflow: hidden; 21 | } 22 | 23 | .ag-course-body { 24 | z-index: 3; 25 | position: relative; 26 | } 27 | 28 | .ag-courses-item_bg { 29 | height: 128px; 30 | width: 128px; 31 | background-color: #f9b234; 32 | z-index: 1; 33 | position: absolute; 34 | top: -75px; 35 | right: -75px; 36 | border-radius: 50%; 37 | transition: v.$transition; 38 | } 39 | 40 | .glide-deals li.glide__slide:nth-child(2n)>.ag-courses_item>.ag-courses-item_bg { 41 | background-color: #3ecd5e; 42 | } 43 | 44 | .glide-deals li.glide__slide:nth-child(3n)>.ag-courses_item>.ag-courses-item_bg { 45 | background-color: #e44002; 46 | } 47 | 48 | .glide-deals li.glide__slide:nth-child(4n)>.ag-courses_item>.ag-courses-item_bg { 49 | background-color: #952aff; 50 | } 51 | 52 | .glide-deals li.glide__slide:nth-child(5n)>.ag-courses_item>.ag-courses-item_bg { 53 | background-color: #cd3e94; 54 | } 55 | 56 | .glide-deals li.glide__slide:nth-child(6n)>.ag-courses_item>.ag-courses-item_bg { 57 | background-color: #4c49ea; 58 | } 59 | 60 | 61 | ////////////////////////////////////////// 62 | .alert-heading:hover { 63 | box-shadow: v.$box-shadow; 64 | } -------------------------------------------------------------------------------- /src/js/glide.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file glide.js 3 | * @description This file initializes and configures Glide.js carousels for the "deals" and "services" sections of the webpage. 4 | */ 5 | 6 | // Import Glide.js CSS files 7 | import "@glidejs/glide/dist/css/glide.core.min.css"; 8 | import "@glidejs/glide/dist/css/glide.theme.min.css"; 9 | 10 | // Import Glide.js library 11 | import Glide from "@glidejs/glide"; 12 | 13 | // Initialize and configure the Glide.js carousel for the "deals" section 14 | new Glide(".glide-deals", { 15 | type: "carousel", 16 | perView: 3, 17 | swipeThreshold: 40, 18 | dragThreshold: 60, 19 | animationDuration: 250, 20 | peek: 40, 21 | breakpoints: { 22 | 992: { 23 | perView: 1, 24 | }, 25 | }, 26 | }).mount(); 27 | 28 | // Initialize and configure the Glide.js carousel for the "services" section 29 | new Glide(".glide-services", { 30 | type: "carousel", 31 | perView: 3, 32 | swipeThreshold: 40, 33 | dragThreshold: 60, 34 | animationDuration: 250, 35 | peek: 40, 36 | breakpoints: { 37 | // 992: { 38 | // perView: 1, 39 | // }, 40 | 768: { 41 | perView: 1, 42 | }, 43 | }, 44 | }).mount(); 45 | 46 | // Initialize and configure the Glide.js carousel for the "reviews" section 47 | new Glide(".glide-reviews", { 48 | type: "carousel", 49 | perView: 3, 50 | swipeThreshold: 40, 51 | dragThreshold: 60, 52 | animationDuration: 250, 53 | breakpoints: { 54 | 992: { 55 | perView: 2, 56 | }, 57 | 768: { 58 | perView: 1, 59 | }, 60 | }, 61 | }).mount(); 62 | -------------------------------------------------------------------------------- /src/scss/_base.scss: -------------------------------------------------------------------------------- 1 | // import partials --- 2 | @use "./variables" as v; 3 | 4 | html { 5 | scroll-padding-top: 68px; 6 | /* scroll-snap-type: y proximity; */ 7 | } 8 | 9 | a { 10 | text-decoration: none; 11 | } 12 | 13 | /////////////////////// 14 | // Section defaults --- 15 | .section { 16 | display: flex; 17 | justify-content: center; 18 | align-items: center; 19 | align-content: center; 20 | place-content: center; 21 | padding: 2rem 0; 22 | /* scroll-snap-align: start; */ 23 | } 24 | .section:nth-child(even) { 25 | background-color: v.$even-bg; 26 | } 27 | .section:nth-child(odd) { 28 | background-color: v.$odd-bg; 29 | } 30 | 31 | .heading-container { 32 | margin-bottom: 1.5rem; 33 | } 34 | 35 | .section-heading { 36 | text-align: center; 37 | padding-bottom: 1rem; 38 | position: relative; 39 | } 40 | 41 | .section-heading::before { 42 | content: ""; 43 | position: absolute; 44 | display: block; 45 | width: 240px; 46 | height: 1px; 47 | background: #ddd; 48 | bottom: 1px; 49 | left: calc(50% - 120px); 50 | } 51 | 52 | .section-heading::after { 53 | content: ""; 54 | position: absolute; 55 | display: block; 56 | width: 80px; 57 | height: 3px; 58 | background: var(--bs-primary); 59 | bottom: 0; 60 | left: calc(50% - 40px); 61 | } 62 | 63 | .section-copy { 64 | max-width: 768px; 65 | margin-left: auto; 66 | margin-right: auto; 67 | } 68 | 69 | //////////// 70 | /*Top Link*/ 71 | body.dark a.top-link { 72 | opacity: 1; 73 | transition: all ease-in-out 0.3s; 74 | visibility: visible; 75 | } 76 | a.top-link { 77 | bottom: 10px; 78 | opacity: 0; 79 | position: fixed; 80 | right: 10px; 81 | transition: all ease-in-out 0.3s; 82 | visibility: hidden; 83 | z-index: 4; 84 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import webfontDownload from "vite-plugin-webfont-dl"; 3 | import { ViteMinifyPlugin } from "vite-plugin-minify"; 4 | const IN_PRODUCTION = process.env.NODE_ENV === "production"; 5 | const IN_DEVELOPMENT = process.env.NODE_ENV === "development"; 6 | 7 | // Hide Preloader while in development. 8 | const hidePreloader = () => { 9 | return { 10 | name: "hide-preloader", 11 | transformIndexHtml(html) { 12 | return html.replace( 13 | ``, 14 | `` 15 | ); 16 | } 17 | } 18 | } 19 | 20 | export default defineConfig({ 21 | plugins: [ 22 | 23 | /* ## Hide Preloader while in Development 24 | --------------------------------------------- */ 25 | IN_DEVELOPMENT && hidePreloader(), 26 | 27 | /* ## Download Google Fonts and attach them with production build for offline use 28 | --------------------------------------------- */ 29 | IN_PRODUCTION && webfontDownload( 30 | [ 31 | "https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;700&display=swap", 32 | ] 33 | ), 34 | 35 | /* ## Minify the output HTML files in production 36 | --------------------------------------------- */ 37 | IN_PRODUCTION && ViteMinifyPlugin({}), 38 | ], 39 | 40 | css: { 41 | preprocessorOptions: { 42 | scss: { 43 | api: 'modern', 44 | } 45 | } 46 | }, 47 | 48 | base: "./", 49 | server: { 50 | port: 3000, 51 | }, 52 | 53 | build: { 54 | // outDir: "./docs", 55 | minify: 'terser', 56 | terserOptions: { 57 | format: { 58 | comments: false, 59 | }, 60 | }, 61 | }, 62 | 63 | }); 64 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 404 - Page Not Found 9 | 10 | 55 | 56 | 57 | 58 |
59 |
60 |
61 |

Oops!

62 |
63 |

404 - Page not found

64 |

Uh-oh! It seems the page you're looking for has either been removed, relocated, or may not exist at all. Before you 65 | venture further, double-check the URL in your browser. If all seems well, consider navigating back to our homepage to 66 | explore anew. Thanks for your understanding!

67 | Go To Homepage 68 |
69 |
70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/scss/_contactus.scss: -------------------------------------------------------------------------------- 1 | // contact form 2 | // import partials --- 3 | @use "./variables" as v; 4 | 5 | 6 | .contact-form { 7 | 8 | & label { 9 | margin-bottom: 0.2rem; 10 | color: var(--bs-primary); 11 | font-weight: 500; 12 | } 13 | 14 | & input, 15 | & textarea { 16 | background-color: v.$even-bg; 17 | border-radius: 5px; 18 | border: 1px solid var(--bs-primary); 19 | margin-bottom: 2rem; 20 | outline: 0; 21 | padding: 0.5rem; 22 | transition: v.$transition; 23 | width: 100%; 24 | } 25 | 26 | & input:focus, 27 | & textarea:focus { 28 | background-color: v.$odd-bg; 29 | box-shadow: inset 1px 1px 2px rgba(0, 0, 0, 0.25); 30 | border-color: v.$info; 31 | } 32 | } 33 | 34 | @media only screen and (max-width: 992px) { 35 | .contact-form { 36 | 37 | & input, 38 | & textarea { 39 | margin-bottom: 1rem; 40 | } 41 | } 42 | } 43 | 44 | 45 | //////////////////////// 46 | /// working hours widget 47 | .working-hours { 48 | border: 1px solid var(--bs-primary); 49 | 50 | & table { 51 | margin-bottom: 0; 52 | 53 | & .thead>tr>th { 54 | background-color: v.$even-bg; 55 | } 56 | 57 | & tbody>tr>td:first-child { 58 | font-weight: 500; 59 | } 60 | 61 | & tbody>tr:nth-child(odd)>td { 62 | background-color: v.$odd-bg; 63 | } 64 | 65 | & tbody>tr:nth-child(even)>td { 66 | background-color: v.$even-bg; 67 | } 68 | 69 | } 70 | 71 | } 72 | 73 | ///////////// 74 | // map widget 75 | .map-widget-container { 76 | position: relative; 77 | padding-bottom: 400px !important; 78 | height: 0; 79 | overflow: hidden; 80 | height: auto; 81 | border: 1px solid var(--bs-primary); 82 | display: block; 83 | 84 | & iframe { 85 | position: absolute; 86 | top: 0; 87 | left: 0; 88 | width: 100% !important; 89 | height: 100% !important; 90 | max-height: 400px !important; 91 | } 92 | } -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | // const autoprefixer = require("autoprefixer"); 2 | // const cssnano = require('cssnano'); 3 | // const purgecss = require("@fullhuman/postcss-purgecss"); 4 | const IN_PRODUCTION = process.env.NODE_ENV === "production"; 5 | const bootstrap = "node_modules/bootstrap" 6 | 7 | 8 | module.exports = { 9 | plugins: [ 10 | IN_PRODUCTION && 11 | require("@fullhuman/postcss-purgecss")({ 12 | content: [ 13 | "index.html", 14 | "./src/**/*.js", 15 | // --- Import only the required components. 16 | `${bootstrap}/js/dist/alert.js`, 17 | // `${bootstrap}/js/dist/base-component.js`, 18 | // `${bootstrap}/js/dist/button.js`, 19 | // `${bootstrap}/js/dist/carousel.js`, 20 | // `${bootstrap}/js/dist/collapse.js`, 21 | // `${bootstrap}/js/dist/dropdown.js`, 22 | // `${bootstrap}/js/dist/modal.js`, 23 | // `${bootstrap}/js/dist/offcanvas.js`, 24 | // `${bootstrap}/js/dist/popover.js`, 25 | // `${bootstrap}/js/dist/scrollspy.js`, 26 | // `${bootstrap}/js/dist/tab.js`, 27 | // `${bootstrap}/js/dist/toast.js`, 28 | // `${bootstrap}/js/dist/tooltip.js`, 29 | ], 30 | defaultExtractor(content) { 31 | const contentWithoutStyleBlocks = content.replace( 32 | //gi, 33 | "" 34 | ); 35 | return ( 36 | contentWithoutStyleBlocks.match( 37 | /[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g 38 | ) || [] 39 | ); 40 | }, 41 | keyframes: true, // remove unsed keyframe rules 42 | variables: true, // remove unused variables as well 43 | // safelist: [ 44 | // /-(leave|enter|appear)(|-(to|from|active))$/, 45 | // /^(?!(|.*?:)cursor-move).+-move$/, 46 | // /^router-link(|-exact)-active$/, 47 | // /data-v-.*/, 48 | // ], 49 | }), 50 | 51 | IN_PRODUCTION && require("autoprefixer"), 52 | 53 | IN_PRODUCTION && 54 | require("cssnano")({ 55 | preset: ["default", { discardComments: { removeAll: true } }], 56 | }), 57 | ], 58 | }; 59 | -------------------------------------------------------------------------------- /src/css/preloader.min.css: -------------------------------------------------------------------------------- 1 | #loader-wrapper{position:fixed;left:0;top:0;right:0;bottom:0;background:rgba(0,0,0,.95);z-index:999;overflow:hidden;height:100%}.hide-preloader{opacity:0;visibility:hidden;transition:all ease-out .5s}.loader{width:150px;height:150px;border:1px #fff solid;position:absolute;left:50%;top:50%;margin:-75px 0 0 -75px;border-radius:50%}.loader .loading{font-size:10px;position:absolute;width:100%;text-align:center;line-height:14px;font-style:italic;left:0;top:50%;margin-top:20px;color:#fff;font-weight:700;text-transform:uppercase}.loader-circle-1{width:138px;height:138px;left:5px;top:5px;border:1px #fff solid;border-radius:50%;position:absolute;border-right-color:transparent;animation:spin 3s linear infinite}.loader-circle-2{width:126px;height:126px;left:5px;top:5px;border:1px transparent solid;border-radius:50%;position:absolute;border-right-color:#e81512;animation:spin 5s linear infinite}.loader .line{width:10px;height:2px;background:#fff;position:absolute}.loader .line:nth-child(1){left:16px;top:50%;margin-top:-1px}.loader .line:nth-child(2){transform:rotate(45deg);left:33px;top:33px}.loader .line:nth-child(3){top:16px;left:50%;width:2px;height:10px}.loader .line:nth-child(4){transform:rotate(135deg);right:33px;top:33px}.loader .line:nth-child(5){right:16px;top:50%;margin-top:-1px}.loader .line:nth-child(6){transform:rotate(45deg);right:33px;bottom:33px;background:#e81512}.loader .subline{position:absolute;width:3px;height:2px;background:#fff}.loader .subline:nth-child(7){transform:rotate(22.5deg);left:21px;top:50px}.loader .subline:nth-child(8){transform:rotate(67.5deg);left:50px;top:21px}.loader .subline:nth-child(9){transform:rotate(112.5deg);right:50px;top:21px}.loader .subline:nth-child(10){transform:rotate(157.5deg);right:21px;top:50px}.loader .subline:nth-child(11){transform:rotate(22.5deg);right:20px;bottom:49px;background:#e81512}.loader .needle{width:14px;height:14px;border-radius:50%;border:1px #fff solid;position:absolute;left:50%;top:50%;margin:-8px 0 0 -8px;z-index:1;animation:pegIt 3s infinite ease-in-out}.loader .needle:before{content:"";width:0;height:0;border-style:solid;border-width:3.5px 50px 3.5px 0;border-color:transparent #e81512 transparent transparent;position:absolute;right:50%;top:50%;margin:-3.5px 0 0 0;border-radius:0 50% 50% 0}@keyframes pegIt{0%{transform:rotate(0)}16%{transform:rotate(75deg)}25%{transform:rotate(55deg)}30%{transform:rotate(90deg)}36%{transform:rotate(170deg)}42%{transform:rotate(150deg)}50%{transform:rotate(227deg)}100%{transform:rotate(0)}}@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Car Mechanic Shop HTML 5 Template 2 | 3 | ## Introduction 4 | 5 | This HTML 5 template is designed for car mechanic shops that want to create a professional-looking website. It is fully responsive and animated, which means it looks great on all devices. The template includes many features such as responsive navigation, introduction section with call us button, list of services with a counter, our services section with icons and brief info, testimonials section, contact section with call, WhatsApp and Google Maps embedded, and a working contact us form for static website that opens up the default email client and types the info automatically on submit button. 6 | 7 | ## Features 8 | 9 | ### Fully responsive and animated 10 | 11 | The template is fully responsive and animated, which means it looks great on all devices. The animations are smooth and add a professional touch to your website. 12 | 13 | ### Responsive navigation 14 | 15 | The navigation menu is responsive, which means it adapts to different screen sizes. This makes it easy for users to navigate your website on any device. 16 | 17 | ### Introduction section with call us button 18 | 19 | The introduction section includes a call us button that makes it easy for users to contact you. This is a great way to generate leads for your business. 20 | 21 | ### List of services with a counter 22 | 23 | The list of services includes a counter that shows how many services have been done. This is a great way to showcase your expertise and build trust with potential customers. 24 | 25 | ### Our services section with icons and brief info 26 | 27 | The our services section includes icons and brief info about each service you offer. This makes it easy for users to understand what you do. 28 | 29 | ### Testimonials section 30 | 31 | The testimonials section includes quotes from satisfied customers. This is a great way to build trust with potential customers. 32 | 33 | ### Contact section with call, WhatsApp and Google Maps embedded 34 | 35 | The contact section includes call, WhatsApp and Google Maps embedded. This makes it easy for users to contact you or find your location. 36 | 37 | ### Working contact us form for static website that opens up the default email client and types the info automatically on submit button 38 | 39 | The working contact us form opens up the default email client and types the info automatically on submit button. This makes it easy for users to contact you without leaving your website. 40 | 41 | ## Getting Started 42 | ```bash 43 | git clone https://github.com/saaqi/car-mechanic-shop.git 44 | ``` 45 | 46 | ### Install dependencies: 47 | ```bash 48 | cd car-mechanic-shop 49 | npm install 50 | ``` 51 | 52 | ### Start the development server: 53 | ```bash 54 | npm run dev 55 | ``` 56 | 57 | ### Build for production: 58 | ```bash 59 | npm run build 60 | ``` 61 | 62 | ### Git commit, bump patch version and Git push all in one command: 63 | ```bash 64 | npm run push 65 | ``` 66 | 67 | ## Credits 68 | 69 | This template is created and maintained by [Saqib Islam](https://saqibtech.com "Saqib Islam - UI/UX Designer & Fullstack Developer.") using HTML5, CSS3, BootStrap and JavaScript. 70 | 71 | ## License 72 | 73 | This template is licensed under the MIT License. You are free to use, modify, and distribute this template for personal or commercial purposes as long as you include the original license in your distribution. 74 | -------------------------------------------------------------------------------- /src/js/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file header.js 3 | * @description This file contains functions to for the header section of the page. 4 | */ 5 | 6 | // Navigation 7 | /* ## Set Mobile Navigation stuff 8 | --------------------------------------------- */ 9 | 10 | // Selecting the element 11 | const menuButton = document.querySelector(".menu-toggle"); 12 | const navigation = document.querySelector("nav.nav-primary"); 13 | 14 | /** 15 | * Toggles the visibility of the site navigation and updates the menu button state. 16 | * 17 | * This function is triggered when the menu button is clicked. It performs the following actions: 18 | * - Toggles the 'show' class on the navigation element to show/hide the navigation. 19 | * - Toggles the 'activated' and 'bx-x' classes on the menu button to update its appearance. 20 | * - Toggles the 'aria-expanded' attribute on the menu button to indicate whether the menu is expanded. 21 | * - Toggles the 'aria-pressed' attribute on the menu button to indicate whether the button is pressed. 22 | */ 23 | menuButton.addEventListener("click", function () { 24 | // Show site navigation 25 | navigation.classList.toggle("show"); 26 | 27 | // Toggle activated class 28 | menuButton.classList.toggle("activated"); 29 | menuButton.classList.toggle("bx-x"); 30 | 31 | // Toggle attrs 32 | if (menuButton.getAttribute("aria-expanded") === "true") { 33 | menuButton.setAttribute("aria-expanded", "false"); 34 | } else { 35 | menuButton.setAttribute("aria-expanded", "true"); 36 | } 37 | 38 | if (menuButton.getAttribute("aria-pressed") === "true") { 39 | menuButton.setAttribute("aria-pressed", "false"); 40 | } else { 41 | menuButton.setAttribute("aria-pressed", "true"); 42 | } 43 | }); 44 | 45 | 46 | /** 47 | * Adds click event listeners to menu links to collapse the navigation menu. 48 | * 49 | * This functionality ensures that when any of the specified links are clicked, the main navigation 50 | * menu is hidden and the menu button state is reset. 51 | */ 52 | 53 | // Collapse menu on click 54 | const menuLinks = document.querySelectorAll( 55 | ".primary-menu .menu-item a, a.top-link, .site-title a" 56 | ); 57 | 58 | menuLinks.forEach((eachLink) => { 59 | eachLink.addEventListener("click", function () { 60 | // Hide Main Navigation on click 61 | navigation.classList.remove("show"); 62 | 63 | // Remove activated class and attributes from menu-toggle button 64 | menuButton.classList.remove("activated"); 65 | menuButton.classList.remove("bx-x"); 66 | menuButton.setAttribute("aria-expanded", "false"); 67 | menuButton.setAttribute("aria-pressed", "false"); 68 | }); 69 | }); 70 | 71 | /* ## Add dark claass to the header and top link 72 | --------------------------------------------- */ 73 | window.addEventListener("scroll", () => { 74 | if (window.scrollY >= 100) { 75 | document.querySelector("body").classList.add("dark"); 76 | } else { 77 | document.querySelector("body").classList.remove("dark"); 78 | } 79 | }); 80 | 81 | 82 | 83 | 84 | 85 | 86 | /* ## SETUP SCROLL SPY 87 | --------------------------------------------- */ 88 | let menuSection = document.querySelectorAll(".nav-primary li.menu-item a"); 89 | // for clickable event 90 | menuSection.forEach((v) => { 91 | v.onclick = () => { 92 | setTimeout(() => { 93 | menuSection.forEach((j) => j.classList.remove("active")); 94 | v.classList.add("active"); 95 | }, 300); 96 | }; 97 | }); 98 | // for window scrolldown event 99 | window.onscroll = () => { 100 | let mainSection = document.querySelectorAll( 101 | "main.entry-content section.section" 102 | ); 103 | 104 | mainSection.forEach((v, i) => { 105 | let rect = v.getBoundingClientRect().y; 106 | 107 | if (rect < window.innerHeight - window.innerHeight + 100) { 108 | /* caculate till section reaches to top */ 109 | menuSection.forEach((v) => v.classList.remove("active")); 110 | menuSection[i].classList.add("active"); 111 | } 112 | }); 113 | }; 114 | 115 | // NATIVE JAVASCRIPT WAY 116 | // document.querySelectorAll('a[href^="#"]').forEach(anchor => { 117 | // anchor.addEventListener('click', function (e) { 118 | // e.preventDefault(); 119 | 120 | // document.querySelector(this.getAttribute('href')).scrollIntoView(); 121 | // }); 122 | // }); 123 | 124 | -------------------------------------------------------------------------------- /src/css/preloader.css: -------------------------------------------------------------------------------- 1 | #loader-wrapper { 2 | position: fixed; 3 | left: 0; 4 | top: 0; 5 | right: 0; 6 | bottom: 0; 7 | background: rgba(0, 0, 0, 0.95); 8 | z-index: 999; 9 | overflow: hidden; 10 | height: 100%; 11 | } 12 | 13 | .hide-preloader { 14 | opacity: 0; 15 | visibility: hidden; 16 | transition: all ease-out 0.5s; 17 | } 18 | 19 | .loader { 20 | width: 150px; 21 | height: 150px; 22 | border: 1px #fff solid; 23 | position: absolute; 24 | left: 50%; 25 | top: 50%; 26 | margin: -75px 0 0 -75px; 27 | border-radius: 50%; 28 | } 29 | 30 | .loader .loading { 31 | font-size: 10px; 32 | position: absolute; 33 | width: 100%; 34 | text-align: center; 35 | line-height: 14px; 36 | font-style: italic; 37 | left: 0; 38 | top: 50%; 39 | margin-top: 20px; 40 | color: #fff; 41 | font-weight: bold; 42 | text-transform: uppercase; 43 | } 44 | 45 | .loader-circle-1 { 46 | width: 138px; 47 | height: 138px; 48 | left: 5px; 49 | top: 5px; 50 | border: 1px #fff solid; 51 | border-radius: 50%; 52 | position: absolute; 53 | border-right-color: transparent; 54 | animation: spin 3s linear infinite; 55 | } 56 | 57 | .loader-circle-2 { 58 | width: 126px; 59 | height: 126px; 60 | left: 5px; 61 | top: 5px; 62 | border: 1px transparent solid; 63 | border-radius: 50%; 64 | position: absolute; 65 | border-right-color: #e81512; 66 | animation: spin 5s linear infinite; 67 | } 68 | 69 | .loader .line { 70 | width: 10px; 71 | height: 2px; 72 | background: #fff; 73 | position: absolute; 74 | } 75 | 76 | .loader .line:nth-child(1) { 77 | left: 16px; 78 | top: 50%; 79 | margin-top: -1px; 80 | } 81 | 82 | .loader .line:nth-child(2) { 83 | transform: rotate(45deg); 84 | left: 33px; 85 | top: 33px; 86 | } 87 | 88 | .loader .line:nth-child(3) { 89 | top: 16px; 90 | left: 50%; 91 | width: 2px; 92 | height: 10px; 93 | } 94 | 95 | .loader .line:nth-child(4) { 96 | transform: rotate(135deg); 97 | right: 33px; 98 | top: 33px; 99 | } 100 | 101 | .loader .line:nth-child(5) { 102 | right: 16px; 103 | top: 50%; 104 | margin-top: -1px; 105 | } 106 | 107 | .loader .line:nth-child(6) { 108 | transform: rotate(45deg); 109 | right: 33px; 110 | bottom: 33px; 111 | background: #e81512; 112 | } 113 | 114 | .loader .subline { 115 | position: absolute; 116 | width: 3px; 117 | height: 2px; 118 | background: #fff; 119 | } 120 | 121 | .loader .subline:nth-child(7) { 122 | transform: rotate(22.5deg); 123 | left: 21px; 124 | top: 50px; 125 | } 126 | 127 | .loader .subline:nth-child(8) { 128 | transform: rotate(67.5deg); 129 | left: 50px; 130 | top: 21px; 131 | } 132 | 133 | .loader .subline:nth-child(9) { 134 | transform: rotate(112.5deg); 135 | right: 50px; 136 | top: 21px; 137 | } 138 | 139 | .loader .subline:nth-child(10) { 140 | transform: rotate(157.5deg); 141 | right: 21px; 142 | top: 50px; 143 | } 144 | 145 | .loader .subline:nth-child(11) { 146 | transform: rotate(22.5deg); 147 | right: 20px; 148 | bottom: 49px; 149 | background: #e81512; 150 | } 151 | 152 | .loader .needle { 153 | width: 14px; 154 | height: 14px; 155 | border-radius: 50%; 156 | border: 1px #fff solid; 157 | position: absolute; 158 | left: 50%; 159 | top: 50%; 160 | margin: -8px 0 0 -8px; 161 | z-index: 1; 162 | animation: pegIt 3s infinite ease-in-out; 163 | } 164 | 165 | .loader .needle:before { 166 | content: ""; 167 | width: 0; 168 | height: 0; 169 | border-style: solid; 170 | border-width: 3.5px 50px 3.5px 0; 171 | border-color: transparent #e81512 transparent transparent; 172 | position: absolute; 173 | right: 50%; 174 | top: 50%; 175 | margin: -3.5px 0 0 0; 176 | border-radius: 0 50% 50% 0; 177 | } 178 | @keyframes pegIt { 179 | 0% { 180 | transform: rotate(0deg); 181 | } 182 | 16% { 183 | transform: rotate(75deg); 184 | } 185 | 25% { 186 | transform: rotate(55deg); 187 | } 188 | 30% { 189 | transform: rotate(90deg); 190 | } 191 | 36% { 192 | transform: rotate(170deg); 193 | } 194 | 42% { 195 | transform: rotate(150deg); 196 | } 197 | 50% { 198 | transform: rotate(227deg); 199 | } 200 | 100% { 201 | transform: rotate(0deg); 202 | } 203 | } 204 | @keyframes spin { 205 | 0% { 206 | transform: rotate(0deg); 207 | } 208 | 100% { 209 | transform: rotate(360deg); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/scss/_navigation.scss: -------------------------------------------------------------------------------- 1 | /* Navigation */ 2 | // import partials --- 3 | @use "./variables" as v; 4 | 5 | /* Site Header 6 | --------------------------------------------- */ 7 | .site-header { 8 | background-color: transparent; 9 | left: 0; 10 | position: fixed; 11 | top: 0; 12 | padding-top: 10px; 13 | width: 100%; 14 | z-index: 11; 15 | } 16 | 17 | .dark .site-header { 18 | background-color: rgba(55, 65, 98, 0.95); 19 | padding-top: 0; 20 | border-bottom: 1px solid var(--bs-primary); 21 | box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25); 22 | } 23 | 24 | .nav-flex { 25 | z-index: 10; 26 | display: grid; 27 | width: 100%; 28 | grid-template-columns: 2fr 7.5fr; 29 | } 30 | 31 | .site-title { 32 | margin-bottom: 0; 33 | } 34 | 35 | .site-title > a { 36 | background: url('/src/assets/media/cc-logo.svg') no-repeat left bottom; 37 | background-size: contain; 38 | display: block; 39 | height: 76px; 40 | text-indent: -9999px; 41 | margin-bottom: 0; 42 | z-index: -99; 43 | filter: drop-shadow(1px 1px 4px rgba(0, 0, 0, 0.3)); 44 | } 45 | 46 | .dark .site-title>a { 47 | height: 66px; 48 | } 49 | 50 | .site-description { 51 | display: block; 52 | height: 0; 53 | margin-bottom: 0; 54 | text-indent: -9999px; 55 | } 56 | 57 | /* Site Navigation 58 | ---------------------------------------------------------------------------------------------------- */ 59 | .primary-menu { 60 | display: flex; 61 | gap: 20px; 62 | justify-content: flex-end; 63 | flex-wrap: wrap; 64 | max-width: 1200px; 65 | } 66 | 67 | ul.primary-menu { 68 | margin: 0; 69 | list-style: none; 70 | } 71 | 72 | .primary-menu .menu-item { 73 | color: var(--bs-light); 74 | text-shadow: v.$text-shadow; 75 | font-size: .875rem; 76 | font-weight: 500; 77 | line-height: 76px; 78 | text-transform: uppercase; 79 | } 80 | 81 | .dark .primary-menu .menu-item { 82 | line-height: 66px; 83 | } 84 | 85 | .dark .primary-menu .menu-item:has(a.active) { 86 | border-bottom: 1px solid var(--bs-secondary); 87 | } 88 | 89 | .primary-menu a.active, 90 | .primary-menu a:hover { 91 | color: var(--bs-secondary); 92 | } 93 | 94 | 95 | /* Media Queries 96 | ---------------------------------------------------------------------------------------------------- */ 97 | /* For Large Devices only */ 98 | @media only screen and (min-width: 769px) { 99 | 100 | /* Responsive Menu 101 | --------------------------------------------- */ 102 | .menu-toggle { 103 | display: none !important; 104 | visibility: hidden; 105 | } 106 | 107 | .nav-primary { 108 | display: block !important; 109 | } 110 | } 111 | 112 | @media only screen and (max-width: 992px) { 113 | .nav-flex { 114 | grid-template-columns: 80% 20%; 115 | } 116 | 117 | nav.nav-primary { 118 | max-height: 0; 119 | transition: v.$transition; 120 | overflow: hidden; 121 | position: relative; 122 | } 123 | 124 | nav.nav-primary.show { 125 | margin-top: 2rem; 126 | max-height: 100svh; 127 | visibility: visible; 128 | } 129 | 130 | ul.primary-menu { 131 | margin-bottom: 20px; 132 | } 133 | 134 | .site-header { 135 | background-color: rgba(55, 65, 98, 0.95); 136 | padding-top: 0; 137 | border-bottom: 1px solid var(--bs-primary); 138 | box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25); 139 | } 140 | 141 | .site-title>a, 142 | .dark .site-title>a { 143 | height: 66px; 144 | } 145 | 146 | .menu-toggle { 147 | background: transparent; 148 | border-width: 0; 149 | color: var(--bs-light); 150 | cursor: pointer; 151 | display: block !important; 152 | margin-top: 2px; 153 | padding: 0; 154 | text-align: right; 155 | visibility: visible; 156 | } 157 | 158 | .primary-menu { 159 | flex-wrap: wrap; 160 | margin-bottom: 20px; 161 | } 162 | 163 | .primary-menu .menu-item { 164 | display: block; 165 | font-size: 1rem; 166 | margin-left: calc(10% + 10px); 167 | width: 100%; 168 | line-height: 30px !important; 169 | } 170 | 171 | .menu-toggle:focus, 172 | .menu-toggle:hover { 173 | background-color: transparent; 174 | border-width: 0; 175 | } 176 | 177 | .menu-toggle::before { 178 | border-radius: 40px; 179 | display: inline-block; 180 | font-size: 2.5rem; 181 | padding: 5px; 182 | text-rendering: auto; 183 | transform: rotateX(0); 184 | transition: v.$transition; 185 | } 186 | 187 | .menu-toggle.activated::before { 188 | transform: rotateX(180deg); 189 | color: var(--bs-secondary); 190 | } 191 | 192 | .menu-toggle::before, 193 | .menu-toggle.activated::before { 194 | text-rendering: auto; 195 | } 196 | } 197 | 198 | /* navigation */ -------------------------------------------------------------------------------- /src/js/whyus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/js/whyus.js 3 | * @description This file contains functions to for Setting up Counter Boxes. 4 | */ 5 | 6 | const counterBoxesData = [ 7 | { 8 | id: "business-counter", 9 | number: 5, 10 | text: "Years in Business", 11 | }, 12 | { 13 | id: "tires-counter", 14 | number: 650, 15 | text: "Tires Changed", 16 | }, 17 | { 18 | id: "wheels-counter", 19 | number: 2250, 20 | text: "Wheels Aligned", 21 | }, 22 | { 23 | id: "oil-counter", 24 | number: 5000, 25 | text: "Oils Changed", 26 | }, 27 | ]; 28 | const counterBoxesHtml = counterBoxesData.map((cb) => { 29 | const id = cb.id ? cb.id : ``; 30 | const number = cb.number ? cb.number : ``; 31 | const text = cb.text ? cb.text : ``; 32 | const output = 33 | `
34 |
35 |
36 |
37 | ${number}+ 38 |
39 |
40 |
${text}
41 |
42 |
`; 43 | return output; 44 | }); 45 | const counterBoxesElement = document.getElementById("counter-boxes"); 46 | if (counterBoxesElement) counterBoxesElement.innerHTML = counterBoxesHtml.join(""); 47 | 48 | // animate counters 49 | const reasonCounter = document.querySelectorAll(`.counter-number`); 50 | reasonCounter.forEach(reason => { 51 | if (reason) countWhenVisible(reason, reason.innerHTML, 1000); 52 | }) 53 | function countWhenVisible(element, targetCount, speed) { 54 | let hasCounted = false; 55 | let startTime = null; 56 | let observer = new IntersectionObserver((entries) => { 57 | if (entries[0].isIntersecting && !hasCounted) { 58 | hasCounted = true; 59 | startTime = performance.now(); 60 | let count = 0; 61 | let duration = speed; 62 | let interval = setInterval(() => { 63 | let elapsedTime = performance.now() - startTime; 64 | let progress = elapsedTime / duration; 65 | if (progress >= 1) { 66 | clearInterval(interval); 67 | element.innerHTML = targetCount; 68 | } else { 69 | count = Math.floor(progress * targetCount); 70 | element.innerHTML = count; 71 | } 72 | }, 20); 73 | } 74 | }); 75 | if (element) observer.observe(element); 76 | } 77 | 78 | 79 | 80 | 81 | // Setup WhyUs Counter Boxes 82 | const reasonBoxesData = [ 83 | { 84 | title: `Unmatched Expertise`, 85 | body: `At Car Club Tire & Auto Repair Service, our team of highly skilled and certified technicians brings years of experience and expertise to the table. You can trust us to handle your vehicle with precision and care, ensuring top-notch repairs and maintenance.`, 86 | }, 87 | { 88 | title: `Customer-Centric Approach`, 89 | body: `We prioritize your satisfaction and go the extra mile to provide personalized attention to every customer. Our friendly and approachable team is always ready to listen to your concerns and offer tailored solutions that suit your specific needs.`, 90 | }, 91 | { 92 | title: `Wide Range of Services`, 93 | body: `From tire work and maintenance to comprehensive auto repairs, we offer a diverse range of services under one roof. Whether it's routine maintenance or complex repairs, we've got you covered, saving you time and hassle.`, 94 | }, 95 | { 96 | title: `Quality and Reliability`, 97 | body: `We are committed to using top-quality parts and materials in all our services. Our emphasis on reliability ensures that your vehicle receives the best possible care, enhancing its performance and longevity.`, 98 | }, 99 | { 100 | title: `Transparent Pricing`, 101 | body: `At Car Club, we believe in transparent and fair pricing. You can count on us to provide honest estimates and no hidden fees, allowing you to make informed decisions without any surprises.`, 102 | }, 103 | { 104 | title: `Swift Turnaround`, 105 | body: `We understand that your time is valuable, which is why we strive to deliver efficient and timely service. Our team works diligently to get you back on the road as quickly as possible, without compromising on quality.`, 106 | }, 107 | ]; 108 | const reasonBoxesHtml = reasonBoxesData.map((rs) => { 109 | const title = rs.title ? rs.title : ``; 110 | const body = rs.body ? rs.body : ``; 111 | const output = 112 | `
113 |
114 |
115 |
${title}
116 | ${body} 117 |
118 |
119 |
`; 120 | return output; 121 | }); 122 | const reasaonBoxesElement = document.getElementById("reason-boxes"); 123 | if (reasaonBoxesElement) reasaonBoxesElement.innerHTML = reasonBoxesHtml.join(""); -------------------------------------------------------------------------------- /src/js/aboutus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file aboutus.js 3 | * @description This file sets up the service slides for the "About Us" section of the page, 4 | * generating HTML content dynamically based on predefined service data. 5 | */ 6 | 7 | /** 8 | * @constant {Array} serviceSlidesData 9 | * @description An array of objects representing the data for each service slide. 10 | * @property {string} icon - The HTML string for the service icon. 11 | * @property {string} title - The title of the service. 12 | * @property {string} body - The description of the service. 13 | */ 14 | const serviceSlidesData = [ 15 | { 16 | icon: ``, 17 | title: `Tire Work`, 18 | body: `Whether you need tire repairs or replacements, our skilled technicians are here to keep your wheels rolling safely and smoothly.`, 19 | }, 20 | { 21 | icon: ``, 22 | title: `New & Used Tire`, 23 | body: `We offer a selection of high-quality new and used tires that provide excellent performance and value for your vehicle.`, 24 | }, 25 | { 26 | icon: ``, 27 | title: `Tire Disposal`, 28 | body: `As part of our commitment to eco-friendly practices, we can dispose of your unsuable tires responsibly for a nominal fee.`, 29 | }, 30 | { 31 | icon: ``, 32 | title: `Patch & Plug`, 33 | body: `Our professional tire patching service restores damaged tires to safe conditions, saving you from the cost of replacement.`, 34 | }, 35 | { 36 | icon: ``, 37 | title: `TPMS & Install`, 38 | body: `Our technicians can install and service Tire Pressure Monitoring System (TPMS) sensors to keep you informed about your tire pressure.`, 39 | }, 40 | { 41 | icon: ``, 42 | title: `Wheel Alignment`, 43 | body: `Improve handling and tire longevity with precision wheel alignment, ensuring all wheels are correctly oriented.`, 44 | }, 45 | { 46 | icon: ``, 47 | title: `A/C Recharge & Dye`, 48 | body: `Stay cool during hot days with our A/C recharge service using the best refrigerant and dye for leak detection.`, 49 | }, 50 | { 51 | icon: ``, 52 | title: `Oil Change & Filter`, 53 | body: `Keep your engine running smoothly and efficiently with our oil change service, including a new filter installation.`, 54 | }, 55 | { 56 | icon: ``, 57 | title: `Front & Rear Brake Pads`, 58 | body: `Ensure reliable stopping power with our front and rear brake pad replacement, using top-quality brake pads for safety and performance.`, 59 | }, 60 | { 61 | icon: ``, 62 | title: `New Battery`, 63 | body: `If it's time for a new battery, trust our experts to install a reliable and durable replacement for your vehicle.`, 64 | }, 65 | { 66 | icon: ``, 67 | title: `Wiper Blades`, 68 | body: `Improve visibility during rain or snow with new wiper blades that fit your vehicle perfectly.`, 69 | }, 70 | { 71 | icon: ``, 72 | title: `Cabin Air Filters`, 73 | body: `Breathe clean air inside your car with our cabin air filter replacement service.`, 74 | }, 75 | { 76 | icon: ``, 77 | title: `Suspension & Steering`, 78 | body: `Experience smoother rides and enhanced handling with our expert suspension and steering services with us. Drive with confidence.`, 79 | }, 80 | { 81 | icon: ``, 82 | title: `Exhaust System repairing`, 83 | body: `Experience smoother rides and enhanced handling with expert suspension and steering services. visit us today!`, 84 | }, 85 | { 86 | icon: ``, 87 | title: `Car electrical system repairs`, 88 | body: `For reliable car electrical system repairs, trust our experienced technicians. Drive worry-free with our top-notch service.`, 89 | }, 90 | { 91 | icon: ``, 92 | title: `Check engine light diagnosis`, 93 | body: `Is your check engine light on? Don't worry! Our skilled technicians specialize in check engine light diagnosis.`, 94 | }, 95 | ]; 96 | 97 | /** 98 | * @function generateSlideBoxesHtml 99 | * @description Generates HTML for each service slide based on the serviceSlidesData array. 100 | * @param {Array} slidesData - The array of service slide data. 101 | * @returns {Array} An array of HTML strings for each service slide. 102 | */ 103 | const generateSlideBoxesHtml = (slidesData) => { 104 | return slidesData.map((sl) => { 105 | const body = sl.body ? sl.body : ``; 106 | const icon = sl.icon ? sl.icon : ``; 107 | const title = sl.title ? sl.title : ``; 108 | const output = 109 | `
  • `+ 110 | `
    `+ 111 | `
    `+ 112 | `
    ${icon} ${title}
    `+ 113 | `${body}`+ 114 | `
    `+ 115 | `
    `+ 116 | `
  • `; 117 | return output; 118 | }); 119 | }; 120 | 121 | /** 122 | * @constant {HTMLElement} slideBoxesElement 123 | * @description The DOM element where the service slides will be inserted. 124 | */ 125 | const slideBoxesElement = document.getElementById("service-slides"); 126 | 127 | /** 128 | * @description If the slideBoxesElement exists, set its innerHTML to the generated slide boxes HTML. 129 | */ 130 | if (slideBoxesElement) slideBoxesElement.innerHTML = generateSlideBoxesHtml(serviceSlidesData).join(""); -------------------------------------------------------------------------------- /src/js/contact.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file contact.js 3 | * @description This file contains functions to determine the store status based on the current date and time, 4 | * update the store status in real-time, and handle the contact form submission with validation. 5 | */ 6 | 7 | 8 | // Function to determine the store status (open or closed) based on the current date and time 9 | /** 10 | * @function storeStatus 11 | * @description Determines the store status (open or closed) based on the current date and time. 12 | * @returns {string} The formatted store status message. 13 | */ 14 | function storeStatus() { 15 | const timeZone = "America/New_York"; 16 | const now = new Date(); 17 | const localTime = new Date(now.toLocaleString("en-US", { timeZone: timeZone })); 18 | const options = { weekday: "long" }; 19 | const formattedTime = now.toLocaleTimeString("en-US", { 20 | hour: "numeric", 21 | minute: "2-digit", 22 | hour12: true, 23 | timeZone: timeZone, 24 | }); 25 | const orangeCityDayToday = new Intl.DateTimeFormat("en-US", options).format(localTime); 26 | const outputDay = `${orangeCityDayToday} ${formattedTime}`; 27 | const hours = localTime.getHours(); 28 | const dayOfWeek = localTime.toLocaleString("en-US", { timeZone, weekday: "long" }); 29 | 30 | let outputTime; 31 | 32 | if ( 33 | (dayOfWeek === "Sunday" && hours >= 9 && hours < 17) || 34 | (dayOfWeek !== "Sunday" && hours >= 8 && hours < 19) 35 | ) { 36 | outputTime = 'Open come on down'; 37 | } else if ( 38 | (dayOfWeek === "Sunday" && hours >= 17 && hours < 24) || 39 | (dayOfWeek !== "Sunday" && hours >= 19 && hours < 24) 40 | ) { 41 | outputTime = 'Closed at the moment see you tomorrow. 🙂'; 42 | } else if (hours >= 0 && (dayOfWeek === "Sunday" ? hours < 8 : hours < 9)) { 43 | if (dayOfWeek === "Sunday" && hours < 8) { 44 | outputTime = 'Closed at the moment see you at 08:00 AM. 😴'; 45 | } else if (dayOfWeek !== "Sunday" && hours < 9) { 46 | outputTime = 'Closed at the moment see you at 09:00 AM. 😴'; 47 | } 48 | } 49 | 50 | return `It is ${outputDay}, We are ${outputTime}`; 51 | } 52 | 53 | // Function to update the store status in real-time 54 | /** 55 | * @function updateStatusRealtime 56 | * @description Updates the store status in real-time by modifying the innerHTML of the element with ID "storestatus". 57 | */ 58 | function updateStatusRealtime() { 59 | document.getElementById("storestatus").innerHTML = `${storeStatus()}`; 60 | } 61 | 62 | // Initial call to update the store status 63 | updateStatusRealtime(); 64 | 65 | // Set an interval to update the store status every 15 seconds 66 | setInterval(updateStatusRealtime, 15000); 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | // Import Bootstrap's alert component 76 | import "bootstrap/js/dist/alert.js"; 77 | 78 | // Define the form ID and get the form element 79 | const formId = 'homepage-contact-form'; 80 | const contactForm = document.getElementById(formId); 81 | 82 | if (contactForm) { 83 | const nameInput = document.querySelector('#' + formId + ' #contact-form-name'); 84 | const emailInput = document.querySelector('#' + formId + ' #contact-form-email'); 85 | const messageInput = document.querySelector('#' + formId + ' #contact-form-message'); 86 | 87 | // Function to validate email format using a regular expression 88 | /** 89 | * @function validateEmail 90 | * @description Validates the email format using a regular expression. 91 | * @param {string} email - The email address to validate. 92 | * @returns {boolean} True if the email format is valid, otherwise false. 93 | */ 94 | const validateEmail = (email) => { 95 | const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 96 | return emailRegex.test(email); 97 | } 98 | 99 | const alertPlaceholder = document.querySelector('#' + formId + ' #errorAlerts'); 100 | 101 | // Function to append an alert message to the alert placeholder 102 | /** 103 | * @function appendAlert 104 | * @description Appends an alert message to the alert placeholder. 105 | * @param {string} message - The alert message to display. 106 | */ 107 | const appendAlert = message => { 108 | alertPlaceholder.innerHTML = [ 109 | `' 113 | ].join(''); 114 | } 115 | 116 | // Function to validate the form inputs 117 | /** 118 | * @function validateForm 119 | * @description Validates the form inputs. 120 | * @returns {boolean} True if all form inputs are valid, otherwise false. 121 | */ 122 | const validateForm = () => { 123 | if (nameInput.value.trim() === '') { 124 | appendAlert('Please enter your name'); 125 | return false; 126 | } 127 | if (emailInput.value.trim() === '' || !validateEmail(emailInput.value)) { 128 | appendAlert('Please enter a valid email address.'); 129 | return false; 130 | } 131 | if (messageInput.value.trim() === '') { 132 | appendAlert('Please enter a message.'); 133 | return false; 134 | } 135 | return true; 136 | } 137 | 138 | // Add an event listener for form submission 139 | contactForm.addEventListener("submit", function (event) { 140 | event.preventDefault(); 141 | 142 | const alertDismiss = this.querySelectorAll('#' + formId + ' #errorAlerts > *'); 143 | if (alertDismiss) { 144 | alertDismiss.forEach(e => { 145 | e.remove(); 146 | }); 147 | } 148 | 149 | if (!validateForm()) { 150 | return false; 151 | } else { 152 | const receiveEmail = "carclubtire@gmail.com"; 153 | const subject = `[Contact-Form] ${nameInput.value}`; 154 | const body = `${messageInput.value}\n\n${nameInput.value}\n${emailInput.value}`; 155 | const mailtoUrl = `mailto:${encodeURIComponent(receiveEmail)}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; 156 | window.open(mailtoUrl); 157 | } 158 | }); 159 | } -------------------------------------------------------------------------------- /src/js/deals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file deals.js 3 | * @description This file sets up the alert boxes and deals slides for the webpage, 4 | * generating HTML content dynamically based on predefined data. 5 | */ 6 | 7 | // Setup Alert Boxes 8 | 9 | /** 10 | * @constant {Array} alertBoxesData 11 | * @description An array of objects representing the data for each alert box. 12 | * @property {string} body - The message to be displayed in the alert box. 13 | */ 14 | const alertBoxesData = [ 15 | { 16 | body: `We are Open 7 days a week.`, 17 | }, 18 | { 19 | body: `Both Appointments and Walkins are Welcome.`, 20 | }, 21 | { 22 | body: `Special Discounts for Veterans & Military Personel, Students, and Senior Citizens.`, 23 | }, 24 | ]; 25 | 26 | /** 27 | * @function generateAlertBoxHtml 28 | * @description Generates HTML for each alert box based on the alertBoxesData array. 29 | * @param {Array} boxesData - The array of alert box data. 30 | * @returns {Array} An array of HTML strings for each alert box. 31 | */ 32 | const generateAlertBoxHtml = (boxesData) => { 33 | return boxesData.map((bx) => { 34 | const body = bx.body ? bx.body : ``; // Use the body if available, otherwise use an empty string 35 | return `
  • ${body}
  • `; // Wrap the body in a list item 36 | }); 37 | }; 38 | 39 | /** 40 | * @constant {HTMLElement} alertBoxesElement 41 | * @description The DOM element where the alert boxes will be inserted. 42 | */ 43 | const alertBoxesElement = document.querySelector("#alert-boxes .card-body"); 44 | 45 | /** 46 | * @description If the alertBoxesElement exists, set its innerHTML to the generated alert boxes HTML. 47 | */ 48 | if (alertBoxesElement) alertBoxesElement.innerHTML = generateAlertBoxHtml(alertBoxesData).join(""); 49 | 50 | // Setup Deals Slides 51 | 52 | /** 53 | * @constant {Array} dealsSlidesData 54 | * @description An array of objects representing the data for each deal slide. 55 | * @property {string} icon - The HTML string for the deal icon. 56 | * @property {string} title - The title of the deal. 57 | * @property {string} body - The description of the deal. 58 | */ 59 | const dealsSlidesData = [ 60 | { 61 | icon: ``, 62 | title: `Wheel Alignment starting at just $59.99`, 63 | body: `🚗 Car pulling left or right? Steering feeling off? You might need a wheel alignment! 64 | For a limited time only, get your wheel alignment today!`, 65 | price: 59.99 66 | }, 67 | { 68 | icon: ``, 69 | title: `Wheel Alignment + Synthetic Oil Change for Just $79.99!`, 70 | body: `Is your car pulling to one side? Feeling a little shaky on the road? It might be time for a wheel alignment! And while you're at it, give your engine the fresh start it deserves with a synthetic oil change.`, 71 | price: 79.99 72 | }, 73 | { 74 | icon: ``, 75 | title: `Coolant + Brake Flush starting at just $79.99`, 76 | body: `Did you know neglecting your coolant & brake fluid can lead to overheating, poor braking, and expensive repairs? 😨 Don't wait until it's too late!`, 77 | price: 79.99 78 | }, 79 | { 80 | icon: ``, 81 | title: `AC System Flush starting at $99.99!`, 82 | body: `Don't let Florida's heat turn your car into a sauna! ☀️ If your AC isn't cooling like it used to, it might be time for a coolant flush.`, 83 | price: 99.99 84 | 85 | }, 86 | // { 87 | // icon: ``, 88 | // title: `Conventional Oil Change for just $18.99`, 89 | // body: `Get your car running smoothly with our oil change specials! Conventional oil change for $18.99. Quality service at great prices!`, 90 | // }, 91 | // { 92 | // icon: ``, 93 | // title: `Full Tune Up Starting from just $249.99`, 94 | // body: `Tune up starting at just $249.99! Limited time offer. Optimize performance and reliability. Visit us today!`, 95 | // }, 96 | // { 97 | // icon: ``, 98 | // title: `4 New Tires from $399.99 & Up`, 99 | // body: `Get rolling with a fantastic deal! 4 new tires for $399.99 & free wheel alignment. Limited time offer. Visit us now!`, 100 | // }, 101 | // { 102 | // icon: ``, 103 | // title: `4 Used Tires from $199.99 & Up`, 104 | // body: `Get road-ready with our special deal! 4 used tires for $199.99 & free wheel alignment. Limited time offer. Visit us now!`, 105 | // }, 106 | // { 107 | // icon: ``, 108 | // title: `FREE Check Engine Light diagnostics.`, 109 | // body: `Worried about your check engine light? Get a FREE diagnostic from our expert technicians and drive with confidence!`, 110 | // }, 111 | // { 112 | // icon: ``, 113 | // title: `Free Vehicle Inspection with any service.`, 114 | // body: `Comprehensive vehicle inspection for FREE with any service! Limited time offer. Visit us today!`, 115 | // }, 116 | ]; 117 | 118 | /** 119 | * @function generateDealsSlidesHtml 120 | * @description Generates HTML for each deal slide based on the dealsSlidesData array. 121 | * @param {Array} slidesData - The array of deal slide data. 122 | * @returns {Array} An array of HTML strings for each deal slide. 123 | */ 124 | const generateDealsSlidesHtml = (slidesData) => { 125 | return slidesData.map((sl) => { 126 | const { 127 | icon = '', 128 | title = '', 129 | body = '', 130 | price = '' 131 | } = sl; 132 | 133 | const output = 134 | `
  • ` + 135 | `
    ` + 136 | `
    ` + 137 | `
    ` + 138 | `
    ${icon}
    ` + 139 | `
    ${title}
    ` + 140 | `
    ` + 141 | `
    142 |

    ${body}

    143 | ${price ? `
    $ ${price.toFixed(2)}
    ` : ''} 144 |
    ` + 145 | `
    ` + 146 | `
  • `; 147 | return output; 148 | }); 149 | }; 150 | 151 | /** 152 | * @constant {HTMLElement} dealsSlidesElement 153 | * @description The DOM element where the deals slides will be inserted. 154 | */ 155 | const dealsSlidesElement = document.getElementById("deals-slides"); 156 | 157 | /** 158 | * @description If the dealsSlidesElement exists, set its innerHTML to the generated deals slides HTML. 159 | */ 160 | if (dealsSlidesElement) dealsSlidesElement.innerHTML = generateDealsSlidesHtml(dealsSlidesData).join(""); -------------------------------------------------------------------------------- /src/js/testimonials.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/js/testimonials.js 3 | * @description This file contains functions to for Setting up Alerts. 4 | */ 5 | 6 | // Array of testimonial objects 7 | const alertBoxesData = [ 8 | { 9 | url: `https://goo.gl/maps/zA9qfkUA6gyxb3Ff9`, 10 | img: `xavier_marin.webp`, 11 | name: `Xavier Marin`, 12 | body: `I brought my 2009 Toyota Highlander here with 239,000 miles for a Diagnostic after taking it to 3 other mechanics even paying twice to replace sensors, because that’s why they said. . Car Club Tire found multiple things wrong and showed dedication and patience with my vehicle to ensure it was fixed. The guys are honest, friendly, and understanding. They exceeded the service on my vehicle. I left with my vehicle with a smile. I need some other work done soon on suspension and my wife’s truck. I’m bringing my family’s vehicles here from now on. I’m happy with Car Club Tires. Thank you so much!`, 13 | }, 14 | { 15 | url: `https://goo.gl/maps/gzSAfPAC8wQ1D2Rb9`, 16 | img: false, 17 | name: `Mubbaya Babar`, 18 | body: `Excellent and professional service 7 days a week. They are truly a blessing to the neighborhood. Any make or model it doesn't matter, and they took care of me. Now I have a Mercedes, and they still take good care of me. 100% dependable very Professional work they did. I highly recommend this to everyone for their vehicle need. They are the best in Orange city thanks….`, 19 | }, 20 | { 21 | url: `https://goo.gl/maps/XoiAT3zMgmoWBJBt7`, 22 | img: false, 23 | name: `Carlos Gonzalez`, 24 | body: `Good Staff with honesty. They explainde me everything before they did everything on my vehículo Most of the places lying by saying I need this and etc they showed me what was exactly wrong and fixed it in no time. other place try to get more money from me but car club team showed the exact part that needed. I highly recomend this place to everyone. You cant find honest place now a days very Hard. Again thank you so much for helping me and saved my car. I Will definatly coming back for all of my service. 10/10`, 25 | }, 26 | { 27 | url: `https://goo.gl/maps/ria7TBqB4D7uDChx8`, 28 | img: `usman_shah.webp`, 29 | name: `Usman Shah`, 30 | body: `I was driving down to Miami and my car broke down in the evening. Car repair shops near me were closing and offered to assist the next morning. I was lucky enough to call this shop. If I remember correctly, the owner was Farhan, and he had my car fixed in no time. It was a wonderful experience. The team is amazing. They also offered complimentary snacks, hands down the best service ever.`, 31 | }, 32 | { 33 | url: `https://maps.app.goo.gl/yaGPXqhh7JXkPeQK8`, 34 | img: false, 35 | name: `Alejandro Nuve`, 36 | body: `I had a great experience ay car club tire i went in for brakes ando rotor. The staff was very friendly ando welcoming from the momoent i walked in and they explained me very well which i apperciated as Someone doesnt know much about cars. I drove all the way from Orlando and it's worth it. The service was quick ando efficent and i was back on Road in no time.,My car feels mucho smoother to drive now. Overall, i highly recomend car club tire . They are very honest and prices was very good. Thanks and i Will be back`, 37 | }, 38 | { 39 | url: `https://maps.app.goo.gl/Kh5UKYhqnJr9vhy59`, 40 | img: `Virag_Anna_Szolgyemy.webp`, 41 | name: `Virag Anna Szolgyemy`, 42 | body: `We were able to do a walk-in and get our alignment done right away. It took 45 mins from start to finish and it was only $60. All the staff was very friendly and helpful. It’s too bad I don’t live around here or else I would be a regular for sure :)`, 43 | }, 44 | { 45 | url: `https://maps.app.goo.gl/TT472PqByJGpnUhh7`, 46 | img: false, 47 | name: `Francisco Alfredo`, 48 | body: `BEST SERVICE I’ve EVER GOT. I highly recommend this place for all your service needs. The staff are very knowledgeable and know what they are doing. They patiently listened and understood my concerns, and they explained all the process before proceeding.and the price was very good. I was very satisfied with their service and will definitely come again.thanks Shan for all the help.`, 49 | }, 50 | { 51 | url: `https://goo.gl/maps/yAAVWdtiPJ9v8y5U7`, 52 | img: `Africans_In_Orlando.webp`, 53 | name: `Africans In Orlando`, 54 | body: `When you're looking for honest price, honest diagnostic, weekend service and great customer service, these guys are the best in the area. Shawn and Forhan are amazing professionals who will take good care of you and your car. I'll be coming here on a regular now 🙂 Happy Customer!`, 55 | }, 56 | { 57 | url: `https://goo.gl/maps/P5FHwGYWsxGWWU3R7`, 58 | img: `Eddrina-Delmont.webp`, 59 | name: `Eddrina Delmont`, 60 | body: `Amazing amazing ! Fixed my breaks in no time. Customer service was excellent. Definitely recommend for anyone who doesn’t want to get ripped off or overpriced by other dealers. Come in and they will take care of you. Thank you so much Farhan for helping me out today and in a timely manner!`, 61 | }, 62 | { 63 | url: `https://maps.app.goo.gl/6gwbvcAP8LdTyMnb6`, 64 | img: `Danielle_Marie.webp`, 65 | name: `Danielle Marie`, 66 | body: `I just bought a used vehicle and a day later turns out I need brakes. I found a Car Club Tire & auto on Groupon. I drove 51 minutes with no brakes just to get there. Thinking I just needed brake pads I was not financially prepared to afford much more than that. Turns out I need orders also. I'm a single mom and can't afford it. Everyone there was super helpful and did their best to help me and found the right option for my situation and I left with all 4 new brake pads and 2 new front rods. I won't be going anywhere else from now on. Everyone was so nice and very professional and treated me very kindly. Thank you so much 👍😊`, 67 | }, 68 | { 69 | url: `https://goo.gl/maps/SJc9B8wmXW4ezHcF6`, 70 | img: `Lex_Scooter.webp`, 71 | name: `Lex Scooter`, 72 | body: `I will always be back to this location for service on my vehicles . I read a review for them that read "this location restored their faith in humanity" & I couldn't agree more. Thank you for doing ,all y'all can do ,for your customers. Fair prices & they gave me a fair quote over the phone , WITH EASE, after calling around for days to find the best deal on brakes .. I went the next day , around a general time i thought I could make it & it was one of the best experiences I've had at an auto repair shop. Can't thank them enough super trust worthy, Will be !!`, 73 | }, 74 | { 75 | url: `https://goo.gl/maps/hDdZ7p2JEXzQrLyB9`, 76 | img: `Mercy_Ares.webp`, 77 | name: `Mercy Ares`, 78 | body: `Found this place on Groupon, they take appts & walk-ins. Arrived and was serviced immediately, the staff employees are professional and courteous. Amazing customer service experience. Super clean facility with an indoor AC comfortable waiting area, very clean bathrooms and free coffee! Was well worth the over an hour drive to get here but I'll definitely be returning even without Groupon to fix other issues on both my minivan & suv!`, 79 | }, 80 | { 81 | url: `https://maps.app.goo.gl/ZrkBPvtVE1q8PyoA7`, 82 | img: false, 83 | name: `Sandra Jordan`, 84 | body: `My fiance was told about how "Great" Car Club Tire is by a co-worker and how Reasonable and Professional they were. I got my struts done on my car, the price was very reasonable. We took my car that morning and they finished the work by evening. Thank you for a Great job👍🏽 …`, 85 | }, 86 | { 87 | url: `https://maps.app.goo.gl/kA71gK8pm4hP5vtz9`, 88 | img: false, 89 | name: `Shalandra Davis`, 90 | body: `I have a Toyota Prius, which is a hybrid vehicle. The battery was no good the owner of the shop and his brother fixed my vehicle in record timing. They were very fast very efficient and I highly recommend the services. I will always use them as my mechanics, the honesty and prices you can’t beat.`, 91 | }, 92 | { 93 | url: `https://goo.gl/maps/qARRRYUnj4GqAedLA`, 94 | img: false, 95 | name: `Maybelline Sierra`, 96 | body: `I needed 4 new tires, a wheel alignment and rear brake pads and rotors for my 2018 Honda Accord. I called at least 6 places for quotes and Car Club gave me the best deal by far. I called them back several times with new questions while I was still shopping around for price quotes and each time, they were super patien+t and kind with me. When I finally decided to go with them, they got me in for service the very next day and did a great job! Now we will be bringing them our second car as well for some needed routine maintenance. I highly recommend them!`, 97 | }, 98 | ] 99 | 100 | const reviewerPics = import.meta.glob('../assets/media/reviewers/**/*', { eager: true }); 101 | import fallBackImg from '../assets/media/cc-logo.svg'; 102 | 103 | const aletBoxHtml = alertBoxesData.map((rv) => { 104 | const { 105 | url = '', 106 | img = '', 107 | name = '', 108 | body = '' 109 | } = rv; 110 | const coverImage = reviewerPics[`../assets/media/reviewers/${img}`]?.default || fallBackImg; 111 | const avatar = img 112 | ? `Google Reviewer's Photo` 113 | : `` 114 | 115 | const output = 116 | `
  • `+ 117 | `
    `+ 118 | `
    `+ 119 | ``+ 120 | `${avatar} ${name} — `+ 121 | ``+ 122 | `
    `+ 123 | `
    `+ 124 | ``+ 125 | ``+ 126 | ``+ 127 | ``+ 128 | ``+ 129 | `
    `+ 130 | `
    "${body}"
    `+ 131 | `
    `+ 132 | `
  • ` 133 | 134 | return output; 135 | }); 136 | 137 | const aletBoxesElement = document.getElementById(`review-slides`) 138 | if (aletBoxesElement) aletBoxesElement.innerHTML = aletBoxHtml.join(``) 139 | -------------------------------------------------------------------------------- /public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 26 | 28 | 36 | 43 | 50 | 73 | 81 | 89 | 102 | 108 | 111 | 119 | 127 | 133 | 136 | 144 | 149 | 157 | 164 | 175 | 181 | 184 | 188 | 193 | 198 | 202 | 206 | 211 | 217 | 221 | 225 | 228 | 233 | 236 | 238 | 242 | 246 | 249 | 251 | 255 | 259 | 263 | 270 | 271 | 272 | -------------------------------------------------------------------------------- /src/assets/media/cc-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 10 | 21 | 26 | 33 | 35 | 37 | 40 | 41 | 42 | 45 | 50 | 51 | 52 | 56 | 61 | 66 | 72 | 77 | 79 | 86 | 87 | 88 | 93 | 96 | 100 | 105 | 108 | 112 | 115 | 119 | 120 | 121 | 123 | 125 | 134 | 138 | 140 | 149 | 151 | 161 | 163 | 165 | 166 | 175 | 177 | 179 | 186 | 188 | 189 | 198 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Car Club Tire & Auto Repair Services - Orange City, Florida 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 93 | 94 | 96 | 97 | 98 | 99 | 100 | 101 |
    102 |
    103 |
    104 |
    105 |
    106 |
    107 |
    108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |
    116 |
    117 |
    118 |
    Loading
    119 |
    120 |
    121 | 122 | 123 | 124 | 162 | 163 |
    164 |
    165 | 166 |
    167 |
    168 |
    169 |
    170 |
    171 |
    172 |
    173 |

    174 | Discover the best place for auto repair services in Orange City, Florida. 175 |

    176 |

    177 | Our expert technicians provide top-notch solutions for all your automotive needs. 178 | From tire change to complete auto repairs, trust us for reliable and certified services. 179 |

    180 |
    181 | 187 |
    188 | 189 | Contact US! 190 |
    191 |
    192 |
    193 |
    194 |
    195 | 196 |
    197 |
    198 | 199 |
    200 |

    201 | Deals for a Limited Time 202 |

    203 |
    204 | 205 | 206 |
      207 |
      208 |
    209 | 210 |
    211 |
    212 |
    213 | 214 |
      215 |
      216 | 221 | 226 |
      227 |
      228 |
      229 |
      230 | 231 |
      232 |
      233 | Lift Kit Services 235 |
      236 |
      237 | Upgrade Radio and Stereo 239 |
      240 |
      241 | HID LED Lights 243 |
      244 |
      245 | 246 |
      247 |
      248 | 249 |
      250 |
      251 | 252 |
      253 |

      254 | About Us 255 |

      256 |
      257 | 258 |

      259 | Welcome to Car Club Tire, your trusted auto repair shop in Orange City, FL. 260 | Our customer-centric approach ensures your satisfaction is our priority. 261 | Our certified technicians deliver top-notch services, from tire replacements to wheel alignments, 262 | providing personalized care and a commitment to excellence. Drive away with confidence and peace 263 | of mind, experience the difference at Car Club Tire. 264 |

      265 | 266 |
      267 |
      268 |
      269 | 270 |
        271 |
        272 | 277 | 282 |
        283 |
        284 |
        285 |
        286 | 287 |
        288 |
        289 | 290 |
        291 |
        292 | 293 |
        294 |

        Why Choose Us

        295 |
        296 | 297 |

        298 | We offer top-notch auto repair services in Orange City, Florida. For all vehicle repairs, modifications, and 299 | maintenance, our team of highly skilled professionals is committed to giving the best possible service. We 300 | distinguish ourselves as the best option for your vehicle's requirements since we are passionate about 301 | automotive quality and customer satisfaction. Your valuable asset is in the capable hands of Car Club, where 302 | we go above and above for the maintenance of your vehicle. 303 |

        304 | 305 | 306 |
        307 | 308 |
        309 | 310 |
        311 |
        312 | 313 |
        314 |
        315 | 316 |
        317 |

        318 | Testimonials 319 |

        320 |
        321 | 322 |
        323 |
        324 |
        325 | 326 |
          327 |
          328 | 333 | 338 |
          339 |
          340 |
          341 |
          342 |
          343 |
          344 | 345 |
          346 |
          347 | 348 |
          349 |

          Contact Us

          350 |
          351 | 352 |

          353 | Feel free to reach out to us at for any of your automotive needs. Whether 354 | it's a tire change, wheel alignment, or any other auto repair service, 355 | our expert technicians are here to assist you. Contact us today to schedule an 356 | appointment and let us keep your vehicle running smoothly and safely on the road. 357 |

          358 | 359 |
          360 |
          361 | 380 | 381 |
          382 |

          383 |
          384 | 385 |
          386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 |
          Day Hours ( EST/UTC-4 )
          Monday08:00 AM - 07:00 PM
          Tuesday08:00 AM - 07:00 PM
          Wednesday08:00 AM - 07:00 PM
          Thursday08:00 AM - 07:00 PM
          Friday08:00 AM - 07:00 PM
          Saturday08:00 AM - 07:00 PM
          Sunday09:00 AM - 05:00 PM
          424 |
          425 |
          426 | 427 |
          428 |
          429 |
          430 | 432 | 434 | 436 | 438 | 440 | 442 | 444 | 446 | 448 | 450 | 452 |
          453 | 457 |
          458 |
          459 |
          460 |
          461 | 462 |
          463 | 467 |
          468 | 469 |
          470 |
          471 | 472 |
          473 |
          474 | 475 |
          477 |
          478 | 479 |
          480 | 481 | 484 | 485 | 515 | 516 |
          517 |
          518 |

          Copyright © 2023

          519 |

          Car Club Tire & Auto Repair Service

          520 |

          Designed by Saaqi

          523 |
          524 |
          525 | 526 |
          527 |
          528 | 530 |
          531 | 532 | 533 | 534 | 535 | 541 | 542 | 543 | --------------------------------------------------------------------------------