Skip to content

(WIP)feat: Customizable Theme#119

Draft
prem-k-r wants to merge 2 commits intomainfrom
edit-theme-refactor
Draft

(WIP)feat: Customizable Theme#119
prem-k-r wants to merge 2 commits intomainfrom
edit-theme-refactor

Conversation

@prem-k-r
Copy link
Owner

@prem-k-r prem-k-r commented Jan 4, 2026

📌 Description

🎨 Visual Changes (Screenshots / Videos)

Preview

🔗 Related Issues

  • Closes #<issue_number>
  • Related to #<issue_number>

✅ Checklist

  • I have read and followed the Contributing Guidelines.
  • My code follows the project's coding style and conventions.
  • I have tested my changes thoroughly to ensure expected behavior.
  • I have verified compatibility across Chrome and Firefox (additional browsers if applicable).
  • I have attached relevant visual evidence (screenshots/videos) if applicable.
  • I have updated the CHANGELOG.md under the appropriate categories with all my changes in this PR.

Customizable Theme Implementation

This PR introduces a comprehensive customizable theme system, refactoring the existing theme handling from scattered global interactions into a centralized, object-oriented architecture. The implementation spans UI, styling, logic, and configuration layers.

Major Changes

Theme Management System (scripts/theme.js)

  • Introduced ThemeManager class to replace ad-hoc DOM/storage interactions with structured theme management
  • Implemented comprehensive color variable system with support for primary, secondary, accent, background, text, and surface colors
  • Added dark mode color generation with HSL-based color space transformations via generateDarkModeColors() and transformColorForDark()
  • Implemented system theme detection and synchronization with user preference handling
  • Centralized storage management through STORAGE_KEYS for theme, custom colors, custom variables, and loading color

User Interface (index.html)

  • Added color editor modal (colorModal) with dedicated controls for editing theme colors (Save, Reset actions)
  • Implemented color palette with preset options (red, yellow, green, cyan, blue, pink, purple, orange, brown, silver) plus black theme toggle
  • Introduced reusable adjustment-icon SVG symbol and replaced multiple inline SVGs with references to consolidate iconography
  • Added new color picker UI with labeled input and editor button within a pickerContainer
  • Restructured theming section to support both preset colors and custom color editing

Styling (style.css)

  • Updated dark theme gating from #darkTheme to #blackTheme selector
  • Refreshed color constants: changed dark mode background from #171615 to #0a0a0a
  • Added comprehensive color picker modal styling including colorModal, modalContent, and related UI scaffolding
  • Introduced animations (fadeIn, slideUp, invalid-shake) for modal interactions
  • Increased bookmark-sidebar width from 380px to 420px and adjusted related layout positioning
  • Enhanced contrast and legibility adjustments across multiple UI elements (todo items, tiles, location text)

Localization & Configuration

  • Removed deprecated rangColor translation key from locales/en.js and scripts/languages.js
  • Updated copyright attribution in LICENSE from "XengShi (2023-2025)" to "Prem (2024-2026)"
  • Updated README.md copyright years to 2024-2026 and added explicit privacy commitments section detailing no ads, no trackers, and local-only data storage
  • Bumped version from 3.3.3 to 3.3.4 in both manifest.json and manifest(firefox).json

Implementation Details

The new ThemeManager class provides:

  • Modal-based color editing workflow with live preview and favicon synchronization
  • Automatic dark mode color generation from light theme values
  • Custom variable persistence across browser sessions
  • Event-driven theme mode changes with UI indicator updates
  • Throttled color adjustments to optimize performance during modal edits
  • Validation for hex color inputs and near-white color detection

Code Quality Impact

This refactor consolidates previously scattered theme logic into a single, maintainable class, improving code organization and enabling future theme customization features.

@prem-k-r prem-k-r added enhancement New feature or request refactor Code improvement without changing behavior work-in-progress Under active development or being worked on by the assignee. Not ready to close or merge yet ui/ux labels Jan 4, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 4, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive theming refactoring alongside UI enhancements. Changes include migrating theme management from scattered globals to a centralized ThemeManager class, introducing a new color editor modal interface, consolidating icon usage, removing the rangColor localization entry, updating copyright information, adding privacy commitments to documentation, and applying corresponding CSS updates for dark mode and the new color picker UI. Version bumped from 3.3.3 to 3.3.4.

