refactor(theme): replace C# theme config with a new CSS-first approach#229
refactor(theme): replace C# theme config with a new CSS-first approach#229desmondinho merged 9 commits intodevfrom
Conversation
WalkthroughThis change removes the C#-based theme configuration system from both the LumexUI library and its documentation, replacing it with a CSS-first theming approach using design tokens (CSS custom properties). All theme-related C# models, utilities, and provider components are deleted, with new documentation, samples, and navigation reflecting the CSS-based theming strategy. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant DocsSite
participant CSS
User->>DocsSite: Navigates to theming docs
DocsSite->>CSS: Loads design tokens and theme CSS
User->>DocsSite: Toggles theme or customizes variables
DocsSite->>CSS: Applies CSS classes/variables for theming
CSS-->>User: UI updates with new theme/styles
Estimated code review effort5 (~2+ hours) Possibly related PRs
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: 10
🔭 Outside diff range comments (1)
docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs (1)
99-99: Remove obsolete LumexThemeProvider reference.The
LumexThemeProvideris still listed in the ComponentsApiCategory, but according to the PR objectives, the C#-based theme configuration system (includingLumexThemeProvider) has been removed in favor of CSS-first theming.- .Add( new( nameof( LumexThemeProvider ) ) );
🧹 Nitpick comments (19)
src/LumexUI/Components/Alert/LumexAlert.razor (2)
28-35: Readable switch, but extract duplicate “custom-or-fallback” logicThe explicit
@ifblock is clearer than the ternary that was here before—nice change.
Because the same pattern appears again for the description (and will likely be needed in other components), consider pulling it into a small helper, e.g.:RenderFragment Resolve(RenderFragment? custom, string fallback) => custom ?? (builder => builder.AddContent(0, fallback));Using the helper would eliminate duplication and keep future edits to the rendering logic in one place.
42-49: Second occurrence of the same pattern – DRY opportunityThis block repeats the title logic verbatim. If you adopt the helper suggested above (or a local function), both slots can become a single-line call, trimming noise and ensuring consistent behaviour.
src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs (1)
10-10: Empty XML documentation should be removed or populated.The XML summary comment is empty and provides no value. Either remove it entirely or add a meaningful description explaining the component's purpose in the new CSS-first theming approach.
-/// <summary> -/// -/// </summary>docs/LumexUI.Docs.Client/Samples/Theming/Customization/customizationGlobal.html (1)
2-3: Consider documenting the purpose of these root-level tokensReaders may benefit from a short comment (even inside the
<pre>block) explaining that these two variables are part of the public design-token contract and should be overridden rather than renamed. This prevents copy-/-paste drift as more tokens are added.docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensConventionCss.html (1)
1-3: Snippet will leak variables unless it is scopedPlacing custom properties without a selector causes them to take effect wherever the snippet is injected, which might confuse newcomers skimming the docs. In earlier samples you scope variables under
:rootor a theme class—doing the same here keeps the examples consistent.-<pre><code class="language-css">--lumex-primary: var(--color-blue-500); ---lumex-primary-foreground: var(--color-white); +<pre><code class="language-css">:root { + --lumex-primary: var(--color-blue-500); + --lumex-primary-foreground: var(--color-white); +}docs/LumexUI.Docs/Styles/prism.css (1)
25-27: Verify the colour change for.token.propertyMoving
.token.propertyfrom the orange group to teal alters property-name contrast in code blocks. Double-check this against the new palette; properties may now look identical to keywords, reducing legibility.docs/LumexUI.Docs.Client/Components/Preview.razor (1)
28-28: Ensurerounded-largeexists in the Tailwind config
rounded-largeisn’t a default Tailwind class. Make sure the plugin/theme file defines it and that TwMerge is aware, otherwise the class will be purged and the preview will render un-rounded.docs/LumexUI.Docs.Client/Samples/Theming/Customization/customizationCustom.html (1)
7-11::is(.dark *)selector has patchy legacy browser supportSafari < 15.4 and older Chromium versions ignore nested
:is()with the universal selector, so the dark-mode override may silently fail. If broad compatibility is required, duplicate the rule with a descendant selector:.theme-orange.dark *, .dark .theme-orange { --lumex-primary: var(--color-orange-500); --lumex-primary-foreground: var(--color-white); --lumex-focus: var(--color-orange-500); }docs/LumexUI.Docs/Styles/themes.css (1)
12-16: Fix indentation for consistency.The nested selector for dark theme support works correctly, but the indentation should be adjusted for better readability.
- .theme-example-custom:is(.dark *) { - --lumex-primary: var(--color-orange-500); - --lumex-primary-foreground: var(--color-white); - --lumex-focus: var(--color-orange-500); - } +.theme-example-custom:is(.dark *) { + --lumex-primary: var(--color-orange-500); + --lumex-primary-foreground: var(--color-white); + --lumex-focus: var(--color-orange-500); +}docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensLayout.html (1)
15-17: Consider providing fallback values for shadow tokens.While the comments indicate these are overridden in themes, empty CSS custom property values could cause issues in some contexts.
Consider providing fallback values:
- --lumex-shadow-small: ; /* Overridden in light/dark themes */ - --lumex-shadow-medium: ; /* Overridden in light/dark themes */ - --lumex-shadow-large: ; /* Overridden in light/dark themes */ + --lumex-shadow-small: none; /* Overridden in light/dark themes */ + --lumex-shadow-medium: none; /* Overridden in light/dark themes */ + --lumex-shadow-large: none; /* Overridden in light/dark themes */docs/LumexUI.Docs.Client/Components/CodeSnippetCollapsible.razor.cs (1)
11-14: Consider making the method public and adding collapse functionality.The
Expand()method should likely be public for external access, and consider adding aCollapse()method for a complete API.- private void Expand() + public void Expand() { _isExpanded = true; } + + public void Collapse() + { + _isExpanded = false; + } + + public void Toggle() + { + _isExpanded = !_isExpanded; + }docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensColors.html (1)
116-146: Consider extracting shared token definitions to a single sourceThe light/dark token blocks here duplicate almost verbatim the content of
designTokensThemesRef.html. Keeping two copies guarantees they will diverge over time. Prefer:
- Move the canonical variables to
src/LumexUI/Styles/_light.css/_dark.css.- In documentation samples, just
@importor inline a trimmed subset.This improves maintainability without altering behaviour.
docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensThemesRef.html (1)
139-143: Shadow tokens are re-defined but already exist in_layout.cssLight theme shadows are declared here while base tokens are empty in
_layout.css. Having two separate sources for the same variable class can be confusing. Consider moving the concrete shadow values to_layout.css(or vice-versa) and keep theme files colour-only.src/LumexUI/Styles/_layout.css (1)
17-21: Empty shadow tokens: provide sensible defaults or document why blank
--lumex-shadow-small/medium/largeare empty here yet overridden in theme files. Tooling that reads tokens directly (e.g., CSS-in-JS, design-token extractors) will treat them as unset. Supply fallback values or a comment indicating intentional override-only behaviour.docs/LumexUI.Docs.Client/Pages/Theming/DesignTokens.razor (1)
74-82:_headingscan be static to avoid per-request allocationThis array is immutable. Mark it
private static readonlyto prevent allocating it on every page instance.- private readonly Heading[] _headings = new Heading[] + private static readonly Heading[] _headings = new[]Micro-optimisation, but cheap and clear.
docs/LumexUI.Docs.Client/Pages/Theming/Customization/Customization.razor (2)
47-52: Make_headingsstatic for the same reason as in DesignTokens pageApplies the same allocation observation.
- private readonly Heading[] _headings = new Heading[] + private static readonly Heading[] _headings = new[]
38-40: Typo: “data-attribute” → “data attribute”Minor copy fix for developer clarity.
- toggle the class on a wrapper element such as <code><html></code> or <code><body></code>. + toggle the class on a wrapper element such as <code><html></code> or <code><body></code>.(The explanatory sentence duplicates “data” in other docs; ensure consistency.)
docs/LumexUI.Docs.Client/Components/ColorSwatches.razor (2)
3-10: Consider simplifying the complex CSS class combinations.The grid layout implementation is functional, but the combination of rotation, writing-mode, and responsive classes creates complexity that may be difficult to maintain. The
*:rotate-180 *:[writing-mode:vertical-lr] sm:*:rotate-325pattern is particularly complex.Consider extracting these styles into dedicated CSS classes for better maintainability:
- <div class="col-start-2 grid grid-cols-10 justify-items-center gap-1.5 py-1 bg-background font-medium text-small text-foreground-900 *:rotate-180 *:[writing-mode:vertical-lr] sm:gap-4 sm:*:rotate-325 sm:*:[writing-mode:horizontal-tb]"> + <div class="col-start-2 grid grid-cols-10 justify-items-center gap-1.5 py-1 bg-background font-medium text-small text-foreground-900 header-labels sm:gap-4">Then define
.header-labelsin CSS with proper responsive breakpoints.
21-27: Address code duplication in sticky header styling.The sticky header implementation works correctly, but it duplicates the complex CSS class pattern from the earlier header. Consider creating a shared CSS class or component to eliminate duplication:
- <div class="sticky top-16 z-10 col-start-2 grid grid-cols-10 justify-items-center gap-1.5 py-1 bg-background font-medium text-small text-foreground-900 *:rotate-180 *:[writing-mode:vertical-lr] sm:gap-4 sm:*:rotate-0 sm:*:[writing-mode:horizontal-tb]"> + <div class="sticky top-16 z-10 col-start-2 shade-header-labels">
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (76)
docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs(2 hunks)docs/LumexUI.Docs.Client/Components/CodeSnippet.razor(1 hunks)docs/LumexUI.Docs.Client/Components/CodeSnippetCollapsible.razor(1 hunks)docs/LumexUI.Docs.Client/Components/CodeSnippetCollapsible.razor.cs(1 hunks)docs/LumexUI.Docs.Client/Components/ColorSwatches.razor(1 hunks)docs/LumexUI.Docs.Client/Components/Layouts/MainLayout.razor(1 hunks)docs/LumexUI.Docs.Client/Components/Preview.razor(1 hunks)docs/LumexUI.Docs.Client/Pages/Customization/Colors.razor(0 hunks)docs/LumexUI.Docs.Client/Pages/Customization/CustomizeTheme.razor(0 hunks)docs/LumexUI.Docs.Client/Pages/Customization/LayoutConfig.razor(0 hunks)docs/LumexUI.Docs.Client/Pages/Customization/Theme.razor(0 hunks)docs/LumexUI.Docs.Client/Pages/Theming/Customization/Customization.razor(1 hunks)docs/LumexUI.Docs.Client/Pages/Theming/Customization/ThemeTogglePreview.razor(1 hunks)docs/LumexUI.Docs.Client/Pages/Theming/DesignTokens.razor(1 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Colors/colorsCommon.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Colors/colorsCommonScale.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Colors/colorsSemantic.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Customize Theme/customizeThemeColors.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Customize Theme/customizeThemeColorsExample.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Customize Theme/customizeThemeLayout.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Customize Theme/customizeThemeLayoutExample.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigFontSize.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigFontSizeExample.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigFull.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigLineHeight.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigLineHeightExample.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigOpacity.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigOpacityExample.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigRadius.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigRadiusExample.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigShadow.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigShadowExample.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutTypesBaseScale.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutTypesFontScale.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeTypesLayoutConfig.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeTypesLumexTheme.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeTypesThemeColors.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeTypesThemeConfig.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeUsageCreate.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeUsageCustom.html(0 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Customization/customizationCustom.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Customization/customizationGlobal.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Customization/customizationToggle.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensColors.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensConventionCss.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensConventionHtml.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensLayout.html(1 hunks)docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensThemesRef.html(1 hunks)docs/LumexUI.Docs.Client/Theme/DocsTheme.cs(0 hunks)docs/LumexUI.Docs.Client/_Imports.razor(0 hunks)docs/LumexUI.Docs/Styles/globals.css(1 hunks)docs/LumexUI.Docs/Styles/prism.css(2 hunks)docs/LumexUI.Docs/Styles/themes.css(1 hunks)src/LumexUI/Common/Enums/ThemeType.cs(0 hunks)src/LumexUI/Components/Alert/LumexAlert.razor(1 hunks)src/LumexUI/Components/Providers/LumexThemeProvider.razor(0 hunks)src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs(1 hunks)src/LumexUI/Styles/_dark.css(1 hunks)src/LumexUI/Styles/_layout.css(1 hunks)src/LumexUI/Styles/_light.css(1 hunks)src/LumexUI/Styles/_theme.css(2 hunks)src/LumexUI/Theme/Colors/Colors.cs(0 hunks)src/LumexUI/Theme/Colors/Models/BaseColors.cs(0 hunks)src/LumexUI/Theme/Colors/Models/ColorScale.cs(0 hunks)src/LumexUI/Theme/Colors/Models/ThemeColors.cs(0 hunks)src/LumexUI/Theme/Colors/SemanticColors.cs(0 hunks)src/LumexUI/Theme/Layout/LayoutConfig.cs(0 hunks)src/LumexUI/Theme/Layout/Models/BaseScale.cs(0 hunks)src/LumexUI/Theme/Layout/Models/FontFamily.cs(0 hunks)src/LumexUI/Theme/Layout/Models/FontScale.cs(0 hunks)src/LumexUI/Theme/LumexTheme.cs(0 hunks)src/LumexUI/Theme/ThemeConfig.cs(0 hunks)src/LumexUI/Utilities/ColorUtils.cs(0 hunks)tests/LumexUI.Tests/Components/Providers/ThemeProviderTests.cs(0 hunks)tests/LumexUI.Tests/Theme/ColorScaleTests.cs(0 hunks)tests/LumexUI.Tests/Theme/ThemeTests.cs(0 hunks)
💤 Files with no reviewable changes (49)
- docs/LumexUI.Docs.Client/Samples/Customization/Colors/colorsSemantic.html
- docs/LumexUI.Docs.Client/_Imports.razor
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigLineHeightExample.html
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigShadow.html
- docs/LumexUI.Docs.Client/Samples/Customization/Customize Theme/customizeThemeColorsExample.html
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigShadowExample.html
- docs/LumexUI.Docs.Client/Samples/Customization/Customize Theme/customizeThemeLayoutExample.html
- docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeUsageCreate.html
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigOpacityExample.html
- docs/LumexUI.Docs.Client/Samples/Customization/Colors/colorsCommon.html
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutTypesBaseScale.html
- docs/LumexUI.Docs.Client/Theme/DocsTheme.cs
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigFontSizeExample.html
- src/LumexUI/Theme/Layout/Models/FontScale.cs
- docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeUsageCustom.html
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigLineHeight.html
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigRadius.html
- src/LumexUI/Common/Enums/ThemeType.cs
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigOpacity.html
- docs/LumexUI.Docs.Client/Samples/Customization/Customize Theme/customizeThemeColors.html
- src/LumexUI/Theme/Layout/LayoutConfig.cs
- docs/LumexUI.Docs.Client/Pages/Customization/Theme.razor
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigRadiusExample.html
- docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeTypesLumexTheme.html
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigFull.html
- docs/LumexUI.Docs.Client/Pages/Customization/Colors.razor
- tests/LumexUI.Tests/Components/Providers/ThemeProviderTests.cs
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutTypesFontScale.html
- src/LumexUI/Theme/Layout/Models/BaseScale.cs
- tests/LumexUI.Tests/Theme/ColorScaleTests.cs
- src/LumexUI/Theme/Colors/SemanticColors.cs
- docs/LumexUI.Docs.Client/Pages/Customization/LayoutConfig.razor
- src/LumexUI/Theme/Layout/Models/FontFamily.cs
- tests/LumexUI.Tests/Theme/ThemeTests.cs
- src/LumexUI/Theme/LumexTheme.cs
- docs/LumexUI.Docs.Client/Samples/Customization/Layout/layoutConfigFontSize.html
- src/LumexUI/Components/Providers/LumexThemeProvider.razor
- src/LumexUI/Theme/Colors/Models/BaseColors.cs
- docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeTypesLayoutConfig.html
- src/LumexUI/Theme/Colors/Models/ThemeColors.cs
- docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeTypesThemeColors.html
- docs/LumexUI.Docs.Client/Samples/Customization/Colors/colorsCommonScale.html
- docs/LumexUI.Docs.Client/Samples/Customization/Customize Theme/customizeThemeLayout.html
- docs/LumexUI.Docs.Client/Pages/Customization/CustomizeTheme.razor
- src/LumexUI/Theme/ThemeConfig.cs
- src/LumexUI/Theme/Colors/Colors.cs
- docs/LumexUI.Docs.Client/Samples/Customization/Theme/themeTypesThemeConfig.html
- src/LumexUI/Utilities/ColorUtils.cs
- src/LumexUI/Theme/Colors/Models/ColorScale.cs
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: desmondinho
PR: LumexUI/lumexui#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.
🪛 Biome (1.9.4)
docs/LumexUI.Docs/Styles/globals.css
[error] 6-7: This @import is in the wrong position.
Any @import rules must precede all other valid at-rules and style rules in a stylesheet (ignoring @charset and @layer), or else the @import rule is invalid.
Consider moving import position.
(lint/correctness/noInvalidPositionAtImportRule)
[error] 7-8: This @import is in the wrong position.
Any @import rules must precede all other valid at-rules and style rules in a stylesheet (ignoring @charset and @layer), or else the @import rule is invalid.
Consider moving import position.
(lint/correctness/noInvalidPositionAtImportRule)
src/LumexUI/Styles/_theme.css
[error] 1-1: expected , but instead found @
Remove @
(parse)
[error] 1-1: expected , but instead found import
Remove import
(parse)
[error] 1-1: Expected a compound selector but instead found '"./_layout"'.
Expected a compound selector here.
(parse)
[error] 1-2: expected , but instead found ;
Remove ;
(parse)
[error] 2-3: This @import is in the wrong position.
Any @import rules must precede all other valid at-rules and style rules in a stylesheet (ignoring @charset and @layer), or else the @import rule is invalid.
Consider moving import position.
(lint/correctness/noInvalidPositionAtImportRule)
[error] 3-4: This @import is in the wrong position.
Any @import rules must precede all other valid at-rules and style rules in a stylesheet (ignoring @charset and @layer), or else the @import rule is invalid.
Consider moving import position.
(lint/correctness/noInvalidPositionAtImportRule)
🔇 Additional comments (22)
docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensConventionHtml.html (1)
1-2: LGTM!The HTML snippet correctly demonstrates the usage of design token CSS classes in the new theming approach.
docs/LumexUI.Docs.Client/Samples/Theming/Customization/customizationToggle.html (1)
1-2: LGTM!The snippet correctly demonstrates how to apply custom theme classes to the body element in the new CSS-first theming approach.
docs/LumexUI.Docs.Client/Components/CodeSnippet.razor (1)
4-4: LGTM!The CSS class updates improve consistency with the new design token system, using standardized spacing (
my-6) and border radius (rounded-large) values.docs/LumexUI.Docs.Client/Components/Layouts/MainLayout.razor (1)
4-4: LGTM! Clean simplification of theme provider usage.The removal of the explicit
Themeparameter aligns perfectly with the CSS-first theming approach. TheLumexThemeProvidernow operates without requiring C# theme configuration, which simplifies the layout implementation.docs/LumexUI.Docs/Styles/themes.css (2)
1-4: LGTM! Good foundation for global theme overrides.The global theme example demonstrates proper use of CSS custom properties for design tokens. The
--lumex-prefix maintains consistency with the design system.
6-10: LGTM! Clean custom theme implementation.The custom theme properly overrides primary and focus colors using CSS custom properties. The color variable references (
--color-orange-600,--color-white) follow a consistent naming pattern.docs/LumexUI.Docs.Client/Samples/Theming/Design Tokens/designTokensLayout.html (3)
1-9: LGTM! Comprehensive typography token system.The typography design tokens are well-structured with consistent naming and include both font sizes and their corresponding line heights. The
--lumex-prefix maintains design system consistency.
11-13: LGTM! Clean radius token definitions.The border radius tokens provide a good range of sizes with clear, semantic naming.
19-23: LGTM! Well-defined opacity tokens for UI states.The opacity tokens cover all essential UI states with appropriate percentage values that provide good visual feedback.
docs/LumexUI.Docs.Client/Components/CodeSnippetCollapsible.razor.cs (1)
7-7: LGTM! Clean inheritance pattern.Good use of inheritance from the existing
CodeSnippetbase class to extend functionality.docs/LumexUI.Docs.Client/Pages/Theming/Customization/ThemeTogglePreview.razor (3)
1-1: LGTM! Appropriate render mode for interactive functionality.Using
InteractiveWebAssemblyis correct for this component's interactive theme switching behavior.
17-21: LGTM! Clean theme application pattern.The use of conditional CSS class application with
theme-example-customdemonstrates the CSS-first theming approach effectively. ThecontentsCSS class allows proper styling inheritance.
24-26: LGTM! Simple and effective state management.Clean boolean state management for theme selection with appropriate default value.
src/LumexUI/Styles/_light.css (1)
1-120: Excellent CSS-first theming foundation.This comprehensive light theme definition provides a solid foundation for the new CSS-first theming approach. The semantic color organization with full shade ranges (50-900) offers excellent flexibility, and the layered shadow definitions create subtle depth effects appropriate for modern UIs.
src/LumexUI/Styles/_dark.css (1)
1-120: Well-implemented dark theme with thoughtful adaptations.The dark theme properly inverts color scales for optimal contrast and includes inset highlights in shadows that enhance the dark mode aesthetic. The consistent inversion pattern across all semantic colors ensures comprehensive theme coverage.
src/LumexUI/Styles/_theme.css (2)
130-139: Typography variable renaming looks good.The updated typography variables using the
--lumex-text-prefix provide better naming consistency and organization compared to the previous approach.
1-3: Validate @import compatibility with your CSS build pipelineI didn’t find any PostCSS, Tailwind, Vite or Webpack config files (e.g. postcss.config.js, tailwind.config.js, vite.config.js, webpack.config.js) in the repo. Without a processor that recognizes
@importin plain CSS, those statements will trigger parser errors.• Confirm which tool or plugin handles CSS imports (e.g. PostCSS with
postcss-import, Tailwind’s PostCSS setup, Vite CSS modules, Sass/SCSS)
• If you rely on PostCSS, add or verify apostcss.config.jswith thepostcss-importplugin
• Alternatively, switch to a supported import syntax (e.g. Sass’s@use/@forward)docs/LumexUI.Docs.Client/Components/CodeSnippetCollapsible.razor (1)
1-17: Component and code-behind file verified: Approved
- The
CodeSnippetCollapsiblecomponent cleanly extendsCodeSnippetwith collapsible functionality.- Corresponding
CodeSnippetCollapsible.razor.csexists atdocs/LumexUI.Docs.Client/Components/CodeSnippetCollapsible.razor.cs.No further action required.
docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs (1)
16-19: Navigation restructuring aligns well with the CSS-first theming approach.The rename from "Customization" to "Theming" with focused "Design Tokens" and "Customization" items properly reflects the new CSS-first theming strategy.
docs/LumexUI.Docs.Client/Pages/Theming/DesignTokens.razor (1)
12-15: Anchor inLumexLinkmay not match generated heading IDs
/docs/theming/design-tokens#colorsrelies on the heading slug beingcolors. Ensure your markdown/DocsSection component generates consistent IDs (case-lower, hyphenated). Broken anchors hurt navigation.docs/LumexUI.Docs.Client/Components/ColorSwatches.razor (2)
12-19: LGTM! Clean implementation of CSS custom properties.The base colors section effectively demonstrates the new CSS-first theming approach. The use of CSS custom properties with the
var(--color-{key})pattern is consistent and maintainable.
46-56: LGTM! Well-structured static collections.The static readonly collections are well-designed and provide a clean separation between data and presentation. The shade values follow standard design system conventions, and the BaseColors dictionary provides clear mapping between technical names and display labels.
|
|
||
| public partial class CodeSnippetCollapsible : CodeSnippet | ||
| { | ||
| private bool _isExpanded; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Consider exposing the expanded state.
The _isExpanded field is private, but the Razor component likely needs access to this state for conditional rendering.
- private bool _isExpanded;
+ protected bool _isExpanded;Or add a public property:
private bool _isExpanded;
+
+ public bool IsExpanded => _isExpanded;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In docs/LumexUI.Docs.Client/Components/CodeSnippetCollapsible.razor.cs at line
9, the private field _isExpanded should be exposed via a public property to
allow the Razor component to access the expanded state for conditional
rendering. Add a public property with a getter (and optionally a setter) that
returns the value of _isExpanded.
| @foreach( var color in Enum.GetValues<ThemeColor>()[1..] ) | ||
| { | ||
| var colorName = color.ToString().ToLower(); | ||
|
|
||
| <p class="capitalize font-medium text-foreground-900 text-small">@colorName</p> | ||
| <div class="grid grid-cols-10 gap-1.5 sm:gap-4"> | ||
| @foreach( var shade in Shades ) | ||
| { | ||
| <div class="aspect-square w-full rounded-sm inset-ring inset-ring-default-900/10 sm:rounded-md" | ||
| style="background-color: var(--color-@colorName-@shade)" /> | ||
| } | ||
| </div> | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify ThemeColor enum usage and improve robustness.
Several concerns with the theme colors implementation:
-
Missing import: The
ThemeColorenum is used but not imported. Ensure the proper using statement is included. -
Fragile enum slicing: Using
[1..]assumes the first enum value should be skipped, but this is brittle if the enum order changes. -
Unsafe enum conversion:
ToString().ToLower()assumes enum names match CSS variable naming conventions without validation.
Consider these improvements:
+@using LumexUI.Common // or wherever ThemeColor is defined
- @foreach( var color in Enum.GetValues<ThemeColor>()[1..] )
+ @foreach( var color in Enum.GetValues<ThemeColor>().Where(c => c != ThemeColor.None) )Verify the ThemeColor enum definition and ensure it's properly accessible:
🏁 Script executed:
#!/bin/bash
# Search for ThemeColor enum definition and its usage
ast-grep --pattern 'enum ThemeColor { $$$ }'
rg -A 10 "enum ThemeColor"Length of output: 669
🏁 Script executed:
#!/bin/bash
set -e
# Print the first 50 lines of the Razor component to check existing using directives
sed -n '1,50p' docs/LumexUI.Docs.Client/Components/ColorSwatches.razorLength of output: 2367
#!/bin/bash
set -e
echo "1. Searching for _Imports.razor in docs/LumexUI.Docs.Client:"
find docs/LumexUI.Docs.Client -type f -name '_Imports.razor' -print
echo
echo "2. Displaying contents of docs/LumexUI.Docs.Client/_Imports.razor (if exists):"
if [ -f docs/LumexUI.Docs.Client/_Imports.razor ]; then
sed -n '1,200p' docs/LumexUI.Docs.Client/_Imports.razor
else
echo " _Imports.razor not found in docs/LumexUI.Docs.Client"
fi
echo
echo "3. Searching for GlobalUsings.cs files:"
find . -type f -name 'GlobalUsings.cs' -print
echo
echo "4. Displaying contents of each GlobalUsings.cs (if any):"
for file in $(find . -type f -name 'GlobalUsings.cs'); do
echo "---- $file ----"
sed -n '1,200p' "$file"
done
echo
echo "5. Searching for explicit using of LumexUI.Common.Enums:"
rg "using .*LumexUI\.Common\.Enums" -n || echo " No explicit using found."🏁 Script executed:
#!/bin/bash
set -e
echo "1. All _Imports.razor files and their first 200 lines:"
find . -type f -name '_Imports.razor' -print -exec sed -n '1,200p' {} \;
echo
echo "2. All GlobalUsings.cs files and their first 200 lines:"
find . -type f -name 'GlobalUsings.cs' -print -exec sed -n '1,200p' {} \;
echo
echo "3. Razor component imports for ThemeColor:"
rg -n "ThemeColor" -g "docs/LumexUI.Docs.Client/**/*.razor"Length of output: 35253
Fix missing import and brittle ThemeColor iteration in ColorSwatches.razor
Several updates to make this more robust:
- Add the correct namespace for
ThemeColorso the enum is always in scope. - Avoid slicing the enum array (
[1..]), which breaks if the enum order changes. - Replace the simple
ToString().ToLower()with a CSS-key helper to handle multi-word names.
Locations to update:
• docs/LumexUI.Docs.Client/_Imports.razor
• docs/LumexUI.Docs.Client/Components/ColorSwatches.razor
Proposed changes:
// docs/LumexUI.Docs.Client/_Imports.razor
@using LumexUI.Common
+@using LumexUI.Common.Enums
// docs/LumexUI.Docs.Client/Components/ColorSwatches.razor
- @foreach( var color in Enum.GetValues<ThemeColor>()[1..] )
+ @foreach( var color in Enum.GetValues<ThemeColor>().Where(c => c != ThemeColor.None) )
{
- var colorName = color.ToString().ToLower();
+ var colorName = color.ToCssKey(); // use an extension mapping enum → CSS variable key
<p class="capitalize font-medium text-foreground-900 text-small">@colorName</p>
…
}You’ll need to implement something like:
namespace LumexUI.Common.Enums
{
public static class ThemeColorExtensions
{
public static string ToCssKey(this ThemeColor color)
=> color switch
{
ThemeColor.LightBlue => "light-blue",
// add any custom mappings here
_ => color.ToString().ToLower()
};
}
}This ensures the enum is imported, the “None” value is excluded explicitly, and CSS-variable names stay correct even for multi-word enums.
🤖 Prompt for AI Agents
In docs/LumexUI.Docs.Client/Components/ColorSwatches.razor around lines 29 to
41, fix the missing import of the ThemeColor enum by adding its namespace to
docs/LumexUI.Docs.Client/_Imports.razor. Replace the brittle slicing of
Enum.GetValues<ThemeColor>()[1..] with explicit filtering to exclude the None
value. Implement a ThemeColorExtensions class with a ToCssKey method to convert
enum values to CSS-friendly strings, handling multi-word names correctly. Update
the razor code to use this ToCssKey method instead of ToString().ToLower() for
generating CSS variable names.
| Preview = "relative p-4 flex flex-wrap items-center gap-4 overflow-visible scrollbar-hide", | ||
| PreviewWrapper = "relative p-4 rounded-large ring ring-foreground-900/10 not-prose", |
There was a problem hiding this comment.
overflow-visible can break horizontal layouts previously relying on scroll
The preview pane earlier used overflow-x-auto to keep wide components from widening the docs column. Switching to overflow-visible risks layout overflow on small screens. Consider restoring overflow-x-auto or adding responsive guards.
🤖 Prompt for AI Agents
In docs/LumexUI.Docs.Client/Components/Preview.razor around lines 27 to 28, the
CSS class for Preview uses overflow-visible which can cause layout overflow on
small screens. To fix this, replace overflow-visible with overflow-x-auto or add
responsive CSS classes to apply overflow-x-auto on smaller screens while keeping
overflow-visible on larger screens, ensuring the preview pane does not break the
horizontal layout.
| <div class="absolute top-0 right-0 flex gap-2"> | ||
| <button type="button" | ||
| class="size-6 rounded-full bg-primary ring-offset-2 data-[selected=true]:ring-2 data-[selected=true]:ring-primary" | ||
| data-selected="@(_isCustomSelected is false ? "true" : "false")" | ||
| @onclick="@(() => _isCustomSelected = false)"> | ||
| </button> | ||
| <button type="button" | ||
| class="size-6 rounded-full bg-orange-500 ring-offset-2 data-[selected=true]:ring-2 data-[selected=true]:ring-orange-500" | ||
| data-selected="@(_isCustomSelected is true ? "true" : "false")" | ||
| @onclick="@(() => _isCustomSelected = true)"> | ||
| </button> | ||
| </div> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Improve accessibility and simplify conditional logic.
The toggle buttons need accessibility improvements and the conditional logic can be simplified.
<div class="absolute top-0 right-0 flex gap-2">
- <button type="button"
+ <button type="button"
+ aria-label="Select default theme"
class="size-6 rounded-full bg-primary ring-offset-2 data-[selected=true]:ring-2 data-[selected=true]:ring-primary"
- data-selected="@(_isCustomSelected is false ? "true" : "false")"
+ data-selected="@(!_isCustomSelected)"
@onclick="@(() => _isCustomSelected = false)">
</button>
- <button type="button"
+ <button type="button"
+ aria-label="Select custom theme"
class="size-6 rounded-full bg-orange-500 ring-offset-2 data-[selected=true]:ring-2 data-[selected=true]:ring-orange-500"
- data-selected="@(_isCustomSelected is true ? "true" : "false")"
+ data-selected="@_isCustomSelected"
@onclick="@(() => _isCustomSelected = true)">
</button>
</div>📝 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.
| <div class="absolute top-0 right-0 flex gap-2"> | |
| <button type="button" | |
| class="size-6 rounded-full bg-primary ring-offset-2 data-[selected=true]:ring-2 data-[selected=true]:ring-primary" | |
| data-selected="@(_isCustomSelected is false ? "true" : "false")" | |
| @onclick="@(() => _isCustomSelected = false)"> | |
| </button> | |
| <button type="button" | |
| class="size-6 rounded-full bg-orange-500 ring-offset-2 data-[selected=true]:ring-2 data-[selected=true]:ring-orange-500" | |
| data-selected="@(_isCustomSelected is true ? "true" : "false")" | |
| @onclick="@(() => _isCustomSelected = true)"> | |
| </button> | |
| </div> | |
| <div class="absolute top-0 right-0 flex gap-2"> | |
| <button type="button" | |
| aria-label="Select default theme" | |
| class="size-6 rounded-full bg-primary ring-offset-2 data-[selected=true]:ring-2 data-[selected=true]:ring-primary" | |
| data-selected="@(!_isCustomSelected)" | |
| @onclick="@(() => _isCustomSelected = false)"> | |
| </button> | |
| <button type="button" | |
| aria-label="Select custom theme" | |
| class="size-6 rounded-full bg-orange-500 ring-offset-2 data-[selected=true]:ring-2 data-[selected=true]:ring-orange-500" | |
| data-selected="@_isCustomSelected" | |
| @onclick="@(() => _isCustomSelected = true)"> | |
| </button> | |
| </div> |
🤖 Prompt for AI Agents
In docs/LumexUI.Docs.Client/Pages/Theming/Customization/ThemeTogglePreview.razor
around lines 4 to 15, improve accessibility by adding appropriate ARIA
attributes such as aria-pressed to the toggle buttons to indicate their state to
assistive technologies. Simplify the conditional logic for the data-selected
attribute by directly binding it to the boolean _isCustomSelected without using
the ternary operator, ensuring the attribute reflects true or false correctly.
| --lumex-default-900: var(--color-zinc-50); | ||
| --lumex-default: var(--lumex-default-300); | ||
| --lumex-default-foreground: var(--color-black); | ||
|
|
There was a problem hiding this comment.
--lumex-default-foreground uses black in the dark palette—likely unreadable
The .dark theme sets --lumex-default-foreground: var(--color-black) while every default/surface background in the same block is a dark zinc shade. This produces near-zero contrast for default text against the dark surfaces.
- --lumex-default-foreground: var(--color-black);
+ --lumex-default-foreground: var(--color-white);Please verify contrast for accessibility (WCAG AA) if you change this.
📝 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.
| --lumex-default-900: var(--color-zinc-50); | |
| --lumex-default: var(--lumex-default-300); | |
| --lumex-default-foreground: var(--color-black); | |
| --lumex-default-900: var(--color-zinc-50); | |
| --lumex-default: var(--lumex-default-300); | |
| --lumex-default-foreground: var(--color-white); |
🤖 Prompt for AI Agents
In docs/LumexUI.Docs.Client/Samples/Theming/Design
Tokens/designTokensColors.html around lines 148 to 151, the dark theme sets
--lumex-default-foreground to black, which results in poor contrast against the
dark zinc backgrounds. To fix this, update --lumex-default-foreground to a
lighter color that ensures sufficient contrast according to WCAG AA guidelines,
such as a light gray or white variant, and verify the contrast ratio for
accessibility compliance.
| --lumex-default-50: var(--color-zinc-900); | ||
| --lumex-default-100: var(--color-zinc-800); | ||
| --lumex-default-200: var(--color-zinc-700); | ||
| --lumex-default-300: var(--color-zinc-600); | ||
| --lumex-default-400: var(--color-zinc-500); | ||
| --lumex-default-500: var(--color-zinc-400); | ||
| --lumex-default-600: var(--color-zinc-300); | ||
| --lumex-default-700: var(--color-zinc-200); | ||
| --lumex-default-800: var(--color-zinc-100); | ||
| --lumex-default-900: var(--color-zinc-50); | ||
| --lumex-default: var(--lumex-default-300); | ||
| --lumex-default-foreground: var(--color-black); | ||
|
|
There was a problem hiding this comment.
Same low-contrast issue for dark/default foreground here
--lumex-default-foreground again maps to var(--color-black) inside the .dark block. Apply the same correction:
- --lumex-default-foreground: var(--color-black);
+ --lumex-default-foreground: var(--color-white);After adjusting, re-generate the docs screenshots to ensure examples still look correct.
📝 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.
| --lumex-default-50: var(--color-zinc-900); | |
| --lumex-default-100: var(--color-zinc-800); | |
| --lumex-default-200: var(--color-zinc-700); | |
| --lumex-default-300: var(--color-zinc-600); | |
| --lumex-default-400: var(--color-zinc-500); | |
| --lumex-default-500: var(--color-zinc-400); | |
| --lumex-default-600: var(--color-zinc-300); | |
| --lumex-default-700: var(--color-zinc-200); | |
| --lumex-default-800: var(--color-zinc-100); | |
| --lumex-default-900: var(--color-zinc-50); | |
| --lumex-default: var(--lumex-default-300); | |
| --lumex-default-foreground: var(--color-black); | |
| --lumex-default-50: var(--color-zinc-900); | |
| --lumex-default-100: var(--color-zinc-800); | |
| --lumex-default-200: var(--color-zinc-700); | |
| --lumex-default-300: var(--color-zinc-600); | |
| --lumex-default-400: var(--color-zinc-500); | |
| --lumex-default-500: var(--color-zinc-400); | |
| --lumex-default-600: var(--color-zinc-300); | |
| --lumex-default-700: var(--color-zinc-200); | |
| --lumex-default-800: var(--color-zinc-100); | |
| --lumex-default-900: var(--color-zinc-50); | |
| --lumex-default: var(--lumex-default-300); | |
| --lumex-default-foreground: var(--color-white); |
🤖 Prompt for AI Agents
In docs/LumexUI.Docs.Client/Samples/Theming/Design
Tokens/designTokensThemesRef.html around lines 167 to 179, the
--lumex-default-foreground variable is set to var(--color-black) inside the
.dark block, causing low contrast issues. Change --lumex-default-foreground to a
lighter color that provides sufficient contrast against the dark background,
similar to the fix applied elsewhere for dark themes. After making this change,
re-generate the documentation screenshots to verify the visual correctness of
the examples.
| @import "./blazor" layer(components); | ||
| @import "./typography" layer(utilities); | ||
| @import "./fonts"; | ||
| @import "./themes"; |
There was a problem hiding this comment.
Fix CSS import ordering issue.
The @import "./themes" statement is in an invalid position according to CSS specifications. @import rules must precede all other valid at-rules except @charset and @layer.
Move the themes import before the imports with layer() directives:
@import "tailwindcss" theme(static);
@import "../bin/lumexui/_theme";
+@import "./fonts";
+@import "./themes";
@import "./prism" layer(components);
@import "./blazor" layer(components);
@import "./typography" layer(utilities);
-@import "./fonts";
-@import "./themes";📝 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.
| @import "./themes"; | |
| @import "tailwindcss" theme(static); | |
| @import "../bin/lumexui/_theme"; | |
| @import "./fonts"; | |
| @import "./themes"; | |
| @import "./prism" layer(components); | |
| @import "./blazor" layer(components); | |
| @import "./typography" layer(utilities); |
🧰 Tools
🪛 Biome (1.9.4)
[error] 6-7: This @import is in the wrong position.
Any @import rules must precede all other valid at-rules and style rules in a stylesheet (ignoring @charset and @layer), or else the @import rule is invalid.
Consider moving import position.
(lint/correctness/noInvalidPositionAtImportRule)
[error] 7-8: This @import is in the wrong position.
Any @import rules must precede all other valid at-rules and style rules in a stylesheet (ignoring @charset and @layer), or else the @import rule is invalid.
Consider moving import position.
(lint/correctness/noInvalidPositionAtImportRule)
🤖 Prompt for AI Agents
In docs/LumexUI.Docs/Styles/globals.css at line 7, the @import "./themes"
statement is placed after other at-rules with layer() directives, which violates
CSS import ordering rules. Move the @import "./themes" line so that it appears
before any other at-rules except @charset and @layer, ensuring it is the first
import statement in the file.
| @@ -1,6 +1,5 @@ | |||
| .token.tag, | |||
There was a problem hiding this comment.
Remove the accidental BOM character
The (U+FEFF) before .token.tag can break some build tools and cause an unexpected glyph in minified output. Delete the BOM to avoid encoding issues.
-.token.tag,
+.token.tag,📝 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.
| .token.tag, | |
| .token.tag, |
🤖 Prompt for AI Agents
In docs/LumexUI.Docs/Styles/prism.css at line 1, remove the accidental BOM
character (U+FEFF) that appears before the `.token.tag` selector. This invisible
character can cause encoding issues and break some build tools, so delete it to
ensure clean and correct CSS processing.
| public partial class LumexThemeProvider : ComponentBase | ||
| { | ||
| private const string Prefix = "lumex"; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the configuration of the theme. | ||
| /// </summary> | ||
| [Parameter] public LumexTheme Theme { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="LumexThemeProvider"/>. | ||
| /// </summary> | ||
| public LumexThemeProvider() | ||
| { | ||
| Theme = new(); | ||
| } | ||
|
|
||
| private string GenerateTheme<TColors>( ThemeConfig<TColors> theme ) where TColors : ThemeColors, new() | ||
| { | ||
| var cssSelector = $"[data-theme={theme.Type.ToDescription()}]"; | ||
|
|
||
| if( theme.Type == Theme.DefaultTheme ) | ||
| { | ||
| cssSelector = $":root, {cssSelector}"; | ||
| } | ||
|
|
||
| var sb = new StringBuilder(); | ||
| sb.AppendLine( $"{cssSelector} {{" ); | ||
|
|
||
| // Colors | ||
| var themeColors = GetThemeColorsDict( theme.Colors ); | ||
|
|
||
| foreach( var color in themeColors ) | ||
| { | ||
| foreach( var scale in color.Value ) | ||
| { | ||
| var scaleKey = scale.Key != "default" | ||
| ? $"{color.Key}-{scale.Key}" | ||
| : $"{color.Key}"; | ||
|
|
||
| sb.AppendLine( $"--{Prefix}-{scaleKey}: {scale.Value};" ); | ||
| } | ||
| } | ||
|
|
||
| // Layout | ||
| sb.AppendLine( $"--{Prefix}-font-sans: {theme.Layout.FontFamily?.Sans};" ); | ||
| sb.AppendLine( $"--{Prefix}-font-mono: {theme.Layout.FontFamily?.Mono};" ); | ||
| sb.AppendLine( $"--{Prefix}-font-size-tiny: {theme.Layout.FontSize.Xs};" ); | ||
| sb.AppendLine( $"--{Prefix}-font-size-small: {theme.Layout.FontSize.Sm};" ); | ||
| sb.AppendLine( $"--{Prefix}-font-size-medium: {theme.Layout.FontSize.Md};" ); | ||
| sb.AppendLine( $"--{Prefix}-font-size-large: {theme.Layout.FontSize.Lg};" ); | ||
| sb.AppendLine( $"--{Prefix}-line-height-tiny: {theme.Layout.LineHeight.Xs};" ); | ||
| sb.AppendLine( $"--{Prefix}-line-height-small: {theme.Layout.LineHeight.Sm};" ); | ||
| sb.AppendLine( $"--{Prefix}-line-height-medium: {theme.Layout.LineHeight.Md};" ); | ||
| sb.AppendLine( $"--{Prefix}-line-height-large: {theme.Layout.LineHeight.Lg};" ); | ||
| sb.AppendLine( $"--{Prefix}-radius-small: {theme.Layout.Radius.Sm};" ); | ||
| sb.AppendLine( $"--{Prefix}-radius-medium: {theme.Layout.Radius.Md};" ); | ||
| sb.AppendLine( $"--{Prefix}-radius-large: {theme.Layout.Radius.Lg};" ); | ||
| sb.AppendLine( $"--{Prefix}-shadow-small: {theme.Layout.Shadow.Sm};" ); | ||
| sb.AppendLine( $"--{Prefix}-shadow-medium: {theme.Layout.Shadow.Md};" ); | ||
| sb.AppendLine( $"--{Prefix}-shadow-large: {theme.Layout.Shadow.Lg};" ); | ||
| sb.AppendLine( $"--{Prefix}-opacity-divider: {theme.Layout.DividerOpacity * 100}%;" ); | ||
| sb.AppendLine( $"--{Prefix}-opacity-disabled: {theme.Layout.DisabledOpacity * 100}%;" ); | ||
| sb.AppendLine( $"--{Prefix}-opacity-focus: {theme.Layout.FocusOpacity * 100}%;" ); | ||
| sb.AppendLine( $"--{Prefix}-opacity-hover: {theme.Layout.HoverOpacity * 100}%;" ); | ||
|
|
||
| sb.AppendLine( "}" ); | ||
| return sb.ToString(); | ||
| } | ||
|
|
||
| private static Dictionary<string, ColorScale> GetThemeColorsDict( ThemeColors colors ) | ||
| { | ||
| return new() | ||
| { | ||
| ["background"] = colors.Background, | ||
| ["foreground"] = colors.Foreground, | ||
| ["overlay"] = colors.Overlay, | ||
| ["focus"] = colors.Focus, | ||
| ["divider"] = colors.Divider, | ||
| ["surface1"] = colors.Surface1, | ||
| ["surface2"] = colors.Surface2, | ||
| ["surface3"] = colors.Surface3, | ||
| ["default"] = colors.Default, | ||
| ["primary"] = colors.Primary, | ||
| ["secondary"] = colors.Secondary, | ||
| ["success"] = colors.Success, | ||
| ["warning"] = colors.Warning, | ||
| ["danger"] = colors.Danger, | ||
| ["info"] = colors.Info, | ||
| }; | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Consider the purpose of keeping an empty component.
The LumexThemeProvider class has been stripped of all functionality. If this component is no longer needed in the CSS-first approach, consider removing it entirely. If it needs to be kept for backward compatibility, add an [Obsolete] attribute with migration guidance.
🏁 Script executed:
#!/bin/bash
# Description: Check if LumexThemeProvider is still being used in the codebase
# Search for usages of LumexThemeProvider in Razor files
echo "=== Searching for LumexThemeProvider usage in Razor files ==="
rg -A 3 "LumexThemeProvider" --glob "*.razor"
# Search for usages in C# files
echo -e "\n=== Searching for LumexThemeProvider usage in C# files ==="
rg -A 3 "LumexThemeProvider" --glob "*.cs" --glob "!**/LumexThemeProvider.razor.cs"
# Check if the corresponding .razor file still exists
echo -e "\n=== Checking for LumexThemeProvider.razor file ==="
fd "LumexThemeProvider.razor" --type fLength of output: 1646
LumexThemeProvider is still referenced—do not remove without a deprecation plan.
The LumexThemeProvider class has lost its implementation but remains used in the docs site and navigation store. Removing it outright will break:
- docs/LumexUI.Docs.Client/Components/Layouts/MainLayout.razor ()
- docs/LumexUI.Docs.Client/Pages/Getting Started/Installation.razor (installation instructions)
- docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs (navigation item)
And there is currently no corresponding LumexThemeProvider.razor markup to satisfy those references.
Please address one of the following:
- Restore a stub
.razorfile (e.g. an empty<div>or<CascadingValue>) so the component remains consumable. - If deprecating, add
[Obsolete("…migration guidance…")]to the class, update/remove docs usage, and provide upgrade instructions.
🤖 Prompt for AI Agents
In src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs around lines 12
to 14, the LumexThemeProvider class is empty but still referenced in multiple
places including the docs site and navigation store, causing potential breakage.
To fix this, either restore a stub LumexThemeProvider.razor file with minimal
markup like an empty div or CascadingValue so the component remains usable, or
if you intend to deprecate it, mark the class with the Obsolete attribute
including migration guidance, update or remove all references in the docs and
navigation store, and provide clear upgrade instructions.
| @@ -0,0 +1,27 @@ | |||
| :root { | |||
There was a problem hiding this comment.
Remove BOM character to avoid unexpected selector names
The first byte is U+FEFF (). Some CSS pipelines treat it as part of the first selector, producing :root which browsers ignore in edge cases and breaks AST tools.
-:root {
+:root {Deleting the BOM makes the selector valid UTF-8 plain text.
📝 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.
| :root { | |
| :root { |
🤖 Prompt for AI Agents
In src/LumexUI/Styles/_layout.css at line 1, remove the BOM character (U+FEFF)
at the start of the file before the :root selector. This will ensure the
selector is valid UTF-8 plain text and prevent issues with CSS pipelines and AST
tools that misinterpret the BOM as part of the selector.
* 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 #227
This PR introduces a new CSS-first theming approach in LumexUI, replacing the previous C#-based configuration system.
What's been done?
Checklist
Additional Notes
Summary by CodeRabbit
New Features
Refactor
Style
Bug Fixes
Chores