Ember.Ember

Components

Components

One sheet per component of the app and the site, with a live preview, its states, its measured anatomy, its actual classes and their source.

Buttons

One language for the app and the site: orange ember-500 for the action, ember-700 for small buttons, text always white, 12 px radius, no shadow.

Site buttons

Site
The large action button is solid ember-500, white text, with no shadow or icon. One orange action per screen.

Variants

Site buttons
RoleHeightPreview
Primary48 pxPrimary
Secondary48 pxSecondary
Link (tertiary)44 pxLink
Header CTA44 px, 40 px from 1,024 pxGet started
Compact (cookies, booking)44 pxDeclineAccept

States

Continue
Hover: ember-600
Continue
Focus: 2 px ember-700 ring
Disabled: opacity-60
Loading: spinner, disabled

A button with an action in progress is disabled and shows Loader2 h-4 w-4 animate-spin before its label.

Measured anatomy

Get started
24 px48 px

min-h-12 · px-6 py-3 · rounded-xl · gap-2 (8 px)

Get started
16 px44 px

h-11 lg:h-10 · px-4 · rounded-xl (header)

Measurements of a site button
MeasureValueClass
Primary and secondary height48 pxmin-h-12
Header CTA height44 px, 40 px from 1,024 pxh-11 lg:h-10
Link and compact button height44 pxmin-h-11
Horizontal padding24 px (16 px for header and compact)px-6 px-4
Vertical padding12 pxpy-3
Radius12 pxrounded-xl
Icon and text gap8 pxgap-2
Font16 px (inherited) or 15 px, 600font-semibold
Transition280 ms (150 ms for header and compact)duration-base

Tokens and code

Primary

inline-flex min-h-12 items-center justify-center gap-2 rounded-xl bg-ember-500 px-6 py-3 font-semibold text-white transition-colors duration-base ease-motion motion-reduce:transition-none hover:bg-ember-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ember-700 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60

Secondary

inline-flex min-h-12 items-center justify-center gap-2 rounded-xl border border-neutral-300 bg-white px-6 py-3 font-semibold text-neutral-900 transition-colors duration-base ease-motion motion-reduce:transition-none hover:border-ember-500 hover:text-ember-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ember-700 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60

Link

inline-flex min-h-11 items-center font-semibold text-neutral-900 underline decoration-neutral-300 underline-offset-4 transition-colors duration-base ease-motion motion-reduce:transition-none hover:decoration-neutral-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ember-700 focus-visible:ring-offset-2

Header CTA

inline-flex h-11 items-center rounded-xl bg-ember-500 px-4 text-sm font-medium text-white transition-colors duration-fast ease-motion motion-reduce:transition-none hover:bg-ember-600 lg:h-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ember-700 focus-visible:ring-offset-2

Rules

  • White text on every orange button, never dark. Large button (14 px and up) ember-500, hover ember-600; small button (13 px and below) ember-700, hover ember-800.
  • No logo, icon or shadow in an action button; never a gradient.
  • One highlighted button per screen, reserved for three buttons: the header, "Choose Pro" and "Build my team". The page shows none.
  • 44 px below 1,024 px; at least 40 px above.

Do and don't

Do

  • Get started

    Do: One orange primary button per screen, white text.

  • See pricing

    Do: The secondary button beside it, neutral-300 border.

Don't

  • Get started

    Don't: Dark text on an orange button.

  • Get started

    Don't: A gradient or an icon in the button.

App Button

App
The Button component is the reference for all app buttons: large CTA, small CTA, black button, outline, icon. Its base: inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium transition-colors duration-fast ease-motion motion-reduce:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ember-700 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-60

Variants

Primary actionSmall CTABlack buttonOutlineSecondarySubtleLinkDelete
App Button variants
VariantClasses
brandbg-ember-500 text-white hover:bg-ember-600
brand-smbg-ember-700 text-xs text-white hover:bg-ember-800
defaultbg-neutral-900 text-white hover:bg-neutral-800
outlineborder border-neutral-300 bg-white text-neutral-900 hover:border-neutral-900
secondarybg-neutral-100 text-neutral-900 hover:bg-neutral-200
ghosttext-neutral-700 hover:bg-neutral-100
linktext-neutral-900 underline decoration-neutral-300 underline-offset-4 hover:decoration-neutral-900
destructivebg-danger-700 text-white hover:bg-danger-800

Sizes

defaultsmlg
App Button sizes
SizeClassesHeight
defaulth-11 px-4 lg:h-1044 px, 40 px from 1,024 px
smh-11 px-3 lg:h-944 px, 36 px from 1,024 px
lgh-12 px-848 px
iconh-11 w-11 lg:h-10 lg:w-1044 px, 40 px from 1,024 px

