v1.0.1: Add comprehensive theme switcher and fix dark mode issues
- Add theme switcher widget with Auto/Light/Dark options in header - Implement manual theme override system with localStorage persistence - Add complete button theme overrides for all variants (primary, outline-*) - Fix missing focus states and placeholder colors for light theme - Add proper alert styling for both themes - Fix expression input error state colors in dark theme - Complete comprehensive theme coverage for all UI elements - Theme switcher overrides CSS media queries when manually selected - All buttons, inputs, and surfaces now properly adapt to theme changes
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jmespath-playground",
|
"name": "jmespath-playground",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "A React-based web application for testing JMESPath expressions against JSON data",
|
"description": "A React-based web application for testing JMESPath expressions against JSON data",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
289
src/App.css
289
src/App.css
@@ -107,6 +107,295 @@ footer a:hover {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Manual theme overrides */
|
||||||
|
.theme-light {
|
||||||
|
/* Force light theme regardless of system preference */
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
color: #212529 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .header-section {
|
||||||
|
background-color: transparent !important;
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .card {
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
|
||||||
|
color: #212529 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .card-header {
|
||||||
|
background-color: #f8f9fa !important;
|
||||||
|
border-bottom: 2px solid #dee2e6 !important;
|
||||||
|
color: #212529 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .jmespath-input {
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
border: 1px solid #ced4da !important;
|
||||||
|
color: #495057 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .json-input,
|
||||||
|
.theme-light .result-output {
|
||||||
|
background-color: #f8f9fa !important;
|
||||||
|
border: 1px solid #dee2e6 !important;
|
||||||
|
color: #495057 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .text-muted {
|
||||||
|
color: #6c757d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .jmespath-input:focus {
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
color: #495057 !important;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .jmespath-input::placeholder {
|
||||||
|
color: #6c757d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .json-input::placeholder,
|
||||||
|
.theme-light .result-output::placeholder {
|
||||||
|
color: #6c757d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .json-input:focus,
|
||||||
|
.theme-light .result-output:focus {
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
color: #495057 !important;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .alert-danger {
|
||||||
|
background-color: #f8d7da !important;
|
||||||
|
border-color: #f5c6cb !important;
|
||||||
|
color: #721c24 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-primary {
|
||||||
|
background-color: #007bff !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-secondary {
|
||||||
|
color: #6c757d !important;
|
||||||
|
border-color: #6c757d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-secondary:hover {
|
||||||
|
background-color: #6c757d !important;
|
||||||
|
border-color: #6c757d !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-success {
|
||||||
|
color: #28a745 !important;
|
||||||
|
border-color: #28a745 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-success:hover {
|
||||||
|
background-color: #28a745 !important;
|
||||||
|
border-color: #28a745 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-info {
|
||||||
|
color: #17a2b8 !important;
|
||||||
|
border-color: #17a2b8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-info:hover {
|
||||||
|
background-color: #17a2b8 !important;
|
||||||
|
border-color: #17a2b8 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-primary {
|
||||||
|
color: #007bff !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-primary:hover {
|
||||||
|
background-color: #007bff !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-danger {
|
||||||
|
color: #dc3545 !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .btn-outline-danger:hover {
|
||||||
|
background-color: #dc3545 !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light footer {
|
||||||
|
background-color: #f8f9fa !important;
|
||||||
|
border-top: 1px solid #dee2e6 !important;
|
||||||
|
color: #212529 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light footer a {
|
||||||
|
color: #6c757d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light footer a:hover {
|
||||||
|
color: #495057 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force dark theme regardless of system preference */
|
||||||
|
.theme-dark {
|
||||||
|
background-color: #1a1a1a !important;
|
||||||
|
color: #e9ecef !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .header-section {
|
||||||
|
background-color: #2d2d2d !important;
|
||||||
|
border-bottom: 1px solid #404040 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .card {
|
||||||
|
background-color: #2d2d2d !important;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
|
||||||
|
color: #e9ecef !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .card-header {
|
||||||
|
background-color: #3a3a3a !important;
|
||||||
|
border-bottom: 2px solid #505050 !important;
|
||||||
|
color: #f8f9fa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .jmespath-input {
|
||||||
|
background-color: #3a3a3a !important;
|
||||||
|
border: 1px solid #505050 !important;
|
||||||
|
color: #f8f9fa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .jmespath-input::placeholder {
|
||||||
|
color: #adb5bd !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .jmespath-input:focus {
|
||||||
|
background-color: #404040 !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .json-input,
|
||||||
|
.theme-dark .result-output {
|
||||||
|
background-color: #2a2a2a !important;
|
||||||
|
border: 1px solid #505050 !important;
|
||||||
|
color: #e9ecef !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .json-input::placeholder,
|
||||||
|
.theme-dark .result-output::placeholder {
|
||||||
|
color: #6c757d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .json-input:focus,
|
||||||
|
.theme-dark .result-output:focus {
|
||||||
|
background-color: #323232 !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .alert-danger {
|
||||||
|
background-color: #3d1a1a !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
color: #f8d7da !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .text-muted {
|
||||||
|
color: #adb5bd !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark footer {
|
||||||
|
background-color: #2d2d2d !important;
|
||||||
|
border-top: 1px solid #404040 !important;
|
||||||
|
color: #e9ecef !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark footer a {
|
||||||
|
color: #adb5bd !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark footer a:hover {
|
||||||
|
color: #e9ecef !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-primary {
|
||||||
|
background-color: #007bff !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-secondary {
|
||||||
|
color: #6c757d !important;
|
||||||
|
border-color: #6c757d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-secondary:hover {
|
||||||
|
background-color: #6c757d !important;
|
||||||
|
border-color: #6c757d !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-success {
|
||||||
|
color: #28a745 !important;
|
||||||
|
border-color: #28a745 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-success:hover {
|
||||||
|
background-color: #28a745 !important;
|
||||||
|
border-color: #28a745 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-info {
|
||||||
|
color: #17a2b8 !important;
|
||||||
|
border-color: #17a2b8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-info:hover {
|
||||||
|
background-color: #17a2b8 !important;
|
||||||
|
border-color: #17a2b8 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-primary {
|
||||||
|
color: #007bff !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-primary:hover {
|
||||||
|
background-color: #007bff !important;
|
||||||
|
border-color: #007bff !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-danger {
|
||||||
|
color: #dc3545 !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .btn-outline-danger:hover {
|
||||||
|
background-color: #dc3545 !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Dark mode support */
|
/* Dark mode support */
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
body {
|
body {
|
||||||
|
|||||||
58
src/App.js
58
src/App.js
@@ -5,6 +5,10 @@ import './App.css';
|
|||||||
// JMESPath Testing Tool - Main Application Component
|
// JMESPath Testing Tool - Main Application Component
|
||||||
function App() {
|
function App() {
|
||||||
const [jmespathExpression, setJmespathExpression] = useState('people[0].name');
|
const [jmespathExpression, setJmespathExpression] = useState('people[0].name');
|
||||||
|
const [theme, setTheme] = useState(() => {
|
||||||
|
// Load theme from localStorage or default to 'auto'
|
||||||
|
return localStorage.getItem('theme') || 'auto';
|
||||||
|
});
|
||||||
const [jsonData, setJsonData] = useState(`{
|
const [jsonData, setJsonData] = useState(`{
|
||||||
"people": [
|
"people": [
|
||||||
{
|
{
|
||||||
@@ -24,6 +28,29 @@ function App() {
|
|||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [jsonError, setJsonError] = useState('');
|
const [jsonError, setJsonError] = useState('');
|
||||||
|
|
||||||
|
// Theme management
|
||||||
|
useEffect(() => {
|
||||||
|
// Apply theme to document
|
||||||
|
const applyTheme = (selectedTheme) => {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.className = ''; // Clear existing theme classes
|
||||||
|
|
||||||
|
if (selectedTheme === 'light') {
|
||||||
|
root.classList.add('theme-light');
|
||||||
|
} else if (selectedTheme === 'dark') {
|
||||||
|
root.classList.add('theme-dark');
|
||||||
|
}
|
||||||
|
// 'auto' uses CSS media queries (no class needed)
|
||||||
|
};
|
||||||
|
|
||||||
|
applyTheme(theme);
|
||||||
|
localStorage.setItem('theme', theme);
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
|
const handleThemeChange = (newTheme) => {
|
||||||
|
setTheme(newTheme);
|
||||||
|
};
|
||||||
|
|
||||||
const evaluateExpression = () => {
|
const evaluateExpression = () => {
|
||||||
try {
|
try {
|
||||||
// Clear previous errors
|
// Clear previous errors
|
||||||
@@ -185,8 +212,37 @@ function App() {
|
|||||||
<div className="header-section py-2">
|
<div className="header-section py-2">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 text-center">
|
<div className="col-12 text-center position-relative">
|
||||||
<h2 className="mb-1">JMESPath Testing Tool</h2>
|
<h2 className="mb-1">JMESPath Testing Tool</h2>
|
||||||
|
{/* Theme switcher */}
|
||||||
|
<div className="position-absolute top-0 end-0">
|
||||||
|
<div className="btn-group btn-group-sm" role="group" aria-label="Theme switcher">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn ${theme === 'auto' ? 'btn-primary' : 'btn-outline-secondary'}`}
|
||||||
|
onClick={() => handleThemeChange('auto')}
|
||||||
|
title="Auto (follow system)"
|
||||||
|
>
|
||||||
|
🌓 Auto
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn ${theme === 'light' ? 'btn-primary' : 'btn-outline-secondary'}`}
|
||||||
|
onClick={() => handleThemeChange('light')}
|
||||||
|
title="Light theme"
|
||||||
|
>
|
||||||
|
☀️ Light
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn ${theme === 'dark' ? 'btn-primary' : 'btn-outline-secondary'}`}
|
||||||
|
onClick={() => handleThemeChange('dark')}
|
||||||
|
title="Dark theme"
|
||||||
|
>
|
||||||
|
🌙 Dark
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -92,4 +92,26 @@ code {
|
|||||||
.result-output {
|
.result-output {
|
||||||
background-color: #e7f3ff;
|
background-color: #e7f3ff;
|
||||||
border-color: #b3d7ff;
|
border-color: #b3d7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode support for error states */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.error {
|
||||||
|
background-color: #4a1e1e !important;
|
||||||
|
border-color: #6d2c2c !important;
|
||||||
|
color: #f8d7da !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manual theme overrides for error states */
|
||||||
|
.theme-dark .error {
|
||||||
|
background-color: #4a1e1e !important;
|
||||||
|
border-color: #6d2c2c !important;
|
||||||
|
color: #f8d7da !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .error {
|
||||||
|
background-color: #f8d7da !important;
|
||||||
|
border-color: #f5c6cb !important;
|
||||||
|
color: #721c24 !important;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user