Navigation

anchor-jump

#existingsource

A compact list of links that jump to sections within the current page — the "On this page" sidebar or in-content table of contents. Each entry is a genuine anchor targeting an element id, so the URL hash updates, the back button works, and the links are operable without JavaScript. Pair section ids in the page with link ids here.

Readiness
complete
Preview
live
Props
3
Examples
2
Code
implementation mapped

When to use

  • Long documents / settings pages with named sections
  • An 'On this page' sidebar next to scrollable content
  • Reference views where users skim to a known section

When not to use

  • Navigating between separate pages — use real router links
  • Switching mutually-exclusive panels — use Tabs / VerticalTabs
  • A hierarchical ancestor trail — use Breadcrumb

Accessibility

Role: navigation

  • Enter — Jump to the linked section (native anchor behaviour)
  • Tab — Move between section links

Screen reader: Rendered as a nav landmark with an accessible label (default "On this page") so SR users can locate and skip it. Entries are a real list of links, announced with their section labels.

Notes: Give the nav a meaningful aria_label when more than one navigation landmark exists on the page so they are distinguishable.

Props

NameTypeDefaultDescription
linksarrayThe jump targets.
aria_labelstringOn this pageAccessible label for the navigation region.
headingstringOptional visible heading rendered above the list.

Examples

On this page

Table of contents for a long settings page.

YAML
type: anchor-jump
props:
  heading: On this page
  aria_label: On this page
  links:
  - id: overview
    label: Overview
  - id: members
    label: Members
  - id: billing
    label: Billing
  - id: danger-zone
    label: Danger zone
Section jumps

Compact in-content jump list without a heading.

YAML
type: anchor-jump
props:
  aria_label: Sections
  links:
  - id: intro
    label: Introduction
  - id: api
    label: API
  - id: faq
    label: FAQ
Edit YAML

brand-lockup

#existingsource

The canonical brand treatment for app chrome: render the real Triform logotype from the shared branding assets, then pair it with a short surface label such as Docs, Console, or Admin. It keeps generated shells from inventing text-only headers or local logo variants.

Readiness
complete
Preview
live
Props
3
Examples
1
Code
implementation mapped

When to use

  • WorkspaceShell or PanelShell headers that identify a Triform-owned surface
  • Generated documentation and internal tools that need first-viewport brand identity
  • Compact top-left chrome where the logo and current product label should travel together

When not to use

  • Customer-branded surfaces where tenant brand assets should take precedence
  • Small repeated rows or cards where a logo would be decorative noise
  • Third-party integration identities - use Avatar, ElementBadge, or vendor-specific icons

Accessibility

Role: link

  • Enter — Navigates to the owning surface home when rendered as a link

Screen reader: Expose an aria-label such as "Triform docs home". Mark the inline SVG as decorative when the label already names the destination.

Notes: Do not replace the logotype with text. If SVG rendering is unavailable, fall back to the word "Triform" in the display font.

Props

NameTypeDefaultDescription
surface_labelstringDocsShort product or surface label displayed beside the logotype.
hrefstringOptional home route when the lockup is interactive.
compactbooleanfalseUses icon mark only when horizontal space is constrained.

Examples

Docs lockup

Docs sidebar header.

YAML
type: brand-lockup
props:
  surface_label: Docs
  href: '#/README'
Edit YAML

load-more

#existingsource

A button placed at the end of a list that fetches and appends the next page in place, keeping prior results visible. Use it for feeds and scannable lists where users care about continuity more than jumping to an exact page. The `loading` state shows an in-button spinner; `disabled` covers the "nothing left to load" terminal state. The optional remaining-count caption sets expectations.

Readiness
complete
Preview
live
Props
4
Examples
3
Code
implementation mapped

When to use

  • Feeds / activity streams where context-continuity matters
  • Lists where users rarely need a specific page number
  • Mobile-friendly paging without a full pager control

When not to use

  • Users need to jump to an arbitrary page — use Pagination
  • Truly infinite content where a scroll sentinel is better
  • A standalone primary action — use Button directly

Accessibility

Role: button

  • Enter — Load the next page
  • Space — Load the next page

Screen reader: The control is a labelled group containing a real button. The remaining-count caption is aria-hidden decorative text; convey the remaining count in the button label itself if it must be announced.