States

Save
Rest
Save
Hover: ember-600
Save
Focus: 2 px ember-700 ring
Disabled: opacity-60
Loading: Loader2, disabled

The disabled state cuts the pointer and sets opacity to 60%. The prop loading forces it disabled and adds the spinner. On hover with a real pointer, a filled button rises by 2 px (hover-lift), no shadow.

Usage code

Primary action

<Button variant="brand">Get started</Button>

Small CTA, size sm

<Button variant="brand-sm" size="sm">Send</Button>

Icon button

<Button variant="ghost" size="icon" aria-label="Close"><X className="h-5 w-5" /></Button>

Fields

A single field for the app and the site: 44 px, 12 px radius, neutral-200 border, ember-700 focus. The site uses the lg size, at 48 px.

Field

AppSite
Field, Label, Hint, Error and Select components.

Live preview

Help text under the field, at 14 px.

Default size, 44 px.

States

Rest: border-neutral-200
Focus: ember-700 border and ring
Error: border-danger-700
Disabled: bg-neutral-50, opacity-60

Dropdown list

Measured anatomy

File name
12 px44 px

min-h-11 · px-3 · rounded-xl · 14 px text

you@company.com
16 px48 px

lg: h-12 · px-4 · 16 px text

Field elements
ElementClasses
Fieldmin-h-11 w-full rounded-xl border border-neutral-200 bg-white px-3 text-sm
lg field (site)h-12 px-4 text-[16px]
Text arearesize-y py-3 leading-[1.6]
Labeltext-sm font-medium text-neutral-800, asterisk text-neutral-500
Helpmt-2 text-sm text-neutral-600
Errorborder-danger-700 on the field, message mt-2 text-sm text-danger-700 with role="alert"
Listappearance-none pr-10 + chevron h-4 w-4 text-neutral-500
Phone16 px text (text-base md:text-sm): prevents iOS zoom
Choice cardmin-h-[96px] rounded-3xl border bg-white p-5, selection border-ember-500, aria-pressed
Form progressh-0.5 rounded-full bg-ember-500 on bg-neutral-200

Usage code

Field

min-h-11 w-full rounded-xl border border-neutral-200 bg-white px-3 text-sm text-neutral-800 placeholder:text-neutral-400 transition-colors duration-fast ease-motion motion-reduce:transition-none focus-visible:outline-none focus-visible:border-ember-700 focus-visible:ring-2 focus-visible:ring-ember-700/25 disabled:bg-neutral-50 disabled:opacity-60

lg field (site)

h-12 w-full rounded-xl border border-neutral-200 bg-white px-4 text-[16px] text-neutral-900 placeholder:text-neutral-400 transition-colors duration-fast ease-motion motion-reduce:transition-none focus-visible:outline-none focus-visible:border-ember-700 focus-visible:ring-2 focus-visible:ring-ember-700/25 disabled:bg-neutral-50 disabled:opacity-60

Do and don't

Do

  • Name

    Do: A visible label above the field.

Don't

  • Don't: A placeholder in place of the label.

Checkboxes, switches, radios

Checkbox, Switch and card radio components, all in ember for the active state.

Checkboxes, switch and card radios

AppSite

Live preview

Switch onOff
Checkboxes, switches and radios
ElementClasses
Checkboxh-4 w-4 accent-ember-500, clickable label min-h-11
Switchrole="switch"; track h-6 w-11 rounded-full, bg-ember-500 on and bg-neutral-300 off; thumb h-5 w-5 rounded-full bg-white, no shadow
Card radiosrole="radiogroup"; rounded-xl border border-neutral-200 px-4 py-3; selection border-ember-500 bg-ember-50 text-ember-800
Dark mode switchrounded-lg bg-neutral-100 p-2 + Sun or Moon h-5 w-5

All of them take the shared focus ring (ring-ember-700).

Do

  • Do: A checkbox in accent-ember-500 with a 44 px clickable label.

Don't

  • Don't: A switch whose thumb does not move: the state is read only by color.

Badges and chips

Small status or context markers. An orange chip has ember-800 text; a state chip takes its color set.

Badges and chips

AppSite

Live preview

ContextInternal accountUnavailableErrorInfoAvailablePressMomentSuggestion

Variants

