OverviewGetting startedComponentsIconsGuidesExamples
OverviewGetting startedComponentsIconsGuidesExamples
GitHub repositoryJoin the c2n-webcomponents Discord

Flow

A pipeline, dependency graph or database schema on a pannable canvas: steps with a status, smooth edges, rows joined row to row, auto layout, draggable nodes saved to localStorage, a hover card and a context menu.

c2-flow draws a directed graph such as a CI pipeline, a data job or a training run. Give it nodes and edges (as properties, or as JSON attributes); every node carries a status, and each edge takes its look from the statuses at both ends: an edge into a running step animates, one out of a failed step turns red and one touching a skipped step is dotted. Every edge ends in an arrowhead at its target, in the edge’s own colour, so the direction of a dependency reads at a glance. An edge with a label shows it halfway along, in a small pill: the “yes” and “no” on the branches of a decision.

The flow lays itself out: one column per dependency depth (direction="LR") or one row ("TB"), with siblings ordered to avoid crossings. Cycles are allowed and their closing edge loops round the outside. Drag a node to arrange the graph yourself; with a storage-key the arrangement and the chosen direction are saved in localStorage and restored on the next visit, and when the graph has changed in between, the saved layout is merged: kept steps stay put, new ones are placed next to their neighbours.

Right-click the canvas or a node (or long-press it, or press Shift+F10) for the view controls: zoom in, zoom out, fit view, layout direction, auto layout and lock layout. With focus in the flow, Ctrl/⌘ + +, Ctrl/⌘ + - and Ctrl/⌘ + 0 zoom and fit, and Ctrl/⌘ + scroll zooms around the pointer. Hover or focus a node for a card with its status and details.

Import

import '@c2n/components/flow'

The examples use icons from the separate @c2n/feather-icons package: npm install @c2n/feather-icons.

The entry also registers c2-context-menu and c2-menu, which it renders internally.

Usage

Drag the steps, hover them, and right-click the canvas. The fourth row draws its nodes with renderNode; the fifth is a plain decision diagram, with labelled branches and the status marker hidden; the sixth places its nodes itself and runs edges between chosen sides (see Sides); the seventh is editable, with an action toolbar on its left edge (add node, auto layout, fit view): double-click the canvas or use + to add a note, drag one of the four handles on a node’s sides to connect it from that side, double-click a node or an edge to rename it or label it, and press Delete to remove what you selected. The last one is a database schema (see Rows and schema diagrams): hover a column to follow its foreign key, drag the handle of a column onto a column of another table to link them, click + 4 more on a long table to show its other columns, and drag a column by the grip at its start to reorder the table.

Editing

With editable the user draws the diagram. The flow stays controlled, like a form field with a value: it never changes nodes or edges itself, it fires an event and your application applies the change (and can refuse it, or save it first). None of the events bubble.

GestureKeyboardEvent
Double-click empty canvas, or Add node in the canvas menuN (beside the focused node, else in the middle of the view), or Enter on the canvas of an empty flownode-add { position }
Drag a node’s side handle and release on empty canvasN on the selected nodenode-add { position, source, sourceSide, targetSide }
Drag a node’s side handle onto another nodeC, then the arrow keys or Tab to choose, Enter to connect, Escape to canceledge-add { source, target, sourceSide, targetSide }
Double-click a node, or Rename in its menuF2, or Enter on the selected node; Enter commits, Escape cancelsnode-edit { id, label }
Double-click an edge or its label, or Edit label in its menuF2 or Enter on the selected edge; Enter commits, Escape cancelsedge-edit { source, target, label }
Delete in a node’s menuDelete or Backspace on the focused nodenode-delete { id }
Click an edge, then Delete; or Delete in its menuE on a node selects its edges in turn, then Deleteedge-delete { source, target }

edge-add never names a self-loop or an edge that already exists. position is in canvas pixels, the top-left corner of the new node: give it to the node as its position and the flow puts it there. While the flow is editable, a change to the graph never moves the nodes that are already on the canvas: their positions are pinned as a custom layout, saved under storage-key and reported by layout-change with the reason edit. A node the keyboard or addNode() asked for is selected and focused once you add it, and the view pans to show it when it landed out of sight.

An editable node shows a handle in the middle of each of its four sides while it is hovered, focused or selected, and a connection leaves by the side whose handle was dragged. Released on a node, it arrives by the handle it was dropped on, or else by the side of that node nearest the pointer; the handle of that side lights up while the connection is drawn. edge-add reports both as sourceSide and targetSide (top, right, bottom or left): add them to the edge and it is drawn between those sides. Dropped on empty canvas, node-add carries sourceSide and the new node’s targetSide, the side facing back to the source, which position puts on the drop point. A connection made with the keyboard (C) goes from the outgoing side of the direction to the incoming one: right to left in LR, bottom to top in TB.

