├── src
├── index.css
├── vite-env.d.ts
├── App.tsx
├── main.tsx
└── examples
│ ├── transition-1
│ ├── styles.css
│ ├── Example.tsx
│ ├── image1.svg
│ ├── image2.svg
│ └── image3.svg
│ ├── transition-2
│ ├── styles.css
│ └── Example.tsx
│ └── transition-3
│ ├── styles.css
│ └── Example.tsx
├── vite.config.ts
├── tsconfig.node.json
├── .gitignore
├── index.html
├── .eslintrc.cjs
├── tsconfig.json
├── package.json
├── README.md
└── public
└── vite.svg
/src/index.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Example } from "./examples/transition-3/Example";
2 |
3 | function App() {
4 | return ;
5 | }
6 |
7 | export default App;
8 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:react-hooks/recommended',
8 | ],
9 | ignorePatterns: ['dist', '.eslintrc.cjs'],
10 | parser: '@typescript-eslint/parser',
11 | plugins: ['react-refresh'],
12 | rules: {
13 | 'react-refresh/only-export-components': [
14 | 'warn',
15 | { allowConstantExport: true },
16 | ],
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-transitions",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.2.15",
18 | "@types/react-dom": "^18.2.7",
19 | "@typescript-eslint/eslint-plugin": "^6.0.0",
20 | "@typescript-eslint/parser": "^6.0.0",
21 | "@vitejs/plugin-react": "^4.0.3",
22 | "eslint": "^8.45.0",
23 | "eslint-plugin-react-hooks": "^4.6.0",
24 | "eslint-plugin-react-refresh": "^0.4.3",
25 | "react-router-dom": "^6.16.0",
26 | "typescript": "^5.0.2",
27 | "vite": "^4.4.5"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React + TypeScript + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
10 | ## Expanding the ESLint configuration
11 |
12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13 |
14 | - Configure the top-level `parserOptions` property like this:
15 |
16 | ```js
17 | parserOptions: {
18 | ecmaVersion: 'latest',
19 | sourceType: 'module',
20 | project: ['./tsconfig.json', './tsconfig.node.json'],
21 | tsconfigRootDir: __dirname,
22 | },
23 | ```
24 |
25 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
26 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
27 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
28 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/examples/transition-1/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | height: 100vh;
4 | background: #f6f2f9;
5 | color: #222222;
6 | font-family: "Euclid Circular A", "Poppins";
7 | }
8 |
9 | h1,
10 | h2 {
11 | font-weight: 500;
12 | }
13 |
14 | h1 {
15 | font-size: 20px;
16 | }
17 |
18 | h2 {
19 | font-size: 20px;
20 | }
21 |
22 | p {
23 | position: relative;
24 | z-index: 0;
25 | opacity: 0.6;
26 | line-height: 1.6;
27 | }
28 |
29 | nav {
30 | position: fixed;
31 | top: 0;
32 | left: 0;
33 | right: 0;
34 | height: 72px;
35 | padding: 0 40px;
36 | display: flex;
37 | align-items: center;
38 | justify-content: space-between;
39 | background: #ffffff;
40 | }
41 |
42 | nav a {
43 | color: inherit;
44 | cursor: pointer;
45 | }
46 |
47 | nav a.active {
48 | color: #8f44fd;
49 | }
50 |
51 | nav ul {
52 | display: flex;
53 | gap: 20px;
54 | list-style-type: none;
55 | }
56 |
57 | main {
58 | padding: 92px 40px 20px;
59 | }
60 |
61 | main article {
62 | display: flex;
63 | align-items: center;
64 | gap: 50px;
65 | padding: 80px 60px;
66 | }
67 |
68 | main article img {
69 | width: 200px;
70 | height: 200px;
71 | object-fit: contain;
72 | }
73 |
74 | .bars {
75 | position: fixed;
76 | z-index: 1;
77 | top: 0;
78 | left: 0;
79 | right: 0;
80 | bottom: 0;
81 | display: grid;
82 | pointer-events: none;
83 | }
84 |
85 | @keyframes show {
86 | 0% {
87 | transform: translateX(-100%);
88 | }
89 | 100% {
90 | transform: translateX(0);
91 | }
92 | }
93 |
94 | @keyframes hide {
95 | 0% {
96 | transform: translateX(0);
97 | }
98 | 100% {
99 | transform: translateX(100%);
100 | }
101 | }
102 |
103 | .bars > div {
104 | background: #8f44fd;
105 | transform: translateX(-100%);
106 | }
107 |
108 | .bars.show > div {
109 | animation: show 0.2s both ease-in-out;
110 | }
111 |
112 | .bars.hide > div {
113 | animation: hide 0.2s both ease-in-out;
114 | }
115 |
--------------------------------------------------------------------------------
/src/examples/transition-2/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | height: 100vh;
4 | color: #222222;
5 | font-family: "Euclid Circular A", "Poppins";
6 | }
7 |
8 | h1,
9 | h2 {
10 | font-weight: 500;
11 | }
12 |
13 | h1 {
14 | font-size: 20px;
15 | }
16 |
17 | h2 {
18 | font-size: 20px;
19 | }
20 |
21 | p {
22 | position: relative;
23 | z-index: 0;
24 | opacity: 0.6;
25 | line-height: 1.6;
26 | }
27 |
28 | nav {
29 | position: fixed;
30 | top: 0;
31 | left: 0;
32 | right: 0;
33 | height: 122px;
34 | padding: 0 40px;
35 | display: flex;
36 | align-items: center;
37 | justify-content: space-between;
38 | }
39 |
40 | nav a {
41 | color: inherit;
42 | cursor: pointer;
43 | }
44 |
45 | nav a.active {
46 | color: #8f44fd;
47 | }
48 |
49 | nav ul {
50 | display: flex;
51 | gap: 20px;
52 | list-style-type: none;
53 | }
54 |
55 | #bubbles {
56 | --size: 250vw;
57 | }
58 |
59 | @media only screen and (min-width: 768px) {
60 | #bubbles {
61 | --size: 300vw;
62 | }
63 | }
64 |
65 | .bubbles__first,
66 | .bubbles__second {
67 | position: fixed;
68 | z-index: 9999;
69 | top: 0;
70 | left: 50%;
71 | translate: -50% 100%;
72 | width: var(--size);
73 | height: var(--size);
74 | border-radius: var(--size);
75 | animation-timing-function: ease-in-out;
76 | }
77 |
78 | #bubbles.show .bubbles__first {
79 | animation-name: bubble-move;
80 | }
81 |
82 | #bubbles.show .bubbles__second {
83 | animation-name: bubble-second-move;
84 | }
85 |
86 | @keyframes bubble-move {
87 | 20% {
88 | border-radius: var(--size);
89 | }
90 | 50%,
91 | 100% {
92 | translate: -50% 0;
93 | border-radius: 0;
94 | }
95 | }
96 |
97 | @keyframes bubble-second-move {
98 | 30% {
99 | translate: -50% 100%;
100 | }
101 | 50% {
102 | border-radius: var(--size);
103 | }
104 | 100% {
105 | translate: -50% 0;
106 | border-radius: 0;
107 | }
108 | }
109 |
110 | @keyframes appear {
111 | 0% {
112 | opacity: 0;
113 | translate: 0 100%;
114 | }
115 | 100% {
116 | opacity: 1;
117 | translate: 0;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/examples/transition-3/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | height: 100vh;
4 | color: #222222;
5 | font-family: "Euclid Circular A", "Poppins";
6 | }
7 |
8 | h1,
9 | h2 {
10 | font-weight: 500;
11 | }
12 |
13 | h1 {
14 | font-size: 20px;
15 | }
16 |
17 | h2 {
18 | font-size: 20px;
19 | }
20 |
21 | p {
22 | position: relative;
23 | z-index: 0;
24 | opacity: 0.6;
25 | line-height: 1.6;
26 | }
27 |
28 | section {
29 | height: 100vh;
30 | }
31 |
32 | nav {
33 | position: fixed;
34 | top: 0;
35 | left: 0;
36 | right: 0;
37 | height: 142px;
38 | padding: 0 60px;
39 | display: flex;
40 | align-items: center;
41 | justify-content: space-between;
42 | }
43 |
44 | nav a {
45 | color: inherit;
46 | cursor: pointer;
47 | font-weight: 400;
48 | }
49 |
50 | nav a.active {
51 | color: #8f44fd;
52 | }
53 |
54 | nav ul {
55 | display: flex;
56 | gap: 20px;
57 | list-style-type: none;
58 | }
59 |
60 | #bubbles {
61 | --size: 250vw;
62 | }
63 |
64 | @media only screen and (min-width: 768px) {
65 | #bubbles {
66 | --size: 300vw;
67 | }
68 | }
69 |
70 | .bubbles__first,
71 | .bubbles__second {
72 | position: fixed;
73 | z-index: 9999;
74 | top: 0;
75 | left: 50%;
76 | translate: -50% 100%;
77 | width: var(--size);
78 | height: var(--size);
79 | border-radius: var(--size);
80 | animation-timing-function: ease-in-out;
81 | }
82 |
83 | .bubbles__first {
84 | background: #8f44fd;
85 | }
86 |
87 | #bubbles.show .bubbles__first {
88 | animation-name: bubble-move;
89 | }
90 |
91 | #bubbles.show .bubbles__second {
92 | animation-name: bubble-second-move;
93 | }
94 |
95 | @keyframes bubble-move {
96 | 20% {
97 | border-radius: var(--size);
98 | }
99 | 50%,
100 | 100% {
101 | translate: -50% 0;
102 | border-radius: 0;
103 | }
104 | }
105 |
106 | @keyframes bubble-second-move {
107 | 30% {
108 | translate: -50% 100%;
109 | }
110 | 50% {
111 | border-radius: var(--size);
112 | }
113 | 100% {
114 | translate: -50% 0;
115 | border-radius: 0;
116 | }
117 | }
118 |
119 | @keyframes appear {
120 | 0% {
121 | opacity: 0;
122 | translate: 0 100%;
123 | }
124 | 100% {
125 | opacity: 1;
126 | translate: 0;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/examples/transition-2/Example.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from "react";
2 | import {
3 | BrowserRouter,
4 | Routes,
5 | Route,
6 | Outlet,
7 | useNavigate,
8 | useLocation,
9 | } from "react-router-dom";
10 | import "./styles.css";
11 |
12 | const Home = () => ;
13 |
14 | const About = () => ;
15 |
16 | const Work = () => ;
17 |
18 | const Link = ({ to, children }: { to: string } & PropsWithChildren) => {
19 | const navigate = useNavigate();
20 | const location = useLocation();
21 |
22 | const handleClicked = () => {
23 | const bubbles = document.getElementById("bubbles");
24 |
25 | bubbles?.classList.add("show");
26 |
27 | setTimeout(() => navigate(to), 1000);
28 |
29 | setTimeout(() => {
30 | bubbles?.classList.remove("show");
31 | bubbles?.classList.add("hide");
32 | }, 1200);
33 |
34 | setTimeout(() => bubbles?.classList.remove("hide"), 2400);
35 | };
36 |
37 | return (
38 |
46 | {children}
47 |
48 | );
49 | };
50 |
51 | const Nav = ({ title }: { title: string }) => (
52 |
53 | {title}
54 |
55 |
56 | Home
57 |
58 |
59 | About
60 |
61 |
62 | Work
63 |
64 |
65 |
66 | );
67 |
68 | const duration = 1200;
69 | const colorStart = "#8f44fd";
70 | const colorEnd = "#ffffff";
71 |
72 | const Bubbles = () => {
73 | return (
74 |
84 | );
85 | };
86 |
87 | const Layout = () => {
88 | return (
89 | <>
90 |
91 |
92 | >
93 | );
94 | };
95 |
96 | export const Example = () => {
97 | return (
98 |
99 |
100 | }>
101 | } />
102 | } />
103 | } />
104 |
105 |
106 |
107 | );
108 | };
109 |
--------------------------------------------------------------------------------
/src/examples/transition-3/Example.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from "react";
2 | import {
3 | BrowserRouter,
4 | Routes,
5 | Route,
6 | Outlet,
7 | useNavigate,
8 | useLocation,
9 | } from "react-router-dom";
10 | import "./styles.css";
11 |
12 | const About = () => (
13 |
16 | );
17 |
18 | const Portfolio = () => (
19 |
22 | );
23 |
24 | const Contact = () => (
25 |
28 | );
29 |
30 | const Link = ({
31 | to,
32 | children,
33 | colorEnd,
34 | }: { to: string; colorEnd: string } & PropsWithChildren) => {
35 | const navigate = useNavigate();
36 | const location = useLocation();
37 |
38 | const handleClicked = () => {
39 | const bubbles = document.getElementById("bubbles");
40 |
41 | bubbles?.classList.add("show");
42 |
43 | const bubbleSecond: HTMLDivElement =
44 | bubbles?.querySelector("div:nth-child(2)")!;
45 |
46 | bubbleSecond.style.background = colorEnd;
47 |
48 | setTimeout(() => navigate(to), 1000);
49 |
50 | setTimeout(() => {
51 | bubbles?.classList.remove("show");
52 | bubbles?.classList.add("hide");
53 | }, 1200);
54 |
55 | setTimeout(() => bubbles?.classList.remove("hide"), 2400);
56 | };
57 |
58 | return (
59 |
67 | {children}
68 |
69 | );
70 | };
71 |
72 | const Nav = ({ title }: { title: string }) => (
73 |
74 | {title}
75 |
76 |
77 |
78 | About
79 |
80 |
81 |
82 |
83 | Portfolio
84 |
85 |
86 |
87 |
88 | Contact
89 |
90 |
91 |
92 |
93 | );
94 |
95 | const duration = 1200;
96 |
97 | const Bubbles = () => {
98 | return (
99 |
109 | );
110 | };
111 |
112 | const Layout = () => {
113 | return (
114 | <>
115 |
116 |
117 | >
118 | );
119 | };
120 |
121 | export const Example = () => {
122 | return (
123 |
124 |
125 | }>
126 | } />
127 | } />
128 | } />
129 |
130 |
131 |
132 | );
133 | };
134 |
--------------------------------------------------------------------------------
/src/examples/transition-1/Example.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from "react";
2 | import {
3 | BrowserRouter,
4 | Routes,
5 | Route,
6 | Outlet,
7 | useNavigate,
8 | useLocation,
9 | } from "react-router-dom";
10 | import "./styles.css";
11 | import image1 from "./image1.svg";
12 | import image2 from "./image2.svg";
13 | import image3 from "./image3.svg";
14 |
15 | const Home = () => (
16 |
17 |
18 |
19 |
Home
20 |
21 | Lorem ipsum dolor, sit amet consectetur adipisicing elit. Officia,
22 | facere, perspiciatis, voluptatum id iste numquam aliquid quo modi
23 | ratione eos ut veritatis molestias ea. Ut autem quos maxime corrupti
24 | mollitia!
25 |
26 |
27 |
28 |
29 |
30 | );
31 |
32 | const About = () => (
33 |
34 |
35 |
36 |
About
37 |
38 | Lorem ipsum dolor, sit amet consectetur adipisicing elit. Officia,
39 | facere, perspiciatis, voluptatum id iste numquam aliquid quo modi
40 | ratione eos ut veritatis molestias ea. Ut autem quos maxime corrupti
41 | mollitia!
42 |
43 |
44 |
45 |
46 |
47 | );
48 |
49 | const Work = () => (
50 |
51 |
52 |
53 |
Work
54 |
55 | Lorem ipsum dolor, sit amet consectetur adipisicing elit. Officia,
56 | facere, perspiciatis, voluptatum id iste numquam aliquid quo modi
57 | ratione eos ut veritatis molestias ea. Ut autem quos maxime corrupti
58 | mollitia!
59 |
60 |
61 |
62 |
63 |
64 | );
65 |
66 | const Link = ({ to, children }: { to: string } & PropsWithChildren) => {
67 | const navigate = useNavigate();
68 | const location = useLocation();
69 |
70 | const handleClicked = () => {
71 | const bars = document.getElementById("bars");
72 |
73 | bars?.classList.add("show");
74 |
75 | setTimeout(() => {
76 | bars?.classList.remove("show");
77 | bars?.classList.add("hide");
78 | navigate(to);
79 | }, 800);
80 |
81 | setTimeout(() => bars?.classList.remove("hide"), 1600);
82 | };
83 |
84 | return (
85 |
93 | {children}
94 |
95 | );
96 | };
97 |
98 | const Bars = () => {
99 | return (
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | );
108 | };
109 |
110 | const Layout = () => {
111 | return (
112 | <>
113 |
114 | Portfolio
115 |
116 |
117 | Home
118 |
119 |
120 | About
121 |
122 |
123 | Work
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | >
132 | );
133 | };
134 |
135 | export const Example = () => {
136 | return (
137 |
138 |
139 | }>
140 | } />
141 | } />
142 | } />
143 |
144 |
145 |
146 | );
147 | };
148 |
--------------------------------------------------------------------------------
/src/examples/transition-1/image1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/examples/transition-1/image2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/examples/transition-1/image3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------