Badges and chips
ElementClasses
Orange chiprounded-full border border-ember-200 bg-ember-50 px-2.5 py-1 text-xs font-semibold text-ember-800
State chiprounded-full border border-{role}-200 bg-{role}-50 px-2.5 py-1 text-xs font-semibold text-{role}-800
Status with dotrounded-full border border-neutral-200 bg-white px-3 py-1 text-[13px] font-medium text-neutral-700 + dot h-1.5 w-1.5 bg-{role}-500
Pressrounded-full bg-ember-700 px-3 py-1 text-xs font-semibold text-white
Moment labelrounded-full border border-neutral-200 bg-neutral-50 px-3 py-1 text-[11px] font-semibold
Home suggestionrounded-full border border-neutral-200 bg-white px-3.5 py-2 text-sm font-medium text-neutral-600 hover:border-ember-200 hover:bg-ember-50 + 16 px icon text-ember-500
Composer attachmentrounded-lg px-2.5 py-1.5 text-xs font-medium; neutral, while uploading neutral-600, on error danger
Offerrounded-full bg-ember-700 px-2 py-0.5 text-[11px] font-semibold text-white
Rule
No gradient in a chip.

Usage code

Orange chip

inline-flex items-center rounded-full border px-2.5 py-1 text-xs font-semibold border-ember-200 bg-ember-50 text-ember-800

Tabs and segmented controls

Two components: Tabs to navigate between pages, Segmented to switch views. Active without shadow, 44 px targets.

Tabs and segmented selectors

AppSite

Live previews: click to switch

Segmented: gray segmented

Tabs: underlined

Pill segmented: two choices

Segmented in a dialog

Filter chips

Tabs and segmented controls
ElementClasses
Tabsrole="tablist"; tab min-h-11 px-4 text-sm font-semibold; rule border-b border-neutral-200; active border-b-2 border-ember-500 text-neutral-950; rest text-neutral-500 hover:text-neutral-700
Segmentedtrack rounded-xl bg-neutral-100 p-1; segment min-h-11 sm:min-h-9 rounded-lg px-3.5 text-sm font-semibold; active border border-neutral-200 bg-white text-neutral-900; rest text-neutral-600 hover:text-neutral-900
Pill segmentedtrack rounded-full border border-neutral-200 bg-neutral-100 p-1; white thumb sliding in duration-base; segments min-h-11 rounded-full px-4 text-sm font-semibold; aria-pressed
Filter chipsmin-h-11 rounded-full border px-5 text-sm font-semibold; active border-neutral-200 bg-neutral-100 text-neutral-900
Keyboardarrows, Home, End; shared focus ring
Rule
No shadow on the active segment: the neutral-200 border is enough.

Dialogs and sheets

Two components: Dialog on desktop, Sheet on phones. A single backdrop, a single shadow: shadow-modal.

Dialog and mobile sheet

AppSite

Live preview

Anatomy and dimensions

Dialog anatomy
ElementValue
Backdropbg-neutral-900/40, animation motion-backdrop-in; blur backdrop-blur-[2px] on viewers
Panelrounded-2xl bg-white shadow-modal; motion-sheet-in on phones, motion-dialog-in on desktop
Mobile sheetrounded-t-2xl; handle h-1 w-10 rounded-full bg-neutral-300; closes by dragging more than 80 px
Sizessm max-w-[26rem], md max-w-xl, lg max-w-4xl; height 90dvh when compact, 100dvh as a sheet
Titlefont-satoshi text-xl font-bold text-neutral-900
Footerborder-t border-neutral-100 px-4 pt-3 pb-[max(env(safe-area-inset-bottom),0.75rem)]
Closeh-11 w-11 rounded-lg + X h-4 w-4
Accessibilityfocus trap, Escape, focus return, portal to document.body

Site dialogs

Site dialogs
DialogDetail
Sign upcard max-w-[480px] rounded-3xl border, shadow shadow-modal
Cookie preferencespanel max-w-[420px] rounded-3xl p-6, title font-satoshi text-lg font-bold, close size-10 rounded-full
Team configuratorrole="dialog", sm:max-w-[640px] sm:rounded-3xl, 44 px Minus and Plus buttons
Cookie bannerfixed bottom-[max(1rem,env(safe-area-inset-bottom))] left-4 right-4 rounded-3xl border border-neutral-200 bg-white p-4 sm:max-w-[360px], no shadow; buttons min-h-11 rounded-xl

Tooltips

A Tooltip component. Every icon-only button gets a tooltip and an aria-label.

Tooltip

App

Live preview

Send messageSecond Brain
Tooltip anatomy
ElementValue
Classesrounded-lg bg-neutral-900 px-2 py-1 text-xs font-medium text-neutral-50
Shadownone
Appearance150 ms fade on hover and keyboard focus
Accessibilityrole="tooltip" linked by aria-describedby
Truncated textthe native title attribute remains allowed

