From 3c9474c05fcf757166f3b4024a3bc6142570ac7a Mon Sep 17 00:00:00 2001 From: andrianfaa Date: Tue, 5 Sep 2023 17:49:49 +0700 Subject: [PATCH] feat: update navbar, add quick center menu, & dark theme --- .vscode/settings.json | 4 + package-lock.json | 111 +++++++++++++- package.json | 6 +- src/assets/anfa-logo.png | Bin 0 -> 25291 bytes src/assets/anfa-logo.svg | 19 +++ .../NavigationBar/NavigationBar.component.tsx | 106 +++++++++++++ .../NavigationBar/NavigationBar.d.ts | 12 ++ .../NavigationBar/NavigationBar.test.tsx | 33 +++++ .../Navigation/NavigationBar/index.ts | 1 + .../QuickCenter/QuickCenter.component.tsx | 139 ++++++++++++++++++ .../Navigation/QuickCenter/QuickCenter.d.ts | 11 ++ .../Navigation/QuickCenter/index.ts | 1 + src/components/Navigation/index.ts | 1 + .../NavigationBar/NavigationBar.test.tsx | 21 --- .../NavigationBar/NavigationBar.tsx | 10 -- src/components/NavigationBar/index.ts | 1 - src/components/Portal/Portal.component.tsx | 19 +++ src/components/Portal/Portal.d.ts | 5 + src/components/Portal/Portal.test.tsx | 22 +++ src/components/Portal/index.ts | 1 + src/components/index.ts | 2 +- src/pages/_app.tsx | 9 +- src/pages/_document.tsx | 17 +++ src/pages/index.tsx | 5 +- src/styles/_base.scss | 5 + src/styles/_components.scss | 43 ++++++ tailwind.config.js | 14 +- 27 files changed, 579 insertions(+), 39 deletions(-) create mode 100644 src/assets/anfa-logo.png create mode 100644 src/assets/anfa-logo.svg create mode 100644 src/components/Navigation/NavigationBar/NavigationBar.component.tsx create mode 100644 src/components/Navigation/NavigationBar/NavigationBar.d.ts create mode 100644 src/components/Navigation/NavigationBar/NavigationBar.test.tsx create mode 100644 src/components/Navigation/NavigationBar/index.ts create mode 100644 src/components/Navigation/QuickCenter/QuickCenter.component.tsx create mode 100644 src/components/Navigation/QuickCenter/QuickCenter.d.ts create mode 100644 src/components/Navigation/QuickCenter/index.ts create mode 100644 src/components/Navigation/index.ts delete mode 100644 src/components/NavigationBar/NavigationBar.test.tsx delete mode 100644 src/components/NavigationBar/NavigationBar.tsx delete mode 100644 src/components/NavigationBar/index.ts create mode 100644 src/components/Portal/Portal.component.tsx create mode 100644 src/components/Portal/Portal.d.ts create mode 100644 src/components/Portal/Portal.test.tsx create mode 100644 src/components/Portal/index.ts create mode 100644 src/pages/_document.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 930eeaa..dcac4a0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,11 @@ "editor.formatOnSave": true, "editor.tabSize": 2, "editor.wordWrap": "on", + "editor.formatOnSaveMode": "file", "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" } } diff --git a/package-lock.json b/package-lock.json index 83db6a4..8b9d66a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,13 @@ "name": "app", "version": "0.1.0", "dependencies": { + "clsx": "^2.0.0", + "framer-motion": "^10.16.4", "next": "13.4.13", + "next-themes": "^0.2.1", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "react-icons": "^4.10.1" }, "devDependencies": { "@testing-library/jest-dom": "^6.0.0", @@ -794,6 +798,21 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2921,6 +2940,14 @@ "node": ">=12" } }, + "node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -4205,6 +4232,29 @@ "url": "https://www.patreon.com/infusion" } }, + "node_modules/framer-motion": { + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.4.tgz", + "integrity": "sha512-p9V9nGomS3m6/CALXqv6nFGMuFOxbWsmaOrdmhyQimMIlLl3LC7h7l86wge/Js/8cRu5ktutS/zlzgR7eBOtFA==", + "dependencies": { + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6613,6 +6663,16 @@ } } }, + "node_modules/next-themes": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", + "integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==", + "peerDependencies": { + "next": "*", + "react": "*", + "react-dom": "*" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -7426,6 +7486,14 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -9595,6 +9663,21 @@ } } }, + "@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "requires": { + "@emotion/memoize": "0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, "@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -11128,6 +11211,11 @@ "wrap-ansi": "^7.0.0" } }, + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -12099,6 +12187,15 @@ "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", "dev": true }, + "framer-motion": { + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.4.tgz", + "integrity": "sha512-p9V9nGomS3m6/CALXqv6nFGMuFOxbWsmaOrdmhyQimMIlLl3LC7h7l86wge/Js/8cRu5ktutS/zlzgR7eBOtFA==", + "requires": { + "@emotion/is-prop-valid": "^0.8.2", + "tslib": "^2.4.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -13859,6 +13956,12 @@ } } }, + "next-themes": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", + "integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==", + "requires": {} + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -14386,6 +14489,12 @@ "scheduler": "^0.23.0" } }, + "react-icons": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", + "requires": {} + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 5cb935c..b7c1fea 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,13 @@ "test:ci": "jest --ci" }, "dependencies": { + "clsx": "^2.0.0", + "framer-motion": "^10.16.4", "next": "13.4.13", + "next-themes": "^0.2.1", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "react-icons": "^4.10.1" }, "devDependencies": { "@testing-library/jest-dom": "^6.0.0", diff --git a/src/assets/anfa-logo.png b/src/assets/anfa-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e35ae3091c48a3311d595a8a72467bd7730af7de GIT binary patch literal 25291 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 literal 0 HcmV?d00001 diff --git a/src/assets/anfa-logo.svg b/src/assets/anfa-logo.svg new file mode 100644 index 0000000..00d7143 --- /dev/null +++ b/src/assets/anfa-logo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Navigation/NavigationBar/NavigationBar.component.tsx b/src/components/Navigation/NavigationBar/NavigationBar.component.tsx new file mode 100644 index 0000000..a32537f --- /dev/null +++ b/src/components/Navigation/NavigationBar/NavigationBar.component.tsx @@ -0,0 +1,106 @@ +import { Portal } from "@/components/Portal"; +import clsx from "clsx"; +import Image from "next/image"; +import { useEffect, useState } from "react"; +import { FiChevronRight, FiGrid } from "react-icons/fi"; +import { QuickCenter } from "../QuickCenter"; +import type { NavigationBarParams } from "./NavigationBar"; + +import AnfaLogoSVG from "@/assets/anfa-logo.svg"; + +const NavigationBar = ({ testId }: NavigationBarParams) => { + const [isOpen, setIsOpen] = useState(false); + const [openQuickCenter, setOpenQuickCenter] = useState(false); + + useEffect(() => { + if (typeof window === "undefined") return; + + window.addEventListener("keydown", (event) => { + const { key, code } = event; + + if (openQuickCenter && (key === "Escape" || code === "Escape")) { + setOpenQuickCenter(false); + } + }); + }, [openQuickCenter]); + + return ( + <> + + setOpenQuickCenter(false)} /> + + +
+
+
+ + Anfa Logo + + + +
+ + + + {/* */} +
+
+ + ); +}; + +export default NavigationBar; diff --git a/src/components/Navigation/NavigationBar/NavigationBar.d.ts b/src/components/Navigation/NavigationBar/NavigationBar.d.ts new file mode 100644 index 0000000..913cbfe --- /dev/null +++ b/src/components/Navigation/NavigationBar/NavigationBar.d.ts @@ -0,0 +1,12 @@ +export interface NavigationBarParams { + /** + * Jest Testing ID + */ + testId?: { + parent?: string; + title?: string; + logo?: string; + navigation?: string; + toggler?: string; + }; +} diff --git a/src/components/Navigation/NavigationBar/NavigationBar.test.tsx b/src/components/Navigation/NavigationBar/NavigationBar.test.tsx new file mode 100644 index 0000000..21f67c1 --- /dev/null +++ b/src/components/Navigation/NavigationBar/NavigationBar.test.tsx @@ -0,0 +1,33 @@ +import { render, screen } from "@testing-library/react"; +import NavigationBar from "./NavigationBar.component"; + +describe("test `` component", () => { + it("should be rendered", () => { + render( + + ); + + const text = screen.getByTestId("navigation-parent"); + + expect(text).toBeVisible(); + }); + + it("should render navigation toggler", () => { + render( + + ); + + const navigationToggler = screen.getByTestId("navigation-toggler"); + + expect(navigationToggler).toBeVisible(); + expect(navigationToggler).toBeValid(); + }); +}); diff --git a/src/components/Navigation/NavigationBar/index.ts b/src/components/Navigation/NavigationBar/index.ts new file mode 100644 index 0000000..123156c --- /dev/null +++ b/src/components/Navigation/NavigationBar/index.ts @@ -0,0 +1 @@ +export { default as NavigationBar } from "./NavigationBar.component"; \ No newline at end of file diff --git a/src/components/Navigation/QuickCenter/QuickCenter.component.tsx b/src/components/Navigation/QuickCenter/QuickCenter.component.tsx new file mode 100644 index 0000000..a09b6f2 --- /dev/null +++ b/src/components/Navigation/QuickCenter/QuickCenter.component.tsx @@ -0,0 +1,139 @@ +import clsx from "clsx"; +import { motion } from "framer-motion"; +import { useTheme } from "next-themes"; +import Link from "next/link"; +import { memo } from "react"; +import { BsFillMoonStarsFill, BsGithub } from "react-icons/bs"; +import { FiSun, FiX } from "react-icons/fi"; +import { QuickCenterParams } from "./QuickCenter"; + +const QuickCenter = ({ isOpen, onClickClose }: QuickCenterParams) => { + const { systemTheme, theme, setTheme } = useTheme(); + + const currentTheme = theme === "system" ? systemTheme : theme; + const animation = { + initial: { + opacity: 0, + translateY: -10 + }, + animate: { + opacity: 1, + translateY: 0 + }, + transition: { + duration: 0.1, + delay: 0.25 + } + }; + + const changeTheme = () => { + if (currentTheme === "dark") { + setTheme("light"); + } else { + setTheme("dark"); + } + }; + + return ( +
+
+ {isOpen && ( + // Panel left +
+ {/* Theme toggler */} + + + + Action center + + + + + + {/* Github */} + + + +

GitHub

+ +
+
+ )} +
+
+ ); +}; + +export default memo(QuickCenter); diff --git a/src/components/Navigation/QuickCenter/QuickCenter.d.ts b/src/components/Navigation/QuickCenter/QuickCenter.d.ts new file mode 100644 index 0000000..3b492d6 --- /dev/null +++ b/src/components/Navigation/QuickCenter/QuickCenter.d.ts @@ -0,0 +1,11 @@ +export interface QuickCenterParams { + isOpen: boolean; + onClickClose: () => void; + + /** + * Jest Testing ID + */ + testId?: { + themeToggler?: string; + }; +} diff --git a/src/components/Navigation/QuickCenter/index.ts b/src/components/Navigation/QuickCenter/index.ts new file mode 100644 index 0000000..5dcb5f1 --- /dev/null +++ b/src/components/Navigation/QuickCenter/index.ts @@ -0,0 +1 @@ +export { default as QuickCenter } from "./QuickCenter.component"; diff --git a/src/components/Navigation/index.ts b/src/components/Navigation/index.ts new file mode 100644 index 0000000..224aa97 --- /dev/null +++ b/src/components/Navigation/index.ts @@ -0,0 +1 @@ +export * from './NavigationBar' \ No newline at end of file diff --git a/src/components/NavigationBar/NavigationBar.test.tsx b/src/components/NavigationBar/NavigationBar.test.tsx deleted file mode 100644 index 1ec1e47..0000000 --- a/src/components/NavigationBar/NavigationBar.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import NavigationBar from "./NavigationBar"; - -describe("test `` component", () => { - it("should be rendered", () => { - render(); - - const text = screen.getByText(/Navigation/); - - expect(text).toBeVisible(); - }); - - it("should render `nav` tag", () => { - render(); - - const navTag = screen.getByTestId("navigation-container"); - - expect(navTag).toBeVisible(); - expect(navTag).toBeValid(); - }); -}); diff --git a/src/components/NavigationBar/NavigationBar.tsx b/src/components/NavigationBar/NavigationBar.tsx deleted file mode 100644 index 9d5082e..0000000 --- a/src/components/NavigationBar/NavigationBar.tsx +++ /dev/null @@ -1,10 +0,0 @@ -const NavigationBar = () => { - return ( -
-

Navigation

- -
- ); -}; - -export default NavigationBar; diff --git a/src/components/NavigationBar/index.ts b/src/components/NavigationBar/index.ts deleted file mode 100644 index bc3b1ac..0000000 --- a/src/components/NavigationBar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Navigation } from "./NavigationBar"; diff --git a/src/components/Portal/Portal.component.tsx b/src/components/Portal/Portal.component.tsx new file mode 100644 index 0000000..61d33fe --- /dev/null +++ b/src/components/Portal/Portal.component.tsx @@ -0,0 +1,19 @@ +import { useEffect, useRef, useState } from "react"; +import { createPortal } from "react-dom"; +import { PortalParams } from "./Portal"; + +const Portal = ({ children }: PortalParams) => { + const ref = useRef(null); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + if (typeof window === "undefined" || typeof document === "undefined") return; + + ref.current = document.querySelector("#portal"); + setMounted(true); + }, []); + + return mounted && ref.current ? createPortal(children, ref.current) : null; +}; + +export default Portal; diff --git a/src/components/Portal/Portal.d.ts b/src/components/Portal/Portal.d.ts new file mode 100644 index 0000000..ff5e893 --- /dev/null +++ b/src/components/Portal/Portal.d.ts @@ -0,0 +1,5 @@ +import { type ReactNode } from "react"; + +export interface PortalParams { + children: ReactNode; +} \ No newline at end of file diff --git a/src/components/Portal/Portal.test.tsx b/src/components/Portal/Portal.test.tsx new file mode 100644 index 0000000..e7fa2fa --- /dev/null +++ b/src/components/Portal/Portal.test.tsx @@ -0,0 +1,22 @@ +import { render, screen } from "@testing-library/react"; +import Portal from "./Portal.component"; + +describe("test component", () => { + it("should be render portal", () => { + render( + <> +
+ + +

Portal

+
+ + ); + + const elementByTestId = screen.getByTestId("portal"); + const elementByTest = screen.getByText("Portal"); + + expect(elementByTestId).toBeInTheDocument(); + expect(elementByTest).toBeInTheDocument(); + }); +}); diff --git a/src/components/Portal/index.ts b/src/components/Portal/index.ts new file mode 100644 index 0000000..29543e0 --- /dev/null +++ b/src/components/Portal/index.ts @@ -0,0 +1 @@ +export { default as Portal } from "./Portal.component"; diff --git a/src/components/index.ts b/src/components/index.ts index 82cf741..11cdf0a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1 +1 @@ -export * from "./NavigationBar"; +export * from "./Navigation/NavigationBar"; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 6b23f9d..d96ebfe 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,9 +1,16 @@ import type { AppProps } from "next/app"; +import { NavigationBar } from "@/components/Navigation"; +import { ThemeProvider } from "next-themes"; import "../styles/root.scss"; function MyApp({ Component, pageProps }: AppProps) { - return ; + return ( + + + + + ); } export default MyApp; diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx new file mode 100644 index 0000000..10147c4 --- /dev/null +++ b/src/pages/_document.tsx @@ -0,0 +1,17 @@ +import { Html, Head, Main, NextScript } from "next/document"; + +const Document = () => { + return ( + + + +
+ +
+ + + + ); +}; + +export default Document; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 8965086..7bfb6ae 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,9 +1,10 @@ +import clsx from "clsx"; import type { NextPage } from "next"; const Home: NextPage = () => { return ( -
-

Hello World!

+
+

This site under development

); }; diff --git a/src/styles/_base.scss b/src/styles/_base.scss index e07841b..ea952ab 100644 --- a/src/styles/_base.scss +++ b/src/styles/_base.scss @@ -8,4 +8,9 @@ @apply scroll-smooth; } } + + body { + @apply bg-slate-50 dark:bg-slate-950; + @apply text-slate-600 dark:text-slate-400; + } } diff --git a/src/styles/_components.scss b/src/styles/_components.scss index 020aaba..324120b 100644 --- a/src/styles/_components.scss +++ b/src/styles/_components.scss @@ -1 +1,44 @@ @tailwind components; + +@layer components { + .container { + @apply sm:max-w-xl md:max-w-3xl lg:max-w-4xl xl:max-w-6xl; + } + + .button { + @apply transition-all duration-200 ease-in-out; + // @apply outline-1 outline-primary dark:outline-1 dark:outline-primary; + // @apply outline-none dark:outline-none; + + &-primary { + @apply bg-primary bg-opacity-10 hover:bg-opacity-20 focus:bg-opacity-25; + @apply text-primary; + } + } + + .navigation { + &-toggler { + @apply w-11 h-11; + @apply relative flex flex-col items-center justify-center; + + > .icon { + @apply absolute; + @apply h-[1px] w-6; + @apply bg-black dark:bg-white; + + &:nth-child(1) { + @apply translate-y-2; + } + + &:nth-child(2) { + @apply w-5; + @apply translate-x-0.5; + } + + &:nth-child(3) { + @apply -translate-y-2; + } + } + } + } +} diff --git a/tailwind.config.js b/tailwind.config.js index c9655b6..bac1bb7 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,9 +1,21 @@ /** @type {import('tailwindcss').Config} */ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const defaultTheme = require("tailwindcss/defaultTheme"); + module.exports = { content: ["./src/**/*.{ts,tsx}"], + darkMode: "class", theme: { - extend: {} + extend: { + screens: { + sm: "576px" + }, + colors: { + ...defaultTheme.colors, + primary: "#7D71EA" + } + } }, plugins: [] };