A navigation bar styled like Tabs, for links that change the page.
Unlike Tabs, every link stays individually reachable by Tab and
the arrow keys are left to the browser — the ARIA tabs pattern covers in-page
panel switching, not navigation. The active link is marked with
aria-current="page", which Ember's LinkTo does not set on its own.
import { TabNav } from 'frontile';
The docs site has no routes for TabNav to link to, so every demo on this
page uses @href and @isActive directly rather than @route. In an app
with routes, @route (below) is the tier to reach for — it derives
@isActive from the router for you.
Pass @route (with @models, @model, or @query as needed) and
nav.Item renders an Ember LinkTo and derives @isActive from the router
itself — no @isActive needed. This tier isn't rendered on this page, since
the docs site has no matching routes; it is shown here as reference.
import { TabNav } from 'frontile';
<template>
<TabNav @label='Settings' as |nav|>
<nav.Item @route='settings.account'>Account</nav.Item>
<nav.Item @route='settings.security'>Security</nav.Item>
<nav.Item @route='settings.billing'>Billing</nav.Item>
</TabNav>
</template>
@isActive always wins over anything derived from the router, so it can
still override a @route item when needed.
The router's isActive check is a prefix match: a route also counts as active
while any of its descendant routes are active. So @route='settings' and
@route='settings.account' in the same list are both marked active while on
settings.account — not just the more specific one. Pass an explicit
@isActive on the parent item (e.g. comparing the current route name exactly)
if you need only the leaf to light up.
nav also yields itemClass and setupItem directly, for a link
component other than nav.Item — ember-link, a custom <AppLink>. Apply
itemClass to the link's class and {{nav.setupItem}} (with a boolean for
whether the link is active) to its element, and it gets the same theme
classes, data-selected, aria-current, and indicator animation as
nav.Item. This tier isn't rendered here either, for the same reason as
@route above.
import { TabNav } from 'frontile';
<template>
<TabNav @label='Settings' as |nav|>
<a href='/settings/account' class={{nav.itemClass}} {{nav.setupItem true}}>
Account
</a>
</TabNav>
</template>
TabNav shares its theme with Tabs, so @variant, @color, and @size
behave the same way — see Tabs for each option.
@isFullWidth={{true}} stretches the bar to its container and gives every
link equal width.
@isDisabled drops the href as well as marking the link aria-disabled —
an anchor cannot be natively disabled, so removing the href is what actually
stops navigation.
TabNav renders a <nav> landmark (not a <ul>), and each nav.Item is a
plain link, not a tab — these links navigate rather than switch an in-page
panel. TabNav needs an accessible name from @label, or pass
aria-labelledby on TabNav directly.
Every link stays in the natural tab order and arrow keys are left to the
browser; there is no roving tabindex and no keyboard handling to document
beyond ordinary link navigation. The active link carries aria-current="page"
and data-selected="true", both a nav.Item derives and {{nav.setupItem}}
sets for a hand-rolled link — a disabled link instead carries aria-disabled
and drops its href.
Element: HTMLElement
A navigation bar styled like Tabs, for links that change the page.
It deliberately does not use rovingFocus: these are links, so every one
of them stays individually reachable by Tab and the arrow keys are left to
the browser. The ARIA tabs pattern covers in-page panel switching only, and
applying it to navigation would remove links from the tab order for no gain.
| Name | Type | Default | Description |
|---|---|---|---|
classes
|
SlotsToClasses<'base' | 'indicator' | 'list' | 'tab' | 'panel'>
|
- | Class names for each slot of the component, merged with the theme's. |
color
|
enum
|
'neutral'
|
The colour intent applied to the indicator. |
isFullWidth
|
boolean
|
false
|
Stretches the bar to its container and gives every link equal width. |
label
|
string
|
- | Accessible name for the navigation landmark. |
orientation
|
enum
|
'horizontal'
|
Lays the links out in a row or a column. |
size
|
enum
|
'md'
|
The size of the links, driving padding and text size. |
variant
|
enum
|
'solid'
|
The visual style of the navigation bar. |
| Name | Type | Default | Description |
|---|---|---|---|
default
*
|
Array
|
- |
Element: HTMLAnchorElement
| Name | Type | Default | Description |
|---|---|---|---|
itemClass
*
|
string
|
- | Supplied by TabNav. Not part of the public API. |
setupItem
*
|
ModifierLike<{ Element: HTMLElement; Args: { Positional: [boolean]; }; }>
|
- | Supplied by TabNav. Not part of the public API. |
class
|
string
|
- | Class names appended to this item's theme classes. |
href
|
string
|
- |
Renders a plain anchor. Ignored when @route is given.
|
isActive
|
boolean
|
- |
Overrides the active state. Always wins over anything derived from the
router, and is the only source of truth when @route is not used.
|
isDisabled
|
boolean
|
false
|
Marks the link as disabled. An anchor cannot be natively disabled, so the
href is dropped as well -- aria-disabled alone still leaves it
clickable.
|
model
|
unknown
|
- |
A single dynamic segment for @route.
|
models
|
Array
|
- |
Dynamic segments for @route.
|
query
|
Record<string, unknown>
|
- |
Query params for @route.
|
route
|
string
|
- |
Renders a LinkTo for this route and derives the active state from the
router. Omit it (and pass @href) to stay entirely router-free.
|
| Name | Type | Default | Description |
|---|---|---|---|
default
*
|
Array
|
- |