feat(theme): introduce a mechanism to toggle light/dark modes#230
feat(theme): introduce a mechanism to toggle light/dark modes#230desmondinho merged 5 commits intodevfrom
Conversation
WalkthroughThis update introduces a comprehensive theming system with support for light, dark, and system themes. It adds a Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ThemeToggle (Razor)
participant ThemeService (.NET)
participant JSThemeModule (JS)
participant Browser
User->>ThemeToggle: Clicks toggle button
ThemeToggle->>ThemeService: SetThemeAsync(theme) / ToggleThemeAsync()
ThemeService->>JSThemeModule: theme.set(theme) / theme.toggle()
JSThemeModule->>Browser: Apply theme class to <html>
JSThemeModule-->>ThemeService: Return new theme
ThemeService-->>ThemeToggle: Update IsDarkMode
ThemeToggle-->>User: UI updates to reflect new theme
Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Suggested labels
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (9)
docs/LumexUI.Docs.Client/Components/NavItemBadge.razor (1)
19-19: Consider using consistent sizing units.The font size was changed from
"text-[0.5rem]"to"text-[9px]". While both produce similar results (0.5rem ≈ 8px at default font size), mixing rem and pixel units can affect consistency across the design system.Consider keeping rem-based sizing for better scalability:
- .Add( "text-[9px]" ) + .Add( "text-[0.5rem]" )docs/LumexUI.Docs.Client/Samples/Theming/Dark Mode/darkModeToggle.html (1)
29-29: Consider using dynamic ID generation for reusability.The fixed dropdown ID
"theme-toggle"could cause conflicts if multiple theme toggle components are used on the same page.Consider generating a unique ID:
- private string _dropdownId = "theme-toggle"; + private string _dropdownId = $"theme-toggle-{Guid.NewGuid():N}";Or use a more lightweight approach:
- private string _dropdownId = "theme-toggle"; + private string _dropdownId = $"theme-toggle-{GetHashCode()}";docs/LumexUI.Docs.Client/Pages/Theming/Dark Mode/ThemeToggle.razor (2)
4-10: Consider adding accessibility attributes.The button implementation is solid with proper icon switching logic. Consider adding an
aria-labelattribute for better accessibility:<LumexButton Variant="@Variant.Outlined" Class="min-w-10 w-10 h-10 p-0" + AriaLabel="Toggle theme" OnClick="@TriggerAsync" data-popoverref="@_dropdownId">
12-26: Consider adding error handling for async theme operations.The dropdown structure and theme options are well-implemented. Consider adding try-catch blocks around the async theme operations to handle potential JavaScript interop failures gracefully:
- <LumexDropdownItem OnClick="@(async () => await ThemeService.SetThemeAsync( Theme.Light ))"> + <LumexDropdownItem OnClick="@(async () => { try { await ThemeService.SetThemeAsync( Theme.Light ); } catch { /* Log or handle error */ } })">Apply similar error handling to the other menu items.
src/LumexUI/wwwroot/js/utils/theme.js (1)
7-21: Consider adding error handling for localStorage access.The initialize function is well-designed with automatic system theme change detection. Consider adding try-catch blocks around localStorage operations to handle cases where storage is disabled or in private browsing mode:
function initialize() { - const initialTheme = get(); + let initialTheme; + try { + initialTheme = get(); + } catch { + initialTheme = "system"; + } applyTheme(initialTheme); // ... rest of function }docs/LumexUI.Docs.Client/Pages/Theming/Dark Mode/DarkMode.razor (1)
50-57: Fix heading structure to match actual sections.The headings array includes a "Setup" heading, but the actual sections are "Add the theme provider" and "Add a mode toggle". Update the headings to match the actual section structure:
new("Mode toggling", [ - new("Setup"), + new("Add the theme provider"), new("Add a mode toggle"), ]),src/LumexUI/Services/Theme/ThemeService.cs (1)
42-50: Consider adding error handling for JavaScript interop.The initialization logic is sound with proper state management. Consider adding try-catch blocks around JavaScript interop calls to handle potential module loading failures or JavaScript errors gracefully:
public async ValueTask InitializeAsync() { - var module = await _moduleTask.Value; - var theme = await module.InvokeAsync<string>( $"{JavaScriptPrefix}.initialize" ); + try + { + var module = await _moduleTask.Value; + var theme = await module.InvokeAsync<string>( $"{JavaScriptPrefix}.initialize" ); + + _currentTheme = TryParseTheme( theme ); + await UpdateIsDarkModeAsync( _currentTheme ); + } + catch + { + // Fallback to system theme if JavaScript fails + _currentTheme = Theme.System; + await UpdateIsDarkModeAsync( _currentTheme ); + } - - _currentTheme = TryParseTheme( theme ); - await UpdateIsDarkModeAsync( _currentTheme ); }tests/LumexUI.Tests/Services/ThemeServiceTests.cs (2)
68-88: Fix the misleading test method name.The test name suggests it should return Light theme, but the test actually mocks the JS to return "dark" and asserts for
Theme.Dark. The test logic is correct, but the name is misleading.Apply this diff to fix the test name:
- public async Task GetThemeAsync_ShouldReturnLight_AndSetIsDarkModeTrue() + public async Task GetThemeAsync_ShouldReturnDark_AndSetIsDarkModeTrue()
210-214: Add null check for better error handling.The helper method should validate that the reflection field lookup succeeds to provide clearer error messages if the internal structure changes.
Apply this diff to improve error handling:
private static void InjectModule( Task<IJSObjectReference> moduleTask, ThemeService service ) { var field = typeof( ThemeService ).GetField( "_moduleTask", BindingFlags.NonPublic | BindingFlags.Instance ); + if( field == null ) + throw new InvalidOperationException( "Unable to find '_moduleTask' field in ThemeService. The internal structure may have changed." ); field?.SetValue( service, new Lazy<Task<IJSObjectReference>>( () => moduleTask ) ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
docs/LumexUI.Docs.Client/Common/Enums/PageStatus.cs(1 hunks)docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs(2 hunks)docs/LumexUI.Docs.Client/Common/Navigation/Types.cs(1 hunks)docs/LumexUI.Docs.Client/Components/Layouts/MainLayout.razor(0 hunks)docs/LumexUI.Docs.Client/Components/NavItemBadge.razor(1 hunks)docs/LumexUI.Docs.Client/Components/NavMenu.razor(1 hunks)docs/LumexUI.Docs.Client/Pages/Theming/Customization/Customization.razor(1 hunks)docs/LumexUI.Docs.Client/Pages/Theming/Dark Mode/DarkMode.razor(1 hunks)docs/LumexUI.Docs.Client/Pages/Theming/Dark Mode/ThemeToggle.razor(1 hunks)docs/LumexUI.Docs.Client/Pages/Theming/Dark Mode/ThemeTogglePreview.razor(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Dark Mode/darkModeProvider.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Dark Mode/darkModeRoot.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Dark Mode/darkModeToggle.html(1 hunks)docs/LumexUI.Docs.Client/_Imports.razor(1 hunks)docs/LumexUI.Docs/Components/App.razor(1 hunks)docs/LumexUI.Docs/Components/Routes.razor(1 hunks)src/LumexUI/Common/Enums/Theme.cs(1 hunks)src/LumexUI/Components/Providers/LumexThemeProvider.cs(1 hunks)src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs(0 hunks)src/LumexUI/Extensions/ServiceCollectionExtensions.cs(3 hunks)src/LumexUI/Services/Theme/ThemeService.cs(1 hunks)src/LumexUI/wwwroot/js/components/popover.bundle.js(3 hunks)src/LumexUI/wwwroot/js/utils/theme.js(1 hunks)tests/LumexUI.Tests/Services/ThemeServiceTests.cs(1 hunks)
💤 Files with no reviewable changes (2)
- docs/LumexUI.Docs.Client/Components/Layouts/MainLayout.razor
- src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs
🧰 Additional context used
🧠 Learnings (4)
docs/LumexUI.Docs.Client/_Imports.razor (1)
Learnt from: desmondinho
PR: #159
File: src/LumexUI.Motion/LumexUI.Motion.csproj:9-11
Timestamp: 2025-01-30T00:22:33.596Z
Learning: LumexUI.Motion is planned to be distributed as a separate NuGet package with its own test coverage, hence the assembly-wide coverage exclusion in the project.
src/LumexUI/Components/Providers/LumexThemeProvider.cs (1)
Learnt from: desmondinho
PR: #159
File: src/LumexUI.Motion/LumexUI.Motion.csproj:9-11
Timestamp: 2025-01-30T00:22:33.596Z
Learning: LumexUI.Motion is planned to be distributed as a separate NuGet package with its own test coverage, hence the assembly-wide coverage exclusion in the project.
src/LumexUI/Extensions/ServiceCollectionExtensions.cs (1)
Learnt from: desmondinho
PR: #159
File: src/LumexUI.Motion/LumexUI.Motion.csproj:9-11
Timestamp: 2025-01-30T00:22:33.596Z
Learning: LumexUI.Motion is planned to be distributed as a separate NuGet package with its own test coverage, hence the assembly-wide coverage exclusion in the project.
tests/LumexUI.Tests/Services/ThemeServiceTests.cs (1)
Learnt from: desmondinho
PR: #159
File: src/LumexUI.Motion/LumexUI.Motion.csproj:9-11
Timestamp: 2025-01-30T00:22:33.596Z
Learning: LumexUI.Motion is planned to be distributed as a separate NuGet package with its own test coverage, hence the assembly-wide coverage exclusion in the project.
🧬 Code Graph Analysis (2)
src/LumexUI/wwwroot/js/components/popover.bundle.js (2)
src/LumexUI/wwwroot/js/utils/dom.js (2)
createOutsideClickHandler(43-59)clickHandler(44-52)src/LumexUI/wwwroot/js/components/popover.js (9)
destroyOutsideClickHandler(20-20)ref(25-25)popover(24-24)popover(109-112)popover(109-112)offsetVal(31-36)middlewares(38-42)data(60-63)arrowElement(26-26)
src/LumexUI/Services/Theme/ThemeService.cs (2)
src/LumexUI/Components/Providers/LumexThemeProvider.cs (1)
Task(22-28)src/LumexUI/Extensions/EnumExtensions.cs (1)
ToDescription(11-19)
🔇 Additional comments (45)
docs/LumexUI.Docs/Components/Routes.razor (1)
1-1: LGTM! Good architectural decision to centralize theme provider at routing level.The placement of
LumexThemeProviderbefore theRoutercomponent with@InteractiveWebAssemblyrendermode ensures theme management is available throughout the application. This centralized approach is cleaner than having it in individual layouts.docs/LumexUI.Docs.Client/Samples/Theming/Dark Mode/darkModeProvider.html (1)
1-2: LGTM! Clean documentation sample for theme provider usage.The HTML-encoded Razor markup provides a clear, minimal example of how to instantiate the
LumexThemeProvidercomponent. The syntax highlighting setup withlanguage-razorclass is appropriate for documentation.docs/LumexUI.Docs.Client/Components/NavMenu.razor (1)
53-53: Enum rename consistency verified
- No occurrences of
ComponentStatusremain in the codebase.PageStatusis applied uniformly (e.g., inPageStatus.cs,NavigationItem, andNavigationStore).All enum references have been updated correctly—no further changes needed.
docs/LumexUI.Docs.Client/Pages/Theming/Customization/Customization.razor (1)
41-41: LGTM! Improved separation of concerns with component change.Replacing
<ThemeTogglePreview />with<CustomThemesPreview />makes semantic sense - the custom themes section should demonstrate custom theme previews rather than the toggle mechanism. This aligns with the broader theming system reorganization where toggle-specific previews are likely moved to the dedicated dark mode documentation.docs/LumexUI.Docs.Client/_Imports.razor (1)
19-19: LGTM! Necessary import for theming infrastructure.Adding
@using LumexUI.Servicesenables components in the docs client project to access the newThemeServiceand other services without full namespace qualification. The placement among other LumexUI imports follows the existing organizational pattern.docs/LumexUI.Docs.Client/Common/Enums/PageStatus.cs (1)
3-12: LGTM! Clean enum refactoring with good practices.The renaming from
ComponentStatustoPageStatusimproves semantic clarity, and the addition of theUpdatedmember extends functionality appropriately. The trailing comma afterPreviewfollows good formatting practices for maintainability.src/LumexUI/Common/Enums/Theme.cs (1)
12-31: Excellent enum design with comprehensive documentation.The
Themeenum is well-structured with:
- Clear XML documentation for each member
- Appropriate
Descriptionattributes using lowercase values (consistent with CSS/JavaScript conventions)- Logical theme options covering user preferences (Light/Dark) and system detection
This provides a solid foundation for the theming system.
docs/LumexUI.Docs.Client/Common/Navigation/Types.cs (1)
31-35: Consistent type updates following enum refactoring.The parameter and property type changes from
ComponentStatus?toPageStatus?correctly align with the enum rename, maintaining the same nullable semantics and ensuring type consistency across the navigation system.src/LumexUI/Components/Providers/LumexThemeProvider.cs (1)
17-29: Well-implemented Blazor component following best practices.The
LumexThemeProvidercorrectly:
- Extends
ComponentBasefor Blazor component functionality- Uses dependency injection with the
[Inject]attribute anddefault!pattern- Implements
OnAfterRenderAsyncwithfirstRendercheck to prevent unnecessary re-initialization- Includes appropriate code coverage exclusion and XML documentation
The lifecycle management ensures theme initialization occurs only once after the component renders.
docs/LumexUI.Docs.Client/Samples/Theming/Dark Mode/darkModeRoot.html (1)
1-12: Clear and effective documentation sample.This HTML sample effectively demonstrates the simplest approach to enable dark mode by adding
class="dark"to the root<html>element. The code is properly structured as a documentation sample with:
- Standard HTML5 doctype and meta tags
- Correct module script loading for LumexUI.js
- Clean, minimal example that's easy to understand and follow
This provides users with a straightforward implementation pattern for global dark mode.
docs/LumexUI.Docs.Client/Components/NavItemBadge.razor (2)
11-11: LGTM! Parameter type updated correctly.The parameter type change from
ComponentStatus?toPageStatus?aligns with the enum refactoring mentioned in the AI summary.
25-31: Dictionary mapping updated correctly with improved color contrast.The dictionary correctly uses
PageStatusenum keys, and the color change forPreviewstatus fromtext-indigo-500totext-indigo-600provides better contrast. The addition ofPageStatus.Updatedstatus is well-integrated.docs/LumexUI.Docs.Client/Pages/Theming/Dark Mode/ThemeTogglePreview.razor (1)
1-6: Well-structured preview component.The component correctly uses
InteractiveWebAssemblyrender mode for the theme toggle functionality and properly integrates with thePreviewCodecomponent. The center justification enhances the visual presentation.docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs (2)
18-20: Navigation items updated correctly for theming section.The theming category properly reflects the new dark mode feature with appropriate status indicators. The "Dark Mode" item correctly uses
PageStatus.Newand "Customization" usesPageStatus.Updated.
25-50: Components section consistently updated with new enum.All component navigation items have been systematically updated from
ComponentStatustoPageStatus. The status assignments appear appropriate for the various components.docs/LumexUI.Docs.Client/Samples/Theming/Dark Mode/darkModeToggle.html (2)
1-10: Component services and button implementation look correct.The service injections are appropriate, and the button implementation with conditional icons based on dark mode CSS classes is well-designed. The
data-popoverrefattribute correctly links to the dropdown.
16-24: Theme setting implementation is correct.The dropdown menu items properly use async calls to
ThemeService.SetThemeAsync()with the appropriateThemeenum values. The implementation covers all three theme options (Light, Dark, System).src/LumexUI/Extensions/ServiceCollectionExtensions.cs (2)
32-32: LGTM! Consistent service registration pattern.The
ThemeServiceregistration follows the established pattern and is correctly added to bothAddLumexServicesoverloads, ensuring the service is available regardless of configuration approach.Also applies to: 45-45
78-81: LGTM! Proper service registration implementation.The
AddThemeServicemethod correctly registersThemeServiceas a scoped service, following the same pattern as other service registrations in this file. The scoped lifetime is appropriate for theme state management.docs/LumexUI.Docs.Client/Pages/Theming/Dark Mode/ThemeToggle.razor (2)
1-2: LGTM! Appropriate service injections.The component correctly injects the required services for theme management and popover functionality.
28-32: LGTM! Clean and focused implementation.The code section is well-structured with a clear separation of concerns. The hardcoded ID is appropriate for a singleton theme toggle component.
src/LumexUI/wwwroot/js/utils/theme.js (6)
23-25: LGTM! Simple and effective with appropriate fallback.The get function correctly retrieves the stored theme with a sensible default. The same localStorage error handling mentioned above would apply here as well.
27-30: LGTM! Clean implementation following persist-then-apply pattern.The set function correctly stores and immediately applies the theme. The localStorage error handling suggestion from the initialize function would also apply here.
32-42: LGTM! Excellent toggle logic handling system theme intelligently.The toggle function correctly handles all three theme states with intuitive behavior for the "system" theme - checking actual system preference and toggling to the opposite. The return value is useful for state synchronization.
44-46: LGTM! Standard implementation for system theme detection.Correctly uses the standard
matchMediaAPI to detect system dark mode preference.
48-59: LGTM! Comprehensive theme application with best practices.The applyTheme function is well-structured, properly handling theme resolution and applying both CSS classes and the
color-schemeproperty for optimal browser integration. The clean-slate approach prevents class conflicts.
61-67: LGTM! Clean module export with all necessary functions.The export structure is well-organized and exposes all required theme management functions.
docs/LumexUI.Docs.Client/Pages/Theming/Dark Mode/DarkMode.razor (2)
1-12: LGTM! Clear documentation of global dark mode setup.The route, layout, and explanation of global dark mode are appropriate and provide clear guidance for users implementing dark mode.
41-44: LGTM! Practical demonstration with interactive preview.The mode toggle section effectively combines explanation with an interactive preview component, providing users with both guidance and a working example.
src/LumexUI/Services/Theme/ThemeService.cs (5)
15-36: LGTM! Well-designed service class with proper resource management.The class structure follows best practices with lazy JavaScript module loading, sealed modifier for a service class, and proper state tracking. The
IAsyncDisposableimplementation ensures proper resource cleanup.
56-65: LGTM! Consistent state management pattern.The method follows the same reliable pattern as
InitializeAsyncwith proper state synchronization. The same error handling suggestion applies here for JavaScript interop robustness.
72-84: LGTM! Efficient implementation with early return optimization.Good use of early return to avoid unnecessary operations when the theme hasn't changed. The
ToDescription()extension method correctly converts the enum for JavaScript interop. The same error handling suggestion applies here to ensure state consistency if JavaScript calls fail.
102-124: LGTM! Well-designed helper methods providing consistent state management.The
UpdateIsDarkModeAsyncmethod correctly handles all theme cases including system preference detection, andTryParseThemeprovides safe parsing with appropriate fallback. These methods ensure consistent behavior across the service.
127-134: LGTM! Proper async disposal implementation.The disposal logic correctly checks if the module was created before disposing and follows the async disposal pattern properly. The explicit interface implementation is appropriate for a service class.
tests/LumexUI.Tests/Services/ThemeServiceTests.cs (7)
17-40: LGTM! Well-structured initialization test.The test correctly mocks both JS calls and verifies the expected behavior when JavaScript returns dark theme.
42-66: LGTM! Comprehensive GetThemeAsync test.The test correctly verifies both the theme retrieval and the IsDarkMode state update for system theme preference.
90-110: LGTM! Correct light theme test.The test properly verifies the light theme retrieval and corresponding IsDarkMode state.
112-135: LGTM! Excellent SetThemeAsync test with parameter validation.The test properly validates that the correct theme value is passed to JavaScript and verifies the IsDarkMode state update.
137-162: LGTM! Important optimization test.This test correctly verifies that redundant theme setting calls are optimized to avoid unnecessary JavaScript interop calls, which is crucial for performance.
164-183: LGTM! Clean toggle theme test.The test correctly verifies the theme toggle functionality and the corresponding IsDarkMode state update.
185-208: LGTM! Proper disposal test.The test correctly verifies that the ThemeService properly disposes of its JavaScript module, ensuring proper resource cleanup.
src/LumexUI/wwwroot/js/components/popover.bundle.js (4)
1483-1490: LGTM! Enhanced outside click detection for multiple elements.The updated function now properly handles multiple elements (reference and popover), which correctly prevents the popover from closing when clicking on either element.
1515-1549: LGTM! Improved initialization logic and middleware ordering.The changes correctly:
- Pass both reference and popover elements to the outside click handler
- Move the offset middleware earlier in the array for proper positioning sequence
- Clean up variable destructuring for better readability
The middleware reordering (offset before flip/shift) ensures proper positioning calculations.
1554-1585: LGTM! Cleaner arrow positioning with improved parameter naming.The changes improve the code by:
- Simplifying the
positionArrowfunction signature to accept only necessary parameters- Renaming
targettopopoverfor better clarity- Explicitly clearing conflicting
rightandbottomstyles to prevent positioning issuesThe internal derivation of placement and static side makes the function more self-contained.
1557-1557: LGTM! More specific error message.The error message now correctly identifies the context as "popover.initialize" instead of a generic message, which improves debugging experience.
* feat: support Tailwind CSS v4 (#183) * build(deps): bump TailwindMerge.NET from 0.3.0 to 1.0.0 * feat: add new CSS theme file * feat/build: add custom targets file to improve library usability * chore: move `Plugin` folder one level higher; remove `Scripts` folder * feat/build: pack new theme and custom `.targets` files * build(docs): add `Directory.Build.props` and `Directory.Build.targets` * chore(docs): remove tailwind npm deps; use standalone CLI instead * docs: apply `static` on theme * refactor(theme-provider): simplify names of box-shadow css variables * refactor(theme-provider): opacities are percentage to match `color-mix` function syntax * chore(components): apply `static` on theme; fix some vars * refactor(components): drop Tailwind CSS v3 support * feat(theme): add custom transition variables * fix(theme): correct `default` color; add `default-foreground` color * chore(theme): add reference for the custom transitions approach (it's not in docs afaik) * fix: rename `shadow-sm` to `shadow-xs` * fix: rename `rounded-sm` to `rounded-xs` * fix: rename `rounded` to `rounded-sm` * fix: rename `outline-none` to `outline-hidden` * fix: rename `ring-1` to `ring` * fix(button): add base `cursor-pointer` class * docs: remove `children` custom variant in favor of `*` * fix(theme): correct `enter` custom animation * chore(components): cleanup styles * fix(theme): add missing comma separator in custom transition vars * docs: configure typography * refactor(theme): simplify `scrollbar-hide` utility * chore(theme): apply `inline` on theme * refactor: replace `theme` function with CSS vars * fix(components): correct scale/translate transitions * feat(theme): update colors from hex to oklch * docs(installation): update installation guide * feat(theme): add leading CSS vars * chore(docs): fix prose `<code>` tag ticks * refactor(utils): remove hex luminance calculator * docs(customization): update Theme and Colors pages * docs(colors): remove 'common colors are not configurable' callout * fix(checkbox): correct radius styles * fix(data-grid): correct striped styles * fix(input/select): correct label placement out transitions * fix(input/select): correct outlined variant focus styles * fix(input): add cursor-pointer style on the clear button * fix(input/select): correct flat variant focus styles * fix(docs): correct some component examples * build(docs): adjust Tailwind standalone CLI file download for Linux * build(docs): adjust Tailwind standalone CLI file download for Linux * ci(deploy): try add staging env in the ci/cd * ci(build-test): change trigger branch * ci(deploy): update trigger branches * ci(deploy): change env vars usage (test) * ci: add deploy-dev.yml; revert deploy.yml * ci(deploy): test staging * chore(docs): nits * chore(components): tweak styles of some components * chore(docs): tweak some components examples * chore: coderabbit comments * ci: remove deploy-dev.yml * fix(theme): remove extra shade (950) from the color scales for consistency in dark mode (#199) * fix(theme): remove extra key (950) from the color scales for consistency in dark mode * build(docs): explicitly set Tailwind v4.0.9 * feat(components): introduce Avatar and AvatarGroup components (#201) * feat: add baseline implementation * feat: add slots * feat: add basic slots styles * feat: add appearance params, such as `Color`, `Radius`, `Size` * feat: add `Bordered` and `Disabled` params * feat: add compound variants styles * feat: apply slots styles * docs: add baseline examples page * feat: add `data-loaded` attribute on img * feat: add `ShowFallback` parameter * chore: fix compound style variants * chore: set `showFallback` on after first render * feat(utils): add implicit cast to string for the `ElementClass` * feat: add LumexAvatarGroup component * feat: take into account when LumexAvatar is rendered inside the LumexAvatarGroup * feat: add `AvatarClasses` parameter in the avatar group component * docs: add Avatar page * build(docs): explicitly set Tailwind v4.0.9 * test: add tests for LumexAvatar and LumexAvatarGroup components * chore: simplify condition for fallback render * fix(docs): replace usages of `-foreground-950` CSS classes with `-foreground-900` * fix(docs): remove `dark:prose-invert` CSS class until dark theme is properly configured * feat(components): introduce Skeleton component (#202) * feat(skeleton): add baseline implementation of the component * feat(skeleton): add slots and styles * feat(skeleton): add XML summaries * fix(skeleton): return back `after` pseudo CSS classes to prevent flickering on state change * docs(skeleton): add Skeleton page * test(skeleton): add tests * docs(skeleton): fix Loading example button text * fix(navbar): add a check before toggling navbar menu on navigation (#204) * feat(components): introduce Spinner component (#207) * feat(spinner): add baseline implementation * feat(spinner): add variants and styles * feat(spinner): add slots * docs(spinner): add Spinner page * docs: nits * test(spinner): add tests * docs: map static assets * build(docs): remove extra MSBuild target for the Tailwind prod build * docs: revert static assets changes * docs: update static assets usage * Revert "docs: update static assets usage" This reverts commit 94ae9ec. * feat(components): introduce Chip component (#211) * feat(chip): add baseline implementation * feat(chip): add ChipVariant enum * feat(chip): add appearance parameters and styles * feat(chip): add AvatarContent parameter * feat(chip): adjust paddings when chip has start/end content * docs(chip): add Chip page * feat(chip): add XML summaries * test(chip): add tests * chore(components): add missing XML documentation summaries * feat(components): add new Badge component (#222) * feat(badge): initial * feat(badge): add badge slots * feat(badge): add badge baseline implementation * feat(badge): add base visual-related params * feat(badge): add majority of badge styles * feat(badge): add outline around badge * feat(badge): add `Invisible` param to control badge visibility * feat(badge): add `IsOneChar` param to make badge equilateral * feat(badge): decrease badge dimensions if no content provided * refactor(badge): rename `IsOneChar` param to `OneChar` * fix(badge): use correct type for `Variant` param * feat(badge): apply CSS classes directly to the badge slot * fix(badge): ensure correct placement styles * fix(badge): correct `Content` check condition * fix(badge): allow null for content * fix(badge): properly render Content as RenderFragment * docs(badge): add Badge docs page * test(badge): add tests * test(badge): add more tests * fix(badge): ensure `OneChar` param is taken into account * fix(badge): fix one char switch * chore: apply CodeRabbit suggestions * build(deps): bump requests from 2.32.0 to 2.32.4 in /scripts (#219) Bumps [requests](https://github.com/psf/requests) from 2.32.0 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](psf/requests@v2.32.0...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(components): add Tooltip component (#224) * feat(tooltip): initial * refactor(popover): introduce PopoverWrapper to simplify open state * fix(popover): ensure arrow is positioned correctly * fix(popover): ensure popover closes on trigger click if already opened * fix(popover): position flicker * test(popover/dropdown): adjust tests * refactor(popover): remove LastShown meta from popover service * feat(tooltip): add baseline implementation * feat(tooltip): add common visual-related parameters to pass into popover * feat(tooltip): add OpenDelay and CloseDelay params * docs(tooltip): add Tooltip page * feat(tooltip): pass slots to popover * chore(popover): add full radius styles * test(tooltip): add tests * docs(tooltip): minor tweaks * docs(tooltip): typo * feat(components): add Alert component (#225) * feat(alert): add baseline implementation * feat(alert): add styles (may be not complete) * feat(alert): complete styles * feat(button): add full radius styles * feat(alert): apply styles * feat(alert): add close button handler * chore(alert): complete XML docs summaries * chore(docs): adjust background colors for preview and toolbar * chore(components): darken text color for warning flat variant * chore(alert): styling tweaks * feat(docs): add Alert page * chore(alert): add missing close-button slot attribute * chore(alert): ensure TitleContent takes precedence over Title * docs(alert): add callout regarding Title and TitleContent parameters usage * test(alert): add tests * fix(theme): add tw custom CSS class-based dark mode variant (#226) * refactor(theme): replace C# theme config with a new CSS-first approach (#229) * feat(theme): add light and dark theme CSS files * refactor(components): cleanup theme provider * refactor(theme): remove Theme directory * test(theme): remove Theme directory * docs(theme): remove theme config * fix(alert): correct RenderFragment parameters usage * chore(theme): correct theme variables * docs(customization): replace Customization section with Theming * test(theme-provider): remove all tests * feat(theme): introduce a mechanism to toggle light/dark modes (#230) * feat(theme): introduce Theme service to manage and persist theme settings (JS) * feat(theme): introduce Theme service to manage and persist theme settings * docs(*): rename ComponentStatus enum to PageStatus * docs(theming): add Dark Mode page * chore(docs): component rename * feat(button): add new `IconOnly` parameter * docs(button): add demo for `IconOnly` parameter * refactor(*): remove all bundled Google Material Icons and related code (#232) * build: add new shared icons project * feat(icons): add base icon component * feat(icons): add dynamic icon component * feat(icons): add some icons * build(deps): reference icons in components * refactor(accordion): use new icon components * docs(components): use new icons in Callout components * docs(*): use new icons * refactor(icons): add "Icon" suffix; add more icons * docs(*): use new icons * test(*): fix tests * refactor(components): remove `LumexIcon` component * docs(datagrid): formatting * refactor(icons): remove all Google Material Icons * refactor(icons): remove script for downloading/updating icons * feat(components): add dark mode support (#234) * feat(alert): add dark mode support * feat(button): add dark mode support * feat(chip): add dark mode support * feat(datagrid): add dark mode support * feat(textbox/numbox): add dark mode support * feat(listbox): add dark mode support * feat(menu): add dark mode support * feat(select): add dark mode support * feat(tabs): add dark mode support * fix(theme): ensure default values are of correct shade in dark mode * feat(theme): add `color-scheme` in light theme * fix(icons): use better icons for alert component * docs: add dark mode support + theme toggle (#235) * docs: add dark mode support * docs: add missing border for the preview component * docs: remove extra border in the preview code component * chore(badge): improve flat variant contrast in light theme * chore(tabs): remove `EditorRequired` attribute from `Id` param * docs: add null check and theme class cleanup to prevent issues * docs: remove IPopoverService injection from theme toggle component * chore(components): remove unused / redundant types * fix(data-grid): correct outside click handler creation * feat(popover): enable position autoUpdate * refactor(popover): make use of popover trigger component instead of service * feat(dropdown): introduce dropdown trigger component instead of relying on popover service * docs(dark-mode): update theme toggle dropdown example * docs: add Home page with library usage examples (#241) * feat(icons): add more icons * docs: add showcases on home page * chore(components): adjust some styles * docs(overview): update paths * chore(docs): nits * fix(components): add missing popover js parts * chore(docs): nits * fix(popover): use overlay to close instead of custom outside click event * fix(tooltip): remove pressed effect from on trigger hover * chore(showcases): complete column visibility toggling in UserTable example * chore(showcases): adjust legend color in usage chart * test(popover): update tests * chore(docs): coderabbit suggestions * chore(*): migrate to DigitalOcean app platform * fix(*): update custom LumexUI targets to copy theme files before build * chore(*): add missing css files in the pack * v2.0.0-preview.4 * perf(*): optimize fonts in docs app * fix(docs): stylesheets import ordering * perf(docs): optimize docs CSS output * fix(docs): correct linux tailwind executable file name * chore(*): delete update-icons.yml * perf(docs): enable stream rendering on all pages * fix(docs): ensure initial theme is set * docs(overview): update content * docs(installation): update content * docs(design-tokens): update content * docs(customization): update content * docs(dark-mode): update content * docs(accordion): update content * refactor(docs): replace `Callout` with `LumexAlert` * docs(avatar): update content * docs(components): replace `Code` component usages with HTML tag * fix(docs): ensure ThemeSelector sets correct theme value on init * docs(landing): make adaptive * docs(customization): add dark mode for global theme sample * refactor(components): remove deprecated `Root` slot * refactor(utils): remove redundant; update access modifiers * docs(card): update slot names * refactor(docs): make stream rendering global * docs(header): remove active state from links; update stars counter * ci(*): remove deploy.yml * ci: run build-test on PR to main * v2.0.0 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Description
Closes #169
This PR introduces a new mechanism for toggling light/dark modes.
What's been done?
Checklist
Additional Notes
Summary by CodeRabbit
New Features
Enhancements
Bug Fixes
Documentation
Tests