diff --git a/bun.lock b/bun.lock index 3e05da7..69a2f53 100644 --- a/bun.lock +++ b/bun.lock @@ -4,7 +4,10 @@ "": { "name": "baraha-town", "dependencies": { + "@mona-health/react-input-mask": "^3.0.3", + "@uidotdev/usehooks": "^2.4.1", "clsx": "^2.1.1", + "motion": "^12.23.24", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "^7.9.5", @@ -116,6 +119,8 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@mona-health/react-input-mask": ["@mona-health/react-input-mask@3.0.3", "", { "dependencies": { "invariant": "^2.2.4", "prop-types": "^15.7.2", "warning": "^4.0.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-E8t4ZAaHjmPT1NS/9nlIGR7OsyshP7hs5OwqEYnVslm/eaWrQg5ZziB6r5IOxGmFUyB+G1muFPobVPA08L2sJg=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -200,6 +205,8 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "eslint-visitor-keys": "^4.2.1" } }, "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg=="], + "@uidotdev/usehooks": ["@uidotdev/usehooks@2.4.1", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.0", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.43", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -330,6 +337,8 @@ "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + "framer-motion": ["framer-motion@12.23.24", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -354,6 +363,8 @@ "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], @@ -422,6 +433,8 @@ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], @@ -432,6 +445,12 @@ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "motion": ["motion@12.23.24", "", { "dependencies": { "framer-motion": "^12.23.24", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw=="], + + "motion-dom": ["motion-dom@12.23.23", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="], + + "motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], @@ -492,6 +511,8 @@ "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], @@ -500,6 +521,8 @@ "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], "react-router": ["react-router@7.9.5", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-JmxqrnBZ6E9hWmf02jzNn9Jm3UqyeimyiwzD69NjxGySG6lIz/1LVPsoTCwN7NBX2XjCEa1LIX5EMz1j2b6u6A=="], @@ -580,6 +603,8 @@ "vite": ["rolldown-vite@7.2.2", "", { "dependencies": { "@oxc-project/runtime": "0.96.0", "fdir": "^6.5.0", "lightningcss": "^1.30.2", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rolldown": "1.0.0-beta.47", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "esbuild": "^0.25.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Fl3ZdmJhDMJGcqrr342pPVrhugXdOcuNBRBauz4S7QGSRXbQy7y8q5QYJtgkcrG8XjY0EENSZeTk58c3m20FxA=="], + "warning": ["warning@4.0.3", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], diff --git a/eslint.config.js b/eslint.config.js index b19330b..40d71e4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,23 +1,26 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' -import { defineConfig, globalIgnores } from 'eslint/config' +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; +import { defineConfig, globalIgnores } from "eslint/config"; export default defineConfig([ - globalIgnores(['dist']), + globalIgnores(["dist"]), { - files: ['**/*.{ts,tsx}'], + files: ["**/*.{ts,tsx}"], extends: [ js.configs.recommended, tseslint.configs.recommended, - reactHooks.configs['recommended-latest'], + reactHooks.configs["recommended-latest"], reactRefresh.configs.vite, ], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, + rules: { + "no-irregular-whitespace": ["off"], + }, }, -]) +]); diff --git a/package.json b/package.json index 5e8f0fb..f52aba2 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,10 @@ "preview": "vite preview" }, "dependencies": { + "@mona-health/react-input-mask": "^3.0.3", + "@uidotdev/usehooks": "^2.4.1", "clsx": "^2.1.1", + "motion": "^12.23.24", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "^7.9.5" diff --git a/src/components/layouts/DefaultLayout.tsx b/src/components/layouts/DefaultLayout.tsx index 4caafa0..a4b73d7 100644 --- a/src/components/layouts/DefaultLayout.tsx +++ b/src/components/layouts/DefaultLayout.tsx @@ -1,11 +1,13 @@ import { Outlet } from "react-router"; -import NavMenu from "./Header"; +import NavMenu from "./NavMenu"; function DefaultLayout() { return (
- +
+ +
); } diff --git a/src/components/layouts/Header.tsx b/src/components/layouts/Header.tsx deleted file mode 100644 index 8c0f13c..0000000 --- a/src/components/layouts/Header.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import HomeIcon from "../icons/HomeIcon"; -import LocationIcon from "../icons/LocationIcon"; -import InfoIcon from "../icons/InfoIcon"; -import MenuLink from "../../ui/HeaderLink"; -import PhoneIcon from "../icons/PhoneIcon"; -import Button from "../../ui/Button"; -import BurgerMenuIcon from "../icons/BurgerMenuIcon"; - -function NavMenu() { - return ( - - ); -} - -export default NavMenu; diff --git a/src/components/layouts/NavMenu.tsx b/src/components/layouts/NavMenu.tsx new file mode 100644 index 0000000..5efb4d1 --- /dev/null +++ b/src/components/layouts/NavMenu.tsx @@ -0,0 +1,131 @@ +import HomeIcon from "../icons/HomeIcon"; +import LocationIcon from "../icons/LocationIcon"; +import InfoIcon from "../icons/InfoIcon"; +import MenuLink from "../../ui/MenuLink"; +import PhoneIcon from "../icons/PhoneIcon"; +import Button from "../../ui/Button"; +import BurgerMenuIcon from "../icons/BurgerMenuIcon"; +import clsx from "clsx"; +import { useState } from "react"; +import { useClickAway } from "@uidotdev/usehooks"; +import { AnimatePresence, motion } from "motion/react"; + +function NavMenu() { + const [isOpen, setIsOpen] = useState(false); + + const ref = useClickAway(handleClose); + + function handleClose() { + setIsOpen(false); + } + + return ( + + ); +} + +export default NavMenu; diff --git a/src/components/pages/ContactsPage.tsx b/src/components/pages/ContactsPage.tsx new file mode 100644 index 0000000..ac26012 --- /dev/null +++ b/src/components/pages/ContactsPage.tsx @@ -0,0 +1,44 @@ +import Button from "../../ui/Button"; +import Input from "../../ui/Input"; + +function ContactsPage() { + return ( +
+

+ Enquire about joining this thriving community at the heart of Abu Hamour + by contacting a member of our sales team today +

+
+ +
+ + +
+
+

What are you interested in?

+
+ + + +
+
+
+
+ ); +} + +export default ContactsPage; diff --git a/src/index.css b/src/index.css index b5c61c9..bbb3c3b 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,43 @@ @tailwind base; @tailwind components; @tailwind utilities; + +body { + @apply tracking-[-4%] text-[#324D43]; +} + +@layer utilities { + .button-l { + @apply 2xl:text-[1.111vw] text-base leading-[115%] tracking-[-4%]; + } + + .button-m { + @apply 2xl:text-[0.972vw] text-sm leading-[115%] tracking-[-4%]; + } + + .button-s { + @apply 2xl:text-[0.833vw] text-xs leading-[115%] tracking-[-4%]; + } + + .web-h1 { + @apply 2xl:text-[5.556vw] md:text-[10.417vw] text-[22.222vw] leading-[90%] tracking-[-4%]; + } + .web-h2 { + @apply 2xl:text-[3.333vw] md:text-[6.25vw] text-[13.333vw] leading-[110%] tracking-[-4%]; + } + .web-h3 { + @apply 2xl:text-[2.222vw] md:text-[4.167vw] text-[8.889vw] leading-[110%] tracking-[-4%]; + } + .web-number { + @apply 2xl:text-[4.444vw] md:text-[8.333vw] text-[8.889vw] md:leading-[100%] leading-[110%] tracking-[-4%]; + } + .text-m { + @apply 2xl:text-[1.111vw] md:text-[2.083vw] text-[4.444vw] 2xl:leading-[120%] leading-[130%] tracking-[-4%]; + } + .text-s { + @apply 2xl:text-[0.972vw] md:text-[1.823vw] text-[3.889vw] leading-[130%] tracking-[-4%]; + } + .web-caption { + @apply 2xl:text-[0.833vw] text-xs leading-[130%]; + } +} diff --git a/src/ui/Button.tsx b/src/ui/Button.tsx index f065d20..c767b40 100644 --- a/src/ui/Button.tsx +++ b/src/ui/Button.tsx @@ -36,9 +36,9 @@ function Button({ variant === "fab" && "bg-[#FFFFFF33] 2xl:backdrop-blur-[0.278vw] backdrop-blur-[4px] text-white active:bg-[#F47F52] disabled:bg-[#FFFFFF33] ring-1 ring-[#FFFFFF33]", size === "large" && - "2xl:p-[0.972vw] p-4 button-m 2xl:rounded-[1.111vw] rounded-2xl", + "2xl:p-[0.972vw] p-4 button-l 2xl:rounded-[1.111vw] rounded-2xl", size === "medium" && - "2xl:p-[0.833vw] p-3 button-s 2xl:rounded-[0.833vw] rounded-xl", + "2xl:p-[0.833vw] p-3 button-m 2xl:rounded-[0.833vw] rounded-xl", size === "small" && "2xl:p-[0.556vw] p-2 button-s 2xl:rounded-[0.556vw] rounded-lg", className diff --git a/src/ui/Input.tsx b/src/ui/Input.tsx new file mode 100644 index 0000000..8de23f0 --- /dev/null +++ b/src/ui/Input.tsx @@ -0,0 +1,56 @@ +import InputMask from "@mona-health/react-input-mask"; +import clsx from "clsx"; + +interface InputProps extends React.InputHTMLAttributes { + label: string; + mask?: string; + isError?: boolean; + errorMessage?: string; + isTextArea?: boolean; +} + +function Input({ + label, + isError, + errorMessage, + id, + mask, + isTextArea = false, + ...props +}: InputProps) { + return ( +
+ +
+ {isTextArea ? ( +