no-double-click-add turns off adding a node by double-clicking (or double-tapping) empty canvas, for an editor whose toolbar has an add button: a double-click on the canvas then never drops a node where the user only meant to deselect. Add node in the canvas menu, N and addNode() still add one.

An edge’s label is edited in the same inline field, opened halfway along the edge. edge-edit carries the trimmed new label, and an empty label when the user cleared it: remove the edge’s label then. editEdgeLabel(source, target) opens the field from script, for example on a branch you have just added.

The inline editor replaces the label of a node drawn by the flow. A node drawn by renderNode or a node:<id> slot gets the editor over its whole body and the same node-edit event, but must render node.label itself for the new name to show.

import type { FlowEdge, FlowEdgeEventDetail } from '@c2n/components/flow'

const flow = document.querySelector('c2-flow')!
let next = 1

flow.addEventListener('node-add', async ({ detail }) => {
  const id = `note-${next++}`
  flow.nodes = [...flow.nodes, { id, label: 'New note', position: detail.position }]
  if (detail.source) flow.edges = [...flow.edges, { source: detail.source, target: id, sourceSide: detail.sourceSide, targetSide: detail.targetSide }]
  await flow.updateComplete
  flow.editLabel(id) // let the user name it straight away
})
flow.addEventListener('node-edit', ({ detail }) => {
  flow.nodes = flow.nodes.map((node) => (node.id === detail.id ? { ...node, label: detail.label } : node))
})
flow.addEventListener('node-delete', ({ detail }) => {
  flow.nodes = flow.nodes.filter((node) => node.id !== detail.id)
  flow.edges = flow.edges.filter((edge) => edge.source !== detail.id && edge.target !== detail.id)
})
flow.addEventListener('edge-add', ({ detail }) => {
  flow.edges = [...flow.edges, detail] // source, target, and sourceSide/targetSide or sourcePort/targetPort
})
// The same two nodes can be joined between several pairs of rows, so the rows are part of an edge's identity.
const same = (edge: FlowEdge, detail: FlowEdgeEventDetail) =>
  edge.source === detail.source && edge.target === detail.target && edge.sourcePort === detail.sourcePort && edge.targetPort === detail.targetPort
flow.addEventListener('edge-delete', ({ detail }) => {
  flow.edges = flow.edges.filter((edge) => !same(edge, detail))
})
flow.addEventListener('edge-edit', ({ detail }) => {
  flow.edges = flow.edges.map((edge) => {
    if (!same(edge, detail)) return edge
    const next: FlowEdge = { ...edge, label: detail.label }
    if (!detail.label) delete next.label
    return next
  })
})

Actions on the canvas

Buttons slotted into actions form a toolbar over the canvas: the place for the commands a diagram editor keeps at hand, such as add node, auto layout or fit view. It sits at the top by default; actions-placement="right", "bottom" or "left" moves it to that edge, where the buttons stack in a column. It is a toolbar for assistive technology, and the arrow keys (along its orientation), Home and End move between its buttons. Clicks on it never pan the canvas or open the context menu.

Wire the buttons to the flow’s methods: addNode() asks for a node exactly as N does, through the usual node-add event: connected after the selected node, else beside the node that last had focus in the flow, else in a free spot in the middle of the view; repeated clicks with a node selected build a chain. dragNewNode(event), called from the same button’s pointerdown, lets the user drag the new node onto the canvas as a design tool drags a shape: a dashed placeholder (--c2-flow__ghost--border, --c2-flow__ghost--background-color) follows the pointer, and releasing it over the canvas asks for the node there, moved aside if it would cover another node; Escape or a release elsewhere, the toolbar included, cancels. A press that does not move, or comes back to the button, drops nothing, so the button’s click can still call addNode(). Give the button touch-action: none so a touch drag is not taken for a page scroll. resetLayout() animates back to the auto layout, and fitView(), zoomIn() and zoomOut() move the view. The panel is styled by --c2-flow__actions--background-color, --border, --border-radius, --box-shadow, --padding and --gap; --c2-flow__actions--align (flex-start, center, flex-end) slides it along its edge and --c2-flow__actions--offset sets its distance from the edge.

<c2-flow editable actions-placement="left">
  <c2-icon-button slot="actions" data-action="add" tooltip="Add node" aria-label="Add node"><c2-feather-plus></c2-feather-plus></c2-icon-button>
  <c2-icon-button slot="actions" data-action="layout" tooltip="Auto layout" aria-label="Auto layout"><c2-feather-layout></c2-feather-layout></c2-icon-button>
  <c2-icon-button slot="actions" data-action="fit" tooltip="Fit view" aria-label="Fit view"><c2-feather-maximize></c2-feather-maximize></c2-icon-button>
