After launching Void Blocks and Snake Game on my portfolio, I discovered several UX issues that needed immediate attention. Mobile users were experiencing overlapping controls, non-functional buttons, and bland dropdown menus that didn't match the terminal aesthetic. Here's how I fixed everything while maintaining the cyberpunk vibe.

The Problems

Void Blocks Mobile View

  1. Overlapping Controls: Fixed mobile touch controls were overlapping with the instruction panels, making the interface cluttered and hard to use
  2. Broken Rotate Button: The rotate button (๐Ÿ”„) wasn't working at all - clicking it did nothing
  3. Insufficient Spacing: The game panels were too close to the fixed controls at the bottom

Snake Game Dropdowns

  1. Generic Appearance: The speed and mode dropdowns looked like standard HTML selects - boring and not fitting the terminal theme
  2. Poor Mobile Experience: Dropdowns were cramped on mobile with inadequate touch targets
  3. No Visual Feedback: Hover and focus states were minimal

Void Blocks: Fixing Mobile Controls

The Overlap Issue

The root cause was insufficient bottom padding. The mobile controls were fixed at bottom: 20px, but the body only had padding-bottom: 180px. This created a scenario where scrollable content would collide with fixed controls.

The Fix:

@media (max-width: 768px) {   body {     /* Increased from 180px to 220px */     padding-bottom: 220px;   }    .panels {     width: 100%;     max-width: 400px;     display: grid;     grid-template-columns: 1fr 1fr;     gap: 10px;     margin-bottom: 20px;  /* Added breathing room */   } }

The combination of increased body padding and panel margin creates a safe zone where fixed controls never overlap with scrollable content.

The Rotate Button Bug

This was a classic case of calling a non-existent function. The mobile rotate button handler was calling rotatePiece(current), but this function didn't exist in the codebase.

Before (Broken):

