Skip to content

Multiple UX Polish Updates#72

Merged
LindseyB merged 1 commit intomainfrom
more-ux-polish
Dec 14, 2025
Merged

Multiple UX Polish Updates#72
LindseyB merged 1 commit intomainfrom
more-ux-polish

Conversation

@LindseyB
Copy link
Owner

Screenshot 2025-12-13 at 19-06-44 Astro Horoscope
NVIDIA_Overlay_14Ajd8ZhAv.mp4
NVIDIA_Overlay_CvEjq6zwV1.mp4

This pull request introduces several UI/UX improvements focused on modernizing form controls, enhancing accessibility, and improving user experience, especially around timezone selection and map display. The most significant updates include replacing the manual timezone input with a searchable dropdown, introducing custom Select2 styling, improving dark mode support for the location map, and refining the visual design of form elements and sparkles.

Timezone selection and dropdown enhancements:

  • Replaced the manual timezone offset text input with a searchable, categorized <select> dropdown using Select2, including a comprehensive list of global timezones and common labels for clarity. [1] [2]
  • Added Select2 initialization and persistence via localStorage in timezone-select.js for a better user experience.
  • Included Select2 CSS/JS dependencies and custom styles for consistent appearance and dark mode support. [1] [2]

Location map and dark mode improvements:

  • Updated the Leaflet map to dynamically switch between light and dark tile layers based on dark mode, ensuring the map matches the overall theme. [1] [2] [3] [4]

Form and button visual refinements:

  • Enhanced styling for inputs, selects, and buttons: consistent use of the 'Space Mono' font, increased padding, border radius, focus states, and gradients for a modern look. [1] [2] [3]

Other UI/UX improvements:

  • Improved the sparkle effect to use star characters and colors matching the star trail for a more cohesive visual. [1] [2]
  • Added a dropdown button script for chart selection and improved help icon for location selection. [1] [2]

Content and accessibility:

  • Updated introductory text for clarity and friendliness. (F1dbde44L1R1)

These changes collectively provide a more polished, accessible, and user-friendly interface.

@LindseyB LindseyB marked this pull request as ready for review December 14, 2025 03:08
@LindseyB LindseyB requested a review from Copilot December 14, 2025 03:09
@LindseyB LindseyB merged commit 7c678f5 into main Dec 14, 2025
8 checks passed
@LindseyB LindseyB deleted the more-ux-polish branch December 14, 2025 03:19
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces comprehensive UI/UX improvements focused on modernizing the birth chart form interface. The changes enhance user experience through a searchable timezone dropdown, improved dark mode support for maps, consistent form styling, and refined visual effects.

  • Replaced manual timezone offset input with a searchable Select2 dropdown containing categorized global timezones
  • Added dynamic map tile switching for dark mode support using CartoDB Dark Matter tiles
  • Standardized form input styling with Space Mono font, consistent padding, border radius, and focus states
  • Updated sparkle animations to use star characters with matching colors from the star trail effect

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
tests/test_ux_improvements.py Comprehensive test suite for all new UX features including timezone dropdown, hidden fields, map dark mode, sparkle updates, and form styling
templates/index.html Replaced timezone text input with extensive Select2 dropdown (100+ timezone options), hidden coordinate fields, and updated help button icon
templates/base.html Added Select2 CSS/JS and jQuery dependencies for searchable dropdown functionality
static/js/timezone-select.js New script initializing Select2 on timezone dropdown with localStorage persistence
static/js/sparkles.js Updated to use star characters (✦, ✧, ⋆, etc.) with colored text shadows matching star trail palette
static/js/location-map.js Added MutationObserver for dark mode detection and dynamic tile layer switching between light/dark themes
static/js/dropdown-button.js New script for dropdown button functionality with click-outside-to-close behavior
static/css/style.css Added extensive Select2 custom styling (135+ lines) with dark mode support, updated button gradients, and consistent Space Mono font application
Comments suppressed due to low confidence (1)

