Styles meant for web apps can be easily shared with native mobile/desktop apps—developers also have the flexibility to tinker with styling on the fly.
MAUI UI July is a month-long community-driven initiative run by Matt Goldman, where anyone gets to share enthusiasm and passion for .NET MAUI. What’s not to like about fresh developer content every day of July? This article is a proud part of MAUI UI July to celebrate developer excitement around .NET MAUI.
Modern cross-platform mobile/desktop apps often strive for delightful UX, and beautifully styled UI is one way developers can achieve the goal. But the web is also ubiquitous. Maybe you have been building modern web apps with Blazor—perhaps styling them with Cascading Style Sheets (CSS) from a custom design system or with accessibility/responsiveness in mind.
Can the styles used for web apps be brought over and shared with native mobile/desktop apps made with .NET MAUI? The answer is yes—modern developer frameworks and tooling make sharing styles rather easy between web and native apps. Let’s explore.
.NET MAUI is the evolution of modern .NET cross-platform development stack, allowing developers to reach mobile and desktop form factors from single shared codebase. While .NET MAUI is squarely meant for developers to build native mobile/desktop apps, armed with modern smart WebViews, .NET MAUI is more than capable to welcome web content to native land. In fact, Blazor/JavaScript developers should feel empowered to bring web UI components, routing, styling and more to native cross-platform .NET MAUI apps, while gaining complete native platform API access.
Native .NET/C# developers will likely write the visual tree of .NET MAUI apps with XAML—the flexible markup language with powerful functionality built in. While for a niche audience, styling XAML visual trees in .NET MAUI apps with CSS has been an option for quite some time now—this enables sharing of styles across web/native apps. Though there are limitations, the basics work and there are even some .NET MAUI–specific CSS styling paradigms.
Let’s start up a vanilla .NET MAUI app—the default project templates pack quite a punch out of the box. While platform specific customizations are possible with a multi-targeted solution, the real benefit is a true single shared project—fonts, images, splash screens and other resources can be shared across iOS, Android, Windows, macOS and Tizen.
One of the nice things about .NET MAUI is shared styles across most of the UI components in use—templates drop extensive XAML styles and default colors. These styles work seamlessly across platforms, but can also be customized easily from one spot.
Now, let’s drop in a CSS file in our .NET MAUI project—in reality, this could be a shared style already used in a companion web app.
Once the CSS file’s Build Action is set to MauiCss
, the style file can be easily referenced inside individual XAML views as below or globally at the app level.
<ContentPage.Resources>
<StyleSheet Source="/Resources/Styles/CSSStyles.css" />
</ContentPage.Resources>
Now, let’s take a look at styling the XAML markup inside the default MainPage.xaml
. Everything is nicely stacked inside a VerticalStackLayout
and we can add a few StyleClass
attributes to individual UI elements we want to style with CSS.
<ScrollView>
<VerticalStackLayout
Padding="30,0"
Spacing="25">
<Image
Source="dotnet_bot.png"
HeightRequest="185"
Aspect="AspectFit"
SemanticProperties.Description="dot net bot in a race car number eight" />
<Label
Text="Hello, World!"
Style="{StaticResource Headline}"
SemanticProperties.HeadingLevel="Level1" />
<Label
Text="Welcome to .NET Multi-platform App UI"
StyleClass="MySpecialLabel"
Style="{StaticResource SubHeadline}"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I" />
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Fill" />
</VerticalStackLayout>
</ScrollView>
One thing to keep in mind—there may be conflicting style directives between XAML styles and CSS, both trying to style a certain UI element. For CSS to win, you may have to comment out specific XAML styles.
Now, let’s write some CSS to style specific XAML UI in our default .NET MAUI page.
verticalstacklayout {
background-color: lightpink;
}
verticalstacklayout label {
background-color: lightblue;
}
.MySpecialLabel {
background-color: tan;
}
#CounterBtn {
background-color: yellow;
}
And we can fire up our .NET MAUI app to run on mobile/desktop platforms to see the CSS styling take effect, as below.
As evident, most CSS techniques also work when styling XAML, like parent-child relationships, regular visual tree selectors, named or class identifiers and more—sadly, no CSS variables. It is encouraging to see wide propoerty support and even some .NET MAUI specific styling. While XAML styling with CSS has been around for some time, it is definitely for a niche audience—still heartwarming to see web styles being able to be reused as is for native .NET MAUI apps.
Blazor is eating the world. Blazor is the free, open-source and much beloved web framework for building modern web apps. Developers can leverage the power of modern .NET, C# front and back, and modern tooling to build interactive beautiful web apps.
The Blazor component model, rendering engine and styling mechanisms offer flexibility—and all of Blazor goodness is also welcome on native mobile/desktop apps through .NET MAUI. If using Telerik UI for Blazor components for web apps, all of the UI also works on hybrid mobile/desktop apps.
Let’s spin up a new Blazor Hybrid app with the default template. This is essentially dropping Blazor UI components/layouts and web app bootstrapping right inside a .NET MAUI native app project.
Blazor Hybrid apps work by leveraging a modern WebView component inside a .NET MAUI app—a smart abstraction that borrows a browser instance from the host OS to render web UI. The WebView only renders web UI and styles—Blazor and .NET MAUI share the common .NET runtime, running on the metal on the mobile/desktop device. Given a BlazorWebView
, Blazor can take over rendering inside—including Blazor UI components, styles, routing and navigation between components.
<BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type local:Components.Routes}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
Guess what else the Blazor Hybrid .NET MAUI app projects include—yep, CSS style files in the wwwroot
folder. This is referenced from index.html
and should be immediately familiar to web developers.
Editing CSS for Blazor Hybrid apps should be the familiar experience, with intelligent help from Visual Studio IDE or VS Code editor. Let’s change the color of the Blazor button in the default template to the beloved .NET purple—just because it is now part of a native cross-platform .NET app.
We can run our Blazor Hybrid app as a mobile/desktop app, on device or through simulators—CSS styles are happy to style Blazor UI components as expected.
While it is nice that CSS styles from Blazor/web apps can be dropped directly inside .NET MAUI Blazor Hybrid apps, this may not be a realistic solution for many. The same CSS styling now has be maintained in two places—one for web apps and one for mobile/desktop apps. A much cleaner solution is to truly share common CSS styling across web and native apps—one style that drives UI/UX across browser and native mobile/desktop apps.
The trick to pull off true style sharing is abstraction—the common web UI components/styles cannot belong inside either the Blazor project for web apps or the Blazor Hybrid project for native apps. Instead, a simple Razor Class Library (RCL)
can house the web UI/styles and this project is referenced from both web and native apps. The huge benefit is one set of web UI components and their corresponding CSS styles can drive the UI across web and native experiences—platform customizations are easy with varied Interface implementations.
While .NET MAUI/Blazor developers can pull off such shared UI manually today, this will get a whole lot easier with .NET 9 runtime. The .NET MAUI for .NET 9 Preview bits now include a brand new Blazor Hybrid + Web project template—this works exactly as desired.
The template spins up a few projects—a native .NET MAUI Blazor Hybrid app, a Blazor web app and a Razor Class Library project that houses shared UI/styles. Code and style sharing between web and native apps is great for developer productivity—good to see frameworks/tooling evolve to provide such flexibility.
Being able to style UI in Blazor Hybrid native apps with CSS is pretty cool. But more needs to be done to include web developers so they are comfortable with CSS styling of native apps. No matter the web development stack, anyone working with CSS needs one indispensable tool—the browser developer tooling. Modern evergreen browsers like Chrome/Edge/Firefox, etc. all include robust developer tooling. The benefit is being able to pull up web source code, inspect UI elements, debug JavaScript and, most importantly, tinker with CSS on the fly.
It can sometimes be tricky to get CSS styling to be perfect—developers/designers love the flexibility to change up styling on the fly while the app is running and seeing the changes implemented immediately. Since .NET MAUI Blazor Hybrid apps essentially sport web UI components, would it not be cool to be able tinker with CSS on the fly with browser developer tools while the native app is running? The answer is yes—otherwise we would not be talking about it.
Let’s look at a sample MauiProgram.cs
file that bootstraps a .NET MAUI Blazor Hybrid app.
using Microsoft.Extensions.Logging;
namespace MauiBlazorHello;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
With dependency injection built in, we’re bringing in the obvious service component BlazorWebView
—this is the cross-platform WebView abstraction that provides the canvas for rendering web UI/styles.
With a compiler directive, we can add an additional service just for Debug mode—the BlazorWebViewDeveloperTools
. As the name suggests, this one liner makes the BlazorWebView content available to browser developer tools—for developers to tinker with HTML/CSS/JavaScript exactly like on the web through a web browser. The differences is web UI/styles are now rendered inside a native cross-platform mobile/desktop app. Every aspect of it is inspectable through browser developer tools.
The dependency addition of BlazorWebViewDeveloperTools should be enough to inspect web content for .NET MAUI Blazor Hybrid apps for Android and Windows. Developers will get Chrome developer tools for Android target and Edge developer tools for Windows desktop target. Apple’s walled garden requires a little more configuration for iOS and macOS targets. Granted MainPage.xaml
is the page hosting the BlazorWebView, here’s a little code for the code-behind file.
namespace MauiBlazorHello;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, EventArgs e)
{
#if MACCATALYST
WebKit.WKWebView view = (WebKit.WKWebView)blazorWebView.Handler.PlatformView;
view.SetValueForKey(Foundation.NSObject.FromObject(true) , new Foundation.NSString("inspectable"));
#endif
}
}
Essentially for iOS/MacCatalyst, we can tap into the corresponding WebView (WKWebView/WebKit) and add a permission on the fly to make the WebView content inspectable by browser developer tools. With the added configuration, Safari can see that web content is being rendered inside a WebView running within a native macOS app. The URL is immaterial since the app is essentially local.
Developers can launch Safari developer tools and be able to inspect every aspect of web UI rendered within the WebView of a .NET MAUI Blazor Hybrid app—HTML UI elements, CSS styles, selectable visual tree, JavaScript, local storage and more. Developers/designers can tinker with CSS styles to their heart’s content to style web UI—just rendered with a WebView inside a native cross-platform .NET MAUI app.
.NET MAUI is the evolution of modern .NET cross-platform development stack, allowing developers to reach mobile and desktop form factors from a single shared codebase. And Blazor is the free, open-source and much beloved .NET web framework for building modern web apps. Blazor Hybrid apps marry up the two technologies beautifully, allowing Blazor web UI components and styles to be reused within native mobile/desktop apps.
Sharing CSS styles between web and native apps would be an obvious benefit for developers. While for a niche audience, web CSS styles can be used to style native XAML visual tree UI within .NET MAUI mobile/desktop apps. Blazor Hybrid apps obviously allow CSS styling of web UI components, but new project templates in .NET 9 truly enable sharing CSS styles between web and native apps. A common shared UI class library can host web UI components and their corresponding CSS styles—powering customizable experiences across platforms.
Modern frameworks and tooling enable sharing of web styles in native apps—developer productivity and code sharing for the win.
Sam Basu is a technologist, author, speaker, Microsoft MVP, gadget-lover and Progress Developer Advocate for Telerik products. With a long developer background, he now spends much of his time advocating modern web/mobile/cloud development platforms on Microsoft/Telerik technology stacks. His spare times call for travel, fast cars, cricket and culinary adventures with the family. You can find him on the internet.