docs: add Home page with library usage examples#241
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughAdds a docs Home layout and landing page with many interactive example components, introduces a DocsThemeService and ThemeSelector, extends Header parameters, integrates ApexCharts and chart JS modules, updates popover/tooltip behavior and JS positioning, adds multiple icon components, and adjusts styles and tests. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant ThemeSel as ThemeSelector
participant Service as DocsThemeService
participant Example as ExampleComponentBase
User->>ThemeSel: select theme color
ThemeSel->>Service: SetThemeColor(color != None)
Service-->>Example: OnChanged event
Example->>Example: StateHasChanged()
Note over Example: ThemeColor reads Service.GetThemeColor()
sequenceDiagram
autonumber
participant User
participant Trigger as PopoverTrigger
participant Popover as LumexPopover
participant Content as PopoverContent
participant JS as popover.js/bundle
User->>Trigger: open trigger
Trigger->>Popover: Open()
Popover->>Content: Render()
alt non-tooltip
Content->>Content: Render overlay [data-popover-overlay]
Content->>JS: initialize(id, options)
JS->>JS: portal(popover + overlay) & autoUpdate(start)
else tooltip
Content->>JS: initialize(id, options)
JS->>JS: portal(popover) & autoUpdate(start)
end
User->>Content: click overlay
Content->>Popover: CloseAsync()
Popover->>JS: destroy()
JS->>JS: cleanup autoUpdate
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests
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. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
src/LumexUI/Styles/InputField.cs (2)
233-252: Fix trailing ! across variant-specific invalid styles; use prefix ! to ensure utilities applySeveral classes use a trailing ! (e.g., bg-danger/20!, text-danger-700!, border-danger!, after:bg-danger!), which is inconsistent with the rest of the codebase and typical Tailwind/UnoCSS important syntax. These likely won’t compile correctly.
Apply this diff to correct all affected utilities:
- .Add( ElementClass.Empty() - .Add( "bg-danger/20!" ) - .Add( "group-hover:bg-danger/10!" ) - .Add( "group-data-[focus=true]:bg-danger/10!" ), when: slot is nameof( _inputWrapper ) ) + .Add( ElementClass.Empty() + .Add( "!bg-danger/20" ) + .Add( "group-hover:!bg-danger/10" ) + .Add( "group-data-[focus=true]:!bg-danger/10" ), when: slot is nameof( _inputWrapper ) ) // For a better accessibility - .Add( "text-danger-700! placeholder:text-danger-500!", when: slot is nameof( _input ) ) - .Add( "text-danger-700!", when: slot is nameof( _label ) ), + .Add( "!text-danger-700 placeholder:!text-danger-500", when: slot is nameof( _input ) ) + .Add( "!text-danger-700", when: slot is nameof( _label ) ), InputVariant.Outlined => ElementClass.Empty() - .Add( ElementClass.Empty() - .Add( "border-danger!" ) - .Add( "group-data-[focus=true]:border-danger!" ), when: slot is nameof( _inputWrapper ) ), + .Add( ElementClass.Empty() + .Add( "!border-danger" ) + .Add( "group-data-[focus=true]:!border-danger" ), when: slot is nameof( _inputWrapper ) ), InputVariant.Underlined => ElementClass.Empty() - .Add( "after:bg-danger!", when: slot is nameof( _inputWrapper ) ), + .Add( "after:!bg-danger", when: slot is nameof( _inputWrapper ) ),
456-462: Broken utility string for has-[label] mt calc; extra parenthesis/bracketThe class string appears malformed:
- has-[label]:mt-[calc(var(--text-small)) _+_8px)] has an extra ) and misplaced underscore spacing compared to the Medium/Large variants.
Apply this diff to fix the calc expression and match the pattern used below:
- .Add( "has-[label]:mt-[calc(var(--text-small))_+_8px)]", when: slot is nameof( _base ) ) + .Add( "has-[label]:mt-[calc(var(--text-small)_+_8px)]", when: slot is nameof( _base ) )src/LumexUI/Styles/ColorVariants.cs (1)
75-75: Typo breaks classes: missing space in Danger/Light variant.
"bg-transparenttext-danger"should be"bg-transparent text-danger". As-is, Tailwind won’t apply either class.Apply this diff:
- [ThemeColor.Danger] = "bg-transparenttext-danger", + [ThemeColor.Danger] = "bg-transparent text-danger",src/LumexUI/wwwroot/js/utils/dom.js (1)
25-41: Keep backward compatibility, rebuild bundles & update interop/tests
In
src/LumexUI/wwwroot/js/utils/dom.js, simplify the signature and add a temporary alias:-export function portal(element, selector = undefined) { +export function portal(element, selector) { if (!(element instanceof HTMLElement)) { throw new Error('The provided element is not a valid HTMLElement.'); } let destination = selector ? document.querySelector(selector) : document.body; if (!destination) { throw new Error(`No portal container with the given selector '${selector}' was found!`); } if (element.parentElement !== destination) { destination.appendChild(element); } } // Temporary alias for backward compatibility (remove in next major) export { portal as portalTo };Regenerate the bundled output in
src/LumexUI/wwwroot/js/components/popover.bundle.js
so that internal calls toportalTo(...)becomeportal(...)and rely on the alias, rather than retaining the old definition.Ensure the Blazor JS-interop surface still exposes
portalTo: either update your interop registration to map
window.Lumex.elementReference.portalTo = portal
or adjust your tests to call the new name once the alias is in place.Update the affected test at
tests/LumexUI.Tests/Components/Navbar/NavbarTests.razor(around line 14)
to reflect the alias or new function name.Verify no leftover references before shipping:
# Catch any remaining portalTo mentions rg -nP '\bportalTo\b' -g '!**/dist/**' -g '!**/build/**'src/LumexUI/wwwroot/js/components/popover.bundle.js (1)
1666-1765: Bundle Out-of-Sync: uses portalTo & outside-click handler instead of overlay portalingWe ran a quick grep to compare the bundle vs. source:
- popover.bundle.js defines and calls
•portalTo(popover)
•createOutsideClickHandler([ref, popover])→destroyOutsideClickHandler- popover.js calls
•portal(popover)
•portal(overlay)
• NocreateOutsideClickHandlerorportalTousageThis mismatch will reintroduce the old outside-click behavior and won’t portal the overlay, causing behavioral inconsistencies.
Please address as follows:
- Rebuild
popover.bundle.jsfrom the currentsrc/LumexUI/wwwroot/js/components/popover.jsso it uses:
portal(popover)andportal(overlay)(in the same order as source)- The shared
autoUpdateflow only (no standalone outside-click wiring)- Remove the
portalToandcreateOutsideClickHandlerdefinitions/calls from the bundle- (If still present) Strip any debug logs related to initialization
Affected files:
- src/LumexUI/wwwroot/js/components/popover.bundle.js
- src/LumexUI/wwwroot/js/components/popover.js
♻️ Duplicate comments (2)
src/LumexUI.Shared.Icons/SparklesIcon.razor (1)
3-12: A11y note mirrors PlusIcon comment.Same accessibility consideration as mentioned for PlusIcon. Please address globally via IconBase if not already handled.
src/LumexUI.Shared.Icons/MinusIcon.razor (1)
3-11: A11y note mirrors PlusIcon comment.Same accessibility consideration as mentioned for PlusIcon. Please address globally via IconBase if not already handled.
🧹 Nitpick comments (68)
docs/LumexUI.Docs/Components/App.razor (2)
31-31: Pin ApexCharts version and add SRI.Unpinned CDN URLs can introduce breaking changes and pose supply-chain risks. Pin to a known-good version and include an integrity/hash.
Action items:
- Choose a specific ApexCharts version (e.g., 3.x.y that you’ve tested).
- Add integrity and crossorigin attributes to the script tag.
Example (replace version and integrity with the correct values you validate):
<script src="https://cdn.jsdelivr.net/npm/apexcharts@<pinned-version>" integrity="<sha512-...>" crossorigin="anonymous" defer></script>
10-10: Adddns-prefetchfallback for broader hint support.Preconnect is good. Add a dns-prefetch hint for browsers that don’t leverage preconnect as effectively.
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin> + <link rel="dns-prefetch" href="https://cdn.jsdelivr.net">src/LumexUI/Styles/ColorVariants.cs (1)
5-8: Consider exposing these as IReadOnlyDictionary to prevent accidental mutation.Static readonly reference still allows modifying dictionary contents at runtime. If these are configuration constants, prefer IReadOnlyDictionary or return new instances.
For example:
public static IReadOnlyDictionary<ThemeColor, string> Solid { get; } = new Dictionary<ThemeColor, string> { // ... };docs/LumexUI.Docs.Client/Pages/Getting Started/Overview.razor (1)
1-1: Dual-route alias approved; add a canonical URL to avoid SEO duplication.The page is reachable via two routes; add a canonical link so crawlers treat content as one source.
Insert after :
<HeadContent> <link rel="canonical" href="/docs/getting-started" /> </HeadContent>src/LumexUI.Shared.Icons/EraserIcon.razor (1)
3-11: Add aria-hidden for decorative SVGs (accessibility).If this icon is decorative, hide it from screen readers.
Apply this diff:
<svg xmlns="http://www.w3.org/2000/svg" width="@Width" height="@Height" viewBox="0 0 24 24" focusable="false" fill="currentColor" + aria-hidden="true" @attributes="@AdditionalAttributes">Optional: If some icons are not decorative, consider adding an optional Title/AriaLabel on IconBase and conditionally setting aria-hidden/role accordingly for all icons in one place.
src/LumexUI.Shared.Icons/SendAltIcon.razor (1)
3-11: Mirror a11y treatment used on other icons.Same suggestion as EraserIcon: hide decorative SVGs from assistive tech.
<svg xmlns="http://www.w3.org/2000/svg" width="@Width" height="@Height" viewBox="0 0 24 24" focusable="false" fill="currentColor" + aria-hidden="true" @attributes="@AdditionalAttributes">src/LumexUI.Shared.Icons/EnvelopeFilledIcon.razor (1)
3-12: Add aria-hidden for decorative usage (a11y consistency).Align with other icons to avoid noise in screen readers.
<svg xmlns="http://www.w3.org/2000/svg" width="@Width" height="@Height" viewBox="0 0 24 24" focusable="false" fill="currentColor" + aria-hidden="true" @attributes="@AdditionalAttributes">docs/LumexUI.Docs.Client/Components/Header.razor (1)
55-61: Make the GitHub link accessible and avoid hardcoded counts.
- Provide a descriptive title/aria-label for the icon-only link plus number.
- Prefer not to hardcode “294”; either pass it as a parameter or fetch at build-time so it doesn’t drift.
- <LumexNavbarItem Class="flex"> - <LumexLink Href="https://github.com/LumexUI/lumexui" - External="@true" - Class="text-foreground-500 hover:text-foreground-600 hover:opacity-100"> + <LumexNavbarItem Class="flex"> + <LumexLink Href="https://github.com/LumexUI/lumexui" + External="@true" + Class="text-foreground-500 hover:text-foreground-600 hover:opacity-100" + aria-label="LumexUI on GitHub (stars)"> <GithubIcon Size="20" /> - <span class="ml-1.5 text-small font-medium">294</span> + <span class="ml-1.5 text-small font-medium">294</span> </LumexLink> </LumexNavbarItem>If desired, I can sketch a small build-time script to fetch the star count via the GitHub API and embed it into the docs.
docs/LumexUI.Docs.Client/Pages/Home/Examples/ShareAccess.razor (1)
29-37: Improve copy feedback for screen readers.When only the icon changes, screen readers may miss the “copied” state. Consider keeping text (“Copied”) or add an aria-live region.
Minimal change to keep text:
- @if( _isCopied ) - { - <CheckIcon Size="16" /> - } + @if( _isCopied ) + { + <CheckIcon Size="16" /> + <span class="sr-only">Copied</span> + } else { <text>Copy link</text> }docs/LumexUI.Docs/wwwroot/js/docs.js (9)
2-11: Path-only comparison may ignore query/hash-driven navigations — confirm intentUsing only window.location.pathname means changes in ?query or #hash won’t trigger the scroll reset. If that’s intentional to preserve scroll on filter/anchor changes, great. If not, compare the full URL parts.
Apply this minimal diff if you want to include query and hash in the comparison:
- let currentUrl = window.location.pathname; + let currentUrl = window.location.pathname + window.location.search + window.location.hash; @@ - let newUrl = window.location.pathname; + let newUrl = window.location.pathname + window.location.search + window.location.hash;
6-8: Use a standards-compliant scroll behavior valueThe value 'instant' is non-standard for scrollTo options. Use 'auto' for an immediate jump, or 'smooth' for animation.
- window.scrollTo({ top: 0, left: 0, behavior: 'instant' }); + window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
12-17: Apply theme on first load, not only after enhanced navigationsTheme assignment only runs inside the enhancedload handler. On the initial page load, you can briefly render with the default theme before this kicks in.
Minimal change inside this block:
- let currentTheme = localStorage.getItem('lumexui.theme'); - if (currentTheme) { - document.documentElement.classList.remove('light', 'dark'); - document.documentElement.classList.add(currentTheme); - } + applyThemeFromLocalStorage();Add this helper once (outside the handler), and call it on DOMContentLoaded (see next comment):
function applyThemeFromLocalStorage() { const currentTheme = localStorage.getItem('lumexui.theme'); if (currentTheme) { document.documentElement.classList.remove('light', 'dark'); document.documentElement.classList.add(currentTheme); } }Would you like me to wire this up across the file in a small PR-ready patch?
36-38: Initialize theme immediately on DOMContentLoadedCall the same helper on first load so the UI renders with the persisted theme without flashing.
document.addEventListener('DOMContentLoaded', () => { interceptNavigation(); + // Ensure theme is applied on first load too + if (typeof applyThemeFromLocalStorage === 'function') { + applyThemeFromLocalStorage(); + } });
20-34: Clipboard fallback for non-secure contexts and older browsersnavigator.clipboard requires a secure context and (often) a user gesture. Provide a safe textarea fallback.
async function copyToClipboard(elementId) { @@ - try { - await navigator.clipboard.writeText(text) - console.log('Text copied to clipboard'); + try { + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(text); + } else { + // Fallback: temp textarea + const ta = document.createElement('textarea'); + ta.value = text ?? ''; + ta.style.position = 'fixed'; + ta.style.top = '-10000px'; + ta.style.opacity = '0'; + document.body.appendChild(ta); + ta.focus(); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); + } + console.log('Text copied to clipboard'); } catch (err) { console.error('Error copying text:', err); } }
41-42: Consider tracking chart instances to avoid leaks and double-rendersRepeated initializations (e.g., re-render or navigation into the same example) can stack multiple charts in the same container. Store instances and destroy prior ones before re-render.
Example approach to add globally (non-diff snippet, place near charts bootstrap):
window.charts = window.charts || {}; window.charts.__instances = window.charts.__instances || {}; window.charts.__render = function(key, el, options) { if (!el) return; if (typeof ApexCharts === 'undefined') { console.warn('ApexCharts is not loaded'); return; } // Destroy existing chart for this key const prev = window.charts.__instances[key]; if (prev && typeof prev.destroy === 'function') prev.destroy(); const inst = new ApexCharts(el, options); window.charts.__instances[key] = inst; inst.render(); };Then, in each initializer, replace new ApexCharts(...) with:
const el = document.getElementById('chart-activity'); window.charts.__render('activity', el, options);I can provide a ready-to-apply diff if you prefer this route.
43-81: Guard against missing element and unloaded ApexCharts before rendering the Activity chartDefensive guards prevent runtime errors if the DOM element isn’t present yet or ApexCharts hasn’t loaded.
- new ApexCharts(document.getElementById('chart-activity'), options).render(); + const el = document.getElementById('chart-activity'); + if (!el) return; + if (typeof ApexCharts === 'undefined') { + console.warn('ApexCharts is not loaded'); + return; + } + new ApexCharts(el, options).render();
82-122: Same guards for the Stats chartMirror the safety checks for consistency.
- new ApexCharts(document.getElementById('chart-stats'), options).render(); + const el = document.getElementById('chart-stats'); + if (!el) return; + if (typeof ApexCharts === 'undefined') { + console.warn('ApexCharts is not loaded'); + return; + } + new ApexCharts(el, options).render();
123-157: Same guards for the Usage donut chartApply the same checks here to keep all chart modules robust.
- new ApexCharts(document.getElementById('chart-usage'), options).render(); + const el = document.getElementById('chart-usage'); + if (!el) return; + if (typeof ApexCharts === 'undefined') { + console.warn('ApexCharts is not loaded'); + return; + } + new ApexCharts(el, options).render();src/LumexUI.Shared.Icons/PhoneFilledIcon.razor (1)
1-10: Icon component implementation looks consistent and correctInherits IconBase, uses currentColor, forwards attributes. Matches the pattern used across your icon set.
For accessibility:
- If the icon is purely decorative, consider setting aria-hidden="true" by default (unless an aria-label/title is provided via AdditionalAttributes).
- If it conveys meaning, ensure consumers pass an accessible name via aria-label or a <title> child.
src/LumexUI.Shared.Icons/PlusIcon.razor (1)
3-10: A11y: Ensure decorative icons are hidden from AT by default (or labeled when meaningful).Please confirm whether IconBase sets sensible defaults (e.g., aria-hidden="true" for decorative usage, or role="img" with aria-label/title when used as content). If IconBase doesn’t handle this, consider standardizing accessibility semantics across icons.
Would you like me to propose a small shared approach in IconBase to centralize this?
docs/LumexUI.Docs.Client/Pages/Home/Examples/CookieSettings.razor.cs (1)
5-14: Make the styling slots field readonly to avoid accidental mutation.The slots are constant for the component; marking it readonly communicates intent and prevents reassignment.
Apply this diff:
- private SwitchSlots _classes = new() + private readonly SwitchSlots _classes = new() { Root = new ElementClass() .Add( "flex-row-reverse" ) .Add( "gap-4" ) .Add( "justify-between" ) };docs/LumexUI.Docs.Client/Pages/Home/Examples/Usage.razor.cs (2)
16-25: Use static readonly for the theme-to-CSS map and add a safe fallback accessor.The map is immutable and shared across instances. Making it static readonly avoids per-instance allocations, and an accessor with a fallback prevents KeyNotFoundException if ThemeColor grows.
Apply this diff to make the dictionary static:
- private readonly Dictionary<ThemeColor, string> _chartCssVariables = new() + private static readonly IReadOnlyDictionary<ThemeColor, string> ChartCssVariables = new Dictionary<ThemeColor, string> { [ThemeColor.Default] = "[--chart-1:var(--color-zinc-900)] [--chart-2:var(--color-zinc-700)] [--chart-3:var(--color-zinc-500)] [--chart-4:var(--color-zinc-300)]", [ThemeColor.Primary] = "[--chart-1:var(--color-blue-900)] [--chart-2:var(--color-blue-600)] [--chart-3:var(--color-blue-400)] [--chart-4:var(--color-blue-300)]", [ThemeColor.Secondary] = "[--chart-1:var(--color-violet-900)] [--chart-2:var(--color-violet-600)] [--chart-3:var(--color-violet-400)] [--chart-4:var(--color-violet-300)]", [ThemeColor.Success] = "[--chart-1:var(--color-green-900)] [--chart-2:var(--color-green-600)] [--chart-3:var(--color-green-400)] [--chart-4:var(--color-green-300)]", [ThemeColor.Warning] = "[--chart-1:var(--color-amber-900)] [--chart-2:var(--color-amber-600)] [--chart-3:var(--color-amber-400)] [--chart-4:var(--color-amber-300)]", [ThemeColor.Danger] = "[--chart-1:var(--color-rose-900)] [--chart-2:var(--color-rose-600)] [--chart-3:var(--color-rose-400)] [--chart-4:var(--color-rose-300)]", [ThemeColor.Info] = "[--chart-1:var(--color-sky-900)] [--chart-2:var(--color-sky-600)] [--chart-3:var(--color-sky-400)] [--chart-4:var(--color-sky-300)]", };Additionally, add a small accessor (use in
.razormarkup) to provide a safe fallback:private string CurrentChartCssVariables => ChartCssVariables.TryGetValue(ThemeColor, out var vars) ? vars : ChartCssVariables[ThemeColor.Default];In Usage.razor, bind wherever you currently consume the variables, e.g.:
<div class="usage-chart-container @CurrentChartCssVariables"> ... </div>I can update the markup snippet if you paste the relevant portion of Usage.razor.
27-33: Optional: Call base.OnAfterRenderAsync for future-proofing.Not required now (base doesn’t override), but calling it keeps the pattern resilient if ExampleComponentBase adds logic later.
Apply this diff:
protected override async Task OnAfterRenderAsync( bool firstRender ) { + await base.OnAfterRenderAsync(firstRender); if( firstRender ) { await JSRuntime.InvokeVoidAsync( "charts.usage.initialize" ); } }src/LumexUI.Shared.Icons/CreditCardFilledIcon.razor (1)
3-9: Mark the SVG as decorative by default (add aria-hidden).For purely presentational icons, add aria-hidden to keep them out of the accessibility tree unless explicitly labeled by consumers.
<svg xmlns="http://www.w3.org/2000/svg" width="@Width" height="@Height" viewBox="0 0 24 24" focusable="false" fill="currentColor" + aria-hidden="true" @attributes="@AdditionalAttributes">If you plan to support accessible labeling in the future, consider adding a Title/AriaLabel parameter to IconBase and toggling aria-hidden conditionally.
src/LumexUI.Shared.Icons/AppleIcon.razor (1)
1-11: Add aria-hidden to the SVG for better default a11y.Same rationale as other presentational icons—hide from AT by default and allow consumers to override via AdditionalAttributes.
<svg xmlns="http://www.w3.org/2000/svg" width="@Width" height="@Height" viewBox="0 0 24 24" focusable="false" fill="currentColor" + aria-hidden="true" @attributes="@AdditionalAttributes">src/LumexUI.Shared.Icons/PaypalIcon.razor (1)
1-11: Default the SVG to decorative (aria-hidden).Keeps screen readers from announcing non-informative glyphs. Consumers can pass aria-label via AdditionalAttributes when needed.
<svg xmlns="http://www.w3.org/2000/svg" width="@Width" height="@Height" viewBox="0 0 24 24" focusable="false" fill="currentColor" + aria-hidden="true" @attributes="@AdditionalAttributes">docs/LumexUI.Docs.Client/Pages/Home/Examples/Usage.razor (3)
23-30: Add an accessible label to the icon-only button.Icon-only buttons should have an aria-label for screen readers.
<LumexDropdownTrigger Class="aria-expanded:*:bg-default/40"> <LumexButton IconOnly="@true" Size="@LumexUI.Common.Size.Small" Radius="@Radius.Full" - Variant="@Variant.Light"> + Variant="@Variant.Light" + aria-label="More options"> <EllipsisVerticalIcon Size="16" /> </LumexButton> </LumexDropdownTrigger>
12-19: Provide a visible or programmatic label for the select.Given LabelPlacement is set, also provide Label to aid a11y. If you prefer a visually hidden label, your component may support a Class to hide it; otherwise a short visible label works.
- <LumexSelect Value="@("Per day")" + <LumexSelect Value="@("Per day")" + Label="Time range" Size="@LumexUI.Common.Size.Small" LabelPlacement="@LabelPlacement.Outside" PopoverClasses="@(new() { Content = "min-w-fit" })">
41-43: Avoid self-closing non-void HTML elements.Use an explicit closing tag for div to prevent HTML parsing quirks and to be consistent with standard markup.
- <div class="h-40 @_chartCssVariables[ThemeColor]"> - <div id="chart-usage" /> - </div> + <div class="h-40 @_chartCssVariables[ThemeColor]"> + <div id="chart-usage"></div> + </div>docs/LumexUI.Docs.Client/Pages/Home/Examples/Stats.razor.cs (1)
16-25: Deduplicate chart CSS variable mappings across example components.This mapping appears in multiple example components (e.g., Usage, Stats). Centralize it (e.g., in ExampleComponentBase or a small helper) to keep themes consistent and reduce maintenance.
Example (outside this file): expose from ExampleComponentBase
protected static readonly IReadOnlyDictionary<ThemeColor, string> ChartCssVariables = new Dictionary<ThemeColor, string> { [ThemeColor.Default] = "[--chart:var(--lumex-default-900)] [--chart-fill:--alpha(var(--chart)_/_5%)]", [ThemeColor.Primary] = "[--chart:var(--lumex-primary)] [--chart-fill:--alpha(var(--chart)_/_5%)]", [ThemeColor.Secondary] = "[--chart:var(--lumex-secondary)] [--chart-fill:--alpha(var(--chart)_/_5%)]", [ThemeColor.Success] = "[--chart:var(--lumex-success)] [--chart-fill:--alpha(var(--chart)_/_5%)]", [ThemeColor.Warning] = "[--chart:var(--lumex-warning)] [--chart-fill:--alpha(var(--chart)_/_5%)]", [ThemeColor.Danger] = "[--chart:var(--lumex-danger)] [--chart-fill:--alpha(var(--chart)_/_5%)]", [ThemeColor.Info] = "[--chart:var(--lumex-info)] [--chart-fill:--alpha(var(--chart)_/_5%)]", };Then in each component: use ChartCssVariables[ThemeColor] in markup.
docs/LumexUI.Docs.Client/Pages/Home/Examples/Stats.razor (2)
25-27: Guard against missing ThemeColor keys when selecting chart CSS variablesIndexing
_chartCssVariablesdirectly risks a KeyNotFoundException if new theme colors are added later. Prefer a safe fallback to Default.Apply in this file:
- <div class="h-30 @_chartCssVariables[ThemeColor]"> + <div class="h-30 @ChartCss">And add this helper in Stats.razor.cs:
// In Stats.razor.cs private string ChartCss => _chartCssVariables.TryGetValue(ThemeColor, out var css) ? css : _chartCssVariables[ThemeColor.Default];
26-26: Prefer a non-self-closing div for claritySelf-closing divs render fine in Razor, but using an explicit closing tag aids readability and avoids surprises with some HTML tooling.
- <div id="chart-stats" /> + <div id="chart-stats"></div>docs/LumexUI.Docs.Client/Pages/Home/Examples/PaymentMethod.razor.cs (3)
8-17: Avoid KeyNotFoundException when ThemeColor changes or expandsDirect dictionary indexing with
_colorMap[ThemeColor]will throw if a future theme is introduced without an entry. Use a safe accessor.Apply in this file:
- .Add( _colorMap[ThemeColor] ), + .Add( GetColorClasses() ),Add this helper below:
private string GetColorClasses() => _colorMap.TryGetValue(ThemeColor, out var classes) ? classes : _colorMap[ThemeColor.Default];
19-28: Make the color map static to avoid per-instance allocationsThis map is immutable and the same for all instances. Mark it static readonly to reduce allocations.
- private readonly Dictionary<ThemeColor, string> _colorMap = new() + private static readonly Dictionary<ThemeColor, string> _colorMap = new() { [ThemeColor.Default] = "bg-surface1 border-default-100 hover:bg-surface2 data-[selected=true]:border-default data-[selected=true]:bg-surface2", [ThemeColor.Primary] = "bg-primary/10 border-primary/15 hover:bg-primary/15 data-[selected=true]:border-primary data-[selected=true]:bg-primary/15", [ThemeColor.Secondary] = "bg-secondary/10 border-secondary/15 hover:bg-secondary/15 data-[selected=true]:border-secondary data-[selected=true]:bg-secondary/15", [ThemeColor.Success] = "bg-success/10 border-success/15 hover:bg-success/15 data-[selected=true]:border-success data-[selected=true]:bg-success/15", - [ThemeColor.Warning] = "bg-warning/5 border-warning/15 hover:bg-warning/15 data-[selected=true]:border-warning data-[selected=true]:bg-warning/15", + [ThemeColor.Warning] = "bg-warning/5 border-warning/15 hover:bg-warning/15 data-[selected=true]:border-warning data-[selected=true]:bg-warning/15", [ThemeColor.Danger] = "bg-danger/10 border-danger/15 hover:bg-danger/15 data-[selected=true]:border-danger data-[selected=true]:bg-danger/15", [ThemeColor.Info] = "bg-info/10 border-info/15 hover:bg-info/15 data-[selected=true]:border-info data-[selected=true]:bg-info/15", };
19-26: Inconsistent opacity for Warning vs other colorsWarning uses 5% background while others use 10%. If intentional, ignore; otherwise consider aligning for visual consistency.
docs/LumexUI.Docs.Client/Components/Layouts/HomeLayout.razor (1)
14-14: Slightly simplify year renderingUsing DateTime.UtcNow.Year avoids string formatting and is culture-agnostic.
- © @(DateTimeOffset.UtcNow.ToString( "yyyy" )) LumexUI. All rights reserved. + © @DateTime.UtcNow.Year LumexUI. All rights reserved.docs/LumexUI.Docs.Client/Pages/Home/Examples/CookieSettings.razor (1)
16-26: Consider binding Value for interactivity in the docsAs written, the first switch is always true and not bound; it may not reflect user interaction in the example. Bind it to a local field to demonstrate two-way binding.
- <LumexSwitch Value="@true" - Color="@ThemeColor" - Classes="@_classes"> + <LumexSwitch @bind-Value="_strictlyNecessary" + Color="@ThemeColor" + Classes="@_classes">And in CookieSettings.razor.cs:
private bool _strictlyNecessary = true;docs/LumexUI.Docs.Client/Pages/Home/Examples/TeamMembers.razor.cs (1)
5-11: Make static readonly data truly staticRoles are constants and identical for every instance. Marking them static reduces per-instance allocations.
- private readonly Dictionary<string, string> _roles = new() + private static readonly Dictionary<string, string> _roles = new() { ["Viewer"] = "Can view and comment", ["Developer"] = "Can view, comment and edit", ["Billing"] = "Can view, comment and manage billing", ["Owner"] = "Admin-level access to all resources", };docs/LumexUI.Docs.Client/Pages/Home/Examples/ExampleComponentBase.cs (1)
18-21: Use InvokeAsync in the theme-changed handler (avoid renderer thread violations) and call base.OnInitialized()Subscribing
StateHasChangeddirectly can trigger “called from the wrong thread” exceptions in some hosting modes. Use a local handler that dispatches viaInvokeAsync, and unsubscribe the same handler inDispose. Also callbase.OnInitialized()for forward compatibility.Apply this diff:
protected override void OnInitialized() { - ThemeService.OnChanged += StateHasChanged; + base.OnInitialized(); + ThemeService.OnChanged += OnThemeChanged; } +private void OnThemeChanged() => InvokeAsync(StateHasChanged); + void IDisposable.Dispose() { - ThemeService.OnChanged -= StateHasChanged; + ThemeService.OnChanged -= OnThemeChanged; }Also applies to: 23-26
docs/LumexUI.Docs.Client/Pages/Home/Examples/Authentication.razor.cs (2)
7-9: [SupplyParameterFromForm] on a private non-parameter property is ineffectiveIn Razor components, [SupplyParameterFromForm] applies to component parameters. On a private non-[Parameter] property it does nothing (and can mislead future readers). If you’re using interactive EditForm binding, remove it; if you intend SSR form posts, make it a public [Parameter].
Option A (interactive form binding – remove the attribute):
- [SupplyParameterFromForm] - private Login LoginModel { get; set; } = new(); + private Login LoginModel { get; set; } = new();Option B (SSR form posts – make it a parameter):
- [SupplyParameterFromForm] - private Login LoginModel { get; set; } = new(); + [Parameter, SupplyParameterFromForm] + public Login LoginModel { get; set; } = new();Please confirm which flow you’re targeting (Interactive/SSR) so we can align the code-behind and markup accordingly.
12-12: Empty Submit handler — add TODO or wire up form eventsIf this is a stub for the docs sample, consider adding a TODO or wiring it to EditForm.OnValidSubmit to demonstrate a complete pattern.
docs/LumexUI.Docs.Client/Pages/Home/Examples/ActivityGoal.razor.cs (2)
32-38: Call base.OnAfterRenderAsync and keep firstRender logicMinor improvement for consistency and forward compatibility: call base before your first-render JS init.
Apply this diff:
protected override async Task OnAfterRenderAsync( bool firstRender ) { - if( firstRender ) + await base.OnAfterRenderAsync( firstRender ); + if( firstRender ) { await JSRuntime.InvokeVoidAsync( "charts.activity.initialize" ); } }
40-43: Clamp calories to [_minCalories, _maxCalories]UI may disable the buttons at limits, but clamping in code-behind prevents out-of-range state if methods are called directly or if limits change.
Apply this diff:
private void DecrementCalories() => SetCalories( -10 ); private void IncrementCalories() => SetCalories( +10 ); -private void SetCalories( int value ) => _caloriesDay += value; +private void SetCalories( int value ) + => _caloriesDay = System.Math.Clamp( _caloriesDay + value, _minCalories, _maxCalories );docs/LumexUI.Docs.Client/Services/DocsThemeService.cs (1)
15-19: Avoid redundant notifications when the theme color doesn’t changeEarly-out prevents unnecessary re-renders across all listening components.
Apply this diff:
public void SetThemeColor( ThemeColor themeColor ) { - _themeColor = themeColor; - NotifyThemeColorChanged(); + if( _themeColor == themeColor ) + { + return; + } + _themeColor = themeColor; + NotifyThemeColorChanged(); } public ThemeColor GetThemeColor() => _themeColor;Also applies to: 21-21
docs/LumexUI.Docs.Client/Pages/Home/Examples/PaymentMethod.razor (1)
16-38: Consider a default selection or binding for the radio groupFor a smoother demo UX, preselect one option (e.g., “card”) or bind to a backing property. This also ensures themed selected styles are visible immediately.
docs/LumexUI.Docs.Client/Pages/Home/Examples/TeamMembers.razor (2)
16-19: Add @key to stabilize list renderingUsing @key improves Blazor diffing and avoids unnecessary re-renders when items update.
- @foreach( var member in _members ) - { - <div class="flex items-center justify-between gap-4"> + @foreach( var member in _members ) + { + @key member.Email + <div class="flex items-center justify-between gap-4">
46-55: Optional: Wire role selection to update the member’s roleRight now the dropdown is static. If you want the example to feel more interactive, handle item click to change the role (and visually indicate the current role).
I can provide a small code-behind helper like
ChangeRole(Member member, string role)and attachOnClick="() => ChangeRole(member, title)"onLumexDropdownItem. Let me know if you want that implemented.docs/LumexUI.Docs.Client/Pages/Home/Home.razor (1)
3-3: Remove unused NavigationManager injection
NavigationManagerisn’t used in this component.-@inject NavigationManager NavigationManagerdocs/LumexUI.Docs.Client/Components/ThemeSelector.razor (3)
14-15: Avoid KeyNotFound if enum grows; add a safe accessorIf new ThemeColor values are added later, direct dictionary indexing could throw. Use a helper to provide a safe fallback.
- <span class="w-4 h-4 border border-default-900/10 rounded-full shrink-0 @_themeColorMap[color]" /> + <span class="w-4 h-4 border border-default-900/10 rounded-full shrink-0 @CssColorFor(color)" />- <span class="w-4 h-4 border border-default-900/10 rounded-full shrink-0 @_themeColorMap[color]" /> + <span class="w-4 h-4 border border-default-900/10 rounded-full shrink-0 @CssColorFor(color)" />Add outside the selected lines:
private string CssColorFor(ThemeColor color) => _themeColorMap.TryGetValue(color, out var cls) ? cls : _themeColorMap[ThemeColor.Default];Also applies to: 22-23
4-9: Optional: Provide a visible label for accessibilityYou set
LabelPlacement.Outsidebut there’s no label. Adding one improves a11y.<LumexSelect TValue="ThemeColor" LabelPlacement="@LabelPlacement.Outside" + Label="Theme" Value="@_themeColor" ValueChanged="@HandleThemeChange" Class="max-w-40">
28-54: Initialize from service so the selector reflects the current themeCurrently
_themeColordefaults toDefaulteven if the service holds a different value. Initialize on component load.Add outside the selected lines:
protected override void OnInitialized() { _themeColor = ThemeService.GetThemeColor(); }docs/LumexUI.Docs.Client/Pages/Home/Examples/ChatSupport.razor.cs (1)
13-18: [SupplyParameterFromForm] likely unnecessary hereFor an interactive component handling input via standard bindings, the attribute isn’t needed and can be confusing.
-[SupplyParameterFromForm] private ChatMessage MessageToSend { get; set; } = new() { SenderId = "Me" };docs/LumexUI.Docs.Client/Pages/Home/Examples/UserTable.razor (2)
73-80: Verify @OnClick:stopPropagation works on a component, not just HTML elements.Event modifiers are guaranteed on HTML elements. On components, they only apply if the component renders the event to the root element via AdditionalAttributes. Confirm that this stops row selection toggling; otherwise, bind an explicit no-op handler with the modifier on the underlying clickable element or use the grid’s row/action column APIs if available.
If needed, wire a no-op handler to ensure the modifier compiles and takes effect:
- <LumexButton IconOnly="@true" + <LumexButton IconOnly="@true" Size="@Size.Small" Radius="@Radius.Full" Variant="@Variant.Light" - @onclick:stopPropagation> + @onclick="() => {}" + @onclick:stopPropagation>
63-71: Guard Against Missing Status Keys in_statusMapIndexing
_statusMap[user.Status]will throw if a status isn’t in the map. To prevent runtime errors when an unknown (or null) status is encountered, update the markup to use a helper with a fallback value.Files to update:
• docs/LumexUI.Docs.Client/Pages/Home/Examples/UserTable.razor (around line 63)
• docs/LumexUI.Docs.Client/Pages/Home/Examples/UserTable.razor.csApply this diff in UserTable.razor:
- <LumexChip Size="@Size.Small" - Color="@_statusMap[user.Status]" + <LumexChip Size="@Size.Small" + Color="@GetStatusColor(user.Status)" Variant="@ChipVariant.Dot" Class="border-none">And add the helper to UserTable.razor.cs:
private ThemeColor GetStatusColor(string? status) => status is not null && _statusMap.TryGetValue(status, out var color) ? color : ThemeColor.Default;docs/LumexUI.Docs.Client/Pages/Home/Examples/ActivityGoal.razor (3)
18-25: Add accessible labels to icon-only buttons.Icon-only controls should expose an aria-label or title for screen readers.
- <LumexButton IconOnly="@true" + <LumexButton IconOnly="@true" Disabled="@(_caloriesDay <= _minCalories)" Size="@LumexUI.Common.Size.Small" Radius="@Radius.Full" Variant="@Variant.Light" OnClick="@DecrementCalories" - Class="size-7 min-w-7 border border-default-900/10"> + AriaLabel="Decrease daily goal" + Class="size-7 min-w-7 border border-default-900/10"> <MinusIcon Size="16" /> </LumexButton> - <LumexButton IconOnly="@true" + <LumexButton IconOnly="@true" Disabled="@(_caloriesDay >= _maxCalories)" Size="@LumexUI.Common.Size.Small" Radius="@Radius.Full" Variant="@Variant.Light" OnClick="@IncrementCalories" - Class="size-7 min-w-7 border border-default-900/10"> + AriaLabel="Increase daily goal" + Class="size-7 min-w-7 border border-default-900/10"> <PlusIcon Size="16" /> </LumexButton>Also applies to: 32-40
18-19: Don’t rely only on Disabled; clamp in code-behind too.Users can still trigger events via keyboard/automation, and state updates may race with UI. Clamp values in SetCalories to [_minCalories, _maxCalories].
Add this in ActivityGoal.razor.cs:
private void SetCalories(int value) { _caloriesDay = Math.Clamp(value, _minCalories, _maxCalories); }Also applies to: 33-34
43-45: Confirm CSS variable injection target (class vs style).The expression
class="h-20 @_chartCssVariables[ThemeColor]"assumes the CSS variable payload is a Tailwind arbitrary property class (e.g., [--var:value]). If the map contains raw CSS (e.g., "--var:value"), it must be applied via style instead.If it’s raw CSS, switch to:
- <div class="h-20 @_chartCssVariables[ThemeColor]"> + <div class="h-20" style="@_chartCssVariables[ThemeColor]">docs/LumexUI.Docs.Client/Pages/Home/Examples/UserTable.razor.cs (2)
13-16: Consider InvariantCulture for predictable filtering across locales.Using CurrentCultureIgnoreCase makes results locale-dependent. If you want consistent behavior regardless of user locale, switch to InvariantCultureIgnoreCase.
- result = result.Where( u => u.Name.Contains( _nameFilter, StringComparison.CurrentCultureIgnoreCase ) ); + result = result.Where( u => u.Name.Contains( _nameFilter, StringComparison.InvariantCultureIgnoreCase ) );
22-30: Use IEnumerable/IReadOnlyList instead of IQueryable for in-memory data.This data is purely in-memory and not provider-backed. Using IQueryable invites accidental expression tree pitfalls and can confuse readers. Prefer IEnumerable or IReadOnlyList.
- private readonly IQueryable<User> _users = new List<User>() + private readonly IReadOnlyList<User> _users = new List<User>() { // ... - }.AsQueryable(); + };And return IEnumerable from FilteredUsers:
- private IQueryable<User> FilteredUsers + private IEnumerable<User> FilteredUsersdocs/LumexUI.Docs.Client/Pages/Home/Examples/Authentication.razor (2)
53-58: Brand spelling nit: “GitHub.”Minor copy tweak to match the brand’s capitalization.
- <ChildContent>Continue with Github</ChildContent> + <ChildContent>Continue with GitHub</ChildContent>
64-67: Make the link actionable or clarify intent.LumexLink has no Href; it renders as non-navigable text. Provide a destination (e.g., “/signup”) or convert to a Button if it’s only illustrative.
- <LumexLink Color="@ThemeColor" - Class="@($"font-medium {(ThemeColor is ThemeColor.Default ? "text-foreground" : default)}")"> + <LumexLink Href="/signup" + Color="@ThemeColor" + Class="@($"font-medium {(ThemeColor is ThemeColor.Default ? "text-foreground" : default)}")"> Sign Up </LumexLink>docs/LumexUI.Docs.Client/Pages/Home/Examples/ChatSupport.razor (2)
85-93: Disable send on whitespace-only messages.Length check allows messages like " ". Prefer IsNullOrWhiteSpace.
- Disabled="@(MessageToSend.Content?.Length == 0)" + Disabled="@string.IsNullOrWhiteSpace(MessageToSend.Content)"
71-72: Optional: simplify chat message rendering by using the method group directlyThe
_renderMessagesfield is declared in ChatSupport.razor.cs (private readonly RenderFragment _renderMessages) and initialized in the constructor (_renderMessages = RenderMessages;). If you don’t need that indirection, you can remove the backing field and render via the method group:• In docs/LumexUI.Docs.Client/Pages/Home/Examples/ChatSupport.razor (line 71), replace
- @_renderMessages + @RenderMessages• In ChatSupport.razor.cs, remove the
private readonly RenderFragment _renderMessages;declaration and its assignment in the constructor.If you prefer keeping the explicit field, no changes are required.
src/LumexUI/Components/Tooltip/LumexTooltip.razor (1)
17-18: Avoid aria-expanded on tooltip triggers;@nullis a no-op and may not suppress internal attributes
- Setting
aria-expanded="@null"results in no attribute being rendered, and won’t reliably override anyaria-expandedthatLumexPopoverTriggermight add internally.- For tooltips, ARIA best practice is to use
aria-describedbyon the trigger pointing to the tooltip content’s id;aria-expandedis not appropriate for tooltips.If the intent is to not render
aria-expandedfor tooltips, remove it here and ensure the trigger usesaria-describedby(can be wired inLumexPopoverTriggerusing the content id). Apply this minimal change:- <LumexPopoverTrigger aria-expanded="@null" + <LumexPopoverTriggerOptionally add a bUnit test to prevent regressions (outside selected lines):
[Fact] public void TooltipTrigger_ShouldNotSetAriaExpanded() { var cut = Render(@<text> <LumexTooltip> <LumexTooltipTrigger> <LumexButton>Hover me</LumexButton> </LumexTooltipTrigger> <LumexTooltipContent>Tip</LumexTooltipContent> </LumexTooltip> </text>); var trigger = cut.Find("[data-popovertarget]"); trigger.HasAttribute("aria-expanded").Should().BeFalse(); }src/LumexUI/wwwroot/js/elementReference.js (1)
5-5: API rename aligned; consider exporting an alias to avoid breaking downstream interopImporting and exposing
portalis consistent with the dom util rename. To avoid breaking consumers that may callelementReference.portalTo, export an alias for a deprecation period.Apply this localized change:
export const elementReference = { getScrollHeight, - portal + // Temporary alias to avoid breaking consumers during transition + portal, + portalTo: portal }Also applies to: 17-18
tests/LumexUI.Tests/Components/Popover/PopoverTests.razor (1)
89-91: LGTM: test now targets the overlay close pathClicking
[data-popover-overlay]reflects the new behavior and the assertion remains valid. Optional: consider renaming the test to clarify semantics (e.g.,ShouldCloseOnOverlayClick) and add a complementary test ensuring tooltips do not render the overlay.Example additional test (outside selected lines):
[Fact] public void Tooltip_ShouldNotRenderOverlay() { var cut = Render(@<text> <LumexTooltip Open="@true"> <LumexTooltipTrigger> <LumexButton>Open Tooltip</LumexButton> </LumexTooltipTrigger> <LumexTooltipContent>Tip</LumexTooltipContent> </LumexTooltip> </text>); // No overlay expected for tooltips cut.FindAll("[data-popover-overlay]").Count.Should().Be(0); }src/LumexUI/wwwroot/js/components/popover.js (1)
25-26: Overlay query is non-blocking; potential race with renderYou await the popover, but overlay lookup is a synchronous query. If the overlay renders a tick later, it won’t be portaled and will remain in its original location. Consider an awaited lookup (with a bounded wait) for robustness.
Apply this diff to try portaling an overlay if/when it appears shortly after:
- const overlay = document.querySelector(`[data-popover-overlay=${id}]`); + let overlay = document.querySelector(`[data-popover-overlay=${id}]`); + if (!overlay) { + try { + overlay = await waitForElement(`[data-popover-overlay=${id}]`); + } catch { /* noop: overlay is optional */ } + }src/LumexUI/wwwroot/js/components/popover.bundle.js (1)
1644-1657: Debug logs in production bundleLeftover console.log statements in the outside-click handler pollute the console.
Apply this diff:
- console.log('hi'); if (!isInsideAny) { - console.log('hi 2'); elements.forEach(el => el?.dispatchEvent(new CustomEvent('clickoutside', { bubbles: true })) ); } ... - console.log('destroy');
| private readonly NavItem[] _navItems = new NavItem[] | ||
| { | ||
| new("/", "Docs"), | ||
| new("/docs/getting-started", "Docs"), |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify updated route exists and is linked correctly.
Ensure a page is bound to “/docs/getting-started” and that any previous "/" or "/docs" routes redirect appropriately.
Run this to confirm the route and find potential stale routes:
🏁 Script executed:
#!/bin/bash
# Confirm a page exists for /docs/getting-started and inspect related pages.
rg -nP -C2 '^\s*@page\s+"?/docs/getting-started"?\s*$' --type=cshtml --type=razor
rg -nP -C2 '^\s*@page\s+"?/(docs(/getting-started)?|)$' --type=cshtml --type=razorLength of output: 228
🏁 Script executed:
#!/bin/bash
# Confirm @page directives exist for /docs/getting-started and related routes
rg -nP -C2 '@page\s+"/docs/getting-started"' -g '*.razor' -g '*.cshtml'
rg -nP -C2 '@page\s+"/docs"' -g '*.razor' -g '*.cshtml'
rg -nP -C2 '@page\s+"/"' -g '*.razor' -g '*.cshtml'Length of output: 692
Missing /docs route redirect
- docs/LumexUI.Docs.Client/Pages/Getting Started/Overview.razor defines
- No @page "/docs" endpoint was found—requests to
/docswill 404 unless you add a redirect or stub page. - The root route (
/) is still served by Home.razor.
Please add a page or redirect for /docs (for example, a Razor component with @page "/docs" that forwards to /docs/getting-started) so old links don’t break.
🤖 Prompt for AI Agents
In docs/LumexUI.Docs.Client/Components/Header.razor around line 66, the app
links to "/docs/getting-started" but there is no "/docs" route, so requests to
"/docs" 404; add a new Razor component with @page "/docs" (e.g.,
Pages/Docs/Index.razor) that immediately redirects to "/docs/getting-started"
using NavigationManager.NavigateTo("/docs/getting-started", true) or an
equivalent server-side redirect, and ensure the new component is included in the
project so legacy "/docs" links resolve.
* 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 #128
This PR adds a Home page with examples demonstrating how different LumexUI components can be composed together.
What's been done?
Checklist
Additional Notes
Summary by CodeRabbit
New Features
Improvements
Bug Fixes