├── .gitignore
├── LICENSE
├── README.md
├── components
├── Character.js
└── Word.js
├── hooks
├── useClickOutside.js
├── useLocalStorage.js
└── useTimer.js
├── package-lock.json
├── package.json
├── pages
├── _app.js
├── api
│ └── hello.js
└── index.js
├── postcss.config.js
├── public
├── CodeType.png
├── favicon.ico
└── vercel.svg
├── staticData.js
├── styles
└── global.css
└── tailwind.config.js
/.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 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Siddhesh Kothadi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # code-type
2 |
3 | ### Typing practice for programmers
4 |
5 | Practice code-typing with top 1000 keywords of the most popular programming languages.
6 |
7 |
8 |
9 | ### Getting Started
10 |
11 | These instructions will help you to set up the app on your local machine for development
12 |
13 | #### Prerequisites
14 |
15 | - Node with npm
16 |
17 | #### Setting Up
18 |
19 |
20 | Clone the repository with the command git clone https://github.com/siddheshkothadi/code-type.git
21 | Enter the cloned repository by running the command cd code-type
22 | Install the dependencies with the command npm install
23 | Run the app with the command npm run dev
. The app will run on port 3000 by default.
24 | Open http://localhost:3000
on your browser to view the app.
25 |
26 |
27 | ### Supported Languages
28 |
29 | - Javascript - .js
30 | - React - .jsx
31 | - Cascading Style Sheets - .css
32 | - HTML - .html
33 | - Java - .java
34 | - Python - .py
35 | - Lua - .lua
36 | - PHP - .php
37 | - Ruby - .rb
38 | - C++ - .cpp
, .c
, .cc
, .cxx
, .h
39 | - Perl - .pm
40 | - C# - .cs
41 | - Scala - .scala
42 | - Go - .go
43 | - SQL - .sql
44 | - Rust - .rs
45 | - Lisp - .lisp
, .lsp
46 | - Clojure - .clj
, .cljs
47 | - Kotlin - .kt
, .kts
, .ktm
48 | - CMake - .cmake
, .cmake.in
49 | - Swift - .swift
50 | - Haskell - .hs
, .hsc
51 | - Elixir - .ex
, .exs
52 | - Objective-C/C++ - .m
, .mm
53 | - F# - .fs
, .fsi
, .fsx
54 | - ELM - .elm
55 | - PureScript - .purs
56 | - Pascal - .pas
57 | - R - .r
58 | - Erlang - .erl
59 | - VimL - .vim
60 | - Groovy - .groovy
61 |
62 | ### Preview and Screenshots
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | ### References
72 |
73 | - Data Source
74 |
--------------------------------------------------------------------------------
/components/Character.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function Character(props) {
4 | const { char, color, isLastTypedCharacter, isStartOfNewWord } = props;
5 |
6 | return (
7 |
26 | {char}
27 |
28 | );
29 | }
30 |
31 | export default Character;
32 |
--------------------------------------------------------------------------------
/components/Word.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Character from "./Character";
3 |
4 | function getColor(index, wordTyped, actualWord) {
5 | if (index < wordTyped.length && index < actualWord.length) {
6 | if (wordTyped[index] === actualWord[index]) {
7 | return "#fca6d1";
8 | }
9 | } else if (index < actualWord.length && index >= wordTyped.length) {
10 | return "#99d6ea";
11 | }
12 | return "#fffb85";
13 | }
14 |
15 | function Word(props) {
16 | const { wordTyped, actualWord, isLastTypedWord } = props;
17 |
18 | if (wordTyped === undefined) {
19 | return (
20 |
21 | {actualWord.split("").map((char, index) => {
22 | return ;
23 | })}
24 |
25 | );
26 | }
27 |
28 | return (
29 |
30 | {actualWord.split("").map((char, index) => {
31 | return (
32 |
43 | );
44 | })}
45 | {wordTyped.length > actualWord.length &&
46 | wordTyped.split("").map((char, index) => {
47 | if (index >= actualWord.length) {
48 | return (
49 |
57 | );
58 | }
59 | })}
60 |
61 | );
62 | }
63 |
64 | export default Word;
65 |
--------------------------------------------------------------------------------
/hooks/useClickOutside.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function useClickOutside(ref, handler) {
4 | React.useEffect(() => {
5 | document.addEventListener("mousedown", handler);
6 | return () => {
7 | document.removeEventListener("mousedown", handler);
8 | };
9 | }, [ref]);
10 | }
11 |
12 | export default useClickOutside;
13 |
--------------------------------------------------------------------------------
/hooks/useLocalStorage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function useLocalStorage(key, defaultValue) {
4 | const [chosenValue, setChosenValue] = React.useState(defaultValue);
5 |
6 | React.useEffect(() => {
7 | const value = localStorage.getItem(key);
8 | if (value) {
9 | setChosenValue(value);
10 | }
11 | }, []);
12 |
13 | return [chosenValue, setChosenValue];
14 | }
15 |
16 | export default useLocalStorage;
17 |
--------------------------------------------------------------------------------
/hooks/useTimer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function useTimer(initialTime, toggle = true) {
4 | const [seconds, setSeconds] = React.useState(initialTime);
5 |
6 | React.useEffect(() => {
7 | let interval = null;
8 |
9 | if (toggle === true) {
10 | interval = setInterval(() => {
11 | setSeconds((seconds) => seconds + 1);
12 | }, 1000);
13 | } else {
14 | clearInterval(interval);
15 | }
16 | return () => {
17 | clearInterval(interval);
18 | };
19 | }, [toggle]);
20 |
21 | return [seconds, setSeconds];
22 | }
23 |
24 | export default useTimer;
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev": "next dev",
5 | "build": "next build",
6 | "start": "next start"
7 | },
8 | "dependencies": {
9 | "@fortawesome/fontawesome-svg-core": "^1.2.36",
10 | "@fortawesome/free-brands-svg-icons": "^5.15.4",
11 | "@fortawesome/free-solid-svg-icons": "^5.15.4",
12 | "@fortawesome/react-fontawesome": "^0.1.16",
13 | "next": "latest",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2"
16 | },
17 | "devDependencies": {
18 | "autoprefixer": "^10.2.6",
19 | "postcss": "^8.3.5",
20 | "tailwindcss": "^2.2.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | // import "tailwindcss/tailwind.css";
2 | import "../styles/global.css";
3 |
4 | function MyApp({ Component, pageProps }) {
5 | return ;
6 | }
7 |
8 | export default MyApp;
9 |
--------------------------------------------------------------------------------
/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default function helloAPI(req, res) {
4 | res.status(200).json({ name: 'John Doe' })
5 | }
6 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import Head from "next/head";
2 | import React from "react";
3 | import Word from "../components/Word";
4 | import useClickOutside from "../hooks/useClickOutside";
5 | import useLocalStorage from "../hooks/useLocalStorage";
6 | import useTimer from "../hooks/useTimer";
7 | import { languages, wordLengths } from "../staticData";
8 |
9 | export default function Home() {
10 | const [wordLength, setWordLength] = React.useState(10);
11 | const [words, setWords] = React.useState("");
12 | const [typedWords, setTypedWords] = React.useState("");
13 | const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
14 | const [chosenLanguage, setChosenLanguage] = useLocalStorage("language", "js");
15 | const [toggle, setToggle] = React.useState(false);
16 | const [typingStarted, setTypingStarted] = React.useState(false);
17 | const [seconds, setSeconds] = useTimer(0, typingStarted);
18 | const [prevAccuracy, setPrevAccuracy] = useLocalStorage("accuracy", null);
19 | const [prevWPM, setPrevWPM] = useLocalStorage("wpm", null);
20 | const [isInputFocused, setIsInputFocused] = React.useState(true);
21 |
22 | const dropdownRef = React.useRef(null);
23 | useClickOutside(dropdownRef, function handleClickOutside(event) {
24 | if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
25 | setIsDropdownOpen(false);
26 | }
27 | });
28 |
29 | const typingAreaRef = React.useRef(null);
30 | useClickOutside(typingAreaRef, function handleClickOutside(event) {
31 | if (
32 | typingAreaRef.current &&
33 | !typingAreaRef.current.contains(event.target)
34 | ) {
35 | setIsInputFocused(false);
36 | }
37 | });
38 |
39 | const calculateWPM = () => {
40 | const wordsPerMinute =
41 | (typedWords.split(" ").filter((word) => {
42 | return word.length > 0;
43 | }).length /
44 | seconds) *
45 | 60;
46 | return wordsPerMinute.toFixed(2);
47 | };
48 |
49 | const calculateAccuracy = (typedSentence) => {
50 | const typedWordsArray = typedSentence.split(" ");
51 | const wordsArray = words.split(" ");
52 | let correctCharacters = 0;
53 | let totalCharacters = 0;
54 | wordsArray.forEach((word, index) => {
55 | const charactersInTypedWords = typedWordsArray[index].split("");
56 | const charactersInWords = word.split("");
57 | charactersInWords.forEach((character, index) => {
58 | totalCharacters++;
59 | if (charactersInTypedWords[index] === character) {
60 | correctCharacters++;
61 | }
62 | });
63 | if (charactersInTypedWords.length > charactersInWords.length) {
64 | totalCharacters +=
65 | charactersInTypedWords.length - charactersInWords.length;
66 | }
67 | });
68 | const accuracy = correctCharacters / totalCharacters;
69 | return accuracy.toFixed(2);
70 | };
71 |
72 | const typingFinished = (typedSentence) => {
73 | const accuracy = calculateAccuracy(typedSentence);
74 | const wpm = calculateWPM();
75 | localStorage.setItem("accuracy", accuracy);
76 | localStorage.setItem("wpm", wpm);
77 | setPrevAccuracy(accuracy);
78 | setPrevWPM(wpm);
79 | setTypingStarted(false);
80 | setSeconds(0);
81 | setTypedWords("");
82 | setToggle(!toggle);
83 | };
84 |
85 | React.useEffect(() => {
86 | localStorage.setItem("language", chosenLanguage);
87 | fetch(
88 | `https://siddheshkothadi.github.io/APIData/language-keywords/${chosenLanguage}.json`
89 | )
90 | .then((res) => res.json())
91 | .then((data) => {
92 | let selectedWords = [];
93 |
94 | while (selectedWords.length < wordLength) {
95 | let random = Math.floor(Math.random() * data.words.length);
96 | if (!selectedWords.includes(data.words[random])) {
97 | selectedWords.push(data.words[random].word);
98 | }
99 | }
100 | setWords(selectedWords.join(" "));
101 | window.document.getElementById("type-box").focus();
102 | setIsInputFocused(true);
103 | })
104 | .catch((err) => {
105 | console.log(err);
106 | });
107 | }, [toggle, chosenLanguage, wordLength]);
108 |
109 | if (words.length === 0) {
110 | return (
111 |
116 | );
117 | }
118 |
119 | return (
120 |
121 |
122 |
CodeType
123 |
124 |
128 |
134 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | {`${typedWords.split(" ").length - 1}/${
146 | words.split(" ").length
147 | }`}
148 |
149 |
150 | {seconds}
151 |
152 |
153 |
154 | {wordLengths.map((length, index) => (
155 |
setWordLength(length)}
165 | key={length}
166 | >
167 | {length}
168 |
169 | ))}
170 |
171 |
172 |
173 |
setIsDropdownOpen(!isDropdownOpen)}>
174 | {chosenLanguage}
175 |
176 | {isDropdownOpen && (
177 |
185 | {languages.map((language) => (
186 |
{
194 | setWords("");
195 | setTypedWords("");
196 | setChosenLanguage(language);
197 | setIsDropdownOpen(false);
198 | }}
199 | >
200 | {language}
201 |
202 | ))}
203 |
204 | )}
205 |
206 |
207 |
{
211 | window.document.getElementById("type-box").focus();
212 | setIsInputFocused(true);
213 | }}
214 | >
215 | {words.split(" ").map((word, index) => {
216 | return (
217 |
223 | );
224 | })}
225 | {!isInputFocused && (
226 |
227 |
228 | Click or tap to focus
229 |
230 |
231 | )}
232 |
233 |
{
240 | if (typingStarted === false && e.target.value.length > 0) {
241 | setTypingStarted(true);
242 | }
243 | setTypedWords(e.target.value);
244 | if (
245 | e.target.value.split(" ").length > words.split(" ").length ||
246 | (e.target.value.split(" ").length === words.split(" ").length &&
247 | e.target.value.split(" ")[e.target.value.split(" ").length - 1]
248 | .length ===
249 | words.split(" ")[words.split(" ").length - 1].length)
250 | ) {
251 | typingFinished(e.target.value);
252 | }
253 | }}
254 | value={typedWords}
255 | autoFocus
256 | />
257 |
258 | {prevAccuracy !== null && (
259 |
260 | {`acc: ${prevAccuracy * 100}%`}
261 |
262 | )}
263 |
{
266 | setTypedWords("");
267 | setSeconds(0);
268 | setTypingStarted(false);
269 | setToggle(!toggle);
270 | }}
271 | >
272 | Refresh
273 |
274 | {prevWPM !== null && (
275 |
{`wpm: ${prevWPM}`}
276 | )}
277 |
278 |
279 |
280 | );
281 | }
282 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | // If you want to use other PostCSS plugins, see the following:
2 | // https://tailwindcss.com/docs/using-with-preprocessors
3 | module.exports = {
4 | plugins: {
5 | tailwindcss: {},
6 | autoprefixer: {},
7 | },
8 | }
9 |
--------------------------------------------------------------------------------
/public/CodeType.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siddheshkothadi/code-type/8cbcc3b06f8071db3fce3d0dc4de3b165d389116/public/CodeType.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siddheshkothadi/code-type/8cbcc3b06f8071db3fce3d0dc4de3b165d389116/public/favicon.ico
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/staticData.js:
--------------------------------------------------------------------------------
1 | const languages = [
2 | "js",
3 | "jsx",
4 | "html",
5 | "css",
6 | "java",
7 | "py",
8 | "lua",
9 | "php",
10 | "rb",
11 | "cpp",
12 | "pl",
13 | "cs",
14 | "scala",
15 | "go",
16 | "sql",
17 | "rs",
18 | "lisp",
19 | "clj",
20 | "kt",
21 | "cmake",
22 | "swift",
23 | "hs",
24 | "ex",
25 | "objc",
26 | "fs",
27 | "elm",
28 | "purs",
29 | "pas",
30 | "r",
31 | "erl",
32 | "vim",
33 | "groovy",
34 | ];
35 |
36 | const wordLengths = [10, 25, 60];
37 |
38 | export { languages, wordLengths };
39 |
--------------------------------------------------------------------------------
/styles/global.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;600;700;800;900&display=swap");
6 | @import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@100;200;300;400;500;600;700;800&display=swap");
7 | @import url("https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@100;200;300;400;500;600;700&display=swap");
8 |
9 | html,
10 | body {
11 | padding: 0;
12 | margin: 0;
13 | /* font-family: "Poppins", sans-serif; */
14 | background-color: #1b1d36;
15 | /* font-family: "JetBrains Mono", monospace; */
16 | font-family: "Roboto Mono", monospace;
17 |
18 | /* 80s after dark */
19 | --bg-color: #1b1d36;
20 | --main-color: #fca6d1;
21 | --caret-color: #99d6ea;
22 | --sub-color: #99d6ea;
23 | --text-color: #fca6d1;
24 | --error-color: #fffb85;
25 | }
26 |
27 | .containerWithoutScrollbar {
28 | -ms-overflow-style: none; /* Internet Explorer 10+ */
29 | scrollbar-width: none; /* Firefox */
30 | }
31 |
32 | .containerWithoutScrollbar::-webkit-scrollbar {
33 | display: none; /* Safari and Chrome */
34 | width: 0;
35 | background: transparent;
36 | }
37 |
38 | .bgColor {
39 | background-color: var(--bg-color);
40 | }
41 |
42 | .mainColor {
43 | color: var(--main-color);
44 | }
45 |
46 | .caretColor {
47 | color: var(--caret-color);
48 | }
49 |
50 | .subColor {
51 | color: var(--sub-color);
52 | }
53 |
54 | .textColor {
55 | color: var(--text-color);
56 | }
57 |
58 | .errorColor {
59 | color: var(--error-color);
60 | }
61 |
62 | code {
63 | font-family: "JetBrains Mono", monospace;
64 | }
65 |
66 | * {
67 | box-sizing: border-box;
68 | }
69 |
70 | @layer utilities {
71 | .noSelect {
72 | -webkit-touch-callout: none; /* iOS Safari */
73 | -webkit-user-select: none; /* Safari */
74 | -khtml-user-select: none; /* Konqueror HTML */
75 | -moz-user-select: none; /* Firefox */
76 | -ms-user-select: none; /* Internet Explorer/Edge */
77 | user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
78 | }
79 |
80 | .blinkLeft {
81 | animation: blinkLeft 1s infinite;
82 | }
83 |
84 | .blinkRight {
85 | animation: blinkRight 1s infinite;
86 | }
87 | }
88 |
89 | @keyframes blinkLeft {
90 | 0% {
91 | border-left-color: rgba(241, 250, 140, 0);
92 | }
93 | 50% {
94 | border-left-color: var(--caret-color);
95 | }
96 | 100% {
97 | border-left-color: rgba(241, 250, 140, 0);
98 | }
99 | }
100 |
101 | @keyframes blinkRight {
102 | 0% {
103 | border-right-color: rgba(241, 250, 140, 0);
104 | }
105 | 50% {
106 | border-right-color: var(--caret-color);
107 | }
108 | 100% {
109 | border-right-color: rgba(241, 250, 140, 0);
110 | }
111 | }
112 |
113 | /* Loader */
114 | #preloader {
115 | position: fixed;
116 | top: 0;
117 | left: 0;
118 | width: 100%;
119 | height: 100%;
120 | }
121 |
122 | #loader {
123 | display: block;
124 | position: relative;
125 | left: 50%;
126 | top: 50%;
127 | width: 150px;
128 | height: 150px;
129 | margin: -75px 0 0 -75px;
130 | border-radius: 50%;
131 | border: 3px solid transparent;
132 | border-top-color: #9370db;
133 | -webkit-animation: spin 2s linear infinite;
134 | animation: spin 2s linear infinite;
135 | }
136 |
137 | #loader:before {
138 | content: "";
139 | position: absolute;
140 | top: 5px;
141 | left: 5px;
142 | right: 5px;
143 | bottom: 5px;
144 | border-radius: 50%;
145 | border: 3px solid transparent;
146 | border-top-color: #ba55d3;
147 | -webkit-animation: spin 3s linear infinite;
148 | animation: spin 3s linear infinite;
149 | }
150 |
151 | #loader:after {
152 | content: "";
153 | position: absolute;
154 | top: 15px;
155 | left: 15px;
156 | right: 15px;
157 | bottom: 15px;
158 | border-radius: 50%;
159 | border: 3px solid transparent;
160 | border-top-color: #ff00ff;
161 | -webkit-animation: spin 1.5s linear infinite;
162 | animation: spin 1.5s linear infinite;
163 | }
164 |
165 | @-webkit-keyframes spin {
166 | 0% {
167 | -webkit-transform: rotate(0deg);
168 | -ms-transform: rotate(0deg);
169 | transform: rotate(0deg);
170 | }
171 | 100% {
172 | -webkit-transform: rotate(360deg);
173 | -ms-transform: rotate(360deg);
174 | transform: rotate(360deg);
175 | }
176 | }
177 |
178 | @keyframes spin {
179 | 0% {
180 | -webkit-transform: rotate(0deg);
181 | -ms-transform: rotate(0deg);
182 | transform: rotate(0deg);
183 | }
184 | 100% {
185 | -webkit-transform: rotate(360deg);
186 | -ms-transform: rotate(360deg);
187 | transform: rotate(360deg);
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | mode: "jit",
3 | purge: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
4 | darkMode: false, // or 'media' or 'class'
5 | theme: {
6 | extend: {
7 | // Dracula color palette
8 | colors: {
9 | drBackground: "#282a36",
10 | drCurrentLine: "#44475a",
11 | drForeGround: "#f8f8f2",
12 | drComment: "#6272a4",
13 | drCyan: "#8be9fd",
14 | drGreen: "#50fa7b",
15 | drOrange: "#ffb86c",
16 | drPink: "#ff79c6",
17 | drPurple: "#bd93f9",
18 | drRed: "#ff5555",
19 | drYellow: "#f1fa8c",
20 |
21 | // 80s after dark
22 | adBgColor: "#1b1d36",
23 | adMainColor: "#fca6d1",
24 | adCaretColor: "#99d6ea",
25 | adSubColor: "#99d6ea",
26 | adTextColor: "#fca6d1",
27 | adErrorColor: "#fffb85",
28 | },
29 | },
30 | },
31 | variants: {
32 | extend: {},
33 | },
34 | plugins: [],
35 | };
36 |
--------------------------------------------------------------------------------