Usage code

Tooltip

rounded-lg bg-neutral-900 px-2 py-1 text-xs font-medium text-neutral-50

Toasts

A Toast component and its provider. Three seconds, bottom center on phones, bottom right on desktop.

Toast

App

Live preview

File savedLink copied
Toast anatomy
ElementValue
Classesrounded-xl bg-neutral-900 px-4 py-3 text-sm text-white shadow-menu
Rolerole="status"
Duration3 s
Positionfixed inset-x-4 bottom-4 on phones; sm:inset-x-auto sm:right-6 sm:w-96 on desktop
SuccessCheckCircle2 h-5 w-5 text-success-500
Celebrationsame toast; confetti in the brand colors (#ff8c42, #f97316, #ffb374, #ffcfa3, #e7e5e4)

Usage code

Toast

rounded-xl bg-neutral-900 px-4 py-3 text-sm text-white shadow-menu

Cards

A single card style: neutral-200 border, no shadow, no gradient. 24 px radius on the site, 12 px in the app.

Cards

AppSite

Live preview

Site card

neutral-200 border, no shadow.

Clickable card

Hover: ember-500 border. Focus: ember-700 ring.

App card

12 px radius, p-4, gap-3.

Measured spacing

p-6: 24 px
24 px
md:p-8: 32 px
32 px
app: p-4: 16 px
16 px
Cards
CardClasses
Siterounded-3xl border border-neutral-200 bg-white p-6 md:p-8
Site, clickablerounded-3xl border border-neutral-200 bg-white p-6 md:p-8 transition-colors duration-base ease-motion motion-reduce:transition-none hover:border-ember-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ember-700 focus-visible:ring-offset-2
Approunded-xl border border-neutral-200 bg-white p-4
App, clickable+ hover:border-neutral-300 hover-lift and the shared focus ring
Card iconh-11 w-11 rounded-xl bg-ember-50 text-ember-800
Credits cardrounded-xl border border-ember-200 bg-ember-50 px-4 py-3.5
Warningsee State colors: rounded-xl border px-4 py-3

Usage code

Site card

rounded-3xl border border-neutral-200 bg-white p-6 md:p-8

App card

rounded-xl border border-neutral-200 bg-white p-4

Tables

A single table style for the app, chat included: 12 px frame, neutral-100 header, neutral-200 rules.

Table

App

Live preview

ItemStatus
MarketTo validate
OfferValidated
Table anatomy
ElementClasses
Frameoverflow-hidden rounded-xl border border-neutral-200
Headerbg-neutral-100; cell px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.05em] text-neutral-600
Cellpx-4 py-3 text-sm text-neutral-800
Rowborder-t border-neutral-200 hover:bg-neutral-50
Chat tablesame style; cell width at most 250 px

Empty states

An EmptyState component: dashed frame, short title, text, one action.

Empty state

App

Live preview

No files yet

Create your first file to find your documents here.

New file
Empty state anatomy
ElementClasses
Framerounded-2xl border border-dashed border-neutral-300 bg-white px-6 py-10 text-center
Titletext-sm font-medium text-neutral-900
Textmx-auto mt-2 max-w-md text-sm text-neutral-500
Actionan ember-500 button, 44 px
Large empty state titlefont-satoshi text-[22px] md:text-[26px]

Loading states

Spinner, skeletons, work indicators. A discreet loading state shows nothing before 300 ms.

Loading states

AppSite

Live previews

Loader2 spinner, three sizes

121620

Three dots

Skeleton

"Ember is working"

Thinking…

Loading states
LoadingDetail
SpinnerLoader2 animate-spin: 16 px by default, 20 px for a page, 12 px inside a chip
Skeletonopacity from 0.5 to 1 over 1.6 s on bg-neutral-100 rounded-lg or rounded-2xl
Ember is working18 px breathing logo, "Thinking…" label in text-[13px] with a text shine; falls back to text-neutral-500 with reduced motion
Three dotsh-1.5 w-1.5 rounded-full bg-ember-500
Reasoningmax-h-24 border-l border-neutral-100 pl-3 text-[13px] text-neutral-500
Discreet loading.motion-delayed-in: nothing before 300 ms

Composer

The Second Brain composer, reused by the home field on the site: a single 24 px surface, from neutral-50 to white on focus, with the same ring as fields, no shadow.

Composer

AppSite

Live preview: click in the field

The buttons, from left to right

"+": bg-neutral-100
Attach, dictate: text-neutral-500
Dictation active: bg-danger-500 + ring-4
Send, active: bg-ember-500
Send, inactive: bg-neutral-100
Stop: bg-neutral-800