</c2-flow>
<script type="module">
  const flow = document.querySelector('c2-flow')
  const run = { add: () => flow.addNode(), layout: () => flow.resetLayout(), fit: () => flow.fitView() }
  flow.addEventListener('click', (event) => run[event.target.closest('[data-action]')?.dataset.action]?.())
</script>

Arrowheads, labels and the status marker

All three are styled through CSS variables. The arrowhead takes the colour of its edge in every state (idle, done, active, failed, selected and the edge being drawn); --c2-flow__arrow--size sizes it and --c2-flow__arrow--display: none turns it off. An edge label is drawn in a pill with its own background, so it stays readable over the line and the dot grid: --c2-flow__edge-label--background-color, --c2-flow__edge-label--color, --c2-flow__edge-label--font-size, --c2-flow__edge-label--padding, --c2-flow__edge-label--border and --c2-flow__edge-label--border-radius. Labels scale with the canvas, like the text of the nodes, and screen readers hear them with the relations of both nodes (“Before Book the night bus, yes; Walk to the station, no.”).

The marker at the start of each default node body shows the step’s status, which is what a pipeline needs and noise in a plain diagram. --c2-flow__marker--display: none hides it, and the label then starts flush with the node’s padding; the hover card keeps its marker.

c2-flow.diagram {
  --c2-flow__marker--display: none;
  --c2-flow__arrow--size: 10px;
  --c2-flow__edge-label--color: #18181b;
}

Sides

An edge leaves its source by the outgoing side of the direction and arrives by the incoming side (right to left in LR, bottom to top in TB), and an edge closing a cycle loops round the outside of the nodes. Give it a sourceSide or a targetSide (top, right, bottom or left) and it runs from the middle of that side instead, leaving and arriving perpendicular to it, with either edge-type: a side left out keeps its default. A step edge goes round both nodes rather than through them, and so does a bezier edge whose curve would cut through one, for example into the far side of a node behind its source. The dots where edges meet a node, its arrowhead and its label follow. The auto layout does not read the sides, so give the nodes a position when the sides only make sense in a layout of your own.

<c2-flow
  nodes='[{"id":"review","label":"Review","position":{"x":280,"y":0}},{"id":"fix","label":"Fix","position":{"x":280,"y":130}}, …]'
  edges='[{"source":"review","target":"fix","sourceSide":"bottom","targetSide":"top","label":"changes"}, …]'
></c2-flow>

Rows and schema diagrams

A node’s rows are drawn as a list under its label: the columns of a table, the fields of a class. Each row has an id, a label, an optional meta (a column type) and icon (a key), and details for a hover card of its own. Every row is a port: an edge that names it in sourcePort or targetPort leaves or arrives level with that row, on its left or right side, whichever faces the other end (or the one sourceSide / targetSide names). Two tables can be joined by several edges through different columns, and a table can be joined to itself between two of its columns.

sourceMarker and targetMarker draw the ends of an edge: an arrow (the default at the target), none (the default at the source), or a crow’s-foot cardinality, one, zero-one, many, one-many or zero-many, in the edge’s colour and width. --c2-flow__edge-marker--size sizes them.

<c2-flow
  edge-type="step"
  nodes='[{"id":"customers","label":"customers","rows":[{"id":"id","label":"id","meta":"uuid"}, …]},
          {"id":"orders","label":"orders","rows":[{"id":"id","label":"id","meta":"uuid"},{"id":"customer_id","label":"customer_id","meta":"uuid"}, …]}]'
  edges='[{"source":"customers","sourcePort":"id","target":"orders","targetPort":"customer_id","sourceMarker":"one","targetMarker":"many"}]'
></c2-flow>

Hovering a row highlights its edges and the rows at their far ends. Screen readers hear a node’s rows and the rows each of its edges joins (“After customers.id through customer_id.”). In an editable flow a node with rows is connected row to row: each row shows a handle on both sides, and edge-add names the rows in sourcePort and targetPort (as do node-add, edge-delete and edge-edit), so the application can store them with the cardinality it chooses. A body drawn by renderNode or a node:<id> slot marks its own ports with data-flow-port="<id>".

A long table can fold its columns: row-limit="4" on the flow (or rowLimit on one node) shows the first four rows and folds the rest into a + N more row, whose hover card lists them. Rows an edge meets always show, so no foreign key disappears. A click on that row, Shift+Enter on the table or Show all rows in its context menu shows every row (rows-toggle), and toggleRows(id, expanded?) does the same from script. With a storage-key, the open tables are saved with the layout.

