Add mobile responsiveness

This commit is contained in:
Roni Laukkarinen
2026-02-01 18:14:41 +02:00
parent 674c2a379b
commit 2745d5c417
3 changed files with 115 additions and 17 deletions
+1
View File
@@ -1,5 +1,6 @@
### 2026-02-01: 1.0.3
* Add mobile responsiveness
* Fix modal accessibility
* Fix progress bar accessibility
* Add Open Library API integration for missing book metadata (opt-in)
+18 -17
View File
@@ -512,7 +512,7 @@ function App() {
return (
<div style={styles.container}>
{/* Top controls */}
<div style={styles.topBar}>
<div style={styles.topBar} className="top-bar">
<div style={styles.topLeft}>
<button
onClick={() => setShowTextInput(!showTextInput)}
@@ -524,7 +524,7 @@ function App() {
title="Edit text"
>
<FileText size={16} />
<span>Text</span>
<span className="text-btn-label">Text</span>
</button>
<button
onClick={() => fileInputRef.current?.click()}
@@ -534,7 +534,7 @@ function App() {
disabled={isLoadingFile}
>
<Upload size={16} />
<span>{isLoadingFile ? "Loading..." : "Upload"}</span>
<span className="text-btn-label">{isLoadingFile ? "Loading..." : "Upload"}</span>
</button>
<input
ref={fileInputRef}
@@ -613,7 +613,7 @@ function App() {
)}
{/* Main display area */}
<div style={styles.mainArea}>
<div style={styles.mainArea} className="main-area">
<div style={styles.displayArea}>
<div style={styles.focalGuide}>
<div style={styles.focalLine} />
@@ -621,14 +621,14 @@ function App() {
<div style={styles.focalLine} />
</div>
<div style={styles.wordContainer}>
<div style={styles.wordContainer} className="word-container">
{currentWord ? (
<div
style={{
...styles.wordDisplay,
transform: `translateY(-50%) translateX(calc(-${orpIndex}ch - 0.5ch))`,
}}
className="mono"
className="mono word-display"
>
<span style={{ ...styles.beforeORP, opacity: sideOpacity }}>
{beforeORP}
@@ -644,7 +644,7 @@ function App() {
...styles.wordDisplay,
transform: "translateY(-50%) translateX(-50%)",
}}
className="mono"
className="mono word-display"
>
<span style={styles.placeholder}>Ready</span>
</div>
@@ -660,7 +660,7 @@ function App() {
</div>
{/* Bottom controls */}
<div style={styles.bottomArea}>
<div style={styles.bottomArea} className="bottom-area">
{/* Controls with play button in center */}
<div style={styles.controlsRow}>
<button
@@ -671,7 +671,7 @@ function App() {
<ChevronLeft size={24} />
<ChevronLeft size={24} style={{ marginLeft: -14 }} />
</button>
<button onClick={togglePlay} style={styles.playBtn}>
<button onClick={togglePlay} style={styles.playBtn} className="play-btn">
{isPlaying ? <PauseSolid size={32} /> : <PlaySolid size={32} />}
</button>
<button
@@ -711,7 +711,7 @@ function App() {
{currentIndex + 1} / {words.length} ({Math.round(progress)}%)
</div>
<div style={styles.hint}>
<div style={styles.hint} className="hint">
<kbd style={styles.kbd}>Space</kbd> play
<kbd style={styles.kbd}></kbd>
<kbd style={styles.kbd}></kbd> word
@@ -723,22 +723,23 @@ function App() {
{/* Book metadata display */}
{bookMetadata && (bookMetadata.title || bookMetadata.cover) && (
<aside style={styles.bookMetadata} aria-label="Current book">
<aside style={styles.bookMetadata} aria-label="Current book" className="book-metadata">
{bookMetadata.cover && (
<img
src={bookMetadata.cover}
alt={`Cover of ${bookMetadata.title || "current book"}`}
style={styles.bookCover}
className="book-cover"
/>
)}
<div style={styles.bookInfo}>
{bookMetadata.title && (
<h3 style={styles.bookTitle}>{bookMetadata.title}</h3>
<h3 style={styles.bookTitle} className="book-title">{bookMetadata.title}</h3>
)}
{bookMetadata.author && (
<p style={styles.bookAuthor}>{bookMetadata.author}</p>
<p style={styles.bookAuthor} className="book-author">{bookMetadata.author}</p>
)}
<p style={styles.bookStats}>
<p style={styles.bookStats} className="book-stats">
{formatReadingTime((words.length - currentIndex) / wpm)} left
</p>
</div>
@@ -753,7 +754,7 @@ function App() {
role="presentation"
>
<div
style={styles.modal}
style={styles.modal} className="modal"
onClick={(e) => e.stopPropagation()}
role="dialog"
aria-modal="true"
@@ -816,7 +817,7 @@ function App() {
{showInfo && (
<div style={styles.modalOverlay} onClick={() => setShowInfo(false)} role="presentation">
<div
style={styles.modal}
style={styles.modal} className="modal"
onClick={(e) => e.stopPropagation()}
role="dialog"
aria-modal="true"
@@ -923,7 +924,7 @@ function App() {
{showSettings && (
<div style={styles.modalOverlay} onClick={() => setShowSettings(false)} role="presentation">
<div
style={styles.modal}
style={styles.modal} className="modal"
onClick={(e) => e.stopPropagation()}
role="dialog"
aria-modal="true"
+96
View File
@@ -47,3 +47,99 @@ button:active {
.wpm-btn:hover {
color: #ccc !important;
}
/* Mobile responsiveness */
@media (max-width: 600px) {
/* Hide text labels on buttons */
.icon-btn span,
.text-btn-label {
display: none !important;
}
/* Reduce top bar padding */
.top-bar {
padding: 12px 16px !important;
}
/* Smaller WPM display */
.wpm-value {
font-size: 1.2rem !important;
}
/* Smaller word display */
.word-display {
font-size: 2.5rem !important;
}
/* Reduce main area padding */
.main-area {
padding: 0 16px !important;
}
/* Smaller word container */
.word-container {
height: 100px !important;
}
/* Reduce bottom area padding */
.bottom-area {
padding: 12px 16px 24px !important;
}
/* Smaller play button */
.play-btn {
width: 56px !important;
height: 56px !important;
}
/* Hide keyboard hints on mobile */
.hint {
display: none !important;
}
/* Smaller book metadata */
.book-metadata {
max-width: 200px !important;
bottom: 12px !important;
left: 12px !important;
}
.book-cover {
width: 36px !important;
}
.book-title {
font-size: 0.7rem !important;
}
.book-author,
.book-stats {
font-size: 0.65rem !important;
}
/* Modal adjustments */
.modal {
max-width: 95% !important;
max-height: 90vh !important;
}
.modal-content {
padding: 16px !important;
}
}
@media (max-width: 400px) {
/* Even smaller word display for very small screens */
.word-display {
font-size: 2rem !important;
}
.word-container {
height: 80px !important;
}
/* Hide book metadata on very small screens */
.book-metadata {
display: none !important;
}
}