Changes

Cohort / File(s) Summary
Metadata Updates
LICENSE, README.md
Updated copyright from "XengShi" (2023-2025) to "Prem" (2024-2026); added privacy commitments section to README (no ads, no trackers, local storage only)
UI Structure & Icons
index.html
Introduced reusable SVG adjustment-icon symbol; added color editor modal with colorModal, colorVariables container, and Reset/Save actions; expanded color palette options (red, yellow, green, cyan, blue, pink, purple, orange, brown, silver, blackTheme); replaced legacy color picker with new modal-driven UI
Localization
locales/en.js, scripts/languages.js
Removed rangColor translation key ("Pick color") from exported locale object and translationMap array
Version Bumps
manifest.json, manifest(firefox).json
Updated version field from "3.3.3" to "3.3.4"
Theme Management Refactor
scripts/theme.js
Replaced scattered globals with new ThemeManager class; introduced storage keys (THEME, CUSTOM_COLOR, PREFERRED_MODE, CUSTOM_VARIABLES, LOADING_COLOR); added system theme detection; implemented comprehensive theme mode management (setThemeMode, applyDarkModeTransformation, generateDarkModeColors); added color utilities (transformColorForDark, adjustColor, isValidHexColor); introduced color editor modal flow (openModal, setupModalInputSync, saveCustomVariables, resetToOriginal) with modal state handling
Styling Updates
style.css
Replaced dark-theme guard from #darkTheme to #blackTheme; updated dark mode background color from #171615 to #0a0a0a; introduced comprehensive color picker modal styling (.colorModal, .modalContent, .colorInput); added animations (fadeIn, slideUp, invalid-shake); widened bookmark-sidebar from min(380px, 100vw) to min(420px, 100vw); updated color references and contrast adjustments for dark mode; enhanced hover states and transitions for new UI elements

Sequence Diagram

sequenceDiagram
    actor User
    participant Modal as Color Editor Modal
    participant ThemeManager as ThemeManager
    participant Storage as LocalStorage
    participant DOM as DOM/Favicon
    
    User->>Modal: Clicks color editor button
    Modal->>ThemeManager: openModal()
    ThemeManager->>Storage: Load modalOriginalValues
    ThemeManager->>Modal: setupModalInputSync(container)
    Modal->>DOM: Initialize color inputs with current values
    
    alt User makes color changes
        User->>Modal: Adjusts color inputs
        Modal->>ThemeManager: Live color updates
        ThemeManager->>DOM: updateFaviconColor()
        DOM-->>Modal: Preview changes
    end
    
    alt User saves changes
        User->>Modal: Clicks Save
        Modal->>ThemeManager: saveCustomVariables(variables)
        ThemeManager->>Storage: Store in CUSTOM_VARIABLES
        ThemeManager->>DOM: Apply changes to page
        ThemeManager->>Modal: closeModal()
    else User resets changes
        User->>Modal: Clicks Reset
        Modal->>ThemeManager: resetToOriginal()
        ThemeManager->>Storage: Restore from modalOriginalValues
        ThemeManager->>DOM: Revert all changes
        ThemeManager->>Modal: closeModal()
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • PR #112: Modifies theming system with segmented theme control replacing dropdown UI—overlaps with main PR's ThemeManager refactoring and theme mode management
  • PR #109: Reorganizes Appearance/Theming section in index.html—related to main PR's color editor modal and appearance section restructuring

Suggested labels

i18n/l10n

Suggested reviewers

  • itz-rj-here

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is incomplete and missing critical information required by the template. Add a detailed description of the changes and their purpose, fill in related issue numbers, complete the checklist items, and update CHANGELOG.md. Include concrete implementation details instead of template placeholders.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '(WIP)feat: Customizable Theme' is directly related to the main changes, which involve significant refactoring of the theme system and introduction of customizable color features throughout the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai

