diff --git a/client/src/assets/fonts/0xProtoNerdFont-Bold.ttf b/client/src/assets/fonts/0xProtoNerdFont-Bold.ttf new file mode 100644 index 0000000..725d2d2 Binary files /dev/null and b/client/src/assets/fonts/0xProtoNerdFont-Bold.ttf differ diff --git a/client/src/assets/fonts/0xProtoNerdFont-Regular.ttf b/client/src/assets/fonts/0xProtoNerdFont-Regular.ttf new file mode 100644 index 0000000..489a6da Binary files /dev/null and b/client/src/assets/fonts/0xProtoNerdFont-Regular.ttf differ diff --git a/client/src/components/Code/index.tsx b/client/src/components/Code/index.tsx index f850089..54a7e81 100644 --- a/client/src/components/Code/index.tsx +++ b/client/src/components/Code/index.tsx @@ -7,6 +7,7 @@ import { KEYS_TO_DISABLE } from 'constants/default'; import { renderCharacter } from './utils'; import './index.css'; +import Spinner from 'components/Spinner'; type CodeProps = { code: string; @@ -72,6 +73,12 @@ function Code({ code, loaded }: CodeProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [timer, characters]); + if (!code) return ( +
+ +
+ ); + return (
{code.split('').map((char, index) => renderCharacter(code, characters, loaded, char, index))} diff --git a/client/src/components/Spinner/index.css b/client/src/components/Spinner/index.css new file mode 100644 index 0000000..599f64c --- /dev/null +++ b/client/src/components/Spinner/index.css @@ -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); + } +} diff --git a/client/src/components/Spinner/index.tsx b/client/src/components/Spinner/index.tsx new file mode 100644 index 0000000..739ff4d --- /dev/null +++ b/client/src/components/Spinner/index.tsx @@ -0,0 +1,11 @@ +import './index.css'; + +function Spinner() { + return ( +
+
+
+ ); +} + +export default Spinner; diff --git a/client/src/constants/default.ts b/client/src/constants/default.ts index 62a4456..91c78bd 100644 --- a/client/src/constants/default.ts +++ b/client/src/constants/default.ts @@ -34,7 +34,6 @@ export const KEYS_TO_DISABLE = [ 'ArrowLeft', 'ArrowRight', ]; - export const SUPPORTED_LANGUAGES = [ 'javascript', 'typescript', diff --git a/client/src/index.css b/client/src/index.css index e804abd..ee2a888 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -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 { --black: #131314; --white: #dddddd; --crimson: #bd1839; --background: #1d1b1b; - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-family: '0xProto', sans-serif; line-height: 1.5; font-weight: 400; @@ -56,13 +70,13 @@ body::-webkit-scrollbar-thumb { } .app { - min-height: 95vh; + min-height: 95vh; } @media (min-width: 1441px) { .app { width: 1368px; - margin: 0 auto; + margin: 0 auto; } } diff --git a/client/src/pages/Home/index.css b/client/src/pages/Home/index.css index 78e555d..12cd826 100644 --- a/client/src/pages/Home/index.css +++ b/client/src/pages/Home/index.css @@ -34,6 +34,10 @@ header p { color: var(--white); } +header span { + color: var(--crimson); +} + header .cta { all: unset; cursor: pointer; @@ -52,7 +56,6 @@ header .cta { } .features { - margin-top: 2rem; padding: 2.5rem 1.25rem; display: grid; diff --git a/client/src/pages/Home/index.tsx b/client/src/pages/Home/index.tsx index b3951fe..2024c3e 100644 --- a/client/src/pages/Home/index.tsx +++ b/client/src/pages/Home/index.tsx @@ -1,16 +1,24 @@ import { NavLink } from 'react-router'; + +import { isMobileBrowser } from 'utils'; + import './index.css'; function Home() { + const isMobile = isMobileBrowser(); + return (

Touch Programming

Master touch typing with real code snippets from your favorite programming languages, powered by AI.

- + {isMobile ? + This app is made to be used in a desktop device. : + + }
diff --git a/client/src/pages/Languages/index.css b/client/src/pages/Languages/index.css index 3c36fb6..ef7f26f 100644 --- a/client/src/pages/Languages/index.css +++ b/client/src/pages/Languages/index.css @@ -25,6 +25,10 @@ header h1 { color: var(--crimson); } +header span { + color: var(--crimson); +} + .languages-wrapper { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); diff --git a/client/src/pages/Languages/index.tsx b/client/src/pages/Languages/index.tsx index fdc2f96..abe8db9 100644 --- a/client/src/pages/Languages/index.tsx +++ b/client/src/pages/Languages/index.tsx @@ -1,10 +1,24 @@ import { NavLink } from 'react-router'; +import { isMobileBrowser } from 'utils'; + import { SUPPORTED_LANGUAGES } from 'constants/default'; import './index.css'; function Languages() { + const isMobile = isMobileBrowser(); + + if (isMobile) { + return ( +
+
+ This app is made to be used in a desktop device. +
+
+ ); + } + return (
diff --git a/client/src/pages/Typing/index.css b/client/src/pages/Typing/index.css index ba867f7..5eef99f 100644 --- a/client/src/pages/Typing/index.css +++ b/client/src/pages/Typing/index.css @@ -24,3 +24,7 @@ header h1 { text-transform: capitalize; } + +header span { + color: var(--crimson); +} diff --git a/client/src/pages/Typing/index.tsx b/client/src/pages/Typing/index.tsx index eaafcf5..4a13427 100644 --- a/client/src/pages/Typing/index.tsx +++ b/client/src/pages/Typing/index.tsx @@ -3,17 +3,22 @@ import { useParams } from 'react-router'; import TypingContextProvider from 'contexts/typing'; +import { isMobileBrowser } from 'utils'; + import Code from 'components/Code'; import Stats from 'components/Stats'; import './index.css'; function Typing() { - const {lang} = useParams(); + const isMobile = isMobileBrowser(); + const { lang } = useParams(); const [code, setCode] = useState(''); const [loaded, setLoaded] = useState(false); useEffect(() => { + if (isMobile) return; + (async function () { setCode(''); @@ -39,7 +44,17 @@ function Typing() { setCode((prev) => prev.trim()); setLoaded(true); })(); - }, [lang]); + }, [isMobile, lang]); + + if (isMobile) { + return ( +
+
+ This app is made to be used in a desktop device. +
+
+ ); + } return ( diff --git a/client/src/utils.ts b/client/src/utils.ts new file mode 100644 index 0000000..5b7eb21 --- /dev/null +++ b/client/src/utils.ts @@ -0,0 +1,5 @@ +export function isMobileBrowser(): boolean { + const width = window.innerWidth; + + return width <= 768 || (width > 768 && width <= 1024); +}