Notes: While loading, the button reflects a busy state via the shared Button primitive; keep the label stable so focus is not lost.

Props

NameTypeDefaultDescription
labelstringLoad moreButton label.
loadingbooleanfalseWhether a fetch is in-flight (button shows a spinner).
disabledbooleanfalseDisable the button (e.g. nothing left to load).
remainingintegerOptional remaining-count caption (renders 'N more').

Examples

More to load

Default state with a remaining-count caption.

YAML
type: load-more
props:
  label: Load more
  remaining: 24
Loading

In-flight fetch.

YAML
type: load-more
props:
  label: Load more
  loading: true
Disabled

Exhausted list — nothing left to load.

YAML
type: load-more
props:
  label: All caught up
  disabled: true
Edit YAML

magnetic-edge-rail

#existingsource

A persistent edge affordance that keeps the workspace calm in its collapsed state while making navigation fully accessible through three layered gestures: (1) proximity — approaching the edge reveals the panel as the user nears; (2) hover-keep-open — the panel stays fully unfolded as long as the pointer is over the handle or any panel item, regardless of edge distance; (3) click-to-pin — a click on the handle or an item locks the rail open until the user clicks outside it or opens the opposing rail. The handle and item panel animate as two hinged faces of the same fixed edge: the handle folds back and the panel folds flat toward the user. When two rails share an exclusivity group (e.g. Library left + Properties right), only one can be pinned at a time — clicking one releases the other.

Readiness
complete
Preview
live
Props
7
Examples
3
Code
implementation mapped

When to use

  • Workspace chrome that should remain discoverable without permanently consuming horizontal space
  • Category or section rails where items are icon-led and repeated frequently
  • Dense canvas/editor surfaces where peripheral controls should stay quiet until needed
  • Paired left/right rails that should be mutually exclusive (the exclusivity_group enforces this automatically)
  • As a progressive enhancement over click/touch accessible rail controls

When not to use

  • Primary app navigation that must remain fully readable at all times
  • Forms, destructive actions, or workflows where hover-only disclosure would hide required context
  • Mobile-only surfaces without a click/tap fallback
  • Long text menus that need scanning more than spatial recall

Accessibility

Role: navigation

  • Tab — Focus enters the collapsed handle, then the expanded item buttons
  • Enter — Toggles the rail handle or activates the focused item; also pins the rail open
  • Escape — Clears the pin and collapses focus-opened state

Screen reader: The collapsed handle is a real button with `aria-expanded`. The expanded item panel is a `nav` region with the same accessible label. Active items use `aria-current=true`; disabled items use native disabled semantics. The pinned state is not separately surfaced to assistive technology — the `aria-expanded` state on the handle already communicates open/closed.

Notes: Proximity, hover, and click-to-pin are layered: each adds to the previous. Keyboard focus and handle click keep the rail fully reachable without hover. Reduced-motion users keep all three disclosure gestures but lose the 3D hinge transform. Touch / coarse-pointer devices receive the pinned-open state by default (via the existing `@media (hover:none)` rule).

Props

NameTypeDefaultDescription
sideenum: left | rightleftViewport/container edge the rail is hinged to.
placementenum: contained | viewportcontained`contained` renders inside a showcase/demo well; `viewport` mounts as app chrome.
label*stringAccessible label for both the collapsed handle and expanded nav region.
handle_icon*stringMaterial Symbols icon shown while the rail is collapsed.
open_radius_pxnumber28Distance from the edge where the rail reaches fully-open magnetic strength.
close_radius_pxnumber156Distance from the edge where the rail fully releases and closes.
items*arrayRail items. `id`, `label`, and `icon` are required; `color`, `active`, and `disabled` are optional.

Examples

Library edge

Left library rail for element categories.

YAML
type: magnetic-edge-rail
props:
  side: left
  placement: contained
  label: Library
  handle_icon: deployed_code
  open_radius_px: 28
  close_radius_px: 156
  items:
  - id: apps
    label: Apps
    icon: rocket_launch
    color: '#9B5CFF'
    active: true
  - id: data
    label: Data
    icon: database
    color: '#2F80FF'
  - id: tools
    label: Tools
    icon: construction
    color: '#18D68A'
  - id: modifiers
    label: Modifiers
    icon: tune
    color: '#FF7A1A'
