├── .gitignore ├── README.md ├── craco.config.js ├── package-lock.json ├── package.json ├── public ├── apple-touch-icon.png ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── meta.png ├── src ├── App.tsx ├── components │ └── Confetti.js ├── fonts │ ├── SegoeUI-Bold.woff │ ├── SegoeUI-Bold.woff2 │ ├── SegoeUI-BoldItalic.woff │ ├── SegoeUI-BoldItalic.woff2 │ ├── SegoeUI-Italic.woff │ ├── SegoeUI-Italic.woff2 │ ├── SegoeUI.woff │ ├── SegoeUI.woff2 │ └── stylesheet.css ├── index.css ├── index.tsx ├── pages │ ├── 404.tsx │ ├── Bday.tsx │ └── Home.tsx ├── react-app-env.d.ts ├── reportWebVitals.js └── setupTests.js ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎉 hbd.today 🎉 2 | 3 | send a simple, fun, virtual birthday card! 4 | 5 | _a simple 3-day frontend app, created with React + Tailwind_ 6 | 7 | ------------------ 8 | 5/12/2022 - I let the hbd.today domain expire. Didn't want to pay $22 to renew it 9 | -------------------------------------------------------------------------------- /craco.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | style: { 3 | postcss: { 4 | plugins: [ 5 | require('tailwindcss'), 6 | require('autoprefixer'), 7 | ], 8 | }, 9 | }, 10 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@craco/craco": "^6.1.2", 7 | "@testing-library/jest-dom": "^5.11.4", 8 | "@testing-library/react": "^11.1.0", 9 | "@testing-library/user-event": "^12.1.10", 10 | "@types/react-dom": "^17.0.5", 11 | "@types/react-router-dom": "^5.1.7", 12 | "canvas-confetti": "^1.4.0", 13 | "framer-motion": "^4.1.17", 14 | "query-string": "^7.0.0", 15 | "react": "^17.0.2", 16 | "react-confetti": "^6.0.1", 17 | "react-dom": "^17.0.2", 18 | "react-router-dom": "^5.2.0", 19 | "react-scripts": "4.0.3", 20 | "typescript": "^4.2.4", 21 | "web-vitals": "^1.0.1" 22 | }, 23 | "scripts": { 24 | "start": "craco start", 25 | "build": "craco build", 26 | "test": "craco test", 27 | "eject": "react-scripts eject" 28 | }, 29 | "eslintConfig": { 30 | "extends": [ 31 | "react-app", 32 | "react-app/jest" 33 | ] 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.2%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 1 chrome version", 43 | "last 1 firefox version", 44 | "last 1 safari version" 45 | ] 46 | }, 47 | "devDependencies": { 48 | "@tailwindcss/postcss7-compat": "^2.1.2", 49 | "@types/react": "^17.0.5", 50 | "autoprefixer": "^9", 51 | "postcss": "^7", 52 | "tailwindcss": "npm:@tailwindcss/postcss7-compat" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 46 | hbd.today 47 | 48 | 49 | 50 |
51 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "hbd.today", 3 | "name": "hbd.today", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.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/meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/public/meta.png -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; 2 | import Bday from "./pages/Bday"; 3 | import Home from "./pages/Home"; 4 | import PageNotFound from "./pages/404"; 5 | 6 | function App() { 7 | return( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /src/components/Confetti.js: -------------------------------------------------------------------------------- 1 | //thanks ian 2 | import confetti from "canvas-confetti"; 3 | import { useEffect, useRef } from "react"; 4 | 5 | export default function ConfettiExplode({ 6 | count = 25, 7 | velocity = 75, 8 | spread = 120, 9 | angle, 10 | children 11 | }) { 12 | const ref = useRef(); 13 | 14 | useEffect(() => { 15 | const center = { 16 | x: 17 | (ref.current?.firstChild?.offsetLeft ?? 0) + 18 | (ref.current?.firstChild?.offsetWidth ?? 0) / 2, 19 | y: ref.current?.firstChild?.offsetTop ?? 0, 20 | }; 21 | 22 | confetti({ 23 | disableForReducedMotion: true, 24 | particleCount: count, 25 | startVelocity: velocity, 26 | spread: spread, 27 | angle: angle, 28 | origin: { 29 | x: center.x / window.innerWidth, 30 | y: center.y / window.innerHeight, 31 | }, 32 | ticks: 300 33 | }); 34 | }); 35 | 36 | return ( 37 |
38 | {children} 39 |
40 | ); 41 | } -------------------------------------------------------------------------------- /src/fonts/SegoeUI-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/src/fonts/SegoeUI-Bold.woff -------------------------------------------------------------------------------- /src/fonts/SegoeUI-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/src/fonts/SegoeUI-Bold.woff2 -------------------------------------------------------------------------------- /src/fonts/SegoeUI-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/src/fonts/SegoeUI-BoldItalic.woff -------------------------------------------------------------------------------- /src/fonts/SegoeUI-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/src/fonts/SegoeUI-BoldItalic.woff2 -------------------------------------------------------------------------------- /src/fonts/SegoeUI-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/src/fonts/SegoeUI-Italic.woff -------------------------------------------------------------------------------- /src/fonts/SegoeUI-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/src/fonts/SegoeUI-Italic.woff2 -------------------------------------------------------------------------------- /src/fonts/SegoeUI.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/src/fonts/SegoeUI.woff -------------------------------------------------------------------------------- /src/fonts/SegoeUI.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrad/hbd-today/22e77da13eade554185b3e76e431c011620d2e03/src/fonts/SegoeUI.woff2 -------------------------------------------------------------------------------- /src/fonts/stylesheet.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Segoe UI'; 3 | src: url('SegoeUI-Bold.woff2') format('woff2'), 4 | url('SegoeUI-Bold.woff') format('woff'); 5 | font-weight: bold; 6 | font-style: normal; 7 | font-display: swap; 8 | } 9 | 10 | @font-face { 11 | font-family: 'Segoe UI'; 12 | src: url('SegoeUI-Italic.woff2') format('woff2'), 13 | url('SegoeUI-Italic.woff') format('woff'); 14 | font-weight: normal; 15 | font-style: italic; 16 | font-display: swap; 17 | } 18 | 19 | @font-face { 20 | font-family: 'Segoe UI'; 21 | src: url('SegoeUI-BoldItalic.woff2') format('woff2'), 22 | url('SegoeUI-BoldItalic.woff') format('woff'); 23 | font-weight: bold; 24 | font-style: italic; 25 | font-display: swap; 26 | } 27 | 28 | @font-face { 29 | font-family: 'Segoe UI'; 30 | src: url('SegoeUI.woff2') format('woff2'), 31 | url('SegoeUI.woff') format('woff'); 32 | font-weight: normal; 33 | font-style: normal; 34 | font-display: swap; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @import url('./fonts/stylesheet.css'); 6 | 7 | body { 8 | margin: 0; 9 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 10 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 11 | sans-serif; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | code { 17 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 18 | monospace; 19 | } 20 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { useHistory } from "react-router-dom"; 2 | 3 | const PageNotFound = () => { 4 | let history = useHistory(); 5 | 6 | const backHome = () => { 7 | history.push(`/`); 8 | } 9 | 10 | return ( 11 |
12 |
13 |
14 | 404 15 |
16 |
17 | Page Not Found :( 18 |
19 | 20 | 21 | 22 |
23 |
24 | ); 25 | } 26 | 27 | export default PageNotFound; 28 | -------------------------------------------------------------------------------- /src/pages/Bday.tsx: -------------------------------------------------------------------------------- 1 | import { motion } from "framer-motion"; 2 | import ConfettiExplode from "../components/Confetti"; 3 | import { useHistory } from "react-router-dom"; 4 | import Confetti from 'react-confetti'; 5 | 6 | //animation - add background elements fading in and out, birthday emojis 7 | 8 | const Bday = () => { 9 | let history = useHistory(); 10 | 11 | const backHome = () => { 12 | history.push(`/`); 13 | } 14 | 15 | const query = require('query-string').parse(window.location.search) 16 | 17 | let name = query.name; 18 | let from = query.from; 19 | let qColor = query.color; 20 | 21 | if(!name || !from || !qColor || !["red", "green", "blue", "purple", "orange", "pink-blue"].includes(qColor)) return( 22 | 23 |
24 | Error: Invalid/Missing query parameters 25 |
26 | 27 | 28 |
29 | ) 30 | 31 | let color:string = qColor; 32 | 33 | switch(color) { 34 | case "green": 35 | color = "bg-gradient-to-br from-green-600 via-green-500 to-green-400"; 36 | break; 37 | 38 | case "red": 39 | color = "bg-gradient-to-br from-red-600 via-red-500 to-red-400"; 40 | break; 41 | 42 | case "blue": 43 | color = "bg-gradient-to-br from-blue-700 via-blue-500 to-blue-400"; 44 | break; 45 | 46 | case "purple": 47 | color = "bg-gradient-to-br from-purple-700 via-purple-600 to-purple-400"; 48 | break; 49 | 50 | case "orange": 51 | color = "bg-gradient-to-br from-yellow-700 via-yellow-600 to-yellow-400"; 52 | break; 53 | case "pink-blue": 54 | color = "bg-gradient-to-br from-blue-800 via-purple-600 to-pink-500"; 55 | break; 56 | } 57 | 58 | const ParentContainer = { 59 | init: { 60 | x: 0 61 | }, 62 | load: { 63 | x: 0, 64 | transition: { 65 | duration: 1, 66 | ease: "easeInOut", 67 | staggerChildren: 0.25 68 | } 69 | } 70 | } 71 | 72 | const ChildrenElems = { 73 | init: { 74 | y: -250, 75 | opacity: 0 76 | }, 77 | load: { 78 | y: 0, 79 | opacity: 1, 80 | transition: { 81 | duration: 1, 82 | ease: [0, 1.5, 0.75, 0.75], 83 | } 84 | } 85 | } 86 | 87 | const copyToClipboard = () => { 88 | 89 | const el = document.createElement('textarea'); 90 | el.value = window.location.href; 91 | document.body.appendChild(el); 92 | el.select(); 93 | document.execCommand('copy'); 94 | document.body.removeChild(el); 95 | 96 | document.querySelector("#copyLink")!.innerHTML = "📋 Copied!"; 97 | setTimeout(() => { 98 | document.querySelector("#copyLink")?.classList.add("opacity-0"); 99 | }, 1000); 100 | 101 | }; 102 | 103 | setTimeout(() => { 104 | document.querySelector("#copyLink")?.classList.remove("opacity-0"); 105 | }, 4 * 1000); 106 | 107 | let stagger = 0; 108 | setTimeout(() => { 109 | document.querySelectorAll(".wordLine").forEach(elem => { 110 | setTimeout(() => {elem.classList.add("animate-bounce")}, stagger) 111 | stagger += 250; 112 | }) 113 | }, 950) 114 | 115 | return ( 116 | <> 117 |
118 | 119 | Happy  120 | Birthday,  121 | {name}! 122 | 123 | From {from} 124 | 125 | 🔗 Copy Link to Share 126 | 127 |
128 | 129 | 130 |
131 | 132 | 133 | 134 |
135 | 136 | 137 | 144 | 145 | 146 | ); 147 | } 148 | 149 | export default Bday; -------------------------------------------------------------------------------- /src/pages/Home.tsx: -------------------------------------------------------------------------------- 1 | import { motion } from "framer-motion"; 2 | import { useEffect, useState } from "react"; 3 | import { useHistory } from "react-router-dom"; 4 | 5 | //custom font tailwind - ubuntu 6 | 7 | const Home = () => { 8 | let history = useHistory(); 9 | 10 | let colorArr = ['Red', 'Purple', 'Green', 'Blue', 'Orange', 'Pink-Blue']; 11 | 12 | let [dropdownOpen, setDropdownOpen] = useState(false); 13 | let [colorSelected, setColorSelected] = useState('Pink-Blue'); 14 | let [menuColor, setMenuColor] = useState('Pink-Blue'); 15 | let [backgroundGradient, setBackgroundGradient] = useState("bg-gradient-to-br from-blue-800 via-purple-600 to-pink-500"); 16 | 17 | const errorBox = (msg:string) => { 18 | let errorBox = document.querySelector("#errorBox")!; 19 | errorBox.innerHTML = msg; 20 | errorBox.classList.remove("opacity-0"); 21 | errorBox.classList.add("opacity-1"); 22 | 23 | setTimeout(() => { 24 | errorBox.classList.add("opacity-0"); 25 | errorBox.classList.remove("opacity-1"); 26 | }, 2000); 27 | } 28 | 29 | 30 | const generateUrl = () => { 31 | let colorSelect = colorSelected.toLowerCase(); 32 | 33 | let nameValue = document.querySelector("#nameInput")!.value; 34 | if(!nameValue) return errorBox("Please provide the recipient's name!"); 35 | 36 | nameValue = nameValue.charAt(0).toUpperCase() + nameValue.slice(1); 37 | 38 | let fromValue = document.querySelector("#fromInput")!.value; 39 | if(!fromValue) return errorBox("Please provide your name!"); 40 | 41 | fromValue = fromValue.charAt(0).toUpperCase() + fromValue.slice(1); 42 | 43 | 44 | history.push(`/bday?name=${nameValue}&from=${fromValue}&color=${colorSelect}`); 45 | } 46 | 47 | useEffect(() => { 48 | let color = colorSelected.toLowerCase(); 49 | 50 | switch(color) { 51 | case "green": 52 | setBackgroundGradient("bg-gradient-to-br from-green-600 via-green-500 to-green-400"); 53 | setMenuColor("bg-green-600"); 54 | break; 55 | 56 | case "red": 57 | setBackgroundGradient("bg-gradient-to-br from-red-600 via-red-500 to-red-400"); 58 | setMenuColor("bg-red-500"); 59 | break; 60 | 61 | case "blue": 62 | setBackgroundGradient("bg-gradient-to-br from-blue-700 via-blue-500 to-blue-400"); 63 | setMenuColor("bg-blue-600"); 64 | break; 65 | 66 | case "purple": 67 | setBackgroundGradient("bg-gradient-to-br from-purple-700 via-purple-600 to-purple-400"); 68 | setMenuColor("bg-purple-700"); 69 | break; 70 | 71 | case "orange": 72 | setBackgroundGradient("bg-gradient-to-br from-yellow-700 via-yellow-600 to-yellow-400"); 73 | setMenuColor("bg-yellow-600"); 74 | break; 75 | 76 | case "pink-blue": 77 | setBackgroundGradient("bg-gradient-to-br from-blue-800 via-purple-600 to-pink-500"); 78 | setMenuColor("bg-purple-700"); 79 | break; 80 | } 81 | }, [colorSelected]) 82 | 83 | const parentAnim = { 84 | init: { 85 | opacity: 0, 86 | }, 87 | load: { 88 | opacity: 1, 89 | transition: { 90 | duration: 0, 91 | ease: "easeInOut", 92 | staggerChildren: 0.5 93 | } 94 | } 95 | } 96 | 97 | const childAnim = { 98 | init: { 99 | opacity: 0, 100 | x: -25 101 | }, 102 | load: { 103 | opacity: 1, 104 | x: 0, 105 | transition: { 106 | duration: 0.5, 107 | ease: "easeInOut", 108 | } 109 | } 110 | } 111 | 112 | const dropMenuChange = (toggled:boolean) => { 113 | let element = document.querySelector("#dropdownOptions"); 114 | 115 | if(toggled){ 116 | element?.classList.remove("hidden"); 117 | setTimeout(() => { 118 | element?.classList.remove("opacity-0"); 119 | }, 10); 120 | } else { 121 | element?.classList.add("opacity-0"); 122 | setTimeout(() => { 123 | element?.classList.add("hidden"); 124 | }, 200); 125 | } 126 | 127 | } 128 | 129 | return ( 130 | 131 | 132 | 🎉 hbd.today 🎉 133 | send a simple, fun, virtual birthday card! 134 | 135 | 136 | 137 | 138 |
{setDropdownOpen(!dropdownOpen)}} className="hover:bg-gray-800 rounded-md transition-all duration-100 hover:bg-opacity-25 select-none cursor-pointer outline-none border-solid border-white border text-white text-xl w-40 py-3 text-center flex flex-row items-center justify-center"> 139 |
{colorSelected}
140 | 141 | 142 | 143 |
144 | 145 | 150 | 151 | 152 | 153 | 154 | Generate Card 155 | 156 | 157 | 158 |
159 |
160 | ); 161 | } 162 | 163 | export default Home; 164 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], 3 | darkMode: false, // or 'media' or 'class' 4 | theme: { 5 | extend: { 6 | colors: { 7 | 'dropdown':'#6636C6', 8 | }, 9 | fontSize: { 10 | 'bigfont': '12rem', 11 | }, 12 | fontFamily: { 13 | 'segoeui': ['Segoe UI', 'sans-serif'] 14 | }, 15 | }, 16 | }, 17 | variants: { 18 | extend: { 19 | backgroundColor: ['active'], 20 | }, 21 | }, 22 | plugins: [], 23 | } 24 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------