HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URLs added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>ImoType</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="caps-lock-warning" style="display: none;">
<p>Caps Lock is on.</p>
</div>
<div class="wrapper">
<input type="text" class="input-field">
<div class="content-box">
<div class="typing-text">
<p></p>
</div>
<div class="content">
<ul class="result-detail">
<li class="time">
<p>Time:</p>
<span><b>60s</b></span>
</li>
<li class="mistake">
<p>Mistakes:</p>
<span>0</span>
</li>
<li class="WPM">
<p>WPM:</p>
<span>60</span>
</li>
<li class="CPM">
<p>CPM:</p>
<span>60</span>
</li>
</ul>
<div class="spotify-play">
<span></span>
</div>
<button>Try Again</button>
</div>
</div>
</div>
</div>
<script src="js/paragraph.js"></script>
<script src="js/script.js"></script>
</body>
</html>
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@1000&family=Zen+Kaku+Gothic+Antique&display=swap');
* {
box-sizing: border-box;
font-family: 'Zen Kaku Gothic Antique', sans-serif;
}
h1 {
font-family: 'Nunito', sans-serif;
}
body {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
min-height: 100vh;
color: #fcf7ec;
background: #0e0e0e;
cursor: url('images/whiterabbitcursor.gif'),auto;
}
.wrapper {
width: 1000px;
background: #1a1a1a;
padding: 20px;
border-radius: 20px;
}
.wrapper .input-field{
z-index:-999;
opacity: 0;
position: absolute;
}
.wrapper .content-box {
padding: 15px 20px 0;
display: flex;
}
.content-box .typing-text {
display: flex;
max-height: 255px;
padding-right:10px;
overflow-y: auto;
}
.typing-text::-webkit-scrollbar {
width: 0;
}
.typing-text {
color: #ffffff;
font-size: 21px;
letter-spacing: 1px;
text-align: justify;
}
.typing-text p span.incorrect{
color:red;
background-color: rgb(236, 205, 194);
border-radius: 3px;
}
.typing-text p span.active {
color: #b6b6b6;
position: relative;
}
.typing-text p span.active::before{
content:"";
position: absolute;
left: 0;
bottom: 0;
height:2px;
width: 100%;
background: #a02828;
animation: blink 4s ease-in-out infinite;
}
@keyframes blink
{
50%{
opacity: 1;
}
}
.content-box .content {
display: flex;
flex-direction: column;
align-content: center;
margin-top: 17px;
margin-left: 10px;
min-width:250px;
align-items: center;
}
.content .result-detail {
justify-content: space-between;
}
.result-detail li {
height: 22px;
display: flex;
list-style: none;
align-items: center;
left:-10px;
position: relative;
}
.result-detail li p {
font-size: 19px;
}
.result-detail li span {
display: block;
list-style: none;
font-size: 20px;
margin-left: 10px;
}
.result-detail li b{
font-weight: 700;
}
.content button {
border-radius: 10px;
border:none;
outline:none;
width:105px;
background:#000000;
height:35px;
font-size: 15px;
color:#ffffff;
font-weight: 1000;
transition: transform 0.3s ease;
}
.content button:active {
transform: scale(0.95);
}
.content button:hover {
cursor: pointer;
background:#383838;
}
const paragraphs = [
"Keep on walkin' Ain't no stoppin' in this dirty, filthy, rotten Nasty little world we call our home They get blickies poppin' Ain't no option for my partners So they resort to scams and robbin' Take away stress, we ganja-coppin' Blow it all out, it's all forgotten Walkin' with my back against the sun I've been runnin' all my life, that's way before my life begun Since my birth and seconds on Earth, I been the first one to confront All of these cycles that get recycled, makin' it stifle while I stunt Roll me a blunt so I forget it But it make the details look so vivid ",
"It's tomato or tomato, either way, the boy the greatest Play it, I won't say it no more I was just fucked up, I was just down, down bad I had to tighten the fuck up, but I'm here for the crown Board of Education vs. Brown I was bored of education, left the town Fuck a résumé and fuck a cap and gown Fuck a background check, back 'round when I get the check",
"She throw it back, hands on her knees I got the racks, so bust the cheeks She love to dance, but not for free So name the price, I'll pay the fee You know that ass want it, I put this cash on it The way you bounce that ass, you make them bitches mad, don't you? You want somebody to come and take care your niece, huh? 'Cause you a bad bitch and you don't fuck with fleas, huh?",
"Yellow diamonds in the watch, this shit cost a lot Never send a bitch your dot, that's how you get shot I DM in vanish mode, I do that shit a lot Took her panties off and this bitch thicker than the plot All my exes ain't nothin', them hoes busted If my opps ain't rappin', they ass duckin' You ain't ready to pull the trigger, don't clutch it I know you on your period baby, can you suck it?",
"These bitches love Sosa, O end or no end Fuckin' with them O boys, you gon' get fucked over 'Raris and Rovers, these hoes love Chief Sosa Hit him with that Cobra, now that boy slumped over They do it all for Sosa, you boys ain't making no noise Y'all know I'm a grown boy, your clique full of broke boys God, y'all some broke boys, God, y'all some broke boys We GBE dope boys, we got lots of dough, boy",
"I got a Draco in my bookbag, lil' bitch Yeah, woo That motherfucker got a kickback on it Yeah, woo That motherfucker got a kickback on it Yeah, woo And I put this on Onyx, I'ma leave my opp in the cement Lay that bitch down in the yard like a picnic He even ain't got no gun, how the fuck he flexin'? We gon' push up, bitch, lil' bitch We gon' push up, bitch, lil' bitch Pull up And I'm takin' shit",
"This ain't what you want, This ain't what you want Ha! 1600 block, I just wanna rock shake it down I just wanna, ah, ah, ah, ah, ah, ah, ah I just wanna rock, body-ody, yeah shake it down Shawty got that body-ody, ah, ah, ah shake it down Hit her once, no ties shake it-shake it How the fuck you gon' kill my vibe? Shake it down Stand on my money, don't know my size shake it-shake it Pick them sides, and you better choose wisely shake it-shake it down-down That's my high, one, two, three, four, throw up the five shake it-shake it"
];
const spotify = [
"<iframe style='border-radius:12px' src='https://open.spotify.com/embed/track/1q8DwZtQen5fvyB7cKbShC?utm_source=generator' width='100%' height='152' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"<iframe style='border-radius:12px' src='https://open.spotify.com/embed/track/36J0iaPDGbYGsHvYpaYs3k?utm_source=generator' width='100%' height='152' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"<iframe style='border-radius:12px' src='https://open.spotify.com/embed/track/27F7BWz4WuC8GuSIcFbx1a?utm_source=generator' width='100%' height='152' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"<iframe style='border-radius:12px' src='https://open.spotify.com/embed/track/1bDbXMyjaUIooNwFE9wn0N?utm_source=generator' width='100%' height='152' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"<iframe style='border-radius:12px' src='https://open.spotify.com/embed/track/01Lr5YepbgjXAWR9iOEyH1?utm_source=generator' width='100%' height='152' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"<iframe style='border-radius:12px' src='https://open.spotify.com/embed/track/3dl8bSF08LQfCf4T6CCksf?utm_source=generator' width='100%' height='152' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
"<iframe style='border-radius:12px' src='https://open.spotify.com/embed/track/4FyesJzVpA39hbYvcseO2d?utm_source=generator' width='100%' height='152' frameBorder='0' allowfullscreen='' allow='autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' loading='lazy'></iframe>",
];
const typingText = document.querySelector(".typing-text p");
inpField = document.querySelector(".wrapper .input-field");
mistakeTag = document.querySelector(".mistake span");
wpmTag = document.querySelector(".WPM span");
cpmTag = document.querySelector(".CPM span");
resetBtn = document.querySelector("button");
timeTag = document.querySelector(".time span b");
spotifyTag = document.querySelector(".spotify-play span");
warningMessage = document.getElementById("caps-lock-warning");
let timer,
maxTime = 60,
timeLeft = maxTime,
charIndex = (mistakes = isTyping = 0);
//function for selecting random paragraph
function randomParagraph() {
//select a random number thats always less than the length of the paragraph array
let randIndex = Math.floor(Math.random() * paragraphs.length);
typingText.innerHTML = "";
spotifyTag.innerHTML = spotify[randIndex];
//selecting a random pragraph and then splitting each individual character
//add each character into a span tag, which should then be inside a p tag
paragraphs[randIndex].split("").forEach((span) => {
let spanTag = `<span>${span}</span>`;
typingText.innerHTML += spanTag;
});
//focus on the input field either when key pressed or click
document.addEventListener("keydown", () => inpField.focus());
typingText.addEventListener("click", () => inpField.focus());
}
//initiate typing function
function initTyping() {
//find charcters in the span tag
// typedchar variable is set to typed characters split in the input box
const characters = typingText.querySelectorAll("span");
let typedChar = inpField.value.split("")[charIndex];
if (charIndex < characters.length - 1 && timeLeft > 0) {
if (!isTyping) {
//if not typing then start timer, otherwise don't restart
timer = setInterval(initTimer, 1000);
isTyping = true;
}
//if the user hasn't entered a charcter, or if they press backspace
if (typedChar == null) {
//go back one index and decrement charINdex
charIndex--;
//decrement mistakes only if the charIndex span has incorrect class
if (characters[charIndex].classList.contains("incorrect")) {
mistakes--;
}
//remove charindex span correct or incorrect
characters[charIndex].classList.remove("correct", "incorrect");
} else {
//if correct character is typed then assign correct to the span
if (characters[charIndex].innerText == typedChar) {
characters[charIndex].classList.add("correct");
} else {
//if incorrect character is typed then assign incorrect to the span and increase mistakes
mistakes++;
characters[charIndex].classList.add("incorrect");
}
//increment char index by one
charIndex++;
}
//remove the previous active character and add the new charindex as active
characters.forEach((span) => span.classList.remove("active"));
characters[charIndex].classList.add("active");
const elapsedSeconds = maxTime - timeLeft;
//calculate wpm
const numCompleteWords = typingText.innerText
.slice(0, charIndex)
.trim()
.split(" ").length;
let wpm = Math.round(numCompleteWords / (elapsedSeconds / 60));
// set wpm to 0 if the wpm is less than 0, null or infinity
wpm = wpm < 0 || !wpm || wpm === Infinity ? 0 : wpm;
//check if caps is on
inpField.addEventListener("keyup", function (event) {
const capsLockOn = event.getModifierState("CapsLock");
if (capsLockOn) {
warningMessage.style.display = "block";
} else {
warningMessage.style.display = "none";
}
});
//update text in result detail parameters
mistakeTag.innerText = mistakes;
cpmTag.innerText = charIndex - mistakes;
wpmTag.innerText = wpm;
} else {
inpField.value = "";
clearInterval(timer);
}
}
//function for starting the timer
function initTimer() {
//if time left is greater than timer then decrement timer
if (timeLeft > 0) {
timeLeft--;
timeTag.innerText = timeLeft;
} else {
clearInterval(timer);
}
}
function resetGame() {
randomParagraph();
inpField.value = "";
timeLeft = maxTime;
charIndex = mistakes = isTyping = 0;
mistakeTag.innerText = mistakes;
cpmTag.innerText = 0;
wpmTag.innerText = 0;
}
randomParagraph();
inpField.addEventListener("input", initTyping);
resetBtn.addEventListener("click", resetGame);
Also see: Tab Triggers