Harmony.Mobile.UI (0.2.14)
Installation
dotnet nuget add source --name antoine --username your_username --password your_token https://git.luxit.be/api/packages/antoine/nuget/index.jsondotnet add package --source antoine --version 0.2.14 Harmony.Mobile.UIAbout this package
Harmony mobile UI library — iOS-styled Blazor components, shell (header, tab bar, page transitions, sheets), services (haptics, navigation stack, sheet host) and Tailwind theme preset. Designed to be consumed by Blazor WebAssembly mobile apps (PWA or MAUI Hybrid).
Harmony.Mobile.UI
iOS-styled Blazor component library for Harmony mobile apps (Blazor WebAssembly PWA or MAUI Hybrid). Provides primitives (Avatar, Card, Icon, List, Sheet, Toggle, …), a complete mobile app-shell (sticky header, bottom tab bar with editor, page transitions, bottom-sheet host), UI services (haptics, navigation stack, sheet host, header state) and a Tailwind 4 theme preset with iOS design tokens.
This package is the first consumable piece of the Harmony platform — it is designed to be reused across all Harmony mobile projects (current and future).
Installation
<PackageReference Include="Harmony.Mobile.UI" Version="0.1.0" />
Configure the Gitea NuGet source in nuget.config at the solution root:
<configuration>
<packageSources>
<add key="gitea-luxit" value="https://git.luxit.be/api/packages/antoine/nuget/index.json" />
</packageSources>
<packageSourceCredentials>
<gitea-luxit>
<add key="Username" value="%GITEA_USER%" />
<add key="ClearTextPassword" value="%GITEA_TOKEN%" />
</gitea-luxit>
</packageSourceCredentials>
</configuration>
Generate a Gitea access token at https://git.luxit.be/user/settings/applications with scopes read:package + write:package, then export GITEA_USER=<your-gitea-login> and GITEA_TOKEN=<token> in your shell.
Wire-up (Blazor WebAssembly)
1. DI
using Harmony.Mobile.UI.Services;
builder.Services.AddHarmonyMobileUI(nav =>
{
nav.RootTabs = new() { "/", "/search", "/profile" };
nav.ModalPrefixes = new() { "/feed/", "/messages/" };
nav.Labels = new()
{
["/"] = "Home",
["/search"] = "Search",
["/profile"] = "Profile",
};
nav.FallbackLabel = "Back";
});
To integrate with your own "enable haptics" user setting, register an IHapticsPreferences implementation:
builder.Services.AddScoped<IHapticsPreferences, MyHapticsPrefs>();
2. index.html
<link rel="stylesheet" href="css/app.generated.css" />
<script src="_content/Harmony.Mobile.UI/js/interop.js"></script>
<script src="_content/Harmony.Mobile.UI/js/sortable.js"></script>
3. Styles
The package ships a pre-compiled CSS bundle (_content/Harmony.Mobile.UI/css/harmony-mobile-ui.css) that contains Tailwind base + the theme + all components + every utility class used inside the library's components. Always include it:
<link rel="stylesheet" href="_content/Harmony.Mobile.UI/css/harmony-mobile-ui.css" />
If your app needs its own Tailwind build (for utilities in your own markup), compile your own app.css and import the theme preset:
@import "tailwindcss";
@import "../../_content/Harmony.Mobile.UI/styles-src/theme.css";
@import "../../_content/Harmony.Mobile.UI/styles-src/components.css";
@source "./**/*.razor";
@source "./**/*.cs";
And keep the pre-compiled bundle linked too — it's the guaranteed way to ship the utility classes the library's components rely on.
5. App shell
@* App.razor *@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Shell.AppLayout)" />
</Found>
</Router>
@* Shell/AppLayout.razor *@
@inherits LayoutComponentBase
<HmMainLayout>
<TabBarContent>
<HmTabBar Tabs="HarmonyTabs.All"
TabOrder="@Settings.Current.TabOrder"
PrimaryCount="4"
HiddenRoutePrefixes="@(new[] { "/messages/" })"
MoreLabel="More" EditLabel="Edit" DoneLabel="Done" ResetLabel="Reset"
OnTabOrderChanged="OnTabOrderChanged" />
</TabBarContent>
@Body
</HmMainLayout>
6. Page template
@page "/"
@inherits Harmony.Mobile.UI.Abstractions.PageBase
<HmNavHeader Title="Home" ScrollId="home-scroll" />
<div id="home-scroll" class="page-scroll" data-scroll-memory="home">
<HmCard Title="Hello" Subtitle="From Harmony" Icon="sparkles" Color="#5AC8FA" />
</div>
Components
| Component | Purpose |
|---|---|
HmAvatar |
Avatar with initials fallback + optional online badge. |
HmCard |
Article/content card with gradient media. |
HmIcon |
Inline Lucide icon (curated subset). |
HmListGroup |
iOS inset-grouped list container. |
HmListRow |
List row with icon, label, description, value, chevron/check. |
HmPullToRefresh |
Pull-to-refresh indicator (JS-driven). |
HmSheet |
Bottom sheet modal with swipe-to-dismiss. |
HmSheetHost |
Single global sheet host, driven by SheetService. |
HmStat |
Small tile showing an icon + value + label. |
HmToggle |
iOS-style switch. |
HmMainLayout |
App shell (header + page stack + tab bar + sheet host). |
HmHeaderRenderer |
Sticky nav header (title, back button, trailing slot). |
HmNavHeader |
Headless page-level header declaration. |
HmPageTransition |
Animated page switcher (slide, zoom, fade). |
HmTabBar |
Bottom tab bar with overflow "More" sheet + edit mode. |
HmTabBarEditor |
Sortable tab list editor. |
Publishing a new version
Full release — use the script:
export GITEA_TOKEN=<your-gitea-access-token>
./scripts/release.sh 0.1.1
scripts/release.sh bumps <Version> in Directory.Build.props, rebuilds the CSS bundle, packs, pushes the .nupkg + .snupkg to the Gitea NuGet registry, commits the bump, tags v0.1.1, and pushes git + tags. It bails early if the working tree is dirty, the token is missing, or the version already has an artifact.
Dev-time pack (no push)
When iterating together on the RCL and Harmony-Stack without wanting to publish every step, pack locally and let Harmony-Stack consume the artifact via the harmony-local NuGet source it already has configured:
./scripts/pack-local.sh # uses current Directory.Build.props version
./scripts/pack-local.sh 0.1.1-dev1 # or override with a pre-release suffix
Then in Harmony-Stack:
./scripts/update-ui.sh 0.1.1-dev1
Compatibility
- .NET 9.0+
- Blazor WebAssembly (tested)
- Blazor Server — not tested yet
- MAUI Hybrid — not tested yet
License
MIT © Mixvoip