c2-navigation-menu is the bar at the top of a site. Each c2-navigation-menu-item child is either a plain link (href) or, as soon as anything is slotted into its panel, a trigger that opens that content below the bar — a stack of c2-navigation-menu-link rows, a multi-column mega panel, or any markup you lay out yourself. Mark the entry of the page being shown current to get aria-current="page" and the indicator line.
A trigger follows its own panel. While the current page is one of the panel’s rows — a currentc2-navigation-menu-link, or any element carrying aria-current — the item sets current-group on itself and wears the same styling as a current item, so the closed bar still shows which section the visitor is in. It keeps working as routes change: marking a different row current moves the group. Only the row keeps aria-current, since the trigger is the group and not the page.
The bar owns which panel is open: value holds the value of that item and '' when everything is closed, and value-change fires on every change. Panels open on hover after open-delay and close close-delay after the pointer has left both the item and the panel, so crossing the gap between them is forgiving; a click on a trigger opens its panel straight away. Set open-on="click" for a bar that ignores hover, where a second click closes the panel again.
Each panel is a c2-overlay popover on the top layer, so a header with overflow: hidden or a stacking context never clips it, and it flips or shifts to stay in the viewport. Dismissal belongs to the bar rather than to the browser: a press anywhere outside it, or Escape, closes the open panel.
The bar is always one row — it never wraps. mode="mobile" is the other layout: the row becomes a single button that opens the whole navigation as one hierarchy, every plain link a row and every panel item a heading with its rows underneath. Add collapsible and those groups open one at a time instead, the open one being value exactly as in the bar, and the list opens on the group holding the current page. Set mobile-breakpoint to a viewport width and the bar switches itself below it; leave it unset and mode is yours to drive.
Keyboard: every item is a tab stop. On a trigger, ArrowLeft / ArrowRight and Home / End walk the bar — and while a panel is open, moving along keeps browsing panels — ArrowDown, Enter or Space opens the panel and moves focus to its first link, and Escape closes it and returns focus to the trigger. Tab from an open trigger walks straight into the panel.
Installation
npm install @c2n/navigation-menu
c2-navigation-menu-item and c2-navigation-menu-link ship in the same package and are registered with it.
Usage
Hover or click a trigger to open its panel.
Products
Analytics
Realtime dashboards over live events
Warehouse
Columnar storage that scales
Pipelines
Scheduled and streaming ETL
Catalog
Lineage, ownership and quality
See all products
Solutions
StartupsEnterprisePublic sector
DocsPricing
Platform
Overview
What the platform does
Security
SOC 2, SSO and audit logs
Status
Opens in a new tab
CompanyChangelog
Fixed Income Products
Reference DataCorporate ActionsPricing
Regulatory Products
ERISA Portfolio AnalyticsMatching Adjustment
HomeAbout
Menu
Fixed Income Products
Reference DataCorporate Actions
Home
Fixed Income Products
Reference DataCorporate Actions
Regulatory Products
ERISA Portfolio AnalyticsMatching Adjustment
Home
Resources
Guides
Step by step tutorials
API reference
Every endpoint and type
mobile-breakpoint is a viewport width in pixels: at or below it the bar sets mode="mobile" itself, above it it goes back to bar. Nothing else changes — the same markup, the same value, the same value-change.
mode reflects, so the current layout is in the DOM (c2-navigation-menu[mode='mobile']) for your own CSS to hang off.
Leave mobile-breakpoint unset when the switch depends on something the window width does not describe — a container width, a user setting, an app-wide breakpoint you already track — and drive mode yourself:
const nav = document.querySelector('c2-navigation-menu')const small = matchMedia('(max-width: 800px)')const apply = () => { nav.mode = small.matches ? 'mobile' : 'bar' nav.collapsible = small.matches // optional: one group open at a time}apply()small.addEventListener('change', apply)
Customizing the mobile button
The default button is a hamburger named by mobile-label, themed through --c2-navigation-menu__mobile-trigger--* (size, colour, background, border, radius, icon size, plus __hover and __focus). Put your own control in the mobile-trigger slot to replace it outright — the list still opens from it, anchors to it and returns focus to it.
value-change fires whenever the open panel changes, including when the bar closes. Drive it from code with open(value) and close(), or by setting value.
const nav = document.querySelector('c2-navigation-menu')nav.addEventListener('value-change', (event) => { console.log(event.detail.value) // 'products', then '' when it closes})nav.open('products')nav.close()// An item without a `value` is given `item-<position>` so it stays addressable.nav.value = 'item-2'
Theming
--c2-navigation-menu--* covers the bar itself (gap, padding, background, border, radius). --c2-navigation-menu-item--* is a trigger, with __hover, __expanded, __current, __focus and __disabled states, an __icon part and the __indicator line under the current or open item. The panel surface hangs off the item as --c2-navigation-menu-item__panel--*, and the rows inside it are --c2-navigation-menu-link--*, including __description and __current.
current-group is an attribute like any other, so a group that should read more quietly than the current page can be styled apart: c2-navigation-menu-item[current-group] { --c2-navigation-menu-item__indicator--color: #d4d4d8 }.
The panel surface belongs to the item, so set --c2-navigation-menu-item__panel--* on the item that needs it. Put a panel width on the bar instead and every panel of that bar inherits it — a mega panel and a short link stack then come out exactly the same width. Left alone, each panel is as wide as its own content, with the 260px floor underneath.
The indicator is the line under the item that is current, holds the current page, or has its panel open. It spans the item’s whole width and squares off the bottom corners of an open trigger so the two read as one surface. Take it away entirely with a height of 0 — the states are still marked by their colour and background:
c2-navigation-menu.no-indicator { --c2-navigation-menu-item__indicator--height: 0; /* Nothing meets the panel any more, so the open trigger can keep its rounded corners. */ --c2-navigation-menu-item__expanded--border-bottom-left-radius: 6px; --c2-navigation-menu-item__expanded--border-bottom-right-radius: 6px; --c2-navigation-menu-item__expanded--background: #e4e4e7;}
Or move it: --c2-navigation-menu-item__indicator--inset pulls it in from both ends, --c2-navigation-menu-item__indicator--bottom lifts it off the edge, and --c2-navigation-menu-item__indicator--height and --c2-navigation-menu-item__indicator--color do the rest.
The mobile layout has its own families: --c2-navigation-menu__mobile-trigger--* for the button, --c2-navigation-menu__mobile-panel--* for the list surface (set its min-width to calc(100vw - 32px) for a sheet that spans the screen), and on the item --c2-navigation-menu-item__mobile-heading--* and --c2-navigation-menu-item__mobile-group--indent for the group headings and their inset rows.
The panel is positioned by c2-overlay: --c2-overlay--offset-y sets the gap under the bar and --c2-overlay--viewport-padding how close a panel may come to the window edge. The panel content is your own markup, so give it a layout that survives the mobile list: a repeat(auto-fit, minmax(…, 1fr)) grid stacks on its own, a fixed two-column one does not.
Every variable, with its default, is listed on the API tab, and the Gallery tab has ready-made looks to start from.
c2-navigation-menu is the bar at the top of a site. Each c2-navigation-menu-item child is either a plain link (href) or, as soon as anything is slotted into its panel, a trigger that opens that content below the bar — a stack of c2-navigation-menu-link rows, a multi-column mega panel, or any markup you lay out yourself. Mark the entry of the page being shown current to get aria-current="page" and the indicator line.
A trigger follows its own panel. While the current page is one of the panel’s rows — a currentc2-navigation-menu-link, or any element carrying aria-current — the item sets current-group on itself and wears the same styling as a current item, so the closed bar still shows which section the visitor is in. It keeps working as routes change: marking a different row current moves the group. Only the row keeps aria-current, since the trigger is the group and not the page.
The bar owns which panel is open: value holds the value of that item and '' when everything is closed, and value-change fires on every change. Panels open on hover after open-delay and close close-delay after the pointer has left both the item and the panel, so crossing the gap between them is forgiving; a click on a trigger opens its panel straight away. Set open-on="click" for a bar that ignores hover, where a second click closes the panel again.
Each panel is a c2-overlay popover on the top layer, so a header with overflow: hidden or a stacking context never clips it, and it flips or shifts to stay in the viewport. Dismissal belongs to the bar rather than to the browser: a press anywhere outside it, or Escape, closes the open panel.
The bar is always one row — it never wraps. mode="mobile" is the other layout: the row becomes a single button that opens the whole navigation as one hierarchy, every plain link a row and every panel item a heading with its rows underneath. Add collapsible and those groups open one at a time instead, the open one being value exactly as in the bar, and the list opens on the group holding the current page. Set mobile-breakpoint to a viewport width and the bar switches itself below it; leave it unset and mode is yours to drive.
Keyboard: every item is a tab stop. On a trigger, ArrowLeft / ArrowRight and Home / End walk the bar — and while a panel is open, moving along keeps browsing panels — ArrowDown, Enter or Space opens the panel and moves focus to its first link, and Escape closes it and returns focus to the trigger. Tab from an open trigger walks straight into the panel.
Installation
npm install @c2n/navigation-menu
c2-navigation-menu-item and c2-navigation-menu-link ship in the same package and are registered with it.
Usage
Hover or click a trigger to open its panel.
Products
Analytics
Realtime dashboards over live events
Warehouse
Columnar storage that scales
Pipelines
Scheduled and streaming ETL
Catalog
Lineage, ownership and quality
See all products
Solutions
StartupsEnterprisePublic sector
DocsPricing
Platform
Overview
What the platform does
Security
SOC 2, SSO and audit logs
Status
Opens in a new tab
CompanyChangelog
Fixed Income Products
Reference DataCorporate ActionsPricing
Regulatory Products
ERISA Portfolio AnalyticsMatching Adjustment
HomeAbout
Menu
Fixed Income Products
Reference DataCorporate Actions
Home
Fixed Income Products
Reference DataCorporate Actions
Regulatory Products
ERISA Portfolio AnalyticsMatching Adjustment
Home
Resources
Guides
Step by step tutorials
API reference
Every endpoint and type
mobile-breakpoint is a viewport width in pixels: at or below it the bar sets mode="mobile" itself, above it it goes back to bar. Nothing else changes — the same markup, the same value, the same value-change.
mode reflects, so the current layout is in the DOM (c2-navigation-menu[mode='mobile']) for your own CSS to hang off.
Leave mobile-breakpoint unset when the switch depends on something the window width does not describe — a container width, a user setting, an app-wide breakpoint you already track — and drive mode yourself:
const nav = document.querySelector('c2-navigation-menu')const small = matchMedia('(max-width: 800px)')const apply = () => { nav.mode = small.matches ? 'mobile' : 'bar' nav.collapsible = small.matches // optional: one group open at a time}apply()small.addEventListener('change', apply)
Customizing the mobile button
The default button is a hamburger named by mobile-label, themed through --c2-navigation-menu__mobile-trigger--* (size, colour, background, border, radius, icon size, plus __hover and __focus). Put your own control in the mobile-trigger slot to replace it outright — the list still opens from it, anchors to it and returns focus to it.
value-change fires whenever the open panel changes, including when the bar closes. Drive it from code with open(value) and close(), or by setting value.
const nav = document.querySelector('c2-navigation-menu')nav.addEventListener('value-change', (event) => { console.log(event.detail.value) // 'products', then '' when it closes})nav.open('products')nav.close()// An item without a `value` is given `item-<position>` so it stays addressable.nav.value = 'item-2'
Theming
--c2-navigation-menu--* covers the bar itself (gap, padding, background, border, radius). --c2-navigation-menu-item--* is a trigger, with __hover, __expanded, __current, __focus and __disabled states, an __icon part and the __indicator line under the current or open item. The panel surface hangs off the item as --c2-navigation-menu-item__panel--*, and the rows inside it are --c2-navigation-menu-link--*, including __description and __current.
current-group is an attribute like any other, so a group that should read more quietly than the current page can be styled apart: c2-navigation-menu-item[current-group] { --c2-navigation-menu-item__indicator--color: #d4d4d8 }.
The panel surface belongs to the item, so set --c2-navigation-menu-item__panel--* on the item that needs it. Put a panel width on the bar instead and every panel of that bar inherits it — a mega panel and a short link stack then come out exactly the same width. Left alone, each panel is as wide as its own content, with the 260px floor underneath.
The indicator is the line under the item that is current, holds the current page, or has its panel open. It spans the item’s whole width and squares off the bottom corners of an open trigger so the two read as one surface. Take it away entirely with a height of 0 — the states are still marked by their colour and background:
c2-navigation-menu.no-indicator { --c2-navigation-menu-item__indicator--height: 0; /* Nothing meets the panel any more, so the open trigger can keep its rounded corners. */ --c2-navigation-menu-item__expanded--border-bottom-left-radius: 6px; --c2-navigation-menu-item__expanded--border-bottom-right-radius: 6px; --c2-navigation-menu-item__expanded--background: #e4e4e7;}
Or move it: --c2-navigation-menu-item__indicator--inset pulls it in from both ends, --c2-navigation-menu-item__indicator--bottom lifts it off the edge, and --c2-navigation-menu-item__indicator--height and --c2-navigation-menu-item__indicator--color do the rest.
The mobile layout has its own families: --c2-navigation-menu__mobile-trigger--* for the button, --c2-navigation-menu__mobile-panel--* for the list surface (set its min-width to calc(100vw - 32px) for a sheet that spans the screen), and on the item --c2-navigation-menu-item__mobile-heading--* and --c2-navigation-menu-item__mobile-group--indent for the group headings and their inset rows.
The panel is positioned by c2-overlay: --c2-overlay--offset-y sets the gap under the bar and --c2-overlay--viewport-padding how close a panel may come to the window edge. The panel content is your own markup, so give it a layout that survives the mobile list: a repeat(auto-fit, minmax(…, 1fr)) grid stacks on its own, a fixed two-column one does not.
Every variable, with its default, is listed on the API tab, and the Gallery tab has ready-made looks to start from.
escAccordionConnected, animated panels with shared borders and single or multiple expansion.LayoutAvatarImage, initials or icon for a person, with status dot and badge.Data displayBadgeTinted pill for status text, counts and dots, optionally pinned to a corner of another element.Data displayBreadcrumbNavigation trail of link buttons with separators, a current page and optional collapsing.NavigationButtonThemeable button with slots for text, prefix, suffix and running icons.ButtonsButton GroupAttached buttons with shared borders, optionally a segmented control with single or multiple selection.ButtonsCardGroups related content and actions on a single bordered surface.LayoutChat InputAuto-growing message box with a send button for chat interfaces.ChatChat MessageOne chat bubble with avatar, title, timestamp and message body.ChatCheckboxNative checkbox behaviour in a quiet, themeable box with an opt-in hover layer.InputsCode ViewerSyntax-highlighted code with line numbers, copy button and dark mode, powered by shiki.Data displayColor AreaTwo-dimensional area for picking saturation and value of a colour.InputsColor SelectColour swatch that opens a full picker built from area and slider.InputsColor SliderHorizontal slider for choosing a hue from 0 to 360.InputsCopy ButtonButton that copies text to the clipboard — the element it sits in, another element by id, or a literal string.ButtonsDetailsCollapsible disclosure built on native details and summary.LayoutIcon ButtonRound, hoverable button wrapping a slotted SVG icon.ButtonsKbdKeyboard key label for shortcuts and command hints, with the semantics of the native kbd element.Data displayLabelCaption that names and activates the control referenced by its for attribute, with a required marker.InputsLink ButtonText-styled control for link and navigation actions, rendered as a real anchor or a button.NavigationListVertical list container with single or multiple selection.Data displayList ItemSelectable row with icon slots, used on its own or as the option of list and select.Data displayMenuCommands, links, checkboxes and submenus in a popover anchored to a trigger.NavigationModalDialog built on the native dialog element: focus trap, backdrop, Escape, title, body and footer.FeedbackNavigation MenuSite navigation bar whose triggers open panels of links below the header.NavigationOverlayAnchored popup built on the browser Popover API, positioned with floating-ui.FeedbackPaginationPage navigation in three layouts: numbered pages, a simple page status, or a table-footer row with rows-per-page.NavigationProgressLinear progress bar, indeterminate or filling to a value, with an optional label and count.FeedbackRadioRadio options built on native inputs, grouped into one value with keyboard navigation.InputsSelectDropdown that pairs a themeable trigger with an anchored list of c2-list-item options.InputsSeperatorHorizontal or vertical rule with an optional label, for dividing content and toolbars.LayoutSheetDialog pinned to an edge of the screen, for content that complements the page rather than interrupting it.FeedbackSide NavResponsive navigation drawer beside the page: pushes the content on large screens, slides over it with a backdrop on small ones.NavigationSkeletonPlaceholder block standing in for content that has not arrived, in three shapes and three animations.FeedbackSliderRange input with a themeable track, thumb, step ticks and value bubble.InputsSpinnerCircular progress indicator, indeterminate or showing a value, with optional text.FeedbackSwitchOn/off toggle on a native switch input, with label, description and thumb icons.InputsTableVirtualized data grid with declarative columns, sorting, selection, pinning and resizing.Data displayTabsTab strip that shows one content panel at a time.NavigationText FieldSingle-line input with icon slots, clear button, helper and error text, and a character counter.InputsTextareaMultiline text input with resizing, helper and error text, and a character counter.InputsToastNotification cards and a manager for stacked, queued notifications with independent lifetimes.FeedbackTooltipContextual hint shown when its target is hovered or focused, rendered in the top layer.FeedbackFeather Icons287 open-source Feather icons, one web component each.IconsMat Icon2,234 Material Icons ligatures rendered through a single element.IconsPhosphor Icons1,512 flexible icons in six weights, one web component each.IconsThemingTheme every c2n component from a handful of design tokens with @c2n/theme, or reach for any component variable directly.GuideUsing c2n in an applicationThe workflow behind every c2n app: load the theme once, use c2-* tags directly, and turn every repeated pattern into a small variant or composed component.Guide