templates/index.html:196

  • The latitude and longitude fields are hidden using inline style="display: none;" but they still contain labels and are marked as required. This creates confusion for screen readers, which will announce these required fields even though they're not visible. Consider using aria-hidden="true" on the parent div or removing the required attribute since these fields are auto-filled and users can't interact with them directly.
    <div class="form-group" style="display: none;">
        <label for="latitude">Latitude (auto-filled from map):</label>
        <input type="text" id="latitude" name="latitude" required 
               placeholder="40n42" readonly>
        <small class="field-hint">Format: DDnMM for North, DDsM for South</small>
    </div>
    
    <div class="form-group" style="display: none;">
        <label for="longitude">Longitude (auto-filled from map):</label>
        <input type="text" id="longitude" name="longitude" required 
               placeholder="74w00" readonly>
        <small class="field-hint">Format: DDwMM for West, DDeMM for East</small>

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

response = self.app.post('/chart', data=form_data)
self.assertEqual(response.status_code, 200)
# Should successfully generate chart with timezone data
self.assertIn(b'timezoneoffset', response.data.lower())
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion on line 101 uses .lower() to check for 'timezoneoffset' in the response, but this test doesn't verify that the timezone value was actually processed correctly. The test should check for more specific evidence that the timezone dropdown value was successfully used, such as checking for the actual timezone offset value in the response or verifying chart generation success indicators.

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +66
<option value="-05:00">Eastern Standard Time (EST) -05:00</option>
<option value="-04:00">Eastern Daylight Time (EDT) -04:00</option>
<option value="-06:00">Central Standard Time (CST) -06:00</option>
<option value="-05:00">Central Daylight Time (CDT) -05:00</option>
<option value="-07:00">Mountain Standard Time (MST) -07:00</option>
<option value="-06:00">Mountain Daylight Time (MDT) -06:00</option>
<option value="-08:00">Pacific Standard Time (PST) -08:00</option>
<option value="-07:00">Pacific Daylight Time (PDT) -07:00</option>
<option value="-09:00">Alaska Standard Time (AKST) -09:00</option>
<option value="-08:00">Alaska Daylight Time (AKDT) -08:00</option>
<option value="-10:00">Hawaii-Aleutian Standard Time (HST) -10:00</option>
</optgroup>
<optgroup label="Europe">
<option value="+00:00">Greenwich Mean Time (GMT) +00:00</option>
<option value="+01:00">British Summer Time (BST) +01:00</option>
<option value="+01:00">Central European Time (CET) +01:00</option>
<option value="+02:00">Central European Summer Time (CEST) +02:00</option>
<option value="+02:00">Eastern European Time (EET) +02:00</option>
<option value="+03:00">Eastern European Summer Time (EEST) +03:00</option>
</optgroup>
<optgroup label="Asia">
<option value="+05:30">India Standard Time (IST) +05:30</option>
<option value="+08:00">China Standard Time (CST) +08:00</option>
<option value="+09:00">Japan Standard Time (JST) +09:00</option>
<option value="+09:00">Korea Standard Time (KST) +09:00</option>
<option value="+05:45">Nepal Time (NPT) +05:45</option>
<option value="+06:00">Bangladesh Standard Time (BST) +06:00</option>
<option value="+07:00">Indochina Time (ICT) +07:00</option>
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are duplicate timezone offset values in the "Common US Timezones" optgroup. For example, both "Eastern Standard Time (EST)" and "Central Daylight Time (CDT)" map to "-05:00", and both "Mountain Standard Time (MST)" and "Central Daylight Time (CDT)" map to "-06:00". This creates ambiguity when a stored value is restored from localStorage, as the Select2 dropdown will select the first matching value, which may not be the one the user originally selected. Consider using unique values for each option or implementing a more sophisticated value scheme that includes timezone identifiers.

