dotnet / AspNetCore.Docs

Documentation for ASP.NET Core
https://docs.microsoft.com/aspnet/core
Creative Commons Attribution 4.0 International
12.57k stars 25.3k forks source link

Consolidate and clarify guidance for Blazor component library authors into a single topic #31591

Open danroth27 opened 7 months ago

danroth27 commented 7 months ago

There are a lot of things to consider when building Blazor component libraries: different render modes, usability, customization, performance, etc. We have some guidance regarding these topics, but it is spread throughout the Blazor docs. We should have a single location to point Blazor component library authors to that consolidates all of our guidance. Proposed title: "Guidance for component library authors".

guardrex commented 6 months ago

@danroth27 ... Let's also consider how a new article will dovetail with (or replace if that's what you have in mind) the existing articles at ...

... which are currently SxS in the Components node. If we add one more, it makes sense to either nest them in a new node (folder) under Components or create a new Blazor root node (folder) for them outside of the Components node, either way probably under a "Component libraries" node name.

Here's what the current articles cover ...

Consume ASP.NET Core Razor components from a Razor class library (RCL)

BTW, almost no complaints ... er ... 🦖 death threats 💀😨😆 ... from readers over the years on this article. Folks seem ok with what's there thus far.

ASP.NET Core Razor class libraries (RCLs) with static server-side rendering (static SSR)

I'll need a bit more info to proceed ...

danroth27 commented 6 months ago

Will we add to existing articles or rearrange coverage with existing articles with a new article added?

I'd like to have a single place that summarizes all our guidance for component library authors. It could be a single article or an article with multiple sub articles. The consume components from an RCL article seems to be more targeted at component library users than authors, although there's certainly some stuff in there for library authors as well. The RCLs with static SSR article is definitely for component authors, just focused on one particular topic. I can imagine the RCLs with static SSR article being a child article of our component library authoring guidance.

Are we adding a node (folder)? ... and if so, where? ... and should it be called "Component libraries"?

I think it should be a TOC node under Components called "Guidance for library authors", or something like that.

For "render modes, usability, customization, performance, etc." ... What new guidance do we need to provide and/or where is the scattered information that you might be referencing? I don't see any mention of class libs/component libs in the Render Modes or Performance articles. I guess you're aware of subjects that we don't cover yet on those aspects ... is that right? If so, I'll need notes for the coverage.

Great question. We need to work with the team & community to decide what guidance should be included here.

A broad concern is that we want Blazor component to work in all Blazor apps and not be tied to a particular hosting model or render mode. This is critical for the health of the Blazor ecosystem.

It looks like the RCLs with state SSR article covers a lot of the render mode related guidance that I was thinking of. But the title seems a bit overly specific - it sounds like the article is just about static SSR, but it really covers a bunch of general render mode concerns. Could the title instead be something like "Handling render modes in component libraries" or something like that?

For performance, I think we have good content already for component library authors in the perf best practices article. Maybe we can just link to that?

Making component libraries trimmable is a topic I don't know that we cover anywhere, and I suspect it's often missed by component library authors. We have general guidance for .NET library authors on how to support trimming that we can link to, and there are some Blazor specific concerns as well, like the section we have on avoiding trimming js-invokable methods.

To improve usability, I think component library authors frequently don't know of all the mechanisms we have for dealing with their static web assets, like using JS initializers so that users don't have to manually add script tags.

For customization, we should guide component authors to support custom CSS styling by passing through additional attributes to the underlying HTML elements.

I'm sure there are accessibility concerns that we should provide some guidance on.

@SteveSandersonMS @javiercn @MackinnonBuck @halter73 @mkArtakMSFT What other guidance should we include here for Blazor component library authors?

rajsite commented 6 months ago

As a library author I'd love guidance on how to well set-up SCSS files in Nuget to be included and leveraged by Blazor users. Right now our Blazor users end up using CSS custom property names directly and lose out on compile time checking of SCSS property names.

Best we got was go find the files and copy and paste them in periodically and setup a bunch of stuff manually 🤷‍♂️ (so as you might guess no one does this 😔).

https://github.com/ni/nimble/tree/main/packages/nimble-blazor#using-nimble-design-tokens-cssscss

stsrki commented 6 months ago

People often ask us how to work with CSS isolation when they use our component library. Then, we need to teach them that once Blazor compiles, it needs to include a unique ID on an HTML element. That ID is later used as a CSS selector. This is not possible for component libraries that are already precompiled. So a guide that explains it would be great and we could point our users there.

rajsite commented 6 months ago

People often ask us how to work with CSS isolation when they use our component library. Then, we need to teach them that once Blazor compiles, it needs to include a unique ID on an HTML element. That ID is later used as a CSS selector. This is not possible for component libraries that are already precompiled. So a guide that explains it would be great and we could point our users there.

Yea, concretely as library authors we enable additional attributes so users can attach a class to the "inner component" that is the actual target.

Then library users need to use a ::deep selector to target the class applied to the "inner component" until an alternate is available for styling components. Other option is library users have to use inline styles for library components but normal isolated style for "native components" styling div, span, etc.

