GitHub

Ember component library

Beautiful, production-ready components for Ember.js.

Accessible by construction, typed all the way into your templates with TypeScript and Glint, and styled with Tailwind Variants.

A workspace members panel built from Frontile's Avatar, Button, Chip, Divider, Input, Table, and ProgressBar components, rendered simultaneously in the light and dark themes and divided by a draggable seam.
AI

Workspace members

Team plan ยท 5 of 20 seats used

Worker ID Actions
#4586936
AT

Alex Turner

alex@acme.com

Product Manager
Contractor
#4586937
ED

Emma Davis

emma@acme.com

Senior Designer
Contractor
#4586933
JS

John Smith

john@acme.com

Chief Technology Officer
Employee
#4586932
KM

Kate Moore

kate@acme.com

Chief Executive Officer
Employee
#4586935
MW

Mike Wilson

mike@acme.com

VP of Engineering
Employee

2 of 5 selected

5 of 20

Both sides are the same markup. Nothing was restyled, duplicated, or patched up for dark mode.

The library

Start anywhere

Every tile below is the real component, rendered by the library, and a link straight into its documentation.

Button documentation Button Five intents, three appearances, four sizes
Table documentation Table Sorting, selection, sticky headers, typed columns
Field documentation Field Labels, hints, and validation wired together
Switch documentation Switch Labelled by the component, not by you
Chip documentation Chip Status, filters, and removable tags
Avatar documentation Avatar Initials, images, and shape variants
ProgressBar documentation ProgressBar Determinate, indeterminate, and labelled
Skeleton documentation Skeleton Shapes that match the content they stand in for
ButtonGroup documentation ButtonGroup Segmented and toggling groups
Checkbox documentation Checkbox Grouped, indeterminate, and validated
Spinner documentation Spinner Four sizes, inherits the current intent
RadioGroup documentation RadioGroup Grouped, labelled, and keyboard-navigable
Notifications documentation Notifications Toasts with live regions and auto-dismiss

Overlays

The parts you have to open

Overlays are where component libraries usually leak: focus escapes, the background scrolls, Escape does nothing. Every layer here shares one focus trap, one scroll lock, and one portal.

Read the focus management guide

Popover

  • Anchored to its trigger, and stays anchored as the page scrolls.
  • Escape closes it, and so does a click anywhere outside it.
  • Opened itself when this section scrolled into view โ€” the trigger still owns it.

Modal

Tab to the end โ€” focus wraps instead of escaping.

Drawer

Scroll the page behind it. It will not move.

Dropdown

Arrow keys move, typeahead jumps, Escape restores focus.

Theming

Make it look like your product

Frontile's colors are semantic roles at named levels, not a numbered scale. Swap the ramp and every component follows, in both themes, with no rebuild.

Swap the primary ramp

Your theme

68%
Primary
Outlined
Faded

Inverted

68%
Primary
Outlined
Faded

One ramp per scheme, in a plain CommonJS file that Frontile compiles to custom properties, generating the matching on-primary-* contrast colors for you. Wire it up with @plugin "./frontile.js" in your stylesheet.

frontile.js

Built in

The work you don't have to do

Keyboard behaviour, ARIA state, and template types arrive with the components. Both of these are running right now, so try them rather than take our word for it.

Keyboard and ARIA, already wired

Tab into the list and use the arrow keys, Home, End, or type a letter.

  • Ember Octane
  • Glimmer components
  • Template tag (.gts)
  • Glint type checking
  • Tailwind Variants

What just happened

Selected: Glint type checking. The list manages aria-activedescendant, roving focus, typeahead, and aria-selected itself. None of that is wired up on this page.

Type errors, before runtime

Glint checks component arguments inside the template, so a mismatched collection is a build error rather than a blank cell.

columns.ts
import { Table, type ColumnConfig } from 'frontile';

interface Member { id: string; name: string; role: string }

const columns = [
  { key: 'name', name: 'Member' },
  { key: 'role', name: 'Role' }
] as const satisfies ColumnConfig<Member>[];
members.gts
<Table @columns={{columns}} @items={{members}} />

{{! Glint checks this against ColumnConfig<Member>: }}
<Table @columns={{columns}} @items={{projects}} />
{{! ^ Type 'Project[]' is not assignable to 'Member[]' }}

Every component

Grouped by the job

Everything Frontile documents today, with the deprecated legacy packages left out. Every name is a link.

Utilities

Small primitives the rest of the library is built out of.

Buttons

One press primitive, five shapes of it.

Overlays

Focus trapping, scroll locking, and portalling, shared by every layer.

Collections

Typed, keyboard-navigable lists and tables that infer against your data.

Feedback

Progress and notifications, with the live regions wired up.

Start here

Pick a component and start reading.

Every component's documentation is built around live, interactive demos of the component itself: the same ones running on this page, with the source beside each of them. Start wherever your interface is thinnest.

Readable by your agent, too

Frontile publishes llms.txt and llms-full.txt to the llms.txt standard, mirrors every page as plain Markdown at the same URL plus .md, and puts a one-click handoff to ChatGPT or Claude on each one. Point your coding agent at a component's real documentation instead of hoping it guessed the API.

 pnpm install frontile @frontile/theme

Two packages, a Tailwind v4 stylesheet, and one @source line so Tailwind stops purging Frontile's classes. The installation guide has the exact paths.

Released under MIT License - Created by Josemar Luedke