Suggested change
<option value="-05:00">Eastern Standard Time (EST) -05:00</option>
<option value="-04:00">Eastern Daylight Time (EDT) -04:00</option>
<option value="-06:00">Central Standard Time (CST) -06:00</option>
<option value="-05:00">Central Daylight Time (CDT) -05:00</option>
<option value="-07:00">Mountain Standard Time (MST) -07:00</option>
<option value="-06:00">Mountain Daylight Time (MDT) -06:00</option>
<option value="-08:00">Pacific Standard Time (PST) -08:00</option>
<option value="-07:00">Pacific Daylight Time (PDT) -07:00</option>
<option value="-09:00">Alaska Standard Time (AKST) -09:00</option>
<option value="-08:00">Alaska Daylight Time (AKDT) -08:00</option>
<option value="-10:00">Hawaii-Aleutian Standard Time (HST) -10:00</option>
</optgroup>
<optgroup label="Europe">
<option value="+00:00">Greenwich Mean Time (GMT) +00:00</option>
<option value="+01:00">British Summer Time (BST) +01:00</option>
<option value="+01:00">Central European Time (CET) +01:00</option>
<option value="+02:00">Central European Summer Time (CEST) +02:00</option>
<option value="+02:00">Eastern European Time (EET) +02:00</option>
<option value="+03:00">Eastern European Summer Time (EEST) +03:00</option>
</optgroup>
<optgroup label="Asia">
<option value="+05:30">India Standard Time (IST) +05:30</option>
<option value="+08:00">China Standard Time (CST) +08:00</option>
<option value="+09:00">Japan Standard Time (JST) +09:00</option>
<option value="+09:00">Korea Standard Time (KST) +09:00</option>
<option value="+05:45">Nepal Time (NPT) +05:45</option>
<option value="+06:00">Bangladesh Standard Time (BST) +06:00</option>
<option value="+07:00">Indochina Time (ICT) +07:00</option>
<option value="America/New_York|-05:00">Eastern Standard Time (EST) -05:00</option>
<option value="America/New_York|-04:00">Eastern Daylight Time (EDT) -04:00</option>
<option value="America/Chicago|-06:00">Central Standard Time (CST) -06:00</option>
<option value="America/Chicago|-05:00">Central Daylight Time (CDT) -05:00</option>
<option value="America/Denver|-07:00">Mountain Standard Time (MST) -07:00</option>
<option value="America/Denver|-06:00">Mountain Daylight Time (MDT) -06:00</option>
<option value="America/Los_Angeles|-08:00">Pacific Standard Time (PST) -08:00</option>
<option value="America/Los_Angeles|-07:00">Pacific Daylight Time (PDT) -07:00</option>
<option value="America/Anchorage|-09:00">Alaska Standard Time (AKST) -09:00</option>
<option value="America/Anchorage|-08:00">Alaska Daylight Time (AKDT) -08:00</option>
<option value="Pacific/Honolulu|-10:00">Hawaii-Aleutian Standard Time (HST) -10:00</option>
</optgroup>
<optgroup label="Europe">
<option value="Europe/London|+00:00">Greenwich Mean Time (GMT) +00:00</option>
<option value="Europe/London|+01:00">British Summer Time (BST) +01:00</option>
<option value="Europe/Paris|+01:00">Central European Time (CET) +01:00</option>
<option value="Europe/Paris|+02:00">Central European Summer Time (CEST) +02:00</option>
<option value="Europe/Athens|+02:00">Eastern European Time (EET) +02:00</option>
<option value="Europe/Athens|+03:00">Eastern European Summer Time (EEST) +03:00</option>
</optgroup>
<optgroup label="Asia">
<option value="Asia/Kolkata|+05:30">India Standard Time (IST) +05:30</option>
<option value="Asia/Shanghai|+08:00">China Standard Time (CST) +08:00</option>
<option value="Asia/Tokyo|+09:00">Japan Standard Time (JST) +09:00</option>
<option value="Asia/Seoul|+09:00">Korea Standard Time (KST) +09:00</option>
<option value="Asia/Kathmandu|+05:45">Nepal Time (NPT) +05:45</option>
<option value="Asia/Dhaka|+06:00">Bangladesh Standard Time (BST) +06:00</option>
<option value="Asia/Bangkok|+07:00">Indochina Time (ICT) +07:00</option>

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +21
document.addEventListener('DOMContentLoaded', function() {
// Initialize Select2 on the timezone select element
$('#timezone_offset').select2({
placeholder: 'Select or search for a timezone...',
allowClear: false,
width: '100%',
dropdownAutoWidth: false
});

// If there's a stored value in localStorage, restore it
var storedTimezone = localStorage.getItem('timezone_offset');
if (storedTimezone) {
$('#timezone_offset').val(storedTimezone).trigger('change');
}

// Save timezone to localStorage when changed
$('#timezone_offset').on('change', function() {
localStorage.setItem('timezone_offset', $(this).val());
});
});
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timezone-select.js script depends on jQuery being loaded, but there's no check to ensure jQuery is available before using it. If jQuery fails to load from the CDN for any reason, this script will throw an error and break the page. Add a check like if (typeof jQuery === 'undefined') or wrap the code in a try-catch block for better error handling.

