
Syncfusion Blazor Context Menu
- 199 installs
- 4 repo stars
- Updated July 28, 2026
- syncfusion/blazor-ui-components-skills
Use syncfusion-blazor-context-menu for development tasks
About
syncfusion-blazor-context-menu: A skill for development. This provides functionality for development workflows.
- syncfusion-blazor-context-menu
Syncfusion Blazor Context Menu by the numbers
- 199 all-time installs (skills.sh)
- +13 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,000 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/blazor-ui-components-skills --skill syncfusion-blazor-context-menuAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 199 |
|---|---|
| repo stars | ★ 4 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/blazor-ui-components-skills ↗ |
What it does
Use syncfusion-blazor-context-menu for development tasks
Files
Syncfusion Blazor ContextMenu Component
When to Use This Skill
Use the Syncfusion Blazor ContextMenu skill when:
- Building right-click context menus for interactive elements
- Creating popup navigation menus triggered by user interaction
- Need hierarchical menu structures with nesting support
- Implementing custom menu templates with shortcuts or icons
- Managing dynamic menu states (enable/disable items)
- Requiring data-driven menus from custom object collections
- Building advanced interactions with dialogs, animations, or scrolling
ContextMenu provides fully customizable, event-driven popup menus perfect for desktop-like interactions in web apps.
---
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- NuGet installation (Navigations + Themes packages)
- Namespace imports and Syncfusion service registration
- Stylesheet and script resource setup
- Basic ContextMenu component markup
- First render and click-to-test validation
- Common import patterns for Blazor projects
Customization and Nesting
📄 Read: references/customization-and-nesting.md
- Custom MenuTemplate for rich UI (shortcuts, badges, icons)
- CssClass property for component-level styling
- Multilevel nesting (2-3 nested levels)
- Nested MenuItem hierarchies
- Best practices for menu depth and UX
Icons and Navigation
📄 Read: references/icons-and-navigation.md
- IconCss property and e-icons class mapping
- Custom icon class support
- Icon positioning in menu items
- Url property for hyperlink navigation
- Navigation target behaviors (_blank, _self, etc.)
Styling and Appearance
📄 Read: references/styling-and-appearance.md
- CSS class override reference (.e-contextmenu-container, .e-menu-item, etc.)
- Focus and selected state styling
- Theme customization with Theme Studio
- Common styling patterns and examples
Events and Interactions
📄 Read: references/events-and-interactions.md
- ItemSelected event binding and patterns
- MenuEventArgs data access
- Programmatic Open/Close methods with coordinates
- Manual menu control and triggering
- Event handler best practices
Data Binding
📄 Read: references/data-binding.md
- Items collection binding
- MenuFieldSettings property mapping
- Custom object data sources
- Hierarchical data binding with nested collections
- Dynamic menu population
Menu Item Management
📄 Read: references/menu-item-management.md
- Disabled property for item state management
- Enable/disable logic and workflows
- OnOpen event for conditional disabling
- Separator property usage and styling
- Controlling item availability dynamically
Advanced Features
📄 Read: references/advanced-features.md
- MenuAnimationSettings with effects (FadeIn, SlideDown, ZoomIn, None)
- Dialog integration with ItemSelected events
- EnableScrolling for large menu lists
- ScrollHeight configuration
- Large menu handling strategies
---
Quick Start Example
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open menu</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Result: Right-click the gray area to see the context menu with Cut, Copy, Paste options.
---
Common Patterns
Pattern 1: Basic Right-Click Menu
<SfContextMenu Target="#myElement" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Edit"></MenuItem>
<MenuItem Text="Delete"></MenuItem>
</MenuItems>
</SfContextMenu>Use when: Simple menu with basic actions on target element.
Pattern 2: Nested Menu (Submenu on Hover)
<SfContextMenu Target="#myElement" TValue="MenuItem">
<MenuItems>
<MenuItem Text="File">
<MenuItems>
<MenuItem Text="Open"></MenuItem>
<MenuItem Text="Save"></MenuItem>
</MenuItems>
</MenuItem>
</MenuItems>
</SfContextMenu>Use when: Hierarchical menu structure with grouped actions.
Pattern 3: Event-Driven Actions
<SfContextMenu Target="#myElement" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Delete"></MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" ItemSelected="@OnItemSelected"></MenuEvents>
</SfContextMenu>
@code {
private void OnItemSelected(MenuEventArgs<MenuItem> e) {
// Handle: e.Item.Text
}
}Use when: Need to execute custom logic on menu item click.
Pattern 4: Dynamic Menu from Data
<SfContextMenu Target="#myElement" Items="@menuItems">
<MenuFieldSettings Text="Title"></MenuFieldSettings>
</SfContextMenu>
@code {
private List<MenuOption> menuItems = new();
protected override void OnInitialized() {
menuItems.Add(new MenuOption { Title = "Option 1" });
menuItems.Add(new MenuOption { Title = "Option 2" });
}
private class MenuOption { public string Title { get; set; } }
}Use when: Menu items come from database or dynamic source.
---
Key Properties
| Property | Type | Purpose |
|---|---|---|
Target | string | CSS selector for right-click target element |
Items | List | Data collection for data-binding |
TValue | generic | Type parameter (usually MenuItem) |
CssClass | string | Custom CSS class for component styling |
ShowItemOnClick | bool | Enable sub-menu on click instead of hover |
EnableScrolling | bool | Enable scroll for large menu lists |
---
Common Use Cases
Use Case 1: File Context Menu
- Right-click file list item → Cut, Copy, Paste, Delete, Rename options
- Reference: events-and-interactions.md + menu-item-management.md
Use Case 2: Rich Text Editor
- Right-click text → Bold, Italic, Underline, Link options
- Customize template with icons and shortcuts
- Reference: customization-and-nesting.md + icons-and-navigation.md
Use Case 3: Admin Dashboard
- Right-click user row → View, Edit, Permissions, Delete
- Enable/disable based on user role
- Reference: menu-item-management.md + events-and-interactions.md
Use Case 4: Navigation Menu
- Click button → Open menu → Navigate to pages
- Nested categories with smooth animation
- Reference: icons-and-navigation.md + advanced-features.md
---
Next Steps
1. Start: Read getting-started.md to install and setup 2. Build: Use customization-and-nesting.md for custom UI 3. Enhance: Add icons, events, and interactivity with other reference guides 4. Explore: Check advanced-features.md for animations, dialogs, scrolling
Advanced Features
Table of Contents
Animation Settings
Use MenuAnimationSettings to control how sub-menus appear and disappear.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="File">
<MenuItems>
<MenuItem Text="New"></MenuItem>
<MenuItem Text="Open"></MenuItem>
<MenuItem Text="Save"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="Edit">
<MenuItems>
<MenuItem Text="Undo"></MenuItem>
<MenuItem Text="Redo"></MenuItem>
</MenuItems>
</MenuItem>
</MenuItems>
<MenuAnimationSettings Effect="MenuEffect.FadeIn" Duration="800"></MenuAnimationSettings>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Properties:
Effect- Animation type (see Animation Effects below)Duration- Duration in milliseconds (default 400ms)
---
Animation Effects
Supported animation effects for sub-menu display:
| Effect | Description | Use Case |
|---|---|---|
MenuEffect.None | No animation (instant) | Performance-sensitive or simple menus |
MenuEffect.SlideDown | Sub-menu slides down from parent | Default feel, natural transition |
MenuEffect.ZoomIn | Sub-menu zooms in from center | Modern, eye-catching |
MenuEffect.FadeIn | Sub-menu fades in gradually | Smooth, professional appearance |
SlideDown Effect
<MenuAnimationSettings
Effect="MenuEffect.SlideDown"
Duration="500">
</MenuAnimationSettings>Sub-menu slides down smoothly (500ms) when parent item is hovered.
ZoomIn Effect
<MenuAnimationSettings
Effect="MenuEffect.ZoomIn"
Duration="600">
</MenuAnimationSettings>Sub-menu grows from center point, appears to zoom in.
FadeIn Effect
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="File">
<MenuItems>
<MenuItem Text="New"></MenuItem>
<MenuItem Text="Save"></MenuItem>
</MenuItems>
</MenuItem>
</MenuItems>
<MenuAnimationSettings
Effect="MenuEffect.FadeIn"
Duration="800">
</MenuAnimationSettings>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Sub-menu appears by gradually fading in (800ms opacity transition).
No Animation
<MenuAnimationSettings
Effect="MenuEffect.None"
Duration="0">
</MenuAnimationSettings>Sub-menu appears instantly (best for performance).
---
Dialog Integration
Open dialogs from menu item selection using the ItemSelected event.
@using Syncfusion.Blazor.Navigations
@using Syncfusion.Blazor.Popups
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Back"></MenuItem>
<MenuItem Text="Forward"></MenuItem>
<MenuItem Text="Reload"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Save As..."></MenuItem>
<MenuItem Text="Print"></MenuItem>
<MenuItem Text="Cast"></MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" ItemSelected="@SelectedHandler"></MenuEvents>
</SfContextMenu>
<SfDialog @ref="dialogObj"
Content="@content"
Visible="false"
Target="#target"
Width="200px"
Height="110px">
<DialogButtons>
<DialogButton Content="Save" IsPrimary="true" @onclick="@SaveClicked"></DialogButton>
<DialogButton Content="Cancel" @onclick="@CancelClicked"></DialogButton>
</DialogButtons>
</SfDialog>
@code {
private SfDialog dialogObj;
private string content = "Save this file as PDF?";
private async Task SelectedHandler(MenuEventArgs<MenuItem> args) {
if (args.Item.Text == "Save As...") {
await dialogObj.Show();
}
}
private async Task SaveClicked() {
await dialogObj.Hide();
// Process save
}
private async Task CancelClicked() {
await dialogObj.Hide();
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Workflow: 1. User right-clicks → ContextMenu appears 2. User clicks "Save As..." → ItemSelected fires 3. Check item text → if (args.Item.Text == "Save As...") 4. Call dialogObj.Show() → Dialog appears 5. User interacts with dialog
Multiple Dialog Integration
@code {
private async Task SelectedHandler(MenuEventArgs<MenuItem> args) {
switch (args.Item.Text) {
case "Save As...":
await saveDialogRef.Show();
break;
case "Print":
await printDialogRef.Show();
break;
case "Share":
await shareDialogRef.Show();
break;
}
}
}---
Scrollable Menus
Enable scrolling for large menus that exceed available viewport height.
@using Syncfusion.Blazor.Navigations
<SfContextMenu Target="#target"
TValue="MenuItem"
EnableScrolling="true"
OnOpen="@OnBeforeOpen">
<MenuEvents TValue="MenuItem" OnOpen="@OnBeforeOpen"></MenuEvents>
<MenuItems>
<MenuItem Text="View">
<MenuItems>
<MenuItem Text="Option 1"></MenuItem>
<MenuItem Text="Option 2"></MenuItem>
<MenuItem Text="Option 3"></MenuItem>
<MenuItem Text="Option 4"></MenuItem>
<MenuItem Text="Option 5"></MenuItem>
<MenuItem Text="Option 6"></MenuItem>
<MenuItem Text="Option 7"></MenuItem>
<MenuItem Text="Option 8"></MenuItem>
<MenuItem Text="Option 9"></MenuItem>
<MenuItem Text="Option 10"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="Refresh"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
</MenuItems>
</SfContextMenu>
<div id="target">Right click to open</div>
@code {
private void OnBeforeOpen(BeforeOpenCloseMenuEventArgs<MenuItem> args) {
// Set scroll height for submenu
args.ScrollHeight = 150; // pixels
}
}
<style>
#target {
border: 1px dashed; height: 250px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Key Points:
EnableScrolling="true"- Enable scroll functionalityOnBeforeOpenevent - Set scroll height per submenuargs.ScrollHeight- Max height in pixels before scroll appears- Scroll bars appear automatically when items exceed height
Configure Scroll Height
@code {
private void OnBeforeOpen(BeforeOpenCloseMenuEventArgs<MenuItem> args) {
// Different scroll heights for different submenus
if (args.ParentItem?.Text == "Large Menu") {
args.ScrollHeight = 200;
} else if (args.ParentItem?.Text == "View") {
args.ScrollHeight = 150;
} else {
args.ScrollHeight = 100;
}
}
}---
Large Menu Handling
Strategies for menus with 20+ items:
Strategy 1: Categorize with Nesting
<MenuItems>
<MenuItem Text="Recently Used">
<MenuItems>
<MenuItem Text="Document 1"></MenuItem>
<MenuItem Text="Document 2"></MenuItem>
<MenuItem Text="Document 3"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="All Documents">
<MenuItems>
<MenuItem Text="Project A"></MenuItem>
<MenuItem Text="Project B"></MenuItem>
<!-- More items -->
</MenuItems>
</MenuItem>
</MenuItems>Group items into logical categories, use sub-menus for organization.
Strategy 2: Enable Scrolling + Set Height
<SfContextMenu Target="#target"
TValue="MenuItem"
EnableScrolling="true"
OnOpen="@OnBeforeOpen">
<!-- Many items -->
</SfContextMenu>
@code {
private void OnBeforeOpen(BeforeOpenCloseMenuEventArgs<MenuItem> args) {
args.ScrollHeight = 200; // Allow vertical scroll
}
}Limit visible height, enable scroll for overflow.
Strategy 3: Data Binding + Search/Filter
@using Syncfusion.Blazor.Navigations
<input type="text" placeholder="Filter..." @onchange="@OnFilterChanged" />
<SfContextMenu Target="#target" Items="@filteredItems">
<MenuFieldSettings Text="Name"></MenuFieldSettings>
</SfContextMenu>
@code {
private List<MenuItem> allItems = new();
private List<MenuItem> filteredItems = new();
protected override void OnInitialized() {
// Load 50+ items
for (int i = 1; i <= 50; i++) {
allItems.Add(new MenuItem { Text = $"Item {i}" });
}
filteredItems = allItems;
}
private void OnFilterChanged(ChangeEventArgs e) {
string filter = e.Value?.ToString().ToLower() ?? "";
filteredItems = allItems
.Where(x => x.Text.ToLower().Contains(filter))
.ToList();
}
}Filter menu items dynamically as user types.
Strategy 4: Pagination
@code {
private List<MenuItem> GetPagedItems(int pageNumber, int pageSize) {
return allItems
.Skip(pageNumber * pageSize)
.Take(pageSize)
.ToList();
}
private List<MenuItem> GetAllWithPagination() {
var paged = GetPagedItems(0, 10);
paged.Add(new MenuItem { Text = "Load More..." });
return paged;
}
}Show limited items with "Load More" option.
---
Complete Example: Advanced Feature Showcase
@using Syncfusion.Blazor.Navigations
@using Syncfusion.Blazor.Popups
<div id="target">Right click to open</div>
<SfContextMenu Target="#target"
TValue="MenuItem"
EnableScrolling="true"
OnOpen="@OnMenuOpen">
<MenuItems>
<MenuItem Text="File">
<MenuItems>
<MenuItem Text="New"></MenuItem>
<MenuItem Text="Open"></MenuItem>
<MenuItem Text="Recent Files">
<MenuItems>
<MenuItem Text="Document 1"></MenuItem>
<MenuItem Text="Document 2"></MenuItem>
<MenuItem Text="Document 3"></MenuItem>
</MenuItems>
</MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="Edit">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Settings"></MenuItem>
<MenuItem Text="Help"></MenuItem>
</MenuItems>
<MenuAnimationSettings Effect="MenuEffect.FadeIn" Duration="600"></MenuAnimationSettings>
<MenuEvents TValue="MenuItem" ItemSelected="@OnItemSelected"></MenuEvents>
</SfContextMenu>
<SfDialog @ref="settingsDialog"
Visible="false"
Title="Settings"
Width="300px">
<DialogContent>
Settings panel content...
</DialogContent>
</SfDialog>
<p>Last Action: <strong>@lastAction</strong></p>
@code {
private SfDialog settingsDialog;
private string lastAction = "None";
private void OnMenuOpen(BeforeOpenCloseMenuEventArgs<MenuItem> args) {
args.ScrollHeight = 200;
}
private async Task OnItemSelected(MenuEventArgs<MenuItem> args) {
lastAction = args.Item.Text;
if (args.Item.Text == "Settings") {
await settingsDialog.Show();
}
}
}
<style>
#target {
border: 1px dashed; height: 200px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Features Demonstrated:
- Nested menus with categorization
- FadeIn animation (600ms)
- Scroll support for large lists
- Dialog integration
- Event handling
- Custom styling
Customization and Nesting
Table of Contents
Custom MenuTemplate
Use MenuTemplate to customize menu item appearance with shortcuts, icons, badges, or other rich content.
Template with Keyboard Shortcuts
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuTemplates TValue="MenuItem">
<Template>
<span>@context.Text</span>
@if (context.Text == "Save As...") {
<span class="shortcut">Ctrl + S</span>
} else if (context.Text == "Inspect") {
<span class="shortcut">Ctrl + Shift + I</span>
}
</Template>
</MenuTemplates>
<MenuItems>
<MenuItem Text="Save As..."></MenuItem>
<MenuItem Text="Inspect"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
.shortcut {
float: right; font-size: 11px; opacity: 0.6;
margin-left: 10px; color: #666;
}
</style>Features:
@context.Text- Access menu item text@context- Full menu item object- Conditional rendering for shortcuts
- Right-aligned shortcut display
Template with Icons and Labels
<MenuTemplates TValue="MenuItem">
<Template>
<span class="e-icons @context.IconCss"></span>
<span>@context.Text</span>
</Template>
</MenuTemplates>---
CssClass Styling
Style entire ContextMenu component with custom CSS class.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem" CssClass="custom-menu">
<MenuItems>
<MenuItem Text="Edit"></MenuItem>
<MenuItem Text="View"></MenuItem>
<MenuItem Text="Delete"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
/* Custom menu styling */
.custom-menu.e-contextmenu-container .e-menu-item {
padding: 8px 15px;
font-size: 12px;
font-style: italic;
}
.custom-menu.e-contextmenu-container .e-menu-item:hover {
background-color: #e3f2fd;
border-radius: 4px;
}
.custom-menu.e-contextmenu-container .e-menu-item.e-focused {
background-color: #bbdefb;
}
</style>CSS Classes Available:
.e-contextmenu-container- Main container.e-menu-item- Individual item.e-menu-item:hover- On hover state.e-menu-item.e-focused- Focused state.e-menu-item.e-selected- Selected state
---
Multilevel Nesting
Create hierarchical menus with nested MenuItems components (up to 3+ levels).
Two-Level Nesting
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="File">
<MenuItems>
<MenuItem Text="New"></MenuItem>
<MenuItem Text="Open"></MenuItem>
<MenuItem Text="Save"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="Edit">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="View"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Three-Level Nesting
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Bookmarks">
<MenuItems>
<MenuItem Text="Most Visited">
<MenuItems>
<MenuItem Text="Google"></MenuItem>
<MenuItem Text="Gmail"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="Recently Added"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="History"></MenuItem>
</MenuItems>
</SfContextMenu>Behavior:
- Hover over parent item → Sub-menu slides in
- Hover off → Sub-menu closes
- Works with unlimited nesting (though 2-3 levels recommended for UX)
---
ShowItemOnClick
By default, sub-menus open on hover. Use ShowItemOnClick="true" to open on click instead.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem" ShowItemOnClick="true">
<MenuItems>
<MenuItem Text="File">
<MenuItems>
<MenuItem Text="New"></MenuItem>
<MenuItem Text="Open"></MenuItem>
<MenuItem Text="Save"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="Edit">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
</MenuItems>
</MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>When to use:
- Touch/mobile interfaces (hover doesn't work well)
- Complex nested menus (reduce accidental opens)
- Desktop apps expecting click behavior
---
Best Practices
Practice 1: Nesting Depth
- Limit to 2-3 levels for usability
- Deeper nesting makes items hard to reach
- Consider flat menu with categories for large item counts
Practice 2: Separator Items
<MenuItem Separator="true"></MenuItem>Use separators to group related items visually.
Practice 3: Template Consistency
- Keep custom template styling consistent across all items
- Use
@contextproperties uniformly - Avoid overly complex templates (performance)
Practice 4: Touch Support
<SfContextMenu Target="#target" ShowItemOnClick="true">Enable ShowItemOnClick if supporting touch devices.
Practice 5: Item Count
- Keep menu to 5-8 items at root level
- Use nesting or scrolling for longer menus (see advanced-features.md)
- Group related actions under common parent
---
Common Patterns
Pattern: Settings Menu with Groups
<MenuItems>
<MenuItem Text="Display">
<MenuItems>
<MenuItem Text="Zoom"></MenuItem>
<MenuItem Text="Theme"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Advanced"></MenuItem>
</MenuItems>Pattern: Rich Template with Badges
<MenuTemplates TValue="MenuItem">
<Template>
@context.Text
<span class="badge">NEW</span>
</Template>
</MenuTemplates>Data Binding
Table of Contents
Basic Data Binding
Bind menu items from a data collection using the Items property.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" Items="@menuItems">
<MenuFieldSettings Text="Title"></MenuFieldSettings>
</SfContextMenu>
@code {
private List<MenuItem> menuItems = new();
protected override void OnInitialized() {
menuItems.Add(new MenuItem { Text = "Cut" });
menuItems.Add(new MenuItem { Text = "Copy" });
menuItems.Add(new MenuItem { Text = "Paste" });
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Key Points:
Items="@menuItems"- Bind to collectionMenuFieldSettings- Map data properties to component propertiesText="Title"- MapsTitleproperty to display text
---
MenuFieldSettings
Use MenuFieldSettings to map custom object properties to ContextMenu component properties.
Common Mappings
<SfContextMenu Target="#target" Items="@menuItems">
<MenuFieldSettings
Text="Label"
IconCss="Icon"
Url="Link"
Disabled="IsDisabled">
</MenuFieldSettings>
</SfContextMenu>| Field Property | Maps to | Purpose |
|---|---|---|
Text | Display text | Item label shown in menu |
IconCss | Icon class | CSS class for icon |
Url | Navigation URL | Link for navigation items |
Disabled | Item disabled state | Disable specific items |
Separator | Separator indicator | Show separator line |
Children | Nested items | Submenu items (hierarchical) |
---
Custom Object Mapping
Create custom classes and map their properties to ContextMenu.
Simple Custom Object
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" Items="@customMenuItems">
<MenuFieldSettings Text="DisplayName" IconCss="CssClass"></MenuFieldSettings>
</SfContextMenu>
@code {
private List<CustomMenuItem> customMenuItems = new();
protected override void OnInitialized() {
customMenuItems.Add(new CustomMenuItem
{
DisplayName = "Cut",
CssClass = "e-icons e-cut",
Action = "cut"
});
customMenuItems.Add(new CustomMenuItem
{
DisplayName = "Copy",
CssClass = "e-icons e-copy",
Action = "copy"
});
customMenuItems.Add(new CustomMenuItem
{
DisplayName = "Paste",
CssClass = "e-icons e-paste",
Action = "paste"
});
}
private class CustomMenuItem {
public string DisplayName { get; set; }
public string CssClass { get; set; }
public string Action { get; set; }
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Custom Object with Event Handler
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" Items="@actionMenuItems">
<MenuFieldSettings Text="Name"></MenuFieldSettings>
<MenuEvents TValue="CustomAction" ItemSelected="@OnActionSelected"></MenuEvents>
</SfContextMenu>
<p>Last action: <strong>@lastAction</strong></p>
@code {
private List<CustomAction> actionMenuItems = new();
private string lastAction = "None";
protected override void OnInitialized() {
actionMenuItems.Add(new CustomAction
{
Name = "Edit",
Callback = () => Edit()
});
actionMenuItems.Add(new CustomAction
{
Name = "Delete",
Callback = () => Delete()
});
actionMenuItems.Add(new CustomAction
{
Name = "Archive",
Callback = () => Archive()
});
}
private void OnActionSelected(MenuEventArgs<CustomAction> args) {
lastAction = args.Item.Name;
args.Item.Callback?.Invoke();
}
private void Edit() => lastAction += " (Editing...)";
private void Delete() => lastAction += " (Deleting...)";
private void Archive() => lastAction += " (Archiving...)";
private class CustomAction {
public string Name { get; set; }
public Action Callback { get; set; }
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>---
Hierarchical Data
Bind nested collections for submenu support using the Children property.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" Items="@hierarchicalItems">
<MenuFieldSettings
Text="Label"
Children="SubItems">
</MenuFieldSettings>
</SfContextMenu>
@code {
private List<MenuItem> hierarchicalItems = new();
protected override void OnInitialized() {
hierarchicalItems.Add(new MenuItem {
Text = "File",
Children = new List<MenuItem> {
new MenuItem { Text = "New" },
new MenuItem { Text = "Open" },
new MenuItem { Text = "Save" }
}
});
hierarchicalItems.Add(new MenuItem {
Text = "Edit",
Children = new List<MenuItem> {
new MenuItem { Text = "Cut" },
new MenuItem { Text = "Copy" },
new MenuItem { Text = "Paste" }
}
});
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Custom Hierarchy Model
@code {
private List<MenuNode> hierarchicalItems = new();
protected override void OnInitialized() {
hierarchicalItems.Add(new MenuNode {
Title = "Bookmarks",
SubItems = new List<MenuNode> {
new MenuNode {
Title = "Most Visited",
SubItems = new List<MenuNode> {
new MenuNode { Title = "Google" },
new MenuNode { Title = "GitHub" }
}
},
new MenuNode { Title = "Recently Added" }
}
});
}
private class MenuNode {
public string Title { get; set; }
public List<MenuNode> SubItems { get; set; }
}
}Mapping:
<SfContextMenu Target="#target" Items="@hierarchicalItems">
<MenuFieldSettings
Text="Title"
Children="SubItems">
</MenuFieldSettings>
</SfContextMenu>---
Dynamic Menu Population
Populate menu items dynamically from databases or APIs.
From Database (Simulated)
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" Items="@dbMenuItems">
<MenuFieldSettings Text="Name" IconCss="Icon"></MenuFieldSettings>
</SfContextMenu>
@code {
private List<DbMenuItem> dbMenuItems = new();
protected override async Task OnInitializedAsync() {
// Simulate database fetch
dbMenuItems = await FetchMenuFromDatabase();
}
private async Task<List<DbMenuItem>> FetchMenuFromDatabase() {
// Simulated async DB call
await Task.Delay(500);
return new List<DbMenuItem> {
new DbMenuItem { Name = "Users", Icon = "e-icons e-people" },
new DbMenuItem { Name = "Reports", Icon = "e-icons e-chart" },
new DbMenuItem { Name = "Settings", Icon = "e-icons e-settings" }
};
}
private class DbMenuItem {
public string Name { get; set; }
public string Icon { get; set; }
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Filtered Menu Based on User Role
@code {
private List<RoleMenuItem> userMenuItems = new();
private string currentRole = "admin"; // From auth
protected override void OnInitialized() {
var allItems = GetAllMenuItems();
// Filter based on role
userMenuItems = allItems
.Where(item => item.AllowedRoles.Contains(currentRole))
.ToList();
}
private List<RoleMenuItem> GetAllMenuItems() {
return new List<RoleMenuItem> {
new RoleMenuItem {
Name = "Edit",
AllowedRoles = new[] { "admin", "editor" }
},
new RoleMenuItem {
Name = "Delete",
AllowedRoles = new[] { "admin" }
},
new RoleMenuItem {
Name = "View",
AllowedRoles = new[] { "admin", "editor", "viewer" }
}
};
}
private class RoleMenuItem {
public string Name { get; set; }
public string[] AllowedRoles { get; set; }
}
}Real-Time Menu Update
@code {
private List<MenuItem> liveMenuItems = new();
protected override void OnInitialized() {
liveMenuItems = GetInitialMenu();
}
// Called from external event or timer
private void RefreshMenuItems() {
liveMenuItems = GetUpdatedMenu();
StateHasChanged(); // Force re-render
}
private List<MenuItem> GetInitialMenu() {
return new List<MenuItem> {
new MenuItem { Text = "Action 1" },
new MenuItem { Text = "Action 2" }
};
}
private List<MenuItem> GetUpdatedMenu() {
return new List<MenuItem> {
new MenuItem { Text = "Action 1" },
new MenuItem { Text = "Action 2" },
new MenuItem { Text = "Action 3 (NEW)" }
};
}
}---
Complete Example: Database-Driven Menu
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" Items="@contextActions">
<MenuFieldSettings
Text="ActionName"
IconCss="IconClass"
Disabled="IsDisabled">
</MenuFieldSettings>
<MenuEvents TValue="ContextAction" ItemSelected="@OnActionClick"></MenuEvents>
</SfContextMenu>
<p>Last performed: <strong>@lastAction</strong></p>
@code {
private List<ContextAction> contextActions = new();
private string lastAction = "None";
protected override async Task OnInitializedAsync() {
contextActions = await LoadActionsFromDatabase();
}
private async Task<List<ContextAction>> LoadActionsFromDatabase() {
// Simulate DB fetch
await Task.Delay(300);
return new List<ContextAction> {
new ContextAction { ActionName = "Edit", IconClass = "e-icons e-edit" },
new ContextAction { ActionName = "Copy", IconClass = "e-icons e-copy" },
new ContextAction { ActionName = "Delete", IconClass = "e-icons e-delete", IsDisabled = false }
};
}
private void OnActionClick(MenuEventArgs<ContextAction> args) {
lastAction = $"{args.Item.ActionName} executed";
}
private class ContextAction {
public string ActionName { get; set; }
public string IconClass { get; set; }
public bool IsDisabled { get; set; }
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Events and Interactions
Table of Contents
ItemSelected Event
The ItemSelected event fires when a user clicks on a menu item. Use it to capture which item was clicked and execute custom logic.
Basic ItemSelected Handler
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" ItemSelected="@OnItemSelected"></MenuEvents>
</SfContextMenu>
<p>Selected: <strong>@selectedItem</strong></p>
@code {
private string selectedItem = "None";
private void OnItemSelected(MenuEventArgs<MenuItem> args) {
selectedItem = args.Item.Text;
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>How it works: 1. User right-clicks → ContextMenu appears 2. User clicks menu item (e.g., "Copy") 3. ItemSelected event fires 4. OnItemSelected() method called with event data 5. args.Item.Text contains "Copy"
Handle Multiple Actions
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Delete"></MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" ItemSelected="@HandleMenuAction"></MenuEvents>
</SfContextMenu>
<p>@message</p>
@code {
private string message = "Ready...";
private void HandleMenuAction(MenuEventArgs<MenuItem> args) {
message = args.Item.Text switch {
"Cut" => "Text cut to clipboard",
"Copy" => "Text copied to clipboard",
"Paste" => "Text pasted from clipboard",
"Delete" => "Text deleted",
_ => "Unknown action"
};
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>---
MenuEventArgs Properties
The MenuEventArgs<MenuItem> object passed to event handlers contains:
| Property | Type | Description |
|---|---|---|
Item | MenuItem | The clicked menu item object |
Item.Text | string | Text of the menu item |
Item.Disabled | bool | Whether item is disabled |
Item.Separator | bool | Whether item is separator |
Item.Url | string | Navigation URL (if set) |
Item.IconCss | string | Icon CSS class |
Access Item Properties
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Edit" IconCss="e-icons e-edit"></MenuItem>
<MenuItem Text="Delete" IconCss="e-icons e-delete"></MenuItem>
<MenuItem Text="Help" Url="https://help.example.com"></MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" ItemSelected="@OnItemSelected"></MenuEvents>
</SfContextMenu>
<div>
<p><strong>Item Text:</strong> @selectedText</p>
<p><strong>Icon CSS:</strong> @selectedIcon</p>
<p><strong>Has URL:</strong> @hasUrl</p>
</div>
@code {
private string selectedText = "";
private string selectedIcon = "";
private bool hasUrl = false;
private void OnItemSelected(MenuEventArgs<MenuItem> args) {
selectedText = args.Item.Text;
selectedIcon = args.Item.IconCss ?? "None";
hasUrl = !string.IsNullOrEmpty(args.Item.Url);
}
}
<style>
#target {
border: 1px dashed; height: 100px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>---
Programmatic Open
Use the @ref attribute to get reference to SfContextMenu, then call Open() method to manually open the menu.
@using Syncfusion.Blazor.Navigations
@using Syncfusion.Blazor.Buttons
<SfButton @onclick="OpenMenu">Open Menu</SfButton>
<div id="target">
<SfContextMenu @ref="contextMenuRef" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Option 1"></MenuItem>
<MenuItem Text="Option 2"></MenuItem>
<MenuItem Text="Option 3"></MenuItem>
</MenuItems>
</SfContextMenu>
</div>
@code {
private SfContextMenu<MenuItem> contextMenuRef;
private async Task OpenMenu(MouseEventArgs e) {
await contextMenuRef.Open(e.ClientX, e.ClientY);
}
}
<style>
#target {
margin-top: 20px; width: 300px; height: 200px;
border: 1px dashed; padding: 10px; color: gray;
}
</style>Key Points:
@ref="contextMenuRef"- Store component referencecontextMenuRef.Open(x, y)- Open at coordinates (x, y in pixels)e.ClientX, e.ClientY- Mouse coordinates from click event- Can also use fixed coordinates:
Open(100, 100)
Open on Button Click
<SfButton @onclick="@((e) => contextMenuRef.Open(100, 50))">
Open Menu
</SfButton>---
Programmatic Close
Use the Close() method to programmatically close an open menu.
@using Syncfusion.Blazor.Navigations
@using Syncfusion.Blazor.Buttons
<div id="target">Right click or use buttons</div>
<SfContextMenu @ref="contextMenuRef" Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Option 1"></MenuItem>
<MenuItem Text="Option 2"></MenuItem>
<MenuItem Text="Close Menu">
<MenuItem Text="Nested Item"></MenuItem>
</MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" ItemSelected="@OnItemSelected"></MenuEvents>
</SfContextMenu>
<div style="margin-top: 20px;">
<SfButton @onclick="CloseMenu">Close Menu</SfButton>
</div>
@code {
private SfContextMenu<MenuItem> contextMenuRef;
private string lastAction = "";
private async Task OnItemSelected(MenuEventArgs<MenuItem> args) {
lastAction = $"Selected: {args.Item.Text}";
await contextMenuRef.Close();
}
private async Task CloseMenu() {
await contextMenuRef.Close();
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Use Cases:
- Auto-close menu after action completed
- Close on external event (ESC key, etc.)
- Manual close button for touch devices
---
Common Event Patterns
Pattern 1: Conditional Action Based on Item
private void HandleMenuAction(MenuEventArgs<MenuItem> args) {
if (args.Item.Text == "Delete") {
// Show confirmation dialog
ShowDeleteConfirmation();
} else if (args.Item.Text == "Edit") {
// Open edit form
OpenEditForm();
}
}Pattern 2: Async Operation
private async Task OnItemSelected(MenuEventArgs<MenuItem> args) {
// Perform async operation
var result = await FetchDataAsync(args.Item.Text);
ProcessResult(result);
// Close menu after operation
await contextMenuRef.Close();
}Pattern 3: Event with State Update
@code {
private string selectedItem = "";
private List<string> history = new();
private void OnItemSelected(MenuEventArgs<MenuItem> args) {
selectedItem = args.Item.Text;
history.Add($"{DateTime.Now:HH:mm:ss} - {selectedItem}");
// Trigger component re-render
StateHasChanged();
}
}Pattern 4: Nested Menu Item Selection
<MenuEvents TValue="MenuItem" ItemSelected="@HandleSelection"></MenuEvents>
@code {
private void HandleSelection(MenuEventArgs<MenuItem> args) {
// Works for both parent and child items
var path = BuildItemPath(args.Item);
Console.WriteLine($"Selected path: {path}");
}
private string BuildItemPath(MenuItem item) {
// Custom logic to build hierarchical path
return item.Text;
}
}Pattern 5: Context Data with Event
<div id="target" data-id="123">
Right click item #123
</div>
@code {
private async Task OnItemSelected(MenuEventArgs<MenuItem> args) {
// Access context element data
var elementId = args.Item.Text; // Could also get from context
var action = args.Item.Text;
await PerformAction(elementId, action);
}
}---
Complete Example: File Manager Context Menu
@using Syncfusion.Blazor.Navigations
<div id="target">Right click file</div>
<SfContextMenu Target="#target" TValue="MenuItem" @ref="contextMenuRef">
<MenuItems>
<MenuItem Text="Cut" IconCss="e-icons e-cut"></MenuItem>
<MenuItem Text="Copy" IconCss="e-icons e-copy"></MenuItem>
<MenuItem Text="Paste" IconCss="e-icons e-paste"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Delete" IconCss="e-icons e-delete"></MenuItem>
<MenuItem Text="Rename"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Properties"></MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" ItemSelected="@HandleFileAction"></MenuEvents>
</SfContextMenu>
<div style="margin-top: 20px;">
<p><strong>Last Action:</strong> @lastAction</p>
<p><strong>Timestamp:</strong> @actionTime</p>
</div>
@code {
private SfContextMenu<MenuItem> contextMenuRef;
private string lastAction = "None";
private string actionTime = "";
private async Task HandleFileAction(MenuEventArgs<MenuItem> args) {
lastAction = args.Item.Text;
actionTime = DateTime.Now.ToString("HH:mm:ss.fff");
// Simulate async operation
await Task.Delay(200);
// Auto-close menu
await contextMenuRef.Close();
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
cursor: context-menu;
}
</style>Getting Started with Blazor ContextMenu
Table of Contents
NuGet Installation
Install the required Syncfusion packages via NuGet Package Manager or CLI:
Package Manager Console:
Install-Package Syncfusion.Blazor.Navigations
Install-Package Syncfusion.Blazor.ThemesOr use .NET CLI:
dotnet add package Syncfusion.Blazor.Navigations
dotnet add package Syncfusion.Blazor.Themes
dotnet restoreRequired Packages:
Syncfusion.Blazor.Navigations- Contains ContextMenu componentSyncfusion.Blazor.Themes- Contains theme stylesheets
Namespace Imports
Open _Imports.razor and add these namespaces:
@using Syncfusion.Blazor
@using Syncfusion.Blazor.NavigationsThis makes SfContextMenu and MenuItem available throughout your project.
Service Registration
In Program.cs, register the Syncfusion Blazor service:
using Syncfusion.Blazor;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient
{ BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
// Add this line:
builder.Services.AddSyncfusionBlazor();
await builder.Build().RunAsync();Add Resources
In index.html (in the <head> section), add the theme stylesheet and script:
<head>
<!-- Other meta tags -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css"
rel="stylesheet" />
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js"
type="text/javascript"></script>
</head>Available themes:
bootstrap5.css(default, modern)material.css(Material Design)fluent.css(Microsoft Fluent)tailwind.css(Tailwind styling)
Basic Component
Add the ContextMenu to your Razor component (e.g., Pages/Index.razor):
@using Syncfusion.Blazor.Navigations
<div id="target">Right click/Touch hold to open the ContextMenu</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
user-select: none; font-size: 14px;
}
</style>Key Elements:
Target="#target"- CSS selector of element to right-clickTValue="MenuItem"- Generic type (usually MenuItem)<MenuItems>- Container for menu items<MenuItem Text="...">- Individual menu item
Test Your Setup
1. Run your Blazor app: dotnet watch run or press Ctrl+F5 2. Navigate to the component page (e.g., https://localhost:7000/) 3. Right-click the gray box labeled "Right click/Touch hold..." 4. Verify the context menu appears with Cut, Copy, Paste options 5. Click any item to close the menu
Expected Result: Gray area responds to right-click with popup menu.
---
Common Issues
Issue: Menu doesn't appear on right-click
- Fix: Verify CSS selector in
Targetmatches HTML element ID - Example:
Target="#target"requires<div id="target">
Issue: Styles not loading (unstyled menu)
- Fix: Ensure stylesheet is added in
index.html<head> - Fix: Verify theme file path:
_content/Syncfusion.Blazor.Themes/bootstrap5.css
Issue: MenuItem is not recognized
- Fix: Verify
@using Syncfusion.Blazor.Navigationsin_Imports.razor - Fix: Verify
AddSyncfusionBlazor()inProgram.cs
---
Next Steps
- Customize menu items: Add icons, custom templates, nested menus (see customization-and-nesting.md)
- Add interactivity: Bind events to handle menu clicks (see events-and-interactions.md)
- Data-driven menus: Populate from collections or databases (see data-binding.md)
Icons and Navigation
Table of Contents
- Adding Icons with IconCss
- Using Syncfusion e-icons
- Custom Icon Classes
- Navigation with Url Property
- Icon Positioning
Adding Icons with IconCss
Use the IconCss property to add icons to menu items using CSS class names.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Cut" IconCss="e-icons e-cut"></MenuItem>
<MenuItem Text="Copy" IconCss="e-icons e-copy"></MenuItem>
<MenuItem Text="Paste" IconCss="e-icons e-paste"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
/* Define custom icon glyphs */
.e-cut::before {
content: '\e279';
}
.e-copy::before {
content: '\e280';
}
.e-paste::before {
content: '\e601';
}
</style>Key Points:
IconCss="e-icons e-cut"- Combines e-icons base class with specific icon classe-icons- Base class that activates icon rendering- Icon-specific class (e.g.,
e-cut) - Maps to Unicode glyph via::beforecontent
---
Using Syncfusion e-icons
Syncfusion provides a built-in icon library. Common icons for ContextMenu:
| Icon Class | Glyph | Use Case |
|---|---|---|
e-cut | ✂️ | Cut action |
e-copy | 📋 | Copy action |
e-paste | 📄 | Paste action |
e-edit | ✏️ | Edit/modify |
e-delete | 🗑️ | Delete action |
e-save | 💾 | Save action |
e-refresh | 🔄 | Refresh/reload |
e-search | 🔍 | Search |
e-download | ⬇️ | Download |
e-upload | ⬆️ | Upload |
Full Icons Example
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Edit" IconCss="e-icons e-edit"></MenuItem>
<MenuItem Text="Delete" IconCss="e-icons e-delete"></MenuItem>
<MenuItem Text="Save" IconCss="e-icons e-save"></MenuItem>
<MenuItem Text="Refresh" IconCss="e-icons e-refresh"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
/* Custom colors for icons */
.e-contextmenu-container .e-menu-item .e-icons {
color: #555;
margin-right: 8px;
}
</style>---
Custom Icon Classes
Use third-party icon libraries (FontAwesome, Material Icons, etc.) with custom CSS classes.
FontAwesome Icons Example
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Edit" IconCss="fa fa-edit"></MenuItem>
<MenuItem Text="Delete" IconCss="fa fa-trash"></MenuItem>
<MenuItem Text="Download" IconCss="fa fa-download"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
/* Assuming FontAwesome CSS is loaded via CDN or npm */
</style>Steps to use custom icons: 1. Include icon library CSS in index.html (e.g., FontAwesome CDN) 2. Reference icon class in IconCss (e.g., fa fa-edit) 3. Syncfusion applies the class to the icon element 4. Browser renders icon from library
---
Navigation with Url Property
Use the Url property to navigate to other pages when menu item is clicked.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Syncfusion" Url="https://www.syncfusion.com"></MenuItem>
<MenuItem Text="GitHub" Url="https://www.github.com"></MenuItem>
<MenuItem Text="Stack Overflow" Url="https://stackoverflow.com"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Behavior:
- Clicking menu item with
Urlnavigates to specified URL - Default target is
_self(opens in current tab) - Useful for external links or page navigation
Navigation with Icons
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Home" Url="/home" IconCss="e-icons e-home"></MenuItem>
<MenuItem Text="About" Url="/about" IconCss="e-icons e-info"></MenuItem>
<MenuItem Text="Contact" Url="/contact" IconCss="e-icons e-mail"></MenuItem>
</MenuItems>
</SfContextMenu>---
Icon Positioning
By default, icons appear left of text. Customize position with CSS.
Left-Aligned Icons (Default)
<MenuItem Text="Edit" IconCss="e-icons e-edit"></MenuItem>Result: [Icon] Edit
Right-Aligned Icons
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem" CssClass="right-icons">
<MenuItems>
<MenuItem Text="Edit" IconCss="e-icons e-edit"></MenuItem>
<MenuItem Text="Delete" IconCss="e-icons e-delete"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
.right-icons.e-contextmenu-container .e-menu-item {
flex-direction: row-reverse;
}
.right-icons.e-contextmenu-container .e-menu-item .e-icons {
margin-left: 8px;
margin-right: 0;
}
</style>Result: Edit [Icon]
Custom Icon Colors
<style>
.e-contextmenu-container .e-menu-item .e-icons {
color: #2196F3; /* Blue */
}
.e-contextmenu-container .e-menu-item:hover .e-icons {
color: #FF5722; /* Orange on hover */
}
</style>---
Common Patterns
Pattern: File Context Menu with Icons
<MenuItems>
<MenuItem Text="Cut" IconCss="e-icons e-cut"></MenuItem>
<MenuItem Text="Copy" IconCss="e-icons e-copy"></MenuItem>
<MenuItem Text="Delete" IconCss="e-icons e-delete"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Properties" IconCss="e-icons e-settings"></MenuItem>
</MenuItems>Pattern: Navigation Menu with Icons
<MenuItems>
<MenuItem Text="Dashboard" Url="/dashboard" IconCss="e-icons e-home"></MenuItem>
<MenuItem Text="Reports" Url="/reports" IconCss="e-icons e-chart"></MenuItem>
<MenuItem Text="Settings" Url="/settings" IconCss="e-icons e-settings"></MenuItem>
</MenuItems>Pattern: Mixed Icons and Text
<MenuItems>
<MenuItem Text="Edit Profile" IconCss="e-icons e-edit"></MenuItem>
<MenuItem Text="Preferences"></MenuItem>
<MenuItem Text="Logout" IconCss="e-icons e-exit"></MenuItem>
</MenuItems>Menu Item Management
Table of Contents
- Disabled Property
- Static Disable
- Dynamic Enable/Disable
- Conditional Disabling with OnOpen
- Separator Property
Disabled Property
The Disabled property controls whether a menu item is active or grayed out/inactive.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste" Disabled="true"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Result: "Paste" appears grayed out and cannot be clicked.
---
Static Disable
Disable items at component definition time (hardcoded).
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Edit" Disabled="false"></MenuItem>
<MenuItem Text="Delete" Disabled="true"></MenuItem>
<MenuItem Text="Archive" Disabled="false"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Use Case: Fixed permissions (e.g., only admins see Delete)
---
Dynamic Enable/Disable
Use component state to dynamically toggle item disabled status.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Undo" Disabled="@isUndoDisabled"></MenuItem>
<MenuItem Text="Redo" Disabled="@isRedoDisabled"></MenuItem>
<MenuItem Text="Copy" Disabled="@isCopyDisabled"></MenuItem>
<MenuItem Text="Paste" Disabled="@isPasteDisabled"></MenuItem>
</MenuItems>
</SfContextMenu>
<div style="margin-top: 20px;">
<button @onclick="PerformAction">Perform Action</button>
<button @onclick="ToggleCopyState">Toggle Copy</button>
</div>
@code {
private bool isUndoDisabled = true;
private bool isRedoDisabled = true;
private bool isCopyDisabled = false;
private bool isPasteDisabled = true;
private void PerformAction() {
// Enable Undo if action was performed
isUndoDisabled = false;
// Disable Redo when new action is performed
isRedoDisabled = true;
}
private void ToggleCopyState() {
isCopyDisabled = !isCopyDisabled;
// If copy is disabled, paste must be disabled too
if (isCopyDisabled) {
isPasteDisabled = true;
}
}
}
<style>
#target {
border: 1px dashed; height: 100px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Logic:
isUndoDisabled= true initially (no history)- User performs action →
isUndoDisabled = false - Paste only enabled if something was copied
- Dynamic state binding with
Disabled="@property"
---
Conditional Disabling with OnOpen
Use the OnOpen event to conditionally disable sub-menu items based on parent context.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="View">
<MenuItems>
<MenuItem Text="Large Icons" Disabled="@largIconsDisabled"></MenuItem>
<MenuItem Text="Medium Icons" Disabled="@mediumIconsDisabled"></MenuItem>
<MenuItem Text="Small Icons"></MenuItem>
</MenuItems>
</MenuItem>
<MenuItem Text="Sort By"></MenuItem>
<MenuItem Text="Refresh"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="New"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Display Settings" Disabled="true"></MenuItem>
<MenuItem Text="Personalize"></MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" OnOpen="@BeforeOpenHandler"></MenuEvents>
</SfContextMenu>
@code {
private bool largIconsDisabled = false;
private bool mediumIconsDisabled = false;
private void BeforeOpenHandler(BeforeOpenCloseMenuEventArgs<MenuItem> e) {
// OnOpen fires for sub-menu opening
if (e.ParentItem != null && e.ParentItem.Text == "View") {
// Toggle disabled state for Medium Icons only when View submenu opens
mediumIconsDisabled = !mediumIconsDisabled;
}
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Key Points:
BeforeOpenCloseMenuEventArgs<MenuItem>provides event datae.ParentItem- Parent menu item being opened- Check parent text to identify which submenu opened
- Modify disabled state before submenu displays
Advanced: Disable Based on Selection
@code {
private MenuItem selectedItem;
private void BeforeOpenHandler(BeforeOpenCloseMenuEventArgs<MenuItem> e) {
if (e.ParentItem?.Text == "Edit") {
// Only enable Delete if something is selected
if (selectedItem == null) {
e.Items.FirstOrDefault(x => x.Text == "Delete").Disabled = true;
}
}
}
}---
Separator Property
Use Separator="true" to add visual dividing lines between menu groups.
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Select All"></MenuItem>
<MenuItem Text="Find"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Delete"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
</style>Result: Menu items grouped into 3 sections by separator lines.
Conditional Separator
@code {
private bool showAdvanced = false;
private void OnInitialized() {
// Dynamically decide to show separator
}
}
<MenuItems>
<MenuItem Text="Basic Action 1"></MenuItem>
<MenuItem Text="Basic Action 2"></MenuItem>
@if (showAdvanced) {
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Advanced Action 1"></MenuItem>
<MenuItem Text="Advanced Action 2"></MenuItem>
}
</MenuItems>---
Complete Example: Role-Based Item Management
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem">
<MenuItems>
<MenuItem Text="View" Disabled="@(!HasPermission("view"))"></MenuItem>
<MenuItem Text="Edit" Disabled="@(!HasPermission("edit"))"></MenuItem>
<MenuItem Text="Delete" Disabled="@(!HasPermission("delete"))"></MenuItem>
<MenuItem Separator="true"></MenuItem>
<MenuItem Text="Share" Disabled="@(!HasPermission("share"))"></MenuItem>
<MenuItem Text="Archive" Disabled="@(!HasPermission("archive"))"></MenuItem>
</MenuItems>
<MenuEvents TValue="MenuItem" ItemSelected="@OnItemSelected"></MenuEvents>
</SfContextMenu>
<p>User Role: <strong>@currentRole</strong></p>
<p>Last Action: <strong>@lastAction</strong></p>
@code {
private string currentRole = "viewer"; // admin, editor, viewer
private string lastAction = "None";
// Permission matrix
private Dictionary<string, string[]> rolePermissions = new() {
{ "admin", new[] { "view", "edit", "delete", "share", "archive" } },
{ "editor", new[] { "view", "edit", "share" } },
{ "viewer", new[] { "view" } }
};
private bool HasPermission(string action) {
return rolePermissions[currentRole].Contains(action);
}
private void OnItemSelected(MenuEventArgs<MenuItem> args) {
lastAction = args.Item.Text;
}
private void SwitchRole(string role) {
currentRole = role;
StateHasChanged();
}
}
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
.role-buttons {
margin-top: 20px;
display: flex; gap: 10px;
}
</style>
<div class="role-buttons">
<button @onclick="() => SwitchRole(\"admin\")">Admin</button>
<button @onclick="() => SwitchRole(\"editor\")">Editor</button>
<button @onclick="() => SwitchRole(\"viewer\")">Viewer</button>
</div>Features:
- Role-based item visibility
- Permission matrix
- Dynamic disable based on role
- Real-time role switching
---
Best Practices
1. Disable vs Hide: Use Disabled for "available but not now", hide for "never available" 2. Separate Groups: Use Separator to logically group related actions 3. Clear Feedback: Disabled items should appear visually distinct (grayed out) 4. Explain Why: Consider tooltips for disabled items (e.g., "Paste disabled: nothing copied") 5. Consistency: Keep permission logic consistent across all menus
Styling and Appearance
Table of Contents
- CSS Class Reference
- Override Default Styles
- Focus and Selected States
- Theme Customization
- Common Styling Examples
CSS Class Reference
The ContextMenu component uses these CSS classes for styling:
| CSS Class | Target | Purpose |
|---|---|---|
.e-contextmenu-container | Container | Main wrapper for entire menu |
.e-contextmenu-container .e-menu-parent | Parent menu | Top-level menu container |
.e-contextmenu-container ul .e-menu-item | Menu items | Individual menu item styling |
.e-contextmenu-container ul .e-menu-item.e-focused | Focused item | Item on keyboard focus or hover |
.e-contextmenu-container ul .e-menu-item.e-selected | Selected item | Currently selected item |
.e-contextmenu-container ul .e-menu-item.e-disabled | Disabled item | Disabled menu items (grayed out) |
.e-contextmenu-container ul .e-menu-item .e-menu-icon | Icon area | Container for item icons |
.e-contextmenu-container ul .e-menu-item.e-selected .e-caret::before | Caret icon | Submenu indicator icon |
.e-contextmenu-container .e-menu-parent.e-ul | Submenu | Nested submenu list |
---
Override Default Styles
Customize menu appearance by overriding default CSS classes.
Change Background and Text Color
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem" CssClass="custom-dark">
<MenuItems>
<MenuItem Text="Cut"></MenuItem>
<MenuItem Text="Copy"></MenuItem>
<MenuItem Text="Paste"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
/* Dark theme override */
.custom-dark.e-contextmenu-container {
background-color: #2c3e50;
border: 1px solid #34495e;
border-radius: 4px;
}
.custom-dark.e-contextmenu-container .e-menu-item {
color: #ecf0f1;
padding: 8px 12px;
}
.custom-dark.e-contextmenu-container .e-menu-item:hover {
background-color: #34495e;
}
</style>Change Border and Padding
<style>
.custom-menu.e-contextmenu-container {
border: 2px solid #3498db;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
.custom-menu.e-contextmenu-container .e-menu-item {
padding: 10px 16px;
font-weight: 500;
}
</style>---
Focus and Selected States
Customize how menu items appear when focused or selected.
Highlight on Hover/Focus
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem" CssClass="highlight-focus">
<MenuItems>
<MenuItem Text="Edit"></MenuItem>
<MenuItem Text="View"></MenuItem>
<MenuItem Text="Delete"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
/* Focused item with left border accent */
.highlight-focus.e-contextmenu-container .e-menu-item.e-focused {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding-left: 12px;
}
.highlight-focus.e-contextmenu-container .e-menu-item:hover {
background-color: #f5f5f5;
}
</style>Custom Selected Indicator
<style>
.custom-selected.e-contextmenu-container .e-menu-item.e-selected {
background-color: #2196F3;
color: white;
border-radius: 3px;
}
.custom-selected.e-contextmenu-container .e-menu-item.e-selected .e-caret::before {
color: white;
}
</style>---
Theme Customization
Syncfusion supports multiple built-in themes. Switch themes by changing the CSS file in index.html.
Available Themes
Bootstrap 5 (Default)
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />Material Design
<link href="_content/Syncfusion.Blazor.Themes/material.css" rel="stylesheet" />Microsoft Fluent
<link href="_content/Syncfusion.Blazor.Themes/fluent.css" rel="stylesheet" />Tailwind CSS
<link href="_content/Syncfusion.Blazor.Themes/tailwind.css" rel="stylesheet" />Theme-Specific Overrides
<style>
/* Override for Bootstrap 5 theme */
.e-contextmenu-container {
/* Customization */
}
/* For Material theme, use matching color palette */
.material .e-contextmenu-container .e-menu-item:hover {
background-color: rgba(0, 0, 0, 0.04);
}
</style>---
Common Styling Examples
Example 1: Modern Minimal Style
@using Syncfusion.Blazor.Navigations
<div id="target">Right click to open</div>
<SfContextMenu Target="#target" TValue="MenuItem" CssClass="minimal">
<MenuItems>
<MenuItem Text="Edit"></MenuItem>
<MenuItem Text="Share"></MenuItem>
<MenuItem Text="Delete"></MenuItem>
</MenuItems>
</SfContextMenu>
<style>
#target {
border: 1px dashed; height: 150px; padding: 10px;
position: relative; text-align: center; color: gray;
}
.minimal.e-contextmenu-container {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
padding: 4px 0;
}
.minimal.e-contextmenu-container .e-menu-item {
padding: 8px 16px;
font-size: 14px;
color: #333;
transition: background-color 0.2s ease;
}
.minimal.e-contextmenu-container .e-menu-item.e-focused {
background-color: #f5f5f5;
border-left: 3px solid #2196F3;
padding-left: 13px;
}
</style>Example 2: Colored Categories
<style>
.category-colors.e-contextmenu-container .e-menu-item[data-category="edit"] {
border-left: 3px solid #4CAF50;
}
.category-colors.e-contextmenu-container .e-menu-item[data-category="danger"] {
border-left: 3px solid #f44336;
}
.category-colors.e-contextmenu-container .e-menu-item[data-category="action"] {
border-left: 3px solid #2196F3;
}
.category-colors.e-contextmenu-container .e-menu-item.e-focused {
background-color: #fafafa;
}
</style>Example 3: Compact Menu (Mobile-friendly)
<style>
.compact.e-contextmenu-container {
min-width: 140px;
}
.compact.e-contextmenu-container .e-menu-item {
padding: 6px 12px;
font-size: 13px;
min-height: 30px;
}
.compact.e-contextmenu-container .e-menu-item .e-icons {
font-size: 12px;
margin-right: 6px;
}
</style>Example 4: High Contrast (Accessibility)
<style>
.high-contrast.e-contextmenu-container {
background-color: #000;
border: 2px solid #fff;
}
.high-contrast.e-contextmenu-container .e-menu-item {
color: #fff;
padding: 10px 14px;
}
.high-contrast.e-contextmenu-container .e-menu-item.e-focused {
background-color: #fff;
color: #000;
}
.high-contrast.e-contextmenu-container .e-menu-item.e-disabled {
color: #888;
}
</style>---
CSS Variables (Advanced)
Some themes support CSS custom properties for dynamic theming:
:root {
--primary-color: #2196F3;
--hover-color: #1976D2;
--text-color: #333;
--border-color: #e0e0e0;
}
.e-contextmenu-container {
border-color: var(--border-color);
}
.e-contextmenu-container .e-menu-item.e-focused {
background-color: var(--hover-color);
color: white;
}---
Tips for Styling
1. Specificity: Use .custom-class.e-contextmenu-container to ensure override 2. Hover states: Include :hover and .e-focused for interactive feedback 3. Icons: Style .e-icons color and size separately 4. Animations: Add transition properties for smooth effects 5. Accessibility: Maintain sufficient color contrast (WCAG AA standard)