├── .eslintrc.json ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── README.md ├── lint-staged.config.js ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── avatars │ └── Avatar.png ├── favicon.ico └── vercel.svg ├── src ├── assets │ └── icons │ │ ├── angular.svg │ │ ├── arrow-up-right.svg │ │ ├── css.svg │ │ ├── discord.svg │ │ ├── docker.svg │ │ ├── facebook.svg │ │ ├── figma.svg │ │ ├── git.svg │ │ ├── github-link.svg │ │ ├── github.svg │ │ ├── html.svg │ │ ├── index.ts │ │ ├── js.svg │ │ ├── link.svg │ │ ├── linkedin.svg │ │ ├── node.svg │ │ ├── npm.svg │ │ ├── react.svg │ │ ├── redux.svg │ │ ├── remix.svg │ │ ├── sass.svg │ │ ├── svelte.svg │ │ ├── ts.svg │ │ ├── twitter.svg │ │ ├── visual.svg │ │ ├── vue.svg │ │ └── webpack.svg ├── components │ ├── AuthorCard.tsx │ ├── Avatar.tsx │ ├── Badge.tsx │ ├── BlogCard.tsx │ ├── Button.tsx │ ├── Container.tsx │ ├── Footer.tsx │ ├── Header.tsx │ ├── Hero.tsx │ ├── IconButton.tsx │ ├── Input.tsx │ ├── Pagination │ │ ├── Pagination.tsx │ │ └── Pagination.types.ts │ ├── Quote │ │ └── QuoteSlider.tsx │ ├── TeamCard.tsx │ ├── index.ts │ └── sections │ │ ├── FaqSection.tsx │ │ ├── FeaturesSection.tsx │ │ ├── MetricsSection.tsx │ │ ├── QuotesSection.tsx │ │ └── TechnologiesSection.tsx ├── content │ └── blog │ │ └── oguz-blog-test.mdx ├── data │ ├── faq.ts │ ├── features.ts │ ├── navigation.ts │ ├── quotes.ts │ └── technologies.ts ├── pages │ ├── _app.tsx │ ├── about.tsx │ ├── api │ │ └── hello.ts │ ├── blog │ │ ├── [slug].tsx │ │ └── index.tsx │ └── index.tsx ├── styles │ └── globals.css ├── types │ └── index.ts └── utils │ ├── blog.js │ └── pagination.util.ts ├── tailwind.config.js └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "next/core-web-vitals", 4 | "prettier", 5 | "plugin:tailwindcss/recommended" 6 | ], 7 | "plugins": ["prettier", "tailwindcss", "simple-import-sort"], 8 | "rules": { 9 | "quotes": ["error", "single"], 10 | "prettier/prettier": "warn", 11 | "no-console": "warn", 12 | "simple-import-sort/imports": "error", 13 | "no-duplicate-imports": "error", 14 | "no-unused-vars": ["error", { "args": "all" }], 15 | "no-duplicate-case": "error", 16 | "no-empty": "error", 17 | "no-use-before-define": "error", 18 | "tailwindcss/classnames-order": "warn", 19 | "tailwindcss/enforces-negative-arbitrary-values": "warn", 20 | "tailwindcss/enforces-shorthand": "warn", 21 | "tailwindcss/migration-from-tailwind-2": "warn", 22 | "tailwindcss/no-arbitrary-value": "off", 23 | "tailwindcss/no-custom-classname": "warn", 24 | "tailwindcss/no-contradicting-classname": "error" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .idea 8 | 9 | # next.js 10 | /.next/ 11 | /out/ 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .pnpm-debug.log* 25 | 26 | # local env files 27 | .env*.local 28 | 29 | # vercel 30 | .vercel 31 | 32 | # typescript 33 | *.tsbuildinfo 34 | next-env.d.ts 35 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | echo 'Running Git Hooks' 5 | 6 | echo "🔎 Checking validity of types with TypeScript" 7 | 8 | yarn type-check || ( 9 | "\n⛔️ There is a type error in the code, fix it, and try commit again. ⛔️"; 10 | false; 11 | ) 12 | 13 | 14 | echo "\n✅ No TypeError found" 15 | echo "\n🔎 Running linter.." 16 | 17 | yarn lint || ( 18 | echo '\n⛔️ There is a problem in the code. ⌛️ I run linter autofix for you.'; 19 | 20 | echo '\n🔎 Running linter autofix..' 21 | yarn lint:fix || ( 22 | echo '\n⛔️ Autofix failed. Please fix the linting errors manually. ⛔️'; 23 | false; 24 | ) 25 | 26 | echo '\n🧐 Please check the changes and commit again.\n' 27 | false; 28 | ) 29 | 30 | echo '✅ No Eslint error found' 31 | echo '⌛️ Running lint staged and git commit ⌛️' 32 | 33 | npx lint-staged -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .next 2 | .cache 3 | package-lock.json 4 | public 5 | node_modules 6 | next-env.d.ts 7 | next.config.ts 8 | yarn.lock -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "none", 4 | "arrowParens": "avoid", 5 | "proseWrap": "preserve", 6 | "quoteProps": "as-needed", 7 | "bracketSameLine": false, 8 | "bracketSpacing": true, 9 | "tabWidth": 2 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with 2 | [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 3 | 4 | ## Getting Started 5 | 6 | First, run the development server: 7 | 8 | ```bash 9 | npm run dev 10 | # or 11 | yarn dev 12 | ``` 13 | 14 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the 15 | result. 16 | 17 | You can start editing the page by modifying `pages/Avatar.tsx`. The page 18 | auto-updates as you edit the file. 19 | 20 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on 21 | [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This 22 | endpoint can be edited in `pages/api/hello.ts`. 23 | 24 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are 25 | treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead 26 | of React pages. 27 | 28 | ## Learn More 29 | 30 | To learn more about Next.js, take a look at the following resources: 31 | 32 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js 33 | features and API. 34 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 35 | 36 | You can check out 37 | [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your 38 | feedback and contributions are welcome! 39 | 40 | ## Deploy on Vercel 41 | 42 | The easiest way to deploy your Next.js app is to use the 43 | [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) 44 | from the creators of Next.js. 45 | 46 | Check out our 47 | [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more 48 | details. 49 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '**/*.(ts|tsx)': () => 'yarn tsc --noEmit', 3 | 4 | '**/*.(ts|tsx|js)': filenames => [ 5 | `yarn eslint --fix ${filenames.join(' ')}`, 6 | `yarn prettier --write ${filenames.join(' ')}` 7 | ], 8 | 9 | '**/*.(md|json)': filenames => `yarn prettier --write ${filenames.join(' ')}` 10 | }; 11 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | 3 | const withSvgr = require('next-svgr'); 4 | 5 | const nextConfig = { 6 | reactStrictMode: true, 7 | swcMinify: true, 8 | webpack(config) { 9 | return config; 10 | }, 11 | images: { 12 | domains: ['i.ytimg.com', 'www.gravatar.com'] 13 | }, 14 | env: { 15 | DISCORD_SERVER_URL: 'https://discord.gg/frontendship' 16 | } 17 | }; 18 | 19 | module.exports = withSvgr(nextConfig); 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontendship-website", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "lint:fix": "next lint --fix .", 11 | "format": "npx prettier --write .", 12 | "commit": "cz", 13 | "type-check": "tsc --noEmit" 14 | }, 15 | "dependencies": { 16 | "@headlessui/react": "^1.7.13", 17 | "@next/font": "^13.0.5", 18 | "@tailwindcss/typography": "^0.5.9", 19 | "@types/node": "18.11.9", 20 | "@types/react": "18.0.25", 21 | "@types/react-dom": "18.0.9", 22 | "class-variance-authority": "^0.4.0", 23 | "commitizen": "^4.2.6", 24 | "cz-conventional-changelog": "^3.3.0", 25 | "eslint": "8.28.0", 26 | "eslint-config-next": "13.0.5", 27 | "eslint-config-prettier": "^8.5.0", 28 | "eslint-plugin-prettier": "^4.2.1", 29 | "gray-matter": "^4.0.3", 30 | "next": "13.0.5", 31 | "next-mdx-remote": "^4.2.0", 32 | "next-svgr": "^0.0.2", 33 | "path": "^0.12.7", 34 | "prettier": "^2.8.0", 35 | "react": "18.2.0", 36 | "react-dom": "18.2.0", 37 | "react-feather": "^2.0.10", 38 | "react-hook-form": "^7.42.0", 39 | "reading-time": "^1.5.0", 40 | "rehype-pretty-code": "^0.3.2", 41 | "rehype-slug": "^5.1.0", 42 | "shiki": "^0.11.1", 43 | "swiper": "^8.4.6", 44 | "tailwind-merge": "^1.8.1", 45 | "typescript": "4.9.3" 46 | }, 47 | "devDependencies": { 48 | "@svgr/webpack": "^6.5.1", 49 | "autoprefixer": "^10.4.13", 50 | "eslint-plugin-simple-import-sort": "^8.0.0", 51 | "eslint-plugin-tailwindcss": "^3.8.0", 52 | "husky": "^8.0.3", 53 | "lint-staged": "^13.1.0", 54 | "postcss": "^8.4.19", 55 | "tailwindcss": "^3.2.4" 56 | }, 57 | "config": { 58 | "commitizen": { 59 | "path": "./node_modules/cz-conventional-changelog" 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /public/avatars/Avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendship/frontendship-website/966990c1d033a2624c1efce30cb5d67e4ffaafa5/public/avatars/Avatar.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frontendship/frontendship-website/966990c1d033a2624c1efce30cb5d67e4ffaafa5/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/angular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/arrow-up-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/css.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/docker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/icons/facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/figma.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/icons/git.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/github-link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/html.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AngularIcon } from './angular.svg'; 2 | export { default as DockerIcon } from './docker.svg'; 3 | export { default as FigmaIcon } from './figma.svg'; 4 | export { default as GitIcon } from './git.svg'; 5 | export { default as GithubIcon } from './github.svg'; 6 | export { default as HTMLIcon } from './html.svg'; 7 | export { default as JavaScriptIcon } from './js.svg'; 8 | export { default as NodeIcon } from './node.svg'; 9 | export { default as NpmIcon } from './npm.svg'; 10 | export { default as ReactIcon } from './react.svg'; 11 | export { default as ReduxIcon } from './redux.svg'; 12 | export { default as VisualStudioIcon } from './visual.svg'; 13 | export { default as VueIcon } from './vue.svg'; 14 | export { default as WebpackIcon } from './webpack.svg'; 15 | export { default as TypeScriptIcon } from './ts.svg'; 16 | export { default as RemixRunIcon } from './remix.svg'; 17 | 18 | export { default as TwitterIcon } from './twitter.svg'; 19 | export { default as FacebookIcon } from './facebook.svg'; 20 | export { default as LinkedinIcon } from './linkedin.svg'; 21 | export { default as LinkIcon } from './link.svg'; 22 | export { default as GithubLinkIcon } from './github-link.svg'; 23 | export { default as DiscordIcon } from './discord.svg'; 24 | -------------------------------------------------------------------------------- /src/assets/icons/js.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/linkedin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/node.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/npm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/redux.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/remix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Shape 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/sass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/svelte.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/ts.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/visual.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/assets/icons/vue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/webpack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/components/AuthorCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function AuthorCard({ children }: { children: React.ReactNode }) { 4 | return
{children}
; 5 | } 6 | 7 | AuthorCard.Avatar = function AuthorAvatar({ 8 | source 9 | }: { 10 | source: { src: string; alt: string }; 11 | }) { 12 | return ( 13 | {source?.alt} 18 | ); 19 | }; 20 | 21 | AuthorCard.Info = function AuthorInfo({ 22 | children 23 | }: { 24 | children: React.ReactNode; 25 | }) { 26 | return
{children}
; 27 | }; 28 | 29 | AuthorCard.Name = function AuthorName({ 30 | children 31 | }: { 32 | children: React.ReactNode; 33 | }) { 34 | return ( 35 | {children} 36 | ); 37 | }; 38 | 39 | AuthorCard.Position = function AuthorPosition({ 40 | children 41 | }: { 42 | children: string; 43 | }) { 44 | return {children}; 45 | }; 46 | -------------------------------------------------------------------------------- /src/components/Avatar.tsx: -------------------------------------------------------------------------------- 1 | import Image, { ImageProps } from 'next/image'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export const SIZES = { 5 | xs: 24, 6 | sm: 32, 7 | md: 40, 8 | lg: 48, 9 | xl: 56, 10 | '2xl': 64 11 | }; 12 | 13 | interface Props extends ImageProps { 14 | className?: string; 15 | size?: keyof typeof SIZES; 16 | src: string; 17 | alt: string; 18 | } 19 | 20 | export const Avatar = ({ 21 | className, 22 | size = 'md', 23 | src, 24 | alt, 25 | ...props 26 | }: Props) => ( 27 | {alt} 38 | ); 39 | 40 | export default Avatar; 41 | -------------------------------------------------------------------------------- /src/components/Badge.tsx: -------------------------------------------------------------------------------- 1 | import { cva, VariantProps } from 'class-variance-authority'; 2 | import { ReactNode } from 'react'; 3 | 4 | const badgeStyles = cva( 5 | [ 6 | 'font-medium', 7 | 'rounded-3xl', 8 | 'flex', 9 | 'shrink-0', 10 | 'items-center', 11 | 'justify-center', 12 | 'w-fit', 13 | 'h-fit' 14 | ], 15 | { 16 | variants: { 17 | variant: { 18 | primary: [ 19 | 'bg-violet-50', 20 | 'text-violet-700', 21 | '[&>.badge-title]:text-violet-50', 22 | '[&>.badge-title]:bg-violet-700' 23 | ], 24 | light: ['bg-violet-50', 'text-violet-700', '[&>.badge-title]:bg-white'], 25 | green: [ 26 | 'bg-green-50', 27 | 'text-green-700', 28 | '[&>.badge-title]:bg-green-700', 29 | '[&>.badge-title]:text-white' 30 | ], 31 | blue: [ 32 | 'bg-blue-50', 33 | 'text-blue-700', 34 | '[&>.badge-title]:bg-blue-700', 35 | '[&>.badge-title]:text-white' 36 | ], 37 | red: [ 38 | 'bg-red-50', 39 | 'text-red-700', 40 | '[&>.badge-title]:bg-red-700', 41 | '[&>.badge-title]:text-white' 42 | ], 43 | orange: [ 44 | 'bg-orange-50', 45 | 'text-orange-700', 46 | '[&>.badge-title]:bg-orange-700', 47 | '[&>.badge-title]:text-white' 48 | ], 49 | gray: [ 50 | 'bg-gray-50', 51 | 'text-gray-700', 52 | '[&>.badge-title]:bg-gray-700', 53 | '[&>.badge-title]:text-white' 54 | ] 55 | }, 56 | size: { 57 | sm: [ 58 | 'text-xs', 59 | 'px-[4px]', 60 | 'py-[1px]', 61 | '[&>*]:px-1', 62 | '[&>*]:py-[1px]', 63 | '[&>*]:my-[2px]' 64 | ], 65 | md: [ 66 | 'text-sm', 67 | 'px-[4px]', 68 | 'py-[1px]', 69 | '[&>*]:px-2', 70 | '[&>*]:py-[1px]', 71 | '[&>*]:my-[2px]' 72 | ], 73 | lg: [ 74 | 'text-sm', 75 | 'px-[6px]', 76 | 'py-[2px]', 77 | '[&>*]:px-2', 78 | '[&>*]:py-[2px]', 79 | '[&>*]:my-[3px]' 80 | ] 81 | } 82 | }, 83 | defaultVariants: { 84 | variant: 'primary', 85 | size: 'md' 86 | } 87 | } 88 | ); 89 | 90 | type BadgeBaseProps = VariantProps; 91 | interface Props extends BadgeBaseProps { 92 | children: ReactNode; 93 | className?: string; 94 | } 95 | 96 | export function Badge({ children, className, size, variant }: Props) { 97 | return ( 98 |
99 | {typeof children == 'string' ? {children} : children} 100 |
101 | ); 102 | } 103 | 104 | Badge.Title = function BadgeTitle({ children }: { children: string }) { 105 | return {children}; 106 | }; 107 | 108 | Badge.Content = function BadgeContent({ children }: { children: string }) { 109 | return {children}; 110 | }; 111 | -------------------------------------------------------------------------------- /src/components/BlogCard.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import Link from 'next/link'; 3 | import React from 'react'; 4 | import { ArrowUpRight } from 'react-feather'; 5 | 6 | interface Props { 7 | children: React.ReactNode; 8 | href?: string; 9 | } 10 | 11 | export function BlogCard({ children }: Props) { 12 | return
{children}
; 13 | } 14 | 15 | BlogCard.Title = function Title({ 16 | children, 17 | href = '/', 18 | ref 19 | }: Props & { ref?: any }) { 20 | return ( 21 | 22 |

{children}

23 | 24 | 25 | ); 26 | }; 27 | 28 | BlogCard.Description = function Description({ children }: Props) { 29 | return

{children}

; 30 | }; 31 | 32 | BlogCard.Image = function Images({ 33 | source 34 | }: { 35 | source: { src: string; alt: string }; 36 | }) { 37 | return ( 38 |
39 | {source.alt} 46 |
47 | ); 48 | }; 49 | 50 | BlogCard.Information = function Information({ 51 | author, 52 | date 53 | }: { 54 | author: string; 55 | date: string; 56 | }) { 57 | return ( 58 |
59 | {author} 60 | 61 | {date} 62 |
63 | ); 64 | }; 65 | -------------------------------------------------------------------------------- /src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { cva, VariantProps } from 'class-variance-authority'; 2 | import React from 'react'; 3 | 4 | const buttonStyles = cva( 5 | ['font-semibold', 'rounded-lg', 'border', 'shadow-sm', 'shadow-violet-100/5'], 6 | { 7 | variants: { 8 | variant: { 9 | filled: [ 10 | 'text-white', 11 | 'bg-blue-600', 12 | 'border-blue-600', 13 | 'hover:bg-blue-700', 14 | 'focus:ring-4', 15 | 'ring-blue-100', 16 | 'disabled:bg-blue-200', 17 | 'disabled:border-blue-200', 18 | 'disabled:cursor-not-allowed' 19 | ], 20 | outline: [ 21 | 'text-black', 22 | 'bg-white', 23 | 'border-gray-300', 24 | 'hover:border-gray-400', 25 | 'focus:ring-4', 26 | 'ring-gray-100', 27 | 'disabled:bg-gray-200', 28 | 'disabled:cursor-not-allowed' 29 | ] 30 | }, 31 | fullWidth: { 32 | true: 'w-full' 33 | }, 34 | size: { 35 | sm: ['text-sm', 'px-[14px]', 'py-2'], 36 | md: ['text-sm', 'px-4', 'py-[10px]'], 37 | lg: ['px-[18px]', 'py-[10px]'], 38 | xl: ['px-5', 'py-3'], 39 | '2xl': ['text-lg', 'px-7', 'py-4'] 40 | } 41 | }, 42 | defaultVariants: { 43 | variant: 'filled', 44 | size: 'md' 45 | } 46 | } 47 | ); 48 | 49 | // TODO: Icon 50 | type ButtonBaseProps = VariantProps; 51 | interface Props 52 | extends React.ButtonHTMLAttributes, 53 | ButtonBaseProps {} 54 | 55 | export const Button = ({ 56 | className, 57 | variant, 58 | fullWidth, 59 | size, 60 | ...props 61 | }: Props) => ( 62 | 32 | 33 | 34 | 35 | 49 | 50 | 51 |
52 | {isMenuOpen && ( 53 | 68 | )} 69 |
70 | 71 | ); 72 | }; 73 | -------------------------------------------------------------------------------- /src/components/Hero.tsx: -------------------------------------------------------------------------------- 1 | import { cva } from 'class-variance-authority'; 2 | import React from 'react'; 3 | 4 | const heroTitle = cva(['mb-6 font-semibold text-gray-900'], { 5 | variants: { 6 | size: { 7 | lg: 'md:text-6xl text-4xl', 8 | md: 'md:text-5xl text-4xl', 9 | sm: 'md:text-4xl text-3xl' 10 | } 11 | }, 12 | defaultVariants: { 13 | size: 'sm' 14 | } 15 | }); 16 | 17 | export function Hero({ children }: { children: React.ReactNode }) { 18 | return ( 19 |
20 | {children} 21 |
22 | ); 23 | } 24 | 25 | Hero.Label = function HeroTitle({ children }: { children: React.ReactNode }) { 26 | return ( 27 | 28 | {children} 29 | 30 | ); 31 | }; 32 | 33 | Hero.Title = function HeroTitle({ 34 | children, 35 | size 36 | }: { 37 | children: React.ReactNode; 38 | size?: 'md' | 'lg' | 'sm'; 39 | }) { 40 | return

{children}

; 41 | }; 42 | 43 | Hero.Subtitle = function HeroSubtitle({ 44 | children 45 | }: { 46 | children: React.ReactNode; 47 | }) { 48 | return ( 49 |

50 | {children} 51 |

52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/components/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import { cva, VariantProps } from 'class-variance-authority'; 2 | import { ButtonHTMLAttributes } from 'react'; 3 | 4 | const iconButtonStyles = cva(['flex w-fit h-fit'], { 5 | variants: { 6 | variant: { 7 | default: [], 8 | gray: [ 9 | 'text-gray-400 fill-current hover:text-gray-500 active:text-gray-600' 10 | ], 11 | bordered: [ 12 | 'border border-gray-200 hover:border-gray-400 active:border-gray-600', 13 | 'rounded-lg', 14 | 'shadow-sm shadow-blue-900/5' 15 | ] 16 | }, 17 | padding: { 18 | none: 'p-0', 19 | sm: 'p-1', 20 | md: 'p-2', 21 | lg: 'p-3' 22 | } 23 | }, 24 | defaultVariants: { 25 | variant: 'default', 26 | padding: 'md' 27 | } 28 | }); 29 | 30 | type IconButtonBaseProps = VariantProps; 31 | interface Props 32 | extends ButtonHTMLAttributes, 33 | IconButtonBaseProps {} 34 | 35 | export const IconButton = ({ 36 | className, 37 | variant, 38 | padding, 39 | ...props 40 | }: Props) => ( 41 | 35 | )} 36 | 37 | ); 38 | }; 39 | 40 | const PaginationNumberButton: React.FC = ({ 41 | onPageClick, 42 | page, 43 | active 44 | }) => { 45 | return ( 46 | 54 | ); 55 | }; 56 | 57 | const PaginationButtonGroup: React.FC = ({ 58 | onPageClick, 59 | pages, 60 | maxPage, 61 | current 62 | }) => { 63 | const [pageItems, setPageItems] = useState([]); 64 | const [totalCount, setTotalCount] = useState(pages.length); 65 | const [centerIndex, setCenterIndex] = useState(0); 66 | 67 | useEffect(() => { 68 | const index = pages.findIndex(p => p === current); 69 | setCenterIndex(index); 70 | setTotalCount(pages.length); 71 | }, [pages, current]); 72 | 73 | useEffect(() => { 74 | setPageItems( 75 | mapPagesToPaginationGroupItems({ 76 | maxPage, 77 | pages, 78 | centerIndex: centerIndex, 79 | totalCount 80 | }) 81 | ); 82 | }, [centerIndex, totalCount, pages, maxPage]); 83 | 84 | return ( 85 |
86 | {pageItems.map((item, index) => ( 87 | onPageClick(item.page)} 92 | /> 93 | ))}{' '} 94 |
95 | ); 96 | }; 97 | 98 | const Pagination: React.FC = ({ 99 | onPageClick, 100 | current, 101 | total, 102 | limit = 10 103 | }) => { 104 | const [isPrevAvailable, setIsPrevAvailable] = useState(false); 105 | const [isNextAvailable, setIsNextAvailable] = useState(false); 106 | const [maxPage, setMaxPage] = useState(0); 107 | const [pages, setPages] = useState([current]); 108 | 109 | useEffect(() => { 110 | setIsPrevAvailable(current > 1); 111 | setIsNextAvailable(current * limit < total); 112 | setMaxPage(Math.ceil(total / limit)); 113 | setPages( 114 | calculatePagination({ 115 | maxPage: maxPage, 116 | minPage: 1, 117 | current: current 118 | }) 119 | ); 120 | }, [maxPage, limit, total, current]); 121 | 122 | return ( 123 |
124 | onPageClick(current - 1)} 128 | content={ 129 | <> 130 | Önceki 131 | 132 | } 133 | />{' '} 134 | onPageClick(page)} 139 | />{' '} 140 | onPageClick(current + 1)} 144 | content={ 145 | <> 146 | Sonraki 147 | 148 | } 149 | /> 150 |
151 | ); 152 | }; 153 | 154 | export default Pagination; 155 | -------------------------------------------------------------------------------- /src/components/Pagination/Pagination.types.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export type Pagination = { 4 | current: number; 5 | limit?: number; 6 | total: number; 7 | }; 8 | 9 | // eslint-disable-next-line no-unused-vars 10 | type PaginationCallback = (current: number) => void; 11 | 12 | export type PaginationProps = Pagination & { 13 | onPageClick: PaginationCallback; 14 | }; 15 | 16 | export type PaginationButtonProps = { 17 | content: React.ReactNode; 18 | show: boolean; 19 | position: 'left' | 'right'; 20 | onPageClick: () => void; 21 | }; 22 | 23 | export type PaginationButtonGroupProps = { 24 | pages: number[]; 25 | current: number; 26 | maxPage: number; 27 | onPageClick: PaginationCallback; 28 | }; 29 | 30 | export type PaginationNumberButtonProps = { 31 | page: number | string; 32 | active: boolean; 33 | onPageClick?: () => void; 34 | }; 35 | 36 | export type PaginationGroupItem = { 37 | content: number | string; 38 | show: boolean; 39 | page: number; 40 | }; 41 | -------------------------------------------------------------------------------- /src/components/Quote/QuoteSlider.tsx: -------------------------------------------------------------------------------- 1 | import 'swiper/css'; 2 | import 'swiper/css/navigation'; 3 | 4 | import { ArrowLeft, ArrowRight } from 'react-feather'; 5 | import { Navigation } from 'swiper'; 6 | import { Swiper, SwiperSlide } from 'swiper/react'; 7 | 8 | import { Avatar } from '@/components'; 9 | import Quotes from '@/data/quotes'; 10 | import type { Quote } from '@/types'; 11 | 12 | const QuoteSlider = () => { 13 | return ( 14 | <> 15 | 20 | {Quotes.map(quote => ( 21 | 22 | 23 | 24 | ))} 25 | 26 | 27 | 28 | ); 29 | }; 30 | 31 | const SliderNavigations = () => ( 32 |
33 | 36 | 39 |
40 | ); 41 | 42 | const QuoteItem = ({ quote }: { quote: Quote }) => ( 43 |
44 |

{quote.content}

45 | 46 |

{quote.author.name}

47 |

{quote.author.title}

48 |
49 | ); 50 | 51 | QuoteSlider.Item = QuoteItem; 52 | QuoteSlider.Navigations = SliderNavigations; 53 | 54 | export default QuoteSlider; 55 | -------------------------------------------------------------------------------- /src/components/TeamCard.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import Link from 'next/link'; 3 | import { ReactNode } from 'react'; 4 | import { GitHub, Linkedin, Twitter } from 'react-feather'; 5 | 6 | export const TeamCard = ({ children }: { children: ReactNode }) => { 7 | return
{children}
; 8 | }; 9 | 10 | const TeamCardImage = ({ src, alt }: { src: string; alt: string }) => { 11 | return ( 12 |
13 | {alt} 20 |
21 | ); 22 | }; 23 | 24 | const TeamCardTitle = ({ children }: { children: ReactNode }) => { 25 | return ( 26 |

27 | {children} 28 |

29 | ); 30 | }; 31 | const TeamCardSubTitle = ({ children }: { children: ReactNode }) => { 32 | return ( 33 |

34 | {children} 35 |

36 | ); 37 | }; 38 | const TeamCardDescription = ({ children }: { children: ReactNode }) => { 39 | return ( 40 |

41 | {children} 42 |

43 | ); 44 | }; 45 | 46 | interface ISocialLinks { 47 | socialLinks: { 48 | twitter: string; 49 | linkedin: string; 50 | github: string; 51 | }; 52 | } 53 | 54 | const TeamCardSocial = ({ socialLinks }: ISocialLinks) => { 55 | return ( 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | ); 68 | }; 69 | 70 | TeamCard.Image = TeamCardImage; 71 | TeamCard.Title = TeamCardTitle; 72 | TeamCard.SubTitle = TeamCardSubTitle; 73 | TeamCard.Description = TeamCardDescription; 74 | TeamCard.Social = TeamCardSocial; 75 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { TeamCard } from './TeamCard'; 2 | export { AuthorCard } from './AuthorCard'; 3 | export { BlogCard } from './BlogCard'; 4 | export { IconButton } from './IconButton'; 5 | 6 | export { Hero } from './Hero'; 7 | export { Avatar } from './Avatar'; 8 | export { Container } from './Container'; 9 | export { Footer } from './Footer'; 10 | export { Header } from './Header'; 11 | export { Button } from './Button'; 12 | export { Input } from './Input'; 13 | export { Badge } from './Badge'; 14 | -------------------------------------------------------------------------------- /src/components/sections/FaqSection.tsx: -------------------------------------------------------------------------------- 1 | import { Disclosure } from '@headlessui/react'; 2 | import React from 'react'; 3 | import { MinusCircle, PlusCircle } from 'react-feather'; 4 | 5 | import { Container, Hero } from '@/components'; 6 | import { FaqList } from '@/data/faq'; 7 | 8 | export const FaqSection: React.FC = () => { 9 | return ( 10 | 11 | 12 | Frequently asked questions 13 | 14 | Everything you need to know about the product and billing. 15 | 16 |
17 | {FaqList.map(({ id, title, description }) => ( 18 | 19 | {({ open }) => ( 20 | <> 21 | 22 | 23 | {title} 24 | 25 | {open ? ( 26 | 27 | ) : ( 28 | 29 | )} 30 | 31 | 32 |

33 | {description} 34 |

35 |
36 | 37 | )} 38 |
39 | ))} 40 |
41 |
42 |
43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/sections/FeaturesSection.tsx: -------------------------------------------------------------------------------- 1 | import { Container, Hero } from '@/components'; 2 | import Features from '@/data/features'; 3 | 4 | export const FeaturesSection = () => { 5 | return ( 6 |
7 | 8 | 9 | Features 10 | Frontendship’te ne yapıyoruz? 11 | 12 | Her seviyeden geliştiricinin potansiyelini artırmak adına topluluğa 13 | ve ekosisteme faydalı projeler ve içerikler sunmayı hedefliyoruz. 14 | 15 |
    16 | {Features.map(({ id, icon: FeatureIcon, title, description }) => ( 17 |
  • 18 |
    19 | 20 |
    21 | 22 | {title} 23 | 24 |

    {description}

    25 |
  • 26 | ))} 27 |
28 |
29 |
30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /src/components/sections/MetricsSection.tsx: -------------------------------------------------------------------------------- 1 | import { Container, Hero } from '@/components'; 2 | 3 | export const MetricsSection = () => { 4 | return ( 5 | 6 | 7 | Verilerle Topluluk 8 | Topluluğumuzu verilerle tanıyın, 9 |
    10 |
  • 11 | 12 | 25 13 | 14 |

    15 | Açık Kaynak Projelere Katkıda Bulunanlar 16 |

    17 |
  • 18 |
  • 19 | 20 | ~500 21 | 22 |

    23 | Günlük Aktif Üye 24 |

    25 |
  • 26 |
  • 27 | 28 | 1k+ 29 | 30 |

    Topluluk Üyesi

    31 |
  • 32 |
33 |
34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/components/sections/QuotesSection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import QuoteSlider from '../Quote/QuoteSlider'; 4 | 5 | export const QuotesSection = () => { 6 | return ( 7 |
8 |
9 | 10 |
11 |
12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/components/sections/TechnologiesSection.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Container, Hero } from '@/components'; 2 | import Technologies from '@/data/technologies'; 3 | 4 | export const TechnologiesSection = () => { 5 | return ( 6 |
7 | 8 | 9 | Teknoloji ve Araçlar 10 | 11 | Toplulukta, geliştirme sürecinin her aşamasına dokunan teknoloji ve 12 | araçlar tartışılıp, deneyimler paylaşılmaktadır. 13 | 14 | 15 |
    16 | {Technologies.map(({ id, icon: TechnologyIcon, name }) => ( 17 |
  • 18 | 19 |
  • 20 | ))} 21 |
22 | 23 | 24 |
25 |
26 |
27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /src/content/blog/oguz-blog-test.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create Menu with Headless UI 3 | description: Learn how to build menu component with Vue.js and Headless UI 4 | author: Oguz Ergul 5 | cover: https://i.ytimg.com/vi/HIJDZxwcPUw/maxresdefault.jpg 6 | avatar: https://pbs.twimg.com/profile_images/1524303338304679937/yd_58miQ_400x400.jpg 7 | date: '22 Aug 2022' 8 | category: Engineering 9 | tags: 10 | - Vue.js 11 | - Headless UI 12 | - Tailwind CSS 13 | content: 14 | - name: Introduction 15 | slug: introduction 16 | - name: Software and tools 17 | slug: software-and-tools 18 | - name: Other resources 19 | slug: other-resources 20 | - name: Conclusion 21 | slug: conclusion 22 | --- 23 | 24 | 25 | ## Introduction 26 | 27 | Mi tincidunt elit, id quisque ligula ac diam, amet. Vel etiam suspendisse morbi eleifend faucibus eget vestibulum felis. Dictum quis montes, sit sit. Tellus aliquam enim urna, etiam. Mauris posuere vulputate arcu amet, vitae nisi, tellus tincidunt. At feugiat sapien varius id. 28 | 29 | Eget quis mi enim, leo lacinia pharetra, semper. Eget in volutpat mollis at volutpat lectus velit, sed auctor. Porttitor fames arcu quis fusce augue enim. Quis at habitant diam at. Suscipit tristique risus, at donec. In turpis vel et quam imperdiet. Ipsum molestie aliquet sodales id est ac volutpat. 30 | 31 | Dolor enim eu tortor urna sed duis nulla. Aliquam vestibulum, nulla odio nisl vitae. In aliquet pellentesque aenean hac vestibulum turpis mi bibendum diam. Tempor integer aliquam in vitae malesuada fringilla. 32 | 33 | Elit nisi in eleifend sed nisi. Pulvinar at orci, proin imperdiet commodo consectetur convallis risus. Sed condimentum enim dignissim adipiscing faucibus consequat, urna. Viverra purus et erat auctor aliquam. Risus, volutpat vulputate posuere purus sit congue convallis aliquet. Arcu id augue ut feugiat donec porttitor neque. Mauris, neque ultricies eu vestibulum, bibendum quam lorem id. Dolor lacus, eget nunc lectus in tellus, pharetra, porttitor. 34 | 35 | Ipsum sit mattis nulla quam nulla. Gravida id gravida ac enim mauris id. Non pellentesque congue eget consectetur turpis. Sapien, dictum molestie sem tempor. Diam elit, orci, tincidunt aenean tempus. Quis velit eget ut tortor tellus. Sed vel, congue felis elit erat nam nibh orci. 36 | 37 | 38 | ```jsx {3,5} showLineNumbers 39 | import React from 'react' 40 | 41 | const MyComponent = () => { 42 | return ( 43 |
44 |

My Component

45 |

My component is awesome

46 |
47 | ) 48 | } 49 | ``` 50 | 51 | > In a world older and more complete than ours they move finished and complete, gifted with extensions of the senses we have lost or never attained, living by voices we shall never hear. 52 | 53 | — Olivia Rhye, Product Designer 54 | 55 | ## Software and tools 56 | 57 | Pharetra morbi libero id aliquam elit massa integer tellus. Quis felis aliquam ullamcorper porttitor. Pulvinar ullamcorper sit dictumst ut eget a, elementum eu. Maecenas est morbi mattis id in ac pellentesque ac. 58 | 59 | ## Other resources 60 | 61 | Sagittis et eu at elementum, quis in. Proin praesent volutpat egestas sociis sit lorem nunc nunc sit. Eget diam curabitur mi ac. Auctor rutrum lacus malesuada massa ornare et. Vulputate consectetur ac ultrices at diam dui eget fringilla tincidunt. Arcu sit dignissim massa erat cursus vulputate gravida id. Sed quis auctor vulputate hac elementum gravida cursus dis. 62 | 63 | 64 | 1. Lectus id duis vitae porttitor enim [gravida morbi](https://duckduckgo.com). 65 | 2. Eu turpis posuere semper feugiat volutpat elit, ultrices suspendisse. Auctor vel in vitae placerat. 66 | 3. Suspendisse maecenas ac donec scelerisque diam sed est duis purus. 67 | 68 | -------------------------------------------------------------------------------- /src/data/faq.ts: -------------------------------------------------------------------------------- 1 | import type { Faq } from 'types'; 2 | 3 | export const FaqList: Faq[] = [ 4 | { 5 | id: 1, 6 | title: 'Is there a free trial available?', 7 | description: 8 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.' 9 | }, 10 | { 11 | id: 2, 12 | title: 'Can I change my plan later?', 13 | description: 14 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.' 15 | }, 16 | { 17 | id: 3, 18 | title: 'What is your cancellation policy?', 19 | description: 20 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.' 21 | }, 22 | { 23 | id: 4, 24 | title: 'Can other info be added to an invoice?', 25 | description: 26 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.' 27 | }, 28 | { 29 | id: 5, 30 | title: 'How does billing work?', 31 | description: 32 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.' 33 | }, 34 | { 35 | id: 6, 36 | title: 'How do I change my account email?', 37 | description: 38 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.' 39 | } 40 | ]; 41 | -------------------------------------------------------------------------------- /src/data/features.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BarChart2, 3 | Command, 4 | Mail, 5 | MessageCircle, 6 | Smile, 7 | Zap 8 | } from 'react-feather'; 9 | import type { Feature } from 'types'; 10 | 11 | const Features: Feature[] = [ 12 | { 13 | id: 1, 14 | title: 'Soru Cevap', 15 | description: 16 | 'Whether you have a team of 2 or 200, our shared team inboxes keep everyone on the same page and in the loop.', 17 | icon: Mail 18 | }, 19 | { 20 | id: 2, 21 | title: 'Gönüllülük', 22 | description: 23 | 'An all-in-one customer service platform that helps you balance everything your customers need to be happy.', 24 | icon: Zap 25 | }, 26 | { 27 | id: 3, 28 | title: 'Açık Kaynak', 29 | description: 30 | 'An all-in-one customer service platform that helps you balance everything your customers need to be happy.', 31 | icon: BarChart2 32 | }, 33 | { 34 | id: 4, 35 | title: 'Etkileşim', 36 | description: 37 | 'Measure what matters with Untitled’s easy-to-use reports. You can filter, export, and drilldown on the data in a couple clicks.', 38 | icon: Smile 39 | }, 40 | { 41 | id: 5, 42 | title: 'Deneyim', 43 | description: 44 | 'Solve a problem or close a sale in real-time with chat. If no one is available, customers are seamlessly routed to email without confusion.', 45 | icon: Command 46 | }, 47 | { 48 | id: 6, 49 | title: 'Feedback', 50 | description: 51 | 'Explore 100+ integrations that make your day-to-day workflow more efficient and familiar. Plus, our extensive developer tools.', 52 | icon: MessageCircle 53 | } 54 | ]; 55 | 56 | export default Features; 57 | -------------------------------------------------------------------------------- /src/data/navigation.ts: -------------------------------------------------------------------------------- 1 | import { FooterLink, Link } from 'types'; 2 | 3 | export const FooterRoutes: FooterLink[] = [ 4 | { 5 | id: 1, 6 | section: 'Product', 7 | routes: [ 8 | { 9 | id: 1, 10 | name: 'Product', 11 | href: '#' 12 | }, 13 | { 14 | id: 2, 15 | name: 'Overview', 16 | href: '#' 17 | }, 18 | { 19 | id: 3, 20 | name: 'Features', 21 | href: '#' 22 | }, 23 | { 24 | id: 4, 25 | name: 'Solutions', 26 | href: '#' 27 | }, 28 | { 29 | id: 5, 30 | name: 'Tutorials', 31 | href: '#' 32 | }, 33 | { 34 | id: 6, 35 | name: 'Pricing', 36 | href: '#' 37 | }, 38 | { 39 | id: 7, 40 | name: 'Releases', 41 | href: '#' 42 | } 43 | ] 44 | }, 45 | { 46 | id: 2, 47 | section: 'Company', 48 | routes: [ 49 | { 50 | id: 1, 51 | name: 'About us', 52 | href: '#' 53 | }, 54 | { 55 | id: 2, 56 | name: 'Careers', 57 | href: '#' 58 | }, 59 | { 60 | id: 3, 61 | name: 'Press', 62 | href: '#' 63 | }, 64 | { 65 | id: 4, 66 | name: 'News', 67 | href: '#' 68 | }, 69 | { 70 | id: 5, 71 | name: 'Media Kit', 72 | href: '#' 73 | }, 74 | { 75 | id: 6, 76 | name: 'Contact', 77 | href: '#' 78 | } 79 | ] 80 | }, 81 | { 82 | id: 3, 83 | section: 'Resources', 84 | routes: [ 85 | { 86 | id: 1, 87 | name: 'Blog', 88 | href: '#' 89 | }, 90 | { 91 | id: 2, 92 | name: 'Newsletter', 93 | href: '#' 94 | }, 95 | { 96 | id: 3, 97 | name: 'Events', 98 | href: '#' 99 | }, 100 | { 101 | id: 4, 102 | name: 'Help Center', 103 | href: '#' 104 | }, 105 | { 106 | id: 5, 107 | name: 'Tutorials', 108 | href: '#' 109 | }, 110 | { 111 | id: 6, 112 | name: 'Support', 113 | href: '#' 114 | } 115 | ] 116 | } 117 | ]; 118 | 119 | export const HeaderLinks: Link[] = [ 120 | { 121 | id: 1, 122 | name: 'FRONTENDSHIP', 123 | accessibleName: 'Anasayfa', 124 | href: '#' 125 | }, 126 | { 127 | id: 2, 128 | name: 'Anasayfa', 129 | href: '/' 130 | }, 131 | { 132 | id: 3, 133 | name: 'Hakkımızda', 134 | href: '/about' 135 | }, 136 | { 137 | id: 4, 138 | name: 'Blog', 139 | href: '/blog' 140 | } 141 | ]; 142 | -------------------------------------------------------------------------------- /src/data/quotes.ts: -------------------------------------------------------------------------------- 1 | import type { Quote } from 'types'; 2 | 3 | const Quotes: Quote[] = [ 4 | { 5 | id: 1, 6 | content: 7 | 'Untitled has saved us thousands of hours of work and has unlock data insights we never thought possible.', 8 | author: { 9 | name: 'Koray Okumuş', 10 | title: 'UX Designer, Circooles', 11 | avatar: '/avatars/Avatar.png' 12 | } 13 | }, 14 | { 15 | id: 2, 16 | content: 17 | 'Untitled has saved us thousands of hours of work and has unlock data insights we never thought possible.', 18 | author: { 19 | name: 'Koray Okumuş', 20 | title: 'UX Designer, Circooles', 21 | avatar: '/avatars/Avatar.png' 22 | } 23 | }, 24 | { 25 | id: 3, 26 | content: 27 | 'Untitled has saved us thousands of hours of work and has unlock data insights we never thought possible.', 28 | author: { 29 | name: 'Koray Okumuş', 30 | title: 'UX Designer, Circooles', 31 | avatar: '/avatars/Avatar.png' 32 | } 33 | } 34 | ]; 35 | 36 | export default Quotes; 37 | -------------------------------------------------------------------------------- /src/data/technologies.ts: -------------------------------------------------------------------------------- 1 | import type { Technology } from 'types'; 2 | 3 | import { 4 | AngularIcon, 5 | DockerIcon, 6 | FigmaIcon, 7 | GithubIcon, 8 | GitIcon, 9 | HTMLIcon, 10 | JavaScriptIcon, 11 | NodeIcon, 12 | NpmIcon, 13 | ReactIcon, 14 | ReduxIcon, 15 | RemixRunIcon, 16 | TypeScriptIcon, 17 | VisualStudioIcon, 18 | VueIcon, 19 | WebpackIcon 20 | } from '@/assets/icons'; 21 | 22 | const Technologies: Technology[] = [ 23 | { 24 | id: 1, 25 | icon: ReactIcon, 26 | name: 'React' 27 | }, 28 | { 29 | id: 2, 30 | icon: VueIcon, 31 | name: 'Vue' 32 | }, 33 | { 34 | id: 3, 35 | icon: JavaScriptIcon, 36 | name: 'JavaScript' 37 | }, 38 | { 39 | id: 4, 40 | icon: GithubIcon, 41 | name: 'Github' 42 | }, 43 | { 44 | id: 5, 45 | icon: DockerIcon, 46 | name: 'Docker' 47 | }, 48 | { 49 | id: 6, 50 | icon: GitIcon, 51 | name: 'Git' 52 | }, 53 | { 54 | id: 7, 55 | icon: RemixRunIcon, 56 | name: 'Remix Run' 57 | }, 58 | { 59 | id: 8, 60 | icon: HTMLIcon, 61 | name: 'HTML' 62 | }, 63 | { 64 | id: 9, 65 | icon: FigmaIcon, 66 | name: 'Figma' 67 | }, 68 | { 69 | id: 10, 70 | icon: AngularIcon, 71 | name: 'Angular' 72 | }, 73 | { 74 | id: 11, 75 | icon: TypeScriptIcon, 76 | name: 'TypeScript' 77 | }, 78 | { 79 | id: 12, 80 | icon: NodeIcon, 81 | name: 'Node' 82 | }, 83 | { 84 | id: 13, 85 | icon: VisualStudioIcon, 86 | name: 'Visual Studio' 87 | }, 88 | { 89 | id: 14, 90 | icon: ReduxIcon, 91 | name: 'Redux' 92 | }, 93 | { 94 | id: 15, 95 | icon: NpmIcon, 96 | name: 'Npm' 97 | }, 98 | { 99 | id: 16, 100 | icon: WebpackIcon, 101 | name: 'Webpack' 102 | } 103 | ]; 104 | 105 | export default Technologies; 106 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | 3 | import { Inter } from '@next/font/google'; 4 | import type { AppProps } from 'next/app'; 5 | 6 | import { Button, Footer, Header, Input } from '@/components'; 7 | 8 | const inter = Inter({ 9 | subsets: ['latin'], 10 | variable: '--font-inter' 11 | }); 12 | 13 | export default function App({ Component, pageProps }: AppProps) { 14 | return ( 15 |
16 |
17 |
18 | 19 |
20 |
21 |
22 |
23 |

24 | Join our newsletter 25 |

26 |

27 | We’ll send you a nice letter once per week. No spam. 28 |

29 |
30 |
31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 |
39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/pages/about.tsx: -------------------------------------------------------------------------------- 1 | import { Container, Hero, TeamCard } from '@/components'; 2 | 3 | const AboutPage = () => { 4 | return ( 5 |
6 | 7 | 8 | The team 9 | Meet the team behind Frontendship 10 | 11 | We’re a small team that loves to create great experiences and make 12 | meaningful connections between builders and customers. Join our 13 | remote ream! 14 | 15 | 16 | 17 | 18 |
19 | 20 | 24 | John Doe 25 | CEO 26 | 27 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem. 28 | 29 | 30 |
31 |
32 | ); 33 | }; 34 | 35 | export default AboutPage; 36 | -------------------------------------------------------------------------------- /src/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiResponse } from 'next'; 3 | 4 | type Data = { 5 | name: string; 6 | }; 7 | 8 | export default function handler(res: NextApiResponse) { 9 | res.status(200).json({ name: 'John Doe' }); 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/blog/[slug].tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import { MDXRemote } from 'next-mdx-remote'; 3 | 4 | import { 5 | GithubLinkIcon, 6 | LinkedinIcon, 7 | LinkIcon, 8 | TwitterIcon 9 | } from '@/assets/icons'; 10 | import { AuthorCard, Badge, Container, Hero, IconButton } from '@/components'; 11 | import { GetAllSlugs, GetNoteBySlug } from '@/utils/blog'; 12 | 13 | export default function BlogDetail({ 14 | source, 15 | meta: { title, category, readTime, date, avatar, author, content } 16 | }: any) { 17 | return ( 18 |
19 | 20 | 21 | {date} 22 | {title} 23 | 24 | {readTime} 25 | {category} 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 |

35 | Table of contents 36 |

37 |
    38 | {content.map( 39 | ({ slug, name }: { slug: string; name: string }) => ( 40 |
  • 44 | {name} 45 |
  • 46 | ) 47 | )} 48 |
49 |
50 | 51 |
52 |

53 | Contributors 54 |

55 | 56 | 62 | 63 | Oğuz Ergül 64 | Software Developer 65 | 66 | 67 |
68 | 69 |
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |
83 |
84 |
85 |
86 |
87 | 88 |
89 |
90 |
91 |
92 | ); 93 | } 94 | export async function getStaticPaths() { 95 | try { 96 | const slugs = GetAllSlugs(); 97 | const paths = slugs.map((slug: any) => ({ params: { slug } })); 98 | 99 | return { 100 | paths, 101 | fallback: false 102 | }; 103 | } catch (error) { 104 | return { notFound: true }; 105 | } 106 | } 107 | 108 | export async function getStaticProps(context: { params: { slug: any } }) { 109 | const { 110 | params: { slug } 111 | } = context; 112 | 113 | const { meta, source } = await GetNoteBySlug(slug); 114 | 115 | return { 116 | props: { 117 | meta, 118 | source 119 | } 120 | }; 121 | } 122 | -------------------------------------------------------------------------------- /src/pages/blog/index.tsx: -------------------------------------------------------------------------------- 1 | import { BlogCard, Container, Hero } from '@/components'; 2 | import { GetAllPostsMeta } from '@/utils/blog'; 3 | 4 | export default function BlogPage({ posts }: { posts: any[] }) { 5 | return ( 6 | 7 | 8 | Our blog 9 | Stories and interviews 10 | 11 | Subscribe to learn about new product features, the latest in 12 | technology, solutions, and updates. 13 | 14 | 15 | 16 |
17 | {posts.map( 18 | ({ meta: { title, description, cover, author, date, slug } }) => ( 19 | 20 | 26 | {title} 27 | {description} 28 | 29 | 30 | ) 31 | )} 32 |
33 |
34 | ); 35 | } 36 | 37 | export async function getStaticProps() { 38 | const posts = GetAllPostsMeta(); 39 | 40 | return { 41 | props: { 42 | posts 43 | } 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { ArrowRight, PlayCircle } from 'react-feather'; 2 | 3 | import { Badge, Button, Container, Hero } from '@/components'; 4 | import { FaqSection } from '@/components/sections/FaqSection'; 5 | import { FeaturesSection } from '@/components/sections/FeaturesSection'; 6 | import { MetricsSection } from '@/components/sections/MetricsSection'; 7 | import { QuotesSection } from '@/components/sections/QuotesSection'; 8 | import { TechnologiesSection } from '@/components/sections/TechnologiesSection'; 9 | 10 | export default function Home() { 11 | return ( 12 | <> 13 |
14 | 15 | 16 | 17 | New feature 18 | Check out the team dashboard 19 | 20 | 21 | 22 | Beautiful analytics to grow smarter 23 | 24 | 25 | Powerful, self-serve product and growth analytics to help you 26 | convert, engage, and retain more users. Trusted by over 4,000 27 | startups. 28 | 29 | 36 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { Icon } from 'react-feather'; 2 | export interface Quote { 3 | id: number; 4 | content: string; 5 | author: { 6 | name: string; 7 | title: string; 8 | avatar: string; 9 | }; 10 | } 11 | 12 | export interface Feature { 13 | id: number; 14 | title: string; 15 | icon: Icon; 16 | description: string; 17 | } 18 | 19 | export interface Technology { 20 | id: number; 21 | name: string; 22 | 23 | icon: Icon; 24 | } 25 | export interface Faq { 26 | id: number; 27 | title: string; 28 | description: string; 29 | } 30 | export interface FooterLink { 31 | id: number; 32 | section?: string; 33 | routes?: { 34 | id: number; 35 | name: string; 36 | href: string; 37 | }[]; 38 | } 39 | 40 | export interface Link { 41 | id: number; 42 | name?: string; 43 | href: string; 44 | accessibleName?: string; 45 | } 46 | -------------------------------------------------------------------------------- /src/utils/blog.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import matter from 'gray-matter'; 3 | import { serialize } from 'next-mdx-remote/serialize'; 4 | import path from 'path'; 5 | import readingTime from 'reading-time'; 6 | import rehypePrettyCode from 'rehype-pretty-code'; 7 | import rehypeSlug from 'rehype-slug'; 8 | 9 | const RootDirectory = path.join(process.cwd(), 'src', 'content', 'blog'); 10 | 11 | const GetAllPosts = () => { 12 | return fs.readdirSync(RootDirectory); 13 | }; 14 | 15 | const GetPostMeta = currentSlug => { 16 | const slug = currentSlug.replace(/\.mdx$/, ''); 17 | const filePath = path.join(RootDirectory, `${slug}.mdx`); 18 | const fileContent = fs.readFileSync(filePath, { encoding: 'utf8' }); 19 | 20 | const { data, content } = matter(fileContent); 21 | const readTime = readingTime(content).text; 22 | 23 | return { 24 | meta: { 25 | ...data, 26 | slug, 27 | readTime 28 | }, 29 | content 30 | }; 31 | }; 32 | 33 | const prettyCodeOptions = { 34 | theme: 'github-light', 35 | onVisitLine(node) { 36 | if (node.children.length === 0) { 37 | node.children = [{ type: 'text', value: ' ' }]; 38 | } 39 | }, 40 | onVisitHighlightedLine(node) { 41 | node.properties.className.push('highlighted'); 42 | }, 43 | onVisitHighlightedWord(node) { 44 | node.properties.className = ['highlighted', 'word']; 45 | } 46 | }; 47 | 48 | export const GetNoteBySlug = async slug => { 49 | const meta = GetPostMeta(slug); 50 | const { content } = meta; 51 | 52 | const mdxSource = await serialize(content, { 53 | mdxOptions: { 54 | rehypePlugins: [rehypeSlug, [rehypePrettyCode, prettyCodeOptions]] 55 | } 56 | }); 57 | 58 | return { 59 | ...meta, 60 | source: mdxSource 61 | }; 62 | }; 63 | 64 | export const GetAllPostsMeta = () => { 65 | return GetAllPosts().map(file => GetPostMeta(file)); 66 | }; 67 | 68 | export const GetAllSlugs = () => { 69 | return GetAllPosts().map(file => file.replace(/\.mdx$/, '')); 70 | }; 71 | -------------------------------------------------------------------------------- /src/utils/pagination.util.ts: -------------------------------------------------------------------------------- 1 | import { PaginationGroupItem } from '@/components/Pagination/Pagination.types'; 2 | 3 | type CalculatePaginationParams = { 4 | current: number; 5 | minPage: number; 6 | maxPage: number; 7 | maxShownPages?: number; 8 | }; 9 | 10 | export const calculatePagination = ( 11 | params: CalculatePaginationParams 12 | ): number[] => { 13 | const pages: number[] = []; 14 | const maxShownPages = params.maxShownPages || 5; 15 | const minPageNumber = Math.max( 16 | params.minPage, 17 | params.current - Math.floor(maxShownPages / 2) 18 | ); 19 | const maxPageNumber = Math.min( 20 | params.maxPage, 21 | params.current + Math.floor(maxShownPages / 2) 22 | ); 23 | const pagesCount = maxPageNumber - minPageNumber + 1; 24 | const pagesToAdd = maxShownPages - pagesCount; 25 | const minPageNumberToAdd = Math.max( 26 | params.minPage, 27 | minPageNumber - pagesToAdd 28 | ); 29 | const maxPageNumberToAdd = Math.min( 30 | params.maxPage, 31 | maxPageNumber + pagesToAdd 32 | ); 33 | for (let i = minPageNumberToAdd; i <= maxPageNumberToAdd; i++) { 34 | pages.push(i); 35 | } 36 | if (pages.length === 0 && params.current > 0) { 37 | pages.push(params.current); 38 | } 39 | return pages; 40 | }; 41 | 42 | type MapPagesToPaginationGroupItemsParams = { 43 | pages: number[]; 44 | centerIndex: number; 45 | totalCount: number; 46 | maxPage: number; 47 | }; 48 | 49 | export const mapPagesToPaginationGroupItems = ({ 50 | pages, 51 | centerIndex, 52 | totalCount, 53 | maxPage 54 | }: MapPagesToPaginationGroupItemsParams) => { 55 | const items: PaginationGroupItem[] = []; 56 | if (centerIndex > 1) { 57 | items.push( 58 | { 59 | content: '1', 60 | page: 1, 61 | show: false 62 | }, 63 | { 64 | content: '...', 65 | page: pages[centerIndex - 2], 66 | show: true 67 | } 68 | ); 69 | } 70 | pages.map((page, index) => { 71 | if (index >= centerIndex - 1 && index <= centerIndex + 1) { 72 | items.push({ 73 | page, 74 | content: page, 75 | show: true 76 | }); 77 | } 78 | }); 79 | if (centerIndex < totalCount - 2) { 80 | items.push( 81 | { 82 | content: '...', 83 | page: pages[centerIndex + 2], 84 | show: true 85 | }, 86 | { 87 | content: maxPage, 88 | page: maxPage, 89 | show: false 90 | } 91 | ); 92 | } 93 | return items; 94 | }; 95 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | // tailwind.config.js 2 | const { fontFamily } = require('tailwindcss/defaultTheme'); 3 | 4 | /** @type {import('tailwindcss').Config} \*/ 5 | module.exports = { 6 | content: [ 7 | './src/pages/**/*.{js,ts,jsx,tsx}', 8 | './src/components/**/*.{js,ts,jsx,tsx}' 9 | ], 10 | theme: { 11 | extend: { 12 | fontFamily: { 13 | sans: ['var(--font-inter)', ...fontFamily.sans] 14 | } 15 | } 16 | }, 17 | plugins: [require('@tailwindcss/typography')] 18 | }; 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "target": "es5", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "paths": { 19 | "@/*": ["./*"] 20 | } 21 | }, 22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "lint-staged.config.js"], 23 | "exclude": ["node_modules"] 24 | } 25 | --------------------------------------------------------------------------------