Copilot uses AI. Check for mistakes.
// Initialize Select2 on the timezone select element
$('#timezone_offset').select2({
placeholder: 'Select or search for a timezone...',
allowClear: false,
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Select2 initialization has allowClear: false which means users cannot clear their timezone selection once made. This could be problematic if a user wants to start over or made a mistake. Consider setting allowClear: true to improve user experience, especially since the field is required and validation will still ensure a value is selected on form submission.

Suggested change
allowClear: false,
allowClear: true,

Copilot uses AI. Check for mistakes.

// If there's a stored value in localStorage, restore it
var storedTimezone = localStorage.getItem('timezone_offset');
if (storedTimezone) {
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When restoring the timezone value from localStorage, there's no validation to ensure the stored value is actually one of the valid timezone options. If localStorage contains an invalid or outdated value, Select2 will fail silently. Consider adding validation by checking if the stored value exists as an option in the select element before attempting to restore it.

Suggested change
if (storedTimezone) {
if (
storedTimezone &&
$('#timezone_offset option[value="' + storedTimezone.replace(/"/g, '\\"') + '"]').length > 0
) {

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +102
class TestTimezoneDropdown(unittest.TestCase):
"""Test timezone dropdown functionality"""

def setUp(self):
"""Set up test client"""
self.app = app.test_client()
self.app.testing = True

def test_timezone_is_select_element(self):
"""Test that timezone field is rendered as a select dropdown"""
response = self.app.get('/')
self.assertEqual(response.status_code, 200)

# Check for select element instead of text input
self.assertIn(b'<select id="timezone_offset"', response.data)
self.assertIn(b'class="timezone-select"', response.data)

def test_timezone_has_common_us_timezones(self):
"""Test that timezone dropdown includes common US timezones"""
response = self.app.get('/')
self.assertEqual(response.status_code, 200)

# Check for common US timezone options
self.assertIn(b'Eastern Standard Time (EST)', response.data)
self.assertIn(b'Pacific Standard Time (PST)', response.data)
self.assertIn(b'Central Standard Time (CST)', response.data)
self.assertIn(b'Mountain Standard Time (MST)', response.data)

def test_timezone_has_international_timezones(self):
"""Test that timezone dropdown includes international timezones"""
response = self.app.get('/')
self.assertEqual(response.status_code, 200)

# Check for international timezone options
self.assertIn(b'Greenwich Mean Time (GMT)', response.data)
self.assertIn(b'Central European Time (CET)', response.data)
self.assertIn(b'India Standard Time (IST)', response.data)
self.assertIn(b'Japan Standard Time (JST)', response.data)

def test_timezone_has_utc_offsets(self):
"""Test that timezone dropdown includes UTC offset options"""
response = self.app.get('/')
self.assertEqual(response.status_code, 200)

# Check for UTC offset options
self.assertIn(b'UTC-05:00', response.data)
self.assertIn(b'UTC+00:00', response.data)
self.assertIn(b'UTC+09:00', response.data)

def test_timezone_has_optgroups(self):
"""Test that timezone options are organized in optgroups"""
response = self.app.get('/')
self.assertEqual(response.status_code, 200)

# Check for optgroup labels
self.assertIn(b'<optgroup label="Common US Timezones">', response.data)
self.assertIn(b'<optgroup label="Europe">', response.data)
self.assertIn(b'<optgroup label="Asia">', response.data)
self.assertIn(b'<optgroup label="All UTC Offsets">', response.data)

def test_timezone_select2_script_loaded(self):
"""Test that Select2 library is loaded for timezone dropdown"""
response = self.app.get('/')
self.assertEqual(response.status_code, 200)

# Check for Select2 CSS and JS
self.assertIn(b'select2.min.css', response.data)
self.assertIn(b'select2.min.js', response.data)

def test_timezone_select_js_accessible(self):
"""Test that timezone-select.js file is accessible"""
response = self.app.get('/static/js/timezone-select.js')
self.assertEqual(response.status_code, 200)
self.assertIn(b'select2', response.data)
self.assertIn(b'timezone_offset', response.data)

def test_timezone_form_submission_with_dropdown(self):
"""Test that form submission works with timezone dropdown"""
form_data = {
'birth_date': '1990-01-01',
'birth_time': '12:00',
'timezone_offset': '-05:00',
'latitude': '40n42',
'longitude': '74w00'
}

response = self.app.post('/chart', data=form_data)
self.assertEqual(response.status_code, 200)
# Should successfully generate chart with timezone data
self.assertIn(b'timezoneoffset', response.data.lower())

Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test class is named TestTimezoneDropdown but the duplicate timezone values issue (where multiple timezone names map to the same offset) is not tested. This could lead to bugs when restoring values from localStorage, as the wrong timezone name might be selected. Consider adding a test to verify that unique timezone selections are properly restored.

Copilot uses AI. Check for mistakes.
select2_pos = response_text.find('select2.min.js')

# jQuery should be loaded before Select2
self.assertGreater(jquery_pos, 0)
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test uses assertGreater(jquery_pos, 0) to verify jQuery is present, but if jQuery is not found, find() returns -1, which would pass this assertion. The test should use self.assertNotEqual(jquery_pos, -1) or self.assertTrue(jquery_pos > -1) to correctly verify that jQuery was found before checking the ordering.

Suggested change
self.assertGreater(jquery_pos, 0)
self.assertNotEqual(jquery_pos, -1)

Copilot uses AI. Check for mistakes.
// Dropdown button functionality
function toggleDropdown() {
const dropdown = document.getElementById('chartDropdown');
dropdown.classList.toggle('show');
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The toggleDropdown function doesn't check if the dropdown element exists before attempting to toggle its class. If the element is not found, this will throw a TypeError. Add a null check: if (dropdown) dropdown.classList.toggle('show'); to prevent errors on pages where the dropdown doesn't exist.

Suggested change
dropdown.classList.toggle('show');
if (dropdown) {
dropdown.classList.toggle('show');
}

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +11
const toggleButton = document.querySelector('.dropdown-toggle');

Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable toggleButton.

Suggested change
const toggleButton = document.querySelector('.dropdown-toggle');

Copilot uses AI. Check for mistakes.
import unittest
import os
import sys
from unittest.mock import patch, MagicMock
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'patch' is not used.
Import of 'MagicMock' is not used.

Suggested change
from unittest.mock import patch, MagicMock

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant