Conversation
release: Components v3.9.4
Had to add this to my imports to get MainLayout to pickup `BpPortalHost`
Update README with out of box working missing code
…ui#291) * feat: add BbComboboxGroup for grouped combobox items Add BbComboboxGroup component that wraps BbCommandGroup to support labeled, filterable item groups in compositional mode. Groups automatically hide when search filters out all their items. Label styling is customizable via LabelClass parameter. * docs: update changelog for 2026-04-06 and 2026-04-07
release: Components v3.9.5
…lazorblueprintui#296) The form field wrapper unconditionally wired a local SearchQueryChanged handler to the inner BbCombobox, which made BbCombobox's auto-detection (`SearchQueryChanged.HasDelegate`) flip into external-filtering mode and install PassthroughFilter — bypassing the internal text filter entirely. The symptom was most visible inside an EditForm with a validator, where the extra re-renders also reset BbCommandInput's display value. Only wire the interception handler (and forward SearchQuery) when the consumer is actually binding SearchQueryChanged, matching the pattern used by BbFormFieldMultiSelect.
…96-combobox-search-filter fix: BbFormFieldCombobox search filter bypassed when wrapper is used (blazorblueprintui#296)
release: Components v3.9.6
…orblueprintui#293) Drops the redundant outer wrapper div and passes class + AdditionalAttributes straight to the Tabs primitive, matching the Accordion/Dialog wrapping pattern. Previously attributes like id and data-* set on BbTabs never reached the primitive's root element.
…93-bbtabs-additional-attributes fix: BbTabs forwards AdditionalAttributes to primitive root (blazorblueprintui#293)
fix: update source property from Avatar component
…eprintui#311) Adds flex-wrap to the filter condition row and to the inner range/InLast sub-rows so the fixed-width selects/inputs wrap onto multiple lines instead of overflowing the parent card on mobile widths.
…eprintui#309) The toolbar's Filter popover shipped a "to be implemented in Phase 2" placeholder UI that consumers had no way to disable. Removed the popover and the unused DataTable.Filter / DataTable.FilterColumns localization keys. Updated XML docs to describe Filterable as scoping the global search and to point users at BbDataGrid for per-column filter UIs.
Wraps the Tailwind utility output and authored component styles inside a dedicated `bb` cascade layer declared as the strongest layer: @layer properties, theme, base, components, utilities, bb. Component utilities like .md:flex on the sidebar now win the cascade regardless of which <link> tag a consumer's app.css loads in relative to ours, fixing the class of bug seen in blazorblueprintui#308 once and for all. @media (prefers-reduced-motion) and @Keyframes stay at top level so they remain authoritative. tw-animate.css is imported unlayered because it uses @Utility (a Tailwind v4 source directive that cannot be nested inside a layer() import); its animation utilities land in the standard utilities layer. Consumer override path: !important (which inverts layer priority) or authored unlayered CSS.
…ss-cascade-and-datatable-cleanup fix: scope component CSS in @layer bb + DataTable / FilterBuilder polish
release: Primitives v3.10.0 + Components v3.10.0
- Pin SDK to 8.0.421 via global.json so newer SDKs (9/10) don't break the Razor source generator on DashboardGridDemo - Mark auto-generated icon data files (Feather/Heroicons/Lucide) with // <auto-generated/> + #nullable enable so analyzers (CA1859) skip them - Fix CA1861 in DataGridHierarchyDemo by lifting inline string[] to a static readonly field - gitignore tailwindcss-macos binary alongside the existing Windows/Linux entries
v3.10.0 wrapped Tailwind's entire output in @layer bb to make BB component utilities win the cascade against consumer Tailwind output (fixing blazorblueprintui#308). The side effect was that consumer-applied utilities like grid-cols-* and col-span-* were also being defeated by BB's emitted utilities, squishing grid layouts on consumer sites and on our own docs. Drop `layer(bb)` from the Tailwind import so BB's bulk output now lives in Tailwind's native layers and ties with the consumer's at parity -- specificity and DOM order decide as normal. Keep @layer bb as the priority layer for BB's hand-authored attribute-selector component rules, which is what it should have been from the start. To preserve the blazorblueprintui#308 sidebar fix without relying on layer-wide priority, add a higher-specificity authored rule inside @layer bb: aside[data-variant][data-side] { display: none; } @media (min-width: 768px) { aside[data-variant][data-side] { display: flex; } } Specificity (0,2,1) beats class-based .md\:flex (0,1,0) regardless of which layer it lands in, so the sidebar's mobile/desktop split is now authoritative even if a consumer's app.css emits a competing .hidden or .md:flex rule. Verified against Kevin Mallinson's blazorblueprintui#308 repro repo with project refs to the patched library, and against the squished-grid scenario on the local demo site. Likely also resolves blazorblueprintui#314 (same root cause).
_Imports.razor compiles into a public ComponentBase-derived class as a build-time Razor convention; it is not a real API surface. Whether it gets included in the assembly's exported types was effectively a coin flip across .NET SDK versions, which broke the snapshot tests when building on macOS with .NET 8 SDK 8.0.421 even though no real public API had changed. Filter underscore-prefixed type names out of the component scan so the snapshot reflects designed APIs only.
…ueprintui#316) BbToastProvider always renders its toast container div, even with zero toasts. The container is position:fixed, pinned bottom-0 right-0, up to 420px wide, with z-[100] and p-4 padding — so the empty box sat over the bottom-right corner of every page and swallowed all clicks there (reported eating the bottom half of the "Send" button in the chat demo). Add pointer-events-none to the container so clicks pass through it. BbToast already carries pointer-events-auto on its own root, so toasts themselves stay fully interactive — the container half of that pattern was simply missing. Fixes both the main stack and per-toast position containers, which share GetContainerCssClass.
…52-copytext-tooltip-clipping fix(copytext): render the tooltip through the portal so containers can't clip it
The text-input family set focus-visible:outline-none (or focus:outline-none) and drew nothing in its place — the browser's native ring was removed and never replaced, so a focused input looked identical to an unfocused one. A WCAG 2.4.7 failure, and per discussion blazorblueprintui#355 the reason three people picked a different library. An omission, not a decision: BbButton, BbCheckbox, BbSwitch, BbTabs, BbToggle, BbSlider, BbCalendar, BbRadioGroupItem and BbPaginationLink had all along used the ring-2/ring-ring/ring-offset-2 pattern. It now applies to the input family too. BbTextarea was a third case — it indicated focus by tinting the border via focus-visible:border-ring — and now matches. BbDrawerItem, BbDatePickerInput and BbResponsiveNavTrigger had the ring but not the offset. Left deliberately alone: sidebar components ring in ring-sidebar-ring, their own theme token, and skip the offset so it doesn't bleed into adjacent rows; event-calendar chips skip it for density; BbBubbleContent keeps its softer inline treatment; overlay containers and roving-focus menu items keep outline-none, being focused programmatically or indicating state through data-[highlighted]. Verified in a browser: computed box-shadow goes from none to a 2px offset plus 2px ring in the theme colour, on both BbInput and BbTextarea.
The library's form rows leave a 3px gap between a label and its control, while ring-offset-2 extends 4px — so the offset ring overlapped the label by a pixel on every labelled field. Measured across 11 labelled textareas: all 11 overlapping before, none after. Drops ring-offset-2 from the input family only. Buttons, checkboxes, switches, tabs, toggles, sliders and the calendar keep theirs — they are usually standalone with margin around them, whereas inputs sit in labelled rows with tight vertical rhythm. shadcn/ui splits it the same way. Still a clear WCAG 2.4.7 pass: a 2px ring in the theme colour against the control's border.
…55-input-focus-styling fix(a11y): give inputs a visible focus indicator
…d-grid-disposed-ref-callbacks fix(DashboardGrid): stop stale JS->.NET callbacks to disposed grids
…dicators Both bugs this session were omissions in markup, not logic errors, so unit tests of the behaviour would have passed while the defects remained. These scan component source as text instead. CultureInvariantStyleTests — flags a numeric value interpolated into an inline style without InvariantCulture. Razor renders a bare @value with CurrentCulture, so a comma-decimal locale produces invalid CSS that the browser discards silently. That shipped twice, in BbColorPicker (blazorblueprintui#436) and BbRangeSlider (blazorblueprintui#443) a fortnight apart; the second was only found by sweeping the tree by hand. FocusIndicatorTests — flags a component that removes the focus outline without drawing a replacement, a WCAG 2.4.7 failure that the whole input family carried until blazorblueprintui#457. Accepts any recognised affordance, not just a ring: menu items indicate focus with a background change, which is correct for a roving-focus list. Overlay panels are exempt by suffix since they are focused programmatically. A short allowlist covers the rest, each entry carrying a reason, and a second test fails if an entry stops being needed — a stale exemption hides a regression. Verified by mutation: reintroducing the RangeSlider interpolation and removing BbInput's ring each turn the relevant test red. No new dependencies; these read the working tree, so SourceTree locates the repo root by walking up from the test assembly.
…convention-guards test: add convention guards for culture-invariant styles and focus indicators
BbScatter, BbLine and BbArea emitted a flat list of Y values, which leaves ECharts to derive each point's X from its index. That is correct for a category axis and wrong for a value axis, which ignores the data handed to it — so X values passed to BbXAxis via DataKey were dropped and points were plotted against ordinal position regardless. The two halves hid each other: the axis looked configured, the labels came from the right property, and the result was a Y-over-position plot wearing X's labels. No arrangement of the existing parameters produced a genuine X:Y plot, including the one advertised in BbScatter's own XML documentation. Add XDataKey to the three cartesian series, emitting explicit [x, y] pairs through a shared SeriesBase.GetPointData helper. Unset, behaviour is unchanged — the flat list is still emitted, so categorical charts and the composite bar-plus-scatter overlay are untouched. Rows missing either coordinate yield a null entry rather than a partial pair, which ECharts would render against a coerced zero. The list stays parallel to the source data rather than compacted, because SymbolSizeKey zips against it by index; compacting would misassign every bubble size after the first gap. BbBar is deliberately excluded: its HasNegativeValues/BuildPerItemData path inspects each datum as a scalar and would misread a pair. BbCandlestick and BbHeatmap already build their own pair data. Also add BbXAxis.Scale, mirroring the existing BbYAxis.Scale. A value axis includes zero by default, so the first genuine numeric X axis put heights of 160-190 in the last sixth of the plot. Opt-in on both axes, since suppressing zero exaggerates small differences. Refs blazorblueprintui#439
…39-scatter-xy-datakey fix(charts): plot scatter, line and area against a real X value
….982 The 9.1 beta pin was deliberate but temporary. The reason for being on 9.1 is unchanged: the 9.0.x stable line hard-pins AngleSharp to exactly [0.17.1], which carries GHSA-pgww-w46g-26qg, and an exact pin cannot be lifted from here without NU1608 — a hard error under TreatWarningsAsErrors. What changed is that 9.1 now has a stable release, so that fix no longer costs a prerelease. HtmlSanitizer and AngleSharp.Css were both prerelease transitive dependencies of a published package, which resolves fine for consumers but can trip supply-chain policies that ban prereleases. Resolved graph is now HtmlSanitizer 9.1.982, AngleSharp 1.7.0 and AngleSharp.Css 1.0.1 — no prerelease, no vulnerable package. Sanitiser output compared across the same 41 inputs used to verify the original move (XSS vectors, Quill markup, Markdig output, malformed and non-ASCII HTML): byte-identical between 9.1.949-beta and 9.1.982, with no executable vector surviving either. The library only touches new HtmlSanitizer() and Sanitize(string), both unchanged. Closes blazorblueprintui#426
…/426-htmlsanitizer-stable chore(deps): move HtmlSanitizer off the prerelease line to stable 9.1.982
…lture
Focusing a BbCurrencyInput multiplied its value by 100, compounding on
every visit: 999,99 came back as 99.999,00.
Three steps read three different cultures for the same value:
FormatCurrency / TryParseValue -> the currency's culture (EUR = de-DE,
comma decimal, dot group separator)
JsOnFocus -> invariant, so the field showed 999.99
GetJsConfig -> the ambient culture's separator
On Blazor Server the ambient culture is the server's, not the user's, so
the sanitiser was told "dot" for every visitor regardless of locale and
left the dot in place. On blur TryParseValue read 999.99 back through
de-DE, stripped the dot as a group separator, and parsed 99999.
Each step was individually defensible; they just disagreed about what a
dot meant. Resolve all three through the currency's culture so the string
written on focus is the string the parser expects on blur.
Not locale-specific to the reporter: any currency whose culture disagrees
with the server's on the decimal separator was affected.
Verified across USD, EUR, JPY (zero decimal places) and a currency using
the Arabic decimal separator, each stable across repeated focus/blur.
Refs blazorblueprintui#438
Value had to be a string already in hand, so text only known at copy time — derived from state that moves, or expensive enough that computing it every render is wasted — had to be recomputed on each render and held in a field. ValueFunc is a Func<string?> evaluated when the user copies. Naming follows the convention already used for this shape (CellClassFunc, DayClassFunc). Value still wins when set. The precedence test is emptiness rather than null: Value is a string, and a bound-but-unset one reaches the empty string naturally, which a literal first-non-null reading would let win and silently copy nothing. Value loses [EditorRequired] — supplying only a func is now a complete configuration, and the analyzer would otherwise nag everyone who does. Resolved once per click rather than per render, so an expensive func is not called speculatively and OnCopied cannot report a different string from the one placed on the clipboard. The async counterpart is deliberately excluded: awaiting a consumer task before writing spends the transient user activation clipboard writes require. Tracked in blazorblueprintui#466. Refs blazorblueprintui#453
…38-currencyinput-culture-split fix(currency-input): resolve focus, sanitise and parse through one culture
…453-copytext-valuefunc feat(copy-text): add ValueFunc for copy-time value resolution
…nstant ButtonClass was static, so it could not observe anything about the instance — including Class, which is the only way to size this component since it has no size parameter. Each button was h-5, and two of them summed to exactly the input's default h-10. The pieces lined up by coincidence rather than by construction, so the mismatch only surfaced once a consumer changed the height: Class="h-8" shrank the field to 32px through TailwindMerge while the stepper stayed at 40px, overhanging it at both ends with the rounded corners no longer meeting the input's border. Buttons now take half the stepper column via flex-1, and the row is items-stretch rather than items-center, so the column sizes itself from the field whatever sets that height. min-h-0 lets them shrink past the chevron's intrinsic height. BbCurrencyInput shares the markup shape but renders a currency symbol rather than stepper buttons, so it is unaffected. Refs blazorblueprintui#465
…65-numericinput-stepper-height fix(numeric-input): size the stepper buttons from the field, not a constant
The ValueFunc demo added in blazorblueprintui#469 used a ButtonSize value that does not exist, breaking the build of every demo host. It got through because the demo build output was piped through tail -2, which shows MSBuild's elapsed-time line on failure as well as success, and the server was then started with --no-build — so the browser check ran against the previous build rather than this markup. Verified this time by exit code across all three demo hosts (Server, Wasm and Auto) rather than by tail, and by exercising the demo in a browser: the button renders at 36px, cycling the environment updates the copied value, and copying reaches the "Copied!" state.
The ValueFunc demo added in blazorblueprintui#469 used a ButtonSize value that does not exist, breaking the build of every demo host. It got through because the demo build output was piped through tail -2, which shows MSBuild's elapsed-time line on failure as well as success, and the server was then started with --no-build — so the browser check ran against the previous build rather than this markup. Verified this time by exit code across all three demo hosts (Server, Wasm and Auto) rather than by tail, and by exercising the demo in a browser: the button renders at 36px, cycling the environment updates the copied value, and copying reaches the "Copied!" state.
BbCopyText tooltips stayed on screen once shown and accumulated one per hover; a later scroll then scattered them over unrelated text, because each sits at the coordinates it was given. The failure is at the boundary between Blazor's rendering and JS's. SetupPositioningAsync shows content by calling applyPosition(makeVisible: true), which writes visibility, opacity and pointer-events onto the element with !important — the style attribute belongs to JS from then on. Blazor diffs against what it last rendered, not against the DOM, and its markup for a closed portal is byte-identical to the markup it rendered before the portal opened. So on close it correctly concludes nothing changed, emits no style update, and the element keeps the visible values JS left behind. Nothing in the close path was broken: isHovered reached false, the component re-rendered, data-state flipped to "closed", the auto-update subscription was disposed, and a MutationObserver recorded no further writes. The style simply had no owner willing to reset it. Add IPositioningService.HidePositionAsync, mirroring GetInitialStyle()'s hidden state, and call it from HideAsync — the single teardown both the ForceMount and standard lifecycles route through. Hiding is now symmetric with showing. Components whose markup changes elsewhere while open — BbTooltip among them — were never affected: any other difference gives Blazor a reason to re-emit the attribute. That is why this looked component-specific rather than structural.
…ement Opening a popover and pressing Escape left it open, and the trigger could not be clicked to recover either: BbPopoverTrigger sets pointer-events: none on itself while open to guard against double-toggling. Clicking elsewhere was the only way out, and from the keyboard there was none. CloseOnEscape has always defaulted to true and its handler was correct — it just never ran. It is bound with @onkeydown on the content element, which only sees the key once focus is inside the popover, and opening one does not move focus. Focus stays on the trigger, which sits in a different part of the DOM now that content renders through the portal, so the keydown bubbled nowhere near the handler. Watch for Escape at the document while open, using the onEscapeKey helper already present in click-outside.js and until now unused. Registered alongside the click-outside listener and torn down with it, taking ownership of the field before the first await so a close racing a teardown cannot double-dispose (blazorblueprintui#441). The element-level handler stays. The document listener fires on bubble, so where focus genuinely is inside the popover the original handler runs first and this one finds the popover already closed. BbSelect was never affected — it routes Escape through its keyboard-navigation module, which is document-level for the same reason.
…opytext-demo-buttonsize fix(demo): correct ButtonSize.Sm to ButtonSize.Small in CopyText demo
…opytext-tooltip-stuck fix(overlays): stuck CopyText tooltips and a popover that Escape could not close
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Hi,
I'm not sure if you want to include something like this
but I've created a simple demo desktop application based on blazor blueprint
It's using PhotinoX / PhotinoX.Blazor to show it's use as a desktop app
So far I've tried it on Windows and Linux
It's targeting dotnet 10 with it's own solution since they changed the way asset paths are handled a bit between dotnet 8 and 10 I think