document.getElementById('mobileRotate').addEventListener('click', () => {   if (current && gameStarted && !gameOver && !paused) {     const rotatedPiece = rotatePiece(current);  // โŒ Function doesn't exist     if (isValidMove(rotatedPiece, 0, 0)) {       current.shape = rotatedPiece.shape;     }   } });

After (Fixed):

document.getElementById('mobileRotate').addEventListener('click', () => {   if (current && gameStarted && !gameOver && !paused) {     const rotated = rotate(current.shape, true);  // โœ… Use existing rotate()     if (isValidMove(current, 0, 0, rotated)) {       current.shape = rotated;     }   } });

The fix uses the existing rotate() function that's already used by keyboard controls, ensuring consistent behavior across input methods.

Snake Game: Lookmaxxing the Dropdowns

The term "lookmaxxing" perfectly describes taking something basic and elevating it to its maximum visual potential. The default HTML select elements needed a complete terminal-aesthetic transformation.

Custom Dropdown Styling

Key Features:

  • Gradient background with terminal green tint
  • Custom SVG arrow icon
  • Multi-layer box shadows for glow effects
  • Smooth state transitions
  • Removed default browser styling
.controls select, .hud select {   background: linear-gradient(135deg,     rgba(0, 0, 0, 0.9) 0%,     rgba(17, 34, 17, 0.8) 100%);   color: var(--terminal-green);   border: 2px solid var(--terminal-green);   padding: 10px 35px 10px 12px;   font-family: inherit;   font-size: 13px;   font-weight: bold;   cursor: pointer;   text-transform: uppercase;   transition: all 0.3s ease;   border-radius: 4px;   box-shadow:     0 0 10px rgba(85, 255, 85, 0.2),     inset 0 1px 0 rgba(85, 255, 85, 0.1);   appearance: none;   -webkit-appearance: none;   -moz-appearance: none; }

Custom Arrow Icon

The secret to terminal-aesthetic dropdowns is a custom SVG arrow that changes color based on state:

background-image:   linear-gradient(135deg, rgba(0, 0, 0, 0.9) 0%, rgba(17, 34, 17, 0.8) 100%),   url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2355ff55' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); background-repeat: no-repeat, no-repeat; background-position: center, right 8px center; background-size: auto, 16px;

The SVG is embedded as a data URI, and the stroke color changes from #55ff55 (terminal green) to #00ff00 (bright green) on hover, and #55ffff (cyan) on focus.

Interactive States

Hover Effect:

.controls select:hover, .hud select:hover {   background: linear-gradient(135deg,     rgba(17, 34, 17, 0.9) 0%,     rgba(34, 51, 34, 0.8) 100%);   color: var(--terminal-bright-green);   border-color: var(--terminal-bright-green);   box-shadow:     0 0 20px rgba(85, 255, 85, 0.4),     inset 0 1px 0 rgba(85, 255, 85, 0.2);   transform: translateY(-1px); }

Focus State (Accessibility):

.controls select:focus, .hud select:focus {   outline: none;   border-color: var(--terminal-cyan);   box-shadow:     0 0 25px rgba(85, 255, 255, 0.5),     inset 0 0 10px rgba(85, 255, 255, 0.1);   color: var(--terminal-cyan); }

The focus state uses cyan to differentiate keyboard navigation from mouse hover, improving accessibility.

Mobile Optimization Strategy

Touch Target Sizing

Mobile devices require larger touch targets for comfortable interaction. Apple's Human Interface Guidelines recommend 44pt minimum, Google suggests 48dp. I implemented 48px minimum heights:

@media (max-width: 768px) {   .controls select, .hud select {     width: 100%;     padding: 12px 40px 12px 15px;     font-size: 14px;     min-height: 48px;  /* Touch-friendly */   } }

Layout Restructuring

On mobile, the Snake game HUD switches from a multi-column grid to a single-column layout:

Desktop:

.hud {   display: grid;   grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));   gap: 10px; }

Mobile:

@media (max-width: 768px) {   .hud {     grid-template-columns: 1fr;     gap: 12px;   } }

This prevents cramped layouts and ensures each stat/control has adequate space.

Disabling Hover on Touch Devices

Hover effects don't work well on touch devices and can cause confusion. I disabled the transform on mobile:

@media (max-width: 768px) {   .controls select:hover, .hud select:hover {     transform: none;  /* No lift animation on touch */   } }

The glow and color changes remain, but the translateY is removed to prevent visual glitches.

Performance Considerations

CSS Transitions Over JavaScript

All animations use CSS transitions instead of JavaScript, ensuring:

  • Hardware acceleration via GPU
  • Smooth 60fps animations
  • Better battery life on mobile
  • Simpler code maintenance

Box Shadow Optimization

Multiple box shadows can be expensive. I kept them minimal and used will-change implicitly through transitions:

transition: all 0.3s ease;

This hints to the browser to optimize rendering for the changing properties.

Testing Approach

  1. Desktop Testing: Chrome DevTools responsive mode
  2. Tablet Testing: iPad Safari (both portrait and landscape)
  3. Mobile Testing: iPhone Safari, Android Chrome
  4. Accessibility: Keyboard navigation, screen reader compatibility
  5. Cross-browser: Safari, Chrome, Firefox

Results

Void Blocks

  • โœ… No more overlapping controls on any device
  • โœ… Rotate button works perfectly
  • โœ… Clean visual hierarchy maintained
  • โœ… Desktop view completely unaffected

Snake Game

  • โœ… Dropdowns match terminal aesthetic
  • โœ… Smooth animations and glow effects
  • โœ… Touch-friendly on all mobile devices
  • โœ… Accessible keyboard navigation
  • โœ… Consistent visual language

Key Takeaways

  1. Always test on real devices - Emulators don't catch everything
  2. Function naming matters - rotatePiece() vs rotate() caused hours of debugging
  3. Spacing is critical - Insufficient padding creates overlap issues
  4. Touch targets need 48px+ - Smaller targets frustrate mobile users
  5. Terminal aesthetic requires custom styling - Default HTML elements break the immersion
  6. Progressive enhancement works - Desktop-first, then enhance for mobile

Technical Debt Avoided

By fixing these issues early, I avoided:

  • Accumulating user complaints
  • Emergency hotfixes
  • Inconsistent mobile experiences
  • Abandoned game sessions due to frustration

Next Steps

Future improvements could include:

  • Custom sound effects for button interactions
  • Haptic feedback on mobile devices
  • Progressive Web App capabilities
  • Offline mode support
  • Game state persistence

The games now provide a polished, terminal-aesthetic experience across all devices while maintaining the cyberpunk vibe that defines my portfolio.


Tech Stack: HTML5 Canvas, Vanilla JavaScript, CSS Grid, CSS Custom Properties Repository: jeffreyjose07.github.io Live Demos: Void Blocks โ€ข Snake Game