commit 4aa3fdbc2ad7d623dddaab7e1a7073b5ca89e6b1 Author: Nazareno Oviedo Date: Mon Mar 28 15:00:11 2022 -0300 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..74b7586 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000..31354ec --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..d2ae35e --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint-staged diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..4b28517 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "bradlc.vscode-tailwindcss", + "heybourn.headwind", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "stylelint.vscode-stylelint" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6bc7cf8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true, + "source.fixAll.stylelint": true + }, + "stylelint.validate": ["css", "scss"], + "css.validate": false, + "less.validate": false, + "scss.validate": false, + "[css]": { + "editor.formatOnSave": false + }, + "[scss]": { + "editor.formatOnSave": false + }, + "[less]": { + "editor.formatOnSave": false + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..783092f --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Laconic + +Repository for `Laconic`'s website. + +## Featured Aspects of the Stack + +- [TypeScript](https://www.typescriptlang.org/) +- [Next.js](https://nextjs.org/) +- [GSAP](https://greensock.com/gsap/) + +## Get Started + +1. Install yarn: + + ``` + npm install -g yarn + ``` + +2. Install the dependencies with: + + ``` + yarn + ``` + +3. Start developing and watch for code changes: + + ``` + yarn dev + ``` + +--- + +If you find you need to make extra config to make this work more seamlessly, feel free to submit a PR suggesting your changes. Our focus is to get you up and running with the least steps and burden as possible. diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next-sitemap.js b/next-sitemap.js new file mode 100644 index 0000000..ca12791 --- /dev/null +++ b/next-sitemap.js @@ -0,0 +1,5 @@ +module.exports = { + siteUrl: 'https://laconic.com/', + generateRobotsTxt: true, + exclude: [] +} diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..b496cd3 --- /dev/null +++ b/next.config.js @@ -0,0 +1,13 @@ +const withPlugins = require('next-compose-plugins') +const withBundleAnalyzer = require('@next/bundle-analyzer') + +module.exports = withPlugins( + [withBundleAnalyzer({ enabled: process.env.ANALYZE === 'true' })], + { + reactStrictMode: false, + swcMinify: true, + images: { + formats: ['image/avif', 'image/webp'] + } + } +) diff --git a/package.json b/package.json new file mode 100644 index 0000000..eeba30e --- /dev/null +++ b/package.json @@ -0,0 +1,250 @@ +{ + "name": "laconic", + "version": "1.0.0", + "description": "Laconic Website", + "repository": "https://github.com/LaconicNetwork/laconic.com", + "author": "basement.studio", + "private": true, + "scripts": { + "prepare": "husky install", + "dev": "next", + "build": "next build && next export", + "build:analyze": "cross-env ANALYZE=true yarn build", + "postbuild": "next-sitemap", + "start": "next start", + "lint": "eslint . --ext .ts,.tsx,.js,.jsx && stylelint '**/*.{css,scss}'", + "tsc": "tsc --pretty --noEmit" + }, + "dependencies": { + "@juggle/resize-observer": "^3.3.1", + "@radix-ui/react-polymorphic": "^0.0.14", + "clsx": "^1.1.1", + "gsap": "https://basement.studio/gsap/bonus-0.0.12.tgz", + "keen-slider": "^6.6.5", + "locomotive-scroll": "^4.1.4", + "next": "^12.1.1", + "next-real-viewport": "^0.7.0", + "next-seo": "^5.2.0", + "react": "^18.0.0-rc.0", + "react-device-detect": "^2.1.2", + "react-dom": "^18.0.0-rc.0", + "react-fast-marquee": "^1.3.1", + "react-hook-form": "^7.28.1", + "react-merge-refs": "^1.1.0", + "react-use-measure": "^2.1.1", + "sharp": "0.30.3" + }, + "devDependencies": { + "@next/bundle-analyzer": "^12.1.1", + "@types/css-font-loading-module": "0.0.7", + "@types/mousetrap": "^1.6.9", + "@types/node": "^17.0.23", + "@types/react": "^17.0.43", + "@types/react-dom": "^17.0.14", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", + "autoprefixer": "10.4.4", + "cross-env": "^7.0.3", + "eslint": "^7.32.0", + "eslint-config-next": "^12.1.1", + "eslint-config-prettier": "8.5.0", + "eslint-import-resolver-typescript": "^2.5.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "7.29.4", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-simple-import-sort": "^7.0.0", + "husky": "^7.0.4", + "lint-staged": "12.3.7", + "next-compose-plugins": "^2.2.1", + "next-sitemap": "^2.5.14", + "next-transpile-modules": "^9.0.0", + "prettier": "^2.6.1", + "sass": "1.49.9", + "stylelint": "^14.6.1", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-standard": "^25.0.0", + "stylelint-config-standard-scss": "^3.0.0", + "stylelint-prettier": "^2.0.0", + "typescript": "^4.6.3" + }, + "engines": { + "node": "14.x", + "yarn": "1.x" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ], + "prettier": { + "semi": false, + "singleQuote": true, + "arrowParens": "always", + "tabWidth": 2, + "printWidth": 80, + "trailingComma": "none" + }, + "eslintConfig": { + "parser": "@typescript-eslint/parser", + "plugins": [ + "react", + "react-hooks", + "simple-import-sort", + "@typescript-eslint" + ], + "ignorePatterns": [ + "src/lib/lambo-jump/*" + ], + "extends": [ + "eslint:recommended", + "plugin:import/recommended", + "plugin:import/typescript", + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended", + "prettier", + "plugin:prettier/recommended" + ], + "env": { + "es6": true, + "browser": true, + "node": true + }, + "rules": { + "react/react-in-jsx-scope": 0, + "react/display-name": 0, + "react/prop-types": 0, + "@typescript-eslint/explicit-function-return-type": 0, + "@typescript-eslint/explicit-member-accessibility": 0, + "@typescript-eslint/indent": 0, + "@typescript-eslint/member-delimiter-style": 0, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-var-requires": 0, + "@typescript-eslint/no-use-before-define": 0, + "@typescript-eslint/ban-ts-comment": 0, + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + "react-hooks/exhaustive-deps": "warn", + "react/no-unescaped-entities": 0, + "curly": [ + "error", + "multi-line" + ], + "react/jsx-no-target-blank": [ + 2, + { + "allowReferrer": true + } + ], + "@typescript-eslint/no-unused-vars": [ + 2, + { + "argsIgnorePattern": "^_" + } + ], + "no-console": [ + 1, + { + "allow": [ + "warn", + "error" + ] + } + ], + "prettier/prettier": [ + "warn" + ], + "@typescript-eslint/explicit-module-boundary-types": "off" + }, + "settings": { + "import/parsers": { + "@typescript-eslint/parser": [ + ".ts", + ".tsx" + ] + }, + "import/resolver": { + "typescript": { + "alwaysTryTypes": true, + "project": "." + } + }, + "react": { + "version": "detect" + } + } + }, + "stylelint": { + "ignoreFiles": [ + "**/*.{ts,tsx,js,jsx}" + ], + "extends": [ + "stylelint-config-standard", + "stylelint-config-standard-scss", + "stylelint-prettier/recommended" + ], + "plugins": [ + "stylelint-prettier" + ], + "rules": { + "scss/at-rule-no-unknown": [ + true, + { + "ignoreAtRules": [ + "tailwind", + "layer", + "apply", + "variants", + "responsive", + "screen", + "include", + "for", + "mixin", + "if", + "else", + "warn", + "return", + "function", + "use", + "each" + ] + } + ], + "selector-pseudo-class-no-unknown": [ + true, + { + "ignorePseudoClasses": [ + "global" + ] + } + ], + "property-no-unknown": [ + true, + { + "ignoreProperties": [ + "font-named-instance" + ] + } + ], + "declaration-block-trailing-semicolon": null, + "no-descending-specificity": null, + "number-leading-zero": null, + "length-zero-no-unit": null, + "alpha-value-notation": null, + "selector-id-pattern": null, + "selector-class-pattern": null, + "property-no-vendor-prefix": null, + "value-no-vendor-prefix": null, + "function-no-unknown": null, + "scss/no-global-function-names": null, + "scss/at-extend-no-missing-placeholder": null + } + }, + "lint-staged": { + "*.@(ts|tsx|css|scss)": [ + "yarn lint --fix" + ] + } +} diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png new file mode 100644 index 0000000..9221abc Binary files /dev/null and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 0000000..0093fcc Binary files /dev/null and b/public/android-chrome-512x512.png differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..67217df Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/browserconfig.xml b/public/browserconfig.xml new file mode 100644 index 0000000..b9639ca --- /dev/null +++ b/public/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #000000 + + + diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000..ca8f67f Binary files /dev/null and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 0000000..8302a90 Binary files /dev/null and b/public/favicon-32x32.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..b19a423 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/fonts/arthemys/ArthemysDisplay-Regular.woff b/public/fonts/arthemys/ArthemysDisplay-Regular.woff new file mode 100644 index 0000000..9643800 Binary files /dev/null and b/public/fonts/arthemys/ArthemysDisplay-Regular.woff differ diff --git a/public/fonts/arthemys/ArthemysDisplay-Regular.woff2 b/public/fonts/arthemys/ArthemysDisplay-Regular.woff2 new file mode 100644 index 0000000..dcc02c7 Binary files /dev/null and b/public/fonts/arthemys/ArthemysDisplay-Regular.woff2 differ diff --git a/public/fonts/dm-mono/DMMono-Regular.woff b/public/fonts/dm-mono/DMMono-Regular.woff new file mode 100644 index 0000000..f6214dc Binary files /dev/null and b/public/fonts/dm-mono/DMMono-Regular.woff differ diff --git a/public/fonts/dm-mono/DMMono-Regular.woff2 b/public/fonts/dm-mono/DMMono-Regular.woff2 new file mode 100644 index 0000000..019aae3 Binary files /dev/null and b/public/fonts/dm-mono/DMMono-Regular.woff2 differ diff --git a/public/fonts/tt-hoves/TTHoves-Italic.woff b/public/fonts/tt-hoves/TTHoves-Italic.woff new file mode 100644 index 0000000..3062818 Binary files /dev/null and b/public/fonts/tt-hoves/TTHoves-Italic.woff differ diff --git a/public/fonts/tt-hoves/TTHoves-Italic.woff2 b/public/fonts/tt-hoves/TTHoves-Italic.woff2 new file mode 100644 index 0000000..b02c8a9 Binary files /dev/null and b/public/fonts/tt-hoves/TTHoves-Italic.woff2 differ diff --git a/public/fonts/tt-hoves/TTHoves-Medium.woff b/public/fonts/tt-hoves/TTHoves-Medium.woff new file mode 100644 index 0000000..e9cd55a Binary files /dev/null and b/public/fonts/tt-hoves/TTHoves-Medium.woff differ diff --git a/public/fonts/tt-hoves/TTHoves-Medium.woff2 b/public/fonts/tt-hoves/TTHoves-Medium.woff2 new file mode 100644 index 0000000..6a78c4f Binary files /dev/null and b/public/fonts/tt-hoves/TTHoves-Medium.woff2 differ diff --git a/public/fonts/tt-hoves/TTHoves-MediumItalic.woff b/public/fonts/tt-hoves/TTHoves-MediumItalic.woff new file mode 100644 index 0000000..c111cbf Binary files /dev/null and b/public/fonts/tt-hoves/TTHoves-MediumItalic.woff differ diff --git a/public/fonts/tt-hoves/TTHoves-MediumItalic.woff2 b/public/fonts/tt-hoves/TTHoves-MediumItalic.woff2 new file mode 100644 index 0000000..6d9146f Binary files /dev/null and b/public/fonts/tt-hoves/TTHoves-MediumItalic.woff2 differ diff --git a/public/fonts/tt-hoves/TTHoves-Regular.woff b/public/fonts/tt-hoves/TTHoves-Regular.woff new file mode 100644 index 0000000..fd7ba06 Binary files /dev/null and b/public/fonts/tt-hoves/TTHoves-Regular.woff differ diff --git a/public/fonts/tt-hoves/TTHoves-Regular.woff2 b/public/fonts/tt-hoves/TTHoves-Regular.woff2 new file mode 100644 index 0000000..a21261b Binary files /dev/null and b/public/fonts/tt-hoves/TTHoves-Regular.woff2 differ diff --git a/public/images/about/jimmy-cash.jpg b/public/images/about/jimmy-cash.jpg new file mode 100644 index 0000000..a55e8df Binary files /dev/null and b/public/images/about/jimmy-cash.jpg differ diff --git a/public/images/cash-mobile.png b/public/images/cash-mobile.png new file mode 100644 index 0000000..5997b03 Binary files /dev/null and b/public/images/cash-mobile.png differ diff --git a/public/images/cash-transition.png b/public/images/cash-transition.png new file mode 100644 index 0000000..562e266 Binary files /dev/null and b/public/images/cash-transition.png differ diff --git a/public/images/cash.png b/public/images/cash.png new file mode 100644 index 0000000..b759df5 Binary files /dev/null and b/public/images/cash.png differ diff --git a/public/images/cursor/default-active.svg b/public/images/cursor/default-active.svg new file mode 100644 index 0000000..e2d444e --- /dev/null +++ b/public/images/cursor/default-active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/cursor/default.svg b/public/images/cursor/default.svg new file mode 100644 index 0000000..b43a286 --- /dev/null +++ b/public/images/cursor/default.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/cursor/pointer-active.svg b/public/images/cursor/pointer-active.svg new file mode 100644 index 0000000..713657e --- /dev/null +++ b/public/images/cursor/pointer-active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/cursor/pointer.svg b/public/images/cursor/pointer.svg new file mode 100644 index 0000000..6122ea9 --- /dev/null +++ b/public/images/cursor/pointer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/faces/jimmy.png b/public/images/faces/jimmy.png new file mode 100644 index 0000000..d70aadf Binary files /dev/null and b/public/images/faces/jimmy.png differ diff --git a/public/images/footer/footer.svg b/public/images/footer/footer.svg new file mode 100644 index 0000000..2a08c1e --- /dev/null +++ b/public/images/footer/footer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/gifs/cash-2.gif b/public/images/gifs/cash-2.gif new file mode 100644 index 0000000..897001e Binary files /dev/null and b/public/images/gifs/cash-2.gif differ diff --git a/public/images/gifs/cash-3.gif b/public/images/gifs/cash-3.gif new file mode 100644 index 0000000..6c5183b Binary files /dev/null and b/public/images/gifs/cash-3.gif differ diff --git a/public/images/gifs/cash.gif b/public/images/gifs/cash.gif new file mode 100644 index 0000000..60a5e19 Binary files /dev/null and b/public/images/gifs/cash.gif differ diff --git a/public/images/gifs/lambo-jump.gif b/public/images/gifs/lambo-jump.gif new file mode 100644 index 0000000..eab61d0 Binary files /dev/null and b/public/images/gifs/lambo-jump.gif differ diff --git a/public/images/home/lambo-jump-poster.png b/public/images/home/lambo-jump-poster.png new file mode 100644 index 0000000..382c238 Binary files /dev/null and b/public/images/home/lambo-jump-poster.png differ diff --git a/public/images/home/lambo-jump-title.svg b/public/images/home/lambo-jump-title.svg new file mode 100644 index 0000000..91a8f49 --- /dev/null +++ b/public/images/home/lambo-jump-title.svg @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/home/pine.png b/public/images/home/pine.png new file mode 100644 index 0000000..a1843e7 Binary files /dev/null and b/public/images/home/pine.png differ diff --git a/public/images/home/placeholder-product-images/1.png b/public/images/home/placeholder-product-images/1.png new file mode 100644 index 0000000..855c29e Binary files /dev/null and b/public/images/home/placeholder-product-images/1.png differ diff --git a/public/images/home/placeholder-product-images/2.png b/public/images/home/placeholder-product-images/2.png new file mode 100644 index 0000000..f939d05 Binary files /dev/null and b/public/images/home/placeholder-product-images/2.png differ diff --git a/public/images/home/placeholder-product-images/3.png b/public/images/home/placeholder-product-images/3.png new file mode 100644 index 0000000..14d043b Binary files /dev/null and b/public/images/home/placeholder-product-images/3.png differ diff --git a/public/images/home/placeholder-product-images/4.png b/public/images/home/placeholder-product-images/4.png new file mode 100644 index 0000000..938fe76 Binary files /dev/null and b/public/images/home/placeholder-product-images/4.png differ diff --git a/public/images/home/slider/slider-1.png b/public/images/home/slider/slider-1.png new file mode 100644 index 0000000..e411098 Binary files /dev/null and b/public/images/home/slider/slider-1.png differ diff --git a/public/images/home/slider/slider-2.png b/public/images/home/slider/slider-2.png new file mode 100644 index 0000000..9c6a211 Binary files /dev/null and b/public/images/home/slider/slider-2.png differ diff --git a/public/images/home/slider/slider-3.png b/public/images/home/slider/slider-3.png new file mode 100644 index 0000000..922923c Binary files /dev/null and b/public/images/home/slider/slider-3.png differ diff --git a/public/images/icons/gift.png b/public/images/icons/gift.png new file mode 100644 index 0000000..de9b52f Binary files /dev/null and b/public/images/icons/gift.png differ diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000..35a7629 Binary files /dev/null and b/public/images/logo.png differ diff --git a/public/images/logos/logo-mrbeast.png b/public/images/logos/logo-mrbeast.png new file mode 100644 index 0000000..61e64bd Binary files /dev/null and b/public/images/logos/logo-mrbeast.png differ diff --git a/public/images/noise-2.png b/public/images/noise-2.png new file mode 100644 index 0000000..3a0eceb Binary files /dev/null and b/public/images/noise-2.png differ diff --git a/public/images/noise.png b/public/images/noise.png new file mode 100644 index 0000000..fd0b54b Binary files /dev/null and b/public/images/noise.png differ diff --git a/public/images/price-badge/left.png b/public/images/price-badge/left.png new file mode 100644 index 0000000..e4924dd Binary files /dev/null and b/public/images/price-badge/left.png differ diff --git a/public/images/price-badge/middle.png b/public/images/price-badge/middle.png new file mode 100644 index 0000000..cb5c941 Binary files /dev/null and b/public/images/price-badge/middle.png differ diff --git a/public/images/price-badge/right.png b/public/images/price-badge/right.png new file mode 100644 index 0000000..e3a97bd Binary files /dev/null and b/public/images/price-badge/right.png differ diff --git a/public/images/product-detail/main-image-bg.jpg b/public/images/product-detail/main-image-bg.jpg new file mode 100644 index 0000000..e60d328 Binary files /dev/null and b/public/images/product-detail/main-image-bg.jpg differ diff --git a/public/images/product-detail/placeholder-1.png b/public/images/product-detail/placeholder-1.png new file mode 100644 index 0000000..de195db Binary files /dev/null and b/public/images/product-detail/placeholder-1.png differ diff --git a/public/images/product-detail/placeholder-2.png b/public/images/product-detail/placeholder-2.png new file mode 100644 index 0000000..7945056 Binary files /dev/null and b/public/images/product-detail/placeholder-2.png differ diff --git a/public/images/product-detail/placeholder-3.png b/public/images/product-detail/placeholder-3.png new file mode 100644 index 0000000..f49f44c Binary files /dev/null and b/public/images/product-detail/placeholder-3.png differ diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..1ac0c04 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "Shop MrBeast x Shrek", + "icons": [ + { + "src": "/android-icon-36x36.png", + "sizes": "36x36", + "type": "image/png", + "density": "0.75" + }, + { + "src": "/android-icon-48x48.png", + "sizes": "48x48", + "type": "image/png", + "density": "1.0" + }, + { + "src": "/android-icon-72x72.png", + "sizes": "72x72", + "type": "image/png", + "density": "1.5" + }, + { + "src": "/android-icon-96x96.png", + "sizes": "96x96", + "type": "image/png", + "density": "2.0" + }, + { + "src": "/android-icon-144x144.png", + "sizes": "144x144", + "type": "image/png", + "density": "3.0" + }, + { + "src": "/android-icon-192x192.png", + "sizes": "192x192", + "type": "image/png", + "density": "4.0" + } + ] +} diff --git a/public/mstile-144x144.png b/public/mstile-144x144.png new file mode 100644 index 0000000..d1e9ffb Binary files /dev/null and b/public/mstile-144x144.png differ diff --git a/public/mstile-150x150.png b/public/mstile-150x150.png new file mode 100644 index 0000000..5d85f1f Binary files /dev/null and b/public/mstile-150x150.png differ diff --git a/public/mstile-310x150.png b/public/mstile-310x150.png new file mode 100644 index 0000000..2117d88 Binary files /dev/null and b/public/mstile-310x150.png differ diff --git a/public/mstile-310x310.png b/public/mstile-310x310.png new file mode 100644 index 0000000..89fa867 Binary files /dev/null and b/public/mstile-310x310.png differ diff --git a/public/mstile-70x70.png b/public/mstile-70x70.png new file mode 100644 index 0000000..ced6571 Binary files /dev/null and b/public/mstile-70x70.png differ diff --git a/public/og.jpeg b/public/og.jpeg new file mode 100644 index 0000000..9ffe404 Binary files /dev/null and b/public/og.jpeg differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..e7a5185 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,9 @@ +# * +User-agent: * +Allow: / + +# Host +Host: https://laconic.com/ + +# Sitemaps +Sitemap: https://laconic.com/sitemap.xml diff --git a/public/safari-pinned-tab.svg b/public/safari-pinned-tab.svg new file mode 100644 index 0000000..ea1ad06 --- /dev/null +++ b/public/safari-pinned-tab.svg @@ -0,0 +1,22 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..6d2f548 --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Laconic", + "short_name": "Laconic", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/public/videos/hero-grid.mp4 b/public/videos/hero-grid.mp4 new file mode 100644 index 0000000..739acce Binary files /dev/null and b/public/videos/hero-grid.mp4 differ diff --git a/src/components/common/README.md b/src/components/common/README.md new file mode 100644 index 0000000..e0d5af9 --- /dev/null +++ b/src/components/common/README.md @@ -0,0 +1,5 @@ +# `common` dir + +- Common components (maybe reused across the whole app) +- Not primitives +- Examples: `/media-card.tsx`, `/sidenav.tsx`, `/fade-in-box.tsx`, etc... diff --git a/src/components/common/cash-transition/cash-transition.module.scss b/src/components/common/cash-transition/cash-transition.module.scss new file mode 100644 index 0000000..ab17533 --- /dev/null +++ b/src/components/common/cash-transition/cash-transition.module.scss @@ -0,0 +1,8 @@ +.transition { + position: fixed; + top: 100%; + left: 0; + z-index: 20; + pointer-events: none; + user-select: none; +} diff --git a/src/components/common/cash-transition/index.tsx b/src/components/common/cash-transition/index.tsx new file mode 100644 index 0000000..bd79728 --- /dev/null +++ b/src/components/common/cash-transition/index.tsx @@ -0,0 +1,59 @@ +import Image from 'next/image' +import * as React from 'react' + +import { DURATION, gsap } from '~/lib/gsap' +import { usePageTransition } from '~/lib/gsap/page-transitions' + +import cashSrc from '../../../../public/images/cash-transition.png' +import s from './cash-transition.module.scss' + +export const CashTransition = React.memo(() => { + const { getTransitionSpace } = usePageTransition() + const cashRef = React.useRef(null) + + React.useEffect(() => { + getTransitionSpace(async () => { + const tl = gsap.timeline({ + onComplete: () => { + gsap.to(cashRef.current, { + top: '100%', + duration: DURATION * 2, + ease: 'power4.inOut' + }) + } + }) + + const vw = window.innerWidth / 100 + const vh = window.innerHeight / 100 + + const newTop = -1 * Math.max(vw * 12, vh * 20) + + tl.to(cashRef.current, { + top: newTop, + duration: DURATION * 2.5, + ease: 'power4.inOut' + }) + await tl + }) + }, [getTransitionSpace]) + + return ( +
+ cash +
+ ) +}) diff --git a/src/components/common/cursor/cursor.module.scss b/src/components/common/cursor/cursor.module.scss new file mode 100644 index 0000000..9091bd6 --- /dev/null +++ b/src/components/common/cursor/cursor.module.scss @@ -0,0 +1,14 @@ +.cursor { + display: flex; + top: 0; + left: 0; + z-index: 50; + max-width: 35px; + pointer-events: none; + user-select: none; + + > span { + position: relative; + display: flex; + } +} diff --git a/src/components/common/cursor/index.tsx b/src/components/common/cursor/index.tsx new file mode 100644 index 0000000..a3aa9ce --- /dev/null +++ b/src/components/common/cursor/index.tsx @@ -0,0 +1,156 @@ +import gsap from 'gsap' +import Head from 'next/head' +import * as React from 'react' + +import { useDeviceDetect } from '~/hooks/use-device-detect' + +import defaultSrc from '../../../../public/images/cursor/default.svg' +import defaultActiveSrc from '../../../../public/images/cursor/default-active.svg' +import pointerSrc from '../../../../public/images/cursor/pointer.svg' +import pointerActiveSrc from '../../../../public/images/cursor/pointer-active.svg' +import s from './cursor.module.scss' + +type CursorType = 'pointer' | 'default' | undefined + +const CursorContext = React.createContext< + { setType: React.Dispatch> } | undefined +>(undefined) + +const Cursor = ({ children }: { children?: React.ReactNode }) => { + const cursorRef = React.useRef(null) + const [type, setType] = React.useState() + const { isMobile } = useDeviceDetect() + + React.useEffect(() => { + if (!cursorRef.current) return + gsap.set(cursorRef.current, { xPercent: -50, yPercent: -50 }) + + const pos = { x: window.innerWidth / 2, y: window.innerHeight / 2 } + const mouse = { x: pos.x, y: pos.y } + const speed = 0.2 + + const xSet = gsap.quickSetter(cursorRef.current, 'x', 'px') + const ySet = gsap.quickSetter(cursorRef.current, 'y', 'px') + + function handleMouseMove(e: MouseEvent) { + mouse.x = e.x + mouse.y = e.y + if (e.target instanceof HTMLElement || e.target instanceof SVGElement) { + if (e.target.dataset.cursor) { + setType(e.target.dataset.cursor as any) + return + } + if (e.target.closest('button') || e.target.closest('a')) { + setType('pointer') + return + } else if ( + e.target.closest('p') || + e.target.closest('span') || + e.target.closest('h1') || + e.target.closest('h2') || + e.target.closest('h3') || + e.target.closest('h4') || + e.target.closest('h5') || + e.target.closest('h5') || + e.target.closest('input') || + e.target.closest('textarea') + ) { + setType('default') // this would be for text, if we'd have any text cursor + return + } + } + setType(undefined) + } + + function handleTick() { + const dt = 1.0 - Math.pow(0.6 - speed, gsap.ticker.deltaRatio()) + + pos.x += (mouse.x - pos.x) * dt + pos.y += (mouse.y - pos.y) * dt + xSet(pos.x) + ySet(pos.y) + } + + window.addEventListener('mousemove', handleMouseMove, { passive: true }) + gsap.ticker.add(handleTick) + + return () => { + window.removeEventListener('mousemove', handleMouseMove) + gsap.ticker.remove(handleTick) + } + }, [isMobile]) + + return ( + <> + {isMobile === false && } + + {children} + + + ) +} + +const CursorFollower = React.forwardRef( + ({ type }, ref) => { + const { src, adjustments } = React.useMemo(() => { + switch (type) { + case 'pointer': + return { + src: pointerSrc, + adjustments: { x: '4px', y: '22px' } + } + default: + return { + src: defaultSrc, + adjustments: { x: '10px', y: '17px' } + } + } + }, [type]) + + React.useEffect(() => { + document.documentElement.classList.add('has-custom-cursor') + + return () => { + document.documentElement.classList.remove('has-custom-cursor') + } + }, []) + + return ( +
+ + {/* preload images */} + {[defaultSrc, defaultActiveSrc, pointerSrc, pointerActiveSrc].map( + (src) => { + return ( + + ) + } + )} + + + {`cursor-${type}`} + +
+ ) + } +) + +export const useCursor = () => { + const context = React.useContext(CursorContext) + if (context === undefined) { + throw new Error('useCursor must be used within a CursorProvider') + } + return context +} + +export default Cursor diff --git a/src/components/common/fade-in-box/index.tsx b/src/components/common/fade-in-box/index.tsx new file mode 100644 index 0000000..29da345 --- /dev/null +++ b/src/components/common/fade-in-box/index.tsx @@ -0,0 +1,94 @@ +import { forwardRef, useEffect, useRef, useState } from 'react' +import mergeRefs from 'react-merge-refs' + +import { useAnimationContext } from '~/context/animation' +import { useIsomorphicLayoutEffect } from '~/hooks/use-isomorphic-layout-effect' +import { DURATION, gsap } from '~/lib/gsap' + +type FadeInBoxProps = { + children: React.ReactNode + className?: string + id?: string + style?: React.CSSProperties + onFadeInCompleteTimeline?: GSAPTimeline +} + +export const FadeInBox = forwardRef( + ({ children, className, id, style, onFadeInCompleteTimeline }, ref) => { + const innerRef = useRef(null) + const { shouldAnimate } = useAnimationContext() + const [inView, setInView] = useState(false) + + useEffect(() => { + const elementToObserve = innerRef.current + if (!elementToObserve) return + const handleObserve: IntersectionObserverCallback = ([element]) => { + if (element) { + setInView((p) => { + // trigger once + if (p === true) return true + else return element.isIntersecting + }) + } + } + + const observer = new IntersectionObserver(handleObserve, { + threshold: 0.14 + }) + + observer.observe(elementToObserve) + + return () => { + observer.disconnect() + } + }, []) + + useIsomorphicLayoutEffect(() => { + if (!shouldAnimate || !inView) { + return + } + if (!innerRef.current) return + + const tl = gsap.timeline({ + paused: true, + smoothChildTiming: true, + defaults: { ease: 'slow', overwrite: true }, + onComplete: () => { + if ( + onFadeInCompleteTimeline && + typeof onFadeInCompleteTimeline !== 'undefined' + ) { + onFadeInCompleteTimeline.play() + } + } + }) + + tl.to(innerRef.current, { + autoAlpha: 1, + scale: 1, + duration: DURATION + }) + + tl.play() + + return () => { + tl.kill() + } + }, [inView, shouldAnimate, onFadeInCompleteTimeline]) + + return ( +
+ {children} +
+ ) + } +) diff --git a/src/components/common/footer/footer.module.scss b/src/components/common/footer/footer.module.scss new file mode 100644 index 0000000..981597b --- /dev/null +++ b/src/components/common/footer/footer.module.scss @@ -0,0 +1,117 @@ +@import '~/css/helpers'; + +.footer { + position: relative; + z-index: 10; + + .container { + display: flex; + justify-content: space-between; + padding-bottom: tovw(56px, 'default', 48px); + border-bottom: tovw(1px, 'default', 1px) solid var(--color-grey-light); + + nav { + display: flex; + gap: tovw(88px, 'default', 64px); + @media screen and (max-width: 1024px) { + display: grid; + column-gap: tovw(97px, 'tablet', 97px); + grid-template-columns: repeat(2, 1fr); + grid-template-rows: auto; + row-gap: tovw(44px, 'tablet', 44px); + } + } + + @media screen and (max-width: 1024px) { + flex-direction: column; + } + } + + ul { + margin: 0; + padding: 0; + list-style-type: none; + + > li { + line-height: 1.35; + + &:first-of-type { + line-height: 1; + margin-bottom: tovw(12px, 'default', 10px); + + a { + font-weight: 500; + } + } + } + } +} + +.logo { + margin-right: tovw(88px, 'default', 64px); + @media screen and (max-width: 1024px) { + margin-right: 0; + margin-bottom: tovw(56px, 'tablet', 56px); + } + + svg { + width: tovw(305px, 'default', 120px); + @media screen and (max-width: 1024px) { + width: 100%; + } + } +} + +.connect__links { + > div { + display: grid; + align-content: flex-start; + gap: tovw(16px, 'default', 16px); + grid-template-columns: repeat(2, 1fr); + grid-template-rows: auto; + + li { + width: tovw(24px, 'default', 24px); + height: tovw(24px, 'default', 24px); + } + } + @media screen and (max-width: 800px) { + display: none; + } +} + +.sub__footer { + margin-top: tovw(18px, 'default', 18px); + margin-bottom: tovw(44px, 'default', 36px); + + a, + p { + font-size: tovw(18px, 'default', 14px); + line-height: 1; + margin: 0; + color: var(--color-grey-light); + } + + ul { + display: flex; + justify-content: space-between; + width: 100%; + @media screen and (max-width: 800px) { + align-items: center; + flex-direction: column; + justify-content: center; + + li:last-of-type { + margin-bottom: 0; + } + } + + div { + display: flex; + gap: tovw(37px, 'default', 24px); + @media screen and (max-width: 800px) { + margin-bottom: tovw(32px, 'tablet', 32px); + } + } + } +} diff --git a/src/components/common/footer/footer.tsx b/src/components/common/footer/footer.tsx new file mode 100644 index 0000000..b15de6e --- /dev/null +++ b/src/components/common/footer/footer.tsx @@ -0,0 +1,51 @@ +import { + Facebook, + Instagram, + Linkedin, + Reddit, + Telegram, + Twitter +} from '~/components/icons/socials' + +export const DevelopersLinks = [ + { href: '/developers', title: 'Developers' }, + { href: '/github', title: 'Github' }, + { href: '/roadmap', title: 'Roadmap' }, + { href: '/chat', title: 'Chat' }, + { href: '/forum', title: 'Forum' } +] + +export const ProductsLinks = [ + { href: '/products', title: 'Products' }, + { href: '/SDK', title: 'SDK' }, + { href: '/watchers', title: 'Watchers' }, + { href: '/network', title: 'Network' }, + { href: '/wallet', title: 'Wallet' }, + { href: '/token', title: 'Token' } +] + +export const AboutLinks = [ + { href: '/about', title: 'About' }, + { href: '/faq', title: 'FAQ' }, + { href: '/team', title: 'Team' }, + { href: '/partners', title: 'Partners' }, + { href: '/newsroom', title: 'Newstoom' }, + { href: '/careers', title: 'Careers' }, + { href: '/contact', title: 'Contact' } +] + +export const CommunityLinks = [ + { href: '/community', title: 'Community' }, + { href: '/validators', title: 'Validators' }, + { href: '/testnet', title: 'Testnet' }, + { href: '/insiders', title: 'Insiders' } +] + +export const ConnectLinks = [ + { href: '/community', title: 'Twitter', logo: }, + { href: '/validators', title: 'Telegram', logo: }, + { href: '/testnet', title: 'Reddit', logo: }, + { href: '/insiders', title: 'Linkedin', logo: }, + { href: '/insiders', title: 'Facebook', logo: }, + { href: '/insiders', title: 'Instagram', logo: } +] diff --git a/src/components/common/footer/index.tsx b/src/components/common/footer/index.tsx new file mode 100644 index 0000000..aaac05f --- /dev/null +++ b/src/components/common/footer/index.tsx @@ -0,0 +1,112 @@ +import { LogoFooter } from '~/components/icons/logo' +import { Container } from '~/components/layout/container' +import Link from '~/components/primitives/link' + +import { + AboutLinks, + CommunityLinks, + ConnectLinks, + DevelopersLinks, + ProductsLinks +} from './footer' +import s from './footer.module.scss' + +export const Footer = () => { + return ( +
+ +
+ + + +
+ +
+ + + +
+ ) +} diff --git a/src/components/common/header/header.module.scss b/src/components/common/header/header.module.scss new file mode 100644 index 0000000..2e5141f --- /dev/null +++ b/src/components/common/header/header.module.scss @@ -0,0 +1,34 @@ +@import '~/css/helpers'; + +.header { + position: fixed; + z-index: 10; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: tovw(14px, 'default', 14px) var(--main-padding-side); + background: rgb(4 4 4 / 0.01); + backdrop-filter: blur(20px); + + nav { + display: flex; + align-items: center; + + svg { + display: block; + } + } + + ul { + display: flex; + margin: 0 0 0 tovw(122px, 'default', 90px); + padding: 0; + list-style-type: none; + gap: tovw(32px, 'default', 20px); + } + + .burger { + width: tovw(22px, 'default', 22px); + } +} diff --git a/src/components/common/header/header.ts b/src/components/common/header/header.ts new file mode 100644 index 0000000..c3fa091 --- /dev/null +++ b/src/components/common/header/header.ts @@ -0,0 +1,8 @@ +export const defaultHeaderLinks = [ + { href: '/', title: 'Home' }, + { href: '/developers', title: 'Developers' }, + { href: '/products', title: 'Products' }, + { href: '/community', title: 'Community' }, + { href: '/about', title: 'About' }, + { href: '/blog', title: 'Blog' } +] diff --git a/src/components/common/header/index.tsx b/src/components/common/header/index.tsx new file mode 100644 index 0000000..730db2b --- /dev/null +++ b/src/components/common/header/index.tsx @@ -0,0 +1,38 @@ +import clsx from 'clsx' +import NextLink from 'next/link' + +import Burger from '~/components/icons/burguer' +import { Logo } from '~/components/icons/logo' +import { Button } from '~/components/primitives/button' +import Link from '~/components/primitives/link' + +import { defaultHeaderLinks } from './header' +import s from './header.module.scss' + +export const Header = () => { + return ( +
+ + + +
+ ) +} diff --git a/src/components/common/meta.tsx b/src/components/common/meta.tsx new file mode 100644 index 0000000..814518d --- /dev/null +++ b/src/components/common/meta.tsx @@ -0,0 +1,96 @@ +import { useRouter } from 'next/dist/client/router' +import NextHead from 'next/head' +import { NextSeo, NextSeoProps } from 'next-seo' +import * as React from 'react' + +import { defaultMeta, siteOrigin } from '~/lib/constants' + +type BasicMeta = { + colorScheme?: 'dark' | 'light' + description?: string + noIndex?: boolean + ogImage?: string + prefetch?: { href: string; as: string }[] + preload?: { href: string; as: string }[] + themeColor?: string + title?: string +} + +export type MetaProps = BasicMeta & { rawNextSeoProps?: NextSeoProps } + +export const Meta = (props: MetaProps) => { + const router = useRouter() + + const nextSeoProps: NextSeoProps = React.useMemo(() => { + return { + title: props.title ?? defaultMeta.title, + description: props.description ?? defaultMeta.description, + canonical: `${siteOrigin}${router.pathname}`, + openGraph: { + images: [ + { + url: props.ogImage ?? defaultMeta.ogImage, + alt: props.title ?? defaultMeta.title, + width: 1200, + height: 630, + type: 'image/jpeg' + } + ] + }, + twitter: { + cardType: 'summary_large_image', + handle: defaultMeta.twitter.handle, + site: defaultMeta.twitter.site + }, + noindex: props.noIndex, + ...props.rawNextSeoProps + } + }, [props, router.pathname]) + + return ( + <> + + + + + + + + + + + + + {props.preload?.map(({ href, as }) => ( + + ))} + {props.prefetch?.map(({ href, as }) => ( + + ))} + + + + + ) +} diff --git a/src/components/common/noise/index.tsx b/src/components/common/noise/index.tsx new file mode 100644 index 0000000..8b48324 --- /dev/null +++ b/src/components/common/noise/index.tsx @@ -0,0 +1,44 @@ +import clsx from 'clsx' + +import { useDeviceDetect } from '~/hooks/use-device-detect' + +import s from './noise.module.css' + +export const Noise = ({ + softLight = true, + colorBurn = true, + ignoreDevice = false, + absolute = false +}) => { + const { isSafari, isMobile, loaded } = useDeviceDetect() + + if (!loaded) return null + return ( + <> + {(ignoreDevice || (!isSafari && !isMobile)) && ( + <> + {softLight && ( +
+ )} + {colorBurn && ( +
+ )} + + )} + + ) +} diff --git a/src/components/common/noise/noise.module.css b/src/components/common/noise/noise.module.css new file mode 100644 index 0000000..24e5ba6 --- /dev/null +++ b/src/components/common/noise/noise.module.css @@ -0,0 +1,77 @@ +.noise { + background-color: white; + background-image: url('/images/noise.png'); + background-repeat: repeat; + background-size: auto; + z-index: 500; + width: 300%; + height: 300%; + left: -100%; + top: -100%; + pointer-events: none; + mix-blend-mode: soft-light; + opacity: 0.2; +} + +.noise2 { + background-color: white; + background-image: url('/images/noise-2.png'); + background-repeat: repeat; + background-size: auto; + z-index: 500; + width: 300%; + height: 300%; + left: -100%; + top: -100%; + pointer-events: none; + mix-blend-mode: color-burn; + opacity: 0.8; + will-change: transform; + animation: grain 16s steps(10) infinite; +} + +@keyframes grain { + 0% { + transform: translate(20%, -15%); + } + + 10% { + transform: translate(-20%, -15%); + } + + 20% { + transform: translate(20%, -5%); + } + + 30% { + transform: translate(-20%, -5%); + } + + 40% { + transform: translate(20%, 5%); + } + + 50% { + transform: translate(-20%, 5%); + } + + 60% { + transform: translate(20%, 15%); + } + + 70% { + transform: translate(-20%, 15%); + } + + 80% { + transform: translate(20%, 5%); + } + + 90% { + transform: translate(-20%, 5%); + } + + 100% { + transform: translate(20%, -5%); + } +} diff --git a/src/components/icons/arrow.tsx b/src/components/icons/arrow.tsx new file mode 100644 index 0000000..8089b97 --- /dev/null +++ b/src/components/icons/arrow.tsx @@ -0,0 +1,59 @@ +const Arrow = ({ className, fill }: { className?: string; fill?: string }) => { + return ( + + + + ) +} + +const ArrowDotted = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + + ) +} + +const ArrowLink = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + ) +} + +export { Arrow, ArrowDotted, ArrowLink } diff --git a/src/components/icons/burguer.tsx b/src/components/icons/burguer.tsx new file mode 100644 index 0000000..f955798 --- /dev/null +++ b/src/components/icons/burguer.tsx @@ -0,0 +1,18 @@ +const Burger = ({ className, fill }: { className?: string; fill?: string }) => { + return ( + + + + ) +} + +export default Burger diff --git a/src/components/icons/logo.tsx b/src/components/icons/logo.tsx new file mode 100644 index 0000000..02ac284 --- /dev/null +++ b/src/components/icons/logo.tsx @@ -0,0 +1,72 @@ +const Isotype = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + ) +} + +const Logo = ({ className, fill }: { className?: string; fill?: string }) => { + return ( + + + + + ) +} + +const LogoFooter = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + ) +} + +export { Isotype, Logo, LogoFooter } diff --git a/src/components/icons/socials.tsx b/src/components/icons/socials.tsx new file mode 100644 index 0000000..d748489 --- /dev/null +++ b/src/components/icons/socials.tsx @@ -0,0 +1,228 @@ +const Telegram = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + + + + + + + ) +} + +const Twitter = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + + + + + + + ) +} + +const Reddit = ({ className, fill }: { className?: string; fill?: string }) => { + return ( + + + + + + + + + + ) +} + +const Linkedin = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + ) +} + +const Facebook = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + + + + + + + ) +} + +const Instagram = ({ + className, + fill +}: { + className?: string + fill?: string +}) => { + return ( + + + + + + + + + + + + + + + + + + + + ) +} + +export { Facebook, Instagram, Linkedin, Reddit, Telegram, Twitter } diff --git a/src/components/layout/aspect-box/aspect-box.module.css b/src/components/layout/aspect-box/aspect-box.module.css new file mode 100644 index 0000000..bf34992 --- /dev/null +++ b/src/components/layout/aspect-box/aspect-box.module.css @@ -0,0 +1,28 @@ +.aspect-box { + position: relative; +} + +@supports (aspect-ratio: var(--raw-ratio)) { + .aspect-box { + aspect-ratio: var(--raw-ratio); + position: relative; + } +} + +@supports not (aspect-ratio: var(--raw-ratio)) { + .aspect-box::before { + content: ''; + width: 1px; + margin-left: -1px; + float: left; + height: 0; + padding-top: var(--ratio); + } + + .aspect-box::after { + /* to clear float */ + content: ''; + display: table; + clear: both; + } +} diff --git a/src/components/layout/aspect-box/index.tsx b/src/components/layout/aspect-box/index.tsx new file mode 100644 index 0000000..8058710 --- /dev/null +++ b/src/components/layout/aspect-box/index.tsx @@ -0,0 +1,26 @@ +import clsx from 'clsx' +import * as React from 'react' + +import s from './aspect-box.module.css' + +export const AspectBox = ({ + ratio, + children, + className, + style, + ...rest +}: { ratio: number } & JSX.IntrinsicElements['div']) => { + return ( +
+ {children} +
+ ) +} diff --git a/src/components/layout/container/container.module.scss b/src/components/layout/container/container.module.scss new file mode 100644 index 0000000..82efddb --- /dev/null +++ b/src/components/layout/container/container.module.scss @@ -0,0 +1,12 @@ +@import '~/css/helpers'; + +.container { + width: 100%; + max-width: tovw(1296px, 'default', 320px); + margin: 0 auto; + + @media screen and (max-width: 800px) { + max-width: 100%; + padding: 0 tovw(27px, 'tablet', 27px); + } +} diff --git a/src/components/layout/container/index.tsx b/src/components/layout/container/index.tsx new file mode 100644 index 0000000..d15c08e --- /dev/null +++ b/src/components/layout/container/index.tsx @@ -0,0 +1,15 @@ +import clsx from 'clsx' +import * as React from 'react' + +import s from './container.module.scss' + +export const Container = React.forwardRef< + HTMLDivElement, + JSX.IntrinsicElements['div'] +>(({ className, ...props }, ref) => { + return ( +
+ ) +}) + +export type ContainerProps = React.ComponentProps diff --git a/src/components/layout/page.tsx b/src/components/layout/page.tsx new file mode 100644 index 0000000..77959fa --- /dev/null +++ b/src/components/layout/page.tsx @@ -0,0 +1,34 @@ +import * as React from 'react' + +import { Footer } from '~/components/common/footer' +import { Header } from '~/components/common/header' +import { LocomotiveScrollProvider } from '~/lib/locomotive-scroll/provider' + +type Props = { + children?: React.ReactNode + extras?: React.ReactNode + locomotiveScroll?: boolean +} + +const ContentMemo = React.memo(({ children, extras }: Props) => { + return ( + <> + {extras} +
+
{children}
+