Skip to content
IzzyBlazor 0.1.0-alpha.17

Enter all results ↑ ↓ move Esc close

Popover

Non-modal content beside a trigger.

A static page with IzzyBlazor's script. No circuit.

Usage

razor
<Popover Label="Filters" StartIcon="Lucide.ListFilter" Title="Filter issues">
    <Form Model="Filters" FormName="filters" OnValidSubmit="ApplyAsync">
        <Checkbox @bind-Value="Filters!.Mine" />
        <FormActions><Button Submit Size="ControlSize.Sm">Apply</Button></FormActions>
    </Form>
</Popover>

@code {
    [SupplyParameterFromForm(FormName = "filters")]
    private IssueFilters? Filters { get; set; }

    protected override void OnInitialized() => Filters ??= new();
}

A popover is for content beside its trigger that people can leave without choosing: filters, a small form, an explanation. For a list of actions use a Menu, for a task that needs an answer a Dialog, and for a short label on hover a Tooltip.

A form in a popover

Use Title to head the panel and name it for screen readers; anything can go inside, such as a Form that filters the page.

Preview

Showing open issues

Code

Filters.razor
@using System.ComponentModel.DataAnnotations

<Row Gap="3" Wrap Align="Align.Center">
    <Popover Label="Filters" StartIcon="Lucide.ListFilter" Title="Filter issues">
        <Form Model="Model" FormName="issue-filters" OnValidSubmit="Apply" Gap="3">
            <Checkbox @bind-Value="Model!.Open" />
            <Checkbox @bind-Value="Model!.Closed" />
            <Checkbox @bind-Value="Model!.Mine" />
            <FormActions><Button Submit Size="ControlSize.Sm">Apply</Button></FormActions>
        </Form>
    </Popover>
    <TextBlock As="span" Size="TextSize.Sm" Color="Color.Muted">@_summary</TextBlock>
</Row>

@code {
    [SupplyParameterFromForm(FormName = "issue-filters")]
    private IssueFilters? Model { get; set; }

    private string _summary = "Showing open issues";

    protected override void OnInitialized() => Model ??= new();

    private void Apply()
    {
        var states = (Model!.Open, Model.Closed) switch
        {
            (true, true) => "all issues",
            (false, true) => "closed issues",
            (true, false) => "open issues",
            _ => "no issues",
        };
        _summary = $"Showing {states}" + (Model.Mine ? " assigned to you" : "");
    }

    public sealed class IssueFilters
    {
        [Display(Name = "Open issues")]
        public bool Open { get; set; } = true;

        [Display(Name = "Closed issues")]
        public bool Closed { get; set; }

        [Display(Name = "Assigned to me")]
        public bool Mine { get; set; }
    }
}

Static: Opening it moves focus into the panel, and closing it from inside puts focus back on the button. Apply posts without a reload, and the panel stays open.

Placement and width

Use Placement for the side it opens on (BottomStart by default) and PanelWidth for a set width; otherwise it's as wide as its content, up to 24rem.

Preview

Code

Sides.razor
@* Each opens where its Placement says, and moves to the other side when there's no room (beside it
   with no room on either side, below it). Opening one closes another: Auto popovers close each
   other. The Stack only gives the panels room in this frame. *@
<Stack Justify="Justify.Center" MinHeight="16.Rem" Grow>
    <Row Gap="3" Wrap Justify="Justify.Center">
        <Popover Label="Below" Title="Below, from the start" StartIcon="Lucide.PanelBottom">
            <TextBlock Size="TextSize.Sm">BottomStart, the default: under the trigger, lined up with its start.</TextBlock>
        </Popover>
        <Popover Label="Above" Title="Above, centered" Placement="Placement.Top" StartIcon="Lucide.PanelTop">
            <TextBlock Size="TextSize.Sm">Top: above the trigger, centered on it.</TextBlock>
        </Popover>
        <Popover Label="Beside" Title="Beside, a set width" Placement="Placement.End" PanelWidth="Length.Rem(14)"
                 StartIcon="Lucide.PanelRight">
            <TextBlock Size="TextSize.Sm">End with PanelWidth 14rem: after the trigger, the same width whatever it holds.</TextBlock>
        </Popover>
    </Row>
</Stack>

It moves to the other side when there's no room, and below the trigger when a panel beside it fits on neither side. Where the browser has no CSS anchor positioning the script places it; with JavaScript off there too, it opens in the middle of the screen.

Staying open

Use Mode="PopoverMode.Manual" for a panel that stays open while people work elsewhere on the page: Escape, a click outside and other popovers don't close it, so it shows a close button.

Preview

Code

