Hone logo
Hone
Problems

Angular Theme Service

This challenge focuses on building a robust theme management service within an Angular application. A well-designed theme service allows for dynamic switching of application styles, enhancing user experience and providing a consistent visual identity across different contexts.

Problem Description

Your task is to create an Angular service that manages the application's theme. This service should allow components to:

  • Apply a theme: Set a specific theme (e.g., "light", "dark", "high-contrast").
  • Observe the current theme: Subscribe to changes in the active theme.
  • Store and retrieve the theme: Persist the user's chosen theme (e.g., using localStorage) so it's remembered across sessions.

The service should be designed to be easily integrated into any Angular component or other services.

Key Requirements:

  1. Theme Definition: Define a set of themes, each represented by a unique identifier (e.g., string) and associated styling properties (e.g., CSS class names, color variables).
  2. Theme Application: Implement a mechanism to apply the selected theme to the application's root element or a designated container. This typically involves adding/removing CSS classes.
  3. Theme State Management: Maintain the current active theme.
  4. Theme Change Notification: Provide an observable that emits the new theme whenever the theme is changed.
  5. Persistence: Save the selected theme to localStorage and load it when the application initializes.
  6. Service Structure: The solution should be encapsulated within an Angular service.

Expected Behavior:

  • When the application loads, the service should attempt to load a previously saved theme from localStorage. If no theme is found, it should default to a predefined theme.
  • Components can subscribe to the theme changes and update their UI accordingly.
  • When a new theme is applied, the old theme's styling should be removed, and the new theme's styling should be applied.
  • The selected theme should be persistent across browser refreshes.

Edge Cases:

  • What happens if localStorage is unavailable or disabled?
  • How to handle invalid theme identifiers being requested?
  • Ensure theme changes are efficient and don't cause excessive DOM manipulation.

Examples

Example 1: Basic Theme Switching

Input: (Conceptual, not direct code input)

  1. Application starts. localStorage is empty.
  2. themeService.applyTheme('dark') is called.
  3. A component subscribes to themeService.themeChanges$.

Output:

  • The service defaults to a "light" theme on initialization.
  • After applyTheme('dark'), the themeService.themeChanges$ observable emits 'dark'.
  • The application's root element (or designated container) now has a CSS class like theme-dark applied.
  • The string 'dark' is saved in localStorage.

Explanation: The service correctly applies the requested theme, notifies subscribers, and persists the choice.

Example 2: Persistence on Reload

Input:

  1. User applies the "high-contrast" theme, which is saved to localStorage.
  2. The browser is refreshed.

Output:

  • On application initialization, the themeService reads "high-contrast" from localStorage.
  • The themeService.themeChanges$ observable emits 'high-contrast' immediately upon initialization.
  • The application's root element has the theme-high-contrast CSS class applied.

Explanation: The theme persists across sessions due to localStorage integration.

Example 3: Handling Invalid Theme

Input:

  1. themeService.applyTheme('nonExistentTheme') is called.

Output:

  • The service should ideally ignore the request or log a warning.
  • The current theme should remain unchanged.
  • The themeService.themeChanges$ observable should not emit.

Explanation: The service gracefully handles requests for unknown themes.

Constraints

  • The theme service must be implemented in TypeScript.
  • The solution should utilize Angular's Dependency Injection system.
  • Styling should be applied by adding/removing CSS classes to a target element (e.g., document.body or a specific host element).
  • localStorage should be used for persistence. If localStorage is not available, the service should degrade gracefully (e.g., by not persisting).
  • The theme change notification mechanism should use RxJS BehaviorSubject or a similar observable pattern.
  • Consider that themes might have multiple associated CSS variables or classes.

Notes

  • Think about how you'll structure your theme definitions. An enum or an object mapping theme names to configuration objects could be useful.
  • The persistence logic should run during the service's initialization.
  • Consider creating a directive or component that can easily subscribe to theme changes and apply dynamic styles based on the emitted theme.
  • The "styling properties" associated with a theme can be as simple as a single CSS class name or more complex, involving multiple classes or CSS variables. For this challenge, focus on applying a single primary theme class.
  • The goal is a clean, reusable, and maintainable theme service.
Loading editor...
typescript