mirror of
https://github.com/hazemKrimi/touch-programming.git
synced 2026-05-01 18:20:26 +00:00
Styling pages WIP
This commit is contained in:
@@ -5,14 +5,21 @@ import Languages from 'pages/Languages';
|
|||||||
import Typing from 'pages/Typing';
|
import Typing from 'pages/Typing';
|
||||||
import NotFound from 'pages/NotFound';
|
import NotFound from 'pages/NotFound';
|
||||||
|
|
||||||
|
import Footer from 'components/Footer';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<div className='app'>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route index element={<Home />} />
|
<Route index element={<Home />} />
|
||||||
<Route path='languages' element={<Languages />} />
|
<Route path='languages' element={<Languages />} />
|
||||||
<Route path='typing/:lang' element={<Typing />} />
|
<Route path='typing/:lang' element={<Typing />} />
|
||||||
<Route path='*' element={<NotFound />} />
|
<Route path='*' element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
.code {
|
.code-container {
|
||||||
padding: 1rem;
|
min-width: 500px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
|
||||||
|
background: var(--black);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pending, .space {
|
.pending, .space {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ function Code({ code, loaded }: CodeProps) {
|
|||||||
}, [timer, characters]);
|
}, [timer, characters]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='code'>
|
<div className='code-container'>
|
||||||
{code.split('').map((char, index) => renderCharacter(code, characters, loaded, char, index))}
|
{code.split('').map((char, index) => renderCharacter(code, characters, loaded, char, index))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
min-height: 5vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a {
|
||||||
|
color: var(--crimson);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import './index.css';
|
||||||
|
|
||||||
|
function Footer() {
|
||||||
|
return (
|
||||||
|
<footer>
|
||||||
|
<p><a href='https://github.com/hazemKrimi/touch-programming' target='_blank' rel="noreferrer">Source code</a> Made by <a href='https://hazemkrimi.tech' target='_blank' rel="noreferrer">Hazem Krimi</a></p>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Footer;
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
.score {
|
.stats-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 1rem;
|
|
||||||
|
gap: 2rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
|
||||||
|
background: var(--black);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function Stats({ loaded }: StatsProps) {
|
|||||||
if (!loaded) return;
|
if (!loaded) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='score'>
|
<div className='stats-container'>
|
||||||
{renderTimer(timer)}
|
{renderTimer(timer)}
|
||||||
<p>WPM: {Math.round(score)}</p>
|
<p>WPM: {Math.round(score)}</p>
|
||||||
<p>Accuracy: {Math.round(accuracy)}%</p>
|
<p>Accuracy: {Math.round(accuracy)}%</p>
|
||||||
|
|||||||
@@ -34,3 +34,31 @@ export const KEYS_TO_DISABLE = [
|
|||||||
'ArrowLeft',
|
'ArrowLeft',
|
||||||
'ArrowRight',
|
'ArrowRight',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const SUPPORTED_LANGUAGES = [
|
||||||
|
'javascript',
|
||||||
|
'typescript',
|
||||||
|
'python',
|
||||||
|
'rust',
|
||||||
|
'golang',
|
||||||
|
'java',
|
||||||
|
'ruby',
|
||||||
|
'php',
|
||||||
|
'swift',
|
||||||
|
'kotlin',
|
||||||
|
'dart',
|
||||||
|
'scala',
|
||||||
|
'elixir',
|
||||||
|
'haskell',
|
||||||
|
'clojure',
|
||||||
|
'lua',
|
||||||
|
'r',
|
||||||
|
'perl',
|
||||||
|
'bash',
|
||||||
|
'powershell',
|
||||||
|
'pascal',
|
||||||
|
'ocaml',
|
||||||
|
'c',
|
||||||
|
'c++',
|
||||||
|
'fortran'
|
||||||
|
];
|
||||||
|
|||||||
+52
-5
@@ -1,4 +1,9 @@
|
|||||||
:root {
|
:root {
|
||||||
|
--black: #131314;
|
||||||
|
--white: #dddddd;
|
||||||
|
--crimson: #bd1839;
|
||||||
|
--background: #1d1b1b;
|
||||||
|
|
||||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@@ -12,10 +17,52 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
padding: 0;
|
||||||
place-items: center;
|
}
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: var(--crimson);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--background);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
body::-webkit-scrollbar {
|
||||||
|
width: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
body::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--crimson);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
min-height: 95vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1441px) {
|
||||||
|
.app {
|
||||||
|
width: 1368px;
|
||||||
|
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,99 @@
|
|||||||
.container {
|
.home-container {
|
||||||
text-align: left;
|
padding: 0rem 3rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.home-container {
|
||||||
|
padding: 0rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2.5rem 1.25rem;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
|
||||||
|
color: var(--crimson);
|
||||||
|
}
|
||||||
|
|
||||||
|
header p {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
header .cta {
|
||||||
|
all: unset;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
background: var(--crimson);
|
||||||
|
color: var(--white);
|
||||||
|
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding: 2.5rem 1.25rem;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature, .benefit {
|
||||||
|
padding: 1.5rem;
|
||||||
|
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
|
||||||
|
background: var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature h3, .benefit h3 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.benefits {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding: 2.5rem 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.benefits h2 {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
|
||||||
|
color: var(--crimson);
|
||||||
|
}
|
||||||
|
|
||||||
|
.benefits-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 3rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,56 @@
|
|||||||
|
import { NavLink } from 'react-router';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
return (
|
return (
|
||||||
<div className='container'>
|
<div className='home-container'>
|
||||||
<h1>Home</h1>
|
<header>
|
||||||
|
<h1>Touch Programming</h1>
|
||||||
|
<p>Master touch typing with real code snippets from your favorite programming languages, powered by AI.</p>
|
||||||
|
<NavLink to='/languages'>
|
||||||
|
<button className='cta'>
|
||||||
|
Start Typing
|
||||||
|
</button>
|
||||||
|
</NavLink>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className='features'>
|
||||||
|
<div className='feature'>
|
||||||
|
<h3>Real Code Snippets</h3>
|
||||||
|
<p>
|
||||||
|
Practice with actual code examples from popular repositories, making your learning relevant to real-world
|
||||||
|
programming.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className='feature'>
|
||||||
|
<h3>Multiple Languages</h3>
|
||||||
|
<p>
|
||||||
|
Choose from Python, JavaScript, TypeScript, Rust, and more. Practice with the languages you use daily.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className='feature'>
|
||||||
|
<h3>AI Powered</h3>
|
||||||
|
<p>
|
||||||
|
Code is generated with the help of the Open Source LLM Models.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className='benefits'>
|
||||||
|
<h2>Why Practice Touch Programming?</h2>
|
||||||
|
<div className='benefits-wrapper'>
|
||||||
|
<div className='benefit'>
|
||||||
|
<h3>Boost Productivity</h3>
|
||||||
|
<p>Increase your coding speed and reduce errors with proper typing technique</p>
|
||||||
|
</div>
|
||||||
|
<div className='benefit'>
|
||||||
|
<h3>Learn Syntax</h3>
|
||||||
|
<p>
|
||||||
|
Familiarize yourself with language syntax while improving your typing speed
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,43 @@
|
|||||||
.container {
|
.languages-container {
|
||||||
text-align: left;
|
padding: 0rem 3rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.languages-container {
|
||||||
|
padding: 0rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2.5rem 1.25rem;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
|
||||||
|
color: var(--crimson);
|
||||||
|
}
|
||||||
|
|
||||||
|
.languages-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language {
|
||||||
|
padding: 1.5rem;
|
||||||
|
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
|
||||||
|
background: var(--black);
|
||||||
|
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
|
import { NavLink } from 'react-router';
|
||||||
|
|
||||||
|
import { SUPPORTED_LANGUAGES } from 'constants/default';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
function Languages() {
|
function Languages() {
|
||||||
return (
|
return (
|
||||||
<div className='container'>
|
<div className='languages-container'>
|
||||||
<h1>Languages</h1>
|
<header>
|
||||||
|
<h1>Choose Your Preferred Language</h1>
|
||||||
|
</header>
|
||||||
|
<div className='languages-wrapper'>
|
||||||
|
{SUPPORTED_LANGUAGES.map((lang) => (
|
||||||
|
<NavLink to={`/typing/${lang}`} key={lang}>
|
||||||
|
<div className='language'>
|
||||||
|
<h2>{lang}</h2>
|
||||||
|
</div>
|
||||||
|
</NavLink>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
.container {
|
.typing-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
column-gap: 1rem;
|
align-items: center;
|
||||||
|
gap: 2rem;
|
||||||
|
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2.5rem 1.25rem;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
|
||||||
|
color: var(--crimson);
|
||||||
|
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
import TypingContextProvider from 'contexts/typing';
|
import TypingContextProvider from 'contexts/typing';
|
||||||
|
|
||||||
@@ -6,7 +7,6 @@ import Code from 'components/Code';
|
|||||||
import Stats from 'components/Stats';
|
import Stats from 'components/Stats';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import { useParams } from 'react-router';
|
|
||||||
|
|
||||||
function Typing() {
|
function Typing() {
|
||||||
const {lang} = useParams();
|
const {lang} = useParams();
|
||||||
@@ -39,11 +39,16 @@ function Typing() {
|
|||||||
setCode((prev) => prev.trim());
|
setCode((prev) => prev.trim());
|
||||||
setLoaded(true);
|
setLoaded(true);
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, [lang]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypingContextProvider>
|
<TypingContextProvider>
|
||||||
<div className='container'>
|
<div className='typing-container'>
|
||||||
|
<header>
|
||||||
|
<h1>
|
||||||
|
Practice Typing in {lang}
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
<Code code={code} loaded={loaded} />
|
<Code code={code} loaded={loaded} />
|
||||||
<Stats loaded={loaded} />
|
<Stats loaded={loaded} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user