Anatomy and dimensions

Composer anatomy
ElementClasses
Framebg-white px-4 pb-[max(env(safe-area-inset-bottom,0px),12px)] pt-3 md:px-8; content mx-auto max-w-3xl
Surfacerelative rounded-3xl border border-neutral-200 bg-neutral-50 transition-all duration-base ease-motion focus-within:border-ember-700 focus-within:bg-white focus-within:ring-2 focus-within:ring-ember-700/25
Rowflex items-end gap-1.5 px-2.5 py-2
Text areamin-h-11 lg:min-h-9 flex-1 resize-none bg-transparent px-2 text-base leading-5 text-neutral-800 placeholder:text-neutral-400 md:text-sm; max-h-[160px]
Animated placeholdercaret .composer-typewriter-caret of 1 px, blinking every 1 s
Buttonsh-11 w-11 lg:h-9 lg:w-9 rounded-full; 16 px icon, 20 px "+"
Sendactive bg-ember-500 text-white hover:bg-ember-600; inactive bg-neutral-100 text-neutral-400; ArrowUp h-4 w-4; FYG mode and site: same button
Drag and dropborder-ember-400 bg-ember-50/50 ring-2 ring-ember-500/20; overlay bg-ember-50/80 backdrop-blur-[2px]
Dark modethe frame loses its background, border and shadow; the halo is hidden
surface: 24 px radiusrow: 10 px padding, 6 px gapbuttons: 44 px, 36 px from 1,024 px

Usage code

Surface

relative rounded-3xl border border-neutral-200 bg-neutral-50 transition-all duration-base ease-motion focus-within:border-ember-700 focus-within:bg-white focus-within:ring-2 focus-within:ring-ember-700/25

Chat bubbles

The user has a gray bubble on the right. The assistant has no bubble: its text is free, on the left.

Messages

App

Live preview

Prepare my application for a founder loan.

Here is the plan I propose. The assumptions still need to be confirmed before sending.

const total = block + block;

Back to bottom
Message anatomy
ElementClasses
Useron the right, max-w-[85%]; whitespace-pre-wrap rounded-xl bg-neutral-100 py-2.5 pl-4 pr-12 text-[15px] text-neutral-800; Copy button top right, visible on hover or focus
Assistantno bubble: max-w-[92%] text-[15px] leading-relaxed text-neutral-800
Reply frames (code, quote)overflow-hidden rounded-xl border border-neutral-300 bg-neutral-50; 16 px margin; no shadow or orange rule
Frame Copy button40 × 40 px area, visible on hover, on keyboard focus and always on touch screens
Inline codebackground #f5f5f4, 4 px radius, 0.85em, weight 500, no orange
Back to bottomrounded-full border border-neutral-200 bg-white px-3 py-1.5 text-xs font-medium text-neutral-700 + ArrowDown h-3.5 w-3.5, no shadow
Attached imagethumbnail h-24 w-24 rounded-lg object-cover
Actions under a replygrid h-11 w-11 lg:h-8 lg:w-8 rounded-lg text-neutral-500 hover:bg-neutral-100

Email and image cards

Chat deliverables: an email to send, a generated image or video.

Email card

App

Live preview

Email
Torecipient
SubjectMeeting

Hello, here is our offer. We remain available to discuss it.

ExpandSend
Email card anatomy
ElementDetail
Frameoverflow-hidden rounded-xl border border-neutral-300 bg-neutral-50
Bannermin-h-11, Mail icon h-3.5 w-3.5, 11 px uppercase label
Sheetwhite background
Headersborder-b border-neutral-200 px-4 py-2 text-[13px] leading-5; label w-12 text-neutral-500
Bodypx-4 py-4 text-[14px] leading-6 text-neutral-800; collapse animated over 280 ms, 48 px white fade
Expandmin-h-11 rounded-lg px-2 text-[13px] font-medium
Send32 px ember-700 small CTA, 44 px touch area through a pseudo-element
Skeletonanimate-pulse, bars h-3 rounded bg-neutral-100

Image and video cards

App

Live preview

Download
Image and video cards
CardClasses
Generated imagerounded-xl, max-h-[560px] object-contain, cursor-zoom-in; download min-h-9 rounded-full bg-neutral-900/60 px-3 text-xs text-white, visible on hover on desktop and always on touch screens
Videorounded-xl border border-neutral-200 bg-white; player bg-neutral-900, play button h-9 w-9 rounded-full bg-white, progress h-1.5 bg-white/30
Media rows.chat-media-row