Settings edge

Right settings/properties rail for focused object sections.

YAML
type: magnetic-edge-rail
props:
  side: right
  placement: contained
  label: Settings
  handle_icon: settings
  open_radius_px: 28
  close_radius_px: 156
  items:
  - id: presentation
    label: Presentation
    icon: info
    color: '#E6E0FF'
    active: true
  - id: brain
    label: Brain
    icon: psychology
    color: '#9B5CFF'
  - id: network
    label: Network
    icon: hub
    color: '#71D5FF'
  - id: history
    label: History
    icon: history
    color: '#B8BEC8'
Reserved viewport edge

Paired library/properties rails that reserve the center-stage viewport.

YAML
type: stack
props:
  direction: row
  gap: md
slots:
  default:
  - type: magnetic-edge-rail
    props:
      side: left
      placement: contained
      label: Library
      handle_icon: deployed_code
      open_radius_px: 28
      close_radius_px: 156
      items:
      - id: tools
        label: Tools
        icon: construction
        color: '#18D68A'
        active: true
      - id: data
        label: Data
        icon: database
        color: '#2F80FF'
  - type: magnetic-edge-rail
    props:
      side: right
      placement: contained
      label: Properties
      handle_icon: settings
      open_radius_px: 28
      close_radius_px: 156
      items:
      - id: presentation
        label: Presentation
        icon: info
        color: '#E6E0FF'
        active: true
      - id: history
        label: History
        icon: history
        color: '#B8BEC8'
Edit YAML

pagination

#existingsource

A numbered pager for jumping to a specific page of a bounded result set. Use it when users need direct access to arbitrary pages and the total page count is known. The window keeps a fixed footprint: first page, last page, and `sibling_count` pages either side of the current, with "…" gaps where the sequence breaks. Prev/next are disabled at the ends.

Readiness
complete
Preview
live
Props
3
Examples
3
Code
implementation mapped

When to use

  • Tables / search results with a known total page count
  • Users need to jump to a specific or last page
  • Result sets where page position is meaningful

When not to use

  • Continuous feeds where continuity matters — use LoadMore
  • Unknown / unbounded totals — use a scroll sentinel
  • Fewer than 2 pages (render nothing instead)

Accessibility

Role: navigation

  • ArrowLeft — Previous page
  • ArrowRight — Next page
  • Enter — Activate the focused page / edge button

Screen reader: Wrapped in a nav labelled "Pagination". The current page button is aria-current="page"; each page button has an explicit "Go to page N" label and the edge buttons are labelled "Previous page" / "Next page". Ellipsis gaps are aria-hidden.

Notes: Disabled prev/next at the bounds are real disabled buttons so keyboard and SR users get consistent boundary feedback.

Props

NameTypeDefaultDescription
currentinteger1Current page (1-based).
totalinteger1Total number of pages.
sibling_countinteger1Sibling pages shown either side of the current page.

Examples

Page 6 of 12

Mid-range page in a large result set (ellipsis both sides).

YAML
type: pagination
props:
  current: 6
  total: 12
3 pages

Small result set — every page shown.

YAML
type: pagination
props:
  current: 1
  total: 3
Two siblings

Wider sibling window.

YAML
type: pagination
props:
  current: 8
  total: 20
  sibling_count: 2
Edit YAML

simple-tabs

#existingsource

SimpleTabs renders a lightweight tablist when panels are owned by the surrounding view. It provides the shared tab visual language without forcing content into the tab component itself.

Readiness
complete
Preview
live
Props
3
Examples
1
Code
implementation mapped

When to use

  • Switching between sibling panels in a compact surface
  • Category, mode, or details tabs where the parent owns panel rendering
  • A tablist that needs the platform underline, pill, or boxed treatment

When not to use

  • Tabs that must own and mount panel content; use Tabs with TabPanel
  • Global navigation between product areas
  • Binary choices; use Switch, Checkbox, or two explicit buttons

Accessibility

Role: tablist

  • Tab — Moves focus into and out of the tablist
  • Enter — Activates the focused tab
  • Space — Activates the focused tab

Screen reader: Each tab exposes aria-selected to announce the active tab.

