Add fonts, spinner and small screen detection

This commit is contained in:
2025-02-20 22:41:33 +01:00
parent 80dd1b48aa
commit f958486fcc
14 changed files with 118 additions and 10 deletions
Binary file not shown.
Binary file not shown.
+7
View File
@@ -7,6 +7,7 @@ import { KEYS_TO_DISABLE } from 'constants/default';
import { renderCharacter } from './utils'; import { renderCharacter } from './utils';
import './index.css'; import './index.css';
import Spinner from 'components/Spinner';
type CodeProps = { type CodeProps = {
code: string; code: string;
@@ -72,6 +73,12 @@ function Code({ code, loaded }: CodeProps) {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [timer, characters]); }, [timer, characters]);
if (!code) return (
<div className='code-container'>
<Spinner />
</div>
);
return ( return (
<div className='code-container'> <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))}
+24
View File
@@ -0,0 +1,24 @@
.spinner-container {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.spinner {
width: 50px;
height: 50px;
border: 5px solid var(--white);
border-top: 5px solid var(--crimson);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
+11
View File
@@ -0,0 +1,11 @@
import './index.css';
function Spinner() {
return (
<div className='spinner-container'>
<div className='spinner'></div>
</div>
);
}
export default Spinner;
-1
View File
@@ -34,7 +34,6 @@ export const KEYS_TO_DISABLE = [
'ArrowLeft', 'ArrowLeft',
'ArrowRight', 'ArrowRight',
]; ];
export const SUPPORTED_LANGUAGES = [ export const SUPPORTED_LANGUAGES = [
'javascript', 'javascript',
'typescript', 'typescript',
+15 -1
View File
@@ -1,10 +1,24 @@
@font-face {
font-family: '0xProto';
font-style: normal;
font-weight: normal;
src: url(assets/fonts/0xProtoNerdFont-Regular.ttf) format(truetype);
}
@font-face {
font-family: '0xProto';
font-style: normal;
font-weight: bold;
src: url(assets/fonts/0xProtoNerdFont-Bold.ttf) format(truetype);
}
:root { :root {
--black: #131314; --black: #131314;
--white: #dddddd; --white: #dddddd;
--crimson: #bd1839; --crimson: #bd1839;
--background: #1d1b1b; --background: #1d1b1b;
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; font-family: '0xProto', sans-serif;
line-height: 1.5; line-height: 1.5;
font-weight: 400; font-weight: 400;
+4 -1
View File
@@ -34,6 +34,10 @@ header p {
color: var(--white); color: var(--white);
} }
header span {
color: var(--crimson);
}
header .cta { header .cta {
all: unset; all: unset;
cursor: pointer; cursor: pointer;
@@ -52,7 +56,6 @@ header .cta {
} }
.features { .features {
margin-top: 2rem;
padding: 2.5rem 1.25rem; padding: 2.5rem 1.25rem;
display: grid; display: grid;
+8
View File
@@ -1,16 +1,24 @@
import { NavLink } from 'react-router'; import { NavLink } from 'react-router';
import { isMobileBrowser } from 'utils';
import './index.css'; import './index.css';
function Home() { function Home() {
const isMobile = isMobileBrowser();
return ( return (
<div className='home-container'> <div className='home-container'>
<header> <header>
<h1>Touch Programming</h1> <h1>Touch Programming</h1>
<p>Master touch typing with real code snippets from your favorite programming languages, powered by AI.</p> <p>Master touch typing with real code snippets from your favorite programming languages, powered by AI.</p>
<NavLink to='/languages'> <NavLink to='/languages'>
{isMobile ?
<span>This app is made to be used in a desktop device.</span> :
<button className='cta'> <button className='cta'>
Start Typing Start Typing
</button> </button>
}
</NavLink> </NavLink>
</header> </header>
+4
View File
@@ -25,6 +25,10 @@ header h1 {
color: var(--crimson); color: var(--crimson);
} }
header span {
color: var(--crimson);
}
.languages-wrapper { .languages-wrapper {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+14
View File
@@ -1,10 +1,24 @@
import { NavLink } from 'react-router'; import { NavLink } from 'react-router';
import { isMobileBrowser } from 'utils';
import { SUPPORTED_LANGUAGES } from 'constants/default'; import { SUPPORTED_LANGUAGES } from 'constants/default';
import './index.css'; import './index.css';
function Languages() { function Languages() {
const isMobile = isMobileBrowser();
if (isMobile) {
return (
<div className='languages-container'>
<header>
<span>This app is made to be used in a desktop device.</span>
</header>
</div>
);
}
return ( return (
<div className='languages-container'> <div className='languages-container'>
<header> <header>
+4
View File
@@ -24,3 +24,7 @@ header h1 {
text-transform: capitalize; text-transform: capitalize;
} }
header span {
color: var(--crimson);
}
+17 -2
View File
@@ -3,17 +3,22 @@ import { useParams } from 'react-router';
import TypingContextProvider from 'contexts/typing'; import TypingContextProvider from 'contexts/typing';
import { isMobileBrowser } from 'utils';
import Code from 'components/Code'; import Code from 'components/Code';
import Stats from 'components/Stats'; import Stats from 'components/Stats';
import './index.css'; import './index.css';
function Typing() { function Typing() {
const {lang} = useParams(); const isMobile = isMobileBrowser();
const { lang } = useParams();
const [code, setCode] = useState<string>(''); const [code, setCode] = useState<string>('');
const [loaded, setLoaded] = useState<boolean>(false); const [loaded, setLoaded] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
if (isMobile) return;
(async function () { (async function () {
setCode(''); setCode('');
@@ -39,7 +44,17 @@ function Typing() {
setCode((prev) => prev.trim()); setCode((prev) => prev.trim());
setLoaded(true); setLoaded(true);
})(); })();
}, [lang]); }, [isMobile, lang]);
if (isMobile) {
return (
<div className='typing-container'>
<header>
<span>This app is made to be used in a desktop device.</span>
</header>
</div>
);
}
return ( return (
<TypingContextProvider> <TypingContextProvider>
+5
View File
@@ -0,0 +1,5 @@
export function isMobileBrowser(): boolean {
const width = window.innerWidth;
return width <= 768 || (width > 768 && width <= 1024);
}