Improve mobile UI

This commit is contained in:
2025-12-30 10:24:25 +01:00
parent 6f67769974
commit 96b236acee
4 changed files with 214 additions and 47 deletions

View File

@@ -10,7 +10,7 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-sm) var(--spacing-xl);
padding: var(--spacing-sm) var(--spacing-md);
background-color: var(--color-surface);
box-shadow: var(--shadow-sm);
}
@@ -23,6 +23,12 @@
color: var(--color-primary);
}
@media (max-width: 400px) {
.logo {
display: none;
}
}
.streak-badge {
background-color: var(--color-primary-dark);
color: white;
@@ -588,6 +594,22 @@ button {
transition: all 0.2s;
}
@media (max-width: 450px) {
.toggle-option {
padding: 4px 8px;
font-size: 0.75rem;
}
/* Hide help button in favor of settings on small screens */
.help-btn {
display: none;
}
.header-controls {
gap: 4px;
}
}
.toggle-option.active {
background-color: var(--color-surface);
color: var(--color-primary);
@@ -636,27 +658,20 @@ button {
justify-content: center;
/* Center vertically */
gap: 0;
height: 100vh;
/* Fixed viewport height */
overflow: hidden;
/* No scrolling for main game loop */
position: relative;
/* Allow scrolling */
height: auto;
min-height: 100vh;
overflow-y: auto;
display: block;
}
.card.sheet-music-card {
max-width: 95vw;
height: 100%;
max-height: none;
flex-direction: column;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
padding: 0;
width: 100%;
background: transparent;
box-shadow: none;
border: none;
flex-shrink: 0;
max-width: 100vw;
width: 100vw;
min-height: 100vh;
margin: 0;
border-radius: 0;
align-items: stretch;
}
/* Staff Row: Horizontal Layout for Staff + Note Name */
@@ -691,9 +706,8 @@ button {
/* Scale UP */
transform-origin: center center;
justify-content: center;
margin-bottom: -10px;
/* Pull fretboard closer */
padding-top: 0;
margin-bottom: 0;
padding-top: 10px;
z-index: 1;
}
@@ -704,19 +718,16 @@ button {
/* Hint card container (Fretboard + Note Name) */
.hint-card {
margin-top: 0;
flex: 1 1 auto;
flex: 1;
width: 100%;
max-width: 100%;
padding: 0 10px;
background: transparent;
padding: 0;
display: flex;
flex-direction: row;
justify-content: center;
flex-direction: column;
justify-content: flex-start;
align-items: center;
gap: 8px;
min-height: 0;
max-height: 200px;
/* Cap fretboard height */
height: auto;
}
/* The note name inside hint card */
@@ -734,9 +745,15 @@ button {
.fretboard-container {
transform: none;
max-width: none;
width: 100%;
flex-grow: 1;
height: 100%;
/* Ensure it fills the space but respects container limits */
display: flex;
align-items: center;
justify-content: center;
background: #2a2a2a;
/* distinct bg for full width feel */
min-height: 200px;
padding: 10px 0;
}
/* Hide Instruction Text ("Play note above") */
@@ -780,27 +797,78 @@ button {
/* Break flex height lock */
}
/* Action Buttons - floating or tucked */
.action-row {
margin-top: 0 !important;
margin-top: 20px !important;
width: 100%;
padding-bottom: 5px;
flex-shrink: 0;
position: absolute;
bottom: 2px;
z-index: 20;
pointer-events: none;
/* Pass through clicks */
padding: 20px;
position: static;
/* Flow naturally below */
display: flex;
flex-direction: row;
justify-content: center;
gap: 20px;
gap: 16px;
background: var(--color-bg);
pointer-events: auto;
}
.action-row button {
/* Reset styles for scrolling row */
pointer-events: auto;
/* Enable buttons */
background: rgba(255, 255, 255, 0.9);
border: 1px solid #ccc;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
width: auto;
height: auto;
background: transparent;
border: none;
box-shadow: none;
border-radius: 0;
padding: 10px;
display: flex;
flex-direction: column;
/* Stack icon/text */
align-items: center;
color: var(--color-text-main);
}
/* Specific Floating Skip Button logic */
.skip-button {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 999;
background-color: var(--color-primary) !important;
color: white !important;
width: 56px !important;
height: 56px !important;
border-radius: 50% !important;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
border: none !important;
}
/* Hide text in floating button, show icon */
.skip-button svg {
width: 24px !important;
height: 24px !important;
margin: 0;
}
.skip-button:active {
transform: scale(0.95);
}
/* Show text labels in scrollable row for others */
.action-row button span,
.action-row button text {
display: block;
font-size: 0.7rem;
margin-top: 4px;
}
/* Adjust icon sizes */
.action-row button svg {
width: 20px;
height: 20px;
}
/* Restore Zen Toggle in Landscape Mobile */

View File

@@ -1,5 +1,6 @@
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { Logo } from './components/Logo';
import { LandscapeSuggestion } from './components/LandscapeSuggestion';
import { SheetMusic } from './components/SheetMusic';
@@ -399,6 +400,7 @@ function App() {
return (
<div className={`app-container ${settings.zenMode ? 'zen-mode' : ''}`}>
<LandscapeSuggestion />
{!settings.zenMode && (
<header className="app-header">
<div className="app-header-left">
@@ -422,7 +424,7 @@ function App() {
</div>
<button
className="icon-button"
className="icon-button help-btn"
onClick={() => setShowHelp(true)}
title="Shortcuts Help"
>

View File

@@ -0,0 +1,29 @@
import React, { useState, useEffect } from 'react';
import { Smartphone } from 'lucide-react';
import '../styles/landscape-suggestion.css';
export const LandscapeSuggestion: React.FC = () => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const checkOrientation = () => {
const isMobile = window.innerWidth <= 768;
const isPortrait = window.innerHeight > window.innerWidth;
setIsVisible(isMobile && isPortrait);
};
checkOrientation();
window.addEventListener('resize', checkOrientation);
return () => window.removeEventListener('resize', checkOrientation);
}, []);
if (!isVisible) return null;
return (
<div className="landscape-suggestion">
<Smartphone size={24} className="rotate-icon" />
<span>For the best experience, please rotate your device to landscape.</span>
<button className="dismiss-btn" onClick={() => setIsVisible(false)}>×</button>
</div>
);
};

View File

@@ -0,0 +1,68 @@
.landscape-suggestion {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.85);
color: white;
padding: 12px 20px;
border-radius: 30px;
display: flex;
align-items: center;
gap: 12px;
z-index: 9999;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
width: 90%;
max-width: 400px;
animation: slideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1);
font-size: 0.9rem;
line-height: 1.4;
}
.rotate-icon {
animation: rotate 2s infinite ease-in-out;
flex-shrink: 0;
}
.dismiss-btn {
background: none;
border: none;
color: rgba(255, 255, 255, 0.6);
font-size: 1.5rem;
cursor: pointer;
margin-left: auto;
padding: 0 4px;
line-height: 1;
}
.dismiss-btn:hover {
color: white;
}
@keyframes slideUp {
from {
transform: translate(-50%, 100%);
opacity: 0;
}
to {
transform: translate(-50%, 0);
opacity: 1;
}
}
@keyframes rotate {
0%,
100% {
transform: rotate(0deg);
}
25% {
transform: rotate(90deg);
}
75% {
transform: rotate(90deg);
}
}