Notes: Parent surfaces must connect the active index to the visible panel.

Props

NameTypeDefaultDescription
tabs*arrayVisible tab labels.
activeinteger0Initial active tab index.
styleenum: underline | pill | boxedunderlineVisual tab treatment.

Examples

Component detail tabs

Tabs for a component detail page.

YAML
type: simple-tabs
props:
  tabs:
  - Overview
  - Props
  - Examples
  active: 1
  style: pill
Edit YAML

tabs

#existingsource

Horizontal tab control for switching between content panels. Three visual variants — Underline (default), Pill, Boxed — share keyboard navigation and ARIA wiring. Pair with `TabPanel` for content. Tabs preserve panel state across switches (mounted-but-hidden), so don't use for content with side-effects on render.

Readiness
complete
Preview
live
Props
2
Examples
2
Code
implementation mapped

When to use

  • Switching between sibling views (Overview / Settings / Logs)
  • Filtering a list by status (All / Active / Archived)
  • Compact navigation inside a panel or modal
  • Settings pages with grouped sections

When not to use

  • Routes — use real router links, not tabs
  • More than ~6 tabs — switch to a Select / Sidebar
  • Vertical orientation — use VerticalTabs (when it lands)
  • Disclosure (one open at a time) — use Accordion

Accessibility

Role: tablist

  • ArrowLeft — Previous tab
  • ArrowRight — Next tab
  • Home — First tab
  • End — Last tab
  • Tab — Move focus from tablist to current panel

Screen reader: Tablist + tab + tabpanel ARIA pattern. SR users can navigate the tab strip with arrow keys, tab to enter the active panel, and shift-tab back to the tablist.

Notes: Activation is "manual" (Enter to commit) by default. Use "automatic" (focus = activate) for low-cost panel switches. Avoid auto-activation when each tab triggers an expensive load.

Props

NameTypeDefaultDescription
activeinteger
styleenum: underline | pill | boxedunderline

Examples

Settings tabs

Settings page tabs.

YAML
type: tabs
props:
  active: ${state.settings_tab}
  style: underline
slots:
  default:
  - type: text
    slots:
      default: General
  - type: text
    slots:
      default: Members
  - type: text
    slots:
      default: Billing
Pill filter

Pill-style filter tabs above a list.

YAML
type: tabs
props:
  active: ${state.filter_tab}
  style: pill
slots:
  default:
  - type: text
    slots:
      default: All
  - type: text
    slots:
      default: Active
  - type: text
    slots:
      default: Archived
Edit YAML

vertical-tabs

#existingsource

A vertical (left-rail) tab control for switching between sibling views when there are more tabs than fit comfortably horizontally, or when the layout already has a sidebar. Same selection model and ARIA wiring as Tabs, but laid out as a column with aria-orientation="vertical" and Up/Down arrow traversal.

Readiness
complete
Preview
live
Props
2
Examples
2
Code
implementation mapped

When to use

  • Settings / preferences with many grouped sections
  • A sidebar + content split where the rail drives the panel
  • More tabs than fit horizontally without wrapping

When not to use

  • 2–4 peer views that fit horizontally — use Tabs
  • Disclosure (one open at a time, stacked) — use Accordion
  • Cross-page navigation — use real router links

Accessibility

Role: tablist

  • ArrowUp — Previous tab
  • ArrowDown — Next tab
  • Home — First tab
  • End — Last tab

Screen reader: Declares role="tablist" with aria-orientation="vertical". The active tab is aria-selected="true" and is the only tab in the tab order (roving tabindex); other tabs are reachable via the arrow keys, matching the WAI-ARIA tabs pattern.

Notes: Arrow keys wrap (last → first and back). The active tab keeps tabindex=0 so Tab moves focus into the tablist at the right place.

Props

NameTypeDefaultDescription
activeinteger0Currently active tab index.
tabsarrayTab labels.

Examples

Settings rail

Settings sidebar with grouped sections.

YAML
type: vertical-tabs
props:
  active: ${state.settings_tab}
  tabs:
  - General
  - Members
  - Billing
  - Integrations
  - Danger zone
Two sections

Compact two-section rail.

YAML
type: vertical-tabs
props:
  active: ${state.profile_tab}
  tabs:
  - Profile
  - Security
Edit YAML