├── .DS_Store
├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── build
├── _redirects
├── asset-manifest.json
├── favicon.png
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
├── robots.txt
└── static
│ ├── css
│ ├── main.fec48c59.css
│ └── main.fec48c59.css.map
│ └── js
│ ├── main.5f22276f.js
│ ├── main.5f22276f.js.LICENSE.txt
│ └── main.5f22276f.js.map
├── package-lock.json
├── package.json
├── public
├── _redirects
├── favicon.png
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.tsx
├── index.css
├── index.tsx
├── library
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── BubblyBubbles.tsx
│ │ ├── BubblyContainer.tsx
│ │ ├── BubblyLink.tsx
│ │ ├── index.d.ts
│ │ ├── index.ts
│ │ └── styles.css
├── react-app-env.d.ts
├── routes
│ ├── About.tsx
│ ├── Contact.tsx
│ └── Docs.tsx
└── shared
│ ├── Nav.tsx
│ └── Title.tsx
└── tsconfig.json
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-bubbly-transitions/079ba7c95983a3c385fdcc2d36e75d71419cfbe0/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "git.ignoreLimitWarning": true
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-bubbly-transitions
2 |
3 | Show bubbly transitions between route changes, in your React 18 apps.
4 |
5 | Click [here for a demo](https://bubbles.frontendjoe.com/).
6 |
7 | Or [check out the npm package here](https://www.npmjs.com/package/react-bubbly-transitions).
8 |
9 | ## Installation
10 |
11 | Just a few quick steps to get started:
12 |
13 | #### 1. Create a React app (optional)
14 |
15 | If you are adding the transitions to an existing app, you can skip this step.
16 |
17 | ```sh
18 | npx create-react-app my-bubbly-app
19 | cd my-bubbly-app
20 | ```
21 |
22 | #### 2. Install dependencies
23 |
24 | Our project depends upon React's router library
25 |
26 | ```sh
27 | npm i react-bubbly-transitions react-router-dom
28 | ```
29 |
30 | #### 3. Add components
31 |
32 | The package relies on two components being present.
33 |
34 | ##### BubblyContainer
35 |
36 | This is what houses our wave transition between route changes and does not require any props.
37 |
38 | ##### BubblyLink
39 |
40 | This button can be declared anywhere inside your Router component.
41 |
42 | It takes the following props:
43 |
44 | | Prop | Description | Example | type | required | default |
45 | | ---------- | -------------------------------------------------------------------------------------------- | ------- | ------------------ | -------- | ------- |
46 | | children | The content inside the link | About | String / Component | true | |
47 | | to | The route that the link will take you to | /about | String | true | |
48 | | colorStart | The background color of the bubble shape that appears first. Must be a hexcode or rgba value | #8f44fd | String | false | #8f44fd |
49 | | colorEnd | The background color of the bubble shape that appears last. Must be a hexcode or rgba value | #ffffff | String | false | #ffffff |
50 |
51 | Be careful with the duration (too fast/slow can ruin the effect) - my recommended duration is between 1000ms and 1600ms.
52 |
53 | ##### Example App.tsx
54 |
55 | Copy this whole code snippet into your App.tsx for a basic example:
56 |
57 | ```typescript
58 | import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
59 |
60 | import { BubblyContainer, BubblyLink } from "react-bubbly-transitions";
61 |
62 | const Home = () =>
Home
;
63 | const About = () => About
;
64 | const Contact = () => Contact
;
65 |
66 | function App() {
67 | return (
68 |
69 |
70 |
71 |
75 | Home
76 | About
77 | Contact
78 |
79 | >
80 | }
81 | >
82 | } />
83 | } />
84 | } />
85 | No Match>} />
86 |
87 |
88 |
89 | );
90 | }
91 |
92 | export default App;
93 | ```
94 |
95 | ### 4. Styling
96 |
97 | To style the BubblyLink component you can target it via css (just be more specific than me 😄):
98 |
99 | ```css
100 | body .react-bubbly-transitions__bubbly-link {
101 | color: #af44fd;
102 | }
103 | ```
104 |
105 | To style the active state of the BubblyLink, just target the .active class and again be more specific.
106 |
107 | ```css
108 | body .react-bubbly-transitions__bubbly-link.active {
109 | text-decoration: underline;
110 | }
111 | ```
112 |
113 | ### 5. DRY (Don't Repeat Yourself)
114 |
115 | To avoid repeating certain BubblyLink props, I recommend creating your own generic link component that sets the props here by default.
116 |
117 | ```typescript
118 | import { FC, ReactNode } from "react";
119 | import { BubblyLink } from "react-bubbly-transitions";
120 |
121 | type Props = {
122 | to: string;
123 | children: ReactNode;
124 | };
125 |
126 | export const MyBubblyLink: FC = ({ to, children }) => (
127 |
128 | {children}
129 |
130 | );
131 | ```
132 |
133 | ### 6. Have fun with it!
134 |
135 | Please hit me up on [My Instagram page](https://instagram.com/frontendjoe) for any support or suggestions 🙂
136 |
--------------------------------------------------------------------------------
/build/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/build/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "main.css": "/static/css/main.fec48c59.css",
4 | "main.js": "/static/js/main.5f22276f.js",
5 | "index.html": "/index.html",
6 | "main.fec48c59.css.map": "/static/css/main.fec48c59.css.map",
7 | "main.5f22276f.js.map": "/static/js/main.5f22276f.js.map"
8 | },
9 | "entrypoints": [
10 | "static/css/main.fec48c59.css",
11 | "static/js/main.5f22276f.js"
12 | ]
13 | }
--------------------------------------------------------------------------------
/build/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-bubbly-transitions/079ba7c95983a3c385fdcc2d36e75d71419cfbe0/build/favicon.png
--------------------------------------------------------------------------------
/build/index.html:
--------------------------------------------------------------------------------
1 | react-wavy-transitions
--------------------------------------------------------------------------------
/build/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-bubbly-transitions/079ba7c95983a3c385fdcc2d36e75d71419cfbe0/build/logo192.png
--------------------------------------------------------------------------------
/build/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-bubbly-transitions/079ba7c95983a3c385fdcc2d36e75d71419cfbe0/build/logo512.png
--------------------------------------------------------------------------------
/build/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "react-wavy-transitions",
3 | "name": "react-wavy-transitions",
4 | "icons": [
5 | {
6 | "src": "favicon.png",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "favicon.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "favicon.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/build/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/build/static/css/main.fec48c59.css:
--------------------------------------------------------------------------------
1 | body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}nav{align-items:flex-start;background:#1d1e22;box-sizing:border-box;color:#f9f9f9;display:flex;flex-direction:column;gap:10px;height:100%;left:0;margin:0;position:fixed;top:0;width:120px;z-index:99999}main,nav{padding:20px}main{margin-left:120px}body{margin:0}.react-wavy-transitions__first,.react-wavy-transitions__second{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;height:100%;left:0;min-width:800px;position:fixed;top:0;width:100%;z-index:9999}.down.react-wavy-transitions__first{-webkit-animation-name:down-first-move;animation-name:down-first-move;top:2px;translate:0 -200%}.down.react-wavy-transitions__second{-webkit-animation-name:down-second-move;animation-name:down-second-move;rotate:180deg;translate:0 -100%}@-webkit-keyframes down-first-move{to{translate:0 0}}@keyframes down-first-move{to{translate:0 0}}@-webkit-keyframes down-second-move{to{translate:0 100%}}@keyframes down-second-move{to{translate:0 100%}}.up.react-wavy-transitions__first{-webkit-animation-name:up-first-move;animation-name:up-first-move;rotate:180deg;translate:0 200%}.up.react-wavy-transitions__second{-webkit-animation-name:up-second-move;animation-name:up-second-move;top:2px;translate:0 100%}@-webkit-keyframes up-first-move{to{translate:0 0}}@keyframes up-first-move{to{translate:0 0}}@-webkit-keyframes up-second-move{to{translate:0 -100%}}@keyframes up-second-move{to{translate:0 -100%}}.react-wavy-transitions__wavy-link{background:transparent;border:0;color:inherit;cursor:pointer;font-family:inherit;font-size:1rem}
2 | /*# sourceMappingURL=main.fec48c59.css.map*/
--------------------------------------------------------------------------------
/build/static/css/main.fec48c59.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"static/css/main.fec48c59.css","mappings":"AAAA,KAKE,kCAAmC,CACnC,iCAAkC,CAJlC,mIAKF,CAEA,IAWE,sBAAuB,CAJvB,kBAAmB,CAQnB,qBAAsB,CAPtB,aAAc,CACd,YAAa,CACb,qBAAsB,CAEtB,QAAS,CANT,WAAY,CAFZ,MAAO,CAUP,QAAS,CAbT,cAAe,CAEf,KAAM,CAEN,WAAY,CAHZ,aAcF,CAEA,SALE,YAQF,CAHA,KACE,iBAEF,CC9BA,KACE,QACF,CAEA,+DASE,6CAAsC,CAAtC,qCAAsC,CADtC,WAAY,CAHZ,MAAO,CAEP,eAAgB,CALhB,cAAe,CAEf,KAAM,CAEN,UAAW,CAHX,YAOF,CAGA,oCAGE,sCAA+B,CAA/B,8BAA+B,CAF/B,OAAQ,CACR,iBAEF,CAEA,qCAGE,uCAAgC,CAAhC,+BAAgC,CAFhC,aAAc,CACd,iBAEF,CAEA,mCACE,GACE,aACF,CACF,CAJA,2BACE,GACE,aACF,CACF,CAEA,oCACE,GACE,gBACF,CACF,CAJA,4BACE,GACE,gBACF,CACF,CAGA,kCAGE,oCAA6B,CAA7B,4BAA6B,CAF7B,aAAc,CACd,gBAEF,CAEA,mCAGE,qCAA8B,CAA9B,6BAA8B,CAF9B,OAAQ,CACR,gBAEF,CAEA,iCACE,GACE,aACF,CACF,CAJA,yBACE,GACE,aACF,CACF,CAEA,kCACE,GACE,iBACF,CACF,CAJA,0BACE,GACE,iBACF,CACF,CAGA,mCACE,sBAAuB,CACvB,QAAS,CACT,aAAc,CAGd,cAAe,CAFf,mBAAoB,CACpB,cAEF","sources":["index.css","library/waves/styles.css"],"sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nnav {\n position: fixed;\n z-index: 99999;\n top: 0;\n left: 0;\n width: 120px;\n height: 100%;\n background: #1d1e22;\n color: #f9f9f9;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 10px;\n padding: 20px;\n margin: 0;\n box-sizing: border-box;\n}\n\nmain {\n margin-left: 120px;\n padding: 20px;\n}\n","body {\n margin: 0;\n}\n\n.react-wavy-transitions__first,\n.react-wavy-transitions__second {\n position: fixed;\n z-index: 9999;\n top: 0;\n left: 0;\n width: 100%;\n min-width: 800px;\n height: 100%;\n animation-timing-function: ease-in-out;\n}\n\n/* direction down */\n.down.react-wavy-transitions__first {\n top: 2px;\n translate: 0 -200%;\n animation-name: down-first-move;\n}\n\n.down.react-wavy-transitions__second {\n rotate: 180deg;\n translate: 0 -100%;\n animation-name: down-second-move;\n}\n\n@keyframes down-first-move {\n 100% {\n translate: 0 0;\n }\n}\n\n@keyframes down-second-move {\n 100% {\n translate: 0 100%;\n }\n}\n\n/* direction up */\n.up.react-wavy-transitions__first {\n rotate: 180deg;\n translate: 0 200%;\n animation-name: up-first-move;\n}\n\n.up.react-wavy-transitions__second {\n top: 2px;\n translate: 0 100%;\n animation-name: up-second-move;\n}\n\n@keyframes up-first-move {\n 100% {\n translate: 0 0;\n }\n}\n\n@keyframes up-second-move {\n 100% {\n translate: 0 -100%;\n }\n}\n\n/* Wavy Link */\n.react-wavy-transitions__wavy-link {\n background: transparent;\n border: 0;\n color: inherit;\n font-family: inherit;\n font-size: 1rem;\n cursor: pointer;\n}\n"],"names":[],"sourceRoot":""}
--------------------------------------------------------------------------------
/build/static/js/main.5f22276f.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*!
2 | * ApexCharts v3.35.5
3 | * (c) 2018-2022 ApexCharts
4 | * Released under the MIT License.
5 | */
6 |
7 | /*! svg.draggable.js - v2.2.2 - 2019-01-08
8 | * https://github.com/svgdotjs/svg.draggable.js
9 | * Copyright (c) 2019 Wout Fierens; Licensed MIT */
10 |
11 | /*! svg.filter.js - v2.0.2 - 2016-02-24
12 | * https://github.com/wout/svg.filter.js
13 | * Copyright (c) 2016 Wout Fierens; Licensed MIT */
14 |
15 | /**
16 | * @license React
17 | * react-dom.production.min.js
18 | *
19 | * Copyright (c) Facebook, Inc. and its affiliates.
20 | *
21 | * This source code is licensed under the MIT license found in the
22 | * LICENSE file in the root directory of this source tree.
23 | */
24 |
25 | /**
26 | * @license React
27 | * react-jsx-runtime.production.min.js
28 | *
29 | * Copyright (c) Facebook, Inc. and its affiliates.
30 | *
31 | * This source code is licensed under the MIT license found in the
32 | * LICENSE file in the root directory of this source tree.
33 | */
34 |
35 | /**
36 | * @license React
37 | * react.production.min.js
38 | *
39 | * Copyright (c) Facebook, Inc. and its affiliates.
40 | *
41 | * This source code is licensed under the MIT license found in the
42 | * LICENSE file in the root directory of this source tree.
43 | */
44 |
45 | /**
46 | * @license React
47 | * scheduler.production.min.js
48 | *
49 | * Copyright (c) Facebook, Inc. and its affiliates.
50 | *
51 | * This source code is licensed under the MIT license found in the
52 | * LICENSE file in the root directory of this source tree.
53 | */
54 |
55 | /**
56 | * @remix-run/router v1.0.2
57 | *
58 | * Copyright (c) Remix Software Inc.
59 | *
60 | * This source code is licensed under the MIT license found in the
61 | * LICENSE.md file in the root directory of this source tree.
62 | *
63 | * @license MIT
64 | */
65 |
66 | /**
67 | * React Router v6.4.2
68 | *
69 | * Copyright (c) Remix Software Inc.
70 | *
71 | * This source code is licensed under the MIT license found in the
72 | * LICENSE.md file in the root directory of this source tree.
73 | *
74 | * @license MIT
75 | */
76 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-bubbly-transitions-examples",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@types/node": "^16.11.64",
7 | "@types/react": "^18.0.21",
8 | "@types/react-dom": "^18.0.6",
9 | "react": "^18.2.0",
10 | "react-dom": "^18.2.0",
11 | "react-router-dom": "^6.4.2",
12 | "react-scripts": "5.0.1",
13 | "typescript": "^4.8.4"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build"
18 | },
19 | "eslintConfig": {
20 | "extends": [
21 | "react-app"
22 | ]
23 | },
24 | "browserslist": {
25 | "production": [
26 | ">0.2%",
27 | "not dead",
28 | "not op_mini all"
29 | ],
30 | "development": [
31 | "last 1 chrome version",
32 | "last 1 firefox version",
33 | "last 1 safari version"
34 | ]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-bubbly-transitions/079ba7c95983a3c385fdcc2d36e75d71419cfbe0/public/favicon.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | react-bubbly-transitions
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-bubbly-transitions/079ba7c95983a3c385fdcc2d36e75d71419cfbe0/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-bubbly-transitions/079ba7c95983a3c385fdcc2d36e75d71419cfbe0/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "react-wavy-transitions",
3 | "name": "react-wavy-transitions",
4 | "icons": [
5 | {
6 | "src": "favicon.png",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "favicon.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "favicon.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Suspense } from "react";
2 | import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
3 |
4 | import About from "./routes/About";
5 | import Docs from "./routes/Docs";
6 | import Contact from "./routes/Contact";
7 |
8 | import { BubblyContainer } from "./library/src";
9 |
10 | function App() {
11 | return (
12 |
13 |
14 |
15 | }>
16 | } />
17 | ...>}>
21 |
22 |
23 | }
24 | />
25 | ...>}>
29 |
30 |
31 | }
32 | />
33 | No Match>} />
34 |
35 |
36 |
37 | );
38 | }
39 |
40 | export default App;
41 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 64px 15% 0;
4 | font-family: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI",
5 | "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
6 | "Helvetica Neue", sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | header {
12 | display: flex;
13 | align-items: center;
14 | justify-content: space-between;
15 | }
16 |
17 | nav {
18 | display: flex;
19 | gap: 10px;
20 | }
21 |
22 | @keyframes in-keyframes {
23 | 0% {
24 | opacity: 0;
25 | translate: 0 100%;
26 | }
27 | 100% {
28 | opacity: 1;
29 | translate: 0 0;
30 | }
31 | }
32 |
33 | .animate-in {
34 | animation-name: in-keyframes;
35 | animation-duration: 0.5s;
36 | animation-fill-mode: both;
37 | }
38 |
39 | /* wave link styles */
40 | body .react-bubbly-transitions__bubbly-link {
41 | padding: 0;
42 | outline: none;
43 | }
44 |
45 | body .react-bubbly-transitions__bubbly-link.active {
46 | position: relative;
47 | color: #8f44fd;
48 | }
49 |
50 | body .react-bubbly-transitions__bubbly-link.active::after {
51 | content: "";
52 | position: absolute;
53 | left: 0;
54 | bottom: 0;
55 | width: 100%;
56 | height: 2px;
57 | border-radius: 3px;
58 | background: #8f44fd;
59 | }
60 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | const root = ReactDOM.createRoot(
7 | document.getElementById("root") as HTMLElement
8 | );
9 | root.render(
10 |
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/src/library/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
--------------------------------------------------------------------------------
/src/library/README.md:
--------------------------------------------------------------------------------
1 | # react-bubbly-transitions
2 |
3 | Show bubbly transitions between route changes, in your React 18 apps.
4 |
5 | Click [here for a demo](https://bubbles.frontendjoe.com/).
6 |
7 | Or [check out the npm package here](https://www.npmjs.com/package/react-bubbly-transitions).
8 |
9 | ## Installation
10 |
11 | Just a few quick steps to get started:
12 |
13 | #### 1. Create a React app (optional)
14 |
15 | If you are adding the transitions to an existing app, you can skip this step.
16 |
17 | ```sh
18 | npx create-react-app my-bubbly-app
19 | cd my-bubbly-app
20 | ```
21 |
22 | #### 2. Install dependencies
23 |
24 | Our project depends upon React's router library
25 |
26 | ```sh
27 | npm i react-bubbly-transitions react-router-dom
28 | ```
29 |
30 | #### 3. Add components
31 |
32 | The package relies on two components being present.
33 |
34 | ##### BubblyContainer
35 |
36 | This is what houses our wave transition between route changes and does not require any props.
37 |
38 | ##### BubblyLink
39 |
40 | This button can be declared anywhere inside your Router component.
41 |
42 | It takes the following props:
43 |
44 | | Prop | Description | Example | type | required | default |
45 | | ---------- | -------------------------------------------------------------------------------------------- | ------- | ------------------ | -------- | ------- |
46 | | children | The content inside the link | About | String / Component | true | |
47 | | to | The route that the link will take you to | /about | String | true | |
48 | | colorStart | The background color of the bubble shape that appears first. Must be a hexcode or rgba value | #8f44fd | String | false | #8f44fd |
49 | | colorEnd | The background color of the bubble shape that appears last. Must be a hexcode or rgba value | #ffffff | String | false | #ffffff |
50 |
51 | Be careful with the duration (too fast/slow can ruin the effect) - my recommended duration is between 1000ms and 1600ms.
52 |
53 | ##### Example App.tsx
54 |
55 | Copy this whole code snippet into your App.tsx for a basic example:
56 |
57 | ```typescript
58 | import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
59 |
60 | import { BubblyContainer, BubblyLink } from "react-bubbly-transitions";
61 |
62 | const Home = () => Home
;
63 | const About = () => About
;
64 | const Contact = () => Contact
;
65 |
66 | function App() {
67 | return (
68 |
69 |
70 |
71 |
75 | Home
76 | About
77 | Contact
78 |
79 | >
80 | }
81 | >
82 | } />
83 | } />
84 | } />
85 | No Match>} />
86 |
87 |
88 |
89 | );
90 | }
91 |
92 | export default App;
93 | ```
94 |
95 | ### 4. Styling
96 |
97 | To style the BubblyLink component you can target it via css (just be more specific than me 😄):
98 |
99 | ```css
100 | body .react-bubbly-transitions__bubbly-link {
101 | color: #af44fd;
102 | }
103 | ```
104 |
105 | To style the active state of the BubblyLink, just target the .active class and again be more specific.
106 |
107 | ```css
108 | body .react-bubbly-transitions__bubbly-link.active {
109 | text-decoration: underline;
110 | }
111 | ```
112 |
113 | ### 5. DRY (Don't Repeat Yourself)
114 |
115 | To avoid repeating certain BubblyLink props, I recommend creating your own generic link component that sets the props here by default.
116 |
117 | ```typescript
118 | import { FC, ReactNode } from "react";
119 | import { BubblyLink } from "react-bubbly-transitions";
120 |
121 | type Props = {
122 | to: string;
123 | children: ReactNode;
124 | };
125 |
126 | export const MyBubblyLink: FC = ({ to, children }) => (
127 |
128 | {children}
129 |
130 | );
131 | ```
132 |
133 | ### 6. Have fun with it!
134 |
135 | Please hit me up on [My Instagram page](https://instagram.com/frontendjoe) for any support or suggestions 🙂
136 |
--------------------------------------------------------------------------------
/src/library/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-bubbly-transitions",
3 | "version": "1.0.1",
4 | "description": "",
5 | "main": "dist/index.js",
6 | "files": [
7 | "dist/*.*"
8 | ],
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1",
11 | "publish:npm": "rm -Rf dist && mkdir dist && babel --extensions .ts,.tsx src --ignore 'src/index.d.ts' -d dist --copy-files"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/frontend-joe/react-bubbly-transitions.git"
16 | },
17 | "author": "@frontendjoe",
18 | "license": "MIT",
19 | "types": "./dist/index.d.ts",
20 | "bugs": {
21 | "url": "https://github.com/frontend-joe/react-bubbly-transitions/issues"
22 | },
23 | "homepage": "https://github.com/frontend-joe/react-bubbly-transitions#readme",
24 | "dependencies": {
25 | "react": "^18.2.0",
26 | "react-dom": "^18.2.0",
27 | "react-router-dom": "^6.4.2"
28 | },
29 | "devDependencies": {
30 | "@babel/cli": "^7.18.10",
31 | "@babel/preset-env": "^7.19.1",
32 | "@babel/preset-react": "^7.18.6",
33 | "@babel/preset-typescript": "^7.18.6"
34 | },
35 | "babel": {
36 | "presets": [
37 | [
38 | "@babel/react",
39 | {
40 | "runtime": "automatic"
41 | }
42 | ],
43 | [
44 | "@babel/preset-typescript"
45 | ],
46 | [
47 | "@babel/preset-env"
48 | ]
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/library/src/BubblyBubbles.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from "react";
2 | import "./styles.css";
3 |
4 | type Props = {
5 | colorStart: string;
6 | colorEnd: string;
7 | duration: number;
8 | };
9 |
10 | export const BubblyBubbles: FC = ({
11 | colorStart,
12 | colorEnd,
13 | duration,
14 | }) => {
15 | return (
16 |
26 | );
27 | };
28 |
--------------------------------------------------------------------------------
/src/library/src/BubblyContainer.tsx:
--------------------------------------------------------------------------------
1 | export const BubblyContainer = () => (
2 |
3 | );
4 |
--------------------------------------------------------------------------------
/src/library/src/BubblyLink.tsx:
--------------------------------------------------------------------------------
1 | import { FC, MouseEvent, ReactNode } from "react";
2 | import { createRoot } from "react-dom/client";
3 | import { useNavigate } from "react-router-dom";
4 | import { BubblyBubbles } from "./BubblyBubbles";
5 |
6 | export type BubblyLinkProps = {
7 | to: string;
8 | children: ReactNode;
9 | colorStart?: string;
10 | colorEnd?: string;
11 | duration?: number;
12 | };
13 |
14 | export const BubblyLink: FC = ({
15 | to,
16 | children,
17 | colorStart = "#8f44fd",
18 | colorEnd = "#ffffff",
19 | duration = 1250,
20 | }) => {
21 | const navigate = useNavigate();
22 |
23 | const handleClick = (e: MouseEvent | undefined) => {
24 | e?.preventDefault();
25 |
26 | if (
27 | !document.getElementById("react-bubbly-transitions__bubbles") &&
28 | window.location.pathname !== to
29 | ) {
30 | // change the url in address bar
31 | window.history.pushState("", "", to);
32 |
33 | // get access to wave container
34 | const container = createRoot(
35 | document.getElementById("react-bubbly-transitions__container")!
36 | );
37 |
38 | // show the waves
39 | container.render(
40 |
45 | );
46 |
47 | // do the route change
48 | setTimeout(() => navigate(to), duration / 2); // half total animation
49 |
50 | // hide the waves
51 | setTimeout(() => container.unmount(), duration); // total animation
52 | }
53 | };
54 |
55 | return (
56 |
65 | );
66 | };
67 |
--------------------------------------------------------------------------------
/src/library/src/index.d.ts:
--------------------------------------------------------------------------------
1 | export { BubblyContainer } from "./BubblyContainer";
2 | export { BubblyLink, BubblyLinkProps } from "./BubblyLink";
3 |
--------------------------------------------------------------------------------
/src/library/src/index.ts:
--------------------------------------------------------------------------------
1 | export { BubblyContainer } from "./BubblyContainer";
2 | export { BubblyLink } from "./BubblyLink";
3 |
--------------------------------------------------------------------------------
/src/library/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | #react-bubbly-transitions__bubbles {
6 | --size: 200vw;
7 | }
8 |
9 | @media only screen and (min-width: 768px) {
10 | #react-bubbly-transitions__bubbles {
11 | --size: 125vw;
12 | }
13 | }
14 |
15 | .react-bubbly-transitions__first,
16 | .react-bubbly-transitions__second {
17 | position: fixed;
18 | z-index: 9999;
19 | top: 0;
20 | left: 50%;
21 | translate: -50% 100%;
22 | width: var(--size);
23 | height: var(--size);
24 | border-radius: var(--size);
25 | animation-timing-function: ease-in-out;
26 | }
27 |
28 | .react-bubbly-transitions__first {
29 | animation-name: bubble-move;
30 | }
31 |
32 | .react-bubbly-transitions__second {
33 | animation-name: bubble-second-move;
34 | }
35 |
36 | @keyframes bubble-move {
37 | 20% {
38 | border-radius: var(--size);
39 | }
40 | 50%,
41 | 100% {
42 | translate: -50% 0;
43 | border-radius: 0;
44 | }
45 | }
46 |
47 | @keyframes bubble-second-move {
48 | 30% {
49 | translate: -50% 100%;
50 | }
51 | 50% {
52 | border-radius: var(--size);
53 | }
54 | 100% {
55 | translate: -50% 0;
56 | border-radius: 0;
57 | }
58 | }
59 |
60 | /* bubbly Link */
61 | .react-bubbly-transitions__bubbly-link {
62 | background: transparent;
63 | border: 0;
64 | color: inherit;
65 | font-family: inherit;
66 | font-size: 1rem;
67 | cursor: pointer;
68 | }
69 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/routes/About.tsx:
--------------------------------------------------------------------------------
1 | import { Nav } from "../shared/Nav";
2 | import { Title } from "../shared/Title";
3 |
4 | const Wrapper = () => (
5 | <>
6 |
10 | >
11 | );
12 |
13 | export default Wrapper;
14 |
--------------------------------------------------------------------------------
/src/routes/Contact.tsx:
--------------------------------------------------------------------------------
1 | import { Nav } from "../shared/Nav";
2 | import { Title } from "../shared/Title";
3 |
4 | const Wrapper = () => (
5 | <>
6 |
10 | >
11 | );
12 |
13 | export default Wrapper;
14 |
--------------------------------------------------------------------------------
/src/routes/Docs.tsx:
--------------------------------------------------------------------------------
1 | import { Nav } from "../shared/Nav";
2 | import { Title } from "../shared/Title";
3 |
4 | const Wrapper = () => (
5 | <>
6 |
10 | >
11 | );
12 |
13 | export default Wrapper;
14 |
--------------------------------------------------------------------------------
/src/shared/Nav.tsx:
--------------------------------------------------------------------------------
1 | import { BubblyLink } from "../library/src";
2 |
3 | const MyBubblyLink = ({ to = "", text = "" }) => (
4 |
5 | {text}
6 |
7 | );
8 |
9 | export const Nav = () => (
10 |
15 | );
16 |
--------------------------------------------------------------------------------
/src/shared/Title.tsx:
--------------------------------------------------------------------------------
1 | import { FC, PropsWithChildren } from "react";
2 |
3 | export const Title: FC = ({ children }) => (
4 |
5 | {children}
6 |
7 | );
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------