Manual.razor
@* The Stack only gives the panel room in this frame. *@
<Stack Justify="Justify.Center" MinHeight="13.Rem" Grow>
    <Row>
        <Popover Label="Release checklist" StartIcon="Lucide.Pin" Title="Before you release" Mode="PopoverMode.Manual" Placement="Placement.End">
            <Stack Gap="2">
                <TextBlock Size="TextSize.Sm">1. Bump the version in the project file.</TextBlock>
                <TextBlock Size="TextSize.Sm">2. Update the changelog.</TextBlock>
                <TextBlock Size="TextSize.Sm">3. Tag the commit and push the tag.</TextBlock>
            </Stack>
        </Popover>
    </Row>
</Stack>

ShowCloseButton adds the close button to an Auto popover too, or takes it off a manual one.

Your own trigger

Use Trigger for a button of your own, such as an info icon, and pass it context with @attributes, which connects it to the panel.

Preview

Billing period

Code

InfoButton.razor
<Row Gap="1" Align="Align.Center">
    <TextBlock As="span" Weight="FontWeight.Medium">Billing period</TextBlock>
    <Popover Title="Billing period" Placement="Placement.Bottom" PanelWidth="Length.Rem(18)">
        <Trigger>
            <IconButton Icon="Lucide.Info" Label="About the billing period" Size="ControlSize.Sm"
                        Variant="ButtonVariant.Ghost" Accent="Accent.Neutral" @attributes="context" />
        </Trigger>
        <ChildContent>
            <TextBlock Size="TextSize.Sm">You're billed on the 1st of each month for the seats in use that day.</TextBlock>
            <Button Href="docs" Variant="ButtonVariant.Link" Size="ControlSize.Sm">Read about billing</Button>
        </ChildContent>
    </Popover>
</Row>

The trigger must be a button with a name of its own. Without Trigger, the built-in button needs a Label, and StartIcon, Accent, Variant and Size style it.

Opening it from the server or C#

Use Open to show it when the page loads, and @bind-Open on an interactive page to open and close it from C# and follow what people do.

razor
@* Interactive page: opens when C# sets _tipOpen, and follows the user. *@
<Popover Label="What's new" Title="What's new" @bind-Open="_tipOpen">
    Saved filters are here: pick one from the list.
</Popover>

@code {
    private bool _tipOpen = true;
}

A popover opened this way leaves focus where it is. Open from the server needs the script: with JavaScript off the panel stays closed until its trigger is pressed.

Mode support

What works in each mode, Popover
FeatureOffStaticLive
Opens and closes; Escape and a click outside close itYesYesYes
Focus moves in when opened and back when closedPartlyYesYes
Placed next to its trigger where CSS can't place itNoYesYes
Open from the server shows itNoYesYes
Open bound to C#NoNoYes

Off: JavaScript disabled. Static: a static SSR page with IzzyBlazor's script. Live: an interactive page (Server, WebAssembly or Auto).

Accessibility

  • The panel is a non-modal dialog named by Title, or by its trigger without one; the trigger has aria-haspopup="dialog".
  • With script, opening it moves focus to an element with autofocus, or to the panel, and closing it puts focus back on the trigger. Focus isn't trapped: Tab can leave it.
  • In Auto mode, Escape and a click outside close it. A Manual popover doesn't close on Escape, so it shows a close button, named "Close" (CloseLabel).
  • With JavaScript off, it opens and closes, and focus moves in only to an element with autofocus.
  • A custom Trigger must be a button with a name of its own.

Parameters

Popover parameters
PropTypeDefault
LabelThe default trigger's text.string?
CloseLabelThe close button's name.string?"Close"
AccentThe default trigger's color.AccentAccent.Neutral
VariantThe default trigger's weight.ButtonVariantButtonVariant.Outline
SizeThe default trigger's size.ControlSizeControlSize.Md
StartIconAn icon before the default trigger's text.IconData?
TriggerA custom trigger in place of the default button.RenderFragment<IReadOnlyDictionary<string, object>>?
TitleA heading at the top, which also names the popover for screen readers.string?
ShowCloseButtonShows a close button at the top.bool?
ModeHow it closes.PopoverModePopoverMode.Auto
PlacementWhere it opens.PlacementPlacement.BottomStart
PanelWidthThe width of the panel, such as Length.Rem(32) for a filter form.Length?computed
ChildContentThe content.RenderFragment?
@bind-OpenWhether it is open.boolfalse
IdThe element id.string?

See also

  • Dialog: a native modal dialog, opened by a trigger, a link or C#.
  • Menu: links, form posts and actions in a popover menu.
  • Tooltip: text on hover and keyboard focus.
  • Drawer: a dialog that slides in from an edge.