In an editable flow the rows can be reordered. A hovered row shows a grip at its start: drag it and the row moves among the table’s shown rows as you go, its links with it, and on release the flow fires row-move with the row and its from and to indices in rows (Escape puts it back). Like every edit, the flow does not apply it: move the row in rows. From the keyboard, R on a focused table picks its first row, the arrows pick another, Alt with an arrow moves it and Enter or Escape ends; a row’s context menu also has Move row up and Move row down.

flow.addEventListener('row-move', ({ detail }) => {
  flow.nodes = flow.nodes.map((node) => {
    if (node.id !== detail.id) return node
    const rows = [...node.rows!]
    rows.splice(detail.to, 0, ...rows.splice(detail.from, 1))
    return { ...node, rows }
  })
})

The rows are styled through --c2-flow__row--*, --c2-flow__row-meta--*, --c2-flow__row-grip--* and --c2-flow__row-more--*; a schema usually also hides the status marker and sets a monospace font:

c2-flow.schema {
  --c2-flow__marker--display: none;
  --c2-flow__node--width: 240px;
  --c2-flow__row--font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  --c2-flow__row__highlighted--background-color: rgba(2, 101, 220, 0.08);
}

Custom nodes and cards

renderNode returns the body of every node and renderCard the content of its hover card, as a Lit template, a DOM node or a string. Handles, dragging, edges and positioning stay with the flow. A framework that renders its own markup can use the node:<id> and card:<id> slots instead.

import { html } from 'lit'
import '@c2n/components/flow'

const flow = document.querySelector('c2-flow')!
flow.renderNode = ({ node, status }) => html`
  <strong>${node.label}</strong>
  <progress max="100" value=${node.data.progress}></progress>
  <small>${status} · ${node.data.metric}</small>
`
flow.addEventListener('node-click', ({ detail }) => openDetails(detail.node))

Context menu

renderContextMenu receives the node that was right-clicked (or null for the canvas), the edge in an editable flow, and the built-in rows (in an editable flow, Add node on the canvas, Rename and Delete on a node, and Edit label and Delete on an edge). The view controls (zoom, fit, layout) belong to the canvas: a node has no built-in rows, so right-clicking one opens a menu only when you return rows for it. Return your own rows next to defaultItems to extend the canvas menu; they fire flow-menu-select with the node. A row with value="flow:details" selects the node and fires node-click. Return undefined to keep the built-in rows, or null to let the browser show its own menu.

flow.renderContextMenu = ({ node }) =>
  node
    ? html`<h6>${node.label}</h6>
        <c2-menu-item value="flow:details">Show details</c2-menu-item>
        ${node.status === 'error' ? html`<c2-menu-item value="retry">Retry</c2-menu-item>` : nothing}`
    : undefined

flow.addEventListener('flow-menu-select', ({ detail }) => {
  if (detail.value === 'retry') retry(detail.node!.id)
})

Layout

The saved layout is a plain object of positions keyed by node id. getLayout() returns the current one and setLayout() applies one (merged like a restored layout), so it can live on a server instead of in localStorage; layout-change fires whenever the user changes it. resetLayout() goes back to the auto layout, and fitView(), zoomIn() and zoomOut() drive the view from your own controls.

Fitting scales the whole graph into the box, never above 100%; it happens on the first render, on fitView(), and when the box is resized or the data changes before the user has panned or zoomed. fit-min-zoom (0.25 to 1, default 0.25) is the least zoom it may choose, for a small preview whose labels must stay readable: a flow too large for its box at that zoom shows its start instead of its middle, the first step at the left edge (direction="LR") or the top edge ("TB"), centred across when it fits that way. Changing it refits an untouched view. Zooming by hand still goes from 25% to 200%.

<c2-flow class="preview" locked no-context-menu no-card fit-min-zoom="0.6"></c2-flow>

A node can also carry its own position. The flow places it there instead of where the auto layout would, unless the user has already moved it (a saved or dragged position wins over the one the node first arrived with); changing the value later moves the node. Nodes without one still follow the auto layout, placed next to their neighbours.

