Repository: itsfaqih/fama
Branch: main
Commit: 73ad6b085888
Files: 95
Total size: 101.6 KB
Directory structure:
gitextract_khp8_3ci/
├── .gitignore
├── .vscode/
│ └── settings.json
├── LICENSE
├── README.md
├── package.json
├── postcss.config.js
├── public/
│ ├── index.html
│ ├── manifest.json
│ └── robots.txt
├── src/
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── assets/
│ │ └── css/
│ │ └── styles.css
│ ├── components/
│ │ ├── animations/
│ │ │ ├── FadeIn.tsx
│ │ │ ├── FromDirection.tsx
│ │ │ ├── Highlighter.tsx
│ │ │ ├── Letter.tsx
│ │ │ ├── Reveal.tsx
│ │ │ ├── StaggerChildren.tsx
│ │ │ └── index.tsx
│ │ ├── atoms/
│ │ │ ├── Avatar/
│ │ │ │ └── index.tsx
│ │ │ ├── Container/
│ │ │ │ └── index.tsx
│ │ │ ├── ContentText/
│ │ │ │ └── index.tsx
│ │ │ ├── ContentTitle/
│ │ │ │ └── index.tsx
│ │ │ ├── FullRoundButton/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── HeaderText/
│ │ │ │ └── index.tsx
│ │ │ ├── HighlightedText/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.tsx
│ │ │ ├── MenuItem/
│ │ │ │ └── index.tsx
│ │ │ ├── Overlay/
│ │ │ │ └── index.tsx
│ │ │ ├── RoundedButton/
│ │ │ │ └── index.tsx
│ │ │ ├── TextBox/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── TextButton/
│ │ │ │ └── index.tsx
│ │ │ ├── TextSelectOption/
│ │ │ │ └── index.tsx
│ │ │ ├── TitleLine/
│ │ │ │ └── index.tsx
│ │ │ └── index.tsx
│ │ ├── decorations/
│ │ │ ├── Bullets.tsx
│ │ │ ├── Stripes.tsx
│ │ │ └── index.tsx
│ │ ├── icons/
│ │ │ └── index.tsx
│ │ ├── molecules/
│ │ │ ├── BlogPost/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── HighlightedTextIcon/
│ │ │ │ └── index.tsx
│ │ │ ├── MainMenu/
│ │ │ │ └── index.tsx
│ │ │ ├── MenuButton/
│ │ │ │ └── index.tsx
│ │ │ ├── ProjectCard/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── SearchBox/
│ │ │ │ └── index.tsx
│ │ │ ├── SectionTitle/
│ │ │ │ └── index.tsx
│ │ │ ├── TextIconButton/
│ │ │ │ └── index.tsx
│ │ │ ├── TextSelect/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.tsx
│ │ │ └── index.tsx
│ │ ├── organisms/
│ │ │ ├── About/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── Blogs/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── Contact/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── Header/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── Navigation/
│ │ │ │ └── index.tsx
│ │ │ ├── Projects/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── Skills/
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ └── index.tsx
│ │ └── templates/
│ │ ├── Landing/
│ │ │ └── index.tsx
│ │ └── index.tsx
│ ├── contexts/
│ │ ├── index.ts
│ │ └── language.ts
│ ├── data/
│ │ ├── blogs.ts
│ │ ├── contact.ts
│ │ ├── header.ts
│ │ ├── index.ts
│ │ ├── projects.ts
│ │ └── skills.tsx
│ ├── index.tsx
│ ├── localization/
│ │ ├── en/
│ │ │ ├── about.tsx
│ │ │ ├── header.ts
│ │ │ ├── index.ts
│ │ │ ├── sections.ts
│ │ │ └── skills.tsx
│ │ └── id/
│ │ ├── about.tsx
│ │ ├── header.ts
│ │ ├── index.ts
│ │ ├── sections.ts
│ │ └── skills.tsx
│ ├── pages/
│ │ └── Home/
│ │ └── index.tsx
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ └── setupTests.ts
├── tailwind.config.js
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/src/assets/css/app.css
================================================
FILE: .vscode/settings.json
================================================
{
"typescript.tsdk": "node_modules\\typescript\\lib"
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Muhammad Faqih Muntashir
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Fama - Portfolio Website Template
TailwindCSS based personal branding template. Built with react and framer-motion.
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).

