LilyDesignSystem.Blazor.SharePicker
0.2.1
dotnet add package LilyDesignSystem.Blazor.SharePicker --version 0.2.1
NuGet\Install-Package LilyDesignSystem.Blazor.SharePicker -Version 0.2.1
<PackageReference Include="LilyDesignSystem.Blazor.SharePicker" Version="0.2.1" />
<PackageVersion Include="LilyDesignSystem.Blazor.SharePicker" Version="0.2.1" />
<PackageReference Include="LilyDesignSystem.Blazor.SharePicker" />
paket add LilyDesignSystem.Blazor.SharePicker --version 0.2.1
#r "nuget: LilyDesignSystem.Blazor.SharePicker, 0.2.1"
#:package LilyDesignSystem.Blazor.SharePicker@0.2.1
#addin nuget:?package=LilyDesignSystem.Blazor.SharePicker&version=0.2.1
#tool nuget:?package=LilyDesignSystem.Blazor.SharePicker&version=0.2.1
SharePicker (Blazor helper)
A headless Blazor 10 share control: an icon button (a bundled outline-arrow SVG, not a Unicode character) that opens the native share sheet where the browser has one, and otherwise shows a list of destinations you supply, plus copy the page URL.
Ships no CSS, no JS file, and no third-party endpoints.
The single source of truth is spec/index.md. This file is the human-readable guide.
Install
Add a project reference to
LilyDesignSystem.Blazor.SharePicker.csproj, or the published
LilyDesignSystem.Blazor.SharePicker NuGet package.
<ProjectReference Include="path/to/LilyDesignSystem.Blazor.SharePicker.csproj" />
Quick start
@using LilyDesignSystem.Blazor.Helpers
<SharePicker Label="Share this page"
Title="An article worth reading"
Targets="@Targets"
CopyLabel="Copy link"
CopiedLabel="Link copied"
CopyFailedLabel="Could not copy — copy it from the address bar" />
@code {
private static readonly ShareTarget[] Targets =
{
new()
{
Id = "mastodon",
Label = "Mastodon",
Href = (url, title, _) =>
$"https://mastodon.social/share?text={Uri.EscapeDataString(title)}%20{Uri.EscapeDataString(url)}",
},
new()
{
Id = "email",
Label = "Email",
Href = (url, title, _) =>
$"mailto:?subject={Uri.EscapeDataString(title)}&body={Uri.EscapeDataString(url)}",
NewTab = false,
},
};
}
Url defaults to the current page (read from NavigationManager), so
the common case needs no wiring.
The control needs an interactive render mode — InteractiveServer,
InteractiveWebAssembly, or InteractiveAuto. Under static SSR it
renders correctly but cannot act: OnAfterRenderAsync never fires, so
nothing can open the sheet, write the clipboard, or move focus.
You supply the destinations
This package ships no social-network URLs. That is deliberate: which
networks belong in your product is an editorial and privacy decision, the
share endpoints change, and networks die. You pass Targets, so the
labels localise with the rest of your copy and no third-party endpoint is
baked into a design system.
Href is a Func<string, string, string, string> — (url, title, text)
— so you own the whole URL and its encoding:
new ShareTarget
{
Id = "linkedin",
Label = "LinkedIn",
Href = (url, _, _) =>
$"https://www.linkedin.com/sharing/share-offsite/?url={Uri.EscapeDataString(url)}",
}
Set NewTab = false on a destination that should open in the same tab —
mailto: links being the usual case. It drops target="_blank" for that
destination only.
Native share sheet
With Strategy.Auto (the default), pressing the button on a device with
navigator.share opens the OS sheet — the user gets their real installed
apps, and nothing is disclosed to a third party by the act of opening it.
Where there is no sheet, the list opens instead.
This means behaviour differs by platform, which is worth knowing when
you write help text or test scripts. Force one path with
Strategy.List or Strategy.Native.
A dismissed sheet ends the interaction — the list does not then pop open, which would resurrect UI the user just dismissed.
Copy to clipboard
Supply CopyLabel and a copy item appears. There is no default label,
because a default would be a hardcoded English string. CopiedLabel and
CopyFailedLabel are announced in a polite live region — copying is
otherwise silent, so without them the user gets no confirmation.
Failure is handled, not assumed away: a denied permission, an insecure
context, or a browser with no async clipboard all announce
CopyFailedLabel rather than throwing.
Why links, not a menu
Destinations render as real <a> elements, not role="menuitem". A
menuitem role strips middle-click, open-in-new-tab, and copy-link-address
— affordances users genuinely reach for on a share list. The WAI-ARIA APG
suggests a disclosure when the items are links. Copy is a real action, so
it is a <button>.
Driving it from your own UI
ActivateAsync(), CopyAsync() and ShareNativelyAsync() are public,
so a @ref lets you trigger the control from elsewhere:
<SharePicker @ref="share" Label="Share" Targets="@Targets" />
<button type="button" @onclick="() => share!.ActivateAsync()">Share</button>
@code {
private SharePicker? share;
}
Custom icon
ChildContent replaces the icon inside the button and receives a
SharePickerContext of { Open, Url }:
<SharePicker Label="Share" Targets="@Targets">
<span class="my-icon" aria-hidden="true">@(context.Open ? "▲" : "▶")</span>
</SharePicker>
It replaces the icon only — it does not render list items.
Parameters
Full table in spec/index.md §4.1.
Required: Label. Everything else is optional.
Static helpers
| Member | Purpose |
|---|---|
SharePicker.NextSharePickerId() |
Mint a stable, prerender-safe id prefix. |
SharePicker.CanShareNativelyAsync(js) |
Does this browser have a share sheet? |
SharePicker.CanCopyAsync(js) |
Does this browser have an async clipboard? |
Both probes are async — the browser is only reachable over JS interop —
and both return false during prerender rather than throwing.
Accessibility
- The icon is
aria-hidden; the name comes fromaria-label, and the list repeats it (aria-label=Label) so a screen reader entering the list hears what it is for. Escapecloses and returns focus to the button; arrows move between items and clamp;Home/Endjump;Tabparks focus on the button before closing, so the next Tab proceeds from the picker's position.- The status region is polite and empty on load.
- Tradeoff: an icon-only control's name rests entirely on
Label— there is no visible text fallback. See docs/accessibility.md.
Styling
Class hooks: .share-picker (root), .share-picker-button,
.share-picker-icon, .share-picker-list, .share-picker-list-item,
.share-picker-target, .share-picker-copy, .share-picker-status.
The package ships no CSS. The root themes/
stylesheets style the button and popup, including the optical icon
sizing that keeps every picker's icon visually consistent.
Position the root and the list yourself (position: relative /
position: absolute), or an open list shoves the page around.
Tests
From ../tests/LilyDesignSystem.Blazor.Helpers.Tests:
dotnet test
34 cases for this package, one or more per §7 clause; 122 across the catalog.
Lily™ and Lily Design System™ are trademarks.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- LilyDesignSystem.Blazor.Headless (>= 0.2.0)
- Microsoft.AspNetCore.Components.Web (>= 10.0.12)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on LilyDesignSystem.Blazor.SharePicker:
| Package | Downloads |
|---|---|
|
LilyDesignSystem.Blazor.PickerBar
Lily Design System Blazor picker bar: composes theme-picker, locale-picker, text-size-picker, and share-picker into one page-header row. Headless, SSR-safe, no CSS. |
GitHub repositories
This package is not used by any popular GitHub repositories.