This comment was marked as outdated.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (4)
style.css (1)

225-281: Remove commented-out code if no longer needed.

This large block of commented CSS appears to be legacy dark mode logic that was replaced by the new ThemeManager approach. If it's no longer needed, it should be removed to maintain code cleanliness. If it's being kept temporarily for reference, consider adding a TODO comment explaining when it can be deleted.

scripts/theme.js (3)

302-306: Variable shadowing - min and max shadow built-in Math functions.

The destructuring assignment uses min and max which shadow Math.min and Math.max used earlier in the function. While not causing immediate bugs here, this can lead to confusion.

🔎 Proposed fix
         // Clamp lightness if specified (values are in percentage, convert to 0-1)
         if (lightClamp) {
-            const [min, max] = lightClamp;
-            l = Math.max(min / 100, Math.min(max / 100, l));
+            const [minL, maxL] = lightClamp;
+            l = Math.max(minL / 100, Math.min(maxL / 100, l));
         }

759-764: Redundant value restoration after save.

After saving, modalOriginalValues is updated to the new values, then closeModal() is called which restores those same values. While not broken, this is unnecessary work.

🔎 Proposed fix - skip restoration after save
         // Update original values to current values (so closing modal won't revert)
-        this.modalOriginalValues = { ...variables };
-
-        // Close modal
-        this.closeModal();
+        // Clear original values and close modal without reverting
+        this.modalOriginalValues = {};
+        const modal = document.getElementById('colorModal');
+        if (modal) modal.classList.remove('active');
     }

342-351: Add defensive check for isRTL variable

The isRTL variable is defined globally in menu-shortcut-page.js which loads before this script, but this dependency is implicit. For better robustness and code clarity, add a defensive check:

Suggested fix
     moveIndicator(mode) {
         const modeIndex = { light: 0, dark: 1, system: 2 };
         const ltrIndex = modeIndex[mode] || 0;
-        const index = isRTL ? 2 - ltrIndex : ltrIndex;
+        const index = (typeof isRTL !== 'undefined' && isRTL) ? 2 - ltrIndex : ltrIndex;

         const indicator = document.querySelector('.themeIndicator');
         if (indicator) {
             indicator.style.transform = `translateX(${index * 100}%)`;
         }
     }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7391279 and 4b2a133.

📒 Files selected for processing (9)
  • LICENSE
  • README.md
  • index.html
  • locales/en.js
  • manifest(firefox).json
  • manifest.json
  • scripts/languages.js
  • scripts/theme.js
  • style.css
💤 Files with no reviewable changes (2)
  • locales/en.js
  • scripts/languages.js
🧰 Additional context used
🪛 Biome (2.1.2)
style.css

[error] 4246-4246: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

width is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

🔇 Additional comments (15)
manifest.json (1)

4-4: Version bump to 3.3.4 looks good.

Straightforward version update aligned with the theming refactor and UI enhancements described in the PR.

manifest(firefox).json (1)

4-4: Version bump to 3.3.4 consistent across both manifests.

Mirrors the Chrome manifest update for consistency across all supported platforms.

LICENSE (1)

634-638: Verify authorship and copyright transition.

The LICENSE file updates the author from "XengShi" to "Prem" and shifts the copyright year range from 2023-2025 to 2024-2026. This represents a significant ownership/maintainership change. Please confirm this reflects the intended project governance and that the original author(s) have consented to this transition.

Also applies to: 658-658

README.md (1)

290-299: LGTM!

The privacy commitments clearly communicate the extension's data handling practices. The copyright year update is consistent with the LICENSE file changes.

style.css (4)

4237-4248: Static analysis false positive - intentional vendor prefixes.

The duplicate width property reported by Biome is intentional here. The code uses -webkit-fill-available for Chrome/Safari and -moz-available for Firefox as progressive enhancement. This is a standard cross-browser compatibility pattern.


3943-3966: LGTM!

The modal system is well-structured with proper layering, smooth animations, and accessibility considerations (ESC key handling is wired in the JS). The fadeIn/slideUp animations provide good UX.