## Live Demo
I'm currently using this template for my portfolio website, you can check it how it looks live by
yourself at https://itsfaqih.vercel.app/
## Available Scripts
In the project directory, you can run:
### `yarn start`
Runs the app in the development mode. Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits. You will also see any lint errors in the console.
### `yarn watch:style`
Runs the postcss-cli to compile the TailwindCSS styles in watch mode.
### `yarn dev`
Runs both `yarn start` and `yarn watch:style` concurrently.
### `yarn build:style`
Compile the TailwindCSS styles for production.
### `yarn test`
Launches the test runner in the interactive watch mode. See the section about
[running tests](https://create-react-app.dev/docs/running-tests/) for more information.
### `yarn build`
Builds the app for production to the `build` folder. It correctly bundles React in production mode
and optimizes the build for the best performance.
The build is minified and the filenames include the hashes. Your app is ready to be deployed!
See the section about [deployment](https://create-react-app.dev/docs/deployment/) for more
information.
### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time.
This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel,
ESLint, etc) right into your project so you have full control over them. All of the commands except
`eject` will still work, but they will point to the copied scripts so you can tweak them. At this
point you’re on your own.
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle
deployments, and you shouldn’t feel obligated to use this feature. However we understand that this
tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
## Learn More
You can learn more in the
[Create React App documentation](https://create-react-app.dev/docs/getting-started/).
To learn React, check out the [React documentation](https://reactjs.org/).
================================================
FILE: package.json
================================================
{
"name": "personal-branding-template",
"version": "0.1.0",
"private": true,
"dependencies": {
"@headlessui/react": "^0.2.0",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/body-scroll-lock": "^2.6.1",
"@types/jest": "^26.0.15",
"@types/node": "^12.0.0",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"body-scroll-lock": "^3.1.5",
"classnames": "^2.2.6",
"framer-motion": "^2.9.4",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-intersection-observer": "^8.30.3",
"react-scripts": "4.0.0",
"typescript": "^4.0.3",
"web-vitals": "^0.2.4"
},
"scripts": {
"watch:style": "postcss ./src/assets/css/styles.css -o ./src/assets/css/app.css -w",
"build:style": "postcss ./src/assets/css/styles.css -o ./src/assets/css/app.css --env production",
"start": "react-scripts start",
"dev": "concurrently \"yarn watch:style\" \"yarn start\"",
"build": "yarn build:style && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@iconify/icons-logos": "^1.0.12",
"@iconify/react": "^1.1.3",
"@types/classnames": "^2.2.11",
"autoprefixer": "^10.0.2",
"concurrently": "^5.3.0",
"postcss": "^8.1.7",
"postcss-cli": "^8.3.0",
"postcss-import": "^13.0.0",
"tailwindcss": "^2.0.1"
}
}
================================================
FILE: postcss.config.js
================================================
module.exports = {
plugins: [require('postcss-import'), require('tailwindcss'), require('autoprefixer')],
};
================================================
FILE: public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#ffffff" />
<meta name="title" content="Faqih Muntashir">
<meta
name="description"
content="Professional Fullstack Developer"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Faqih Muntashir's Portfolio</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="overflow-x-hidden"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
================================================
FILE: public/manifest.json
================================================
{
"short_name": "itsfaqih Portfolio",
"name": "Faqih Muntashir's Portfolio",
"icons": [],
"start_url": ".",
"display": "standalone",
"theme_color": "#ffffff",
"background_color": "#ffffff"
}
================================================
FILE: public/robots.txt
================================================
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
================================================
FILE: src/App.test.tsx
================================================
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
================================================
FILE: src/App.tsx
================================================
import React from 'react';
import 'assets/css/app.css';
import Home from 'pages/Home';
import { LanguageContext } from 'contexts';
import { useState } from 'react';
function App() {
const [languange, setLanguage] = useState<'en' | 'id'>('en');
const changeLanguage = (lang: 'en' | 'id') => setLanguage(lang);
return (
<LanguageContext.Provider value={{ value: languange, change: changeLanguage }}>
<Home />
</LanguageContext.Provider>
);
}
export default App;
================================================
FILE: src/assets/css/styles.css
================================================
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@layer utilities {
@variants hover, focus-within {
.filter-none {
filter: none;
}
.filter-grayscale {
filter: grayscale(100%);
}
}
}
================================================
FILE: src/components/animations/FadeIn.tsx
================================================
import { HTMLMotionProps, motion, Variants } from 'framer-motion';
import React from 'react';
interface Props extends HTMLMotionProps<'div'> {
delay?: number;
duration?: number;
}
export default function FadeIn({ children, delay = 0, duration = 0.5, animate, ...props }: Props) {
const variants: Variants = {
show: (i: number = 1) => ({
opacity: 1,
transition: {
delay: i * delay,
duration: duration,
},
}),
hidden: {
opacity: 0,
},
};
return (
<motion.div {...props} variants={variants} initial="hidden" animate={animate}>
{children}
</motion.div>
);
}
================================================
FILE: src/components/animations/FromDirection.tsx
================================================
import React, { useState } from 'react';
import { motion, HTMLMotionProps, Variants } from 'framer-motion';
import classNames from 'classnames';
import { useRef } from 'react';
interface Props extends HTMLMotionProps<'div'> {
from:
| 'left'
| 'top'
| 'right'
| 'bottom'
| 'top-left'
| 'top-right'
| 'bottom-left'
| 'bottom-right';
delay?: number;
duration?: number;
innerClassName?: string;
}
export default function FromDirection({
children,
from,
delay = 0,
duration = 0.6,
className,
innerClassName,
onAnimationComplete,
...props
}: Props) {
const [overflow, setOverflow] = useState<string | null>('overflow-hidden');
const variants: Variants = {
show: (i: number = 1) => ({
y: 0,
x: 0,
transition: {
duration: duration,
delay: i * delay,
},
}),
hidden: {
...(from.includes('top') && { y: '-100%' }),
...(from.includes('bottom') && { y: '100%' }),
...(from.includes('left') && { x: '-100%' }),
...(from.includes('right') && { x: '100%' }),
},
};
const ref = useRef<HTMLDivElement>(null);
return (
<div className={classNames(overflow, className)}>
<motion.div
variants={variants}
initial="hidden"
animate="show"
onAnimationComplete={() => {
if (onAnimationComplete) {
onAnimationComplete();
}
setOverflow(null);
if (ref.current !== null) {
ref.current.style.transform = '';
}
}}
className={innerClassName}
ref={ref}
{...props}
>
{children}
</motion.div>
</div>
);
}
================================================
FILE: src/components/animations/Highlighter.tsx
================================================
import React from 'react';
import { motion, Variants, SVGMotionProps } from 'framer-motion';
import classNames from 'classnames';
interface Props extends SVGMotionProps<SVGElement> {
rad: number;
delay?: number;
}
export default function Highlighter({ className, rad, delay = 0, ...props }: Props) {
const variants: Variants = {
show: (i: number = 1) => ({
width: '100%',
transition: {
type: 'tween',
delay: i * delay,
duration: 0.7,
},
}),
hidden: {
width: 0,
},
};
return (
<svg
className={classNames('absolute w-full-2 text-indigo-200 -z-10 -left-1', className)}
fill="currentColor"
>
<motion.rect
height="100%"
rx={rad}
ry={rad}
variants={variants}
initial="hidden"
animate="show"
{...props}
/>
</svg>
);
}
================================================
FILE: src/components/animations/Letter.tsx
================================================
import React from 'react';
import { motion, Variants, HTMLMotionProps } from 'framer-motion';
interface Props extends HTMLMotionProps<'div'> {
text: string;
delay?: number;
duration?: number;
}
export default function Letter({ text, delay = 0, duration = 0.05, ...props }: Props) {
const letters = Array.from(text);
const container: Variants = {
hidden: {
opacity: 0,
},
show: (i: number = 1) => ({
opacity: 1,
transition: { staggerChildren: duration, delayChildren: i * delay },
}),
};
const child: Variants = {
show: {
y: 0,
transition: {
type: 'spring',
damping: 15,
},
},
hidden: {
y: '100%',
},
};
return (
<motion.div
className="flex overflow-hidden"
variants={container}
initial="hidden"
animate="show"
{...props}
>
{letters.map((letter, index) => (
<motion.span key={index} variants={child}>
{letter === ' ' ? '\u00A0' : letter}
</motion.span>
))}
</motion.div>
);
}
================================================
FILE: src/components/animations/Reveal.tsx
================================================
import React, { useState } from 'react';
import { motion, HTMLMotionProps, Variants } from 'framer-motion';
import classNames from 'classnames';
interface Props extends HTMLMotionProps<'div'> {
duration?: number;
delay?: number;
from: 'left' | 'top' | 'right' | 'bottom';
}
export default function Reveal({
children,
duration,
from,
delay = 0.6,
className,
...props
}: Props) {
const [animatedFinish, setAnimatedFinish] = useState(false);
const variants: Variants = {
show: (i: number = 1) => ({
...(from === 'right' && { x: '-100%' }),
...(from === 'left' && { x: '100%' }),
...(from === 'bottom' && { y: '-100%' }),
...(from === 'top' && { y: '100%' }),
transition: {
delay: i * delay,
duration: duration,
},
}),
hidden: {
x: 0,
y: 0,
},
};
return (
<div className={classNames('overflow-hidden relative', className)}>
{children}
<motion.div
className={classNames('absolute top-0 left-0 w-full h-full', {
'bg-indigo-500': !animatedFinish,
})}
variants={variants}
initial="hidden"
animate="show"
{...props}
onAnimationComplete={() => setAnimatedFinish(true)}
/>
</div>
);
}
================================================
FILE: src/components/animations/StaggerChildren.tsx
================================================
import { HTMLMotionProps, motion, Variants } from 'framer-motion';
import React from 'react';
interface Props extends HTMLMotionProps<'div'> {
duration?: number;
delay?: number;
}
export default function StaggerChildren({ children, duration = 0.5, delay = 0, ...props }: Props) {
const variants: Variants = {
hidden: {
opacity: 0,
},
show: (i: number = 1) => ({
opacity: 1,
transition: {
delay: delay,
staggerChildren: duration,
delayChildren: i * delay,
},
}),
};
return (
<motion.div variants={variants} initial="hidden" animate="show" {...props}>
{children}
</motion.div>
);
}
================================================
FILE: src/components/animations/index.tsx
================================================
import Letter from 'components/animations/Letter';
import Highlighter from 'components/animations/Highlighter';
import FromDirection from 'components/animations/FromDirection';
import Reveal from 'components/animations/Reveal';
import FadeIn from 'components/animations/FadeIn';
import StaggerChildren from 'components/animations/StaggerChildren';
const Animated = { Letter, Highlighter, FromDirection, Reveal, FadeIn, StaggerChildren };
export default Animated;
================================================
FILE: src/components/atoms/Avatar/index.tsx
================================================
import React from 'react';
import classNames from 'classnames';
interface Props {
src: string;
alt: string;
className?: string;
}
export default function Avatar({ src, alt, className }: Props) {
return (
<img
src={src}
alt={alt}
width="auto"
height="auto"
className={classNames(
'object-cover border-4 border-indigo-400 rounded-3xl w-28 h-28 md:w-60 md:h-60 shadow-xl',
className
)}
/>
);
}
================================================
FILE: src/components/atoms/Container/index.tsx
================================================
import React from 'react';
import classNames from 'classnames';
interface Props {
children?: React.ReactNode;
className?: string;
fluid?: boolean;
}
export default function Container({ children, className, fluid }: Props) {
return (
<div
className={classNames(
{
'max-w-md px-6 mx-auto sm:px-0 sm:max-w-lg md:max-w-2xl lg:max-w-4xl xl:max-w-5xl': !fluid,
'px-3 sm:px-6': fluid,
},
className
)}
>
{children}
</div>
);
}
================================================
FILE: src/components/atoms/ContentText/index.tsx
================================================
import React, { ReactNode } from 'react';
import classNames from 'classnames';
interface Props {
children: ReactNode;
className?: string;
}
export default function ContentText({ children, className }: Props) {
return (
<p className={classNames('text-center text-gray-700 leading-extra-loose', className)}>
{children}
</p>
);
}
================================================
FILE: src/components/atoms/ContentTitle/index.tsx
================================================
import React, { ReactNode } from 'react';
import classNames from 'classnames';
interface Props {
children: ReactNode;
className?: string;
}
export default function ContentTitle({ children, className }: Props) {
return <h3 className={classNames('text-lg font-bold text-gray-800', className)}>{children}</h3>;
}
================================================
FILE: src/components/atoms/FullRoundButton/index.tsx
================================================
import React from 'react';
import classNames from 'classnames';
import { motion } from 'framer-motion';
import FullRoundButtonProps from 'components/atoms/FullRoundButton/types';
import { useState } from 'react';
import { useEffect } from 'react';
export default function FullRoundButton({
children,
label,
as = 'button',
href = '',
className,
animationComplete,
active = false,
onClick
}: FullRoundButtonProps) {
const [animationFinish, setAnimationFinish] = useState(false);
const classes = classNames(
'flex items-center justify-center w-12 h-12 rounded-full focus:outline-none focus:ring-2 ring-indigo-500 ring-offset-2',
className,
{
'shadow-lg': animationComplete,
'scale-75': animationComplete === false,
'transition-all transform duration-500': animationComplete != null && !animationFinish,
'bg-indigo-500 hover:bg-indigo-600 text-white': !active,
'bg-white text-indigo-600': active
}
);
useEffect(() => {
if (animationComplete) {
setTimeout(() => {
setAnimationFinish(true);
}, 500);
}
}, [animationComplete]);
if (as === 'button') {
return (
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.8 }} className={classes} onClick={onClick}>
{children}
<span className="sr-only">{label}</span>
</motion.button>
);
}
return (
<motion.a href={href} whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} className={classes} onClick={onClick}>
{children}
<span className="sr-only">{label}</span>
</motion.a>
);
}
================================================
FILE: src/components/atoms/FullRoundButton/types.ts
================================================
import { ReactNode } from 'react';
export default interface FullRoundButtonProps {
children?: ReactNode;
label?: string;
as?: 'button' | 'a';
href?: string;
className?: string;
animationComplete?: boolean;
active?: boolean;
onClick?: (params: any) => any;
}
================================================
FILE: src/components/atoms/HeaderText/index.tsx
================================================
import React, { ReactNode } from 'react';
import classNames from 'classnames';
interface Props {
children: ReactNode;
className?: string;
tag?: 'div' | 'h2';
}
export default function HeaderText({ children, className, tag = 'div' }: Props) {
if (tag === 'div') {
return (
<div className={classNames('text-3xl font-bold leading-normal text-gray-800', className)}>
{children}
</div>
);
}
return (
<h2 className={classNames('text-3xl font-bold leading-normal text-gray-800', className)}>
{children}
</h2>
);
}
================================================
FILE: src/components/atoms/HighlightedText/index.tsx
================================================
import React from 'react';
import classNames from 'classnames';
import HighlightedTextProps from 'components/atoms/HighlightedText/types';
export default function HighlightedText({
children,
as = 'span',
href,
className,
}: HighlightedTextProps) {
const classes = classNames('px-3 bg-white rounded-full shadow-md focus:outline-none focus:ring-2 ring-indigo-500', className);
if (as === 'span') {
return (
<span className={classes}>
{children}
</span>
);
}
return (
<a href={href} className={classes}>
{children}
</a>
);
}
================================================
FILE: src/components/atoms/HighlightedText/types.tsx
================================================
import { ReactNode } from "react";
export default interface HighlightedTextProps {
children: ReactNode;
as?: 'a' | 'span';
href?: string;
className?: string;
}
================================================
FILE: src/components/atoms/MenuItem/index.tsx
================================================
import React from 'react';
import { ReactNode } from 'react';
import classNames from 'classnames';
interface Props {
children: ReactNode;
active: boolean;
}
export default function MenuItem({ children, active }: Props) {
return (
<span className="relative">
{children}
<svg
className={classNames(
'-left-1 bottom-0.5 -z-10 absolute w-0 h-3.5 text-indigo-800 transition-all',
{ 'w-full-2': active }
)}
fill="currentColor"
>
<rect width="100%" height="100%" rx={6} ry={6}></rect>
</svg>
</span>
);
}
================================================
FILE: src/components/atoms/Overlay/index.tsx
================================================
import React from 'react';
import classNames from 'classnames';
import { disableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock';
import { RefObject } from 'react';
interface Props {
contentRef: RefObject<HTMLElement | Element>;
classOnOpen: string;
classOnClose: string;
open?: boolean;
animationFinish?: boolean;
useAnimationFinish?: boolean;
className?: string;
}
export default function Overlay({
contentRef,
classOnOpen,
classOnClose,
open = false,
animationFinish = false,
className,
}: Props) {
if (open) {
if (contentRef.current) disableBodyScroll(contentRef.current as HTMLElement);
} else clearAllBodyScrollLocks();
return (
<svg
className={classNames(
'h-full top-0 right-0 transition-all duration-1000 z-10',
{
[classOnClose]: !open,
[classOnOpen]: open,
fixed: animationFinish,
hidden: !animationFinish,
},
className
)}
fill="currentColor"
>
<rect width="100%" height="100%" />
</svg>
);
}
================================================
FILE: src/components/atoms/RoundedButton/index.tsx
================================================
import React, { ReactNode } from 'react';
import classNames from 'classnames';
import { motion } from 'framer-motion';
interface Props {
children: ReactNode;
as?: 'button' | 'a';
href?: string;
className?: string;
}
export default function RoundedButton({ children, as = 'button', href = '', className }: Props) {
const classes = classNames(
'flex items-center justify-center h-12 px-12 shadow-lg hover:shadow-none transition-shadow text-sm text-white hover:bg-indigo-600 bg-indigo-500 font-bold rounded-full transform focus:outline-none focus:ring-2 ring-indigo-500 ring-offset-2',
className
);
if (as === 'button') {
return (
<motion.button whileHover={{ translateY: 10 }} whileTap={{ scale: 0.8 }} className={classes}>
{children}
</motion.button>
);
}
return (
<motion.a
href={href}
whileHover={{ translateY: 10 }}
whileTap={{ scale: 0.8 }}
className={classes}
>
{children}
</motion.a>
);
}
================================================
FILE: src/components/atoms/TextBox/index.tsx
================================================
import React from 'react';
import classNames from 'classnames';
import TextBoxProps from 'components/atoms/TextBox/types';
export default function TextBox({ placeholder, className, value, onChange }: TextBoxProps) {
return (
<input
type="text"
className={classNames('py-2.5 placeholder-gray-500 bg-transparent', className)}
value={value}
placeholder={placeholder}
onChange={onChange}
/>
);
}
================================================
FILE: src/components/atoms/TextBox/types.ts
================================================
export default interface TextBoxProps {
placeholder?: string;
className?: string;
value?: string;
onChange?: (param: any) => any;
}
================================================
FILE: src/components/atoms/TextButton/index.tsx
================================================
import React from 'react';
import classNames from 'classnames';
import { ReactNode } from 'react';
interface Props {
children: ReactNode;
className?: string;
onClick?: (params: any) => any;
}
export default function TextButton({ children, className, onClick }: Props) {
return (
<button
onClick={onClick}
className={classNames(
'inline-flex items-center text-gray-600 focus:outline-none focus:ring-2 ring-indigo-500 rounded-lg ring-offset-2',
className
)}
>
{children}
</button>
);
}
================================================
FILE: src/components/atoms/TextSelectOption/index.tsx
================================================
import React, { ReactNode } from 'react';
import classNames from 'classnames';
import Icons from 'components/icons';
interface Props {
children: ReactNode;
active: boolean;
selected: boolean;
onClick: (any: any) => any;
}
export default function TextSelectOption({ children, active, selected, onClick }: Props) {
return (
<li
className={classNames('py-4 px-8 sm:py-2 sm:px-4 w-60 sm:w-32 flex items-center cursor-pointer hover:bg-indigo-50', {
'bg-indigo-50': active,
})}
onClick={onClick}
>
{children}
{selected && <Icons.CheckRounded className="absolute text-indigo-500 w-7 h-7 sm:w-4 sm:h-4 right-4" />}
</li>
);
}
================================================
FILE: src/components/atoms/TitleLine/index.tsx
================================================
import React from 'react';
import { motion, SVGMotionProps, Variants } from 'framer-motion';
import classNames from 'classnames';
export default function TitleLine({ className, ...props }: SVGMotionProps<SVGElement>) {
const variants: Variants = {
show: {
width: '100%',
},
hidden: {
width: 0,
},
};
return (
<svg className={classNames('w-20 h-1 text-indigo-600 mt-3', className)} fill="currentColor">
<motion.rect variants={variants} initial="hidden" animate="show" height="100%" {...props} />
</svg>
);
}
================================================
FILE: src/components/atoms/index.tsx
================================================
import Avatar from 'components/atoms/Avatar';
import HeaderText from 'components/atoms/HeaderText';
import TextButton from 'components/atoms/TextButton';
import TextSelectOption from 'components/atoms/TextSelectOption';
import FullRoundButton from 'components/atoms/FullRoundButton';
import RoundedButton from 'components/atoms/RoundedButton';
import HighlightedText from 'components/atoms/HighlightedText';
import ContentTitle from 'components/atoms/ContentTitle';
import TextBox from 'components/atoms/TextBox';
import ContentText from 'components/atoms/ContentText';
import Container from 'components/atoms/Container';
import TitleLine from 'components/atoms/TitleLine';
import MenuItem from 'components/atoms/MenuItem';
import Overlay from 'components/atoms/Overlay';
export {
Avatar,
HeaderText,
TextButton,
TextSelectOption,
FullRoundButton,
RoundedButton,
HighlightedText,
ContentTitle,
TextBox,
ContentText,
Container,
TitleLine,
MenuItem,
Overlay
};
================================================
FILE: src/components/decorations/Bullets.tsx
================================================
import React from 'react';
export default function Bullets({ className }: { className?: string }) {
return (
<svg
className={className}
viewBox="0 0 192 192"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M10 5C10 7.76142 7.76142 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5Z" />
<path d="M10 109C10 111.761 7.76142 114 5 114C2.23858 114 0 111.761 0 109C0 106.239 2.23858 104 5 104C7.76142 104 10 106.239 10 109Z" />
<path d="M10 57C10 59.7614 7.76142 62 5 62C2.23858 62 0 59.7614 0 57C0 54.2386 2.23858 52 5 52C7.76142 52 10 54.2386 10 57Z" />
<path d="M10 161C10 163.761 7.76142 166 5 166C2.23858 166 0 163.761 0 161C0 158.239 2.23858 156 5 156C7.76142 156 10 158.239 10 161Z" />
<path d="M10 31C10 33.7614 7.76142 36 5 36C2.23858 36 0 33.7614 0 31C0 28.2386 2.23858 26 5 26C7.76142 26 10 28.2386 10 31Z" />
<path d="M10 135C10 137.761 7.76142 140 5 140C2.23858 140 0 137.761 0 135C0 132.239 2.23858 130 5 130C7.76142 130 10 132.239 10 135Z" />
<path d="M10 83C10 85.7614 7.76142 88 5 88C2.23858 88 0 85.7614 0 83C0 80.2386 2.23858 78 5 78C7.76142 78 10 80.2386 10 83Z" />
<path d="M10 187C10 189.761 7.76142 192 5 192C2.23858 192 0 189.761 0 187C0 184.239 2.23858 182 5 182C7.76142 182 10 184.239 10 187Z" />
<path d="M114 5C114 7.76142 111.761 10 109 10C106.239 10 104 7.76142 104 5C104 2.23858 106.239 0 109 0C111.761 0 114 2.23858 114 5Z" />
<path d="M114 109C114 111.761 111.761 114 109 114C106.239 114 104 111.761 104 109C104 106.239 106.239 104 109 104C111.761 104 114 106.239 114 109Z" />
<path d="M114 57C114 59.7614 111.761 62 109 62C106.239 62 104 59.7614 104 57C104 54.2386 106.239 52 109 52C111.761 52 114 54.2386 114 57Z" />
<path d="M114 161C114 163.761 111.761 166 109 166C106.239 166 104 163.761 104 161C104 158.239 106.239 156 109 156C111.761 156 114 158.239 114 161Z" />
<path d="M114 31C114 33.7614 111.761 36 109 36C106.239 36 104 33.7614 104 31C104 28.2386 106.239 26 109 26C111.761 26 114 28.2386 114 31Z" />
<path d="M114 135C114 137.761 111.761 140 109 140C106.239 140 104 137.761 104 135C104 132.239 106.239 130 109 130C111.761 130 114 132.239 114 135Z" />
<path d="M114 83C114 85.7614 111.761 88 109 88C106.239 88 104 85.7614 104 83C104 80.2386 106.239 78 109 78C111.761 78 114 80.2386 114 83Z" />
<path d="M114 187C114 189.761 111.761 192 109 192C106.239 192 104 189.761 104 187C104 184.239 106.239 182 109 182C111.761 182 114 184.239 114 187Z" />
<path d="M36 5C36 7.76142 33.7614 10 31 10C28.2386 10 26 7.76142 26 5C26 2.23858 28.2386 0 31 0C33.7614 0 36 2.23858 36 5Z" />
<path d="M36 109C36 111.761 33.7614 114 31 114C28.2386 114 26 111.761 26 109C26 106.239 28.2386 104 31 104C33.7614 104 36 106.239 36 109Z" />
<path d="M36 57C36 59.7614 33.7614 62 31 62C28.2386 62 26 59.7614 26 57C26 54.2386 28.2386 52 31 52C33.7614 52 36 54.2386 36 57Z" />
<path d="M36 161C36 163.761 33.7614 166 31 166C28.2386 166 26 163.761 26 161C26 158.239 28.2386 156 31 156C33.7614 156 36 158.239 36 161Z" />
<path d="M36 31C36 33.7614 33.7614 36 31 36C28.2386 36 26 33.7614 26 31C26 28.2386 28.2386 26 31 26C33.7614 26 36 28.2386 36 31Z" />
<path d="M36 135C36 137.761 33.7614 140 31 140C28.2386 140 26 137.761 26 135C26 132.239 28.2386 130 31 130C33.7614 130 36 132.239 36 135Z" />
<path d="M36 83C36 85.7614 33.7614 88 31 88C28.2386 88 26 85.7614 26 83C26 80.2386 28.2386 78 31 78C33.7614 78 36 80.2386 36 83Z" />
<path d="M36 187C36 189.761 33.7614 192 31 192C28.2386 192 26 189.761 26 187C26 184.239 28.2386 182 31 182C33.7614 182 36 184.239 36 187Z" />
<path d="M140 5C140 7.76142 137.761 10 135 10C132.239 10 130 7.76142 130 5C130 2.23858 132.239 0 135 0C137.761 0 140 2.23858 140 5Z" />
<path d="M140 109C140 111.761 137.761 114 135 114C132.239 114 130 111.761 130 109C130 106.239 132.239 104 135 104C137.761 104 140 106.239 140 109Z" />
<path d="M140 57C140 59.7614 137.761 62 135 62C132.239 62 130 59.7614 130 57C130 54.2386 132.239 52 135 52C137.761 52 140 54.2386 140 57Z" />
<path d="M140 161C140 163.761 137.761 166 135 166C132.239 166 130 163.761 130 161C130 158.239 132.239 156 135 156C137.761 156 140 158.239 140 161Z" />
<path d="M140 31C140 33.7614 137.761 36 135 36C132.239 36 130 33.7614 130 31C130 28.2386 132.239 26 135 26C137.761 26 140 28.2386 140 31Z" />
<path d="M140 135C140 137.761 137.761 140 135 140C132.239 140 130 137.761 130 135C130 132.239 132.239 130 135 130C137.761 130 140 132.239 140 135Z" />
<path d="M140 83C140 85.7614 137.761 88 135 88C132.239 88 130 85.7614 130 83C130 80.2386 132.239 78 135 78C137.761 78 140 80.2386 140 83Z" />
<path d="M140 187C140 189.761 137.761 192 135 192C132.239 192 130 189.761 130 187C130 184.239 132.239 182 135 182C137.761 182 140 184.239 140 187Z" />
<path d="M62 5C62 7.76142 59.7614 10 57 10C54.2386 10 52 7.76142 52 5C52 2.23858 54.2386 0 57 0C59.7614 0 62 2.23858 62 5Z" />
<path d="M62 109C62 111.761 59.7614 114 57 114C54.2386 114 52 111.761 52 109C52 106.239 54.2386 104 57 104C59.7614 104 62 106.239 62 109Z" />
<path d="M62 57C62 59.7614 59.7614 62 57 62C54.2386 62 52 59.7614 52 57C52 54.2386 54.2386 52 57 52C59.7614 52 62 54.2386 62 57Z" />
<path d="M62 161C62 163.761 59.7614 166 57 166C54.2386 166 52 163.761 52 161C52 158.239 54.2386 156 57 156C59.7614 156 62 158.239 62 161Z" />
<path d="M62 31C62 33.7614 59.7614 36 57 36C54.2386 36 52 33.7614 52 31C52 28.2386 54.2386 26 57 26C59.7614 26 62 28.2386 62 31Z" />
<path d="M62 135C62 137.761 59.7614 140 57 140C54.2386 140 52 137.761 52 135C52 132.239 54.2386 130 57 130C59.7614 130 62 132.239 62 135Z" />
<path d="M62 83C62 85.7614 59.7614 88 57 88C54.2386 88 52 85.7614 52 83C52 80.2386 54.2386 78 57 78C59.7614 78 62 80.2386 62 83Z" />
<path d="M62 187C62 189.761 59.7614 192 57 192C54.2386 192 52 189.761 52 187C52 184.239 54.2386 182 57 182C59.7614 182 62 184.239 62 187Z" />
<path d="M166 5C166 7.76142 163.761 10 161 10C158.239 10 156 7.76142 156 5C156 2.23858 158.239 0 161 0C163.761 0 166 2.23858 166 5Z" />
<path d="M166 109C166 111.761 163.761 114 161 114C158.239 114 156 111.761 156 109C156 106.239 158.239 104 161 104C163.761 104 166 106.239 166 109Z" />
<path d="M166 57C166 59.7614 163.761 62 161 62C158.239 62 156 59.7614 156 57C156 54.2386 158.239 52 161 52C163.761 52 166 54.2386 166 57Z" />
<path d="M166 161C166 163.761 163.761 166 161 166C158.239 166 156 163.761 156 161C156 158.239 158.239 156 161 156C163.761 156 166 158.239 166 161Z" />
<path d="M166 31C166 33.7614 163.761 36 161 36C158.239 36 156 33.7614 156 31C156 28.2386 158.239 26 161 26C163.761 26 166 28.2386 166 31Z" />
<path d="M166 135C166 137.761 163.761 140 161 140C158.239 140 156 137.761 156 135C156 132.239 158.239 130 161 130C163.761 130 166 132.239 166 135Z" />
<path d="M166 83C166 85.7614 163.761 88 161 88C158.239 88 156 85.7614 156 83C156 80.2386 158.239 78 161 78C163.761 78 166 80.2386 166 83Z" />
<path d="M166 187C166 189.761 163.761 192 161 192C158.239 192 156 189.761 156 187C156 184.239 158.239 182 161 182C163.761 182 166 184.239 166 187Z" />
<path d="M88 5C88 7.76142 85.7614 10 83 10C80.2386 10 78 7.76142 78 5C78 2.23858 80.2386 0 83 0C85.7614 0 88 2.23858 88 5Z" />
<path d="M88 109C88 111.761 85.7614 114 83 114C80.2386 114 78 111.761 78 109C78 106.239 80.2386 104 83 104C85.7614 104 88 106.239 88 109Z" />
<path d="M88 57C88 59.7614 85.7614 62 83 62C80.2386 62 78 59.7614 78 57C78 54.2386 80.2386 52 83 52C85.7614 52 88 54.2386 88 57Z" />
<path d="M88 161C88 163.761 85.7614 166 83 166C80.2386 166 78 163.761 78 161C78 158.239 80.2386 156 83 156C85.7614 156 88 158.239 88 161Z" />
<path d="M88 31C88 33.7614 85.7614 36 83 36C80.2386 36 78 33.7614 78 31C78 28.2386 80.2386 26 83 26C85.7614 26 88 28.2386 88 31Z" />
<path d="M88 135C88 137.761 85.7614 140 83 140C80.2386 140 78 137.761 78 135C78 132.239 80.2386 130 83 130C85.7614 130 88 132.239 88 135Z" />
<path d="M88 83C88 85.7614 85.7614 88 83 88C80.2386 88 78 85.7614 78 83C78 80.2386 80.2386 78 83 78C85.7614 78 88 80.2386 88 83Z" />
<path d="M88 187C88 189.761 85.7614 192 83 192C80.2386 192 78 189.761 78 187C78 184.239 80.2386 182 83 182C85.7614 182 88 184.239 88 187Z" />
<path d="M192 5C192 7.76142 189.761 10 187 10C184.239 10 182 7.76142 182 5C182 2.23858 184.239 0 187 0C189.761 0 192 2.23858 192 5Z" />
<path d="M192 109C192 111.761 189.761 114 187 114C184.239 114 182 111.761 182 109C182 106.239 184.239 104 187 104C189.761 104 192 106.239 192 109Z" />
<path d="M192 57C192 59.7614 189.761 62 187 62C184.239 62 182 59.7614 182 57C182 54.2386 184.239 52 187 52C189.761 52 192 54.2386 192 57Z" />
<path d="M192 161C192 163.761 189.761 166 187 166C184.239 166 182 163.761 182 161C182 158.239 184.239 156 187 156C189.761 156 192 158.239 192 161Z" />
<path d="M192 31C192 33.7614 189.761 36 187 36C184.239 36 182 33.7614 182 31C182 28.2386 184.239 26 187 26C189.761 26 192 28.2386 192 31Z" />
<path d="M192 135C192 137.761 189.761 140 187 140C184.239 140 182 137.761 182 135C182 132.239 184.239 130 187 130C189.761 130 192 132.239 192 135Z" />
<path d="M192 83C192 85.7614 189.761 88 187 88C184.239 88 182 85.7614 182 83C182 80.2386 184.239 78 187 78C189.761 78 192 80.2386 192 83Z" />
<path d="M192 187C192 189.761 189.761 192 187 192C184.239 192 182 189.761 182 187C182 184.239 184.239 182 187 182C189.761 182 192 184.239 192 187Z" />
</svg>
);
}
================================================
FILE: src/components/decorations/Stripes.tsx
================================================
import React from 'react';
export default function Stripes({ className }: { className?: string }) {
return (
<svg
className={className}
viewBox="0 0 199 197"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0 27.1806V33.5152L33.8184 0H27.4264L0 27.1806ZM53.8795 0L0 53.3965V59.7311L60.2714 0H53.8795ZM78.3504 0L0 77.648V83.9827L84.7423 0H78.3504ZM104.966 0L0 104.025V110.36L111.358 0H104.966ZM130.401 0L0 129.232V135.567L136.793 0H130.401ZM154.445 0L0 153.061V159.395L160.837 0H154.445ZM181.175 0L0 179.551V185.886L187.567 0H181.175ZM198.331 9.47616L9.11099 197H15.5029L198.331 15.8108V9.47616ZM198.331 31.8572L31.6945 197H38.0864L198.331 38.1919V31.8572Z"
/>
</svg>
);
}
================================================
FILE: src/components/decorations/index.tsx
================================================
import Bullets from 'components/decorations/Bullets';
import Stripes from 'components/decorations/Stripes';
const Decorations = {
Bullets,
Stripes
};
export default Decorations;
================================================
FILE: src/components/icons/index.tsx
================================================
import React from 'react';
const Dribbble = ({ className }: { className?: string }) => (
<svg className={className} viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M50 0C22.397 0 0 22.397 0 50C0 77.603 22.397 100 50 100C77.5488 100 100 77.603 100 50C100 22.397 77.5488 0 50 0ZM83.026 23.0477C88.9913 30.3145 92.5705 39.5879 92.679 49.6204C91.269 49.3492 77.1692 46.4751 62.961 48.2646C62.6356 47.5597 62.3644 46.8004 62.0391 46.0412C61.1714 43.9805 60.1952 41.8655 59.2191 39.859C74.9458 33.4599 82.1041 24.2408 83.026 23.0477ZM50 7.37527C60.846 7.37527 70.7701 11.4425 78.308 18.1128C77.5488 19.1974 71.0954 27.82 55.9111 33.5141C48.9154 20.6616 41.1605 10.141 39.9675 8.5141C43.167 7.75488 46.5293 7.37527 50 7.37527ZM31.833 11.3883C32.9718 12.9067 40.564 23.4816 47.6681 36.0629C27.7115 41.3774 10.0868 41.269 8.18872 41.269C10.9544 28.0369 19.9024 17.0282 31.833 11.3883ZM7.26681 50.0542C7.26681 49.6204 7.26681 49.1866 7.26681 48.7527C9.11063 48.8069 29.8265 49.0781 51.1388 42.679C52.3861 45.0651 53.5249 47.5054 54.6095 49.9458C54.0672 50.1085 53.4707 50.2712 52.9284 50.4338C30.9111 57.538 19.1974 76.9523 18.2213 78.5792C11.4425 71.0412 7.26681 61.0087 7.26681 50.0542ZM50 92.7332C40.1302 92.7332 31.0195 89.3709 23.8069 83.731C24.5662 82.1584 33.243 65.4555 57.321 57.0499C57.4295 56.9957 57.4837 56.9957 57.5922 56.9414C63.6117 72.5054 66.0521 85.5748 66.7028 89.3167C61.551 91.5401 55.9111 92.7332 50 92.7332ZM73.807 85.4122C73.3731 82.8091 71.0955 70.3362 65.5098 54.9892C78.9046 52.8742 90.6182 56.3449 92.0824 56.833C90.2386 68.7093 83.4056 78.9588 73.807 85.4122Z"
fill="#EA4C89"
/>
</svg>
);
const Github = ({ className }: { className?: string }) => (
<svg className={className} viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M20 0C8.95 0 0 8.95 0 20C0 28.85 5.725 36.325 13.675 38.975C14.675 39.15 15.05 38.55 15.05 38.025C15.05 37.55 15.025 35.975 15.025 34.3C10 35.225 8.7 33.075 8.3 31.95C8.075 31.375 7.1 29.6 6.25 29.125C5.55 28.75 4.55 27.825 6.225 27.8C7.8 27.775 8.925 29.25 9.3 29.85C11.1 32.875 13.975 32.025 15.125 31.5C15.3 30.2 15.825 29.325 16.4 28.825C11.95 28.325 7.3 26.6 7.3 18.95C7.3 16.775 8.075 14.975 9.35 13.575C9.15 13.075 8.45 11.025 9.55 8.275C9.55 8.275 11.225 7.75 15.05 10.325C16.65 9.875 18.35 9.65 20.05 9.65C21.75 9.65 23.45 9.875 25.05 10.325C28.875 7.725 30.55 8.275 30.55 8.275C31.65 11.025 30.95 13.075 30.75 13.575C32.025 14.975 32.8 16.75 32.8 18.95C32.8 26.625 28.125 28.325 23.675 28.825C24.4 29.45 25.025 30.65 25.025 32.525C25.025 35.2 25 37.35 25 38.025C25 38.55 25.375 39.175 26.375 38.975C34.275 36.325 40 28.825 40 20C40 8.95 31.05 0 20 0Z"
fill="#1B1F23"
/>
</svg>
);
const Facebook = ({ className }: { className?: string }) => (
<svg className={className} viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M30.0001 3.33325H25.0001C22.7899 3.33325 20.6703 4.21123 19.1075 5.77403C17.5447 7.33683 16.6667 9.45645 16.6667 11.6666V16.6666H11.6667V23.3333H16.6667V36.6666H23.3334V23.3333H28.3334L30.0001 16.6666H23.3334V11.6666C23.3334 11.2246 23.509 10.8006 23.8216 10.4881C24.1341 10.1755 24.5581 9.99992 25.0001 9.99992H30.0001V3.33325Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
const Twitter = ({ className }: { className?: string }) => (
<svg className={className} viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M38.3333 4.99991C36.7373 6.12571 34.9702 6.98676 33.1 7.54991C32.0962 6.39577 30.7622 5.57773 29.2784 5.20646C27.7945 4.83518 26.2325 4.92857 24.8035 5.474C23.3744 6.01943 22.1474 6.99058 21.2883 8.25611C20.4292 9.52163 19.9795 11.0205 20 12.5499V14.2166C17.071 14.2925 14.1688 13.6429 11.5517 12.3257C8.93456 11.0084 6.68385 9.06431 4.99999 6.66658C4.99999 6.66658 -1.66668 21.6666 13.3333 28.3332C9.90087 30.6632 5.81192 31.8315 1.66666 31.6666C16.6667 39.9999 35 31.6666 35 12.4999C34.9985 12.0357 34.9538 11.5726 34.8667 11.1166C36.5677 9.43907 37.768 7.3211 38.3333 4.99991V4.99991Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
const Instagram = ({ className }: { className?: string }) => (
<svg className={className} viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M28.3333 3.33325H11.6666C7.06427 3.33325 3.33331 7.06421 3.33331 11.6666V28.3333C3.33331 32.9356 7.06427 36.6666 11.6666 36.6666H28.3333C32.9357 36.6666 36.6666 32.9356 36.6666 28.3333V11.6666C36.6666 7.06421 32.9357 3.33325 28.3333 3.33325Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M26.6668 18.95C26.8725 20.3371 26.6356 21.7537 25.9897 22.9984C25.3439 24.243 24.3221 25.2524 23.0695 25.8828C21.817 26.5132 20.3976 26.7327 19.0131 26.5099C17.6287 26.2871 16.3497 25.6335 15.3582 24.6419C14.3667 23.6504 13.713 22.3715 13.4903 20.987C13.2675 19.6026 13.4869 18.1832 14.1174 16.9306C14.7478 15.6781 15.7571 14.6562 17.0018 14.0104C18.2464 13.3646 19.6631 13.1277 21.0501 13.3333C22.465 13.5432 23.7749 14.2025 24.7863 15.2139C25.7977 16.2253 26.457 17.5351 26.6668 18.95Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M29.1667 10.8333H29.1833"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
const CheckRounded = ({ className }: { className?: string }) => (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
);
const Menu = ({ className }: { className?: string }) => (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16m-7 6h7" />
</svg>
);
const Close = ({ className }: { className?: string }) => (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
);
const Icons = {
Dribbble,
Github,
Facebook,
Twitter,
Instagram,
CheckRounded,
Menu,
Close,
};
export default Icons;
================================================
FILE: src/components/molecules/BlogPost/index.tsx
================================================
import { ContentTitle } from 'components/atoms';
import { LanguageContext } from 'contexts';
import React, { useContext } from 'react';
import BlogsProps from 'components/molecules/BlogPost/types';
import classNames from 'classnames';
export default function BlogPost({ data, className }: BlogsProps) {
const { title, date, url } = data;
const language = useContext(LanguageContext);
const formattedDate = new Intl.DateTimeFormat(language.value === 'en' ? 'en-US' : 'id-ID', {
year: 'numeric',
month: 'long',
day: 'numeric',
}).format(date);
return (
<article className={classNames(className, 'focus-within:ring-2 ring-indigo-500 ring-offset-8 rounded-lg')}>
<a href={url} className="relative inline-block rounded-lg group focus:outline-none">
<ContentTitle>{title}</ContentTitle>
<svg className="-left-1 bottom-0.5 -z-10 absolute w-0 h-2 text-indigo-200 transition-all group-focus:w-full-2 group-hover:w-full-2" fill="currentColor">
<rect width="100%" height="100%" rx={4} ry={4}></rect>
</svg>
</a>
<span className="block mt-3 text-gray-700">{formattedDate}</span>
</article>
);
}
================================================
FILE: src/components/molecules/BlogPost/types.ts
================================================
export default interface BlogsProps {
data: BlogsPostProps;
className?: string;
}
export interface BlogsPostProps {
title: string;
date: Date;
url: string;
}
================================================
FILE: src/components/molecules/HighlightedTextIcon/index.tsx
================================================
import { HighlightedText } from 'components/atoms';
import HighlightedTextProps from 'components/atoms/HighlightedText/types';
import React, { ReactElement } from 'react';
import classNames from 'classnames';
interface Props extends HighlightedTextProps {
icon: ReactElement;
}
export default function HighlightedTextIcon({ icon, children, className, ...props }: Props) {
const Icon = React.cloneElement(icon, { className: 'w-7 h-7 pr-2 self-center' });
return (
<HighlightedText
{...props}
className={classNames(
'inline-flex items-baseline text-gray-800 font-medium transition-all transform hover:-translate-y-1 hover:shadow-xl',
className
)}
>
{Icon}
{children}
</HighlightedText>
);
}
================================================
FILE: src/components/molecules/MainMenu/index.tsx
================================================
import { Menu } from '@headlessui/react';
import { MenuButton } from 'components/molecules';
import React from 'react';
import Animated from 'components/animations';
import { AnimatePresence } from 'framer-motion';
import { useContext } from 'react';
import { LanguageContext } from 'contexts';
import { MenuItem, Overlay } from 'components/atoms';
import { useRef } from 'react';
interface Props {
animationFinish?: boolean;
sections: { about: string; projects: string; blogs: string; skills: string; contact: string };
}
export default function MainMenu({ animationFinish, sections }: Props) {
const language = useContext(LanguageContext);
const menu = useRef<HTMLDivElement>(null);
return (
<div className="self-center ml-auto sm:ml-0">
<Menu>
{({ open }) => (
<>
<Menu.Button as="div" className="relative z-20">
<MenuButton isOpen={open} animationComplete={animationFinish} />
</Menu.Button>
<Overlay contentRef={menu} open={open} animationFinish={animationFinish} classOnOpen="w-full" classOnClose="w-0" className="text-indigo-600"/>
<AnimatePresence initial={false}>
{open && (
<Menu.Items
className="fixed top-0 left-0 z-10 flex items-center justify-center w-full h-full text-4xl font-bold text-white focus:outline-none"
static
>
<div className="grid gap-x-32 gap-y-10" ref={menu}>
<Menu.Item as="a" href="#about">
{({ active }: { active: boolean }) => (
<Animated.FromDirection
from="bottom"
animate="show"
exit={{ opacity: 0 }}
custom={1}
delay={0.2}
>
<MenuItem active={active}>{sections.about}</MenuItem>
</Animated.FromDirection>
)}
</Menu.Item>
<Menu.Item as="a" href="#projects">
{({ active }: { active: boolean }) => (
<Animated.FromDirection
from="bottom"
animate="show"
exit={{ opacity: 0 }}
custom={2}
delay={0.2}
>
<MenuItem active={active}>{sections.projects}</MenuItem>
</Animated.FromDirection>
)}
</Menu.Item>
<Menu.Item as="a" href="#blogs">
{({ active }: { active: boolean }) => (
<Animated.FromDirection
from="bottom"
animate="show"
exit={{ opacity: 0 }}
custom={3}
delay={0.2}
>
<MenuItem active={active}>{sections.blogs}</MenuItem>
</Animated.FromDirection>
)}
</Menu.Item>
<Menu.Item as="a" href="#skills">
{({ active }: { active: boolean }) => (
<Animated.FromDirection
from="bottom"
animate="show"
exit={{ opacity: 0 }}
custom={4}
delay={0.2}
>
<MenuItem active={active}>{sections.skills}</MenuItem>
</Animated.FromDirection>
)}
</Menu.Item>
<Menu.Item as="a" href="#contact">
{({ active }: { active: boolean }) => (
<Animated.FromDirection
from="bottom"
animate="show"
exit={{ opacity: 0 }}
custom={5}
delay={0.2}
>
<MenuItem active={active}>
{language.value === 'en' ? 'Contact' : 'Kontak'}
</MenuItem>
</Animated.FromDirection>
)}
</Menu.Item>
</div>
</Menu.Items>
)}
</AnimatePresence>
</>
)}
</Menu>
</div>
);
}
================================================
FILE: src/components/molecules/MenuButton/index.tsx
================================================
import { FullRoundButton } from 'components/atoms';
import FullRoundButtonProps from 'components/atoms/FullRoundButton/types';
import Icons from 'components/icons';
import React from 'react';
interface Props extends FullRoundButtonProps {
isOpen?: boolean;
}
export default function MenuButton({ isOpen = false, onClick, ...props }: Props) {
return (
<FullRoundButton active={isOpen} {...props} label="Menu" onClick={onClick}>
{isOpen ? <Icons.Close className="w-6 h-6" /> : <Icons.Menu className="w-6 h-6" />}
</FullRoundButton>
);
}
================================================
FILE: src/components/molecules/ProjectCard/index.tsx
================================================
import { ContentTitle } from 'components/atoms';
import Icons from 'components/icons';
import { LanguageContext } from 'contexts';
import React, { useContext } from 'react';
import classNames from 'classnames';
import ProjectCardProps from 'components/molecules/ProjectCard/types';
export default function ProjectCard({ data, className }: ProjectCardProps) {
const language = useContext(LanguageContext);
const { title, description, type, url } = data;
return (
<article
className={classNames('flex flex-col bg-white rounded-xl shadow py-4 px-6', className)}
>
<ContentTitle>{title}</ContentTitle>
<p className="mt-2 mb-8 text-sm text-gray-700">{description}</p>
<div className="flex items-center justify-between mt-auto">
{type === 'dribbble' ? (
<Icons.Dribbble className="w-7 h-7" />
) : (
<Icons.Github className="w-7 h-7" />
)}
<a className="font-medium text-indigo-400 rounded-lg hover:text-indigo-500 focus:outline-none focus:ring-2 ring-indigo-500 ring-offset-2 focus:text-indigo-500" href={url}>
{language.value === 'en' ? 'Details' : 'Lihat'}
</a>
</div>
</article>
);
}
================================================
FILE: src/components/molecules/ProjectCard/types.ts
================================================
export default interface ProjectCardProps {
data: ProjectCardDataProps;
className: string;
}
export interface ProjectCardDataProps {
title: string;
description: string;
type: 'github' | 'dribbble';
url: string;
}
================================================
FILE: src/components/molecules/SearchBox/index.tsx
================================================
import { TextBox } from 'components/atoms';
import TextBoxProps from 'components/atoms/TextBox/types';
import React, { useContext } from 'react';
import classNames from 'classnames';
import { LanguageContext } from 'contexts';
export default function SearchBox({ className }: TextBoxProps) {
const language = useContext(LanguageContext);
return (
<div
className={classNames('focus-within:ring-2 ring-indigo-500 focus-within:shadow-xl focus-within:bg-white transition-all transform focus-within:scale-110 flex items-center overflow-hidden bg-gray-200 rounded-xl', className)}
>
<svg
className="w-4 h-4 mx-4 text-gray-500"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
<TextBox
className="flex-1 focus:outline-none"
placeholder={language.value === 'en' ? 'Search...' : 'Cari sesuatu...'}
/>
</div>
);
}
================================================
FILE: src/components/molecules/SectionTitle/index.tsx
================================================
import { HeaderText, TitleLine } from 'components/atoms';
import React, { ReactNode } from 'react';
import classNames from 'classnames';
import { SVGMotionProps } from 'framer-motion';
interface Props {
children: ReactNode;
variant?: SectionVariants;
lineProps?: SVGMotionProps<SVGElement>;
}
export enum SectionVariants {
LEFT = 'left',
CENTER = 'center',
}
export default function SectionTitle({
children,
variant = SectionVariants.LEFT,
lineProps,
}: Props) {
return (
<HeaderText
className={classNames('flex flex-col items-center', {
'lg:flex-row': variant === SectionVariants.LEFT,
})}
>
{children}
<TitleLine
className={classNames({
'lg:ml-6 lg:mt-0': variant === SectionVariants.LEFT,
})}
{...lineProps}
/>
</HeaderText>
);
}
================================================
FILE: src/components/molecules/TextIconButton/index.tsx
================================================
import { TextButton } from 'components/atoms';
import React, { ReactElement } from 'react';
import classNames from 'classnames';
interface Props {
text: string;
icon: ReactElement;
className?: string;
onClick: (params: any) => any;
}
export default function TextIconButton({ text, icon, className, onClick }: Props) {
const Icon = React.cloneElement(icon, { className: classNames('w-4 h-4', icon.props.className) });
return (
<TextButton className={className} onClick={onClick}>
<span className="pr-2">{text}</span>
{Icon}
</TextButton>
);
}
================================================
FILE: src/components/molecules/TextSelect/index.tsx
================================================
import React, { Fragment } from 'react';
import { Listbox, Transition } from '@headlessui/react';
import { TextSelectOption } from 'components/atoms';
import TextSelectProps from 'components/molecules/TextSelect/types';
import classNames from 'classnames';
export default function TextSelect({ options, state, className }: TextSelectProps) {
const [selectedOption, setSelectedOption] = state;
return (
<Listbox as="div" className={className} value={selectedOption} onChange={setSelectedOption}>
{({ open }) => (
<>
<Listbox.Button className="flex items-center px-4 py-2 text-gray-700 bg-white rounded-lg focus:outline-none focus:ring-2 ring-indigo-500">
{selectedOption.label}
<svg
className={classNames('w-4 h-4 ml-2 transition-transform transform', {
'rotate-180': open,
})}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</Listbox.Button>
<Transition
show={open}
enter="duration-100 ease-out"
enterFrom="scale-90 opacity-0"
enterTo="scale-110 opacity-100"
leave="duration-75 ease-out"
leaveFrom="scale-110 opacity-100"
leaveTo="scale-90 opacity-0"
className="fixed z-20 transition duration-150 transform -translate-x-1/2 -translate-y-1/2 sm:translate-x-0 sm:translate-y-0 sm:mt-4 sm:absolute left-1/2 top-1/2 sm:left-auto sm:top-auto"
>
<Listbox.Options
static
className="overflow-hidden text-3xl bg-white shadow-md rounded-xl focus:outline-none focus:ring-2 ring-indigo-500 sm:text-base"
>
{options.map(option => (
<Listbox.Option as={Fragment} key={option.value} value={option}>
{({ active, selected }) => (
<TextSelectOption
active={active}
selected={selected}
onClick={() => setSelectedOption(option)}
>
{option.label}
</TextSelectOption>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</>
)}
</Listbox>
);
}
================================================
FILE: src/components/molecules/TextSelect/types.tsx
================================================
export default interface TextSelectProps {
options: TextSelectOptionProps[];
state: [TextSelectOptionProps, (any: any) => void];
className?: string;
}
export interface TextSelectOptionProps {
value: string | number;
label: string;
}
================================================
FILE: src/components/molecules/index.tsx
================================================
import TextIconButton from 'components/molecules/TextIconButton';
import TextSelect from 'components/molecules/TextSelect';
import MenuButton from 'components/molecules/MenuButton';
import SectionTitle from 'components/molecules/SectionTitle';
import HighlightedTextIcon from 'components/molecules/HighlightedTextIcon';
import SearchBox from 'components/molecules/SearchBox';
import BlogPost from 'components/molecules/BlogPost';
import MainMenu from 'components/molecules/MainMenu';
export {
TextIconButton,
TextSelect,
MenuButton,
SectionTitle,
HighlightedTextIcon,
SearchBox,
BlogPost,
MainMenu,
};
================================================
FILE: src/components/organisms/About/index.tsx
================================================
import Animated from 'components/animations';
import { ContentText, RoundedButton } from 'components/atoms';
import { SectionTitle } from 'components/molecules';
import { LanguageContext } from 'contexts';
import React, { useContext } from 'react';
import AboutSectionProps from 'components/organisms/About/types';
import { useAnimation } from 'framer-motion';
import { useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
import { forwardRef } from 'react';
const About = forwardRef<HTMLElement | undefined, AboutSectionProps>(
({ title, content, className }: AboutSectionProps, ref) => {
const controls = useAnimation();
const [refView, inView] = useInView({
threshold: 0.5,
});
const language = useContext(LanguageContext);
useEffect(() => {
if (inView) {
controls.start('show');
}
}, [controls, inView]);
return (
<section
id="about"
className={className}
ref={section => {
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
<SectionTitle
lineProps={{
animate: controls,
custom: 1,
transition: {
delay: 1,
},
}}
>
<Animated.Letter text={title} animate={controls} custom={0} delay={1} />
</SectionTitle>
<div className="grid grid-rows-2 mt-6 lg:grid-cols-3 lg:grid-rows-1">
<Animated.FromDirection
className="lg:col-span-2"
from="left"
animate={controls}
custom={0}
delay={1}
>
<Animated.Reveal from="left" animate={controls} custom={1} delay={1}>
<ContentText className="lg:text-left">{content.text}</ContentText>
</Animated.Reveal>
</Animated.FromDirection>
<div className="mx-auto mt-6 lg:mr-0 lg:ml-auto lg:mt-0">
<Animated.FromDirection from="bottom" animate={controls} custom={1} delay={1}>
<RoundedButton as="a" href="./#">
{language.value === 'en' ? 'Download' : 'Unduh'} Resume
</RoundedButton>
</Animated.FromDirection>
</div>
</div>
</section>
);
}
);
export default About;
================================================
FILE: src/components/organisms/About/types.ts
================================================
import { ReactNode } from 'react';
export default interface AboutSectionProps {
title: string;
content: AboutSectionContentProps;
className?: string;
}
export interface AboutSectionContentProps {
text: ReactNode;
}
================================================
FILE: src/components/organisms/Blogs/index.tsx
================================================
import Animated from 'components/animations';
import { RoundedButton } from 'components/atoms';
import { SectionTitle, BlogPost } from 'components/molecules';
import { LanguageContext } from 'contexts';
import { useAnimation } from 'framer-motion';
import React, { forwardRef, useContext, useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
import BlogsSectionProps from './types';
const Blogs = forwardRef<HTMLElement | undefined, BlogsSectionProps>(
({ title, content, className }: BlogsSectionProps, ref) => {
const controls = useAnimation();
const [refView, inView] = useInView({
threshold: 0.45,
});
const language = useContext(LanguageContext);
const { items } = content;
useEffect(() => {
if (inView) {
controls.start('show');
}
}, [controls, inView]);
return (
<section
id="blogs"
className={className}
ref={section => {
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
<div className="flex items-center justify-between">
<SectionTitle
lineProps={{
animate: controls,
custom: 1,
transition: {
delay: 1,
},
}}
>
<Animated.Letter text={title} animate={controls} custom={0} delay={1} />
</SectionTitle>
<Animated.FromDirection from="bottom" animate={controls} custom={0.5} delay={1}>
<RoundedButton as="a" href="https://medium.com/@itsfaqih">
{language.value === 'en' ? 'More article' : 'Artikel lainnya'}
</RoundedButton>
</Animated.FromDirection>
</div>
<div className="grid grid-cols-1 mt-10 md:mt-8 md:grid-cols-2 md:gap-x-20 gap-y-10 md:gap-y-16">
{items.map((item, index) => (
<Animated.FromDirection
key={index}
from="left"
animate={controls}
custom={index + 1}
delay={0.4}
>
<Animated.Reveal from="left" animate={controls} custom={index + 3} delay={0.4}>
<BlogPost data={item} />
</Animated.Reveal>
</Animated.FromDirection>
))}
</div>
</section>
);
}
);
export default Blogs;
================================================
FILE: src/components/organisms/Blogs/types.ts
================================================
import { BlogsPostProps } from 'components/molecules/BlogPost/types';
export default interface BlogsSectionProps {
title: string;
content: BlogsSectionContentProps;
className?: string;
}
export interface BlogsSectionContentProps {
items: BlogsPostProps[];
}
================================================
FILE: src/components/organisms/Contact/index.tsx
================================================
import { FullRoundButton } from 'components/atoms';
import Icons from 'components/icons';
import React, { useEffect } from 'react';
import ContactSectionProps from 'components/organisms/Contact/types';
import Animated from 'components/animations';
import { useAnimation } from 'framer-motion';
import { useInView } from 'react-intersection-observer';
import { forwardRef } from 'react';
const Contact = forwardRef<HTMLElement | undefined, ContactSectionProps>(
({ title, content }: ContactSectionProps, ref) => {
const { email, socials } = content;
const controls = useAnimation();
const [refView, inView] = useInView({
threshold: 0.4,
});
useEffect(() => {
if (inView) {
controls.start('show');
}
}, [controls, inView]);
return (
<section
id="contact"
ref={section => {
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
<h2 className="flex justify-center text-3xl font-bold text-gray-600 md:justify-start">
<Animated.Letter text={title} animate={controls} custom={0} delay={0.6} />
</h2>
<div className="flex flex-col items-center md:flex-row md:justify-between">
<a
href={`mailto:${email}`}
className="relative mt-6 text-4xl font-bold text-gray-800 rounded-lg focus:outline-none focus:ring-2 ring-indigo-500 ring-offset-8"
>
<Animated.Letter text={email} animate={controls} custom={1} delay={0.6} />
<Animated.Highlighter
className="h-4 bottom-0.5"
rad={8}
animate={controls}
custom={2}
delay={0.6}
/>
</a>
<div className="flex items-center mt-8 md:mt-0">
<Animated.FromDirection from="bottom" animate={controls} custom={2.2} delay={0.6}>
<FullRoundButton as="a" href={socials.facebook} label="Facebook">
<Icons.Facebook className="w-6" />
</FullRoundButton>
</Animated.FromDirection>
<Animated.FromDirection from="bottom" animate={controls} custom={2.4} delay={0.6}>
<FullRoundButton as="a" href={socials.twitter} className="mx-6" label="Twitter">
<Icons.Twitter className="w-6" />
</FullRoundButton>
</Animated.FromDirection>
<Animated.FromDirection from="bottom" animate={controls} custom={2.6} delay={0.6}>
<FullRoundButton as="a" href={socials.instagram} label="Instagram">
<Icons.Instagram className="w-6" />
</FullRoundButton>
</Animated.FromDirection>
</div>
</div>
</section>
);
}
);
export default Contact;
================================================
FILE: src/components/organisms/Contact/types.ts
================================================
export default interface ContactSectionProps {
title: string;
content: ContactSectionContentProps;
}
export interface ContactSectionContentProps {
email: string;
socials: {
facebook: string;
twitter: string;
instagram: string;
};
}
================================================
FILE: src/components/organisms/Header/index.tsx
================================================
import Animated from 'components/animations';
import { Avatar, HeaderText } from 'components/atoms';
import Decorations from 'components/decorations';
import { TextIconButton } from 'components/molecules';
import HeaderProps from 'components/organisms/Header/types';
import { LanguageContext } from 'contexts';
import React, { useContext } from 'react';
export default function Header({ data, text, lowerSectionRef }: HeaderProps) {
const languange = useContext(LanguageContext);
const { profilePicture, name, job } = data;
const { headerText } = text;
const { greetings, intro, preTitle } = headerText;
return (
<header className="flex justify-center">
<div className="grid grid-rows-3 md:grid-cols-2 md:h-60">
<div className="relative col-start-1 row-span-3 justify-self-center">
<Animated.FromDirection
from="top"
delay={2.2}
duration={1}
className="absolute -top-6 md:-top-10 -left-6 md:-left-10 -z-10"
>
<Decorations.Stripes className="w-24 h-24 text-indigo-300 md:w-40 md:h-40" />
</Animated.FromDirection>
<Animated.FromDirection from="right" custom={1} delay={0.3} duration={0.8}>
<Animated.Reveal from="right" className="rounded-3xl" delay={0.8} duration={1.2}>
<Avatar src={profilePicture} alt="Profile Picture" />
</Animated.Reveal>
</Animated.FromDirection>
</div>
<HeaderText className="flex flex-col items-center mt-6 md:items-start md:row-start-2 md:col-start-2 md:self-center md:mt-0">
<Animated.Letter text={`${greetings} 👋`} custom={0} delay={0.3} />
<Animated.Letter text={`${intro} ${name}`} custom={2} delay={0.3} />
<div className="relative inline-flex">
<Animated.Letter
text={`${preTitle ? `${preTitle} ` : ''}${job}`}
custom={4}
delay={0.3}
/>
<Animated.Highlighter className="h-3 bottom-1.5" rad={6} custom={8} delay={0.3} />
</div>
</HeaderText>
<Animated.FromDirection
from="top"
custom={8}
delay={0.3}
className="mx-auto mt-6 md:-ml-0 md:mr-auto md:mt-auto md:col-start-2 md:row-start-3"
>
<TextIconButton
className="group"
onClick={() => lowerSectionRef?.current?.scrollIntoView({ behavior: 'smooth' })}
text={languange.value === 'en' ? 'Scroll down' : 'Scroll ke bawah'}
icon={
<svg
className="transition-all transform group-hover:translate-y-1"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
}
/>
</Animated.FromDirection>
</div>
</header>
);
}
================================================
FILE: src/components/organisms/Header/types.ts
================================================
import { MutableRefObject } from "react";
export default interface HeaderProps {
data: HeaderDataProps;
text: HeaderTextProps;
lowerSectionRef?: MutableRefObject<HTMLElement | undefined>;
}
export interface HeaderDataProps {
profilePicture: string;
name: string;
job: string;
}
export interface HeaderTextProps {
headerText: {
greetings: string;
intro: string;
preTitle: string;
};
}
================================================
FILE: src/components/organisms/Navigation/index.tsx
================================================
import Animated from 'components/animations';
import { MainMenu, TextSelect } from 'components/molecules';
import { TextSelectOptionProps } from 'components/molecules/TextSelect/types';
import { LocaleProps } from 'contexts/language';
import React from 'react';
import { useState } from 'react';
import classNames from 'classnames';
import { Container } from 'components/atoms';
interface Props {
locale: LocaleProps;
sections: { about: string; projects: string; blogs: string; skills: string; contact: string };
className?: string;
}
export default function Navigation({ locale, sections, className }: Props) {
const [animationFinish, setAnimationFinish] = useState(false);
const languageOptions: TextSelectOptionProps[] = [
{ label: 'English', value: 'en' },
{ label: 'Indonesia', value: 'id' },
];
const selectedLanguage =
languageOptions.find(option => option.value === locale.value) || languageOptions[0];
const setSelectedLanguage = (option: TextSelectOptionProps) => {
if (option.value === 'en' || option.value === 'id') {
locale.change(option.value);
}
};
return (
<nav>
<Animated.FromDirection
from="top"
className={classNames('w-full', className)}
duration={1}
onAnimationComplete={() => setAnimationFinish(true)}
>
<Container className="flex">
<div className="flex self-center flex-1 pr-8">
<div className="sm:ml-auto">
<TextSelect
state={[selectedLanguage, setSelectedLanguage]}
options={languageOptions}
/>
</div>
</div>
<MainMenu sections={sections} animationFinish={animationFinish} />
</Container>
</Animated.FromDirection>
</nav>
);
}
================================================
FILE: src/components/organisms/Projects/index.tsx
================================================
import { SectionTitle } from 'components/molecules';
import React, { useContext, useEffect } from 'react';
import { SectionVariants } from 'components/molecules/SectionTitle';
import { RoundedButton } from 'components/atoms';
import { LanguageContext } from 'contexts';
import ProjectCard from 'components/molecules/ProjectCard';
import Decorations from 'components/decorations';
import ProjectsSectionProps from 'components/organisms/Projects/types';
import { useAnimation } from 'framer-motion';
import { useInView } from 'react-intersection-observer';
import Animated from 'components/animations';
import { forwardRef } from 'react';
const Projects = forwardRef<HTMLElement | undefined, ProjectsSectionProps>(
({ title, content, className }: ProjectsSectionProps, ref) => {
const controls = useAnimation();
const [refView, inView] = useInView({
threshold: 0.4,
});
const language = useContext(LanguageContext);
const { items } = content;
const bulletsClasses = 'w-32 h-32 text-indigo-300 md:w-40 md:h-40';
useEffect(() => {
if (inView) {
controls.start('show');
}
}, [controls, inView]);
return (
<section
id="projects"
className={className}
ref={section => {
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
<SectionTitle
variant={SectionVariants.CENTER}
lineProps={{
animate: controls,
custom: 1,
transition: {
delay: 1,
},
}}
>
<Animated.Letter text={title} animate={controls} custom={0} delay={1} />
</SectionTitle>
<div className="relative grid grid-cols-1 gap-8 mt-6 sm:grid-cols-2 lg:grid-cols-3">
<Animated.FromDirection
from="top"
animate={controls}
custom={1}
delay={1}
className="absolute -top-8 -left-12 md:-top-9 md:-left-16 -z-10"
>
<Decorations.Bullets className={bulletsClasses} />
</Animated.FromDirection>
<Animated.FromDirection
from="bottom"
animate={controls}
custom={1}
delay={1}
className="absolute -bottom-8 -right-12 md:-bottom-9 md:-right-16 -z-10"
>
<Decorations.Bullets className={bulletsClasses} />
</Animated.FromDirection>
{items.map((item, index) => (
<Animated.FadeIn key={index} animate={controls} custom={index} delay={0.2}>
<ProjectCard
data={item}
className="h-full transition-all transform hover:shadow-lg hover:scale-110 focus-within:scale-110 focus-within:shadow-lg"
/>
</Animated.FadeIn>
))}
</div>
<Animated.FromDirection className="flex justify-center mt-8" from="bottom" animate={controls} custom={2} delay={1}>
<RoundedButton as="a" href="https://github.com/itsfaqih">
{language.value === 'en' ? 'See more' : 'Lihat Selengkapnya'}
</RoundedButton>
</Animated.FromDirection>
</section>
);
}
);
export default Projects;
================================================
FILE: src/components/organisms/Projects/types.ts
================================================
import { ProjectCardDataProps } from 'components/molecules/ProjectCard/types';
export default interface ProjectsSectionProps {
title: string;
content: ProjectsSectionContentProps;
className?: string;
}
export interface ProjectsSectionContentProps {
items: ProjectCardDataProps[];
}
================================================
FILE: src/components/organisms/Skills/index.tsx
================================================
import Animated from 'components/animations';
import { ContentText } from 'components/atoms';
import { SectionTitle } from 'components/molecules';
import { SectionVariants } from 'components/molecules/SectionTitle';
import { useAnimation } from 'framer-motion';
import React, { useEffect } from 'react';
import { forwardRef } from 'react';
import { useInView } from 'react-intersection-observer';
import SkillsSectionProps from './types';
const Skills = forwardRef<HTMLElement | undefined, SkillsSectionProps>(
({ title, content, className }: SkillsSectionProps, ref) => {
const { text, items } = content;
const upperSkills = items.slice(0, items.length / 2);
const lowerSkills = items.slice(items.length / 2);
const skillIconsClasses =
'mx-5 transition-all transform hover:scale-125 hover:filter-none filter-grayscale focus-within:filter-none focus-within:scale-125';
const controls = useAnimation();
const [refView, inView] = useInView({
threshold: 0.5,
});
useEffect(() => {
if (inView) {
controls.start('show');
}
}, [controls, inView]);
return (
<section
id="skills"
className={className}
ref={section => {
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
<SectionTitle
variant={SectionVariants.CENTER}
lineProps={{
animate: controls,
custom: 1,
transition: {
delay: 1,
},
}}
>
<Animated.Letter text={title} animate={controls} custom={0} delay={1} />
</SectionTitle>
<Animated.FromDirection
className="mx-auto mt-6 lg:w-2/3"
from="left"
animate={controls}
custom={0}
delay={1}
>
<Animated.Reveal from="left" animate={controls} custom={1} delay={1}>
<ContentText className="text-center">{text}</ContentText>
</Animated.Reveal>
</Animated.FromDirection>
<div className="flex items-center justify-center mt-8">
{upperSkills.map((SkillItem, index) => (
<Animated.FromDirection
key={index}
from="bottom"
animate={controls}
custom={index + 2}
delay={0.3}
>
<div className={skillIconsClasses}>{SkillItem}</div>
</Animated.FromDirection>
))}
</div>
<div className="flex items-center justify-center mt-6">
{lowerSkills.map((SkillItem, index) => (
<Animated.FromDirection
key={index}
from="bottom"
animate={controls}
custom={index + 5}
delay={0.3}
>
<div className={skillIconsClasses}>{SkillItem}</div>
</Animated.FromDirection>
))}
</div>
</section>
);
}
);
export default Skills;
================================================
FILE: src/components/organisms/Skills/types.ts
================================================
import { ReactNode } from 'react';
export default interface SkillsSectionProps {
title: string;
content: SkillsSectionContentProps;
className?: string;
}
export interface SkillsSectionContentProps {
text: ReactNode;
items: ReactNode[];
}
================================================
FILE: src/components/organisms/index.tsx
================================================
import Header from 'components/organisms/Header';
import About from 'components/organisms/About';
import Projects from 'components/organisms/Projects';
import Blogs from 'components/organisms/Blogs';
import Skills from 'components/organisms/Skills';
import Contact from 'components/organisms/Contact';
import Navigation from 'components/organisms/Navigation';
export { Header, About, Projects, Blogs, Skills, Contact, Navigation };
================================================
FILE: src/components/templates/Landing/index.tsx
================================================
import { Container } from 'components/atoms';
import { About, Blogs, Contact, Header, Navigation, Projects, Skills } from 'components/organisms';
import { AboutSectionContentProps } from 'components/organisms/About/types';
import { BlogsSectionContentProps } from 'components/organisms/Blogs/types';
import { ContactSectionContentProps } from 'components/organisms/Contact/types';
import HeaderProps from 'components/organisms/Header/types';
import { ProjectsSectionContentProps } from 'components/organisms/Projects/types';
import { SkillsSectionContentProps } from 'components/organisms/Skills/types';
import { LocaleProps } from 'contexts/language';
import React from 'react';
import { useRef } from 'react';
interface Props {
data: {
header: HeaderProps;
about: AboutSectionContentProps;
projects: ProjectsSectionContentProps;
blogs: BlogsSectionContentProps;
skills: SkillsSectionContentProps;
contact: ContactSectionContentProps;
};
locale: LocaleProps;
sections: { about: string; projects: string; blogs: string; skills: string; contact: string };
}
export default function Landing({ data, locale, sections }: Props) {
const { header, about, projects, blogs, skills, contact } = data;
const lowerSectionRef = useRef();
return (
<>
<Navigation className="fixed z-10 top-8" locale={locale} sections={sections} />
<Container className="pt-32">
<div className="flex flex-wrap">
<main className="w-full mt-12">
<Header data={header.data} text={header.text} lowerSectionRef={lowerSectionRef} />
<About
title={sections.about}
content={{
text: about.text,
}}
className="pt-32 mt-24"
ref={lowerSectionRef}
/>
<Projects
title={sections.projects}
content={{
items: projects.items,
}}
className="pt-12 mt-40"
/>
<Blogs
title={sections.blogs}
content={{ items: blogs.items }}
className="mt-12 pt-28"
/>
<Skills
title={sections.skills}
content={{ text: skills.text, items: skills.items }}
className="pt-24 mt-28"
/>
</main>
<footer className="w-full pt-20 pb-16 mt-32">
<Contact
title={sections.contact}
content={{
email: contact.email,
socials: contact.socials,
}}
/>
</footer>
</div>
</Container>
</>
);
}
================================================
FILE: src/components/templates/index.tsx
================================================
import Landing from 'components/templates/Landing';
export { Landing };
================================================
FILE: src/contexts/index.ts
================================================
import LanguageContext from 'contexts/language';
export { LanguageContext };
================================================
FILE: src/contexts/language.ts
================================================
import { createContext } from 'react';
export interface LocaleProps {
value: 'en' | 'id';
change: (lang: 'en' | 'id') => void;
}
const LanguageContext = createContext<LocaleProps>({
value: 'en',
change: lang => {},
});
export default LanguageContext;
================================================
FILE: src/data/blogs.ts
================================================
import { BlogsPostProps } from 'components/molecules/BlogPost/types';
const blogs: BlogsPostProps[] = [
{
title: 'Membuat Otentikasi JWT dengan PHP Native',
date: new Date('2020-11-16'),
url: 'https://medium.com/@itsfaqih/membuat-otentikasi-jwt-dengan-php-native-a9d080953358',
},
{
title: 'Apa itu JWT (JSON Web Token)?',
date: new Date('2020-11-14'),
url: 'https://medium.com/@itsfaqih/apa-itu-jwt-json-web-token-63407936da10',
},
{
title: 'Penerapan Sistem Grid Responsive dengan Flexbox',
date: new Date('2020-06-13'),
url:
'https://medium.com/@itsfaqih/penerapan-sistem-grid-responsive-dengan-flexbox-c479c84be129',
},
{
title: 'Memahami Satuan Persen (%) dalam CSS',
date: new Date('2020-06-11'),
url: 'https://medium.com/@itsfaqih/memahami-satuan-persen-dalam-css-12479cba1c32',
},
];
export default blogs;
================================================
FILE: src/data/contact.ts
================================================
import { ContactSectionContentProps } from 'components/organisms/Contact/types';
const contact: ContactSectionContentProps = {
email: 'itsfaqih@gmail.com',
socials: {
facebook: 'https://facebook.com/itsfaqih',
twitter: 'https://twitter.com/itsfaqih_',
instagram: 'https://instagram.com/itsfaqih',
},
};
export default contact;
================================================
FILE: src/data/header.ts
================================================
import { HeaderDataProps } from 'components/organisms/Header/types';
const header: HeaderDataProps = {
name: 'Faqih Muntashir',
profilePicture: './img/person.jpg',
job: 'Fullstack Developer',
};
export default header;
================================================
FILE: src/data/index.ts
================================================
import header from 'data/header';
import projects from 'data/projects';
import blogs from 'data/blogs';
import skills from 'data/skills';
import contact from 'data/contact';
export { header, projects, blogs, skills, contact };
================================================
FILE: src/data/projects.ts
================================================
import { ProjectCardDataProps } from "components/molecules/ProjectCard/types";
const projects: ProjectCardDataProps[] = [
{
title: 'usePagination',
description: 'Simple client side pagination hooks for react.',
type: 'github',
url: 'https://github.com/itsfaqih/usepagination',
},
{
title: 'larareactia',
description:
'Laravel SPA + React + Inertia.js + TailwindCSS starter template with authentication.',
type: 'github',
url: 'https://github.com/itsfaqih/larareactia',
},
{
title: 'Overflow',
description: 'TailwindCSS Admin Dashboard Template.',
type: 'dribbble',
url: 'https://dribbble.com/shots/11877395-Overflow-TailwindCSS-Admin-Dashboard-Template',
},
{
title: 'kos-manager',
description: 'Lodgement management application. Developed with Laravel, Inertiajs, and React.',
type: 'github',
url: 'http://github.com/itsfaqih/kos-manager',
},
{
title: 'Nyx',
description: 'TailwindCSS Landing Page Template.',
type: 'dribbble',
url: 'https://dribbble.com/shots/11712756-Nyx-TailwindCSS-Landing-Page-Template',
},
{
title: 'flexbox-tutorial',
description: 'CSS layouting using flexbox tutorial.',
type: 'github',
url: 'http://github.com/itsfaqih/flexbox-tutorial',
},
];
export default projects;
================================================
FILE: src/data/skills.tsx
================================================
import React from 'react';
import { Icon } from '@iconify/react';
import reactIcon from '@iconify/icons-logos/react';
import tailwindcssIcon from '@iconify/icons-logos/tailwindcss-icon';
import laravelIcon from '@iconify/icons-logos/laravel';
import codeigniterIcon from '@iconify/icons-logos/codeigniter';
import postCssIcon from '@iconify/icons-logos/postcss';
import bootstrapIcon from '@iconify/icons-logos/bootstrap';
import nextjsIcon from '@iconify/icons-logos/nextjs';
const skills = [
<a href="https://reactjs.org" className="focus:outline-none">
<Icon className="text-5xl" icon={reactIcon} />
</a>,
<a href="https://tailwindcss.com" className="focus:outline-none">
<Icon className="text-4xl" icon={tailwindcssIcon} />
</a>,
<a href="https://laravel.com" className="focus:outline-none">
<Icon className="text-5xl" icon={laravelIcon} />
</a>,
<a href="https://codeigniter.com" className="focus:outline-none">
<Icon className="text-6xl" icon={codeigniterIcon} />
</a>,
<a href="https://postcss.org" className="focus:outline-none">
<Icon className="text-5xl" icon={postCssIcon} />
</a>,
<a href="https://getboostrap.com" className="focus:outline-none">
<Icon className="text-5xl" icon={bootstrapIcon} />
</a>,
<a href="https://nextjs.org" className="focus:outline-none">
<Icon className="text-5xl" icon={nextjsIcon} />
</a>,
];
export default skills;
================================================
FILE: src/index.tsx
================================================
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
================================================
FILE: src/localization/en/about.tsx
================================================
import React from 'react';
import { Icon } from '@iconify/react';
import { HighlightedTextIcon } from 'components/molecules';
import { AboutSectionContentProps } from 'components/organisms/About/types';
import reactIcon from '@iconify/icons-logos/react';
import tailwindcssIcon from '@iconify/icons-logos/tailwindcss-icon';
const Text = () => (
<>
I’m currently working as a freelance{' '}
<span className="font-bold text-gray-800">fullstack developer</span> and{' '}
<span className="font-bold text-gray-800">UI/UX designer</span>. I enjoy building web apps using
<HighlightedTextIcon
as="a"
href="https://tailwindcss.com"
className="mx-2"
icon={<Icon icon={tailwindcssIcon} />}
>
TailwindCSS
</HighlightedTextIcon>{' '}
and{' '}
<HighlightedTextIcon
as="a"
href="https://reactjs.org"
className="mx-2"
icon={<Icon icon={reactIcon} />}
>
React
</HighlightedTextIcon>
. I'm also an active student in University of AMIKOM Yogyakarta.
</>
);
const about: AboutSectionContentProps = {
text: <Text />,
};
export default about;
================================================
FILE: src/localization/en/header.ts
================================================
import { HeaderTextProps } from 'components/organisms/Header/types';
const header: HeaderTextProps = {
headerText: {
greetings: 'Hello',
intro: "I'm",
preTitle: 'a',
}
};
export default header;
================================================
FILE: src/localization/en/index.ts
================================================
import header from 'localization/en/header';
import about from 'localization/en/about';
import sections from 'localization/en/sections';
import skills from 'localization/en/skills';
export { header, about, sections, skills };
================================================
FILE: src/localization/en/sections.ts
================================================
const section = {
about: 'About me',
projects: 'Projects',
blogs: 'Blogs',
skills: 'Skills',
contact: 'Get in touch 🤙',
};
export default section;
================================================
FILE: src/localization/en/skills.tsx
================================================
import React from 'react';
import { Icon } from '@iconify/react';
import phpIcon from '@iconify/icons-logos/php';
import javascriptIcon from '@iconify/icons-logos/javascript';
import { HighlightedTextIcon } from 'components/molecules';
const Text = () => (
<>
I’ve spent about two years on improving my skills in{' '}
<span className="font-bold text-gray-800">website development</span> using{' '}
<HighlightedTextIcon
as="a"
href="https://php.net"
className="mx-2"
icon={<Icon icon={phpIcon} />}
>
PHP
</HighlightedTextIcon>{' '}
and{' '}
<HighlightedTextIcon className="mx-2" icon={<Icon icon={javascriptIcon} />}>
Javascript
</HighlightedTextIcon>{' '}
that I’m now pretty confident in building apps using these tools:
</>
);
const skills = {
text: <Text />,
};
export default skills;
================================================
FILE: src/localization/id/about.tsx
================================================
import React from 'react';
import { HighlightedTextIcon } from 'components/molecules';
import { AboutSectionContentProps } from 'components/organisms/About/types';
const Text = () => (
<>
Saat ini saya sedang bekerja sebagai freelance{' '}
<span className="font-bold text-gray-800">fullstack developer</span> dan{' '}
<span className="font-bold text-gray-800">UI/UX designer</span>, saya sangat suka menggunakan
<HighlightedTextIcon
as="a"
href="https://tailwindcss.com"
className="mx-2"
icon={
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 54 33">
<g clipPath="url(#prefix__clip0)">
<path
fill="#14B4C6"
fillRule="evenodd"
d="M27 0c-7.2 0-11.7 3.6-13.5 10.8 2.7-3.6 5.85-4.95 9.45-4.05 2.054.513 3.522 2.004 5.147 3.653C30.744 13.09 33.808 16.2 40.5 16.2c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C36.756 3.11 33.692 0 27 0zM13.5 16.2C6.3 16.2 1.8 19.8 0 27c2.7-3.6 5.85-4.95 9.45-4.05 2.054.514 3.522 2.004 5.147 3.653C17.244 29.29 20.308 32.4 27 32.4c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C23.256 19.31 20.192 16.2 13.5 16.2z"
clipRule="evenodd"
/>
</g>
<defs>
<clipPath id="prefix__clip0">
<path fill="#fff" d="M0 0h54v32.4H0z" />
</clipPath>
</defs>
</svg>
}
>
TailwindCSS
</HighlightedTextIcon>{' '}
dan{' '}
<HighlightedTextIcon
as="a"
href="https://reactjs.org"
className="mx-2"
icon={
<svg
width="30"
height="27"
viewBox="0 0 30 27"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0)">
<path
d="M24.6652 8.75216C24.3461 8.64133 24.0243 8.53842 23.7 8.44342C23.7539 8.22175 23.8039 7.99921 23.85 7.77579C24.5804 4.18696 24.103 1.29478 22.47 0.342155C20.9048 -0.570886 18.3457 0.381738 15.7604 2.65774C15.5057 2.88205 15.257 3.11294 15.0143 3.35044C14.8517 3.19211 14.6865 3.0373 14.5187 2.886C11.8083 0.454306 9.0913 -0.569567 7.46087 0.383057C5.89957 1.29874 5.43652 4.01807 6.0913 7.41954C6.15826 7.75644 6.23217 8.09157 6.31304 8.42495C5.93087 8.53578 5.55913 8.6532 5.20435 8.77855C2.02826 9.90006 0 11.6549 0 13.4757C0 15.3559 2.17826 17.2427 5.48609 18.384C5.75391 18.4772 6.02435 18.5634 6.29739 18.6426C6.2087 19.0006 6.13043 19.3612 6.06261 19.7245C5.43391 23.0666 5.92435 25.7213 7.48435 26.6317C9.09391 27.5711 11.7965 26.6053 14.43 24.2778C14.6387 24.0949 14.8474 23.9005 15.0561 23.6946C15.3187 23.9524 15.5883 24.2022 15.8648 24.4441C18.4161 26.6633 20.9335 27.5579 22.4909 26.6475C24.1017 25.7041 24.6248 22.8528 23.9452 19.3841C23.893 19.1202 23.833 18.8502 23.7652 18.574C23.9548 18.5177 24.1409 18.4592 24.3235 18.3985C27.763 17.2453 30 15.3809 30 13.4757C30 11.647 27.9065 9.88027 24.6652 8.75216ZM16.6043 3.63676C18.8191 1.68665 20.8891 0.918743 21.8348 1.46762C22.8391 2.05345 23.2278 4.41654 22.5965 7.51586C22.5557 7.71817 22.5109 7.91917 22.4622 8.11884C21.1402 7.81573 19.7999 7.60152 18.45 7.4776C17.6765 6.3537 16.8289 5.28385 15.913 4.27536C16.1374 4.05721 16.367 3.84435 16.6017 3.63676H16.6043ZM8.85652 15.0287C9.1287 15.5599 9.41261 16.0833 9.70826 16.5988C10.01 17.1265 10.3239 17.6468 10.65 18.1597C9.72128 18.0581 8.79832 17.9084 7.88478 17.7111C8.15087 16.8442 8.47696 15.943 8.85783 15.03L8.85652 15.0287ZM8.85652 11.9808C8.48348 11.0862 8.16522 10.2062 7.90435 9.35514C8.7587 9.16118 9.67174 9.00285 10.6226 8.88278C10.3043 9.38417 9.99783 9.89434 9.70304 10.4133C9.40739 10.9288 9.12478 11.4513 8.85522 11.9808H8.85652ZM9.53739 13.506C9.93217 12.6748 10.3583 11.8594 10.8157 11.0598C11.2696 10.2594 11.7539 9.47872 12.2687 8.71786C13.1609 8.64925 14.0752 8.6123 15 8.6123C15.9287 8.6123 16.8443 8.64925 17.7365 8.71786C18.2452 9.47872 18.727 10.2572 19.1817 11.0532C19.6391 11.8502 20.0704 12.6616 20.4757 13.4876C20.0757 14.3214 19.6461 15.1404 19.187 15.9443C18.7322 16.743 18.2522 17.5259 17.747 18.2929C16.8574 18.3589 15.9378 18.3906 15 18.3906C14.0661 18.3906 13.1583 18.3615 12.2843 18.3035C11.7661 17.5382 11.2774 16.754 10.8183 15.9509C10.3609 15.1505 9.93391 14.3351 9.53739 13.5047V13.506ZM20.2983 16.5935C20.5991 16.0631 20.8887 15.5265 21.167 14.9838C21.5481 15.8557 21.8842 16.7471 22.1739 17.6543C21.2502 17.864 20.3163 18.0247 19.3761 18.1359C19.6926 17.6275 19.9996 17.1129 20.297 16.5922L20.2983 16.5935ZM21.1539 11.9821C20.8774 11.4508 20.5904 10.9248 20.293 10.4041C19.9991 9.89214 19.6948 9.38637 19.38 8.88674C20.3361 9.00945 21.253 9.17174 22.1152 9.37097C21.8384 10.2576 21.5176 11.1295 21.1539 11.9834V11.9821ZM15.013 5.20555C15.6373 5.89365 16.2267 6.61321 16.7791 7.36149C15.5983 7.30431 14.4165 7.30431 13.2339 7.36149C13.817 6.58303 14.4143 5.85999 15.0143 5.20555H15.013ZM8.10522 1.50457C9.10957 0.917424 11.327 1.75526 13.667 3.8571C13.817 3.99168 13.967 4.13154 14.117 4.27668C13.1958 5.28532 12.3417 6.35464 11.5604 7.4776C10.2145 7.59982 8.87779 7.81005 7.5587 8.10696C7.48217 7.79734 7.41348 7.48508 7.35261 7.17017V7.17281C6.78783 4.25689 7.16217 2.05873 8.10522 1.50721V1.50457ZM6.63913 17.3944C6.3913 17.3223 6.14522 17.2444 5.90087 17.1608C4.62965 16.7563 3.44292 16.1185 2.4 15.2794C2.10155 15.0718 1.84836 14.8046 1.6561 14.4941C1.46383 14.1836 1.33656 13.8366 1.28217 13.4744C1.28217 12.37 2.91 10.9609 5.62565 10.0043C5.96739 9.88291 6.31174 9.77296 6.6587 9.67444C7.06165 10.9828 7.54967 12.2628 8.11957 13.506C7.54246 14.7674 7.04834 16.0659 6.64044 17.3931L6.63913 17.3944ZM13.5861 23.3054C12.6048 24.2159 11.4663 24.936 10.2274 25.4297C9.90105 25.5879 9.5461 25.6769 9.18447 25.6909C8.82283 25.705 8.46218 25.6439 8.12478 25.5115C7.17913 24.9573 6.78522 22.8278 7.3213 19.9699C7.38565 19.6313 7.4587 19.2952 7.54044 18.9619C8.87349 19.2512 10.2244 19.4487 11.5839 19.553C12.3715 20.682 13.2317 21.7574 14.1587 22.7724C13.9726 22.9544 13.7822 23.1317 13.5874 23.3041L13.5861 23.3054ZM15.0496 21.8382C14.4417 21.1759 13.8365 20.4423 13.2443 19.6532C13.8183 19.6761 14.4035 19.6875 15 19.6875C15.6113 19.6875 16.2148 19.6744 16.8104 19.648C16.2608 20.408 15.6731 21.139 15.0496 21.8382ZM22.8326 23.6419C22.805 24.0072 22.7036 24.3629 22.5346 24.6869C22.3657 25.011 22.1328 25.2966 21.8504 25.526C20.9048 26.0802 18.883 25.3597 16.7022 23.4624C16.4526 23.246 16.2013 23.0147 15.9483 22.7684C16.8575 21.7503 17.6976 20.6712 18.463 19.5385C19.8301 19.4218 21.1877 19.2102 22.5261 18.9051C22.587 19.1549 22.6409 19.3995 22.6878 19.6387C22.9781 20.9533 23.0268 22.3108 22.8313 23.6432L22.8326 23.6419ZM23.9204 17.1674C23.7548 17.2229 23.5865 17.2756 23.4143 17.3258C22.9951 16.0112 22.4878 14.7271 21.8961 13.4823C22.466 12.2544 22.954 10.9893 23.357 9.69555C23.6622 9.78395 23.9609 9.88027 24.2478 9.97923C27.0261 10.945 28.7191 12.3753 28.7191 13.4757C28.7191 14.65 26.8891 16.1726 23.9191 17.1674H23.9204Z"
fill="#61DAFB"
/>
<path
d="M15 10.7652C15.53 10.7652 16.0481 10.9242 16.4887 11.2221C16.9293 11.52 17.2727 11.9434 17.4754 12.4388C17.6781 12.9341 17.731 13.4792 17.6275 14.0049C17.5239 14.5307 17.2684 15.0136 16.8935 15.3925C16.5186 15.7714 16.0409 16.0293 15.5211 16.1336C15.0012 16.2379 14.4624 16.1838 13.9729 15.9783C13.4834 15.7728 13.0652 15.425 12.7711 14.979C12.477 14.533 12.3204 14.0087 12.3209 13.4726C12.3219 12.7545 12.6046 12.0662 13.1069 11.5589C13.6093 11.0515 14.2901 10.7665 15 10.7665"
fill="#61DAFB"
/>
</g>
<defs>
<clipPath id="clip0">
<rect width="30" height="27" fill="white" />
</clipPath>
</defs>
</svg>
}
>
React
</HighlightedTextIcon>
. Saya juga seorang mahasiswa di Universitas AMIKOM Yogyakarta.
</>
);
const about: AboutSectionContentProps = {
text: <Text />,
};
export default about;
================================================
FILE: src/localization/id/header.ts
================================================
import { HeaderTextProps } from 'components/organisms/Header/types';
const header: HeaderTextProps = {
headerText: {
greetings: 'Halo',
intro: 'Saya',
preTitle: '',
},
};
export default header;
================================================
FILE: src/localization/id/index.ts
================================================
import header from 'localization/id/header';
import about from 'localization/id/about';
import sections from 'localization/id/sections';
import skills from 'localization/id/skills';
export { header, about, sections, skills };
================================================
FILE: src/localization/id/sections.ts
================================================
const section = {
about: 'Tentang saya',
projects: 'Proyek',
blogs: 'Blog',
skills: 'Kemampuan',
contact: 'Hubungi saya 🤙',
};
export default section;
================================================
FILE: src/localization/id/skills.tsx
================================================
import React from 'react';
import { Icon } from '@iconify/react';
import phpIcon from '@iconify/icons-logos/php';
import javascriptIcon from '@iconify/icons-logos/javascript';
import { HighlightedTextIcon } from 'components/molecules';
const Text = () => (
<>
Saya sudah berpengalaman selama dua tahun mendalami{' '}
<span className="font-bold text-gray-800">pengembangan website</span> menggunakan{' '}
<HighlightedTextIcon
as="a"
href="https://php.net"
className="mx-2"
icon={<Icon icon={phpIcon} style={{ fontSize: '24px' }} />}
>
PHP
</HighlightedTextIcon>{' '}
dan{' '}
<HighlightedTextIcon
className="mx-2"
icon={<Icon icon={javascriptIcon} style={{ fontSize: '24px' }} />}
>
Javascript
</HighlightedTextIcon>{' '}
hingga saya cukup percaya diri untuk membangun aplikasi menggunakan:
</>
);
const skills = {
text: <Text />,
};
export default skills;
================================================
FILE: src/pages/Home/index.tsx
================================================
import { Landing } from 'components/templates';
import LanguageContext from 'contexts/language';
import {
header as headerData,
projects as projectsData,
blogs as blogsData,
skills as skillsData,
contact as contactData
} from 'data';
import {
sections as sectionsEn,
header as headerEn,
about as aboutEn,
skills as skillsEn,
} from 'localization/en';
import {
sections as sectionsId,
header as headerId,
about as aboutId,
skills as skillsId,
} from 'localization/id';
import React, { useContext } from 'react';
export default function Home() {
const languange = useContext(LanguageContext);
let locale;
if (languange.value === 'en') {
locale = {
sections: sectionsEn,
header: headerEn,
about: aboutEn,
skills: skillsEn,
};
} else {
locale = {
sections: sectionsId,
header: headerId,
about: aboutId,
skills: skillsId,
};
}
return (
<Landing
sections={locale.sections}
data={{
header: {
data: headerData,
text: locale.header,
},
about: {
text: locale.about.text,
},
projects: {
items: projectsData,
},
blogs: {
items: blogsData,
},
skills: {
text: locale.skills.text,
items: skillsData,
},
contact: contactData
}}
locale={{ value: languange.value, change: languange.change }}
/>
);
}
================================================
FILE: src/react-app-env.d.ts
================================================
/// <reference types="react-scripts" />
================================================
FILE: src/reportWebVitals.ts
================================================
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
}
export default reportWebVitals;
================================================
FILE: src/setupTests.ts
================================================
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
================================================
FILE: tailwind.config.js
================================================
const colors = require('tailwindcss/colors');
const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = {
purge: ['./src/**/*.tsx', './public/**/*.html'],
darkMode: false, // or 'media' or 'class'
theme: {
colors: {
indigo: colors.indigo,
gray: colors.coolGray,
yellow: colors.yellow,
white: colors.white,
transparent: 'transparent',
},
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
},
extend: {
spacing: {
'full-2': 'calc(100% + 0.5rem)',
},
lineHeight: {
'extra-loose': '2.5',
},
zIndex: {
'-10': '-10',
},
scale: {
'999': '99'
}
},
},
variants: {
extend: {
scale: ['focus-within'],
width: ['group-hover', 'group-focus'],
translate: ['group-hover'],
},
},
plugins: [],
};
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"baseUrl": "./src"
},
"include": ["src"]
}
gitextract_khp8_3ci/ ├── .gitignore ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.md ├── package.json ├── postcss.config.js ├── public/ │ ├── index.html │ ├── manifest.json │ └── robots.txt ├── src/ │ ├── App.test.tsx │ ├── App.tsx │ ├── assets/ │ │ └── css/ │ │ └── styles.css │ ├── components/ │ │ ├── animations/ │ │ │ ├── FadeIn.tsx │ │ │ ├── FromDirection.tsx │ │ │ ├── Highlighter.tsx │ │ │ ├── Letter.tsx │ │ │ ├── Reveal.tsx │ │ │ ├── StaggerChildren.tsx │ │ │ └── index.tsx │ │ ├── atoms/ │ │ │ ├── Avatar/ │ │ │ │ └── index.tsx │ │ │ ├── Container/ │ │ │ │ └── index.tsx │ │ │ ├── ContentText/ │ │ │ │ └── index.tsx │ │ │ ├── ContentTitle/ │ │ │ │ └── index.tsx │ │ │ ├── FullRoundButton/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── HeaderText/ │ │ │ │ └── index.tsx │ │ │ ├── HighlightedText/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.tsx │ │ │ ├── MenuItem/ │ │ │ │ └── index.tsx │ │ │ ├── Overlay/ │ │ │ │ └── index.tsx │ │ │ ├── RoundedButton/ │ │ │ │ └── index.tsx │ │ │ ├── TextBox/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── TextButton/ │ │ │ │ └── index.tsx │ │ │ ├── TextSelectOption/ │ │ │ │ └── index.tsx │ │ │ ├── TitleLine/ │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ ├── decorations/ │ │ │ ├── Bullets.tsx │ │ │ ├── Stripes.tsx │ │ │ └── index.tsx │ │ ├── icons/ │ │ │ └── index.tsx │ │ ├── molecules/ │ │ │ ├── BlogPost/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── HighlightedTextIcon/ │ │ │ │ └── index.tsx │ │ │ ├── MainMenu/ │ │ │ │ └── index.tsx │ │ │ ├── MenuButton/ │ │ │ │ └── index.tsx │ │ │ ├── ProjectCard/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── SearchBox/ │ │ │ │ └── index.tsx │ │ │ ├── SectionTitle/ │ │ │ │ └── index.tsx │ │ │ ├── TextIconButton/ │ │ │ │ └── index.tsx │ │ │ ├── TextSelect/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.tsx │ │ │ └── index.tsx │ │ ├── organisms/ │ │ │ ├── About/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── Blogs/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── Contact/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── Header/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── Navigation/ │ │ │ │ └── index.tsx │ │ │ ├── Projects/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── Skills/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ └── index.tsx │ │ └── templates/ │ │ ├── Landing/ │ │ │ └── index.tsx │ │ └── index.tsx │ ├── contexts/ │ │ ├── index.ts │ │ └── language.ts │ ├── data/ │ │ ├── blogs.ts │ │ ├── contact.ts │ │ ├── header.ts │ │ ├── index.ts │ │ ├── projects.ts │ │ └── skills.tsx │ ├── index.tsx │ ├── localization/ │ │ ├── en/ │ │ │ ├── about.tsx │ │ │ ├── header.ts │ │ │ ├── index.ts │ │ │ ├── sections.ts │ │ │ └── skills.tsx │ │ └── id/ │ │ ├── about.tsx │ │ ├── header.ts │ │ ├── index.ts │ │ ├── sections.ts │ │ └── skills.tsx │ ├── pages/ │ │ └── Home/ │ │ └── index.tsx │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ └── setupTests.ts ├── tailwind.config.js └── tsconfig.json
SYMBOL INDEX (83 symbols across 49 files)
FILE: src/App.tsx
function App (line 7) | function App() {
FILE: src/components/animations/FadeIn.tsx
type Props (line 4) | interface Props extends HTMLMotionProps<'div'> {
function FadeIn (line 9) | function FadeIn({ children, delay = 0, duration = 0.5, animate, ...props...
FILE: src/components/animations/FromDirection.tsx
type Props (line 6) | interface Props extends HTMLMotionProps<'div'> {
function FromDirection (line 21) | function FromDirection({
FILE: src/components/animations/Highlighter.tsx
type Props (line 5) | interface Props extends SVGMotionProps<SVGElement> {
function Highlighter (line 10) | function Highlighter({ className, rad, delay = 0, ...props }: Props) {
FILE: src/components/animations/Letter.tsx
type Props (line 4) | interface Props extends HTMLMotionProps<'div'> {
function Letter (line 10) | function Letter({ text, delay = 0, duration = 0.05, ...props }: Props) {
FILE: src/components/animations/Reveal.tsx
type Props (line 5) | interface Props extends HTMLMotionProps<'div'> {
function Reveal (line 11) | function Reveal({
FILE: src/components/animations/StaggerChildren.tsx
type Props (line 4) | interface Props extends HTMLMotionProps<'div'> {
function StaggerChildren (line 9) | function StaggerChildren({ children, duration = 0.5, delay = 0, ...props...
FILE: src/components/atoms/Avatar/index.tsx
type Props (line 4) | interface Props {
function Avatar (line 10) | function Avatar({ src, alt, className }: Props) {
FILE: src/components/atoms/Container/index.tsx
type Props (line 3) | interface Props {
function Container (line 9) | function Container({ children, className, fluid }: Props) {
FILE: src/components/atoms/ContentText/index.tsx
type Props (line 4) | interface Props {
function ContentText (line 9) | function ContentText({ children, className }: Props) {
FILE: src/components/atoms/ContentTitle/index.tsx
type Props (line 4) | interface Props {
function ContentTitle (line 9) | function ContentTitle({ children, className }: Props) {
FILE: src/components/atoms/FullRoundButton/index.tsx
function FullRoundButton (line 8) | function FullRoundButton({
FILE: src/components/atoms/FullRoundButton/types.ts
type FullRoundButtonProps (line 3) | interface FullRoundButtonProps {
FILE: src/components/atoms/HeaderText/index.tsx
type Props (line 4) | interface Props {
function HeaderText (line 10) | function HeaderText({ children, className, tag = 'div' }: Props) {
FILE: src/components/atoms/HighlightedText/index.tsx
function HighlightedText (line 5) | function HighlightedText({
FILE: src/components/atoms/HighlightedText/types.tsx
type HighlightedTextProps (line 3) | interface HighlightedTextProps {
FILE: src/components/atoms/MenuItem/index.tsx
type Props (line 5) | interface Props {
function MenuItem (line 10) | function MenuItem({ children, active }: Props) {
FILE: src/components/atoms/Overlay/index.tsx
type Props (line 6) | interface Props {
function Overlay (line 16) | function Overlay({
FILE: src/components/atoms/RoundedButton/index.tsx
type Props (line 5) | interface Props {
function RoundedButton (line 12) | function RoundedButton({ children, as = 'button', href = '', className }...
FILE: src/components/atoms/TextBox/index.tsx
function TextBox (line 5) | function TextBox({ placeholder, className, value, onChange }: TextBoxPro...
FILE: src/components/atoms/TextBox/types.ts
type TextBoxProps (line 1) | interface TextBoxProps {
FILE: src/components/atoms/TextButton/index.tsx
type Props (line 5) | interface Props {
function TextButton (line 11) | function TextButton({ children, className, onClick }: Props) {
FILE: src/components/atoms/TextSelectOption/index.tsx
type Props (line 5) | interface Props {
function TextSelectOption (line 12) | function TextSelectOption({ children, active, selected, onClick }: Props) {
FILE: src/components/atoms/TitleLine/index.tsx
function TitleLine (line 5) | function TitleLine({ className, ...props }: SVGMotionProps<SVGElement>) {
FILE: src/components/decorations/Bullets.tsx
function Bullets (line 3) | function Bullets({ className }: { className?: string }) {
FILE: src/components/decorations/Stripes.tsx
function Stripes (line 3) | function Stripes({ className }: { className?: string }) {
FILE: src/components/molecules/BlogPost/index.tsx
function BlogPost (line 7) | function BlogPost({ data, className }: BlogsProps) {
FILE: src/components/molecules/BlogPost/types.ts
type BlogsProps (line 1) | interface BlogsProps {
type BlogsPostProps (line 6) | interface BlogsPostProps {
FILE: src/components/molecules/HighlightedTextIcon/index.tsx
type Props (line 6) | interface Props extends HighlightedTextProps {
function HighlightedTextIcon (line 10) | function HighlightedTextIcon({ icon, children, className, ...props }: Pr...
FILE: src/components/molecules/MainMenu/index.tsx
type Props (line 11) | interface Props {
function MainMenu (line 16) | function MainMenu({ animationFinish, sections }: Props) {
FILE: src/components/molecules/MenuButton/index.tsx
type Props (line 6) | interface Props extends FullRoundButtonProps {
function MenuButton (line 10) | function MenuButton({ isOpen = false, onClick, ...props }: Props) {
FILE: src/components/molecules/ProjectCard/index.tsx
function ProjectCard (line 8) | function ProjectCard({ data, className }: ProjectCardProps) {
FILE: src/components/molecules/ProjectCard/types.ts
type ProjectCardProps (line 1) | interface ProjectCardProps {
type ProjectCardDataProps (line 6) | interface ProjectCardDataProps {
FILE: src/components/molecules/SearchBox/index.tsx
function SearchBox (line 7) | function SearchBox({ className }: TextBoxProps) {
FILE: src/components/molecules/SectionTitle/index.tsx
type Props (line 6) | interface Props {
type SectionVariants (line 12) | enum SectionVariants {
function SectionTitle (line 17) | function SectionTitle({
FILE: src/components/molecules/TextIconButton/index.tsx
type Props (line 5) | interface Props {
function TextIconButton (line 12) | function TextIconButton({ text, icon, className, onClick }: Props) {
FILE: src/components/molecules/TextSelect/index.tsx
function TextSelect (line 7) | function TextSelect({ options, state, className }: TextSelectProps) {
FILE: src/components/molecules/TextSelect/types.tsx
type TextSelectProps (line 1) | interface TextSelectProps {
type TextSelectOptionProps (line 7) | interface TextSelectOptionProps {
FILE: src/components/organisms/About/types.ts
type AboutSectionProps (line 3) | interface AboutSectionProps {
type AboutSectionContentProps (line 9) | interface AboutSectionContentProps {
FILE: src/components/organisms/Blogs/types.ts
type BlogsSectionProps (line 3) | interface BlogsSectionProps {
type BlogsSectionContentProps (line 9) | interface BlogsSectionContentProps {
FILE: src/components/organisms/Contact/types.ts
type ContactSectionProps (line 1) | interface ContactSectionProps {
type ContactSectionContentProps (line 6) | interface ContactSectionContentProps {
FILE: src/components/organisms/Header/index.tsx
function Header (line 9) | function Header({ data, text, lowerSectionRef }: HeaderProps) {
FILE: src/components/organisms/Header/types.ts
type HeaderProps (line 3) | interface HeaderProps {
type HeaderDataProps (line 9) | interface HeaderDataProps {
type HeaderTextProps (line 15) | interface HeaderTextProps {
FILE: src/components/organisms/Navigation/index.tsx
type Props (line 10) | interface Props {
function Navigation (line 16) | function Navigation({ locale, sections, className }: Props) {
FILE: src/components/organisms/Projects/types.ts
type ProjectsSectionProps (line 3) | interface ProjectsSectionProps {
type ProjectsSectionContentProps (line 9) | interface ProjectsSectionContentProps {
FILE: src/components/organisms/Skills/types.ts
type SkillsSectionProps (line 3) | interface SkillsSectionProps {
type SkillsSectionContentProps (line 9) | interface SkillsSectionContentProps {
FILE: src/components/templates/Landing/index.tsx
type Props (line 13) | interface Props {
function Landing (line 26) | function Landing({ data, locale, sections }: Props) {
FILE: src/contexts/language.ts
type LocaleProps (line 3) | interface LocaleProps {
FILE: src/pages/Home/index.tsx
function Home (line 24) | function Home() {
Condensed preview — 95 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (114K chars).
[
{
"path": ".gitignore",
"chars": 335,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": ".vscode/settings.json",
"chars": 56,
"preview": "{\n \"typescript.tsdk\": \"node_modules\\\\typescript\\\\lib\"\n}"
},
{
"path": "LICENSE",
"chars": 1081,
"preview": "MIT License\n\nCopyright (c) 2020 Muhammad Faqih Muntashir\n\nPermission is hereby granted, free of charge, to any person ob"
},
{
"path": "README.md",
"chars": 2620,
"preview": "# Fama - Portfolio Website Template\n\nTailwindCSS based personal branding template. Built with react and framer-motion.\n\n"
},
{
"path": "package.json",
"chars": 1787,
"preview": "{\n \"name\": \"personal-branding-template\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"dependencies\": {\n \"@headlessui/"
},
{
"path": "postcss.config.js",
"chars": 111,
"preview": "module.exports = {\n plugins: [require('postcss-import'), require('tailwindcss'), require('autoprefixer')],\n};\n"
},
{
"path": "public/index.html",
"chars": 1819,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "public/manifest.json",
"chars": 205,
"preview": "{\n \"short_name\": \"itsfaqih Portfolio\",\n \"name\": \"Faqih Muntashir's Portfolio\",\n \"icons\": [],\n \"start_url\": \".\",\n \"d"
},
{
"path": "public/robots.txt",
"chars": 67,
"preview": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
},
{
"path": "src/App.test.tsx",
"chars": 273,
"preview": "import React from 'react';\nimport { render, screen } from '@testing-library/react';\nimport App from './App';\n\ntest('rend"
},
{
"path": "src/App.tsx",
"chars": 486,
"preview": "import React from 'react';\nimport 'assets/css/app.css';\nimport Home from 'pages/Home';\nimport { LanguageContext } from '"
},
{
"path": "src/assets/css/styles.css",
"chars": 260,
"preview": "@import 'tailwindcss/base';\n@import 'tailwindcss/components';\n@import 'tailwindcss/utilities';\n\n@layer utilities {\n @va"
},
{
"path": "src/components/animations/FadeIn.tsx",
"chars": 639,
"preview": "import { HTMLMotionProps, motion, Variants } from 'framer-motion';\nimport React from 'react';\n\ninterface Props extends H"
},
{
"path": "src/components/animations/FromDirection.tsx",
"chars": 1692,
"preview": "import React, { useState } from 'react';\nimport { motion, HTMLMotionProps, Variants } from 'framer-motion';\nimport class"
},
{
"path": "src/components/animations/Highlighter.tsx",
"chars": 882,
"preview": "import React from 'react';\nimport { motion, Variants, SVGMotionProps } from 'framer-motion';\nimport classNames from 'cla"
},
{
"path": "src/components/animations/Letter.tsx",
"chars": 1070,
"preview": "import React from 'react';\nimport { motion, Variants, HTMLMotionProps } from 'framer-motion';\n\ninterface Props extends H"
},
{
"path": "src/components/animations/Reveal.tsx",
"chars": 1277,
"preview": "import React, { useState } from 'react';\nimport { motion, HTMLMotionProps, Variants } from 'framer-motion';\nimport class"
},
{
"path": "src/components/animations/StaggerChildren.tsx",
"chars": 673,
"preview": "import { HTMLMotionProps, motion, Variants } from 'framer-motion';\nimport React from 'react';\n\ninterface Props extends H"
},
{
"path": "src/components/animations/index.tsx",
"chars": 465,
"preview": "import Letter from 'components/animations/Letter';\nimport Highlighter from 'components/animations/Highlighter';\nimport F"
},
{
"path": "src/components/atoms/Avatar/index.tsx",
"chars": 463,
"preview": "import React from 'react';\nimport classNames from 'classnames';\n\ninterface Props {\n src: string;\n alt: string;\n class"
},
{
"path": "src/components/atoms/Container/index.tsx",
"chars": 504,
"preview": "import React from 'react';\nimport classNames from 'classnames';\ninterface Props {\n children?: React.ReactNode;\n classN"
},
{
"path": "src/components/atoms/ContentText/index.tsx",
"chars": 351,
"preview": "import React, { ReactNode } from 'react';\nimport classNames from 'classnames';\n\ninterface Props {\n children: ReactNode;"
},
{
"path": "src/components/atoms/ContentTitle/index.tsx",
"chars": 318,
"preview": "import React, { ReactNode } from 'react';\nimport classNames from 'classnames';\n\ninterface Props {\n children: ReactNode;"
},
{
"path": "src/components/atoms/FullRoundButton/index.tsx",
"chars": 1595,
"preview": "import React from 'react';\nimport classNames from 'classnames';\nimport { motion } from 'framer-motion';\nimport FullRound"
},
{
"path": "src/components/atoms/FullRoundButton/types.ts",
"chars": 275,
"preview": "import { ReactNode } from 'react';\n\nexport default interface FullRoundButtonProps {\n children?: ReactNode;\n label?: st"
},
{
"path": "src/components/atoms/HeaderText/index.tsx",
"chars": 565,
"preview": "import React, { ReactNode } from 'react';\nimport classNames from 'classnames';\n\ninterface Props {\n children: ReactNode;"
},
{
"path": "src/components/atoms/HighlightedText/index.tsx",
"chars": 583,
"preview": "import React from 'react';\nimport classNames from 'classnames';\nimport HighlightedTextProps from 'components/atoms/Highl"
},
{
"path": "src/components/atoms/HighlightedText/types.tsx",
"chars": 168,
"preview": "import { ReactNode } from \"react\";\n\nexport default interface HighlightedTextProps {\n children: ReactNode;\n as?: 'a' | "
},
{
"path": "src/components/atoms/MenuItem/index.tsx",
"chars": 592,
"preview": "import React from 'react';\nimport { ReactNode } from 'react';\nimport classNames from 'classnames';\n\ninterface Props {\n "
},
{
"path": "src/components/atoms/Overlay/index.tsx",
"chars": 1067,
"preview": "import React from 'react';\nimport classNames from 'classnames';\nimport { disableBodyScroll, clearAllBodyScrollLocks } fr"
},
{
"path": "src/components/atoms/RoundedButton/index.tsx",
"chars": 996,
"preview": "import React, { ReactNode } from 'react';\nimport classNames from 'classnames';\nimport { motion } from 'framer-motion';\n\n"
},
{
"path": "src/components/atoms/TextBox/index.tsx",
"chars": 435,
"preview": "import React from 'react';\nimport classNames from 'classnames';\nimport TextBoxProps from 'components/atoms/TextBox/types"
},
{
"path": "src/components/atoms/TextBox/types.ts",
"chars": 140,
"preview": "export default interface TextBoxProps {\n placeholder?: string;\n className?: string;\n value?: string;\n onChange?: (pa"
},
{
"path": "src/components/atoms/TextButton/index.tsx",
"chars": 548,
"preview": "import React from 'react';\nimport classNames from 'classnames';\nimport { ReactNode } from 'react';\n\ninterface Props {\n "
},
{
"path": "src/components/atoms/TextSelectOption/index.tsx",
"chars": 682,
"preview": "import React, { ReactNode } from 'react';\nimport classNames from 'classnames';\nimport Icons from 'components/icons';\n\nin"
},
{
"path": "src/components/atoms/TitleLine/index.tsx",
"chars": 559,
"preview": "import React from 'react';\nimport { motion, SVGMotionProps, Variants } from 'framer-motion';\nimport classNames from 'cla"
},
{
"path": "src/components/atoms/index.tsx",
"chars": 988,
"preview": "import Avatar from 'components/atoms/Avatar';\nimport HeaderText from 'components/atoms/HeaderText';\nimport TextButton fr"
},
{
"path": "src/components/decorations/Bullets.tsx",
"chars": 9652,
"preview": "import React from 'react';\n\nexport default function Bullets({ className }: { className?: string }) {\n return (\n <svg"
},
{
"path": "src/components/decorations/Stripes.tsx",
"chars": 805,
"preview": "import React from 'react';\n\nexport default function Stripes({ className }: { className?: string }) {\n return (\n <svg"
},
{
"path": "src/components/decorations/index.tsx",
"chars": 184,
"preview": "import Bullets from 'components/decorations/Bullets';\nimport Stripes from 'components/decorations/Stripes';\n\nconst Decor"
},
{
"path": "src/components/icons/index.tsx",
"chars": 6877,
"preview": "import React from 'react';\n\nconst Dribbble = ({ className }: { className?: string }) => (\n <svg className={className} v"
},
{
"path": "src/components/molecules/BlogPost/index.tsx",
"chars": 1169,
"preview": "import { ContentTitle } from 'components/atoms';\nimport { LanguageContext } from 'contexts';\nimport React, { useContext "
},
{
"path": "src/components/molecules/BlogPost/types.ts",
"chars": 169,
"preview": "export default interface BlogsProps {\n data: BlogsPostProps;\n className?: string;\n}\n\nexport interface BlogsPostProps {"
},
{
"path": "src/components/molecules/HighlightedTextIcon/index.tsx",
"chars": 758,
"preview": "import { HighlightedText } from 'components/atoms';\nimport HighlightedTextProps from 'components/atoms/HighlightedText/t"
},
{
"path": "src/components/molecules/MainMenu/index.tsx",
"chars": 4655,
"preview": "import { Menu } from '@headlessui/react';\nimport { MenuButton } from 'components/molecules';\nimport React from 'react';\n"
},
{
"path": "src/components/molecules/MenuButton/index.tsx",
"chars": 557,
"preview": "import { FullRoundButton } from 'components/atoms';\nimport FullRoundButtonProps from 'components/atoms/FullRoundButton/t"
},
{
"path": "src/components/molecules/ProjectCard/index.tsx",
"chars": 1203,
"preview": "import { ContentTitle } from 'components/atoms';\nimport Icons from 'components/icons';\nimport { LanguageContext } from '"
},
{
"path": "src/components/molecules/ProjectCard/types.ts",
"chars": 226,
"preview": "export default interface ProjectCardProps {\n data: ProjectCardDataProps;\n className: string;\n}\n\nexport interface Proje"
},
{
"path": "src/components/molecules/SearchBox/index.tsx",
"chars": 1139,
"preview": "import { TextBox } from 'components/atoms';\nimport TextBoxProps from 'components/atoms/TextBox/types';\nimport React, { u"
},
{
"path": "src/components/molecules/SectionTitle/index.tsx",
"chars": 840,
"preview": "import { HeaderText, TitleLine } from 'components/atoms';\nimport React, { ReactNode } from 'react';\nimport classNames fr"
},
{
"path": "src/components/molecules/TextIconButton/index.tsx",
"chars": 578,
"preview": "import { TextButton } from 'components/atoms';\nimport React, { ReactElement } from 'react';\nimport classNames from 'clas"
},
{
"path": "src/components/molecules/TextSelect/index.tsx",
"chars": 2605,
"preview": "import React, { Fragment } from 'react';\nimport { Listbox, Transition } from '@headlessui/react';\nimport { TextSelectOpt"
},
{
"path": "src/components/molecules/TextSelect/types.tsx",
"chars": 244,
"preview": "export default interface TextSelectProps {\n options: TextSelectOptionProps[];\n state: [TextSelectOptionProps, (any: an"
},
{
"path": "src/components/molecules/index.tsx",
"chars": 619,
"preview": "import TextIconButton from 'components/molecules/TextIconButton';\nimport TextSelect from 'components/molecules/TextSelec"
},
{
"path": "src/components/organisms/About/index.tsx",
"chars": 2442,
"preview": "import Animated from 'components/animations';\nimport { ContentText, RoundedButton } from 'components/atoms';\nimport { Se"
},
{
"path": "src/components/organisms/About/types.ts",
"chars": 225,
"preview": "import { ReactNode } from 'react';\n\nexport default interface AboutSectionProps {\n title: string;\n content: AboutSectio"
},
{
"path": "src/components/organisms/Blogs/index.tsx",
"chars": 2501,
"preview": "import Animated from 'components/animations';\nimport { RoundedButton } from 'components/atoms';\nimport { SectionTitle, B"
},
{
"path": "src/components/organisms/Blogs/types.ts",
"chars": 268,
"preview": "import { BlogsPostProps } from 'components/molecules/BlogPost/types';\n\nexport default interface BlogsSectionProps {\n ti"
},
{
"path": "src/components/organisms/Contact/index.tsx",
"chars": 2914,
"preview": "import { FullRoundButton } from 'components/atoms';\nimport Icons from 'components/icons';\nimport React, { useEffect } fr"
},
{
"path": "src/components/organisms/Contact/types.ts",
"chars": 255,
"preview": "export default interface ContactSectionProps {\n title: string;\n content: ContactSectionContentProps;\n}\n\nexport interfa"
},
{
"path": "src/components/organisms/Header/index.tsx",
"chars": 3131,
"preview": "import Animated from 'components/animations';\nimport { Avatar, HeaderText } from 'components/atoms';\nimport Decorations "
},
{
"path": "src/components/organisms/Header/types.ts",
"chars": 415,
"preview": "import { MutableRefObject } from \"react\";\n\nexport default interface HeaderProps {\n data: HeaderDataProps;\n text: Heade"
},
{
"path": "src/components/organisms/Navigation/index.tsx",
"chars": 1795,
"preview": "import Animated from 'components/animations';\nimport { MainMenu, TextSelect } from 'components/molecules';\nimport { Text"
},
{
"path": "src/components/organisms/Projects/index.tsx",
"chars": 3352,
"preview": "import { SectionTitle } from 'components/molecules';\nimport React, { useContext, useEffect } from 'react';\nimport { Sect"
},
{
"path": "src/components/organisms/Projects/types.ts",
"chars": 292,
"preview": "import { ProjectCardDataProps } from 'components/molecules/ProjectCard/types';\n\nexport default interface ProjectsSection"
},
{
"path": "src/components/organisms/Skills/index.tsx",
"chars": 3119,
"preview": "import Animated from 'components/animations';\nimport { ContentText } from 'components/atoms';\nimport { SectionTitle } fr"
},
{
"path": "src/components/organisms/Skills/types.ts",
"chars": 250,
"preview": "import { ReactNode } from 'react';\n\nexport default interface SkillsSectionProps {\n title: string;\n content: SkillsSect"
},
{
"path": "src/components/organisms/index.tsx",
"chars": 433,
"preview": "import Header from 'components/organisms/Header';\nimport About from 'components/organisms/About';\nimport Projects from '"
},
{
"path": "src/components/templates/Landing/index.tsx",
"chars": 2674,
"preview": "import { Container } from 'components/atoms';\nimport { About, Blogs, Contact, Header, Navigation, Projects, Skills } fro"
},
{
"path": "src/components/templates/index.tsx",
"chars": 72,
"preview": "import Landing from 'components/templates/Landing';\n\nexport { Landing };"
},
{
"path": "src/contexts/index.ts",
"chars": 78,
"preview": "import LanguageContext from 'contexts/language';\n\nexport { LanguageContext };\n"
},
{
"path": "src/contexts/language.ts",
"chars": 262,
"preview": "import { createContext } from 'react';\n\nexport interface LocaleProps {\n value: 'en' | 'id';\n change: (lang: 'en' | 'id"
},
{
"path": "src/data/blogs.ts",
"chars": 886,
"preview": "import { BlogsPostProps } from 'components/molecules/BlogPost/types';\n\nconst blogs: BlogsPostProps[] = [\n {\n title: "
},
{
"path": "src/data/contact.ts",
"chars": 347,
"preview": "import { ContactSectionContentProps } from 'components/organisms/Contact/types';\n\nconst contact: ContactSectionContentPr"
},
{
"path": "src/data/header.ts",
"chars": 226,
"preview": "import { HeaderDataProps } from 'components/organisms/Header/types';\n\nconst header: HeaderDataProps = {\n name: 'Faqih M"
},
{
"path": "src/data/index.ts",
"chars": 228,
"preview": "import header from 'data/header';\nimport projects from 'data/projects';\nimport blogs from 'data/blogs';\nimport skills fr"
},
{
"path": "src/data/projects.ts",
"chars": 1324,
"preview": "import { ProjectCardDataProps } from \"components/molecules/ProjectCard/types\";\n\nconst projects: ProjectCardDataProps[] ="
},
{
"path": "src/data/skills.tsx",
"chars": 1415,
"preview": "import React from 'react';\nimport { Icon } from '@iconify/react';\nimport reactIcon from '@iconify/icons-logos/react';\nim"
},
{
"path": "src/index.tsx",
"chars": 478,
"preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\nimport reportWebVitals from './rep"
},
{
"path": "src/localization/en/about.tsx",
"chars": 1135,
"preview": "import React from 'react';\nimport { Icon } from '@iconify/react';\nimport { HighlightedTextIcon } from 'components/molecu"
},
{
"path": "src/localization/en/header.ts",
"chars": 212,
"preview": "import { HeaderTextProps } from 'components/organisms/Header/types';\n\nconst header: HeaderTextProps = {\n headerText: {\n"
},
{
"path": "src/localization/en/index.ts",
"chars": 227,
"preview": "import header from 'localization/en/header';\nimport about from 'localization/en/about';\nimport sections from 'localizati"
},
{
"path": "src/localization/en/sections.ts",
"chars": 158,
"preview": "const section = {\n about: 'About me',\n projects: 'Projects',\n blogs: 'Blogs',\n skills: 'Skills',\n contact: 'Get in "
},
{
"path": "src/localization/en/skills.tsx",
"chars": 869,
"preview": "import React from 'react';\nimport { Icon } from '@iconify/react';\nimport phpIcon from '@iconify/icons-logos/php';\nimport"
},
{
"path": "src/localization/id/about.tsx",
"chars": 8003,
"preview": "import React from 'react';\nimport { HighlightedTextIcon } from 'components/molecules';\nimport { AboutSectionContentProps"
},
{
"path": "src/localization/id/header.ts",
"chars": 212,
"preview": "import { HeaderTextProps } from 'components/organisms/Header/types';\n\nconst header: HeaderTextProps = {\n headerText: {\n"
},
{
"path": "src/localization/id/index.ts",
"chars": 227,
"preview": "import header from 'localization/id/header';\nimport about from 'localization/id/about';\nimport sections from 'localizati"
},
{
"path": "src/localization/id/sections.ts",
"chars": 162,
"preview": "const section = {\n about: 'Tentang saya',\n projects: 'Proyek',\n blogs: 'Blog',\n skills: 'Kemampuan',\n contact: 'Hub"
},
{
"path": "src/localization/id/skills.tsx",
"chars": 953,
"preview": "import React from 'react';\nimport { Icon } from '@iconify/react';\nimport phpIcon from '@iconify/icons-logos/php';\nimport"
},
{
"path": "src/pages/Home/index.tsx",
"chars": 1480,
"preview": "import { Landing } from 'components/templates';\nimport LanguageContext from 'contexts/language';\nimport {\n header as he"
},
{
"path": "src/react-app-env.d.ts",
"chars": 40,
"preview": "/// <reference types=\"react-scripts\" />\n"
},
{
"path": "src/reportWebVitals.ts",
"chars": 426,
"preview": "import { ReportHandler } from 'web-vitals';\n\nconst reportWebVitals = (onPerfEntry?: ReportHandler) => {\n if (onPerfEntr"
},
{
"path": "src/setupTests.ts",
"chars": 241,
"preview": "// jest-dom adds custom jest matchers for asserting on DOM nodes.\n// allows you to do things like:\n// expect(element).to"
},
{
"path": "tailwind.config.js",
"chars": 894,
"preview": "const colors = require('tailwindcss/colors');\nconst defaultTheme = require('tailwindcss/defaultTheme');\n\nmodule.exports "
},
{
"path": "tsconfig.json",
"chars": 523,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"sk"
}
]
About this extraction
This page contains the full source code of the itsfaqih/fama GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 95 files (101.6 KB), approximately 35.7k tokens, and a symbol index with 83 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.