Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions test/DARK_MODE_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Dark/Light Mode Toggle Feature

This feature adds a dark/light mode toggle to the p5.js test interfaces, addressing [Issue #8276](https://github.com/processing/p5.js/issues/8276).

## Features

- **One-click toggle**: Simple button to switch between dark and light themes
- **Persistent preferences**: User's theme choice is saved in localStorage
- **Works for all users**: Functions for both anonymous and logged-in users
- **Comprehensive theming**: Applies to background, sidebar, code panels, and all UI elements
- **Accessible**: Includes proper ARIA labels and keyboard navigation support
- **Smooth transitions**: CSS transitions for a polished user experience

## Files Added

- `test/dark-mode-toggle.js` - JavaScript module that handles theme switching logic
- `test/dark-mode.css` - CSS styles for both dark and light themes

## Files Modified

- `test/test.html` - Added dark mode toggle support
- `test/test-reference.html` - Added dark mode toggle support
- `test/test-minified.html` - Added dark mode toggle support

## Usage

The dark mode toggle is automatically initialized when the test pages load. Users can:

1. Click the theme toggle button (🌙/☀️) in the top-right corner
2. The theme preference is automatically saved
3. The preference persists across page reloads and browser sessions

## Implementation Details

### Theme Storage
- Uses `localStorage` with key `p5js-theme-preference`
- Defaults to 'light' mode if no preference is stored
- Gracefully handles cases where localStorage is unavailable

### Theme Application
- Applies `dark-mode` or `light-mode` classes to `<html>` and `<body>` elements
- Uses CSS custom properties (CSS variables) for easy theme customization
- All colors and styles are defined in `dark-mode.css`

### Button Placement
- Positioned in the top-right corner, near the mocha stats
- Fixed position for easy access
- Responsive and accessible

## Browser Support

Works in all modern browsers that support:
- localStorage
- CSS custom properties (CSS variables)
- classList API

## Testing

To test the feature:

1. Open any test HTML file (e.g., `test/test.html`) in a browser
2. Look for the theme toggle button in the top-right corner
3. Click to switch between dark and light modes
4. Reload the page to verify the preference persists

## Future Enhancements

Potential improvements for the web editor implementation:
- System preference detection (prefers-color-scheme media query)
- More granular theme controls
- Custom theme colors
- Theme synchronization across tabs

142 changes: 142 additions & 0 deletions test/dark-mode-toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* Dark/Light Mode Toggle for p5.js Test Interfaces
*
* This module provides a dark/light mode toggle functionality that:
* - Allows one-click theme switching
* - Persists user preference in localStorage
* - Works for both anonymous and logged-in users
* - Applies theme to background, sidebar, and code panels
*/

(function() {
'use strict';

// Theme configuration
var THEME_STORAGE_KEY = 'p5js-theme-preference';
var DARK_THEME_CLASS = 'dark-mode';
var LIGHT_THEME_CLASS = 'light-mode';

// Get current theme from localStorage or default to light
function getStoredTheme() {
try {
return localStorage.getItem(THEME_STORAGE_KEY) || 'light';
} catch (e) {
return 'light';
}
}

// Save theme preference to localStorage
function saveTheme(theme) {
try {
localStorage.setItem(THEME_STORAGE_KEY, theme);
} catch (e) {
// localStorage might not be available, silently fail
console.warn('Could not save theme preference:', e);
}
}

// Apply theme to document
function applyTheme(theme) {
var html = document.documentElement;
var body = document.body;

// Remove existing theme classes
html.classList.remove(DARK_THEME_CLASS, LIGHT_THEME_CLASS);
body.classList.remove(DARK_THEME_CLASS, LIGHT_THEME_CLASS);

// Add new theme class
html.classList.add(theme === 'dark' ? DARK_THEME_CLASS : LIGHT_THEME_CLASS);
body.classList.add(theme === 'dark' ? DARK_THEME_CLASS : LIGHT_THEME_CLASS);

// Save preference
saveTheme(theme);
}

// Toggle between dark and light themes
function toggleTheme() {
var currentTheme = getStoredTheme();
var newTheme = currentTheme === 'dark' ? 'light' : 'dark';
applyTheme(newTheme);
updateToggleButton(newTheme);
return newTheme;
}

// Create and insert toggle button
function createToggleButton() {
var button = document.createElement('button');
button.id = 'theme-toggle';
button.className = 'theme-toggle-button';
button.setAttribute('aria-label', 'Toggle dark/light mode');
button.setAttribute('title', 'Toggle dark/light mode');

// Set initial icon based on current theme
var currentTheme = getStoredTheme();
updateToggleButton(currentTheme, button);

// Add click handler
button.addEventListener('click', function() {
toggleTheme();
});

// Insert button into page
// Try to find a good location (header, stats area, etc.)
var stats = document.getElementById('mocha-stats');
if (stats) {
stats.appendChild(button);
} else {
// Fallback: insert at top of body
var header = document.querySelector('header') || document.body;
if (header) {
header.style.position = 'relative';
header.appendChild(button);
} else {
document.body.insertBefore(button, document.body.firstChild);
}
}

return button;
}

// Update toggle button icon and aria-label
function updateToggleButton(theme, button) {
button = button || document.getElementById('theme-toggle');
if (!button) return;

// Update icon (using Unicode symbols for simplicity)
if (theme === 'dark') {
button.textContent = '☀️';
button.setAttribute('aria-label', 'Switch to light mode');
button.setAttribute('title', 'Switch to light mode');
} else {
button.textContent = '🌙';
button.setAttribute('aria-label', 'Switch to dark mode');
button.setAttribute('title', 'Switch to dark mode');
}
}

// Initialize dark mode on page load
function initDarkMode() {
// Apply stored theme or default
var theme = getStoredTheme();
applyTheme(theme);

// Create toggle button
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createToggleButton);
} else {
createToggleButton();
}
}

// Export for external use if needed
window.p5DarkMode = {
toggle: toggleTheme,
setTheme: applyTheme,
getTheme: getStoredTheme,
init: initDarkMode
};

// Auto-initialize
initDarkMode();
})();

Loading