AccordionConnected, animated panels with shared borders and single or multiple expansion.LayoutAction BarA floating bar for the current selection: the count, the bulk actions and a button that clears it.LayoutApp ShellThe frame of an application: header, navigation, main, an optional details panel, banner and footer, laid out from its own width.LayoutArea chartA line chart with the region under each line filled — every line-chart attribute, plus a fill opacity.ChartAttachmentFile and image attachments with metadata, upload progress, failure states, and actions.Data displayAutocompleteSearchable combobox for local or remote items with customizable list rows.InputsAvatarImage, 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 displayBannerA persistent, full-width message in the page flow for system notices, outages and account status.FeedbackBar chartBars over categories or time buckets, with several series drawn side by side within each band.ChartBar ListA ranked list whose rows carry their own bar: top pages, referrers, countries, or the changes between two periods.Data displayBorder BeamA decorative beam that travels around the border of any positioned container.LayoutBottom NavThe mobile tab bar: three to five destinations along the bottom of the screen, each an icon over a short label with a count or dot badge.NavigationBreadcrumbNavigation trail of link buttons with separators, a current page and optional collapsing.NavigationBubble chartAn ECharts scatter plot with a third measure encoded as bubble area, for comparing how two measures relate while showing how much each point weighs.ChartButterfly chartTwo series back to back on a shared category axis, the categories named between them. The population pyramid is the classic case.ChartButtonThemeable button with slots for text, prefix, suffix and running icons.ButtonsButton GroupJoined actions and polished segmented controls with single or multiple selection.ButtonsCandlestick chartAn ECharts OHLC chart for market sessions and other open-close ranges, with semantic positive and negative colours.ChartCardGroups related content and actions on a single bordered surface.LayoutCard InputCard number, expiry date and security code in one field, formatted as the brand prints them and checked as they are typed.InputsCarouselA swipeable slideshow with previous/next controls, indicators, looping and optional autoplay.LayoutCascaderSelect a value from related, multi-level data in one floating panel.InputsChat InputAuto-growing message composer with keyboard submission, toolbar actions and native form support.ChatChat MessageFlexible message row for conversations, assistant answers and activity updates.ChatChat Message ListScrolling conversation log that follows new messages, counts unread ones and loads older history.ChatCheckboxNative checkbox behaviour in a quiet, themeable box with an opt-in hover layer.InputsCheckbox groupCheckboxes grouped into one form control with a value array, a group label and an at-least-one rule.InputsChipA compact pill for a filter, a choice or an entered value: selectable as a toggle, removable with a button or the keyboard.InputsChip GroupA single row of chips or badges that collapses whatever does not fit into a +N indicator, recomputed as the row or its items change size.Data displayChoice MatrixRows × columns of checkboxes or radio buttons in one form control: permissions, notification settings, agreement scales.InputsChord chartFlows between groups around a circle, where one row is one flow from a source to a target.ChartCitationA numbered inline marker that names its source, with a hover preview of the page it cites.ChatCode EditorEditable, syntax-highlighted source field on CodeMirror 6, themed entirely through CSS variables.Editors & viewersCode ViewerSyntax-highlighted code with line numbers, copy button and dark mode, powered by shiki.Editors & viewersColor 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.InputsCommandA searchable list of commands: type to filter, arrows to highlight, Enter to run. The core of a ⌘K palette.NavigationComparison BarSegmented ratio bar comparing two or three quantities, such as the bid/ask balance of an order book.Data displayConfirm DialogAsks the user to confirm an action: await show() or confirm() for a true/false answer, with a destructive variant and an async confirm action.FeedbackContext MenuA menu opened by right-clicking or long-pressing an area, built from a slotted c2-menu or a render function per clicked spot.NavigationCopy ButtonButton that copies text to the clipboard — the element it sits in, another element by id, or a literal string.ButtonsCountdownTime left until a date, or through a timer, drawn as segments, a clock or a phrase, and kept current to the second.Data displayDashboardGrid of resizable panes, dragged by the edges they share.LayoutDate InputForm-associated single-date input with native picker, constraints, helper text and error states.InputsDate SelectorAccessible one- or two-month calendar for choosing a date range.InputsDate StripA row of days to pick one from, a week at a time, with dots on the days that have something on them.InputsDate Time PickerForm-associated date-and-time field with a calendar and time popup, min/max/step constraints and an IANA time zone.InputsDepth chartCumulative bid and ask size around the mid price of an order book, as trading screens show market depth.ChartDescription ListRead-only label and value pairs for a detail page, in columns that wrap with the available width.Data displayDetailsCollapsible disclosure built on native details and summary.LayoutDiff ViewerRead-only line diff of two texts, unified or side by side, with folded unchanged lines and word highlights.Editors & viewersDistribution BarOne 100% stacked bar of a breakdown, with a legend of shares and the hovered part brought forward.Data displayEmoji PickerA searchable grid of emoji in categories, with skin tones and the emoji used last.InputsFacet ListOne facet of a filter sidebar: an attribute’s values with counts, to include or exclude from a search.InputsFieldsetGroups related controls under a legend, with a description, a group-level error and a disabled state that reaches every control inside.InputsFilter BuilderA filter bar of sentence chips (Status is any of Active), an add-filter picker and an editor for each part; it reports a plain JSON filter tree.InputsFloat ButtonA floating action button for the main action of a screen, which can expand into a speed dial of related actions.ButtonsFlowA pipeline, dependency graph or database schema on a pannable canvas: steps with a status, smooth edges, rows joined row to row, auto layout, draggable nodes saved to localStorage, a hover card and a context menu.Editors & viewersFormForm container with validation timing, an error summary, server errors, dirty tracking and async submit.InputsForm FieldLabel, hint, error, warning and character counter around any form control, wired for assistive technology.InputsFormat DateA date and time formatted for the reader's language and, optionally, a fixed time zone.Data displayFormat NumberA number formatted for its locale: currency, percent, units, compact counts and byte sizes, from Intl.NumberFormat.Data displayGanttA read-only project timeline: tasks, groups, milestones and dependencies on a day, week or month scale.PlanningGauge chartA focused radial KPI gauge drawn by ECharts, with a configurable scale, progress arc and pointer.ChartGoogle MapGoogle Maps with markers, a road route from A to B, and Street View, loaded once per page from a browser key.Data displayHeaderArranges brand, navigation, actions and a mobile trigger in a reusable site shell.LayoutHeatmap chartRows binned into cells by two keys, aggregated, and coloured by a sequential, diverging or stepped scale: a currency matrix, pair × tenor, hour × weekday, or a calendar of days. Drawn by ECharts, with a colour key that counts, marks and filters.ChartHighlightMarks the parts of a text that match a search query.Data displayHover CardInteractive preview card shown when a link or mention is hovered or focused, rendered in the top layer.FeedbackIcon ButtonRound, hoverable button wrapping a slotted SVG icon.ButtonsImageImage with lazy loading, a loading placeholder, an error fallback, click-to-load for untrusted URLs and a full-screen preview.Data displayImage CropperChoose the part of an image to keep by moving and resizing a selection, then read it back as a canvas or a blob.Editors & viewersIndicatorSolid dot or count pinned to an edge or corner of any element, with an accessible name for the count.Data displayInline EditText that turns into a field on click: Enter (Ctrl/⌘+Enter when multiline) commits, Escape cancels, and any control with a value that fires change can be the editor.InputsJSON FormA form generated from a JSON Schema, laid out by the schema, a UI schema or your own markup.InputsJSON ViewerBrowse a JSON value as a collapsible tree, search it, and copy any path or value.Editors & viewersKanbanBoard layout and card movement by pointer or keyboard, with cards drawn by your own renderItem.PlanningKbdKeyboard key label for shortcuts and command hints, with the semantics of the native kbd element.Data displayKey Value EditorEditable KEY=value rows for environment variables and headers: paste a .env file to split it into rows, mask secret values, and flag duplicate or malformed keys.InputsLabelCaption that names and activates the control referenced by its for attribute, with a required marker.InputsLine chartA line chart over a time or numeric x axis, drawn on canvas by uPlot, with series declared as children.ChartLink 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 displayLoading OverlayCovers a region while it loads: the content stays in place, dimmed and blocked, with a spinner and an announced label.FeedbackLog ViewerExplore large logs with variable-height virtual scrolling and sticky tabular attributes.Editors & viewersMap chartA choropleth and point map on vector outlines, drawn by ECharts: regions shaded by a value, with layers of sized points and flow lines, on the bundled world and US maps or your own GeoJSON, TopoJSON or SVG.ChartMarkdownSafe markdown renderer that streams: chat answers and READMEs, with code, math and diagrams.ChatMarkerAn inline marker that highlights, underlines, strikes through, boxes or circles a run of text.Data displayMask InputA text input that fits what is typed to a pattern, for postcodes, IBANs, tax IDs, expiry dates and other fixed formats.InputsMasonryAutomatically pack differently sized dashboard tiles, then move or resize them in edit mode.LayoutMaster DetailA list beside the record it opens, one pane at a time with a Back step when the element is narrow.LayoutMathTeX formulas rendered as native MathML: no font CSS, readable by screen readers, safe for untrusted input.Data displayMenuCommands, links, checkboxes and submenus in a popover anchored to a trigger.NavigationMenubarDesktop-style File, Edit and View menus in one bar, with the arrow-key and hover movement of an application menu bar.NavigationMermaidMermaid text diagrams drawn in your theme colours, loaded on demand and safe for untrusted sources.Data displayMeterA measurement inside a known range — storage, quota, strength — with native meter thresholds and an optional segmented breakdown.Data displayModalDialog built on the native dialog element: focus trap, backdrop, Escape, title, body and footer.FeedbackMoney InputCurrency amount field that groups digits as you type, follows the locale and stores an integer count of minor units.InputsMonth PickerPick a month, or a year: billing periods, reports and card expiry.InputsMonth PlannerMonth calendar that draws events and trips as bars across the days they cover.PlanningNavigation MenuSite navigation bar whose triggers open panels of links below the header.NavigationNotepadA rich-text notepad that looks like paper: handwriting on ruled lines, a formatting toolbar over the selection, checklists and tear-off pages.PlanningNotification CenterAn inbox panel of notifications grouped by day, with read and unread states and mark all as read.FeedbackNumber InputForm-associated numeric input with native validation, step controls, adornments and helper states.InputsOrg ChartA hierarchy drawn as connected cards that collapse and expand: an organization chart, a reporting line or a team structure.Data displayOTP InputForm-associated one-time-code field with one cell per character, paste and SMS autofill, grouping and masking.InputsOverflow ListOne row of actions or links that shows what fits and folds the rest into a "More" menu, refitting as its width changes.LayoutOverlap chartA Venn diagram of two or three sets, where each circle is a set and each shared area counts the members of exactly that combination.ChartOverlayAnchored popup built on the browser Popover API, positioned with floating-ui.FeedbackPage EditorA Notion-style editor for long text: Markdown shortcuts while you type, a / menu for blocks, a toolbar over selected text, code blocks with syntax colours and eight named colours.Editors & viewersPage HeaderThe top of a page: breadcrumb or parent link, your heading with status badges, description, metadata, page actions that fold into a menu, tabs and a toolbar.LayoutPaginationPage navigation in three layouts: numbered pages, a simple page status, or a table-footer row with rows-per-page.NavigationPassword FieldA password input with a show/hide toggle, a strength meter, a requirements checklist and a Caps Lock warning.InputsPhone InputA phone number field with a searchable country picker, as-you-type grouping and an E.164 value.InputsPie chartA pie or donut chart drawn by ECharts, where one row is one slice and label-field names it.ChartPopconfirmA small "Are you sure?" popup anchored to the button that asked for it.FeedbackProgressProgress bar or ring, indeterminate or filling to a value, with an optional label and count.FeedbackPyramid chartRanked levels of a whole, stacked from a narrow apex to a wide base, where one row is one level.ChartQR CodeGenerate accessible, themeable QR codes locally as crisp SVG graphics.Data displayQuery InputA key:value query field whose filters are chips you can switch off or remove, with syntax highlighting and field and value suggestions, as in Datadog, GitHub and Sentry.InputsQuestionnaireA multi-step single- and multiple-choice flow with validation, shortcuts and form submission.InputsRadar chartCompare several profiles across the same set of normalized indicators.ChartRadioRadio options built on native inputs, grouped into one value with keyboard navigation.InputsRateAccessible star rating input with hover preview, keyboard control and optional half values.InputsReaction BarEmoji reactions with counts under a message or comment, toggled by the current user, with a picker to add more.ChatRelative TimeTime relative to now, such as "3 minutes ago" or "in 2 days", that keeps itself current and shows the full date on hover.Data displayReorder ListReorder a vertical queue with pointer or keyboard input while the application owns persistence.Data displayRich Text EditorA compact formatted-text field for comments, replies and email bodies: bold, links, lists, quotes and code, a one-tab-stop toolbar, and a Markdown value.InputsSankey chartQuantities moving through stages, where one row is one flow and each band is as wide as what it carries.ChartSave BarA floating bar that appears while a form has unsaved changes, with Discard and Save.LayoutScatter chartAn ECharts scatter plot for finding relationships, clusters and outliers across two numeric measures.ChartSearch FieldA search box with a debounced search event, a clear button, a keyboard shortcut hint and recent searches.InputsSecret FieldShows an API key or other secret masked, with buttons to reveal it, copy it and ask for a new one.InputsSelectDropdown that pairs a themeable trigger with an anchored list of c2-list-item options.InputsSeparatorHorizontal 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.FeedbackShortcutKeyboard shortcuts for a whole application from one element: each binding fires an action event, runs a handler or activates a target.InputsSide 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.FeedbackSkip LinkA "Skip to main content" link that appears when a keyboard user tabs onto it and moves focus into the main content.NavigationSliderRange input with a themeable track, thumb, step ticks and value bubble.InputsSparklineA chromeless trend line sized for a table cell, a list row or the trend slot of a c2-stat.ChartSpinnerCircular progress indicator, indeterminate or showing a value, with optional text.FeedbackSplit ButtonA main action with a caret menu of alternatives, such as "Send ▾", or a remembered choice like a merge method.ButtonsSplit PanelTwo panels separated by a divider the user drags, or moves with the keyboard, to resize them.LayoutSpreadsheetA small editable grid with basic formulas, for budgets, quotes, price lists and score sheets.Editors & viewersStatDisplays a KPI with an optional icon, trend and supporting description.Data displayState TimelineOne band per series, coloured by the discrete state it was in over time: service health, CI runs, host power.ChartStatus LightColoured dot plus a short label for the state of something, such as Live, Building or Failed.Data displayStatus PanelCommunicate empty states, operation outcomes and recoverable errors with clear next steps.FeedbackStepsThe trace of a task as it runs, or a stepper across the top of a view: statuses, durations, stages that open while they work, and steps the reader can select.Data displayStickyPins content to the top or bottom of its scroll container and tells you when it is stuck, optionally hiding it while the reader scrolls down.LayoutStreaming TextPlain text that arrives in bursts, such as an LLM answer, revealed at a steady pace behind a caret.ChatSwipe ActionA list row that slides sideways to reveal actions behind it, as in a mail or task list on a phone.LayoutSwitchOn/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 displayTable Of ContentsAn "On this page" navigation that lists the headings of a document, nests them by level and highlights the section being read.NavigationTabsTab strip that shows one content panel at a time.NavigationTag InputTurns typed or pasted text into removable tags — recipients, keywords, labels — with configurable delimiters, parsing and validation.InputsText 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.InputsTheme SelectColour-theme switcher: click to step to the next mode, hover for the full menu.InputsTime InputForm-associated time-of-day input with a scroll-wheel picker, step and range constraints, helper text and error states.InputsTime Range PickerDashboard time range control: searchable quick ranges, an absolute range typed or picked on a calendar, time zone, shift and zoom, and auto-refresh.InputsTime Slot PickerPick a free time from someone's calendar: a row of days, then that day's times grouped into morning, afternoon and evening.InputsTimelineA vertical sequence of dated events on a connected rail: order history, activity feeds, changelogs.Data displayToastNotification cards and a manager for stacked, queued notifications with independent lifetimes.FeedbackTodo ListA to-do list with the feel of paper: hand-drawn ticks, notes, highlighters, swipe and drag, and a remembered look.PlanningToggletipA small ⓘ button that opens a help bubble on click or tap; the bubble can hold links and is announced to screen readers.FeedbackTooltipContextual hint shown when its target is hovered or focused, rendered in the top layer.FeedbackTourA guided product tour that spotlights one element at a time and explains it in a panel beside it.FeedbackTrace WaterfallShow the spans of a distributed trace as a tree, each with a duration bar on one shared time axis.Data displayTreeHierarchical tree view with expansion, selection, checkboxes and lazy loading.Data displayTree SelectA dropdown for picking one or several rows from a hierarchy, with checkboxes, search and a form value.InputsTreemap chartA whole split into nested rectangles, each sized by its value: market maps, budgets and disk usage.ChartTruncateClamps text to a number of lines, with an optional Show more button.Data displayUploadDrag-and-drop file selection with validation, upload progress, retry, cancellation, and attachment results.InputsView OptionsThe Display popover of a collection: layout, grouping, ordering on several levels, Show switches and visible properties, reported as one plain value.InputsVirtual ListWindowed list with built-in search, sorting, selection and an async data source.Data displayVisually HiddenText for screen readers only: kept in the accessibility tree, drawn nowhere, optionally shown while it holds focus.LayoutWaterfall chartA running total bridged by the steps that move it, where one row is one step and totals mark where the bridge lands.ChartWeek PlannerWeekly schedule with time blocks: a typical week with optional odd and even weeks, or a dated week the user edits.PlanningWorking IndicatorA live "work in progress" line: animated mark, shimmering label, trailing dots and elapsed time.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.IconsSymbols157 themeable spot illustrations for empty states, status panels, onboarding and error pages.IconsTask icons191 duotone icons for to-do items: work, home, health, sport, learning, food, money, travel and more.IconsThemingTheme every c2n component from a handful of design tokens with @c2n/theme, or reach for any component variable directly.GuideLanguagesComponents show their text in English; add your app’s languages as one JSON object, picked by the nearest lang.GuideFrameworksUse the components from React, Vue, Angular, Svelte, Lit or plain HTML — typed events, generated template types, and the one piece of configuration each framework needs.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.GuideAI toolsGive Claude Code, Codex, Google Antigravity or GitHub Copilot the c2n skill, live component APIs, examples and application conventions.GuideNo components match.