Make game creation wizard fit viewport without scrolling
Replace scrollable form content with responsive sizing that automatically scales elements to fit available viewport height. CSS improvements: - Disable scrolling: overflow-y:auto → overflow:hidden in form-content - Implement fluid typography with clamp() for titles, labels, buttons - Add responsive spacing using clamp() for margins and padding - Scale progress dots from 10px-16px based on viewport height - Reduce button dimensions (60px min-width, 36px min-height) - Enable element shrinking with flex-shrink:1 and min-height:0 Component cleanup: - Remove auto-focus useEffect from Player1/2/3Step components - Prevents unwanted layout shifts on wizard mount Benefits: - All elements visible without scrolling - Responsive design scales smoothly across viewport sizes - Cleaner UX with no scrollbars in form wizard - Better space utilization on small screens
This commit is contained in:
@@ -21,10 +21,10 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
.screen-title {
|
.screen-title {
|
||||||
font-size: var(--font-size-xxl);
|
font-size: clamp(1.25rem, 3vh, 1.5rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
margin-bottom: var(--space-xl);
|
margin-bottom: clamp(0.5rem, 2vh, 2rem);
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,8 @@
|
|||||||
gap: var(--space-lg);
|
gap: var(--space-lg);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: var(--space-xl);
|
margin-bottom: var(--space-xl);
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
.player-input {
|
.player-input {
|
||||||
background: var(--color-background);
|
background: var(--color-background);
|
||||||
@@ -42,6 +44,9 @@
|
|||||||
border: 2px solid var(--color-border);
|
border: 2px solid var(--color-border);
|
||||||
transition: border-color var(--transition-base);
|
transition: border-color var(--transition-base);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.player-input:focus-within {
|
.player-input:focus-within {
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
@@ -49,9 +54,9 @@
|
|||||||
}
|
}
|
||||||
.player-input label {
|
.player-input label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: var(--space-md);
|
margin-bottom: clamp(0.5rem, 2vh, 1rem);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
font-size: var(--font-size-lg);
|
font-size: clamp(1rem, 2.5vh, 1.125rem);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.name-input-container {
|
.name-input-container {
|
||||||
@@ -135,15 +140,16 @@
|
|||||||
|
|
||||||
.form-header {
|
.form-header {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: var(--space-xl) var(--space-lg) var(--space-md) var(--space-lg);
|
padding: clamp(0.5rem, 2vh, 2rem) var(--space-lg) clamp(0.25rem, 1vh, 1rem) var(--space-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-content {
|
.form-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow: hidden;
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 0 var(--space-lg);
|
padding: 0 var(--space-lg);
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-footer {
|
.form-footer {
|
||||||
@@ -154,12 +160,12 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-md);
|
gap: clamp(0.5rem, 1.5vw, 1rem);
|
||||||
margin-bottom: var(--space-lg);
|
margin-bottom: clamp(0.5rem, 2vh, 1.5rem);
|
||||||
}
|
}
|
||||||
.progress-dot {
|
.progress-dot {
|
||||||
width: 16px;
|
width: clamp(10px, 2vh, 16px);
|
||||||
height: 16px;
|
height: clamp(10px, 2vh, 16px);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--color-border);
|
background: var(--color-border);
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
@@ -172,18 +178,27 @@
|
|||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
box-shadow: 0 0 0 4px var(--color-primary-light);
|
box-shadow: 0 0 0 4px var(--color-primary-light);
|
||||||
}
|
}
|
||||||
|
.quick-pick-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.quick-pick-btn {
|
.quick-pick-btn {
|
||||||
min-width: 80px;
|
min-width: 60px;
|
||||||
min-height: var(--touch-target-comfortable);
|
min-height: 36px;
|
||||||
font-size: var(--font-size-base);
|
font-size: clamp(0.75rem, 2vw, 1rem);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
background: var(--color-secondary);
|
background: var(--color-secondary);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: var(--space-sm) var(--space-md);
|
padding: 0.4rem 0.8rem;
|
||||||
transition: all var(--transition-base);
|
transition: all var(--transition-base);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.quick-pick-btn:hover, .quick-pick-btn:focus {
|
.quick-pick-btn:hover, .quick-pick-btn:focus {
|
||||||
background: var(--color-secondary-hover);
|
background: var(--color-secondary-hover);
|
||||||
|
|||||||
@@ -18,13 +18,6 @@ export const Player1Step = ({ playerNameHistory, onNext, onCancel, initialValue
|
|||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const el = inputRef.current;
|
|
||||||
if (el) {
|
|
||||||
el.focus();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!player1) {
|
if (!player1) {
|
||||||
setFilteredNames(playerNameHistory);
|
setFilteredNames(playerNameHistory);
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ export const Player2Step = ({ playerNameHistory, onNext, onCancel, initialValue
|
|||||||
const [filteredNames, setFilteredNames] = useState(playerNameHistory);
|
const [filteredNames, setFilteredNames] = useState(playerNameHistory);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const el = inputRef.current;
|
|
||||||
if (el) {
|
|
||||||
el.focus();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!player2) {
|
if (!player2) {
|
||||||
setFilteredNames(playerNameHistory);
|
setFilteredNames(playerNameHistory);
|
||||||
|
|||||||
@@ -14,13 +14,6 @@ export const Player3Step = ({ playerNameHistory, onNext, onCancel, initialValue
|
|||||||
const [filteredNames, setFilteredNames] = useState(playerNameHistory);
|
const [filteredNames, setFilteredNames] = useState(playerNameHistory);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const el = inputRef.current;
|
|
||||||
if (el) {
|
|
||||||
el.focus();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!player3) {
|
if (!player3) {
|
||||||
setFilteredNames(playerNameHistory);
|
setFilteredNames(playerNameHistory);
|
||||||
|
|||||||
Reference in New Issue
Block a user