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
================================================
Faqih Muntashir's Portfolio
================================================
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();
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 (
);
}
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 (
{children}
);
}
================================================
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('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(null);
return (
{
if (onAnimationComplete) {
onAnimationComplete();
}
setOverflow(null);
if (ref.current !== null) {
ref.current.style.transform = '';
}
}}
className={innerClassName}
ref={ref}
{...props}
>
{children}
);
}
================================================
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 {
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 (
);
}
================================================
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 (
{letters.map((letter, index) => (
{letter === ' ' ? '\u00A0' : letter}
))}
);
}
================================================
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 (
{children}
setAnimatedFinish(true)}
/>
);
}
================================================
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 (
{children}
);
}
================================================
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 (
);
}
================================================
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 (
{children}
);
}
================================================
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 (
{children}
);
}
================================================
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 {children}
;
}
================================================
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 (
{children}
{label}
);
}
return (
{children}
{label}
);
}
================================================
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 (
{children}
);
}
return (
{children}
);
}
================================================
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 (
{children}
);
}
return (
{children}
);
}
================================================
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 (
{children}
);
}
================================================
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;
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 (
);
}
================================================
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 (
{children}
);
}
return (
{children}
);
}
================================================
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 (
);
}
================================================
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 (
);
}
================================================
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 (
{children}
{selected && }
);
}
================================================
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) {
const variants: Variants = {
show: {
width: '100%',
},
hidden: {
width: 0,
},
};
return (
);
}
================================================
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 (
);
}
================================================
FILE: src/components/decorations/Stripes.tsx
================================================
import React from 'react';
export default function Stripes({ className }: { className?: string }) {
return (
);
}
================================================
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 }) => (
);
const Github = ({ className }: { className?: string }) => (
);
const Facebook = ({ className }: { className?: string }) => (
);
const Twitter = ({ className }: { className?: string }) => (
);
const Instagram = ({ className }: { className?: string }) => (
);
const CheckRounded = ({ className }: { className?: string }) => (
);
const Menu = ({ className }: { className?: string }) => (
);
const Close = ({ className }: { className?: string }) => (
);
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 (
{title}
{formattedDate}
);
}
================================================
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 (
{Icon}
{children}
);
}
================================================
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(null);
return (
);
}
================================================
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 (
{isOpen ? : }
);
}
================================================
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 (
{title}
{description}
);
}
================================================
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 (
);
}
================================================
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;
}
export enum SectionVariants {
LEFT = 'left',
CENTER = 'center',
}
export default function SectionTitle({
children,
variant = SectionVariants.LEFT,
lineProps,
}: Props) {
return (
{children}
);
}
================================================
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 (
{text}
{Icon}
);
}
================================================
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 (
{({ open }) => (
<>
{selectedOption.label}
{options.map(option => (
{({ active, selected }) => (
setSelectedOption(option)}
>
{option.label}
)}
))}
>
)}
);
}
================================================
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(
({ 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 (
{
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
{content.text}
{language.value === 'en' ? 'Download' : 'Unduh'} Resume
);
}
);
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(
({ 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 (
{
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
{language.value === 'en' ? 'More article' : 'Artikel lainnya'}
{items.map((item, index) => (
))}
);
}
);
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(
({ 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 (
);
}
);
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 (
lowerSectionRef?.current?.scrollIntoView({ behavior: 'smooth' })}
text={languange.value === 'en' ? 'Scroll down' : 'Scroll ke bawah'}
icon={
}
/>
);
}
================================================
FILE: src/components/organisms/Header/types.ts
================================================
import { MutableRefObject } from "react";
export default interface HeaderProps {
data: HeaderDataProps;
text: HeaderTextProps;
lowerSectionRef?: MutableRefObject;
}
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 (
);
}
================================================
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(
({ 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 (
{
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
{items.map((item, index) => (
))}
{language.value === 'en' ? 'See more' : 'Lihat Selengkapnya'}
);
}
);
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(
({ 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 (
{
refView(section);
if (ref) {
if (typeof ref === 'function') {
ref(section);
} else {
ref.current = section;
}
}
}}
>
{text}
{upperSkills.map((SkillItem, index) => (
{SkillItem}
))}
{lowerSkills.map((SkillItem, index) => (
{SkillItem}
))}
);
}
);
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 (
<>
>
);
}
================================================
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({
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 = [
,
,
,
,
,
,
,
];
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(
,
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{' '}
fullstack developer and{' '}
UI/UX designer. I enjoy building web apps using
}
>
TailwindCSS
{' '}
and{' '}
}
>
React
. I'm also an active student in University of AMIKOM Yogyakarta.
>
);
const about: AboutSectionContentProps = {
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{' '}
website development using{' '}
}
>
PHP
{' '}
and{' '}
}>
Javascript
{' '}
that I’m now pretty confident in building apps using these tools:
>
);
const skills = {
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{' '}
fullstack developer dan{' '}
UI/UX designer, saya sangat suka menggunakan
}
>
TailwindCSS
{' '}
dan{' '}
}
>
React
. Saya juga seorang mahasiswa di Universitas AMIKOM Yogyakarta.
>
);
const about: AboutSectionContentProps = {
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{' '}
pengembangan website menggunakan{' '}
}
>
PHP
{' '}
dan{' '}
}
>
Javascript
{' '}
hingga saya cukup percaya diri untuk membangun aplikasi menggunakan:
>
);
const skills = {
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 (
);
}
================================================
FILE: src/react-app-env.d.ts
================================================
///
================================================
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"]
}