It's a stumbling block for everyone that the patterns of styling library components end up needing workarounds compared to "native".

KristofferStrube commented 6 months ago

For me, a central part of RCLs is JSInterop. Not all JS can be called directly using plain JSInterop Invoke calls, so we often need some extra JS functions to handle things like constructing JS classes and accessing attributes. Older libraries used a packaged file that the library's consumers had to add to the index.html or Host.cshtml page themselves. But since the introduction of IJSObjectReferences, we prefer to import the needed functions using the import function from within the library itself and then invoke the functions on that IJSObjectReference. This ensures that we don't pollute the global JS scope. I think this approach is not covered in the existing guidance for library authors.

mayur-ekbote commented 6 months ago

Based on the SO questions that have been asked - many devs struggle with choosing the correct state model. The approaches include:

  1. Routing (those who are used to consuming APIs)
  2. Using @ref directly - those coming more from WPF style architectures (notably, libraries like Syncfusion use this approach)
  3. Manipulating render trees directly!
  4. Overuse of JS Interop - people with strong JS background want to manipulate the DOM directly - but using Blazor.

Now, in the context of RCL - it would be good if some clear guidance is provided on state management of Blazor components. (I personally think that MVVM + event-based architecture is the cleanest and it avoids most of the pitfalls. It allows components to remain open for state modification but close for direct manipulation). Routine scenarios such as wizards, routing, in-component refresh on external notification etc - with clear examples of how the component state was initiated and manipulated would immensely help.

guardrex commented 6 months ago

Note an issue for coverage when this issue is worked ...

Reference to {ASSEMBLY NAME}.styles.css as the generated css file seems to be incorrect or incomplete https://github.com/dotnet/AspNetCore.Docs/issues/32148

I added a <PackageId></PackageId> to the project file and altered the name slightly from the {AssemblyName}. The output of the generated css file changed to the packageID name {PackageID}.styles.css while the output of project {AssemblyName}.dll remained the same.

Thanks, @ocdsoft!

sbwalker commented 5 months ago

When building component libraries one of the goals is to create components which function properly on all render modes. Further, it is highly beneficial if the component code does not need to rely on extensive conditional logic to determine which render mode is being used, as it makes the code much more difficult to test and maintain in the long term.

In order to write code which works on both render modes, it seems that static rendering is the lowest common denominator ie. if you write code for static rendering it will work on interactive rendering as well. This means avoiding interactive concepts such as "onclick", etc... and relying on primitive HTML form elements.

The problem is that form elements do not behave consistently when running on static vs interacticve render mode. For example #53993 provides a very simplistic example where a form with no inputs - just 2 simple buttons works fine on static render mode but does not work properly on interactive render mode.

The net effect of this inconsistency is that a component developer would need to include conditional logic based on render mode in their component in order to support multiple render modes - which is highly unfortunate.

guardrex commented 5 months ago

@danroth27 ... No new info came in via your pings. I can summarize thus far ...

Do you want to wait for feedback from the pinged team members, or shall I proceed to pull this together into a PR?

guardrex commented 5 months ago

BTW @danroth27 ... The team is doing it again 😄 (e.g., Razor components/Blazor components). A class lib that provides components in the docs has been described one way as a "Razor class library (RCL)" ... not a "Razor component library" that one hears around the Interwebs. It's hard to keep up with multiple names for the same things ... even confusing one could say.

Are we sticking with RCL naming, or do you want to change it, or do you want to try and apply both in some way? I suppose at a very minimum the new node overview can say right of the bat that the formal name of a library that provides components is an RCL and that a "Razor component library" is an unofficial name ... if that makes sense ... if my understanding is correct. It would be treated the way we handle "Blazor components." "Blazor components" isn't used anywhere except to say that it's an unofficial name, and we could do the same here.

guardrex commented 4 months ago

Noting another issue for possible coverage for when this issue is worked ...

Libs that target server and client and support pages/views (IF a valid scenario†) need to knock out the shared framework metapackage for the client. I think this is done (or one way that it's done) is via a MSBuild condition in the lib's project file ...

<ItemGroup Condition="'$(IsWasmProject)' != 'true'">
  <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

IF a valid scenario: I don't know if lib authors are deploying such libs and how else are they managing code in such libs that would only apply to server (e.g., the pages/views should also be knocked out along with the metapackage on WebAssembly).

If any component lib authors here are aware of this and/or other methods for dealing with that scenario ... or even if this type of lib isn't a valid scenario for their users/customers, I'm 👂 here for feedback. I'm rolling that example into the Class libraries article now in the section on generating the RCL with Support pages and views checkbox enabled, along with a few other changes to support components in such libs (https://github.com/dotnet/AspNetCore.Docs/pull/32458).

Generally ... broadly WRT this scenario, we haven't stated thus far in our list of items to cover in the new Guidance for library authors article that dealing with targeting server vs. WebAssembly is a potential area for coverage. This example for pages/views + Razor components is one example, and there are probably other scenarios to consider.