350-351: Verify intentional hardcoding of color values.

The change from var(--whitishColor-blue) to white reduces theming flexibility. This appears intentional for ensuring contrast on dark backgrounds, but please verify this doesn't cause issues when users apply custom themes where the background might be light.


652-688: Verify bookmark button positioning calculation.

The sidebar width was increased to 420px but the rotated button position is set to 450px. This 30px difference may be intentional for spacing, but ensure this doesn't create visual gaps or overlap issues when the sidebar is open.

scripts/theme.js (4)

794-812: LGTM!

The throttle implementation is correctly implemented using an arrow function class property for proper this binding, with appropriate closure handling for timing variables.


7-37: Well-structured ThemeManager class.

The centralization of theme logic into a class with clear storage keys, color variable definitions, and system theme detection is a solid refactor. The separation of concerns between theme modes, color application, and modal handling improves maintainability.


814-815: The script is loaded with the defer attribute in index.html (line 26), which ensures the DOM is fully parsed before execution. The initialization at lines 814-815 is correct and the comment accurately reflects this behavior. No changes needed.

Likely an incorrect or invalid review comment.


585-587: No action needed. closeMenuBar() is properly defined at global scope in scripts/menu-shortcut-page.js and will be available when openModal() executes due to the load order in index.html (menu-shortcut-page.js loads before theme.js). The defensive check is unnecessary.

Likely an incorrect or invalid review comment.

index.html (3)

3-3: LGTM!

Copyright year update is appropriate for the new year.


1197-1203: LGTM!

The reusable SVG symbol pattern is well-implemented. Consolidating the adjustment icon into a shared symbol promotes maintainability and reduces code duplication.


1278-1280: LGTM!

The adjustment icon is correctly referenced using the <use> element, successfully replacing inline SVG duplication with a shared symbol reference.

Also applies to: 1341-1343

Comment on lines +509 to +514
} else if (customVars) {
const vars = JSON.parse(customVars);
const primaryColor = vars['--darkColor-blue'] || '#4382EC';
colorPickerLabel.style.borderColor = '';
colorEditorBtn.style.borderColor = primaryColor;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add error handling for JSON.parse.

Unlike loadStoredTheme() which wraps JSON.parse in try-catch, this method parses stored JSON without protection. If the stored data is corrupted, this will throw an unhandled exception.

🔎 Proposed fix
         } else if (customVars) {
-            const vars = JSON.parse(customVars);
-            const primaryColor = vars['--darkColor-blue'] || '#4382EC';
-            colorPickerLabel.style.borderColor = '';
-            colorEditorBtn.style.borderColor = primaryColor;
+            try {
+                const vars = JSON.parse(customVars);
+                const primaryColor = vars['--darkColor-blue'] || '#4382EC';
+                colorPickerLabel.style.borderColor = '';
+                colorEditorBtn.style.borderColor = primaryColor;
+            } catch (e) {
+                console.error('Failed to parse custom variables:', e);
+            }
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (customVars) {
const vars = JSON.parse(customVars);
const primaryColor = vars['--darkColor-blue'] || '#4382EC';
colorPickerLabel.style.borderColor = '';
colorEditorBtn.style.borderColor = primaryColor;
}
} else if (customVars) {
try {
const vars = JSON.parse(customVars);
const primaryColor = vars['--darkColor-blue'] || '#4382EC';
colorPickerLabel.style.borderColor = '';
colorEditorBtn.style.borderColor = primaryColor;
} catch (e) {
console.error('Failed to parse custom variables:', e);
}
}
🤖 Prompt for AI Agents
In scripts/theme.js around lines 509 to 514, the code calls
JSON.parse(customVars) without protection which will throw on malformed stored
data; wrap JSON.parse in a try-catch, on failure fall back to an empty object or
defaults (so primaryColor uses '#4382EC'), optionally remove or reset the
corrupted storage entry and log the error to console for debugging, and then
continue without applying broken values so the UI remains stable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request refactor Code improvement without changing behavior ui/ux work-in-progress Under active development or being worked on by the assignee. Not ready to close or merge yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant