1
0
Fork 0

Dark mode for contrast and more bug squashing

This commit is contained in:
Atridad Lahiji 2025-03-25 00:43:08 -06:00
parent fe0cc3482e
commit 84eef1beb2
Signed by: atridad
SSH key fingerprint: SHA256:LGomp8Opq0jz+7kbwNcdfTcuaLRb5Nh0k5AchDDb438
7 changed files with 244 additions and 136 deletions

View file

@ -2,7 +2,7 @@
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: white;
background-color: #000000;
position: relative;
}
@ -11,14 +11,14 @@
top: 20px;
left: 20px;
transform: none;
color: #213547;
background-color: white;
color: #ffffff;
background-color: rgba(40, 40, 40, 0.8);
padding: 12px 20px;
border-radius: 8px;
font-family: Inter, system-ui, Arial, sans-serif;
font-size: 14px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
border: 1px solid #ddd;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
border: 1px solid #444;
z-index: 100;
display: flex;
gap: 12px;
@ -43,27 +43,27 @@
width: calc(100vw / 0.01); /* Width based on minimum zoom (0.01) */
height: calc((100vh - 100px) / 0.01); /* Height based on minimum zoom minus UI height */
transform-origin: 0 0;
background-color: white;
background-color: #000000;
will-change: transform;
contain: layout paint;
}
.target {
position: absolute;
background-color: #444444; /* Darker gray for better visibility */
background-color: #666666; /* Light gray for better contrast on black */
border-radius: 50%;
transition: background-color 0.2s ease;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);
box-shadow: 0 0 15px rgba(100, 100, 100, 0.4);
transform: translate(-50%, -50%);
will-change: transform;
contain: layout;
border: 4px solid rgba(0, 0, 0, 0.4); /* Darker border for better contrast */
border: 4px solid rgba(180, 180, 180, 0.6); /* Lighter border for contrast */
}
.target.active {
background-color: #22aa22; /* Darker green for better visibility */
box-shadow: 0 0 30px rgba(34, 170, 34, 0.8);
border: 4px solid white;
background-color: #22cc22; /* Bright green for better visibility */
box-shadow: 0 0 30px rgba(34, 204, 34, 0.8);
border: 4px solid #ffffff;
z-index: 10;
}
@ -75,7 +75,7 @@ body,
width: 100%;
height: 100%;
overflow: hidden;
background-color: white;
background-color: #000000;
}
.controls {
@ -94,13 +94,14 @@ body,
display: flex;
align-items: center;
gap: 6px;
background: white;
border: 1px solid #ddd;
background: rgba(40, 40, 40, 0.8);
border: 1px solid #444;
padding: 8px 16px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
color: #ffffff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
transition: all 0.2s ease;
}
@ -108,7 +109,7 @@ body,
.rounds-toggle:hover,
.export-button:hover,
.reset-button:hover {
background: #f5f5f5;
background: #444444;
border-color: #646cff;
}
@ -118,17 +119,17 @@ body,
}
.export-button:disabled:hover {
background: white;
border-color: #ddd;
background: rgba(40, 40, 40, 0.8);
border-color: #444;
}
.reset-button {
border-color: #ffcdd2;
border-color: #aa3333;
}
.reset-button:hover {
background: #fff5f5;
border-color: #ef5350;
background: #442222;
border-color: #ff4444;
}
/* Preset Selection */
@ -144,8 +145,9 @@ body,
.preset-button {
padding: 8px 16px;
background: white;
border: 1px solid #ddd;
background: #333333;
color: #ffffff;
border: 1px solid #555;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
@ -154,32 +156,14 @@ body,
.preset-button.active {
background: #646cff;
color: white;
border-color: #646cff;
border-color: #8088ff;
}
.preset-button:hover:not(.active) {
background: #f5f5f5;
background: #444444;
border-color: #646cff;
}
/* Mode Selection */
.mode-selection {
margin-bottom: 20px;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.radio-group label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
/* Break Screen */
.break-screen {
position: fixed;
@ -187,7 +171,7 @@ body,
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
@ -195,17 +179,18 @@ body,
}
.break-container {
background-color: white;
background-color: #222222;
color: #ffffff;
padding: 30px;
border-radius: 10px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.5);
max-width: 500px;
text-align: center;
}
.break-container h2 {
margin-top: 0;
color: #213547;
color: #ffffff;
}
.break-container button {
@ -221,7 +206,7 @@ body,
}
.break-container button:hover {
background-color: #4e57cc;
background-color: #7a82ff;
}
/* Resize Warning Popup */
@ -231,7 +216,7 @@ body,
left: 50%;
transform: translate(-50%, -50%);
z-index: 2000;
background-color: rgba(0, 0, 0, 0.5);
background-color: rgba(0, 0, 0, 0.7);
width: 100%;
height: 100%;
display: flex;
@ -241,17 +226,18 @@ body,
}
.resize-warning-content {
background-color: white;
background-color: #222222;
color: #ffffff;
padding: 20px 30px;
border-radius: 10px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.5);
max-width: 400px;
text-align: center;
}
.resize-warning-content h3 {
margin-top: 0;
color: #213547;
color: #ffffff;
}
.resize-warning-content button {
@ -267,7 +253,7 @@ body,
}
.resize-warning-content button:hover {
background-color: #4e57cc;
background-color: #7a82ff;
}
@keyframes fadeIn {
@ -292,26 +278,26 @@ body,
padding: 20px;
border-radius: 12px;
color: white;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
text-align: center;
transition: background-color 0.3s ease;
}
.preset-indicator.preset-A {
background-color: #4285f4; /* Blue for Preset A */
background-color: rgba(66, 133, 244, 0.9); /* Blue for Preset A */
}
.preset-indicator.preset-B {
background-color: #34a853; /* Green for Preset B */
background-color: rgba(52, 168, 83, 0.9); /* Green for Preset B */
}
.preset-indicator.preset-C {
background-color: #ea4335; /* Red for Preset C */
background-color: rgba(234, 67, 53, 0.9); /* Red for Preset C */
}
.preset-indicator.preset-D {
background-color: #fbbc05; /* Yellow for Preset D */
background-color: rgba(251, 188, 5, 0.9); /* Yellow for Preset D */
}
/* Preset Transition Effect */
@ -334,7 +320,7 @@ body,
font-size: 36px;
font-weight: bold;
color: white;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.8);
padding: 30px 60px;
border-radius: 16px;
text-align: center;
@ -342,19 +328,19 @@ body,
}
.preset-transition-overlay.preset-A .preset-transition-content {
background-color: rgba(66, 133, 244, 0.85); /* Blue for Preset A */
background-color: rgba(66, 133, 244, 0.9); /* Blue for Preset A */
}
.preset-transition-overlay.preset-B .preset-transition-content {
background-color: rgba(52, 168, 83, 0.85); /* Green for Preset B */
background-color: rgba(52, 168, 83, 0.9); /* Green for Preset B */
}
.preset-transition-overlay.preset-C .preset-transition-content {
background-color: rgba(234, 67, 53, 0.85); /* Red for Preset C */
background-color: rgba(234, 67, 53, 0.9); /* Red for Preset C */
}
.preset-transition-overlay.preset-D .preset-transition-content {
background-color: rgba(251, 188, 5, 0.85); /* Yellow for Preset D */
background-color: rgba(251, 188, 5, 0.9); /* Yellow for Preset D */
}
@keyframes fadeInOut {

View file

@ -4,7 +4,7 @@
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
@ -12,15 +12,16 @@
}
.instructions-panel {
background: white;
background: #222222;
border-radius: 12px;
padding: 30px;
max-width: 600px;
width: 90%;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
position: relative;
max-height: 90vh;
overflow-y: auto;
color: #ffffff;
}
.instructions-header {
@ -33,18 +34,18 @@
.instructions-header h2 {
margin: 0;
font-size: 1.5em;
color: #213547;
color: #ffffff;
}
.instructions-content {
color: #4a5568;
color: #cccccc;
line-height: 1.6;
}
.instructions-content h3 {
margin-top: 25px;
margin-bottom: 15px;
color: #213547;
color: #ffffff;
}
.instructions-content ul {
@ -81,20 +82,21 @@
.keyboard-shortcut {
display: inline-block;
background: #f1f5f9;
background: #333333;
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
margin: 0 2px;
color: #ffffff;
}
/* Mode Selection Styles */
.mode-selection {
background: #f8fafc;
background: #333333;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
border: 1px solid #e2e8f0;
border: 1px solid #444444;
}
.mode-selection .radio-group {
@ -107,9 +109,9 @@
display: flex;
flex-direction: column;
padding: 15px;
border: 1px solid #e2e8f0;
border: 1px solid #444444;
border-radius: 8px;
background: white;
background: #222222;
cursor: pointer;
transition: all 0.2s;
position: relative;
@ -117,25 +119,26 @@
.mode-selection label:hover {
border-color: #646cff;
box-shadow: 0 2px 8px rgba(100, 108, 255, 0.1);
box-shadow: 0 2px 8px rgba(100, 108, 255, 0.3);
}
.mode-selection input[type="radio"] {
position: absolute;
top: 15px;
left: 15px;
accent-color: #646cff;
}
.mode-label {
font-weight: bold;
color: #213547;
color: #ffffff;
margin-left: 25px;
font-size: 16px;
}
.mode-description {
margin: 8px 0 0 25px;
color: #64748b;
color: #cccccc;
font-size: 14px;
}
@ -144,11 +147,11 @@
}
.mode-selection input[type="radio"]:checked ~ .mode-description {
color: #4a5568;
color: #dddddd;
}
.warning-message {
background-color: #fff8e1;
background-color: #332211;
border-left: 4px solid #ffb300;
padding: 15px;
border-radius: 4px;
@ -156,7 +159,7 @@
}
.warning-message h3 {
color: #e65100;
color: #ffb300;
margin-top: 0;
margin-bottom: 10px;
display: flex;
@ -165,5 +168,5 @@
.warning-message p {
margin: 0;
color: #333;
color: #dddddd;
}

View file

@ -2,16 +2,16 @@
position: fixed;
top: 80px;
left: 20px;
background: white;
border: 1px solid #ddd;
background: #222222;
border: 1px solid #444;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
width: 300px;
max-height: calc(100vh - 120px);
overflow-y: auto;
color: #213547;
color: #ffffff;
}
.rounds-header {
@ -21,16 +21,16 @@
margin-bottom: 20px;
position: sticky;
top: 0;
background: white;
background: #222222;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
border-bottom: 1px solid #444;
}
.rounds-header h2 {
margin: 0;
font-size: 1.2em;
font-weight: 600;
color: #213547;
color: #ffffff;
}
.rounds-header button {
@ -40,6 +40,7 @@
cursor: pointer;
padding: 0 8px;
box-shadow: none;
color: #ffffff;
}
.rounds-header button:hover {
@ -55,27 +56,27 @@
.round-item {
padding: 12px;
border: 1px solid #eee;
border: 1px solid #444;
border-radius: 6px;
background: #f9f9f9;
background: #333333;
transition: all 0.2s ease;
}
.round-item:hover {
border-color: #ddd;
background: white;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
border-color: #555;
background: #3a3a3a;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
.round-number {
font-weight: 600;
margin-bottom: 8px;
color: #213547;
color: #ffffff;
}
.round-stats {
font-size: 0.9em;
color: #666;
color: #cccccc;
display: flex;
flex-direction: column;
gap: 4px;

View file

@ -2,28 +2,36 @@
position: fixed;
top: 80px;
right: 20px;
background: white;
border: 1px solid #ddd;
background: #222222;
border: 1px solid #444;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 15px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
width: 300px;
color: #213547;
width: 260px;
color: #ffffff;
max-height: 80vh;
overflow-y: auto;
}
/* Override all child backgrounds except for specific elements */
.settings-panel * {
background-color: #222222;
color: #ffffff;
}
.settings-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
margin-bottom: 15px;
}
.settings-header h2 {
margin: 0;
font-size: 1.2em;
font-weight: 600;
color: #213547;
color: #ffffff;
}
.settings-header button {
@ -33,6 +41,7 @@
cursor: pointer;
padding: 0 8px;
box-shadow: none;
color: #ffffff;
}
.settings-header button:hover {
@ -43,14 +52,14 @@
.slider-row {
display: flex;
align-items: center;
margin: 10px 0;
margin: 8px 0;
gap: 10px;
}
.slider-row label {
width: 120px;
width: 100px;
font-size: 14px;
color: #213547;
color: #ffffff;
}
.slider-row input[type="range"] {
@ -58,14 +67,14 @@
}
.slider-row .value-input {
width: 50px;
width: 40px;
text-align: center;
font-size: 14px;
color: #213547;
color: #ffffff;
padding: 4px;
border: 1px solid #ddd;
border: 1px solid #444;
border-radius: 4px;
background: white;
background: #333333;
}
.slider-row .value-input:focus {
@ -75,13 +84,13 @@
}
.slider-row .value-input:disabled {
background: #f5f5f5;
background: #444;
color: #999;
cursor: not-allowed;
}
.acceleration-toggle {
margin-bottom: 20px;
margin-bottom: 15px;
}
.acceleration-toggle label {
@ -90,12 +99,93 @@
gap: 8px;
cursor: pointer;
font-size: 14px;
color: #213547;
color: #ffffff;
}
.sliders-container h3 {
margin: 0 0 15px 0;
margin: 0 0 12px 0;
font-size: 1em;
font-weight: 600;
color: #213547;
color: #ffffff;
}
/* Mode selection styling */
.mode-selection {
margin-bottom: 15px;
background: #222222;
color: #ffffff;
}
.mode-selection h3 {
margin: 0 0 12px 0;
font-size: 1em;
font-weight: 600;
color: #ffffff;
}
.mode-selection .radio-group {
display: flex;
flex-direction: column;
gap: 8px;
color: #ffffff;
background: #222222;
}
/* Completely restyle radio buttons */
.radio-group label {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
font-size: 14px;
color: #ffffff;
background: #222222;
border: none;
padding: 5px;
border-radius: 4px;
}
.radio-group input[type="radio"] {
accent-color: #646cff;
width: 16px;
height: 16px;
min-width: 16px;
margin: 0;
padding: 0;
}
/* Preset Selection */
.preset-selection {
margin-bottom: 15px;
background: #222222;
}
.preset-selection h3 {
margin: 0 0 12px 0;
font-size: 1em;
font-weight: 600;
color: #ffffff;
}
.preset-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.preset-button {
padding: 5px 10px;
background: #333333;
color: #ffffff;
border: 1px solid #555;
border-radius: 5px;
cursor: pointer;
transition: all 0.2s ease;
font-size: 13px;
}
.preset-button.active {
background: #646cff;
color: white;
border-color: #8088ff;
}

View file

@ -102,7 +102,7 @@ export const Settings: React.FC<SettingsProps> = ({
checked={mode === 'freestyle'}
onChange={() => handleModeChange('freestyle')}
/>
Freestyle Mode
<span>Freestyle Mode</span>
</label>
<label>
<input
@ -112,7 +112,7 @@ export const Settings: React.FC<SettingsProps> = ({
checked={mode === 'study'}
onChange={() => handleModeChange('study')}
/>
Study Mode (25 targets per preset)
<span>Study Mode (25 targets per preset)</span>
</label>
</div>
</div>

View file

@ -10,24 +10,36 @@ export const calculateZoomDelta = (
export const calculateInitialViewport = (initialZoom: number): ViewportOffset => {
// When fully zoomed out, we want to see the entire content area
// The target distribution is sized to match the viewport at minimum zoom
// Account for the UI exclusion zone at the top
// Account for safety margins and UI exclusion zones
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// Same UI margin as in target generation
// Same UI margins as in target generation
const UI_TOP_MARGIN = 100;
const UI_BOTTOM_MARGIN = 50; // Added bottom margin
// Center the viewport on the target area
const areaWidth = windowWidth / CONSTANTS.ZOOM_LEVELS.min;
const areaHeight = (windowHeight - UI_TOP_MARGIN) / CONSTANTS.ZOOM_LEVELS.min;
// We need to account for the safety margin used in target generation
const MAX_RADIUS = CONSTANTS.TARGET_MAX_RADIUS;
const safetyMargin = MAX_RADIUS * 1.5; // Increased from 1.2 to 1.5
// Calculate the full target area including margins
const minZoom = CONSTANTS.ZOOM_LEVELS.min;
const areaWidth = windowWidth / minZoom;
const areaHeight = (windowHeight - UI_TOP_MARGIN - UI_BOTTOM_MARGIN) / minZoom;
// Account for the UI exclusion zone in Y offset
const startY = UI_TOP_MARGIN / CONSTANTS.ZOOM_LEVELS.min;
const startY = UI_TOP_MARGIN / minZoom;
// Center viewport on the usable area
const usableWidth = areaWidth - (safetyMargin * 2);
const usableHeight = areaHeight - (safetyMargin * 2);
const usableCenterX = safetyMargin + (usableWidth / 2);
const usableCenterY = startY + safetyMargin + (usableHeight / 2);
// Position viewport so that the center of the usable area is in the middle of the screen
return {
x: (areaWidth - windowWidth / initialZoom) / 2,
y: startY + (areaHeight - windowHeight / initialZoom) / 2
x: usableCenterX - (windowWidth / initialZoom / 2),
y: usableCenterY - (windowHeight / initialZoom / 2)
};
};

View file

@ -11,14 +11,15 @@ export const generateTargets = () => {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// Define UI exclusion zone (top area where UI elements are located)
// Define UI exclusion zones
const UI_TOP_MARGIN = 100; // pixels from top to exclude
const UI_BOTTOM_MARGIN = 50; // pixels from bottom to exclude for any potential UI
// Base the target area on the minimum zoom level
// This ensures when fully zoomed out, we see the entire grid
const minZoom = CONSTANTS.ZOOM_LEVELS.min;
const areaWidth = windowWidth / minZoom;
const areaHeight = (windowHeight - UI_TOP_MARGIN) / minZoom;
const areaHeight = (windowHeight - UI_TOP_MARGIN - UI_BOTTOM_MARGIN) / minZoom;
// Start Y position below the UI elements
const startY = UI_TOP_MARGIN / minZoom;
@ -28,18 +29,33 @@ export const generateTargets = () => {
const cellWidth = areaWidth / gridSize;
const cellHeight = areaHeight / gridSize;
// Increase safety margin to prevent any clipping
const safetyMargin = MAX_RADIUS * 1.5; // Increased from 1.2 to 1.5
// Adjust the usable area to prevent clipping at edges
const usableWidth = areaWidth - (safetyMargin * 2);
const usableHeight = areaHeight - (safetyMargin * 2);
// New starting point that accounts for target radius
const startUsableX = safetyMargin;
const startUsableY = startY + safetyMargin;
// Calculate adjusted cell dimensions for the usable area
const adjustedCellWidth = usableWidth / gridSize;
const adjustedCellHeight = usableHeight / gridSize;
for (let i = 0; i < CONSTANTS.TARGET_COUNT; i++) {
// Calculate grid position
const gridX = i % gridSize;
const gridY = Math.floor(i / gridSize);
// Add some randomness within each grid cell
const randomOffsetX = (Math.random() - 0.5) * cellWidth * 0.6;
const randomOffsetY = (Math.random() - 0.5) * cellHeight * 0.6;
// Reduce randomness to keep targets more centered in their cells
const randomOffsetX = (Math.random() - 0.5) * adjustedCellWidth * 0.4; // Reduced from 0.6
const randomOffsetY = (Math.random() - 0.5) * adjustedCellHeight * 0.4; // Reduced from 0.6
// Calculate final position - add startY to push everything down below UI
const x = cellWidth * (gridX + 0.5) + randomOffsetX;
const y = startY + cellHeight * (gridY + 0.5) + randomOffsetY;
// Calculate final position - add margin to prevent edge clipping
const x = startUsableX + adjustedCellWidth * (gridX + 0.5) + randomOffsetX;
const y = startUsableY + adjustedCellHeight * (gridY + 0.5) + randomOffsetY;
// Random radius
const radius = MIN_RADIUS + Math.random() * (MAX_RADIUS - MIN_RADIUS);