Skip to content

Roadmap — @gnome-ui/react

Component implementation plan following the GNOME Human Interface Guidelines.

Legend: ✅ Done · 🚧 In progress · ⬜ Pending


Next Package Priorities

Framework expansion will proceed in this order. All three packages build on the framework-agnostic tokens and themes provided by @gnome-ui/core.

Priority Status Package Description
1 🚧 @gnome-ui/web-components Framework-agnostic Custom Elements for GNOME widgets. Foundation: gnome-button, gnome-dialog, gnome-menu, gnome-toast, and gnome-popover; uses semantic HTML and light DOM, exposes standard DOM events, preserves accessibility, and remains compatible with htmx and server-rendered fragments.
2 ⬜ @gnome-ui/angular Angular package built after the Web Components foundation. Provide standalone components, Angular Forms integration where applicable, and reuse @gnome-ui/core tokens plus Web Components rather than duplicating styling and behavior. Blocked on Priority 1.
3 🚧 @gnome-ui/react-native React Native package targeting iOS/Android/GNOME-mobile shells. No DOM to reuse from Web Components, so components are rebuilt with native primitives (View, Text, Pressable) and StyleSheet; consumes @gnome-ui/core tokens via a generated RN-compatible theme object (JS values, not CSS custom properties) and mirrors the @gnome-ui/react component API/props where the platforms overlap. Independent of Priority 1/2 — can start in parallel since it does not depend on the Web Components foundation.

Tier 1 — Base

Foundation components. Everything else depends on these.

Status Component Description
✅ Button Default, Suggested, Destructive, Flat, Pill, Circular
✅ Text All 12 Adwaita styles: large-title, title-1…4, heading, body, document, caption, caption-heading, monospace, numeric
✅ Link Inline hyperlink with accent colour, hover underline, and external-URL variant
✅ Text Field Text input with label, helper text, and error state
✅ Switch On/off toggle — replaces checkbox in settings UIs
✅ Checkbox Multi-selection with indeterminate state
✅ Radio Button Single selection within a group

Tier 2 — Layout & Containers

Structure and grouping. Core patterns of GNOME app UIs.

Status Component Description
✅ Separator Horizontal/vertical divider line
✅ Card Elevated surface for grouping related content
✅ Boxed List Rounded bordered list of ActionRow items — the most common GNOME pattern
✅ Action Row Row with title, subtitle, and an end widget (switch, button…)
✅ Header Bar Title bar with centered title and action buttons

Tier 3 — Navigation

Status Component Description
✅ Tabs / Tab Bar Tab-based navigation
✅ View Switcher Segmented control for switching between views
✅ Sidebar Lateral navigation panel
✅ Search Bar Collapsible search bar
✅ PathBar Breadcrumb path bar for hierarchical navigation; ancestor segments are interactive buttons, current segment is bold non-interactive label; optional per-segment icons — issue #17

Tier 4 — Feedback

Status Component Description
✅ Spinner Indeterminate loading indicator
✅ Progress Bar Determinate and indeterminate progress
✅ Skeleton Content-shaped loading placeholder for web-style skeleton screens; pragmatic extension beyond GNOME HIG loading patterns — issue #95
✅ Toast Non-blocking temporary notification
✅ Banner Persistent message at the top of a view
✅ Dialog Blocking modal with title, body, and buttons
✅ Tooltip Informational text on hover
✅ Status Page Empty state with icon, title, and description
✅ AnimatedIcon Plays the CSS animation embedded in an animated icon from @gnome-ui/icons (Syncing, Recording, Downloading, Connecting); always honors prefers-reduced-motion. Pragmatic web analog to GTK 4.22's GtkSvg native animated-SVG renderer — see @gnome-ui/icons — Icon Library below

Tier 5 — Advanced Controls

Status Component Description
✅ Dropdown / Select Expandable option list
✅ Slider Numeric range with draggable handle
✅ Spin Button Numeric input with +/− buttons
✅ Avatar Circular user image with initials fallback
✅ Badge Counter or status indicator overlaid on another element
✅ Popover Floating panel anchored to a trigger element

Tier 6 — Adaptive Layout

Responsive primitives mirroring the libadwaita adaptive layout system.

GNOME breakpoints (1 sp ≈ 1 px at 1× density): | Threshold | Pattern triggered | |-----------|-------------------| | ≤ 400 sp | Collapse split views to single pane; sidebar becomes overlay | | ≤ 550 sp | Move ViewSwitcher from header bar to a bottom bar | | ≤ 860 sp | Collapse outer pane in nested two-level split views |

Sizing guidelines for sidebars: - Width fraction: 25 % of total width - Min width: 180 sp · Max width: 280 sp

Status Component Description
✅ useBreakpoint Hook that tracks viewport width against GNOME breakpoints and fires callbacks on change — mirrors AdwBreakpoint
✅ Clamp Constrains content to a maximum width while allowing it to shrink freely — mirrors AdwClamp
✅ NavigationSplitView Two-pane sidebar + content layout; collapses to a single navigable pane at ≤ 400 sp — mirrors AdwNavigationSplitView
✅ OverlaySplitView Sidebar + content layout where the sidebar becomes a slide-over overlay at ≤ 400 sp — mirrors AdwOverlaySplitView
✅ ViewSwitcherBar Bottom bar that replaces the header-bar ViewSwitcher on narrow windows (≤ 550 sp) — mirrors AdwViewSwitcherBar

Tier 7 — GNOME 48–50 (libadwaita 1.7–1.9)

New widgets introduced in libadwaita 1.7 (GNOME 48), 1.8 (GNOME 49), and 1.9 (GNOME 50 "Tokyo", March 2026).

libadwaita 1.7 — GNOME 48

Status Component Description
✅ ToggleGroup Unified group of mutually-exclusive toggle buttons — replaces the pattern of multiple Button in a box; mirrors AdwToggleGroup
✅ WrapBox Flexible wrapping layout for tag/chip lists that flows across multiple lines — mirrors AdwWrapBox
✅ Chip Compact pill-shaped label for tags, filters, and multi-select states; static, removable, and selectable modes

libadwaita 1.8 — GNOME 49

Status Component Description
✅ ShortcutsDialog Modal listing keyboard shortcuts with integrated search — mirrors AdwShortcutsDialog (replaces deprecated GtkShortcutsWindow)

libadwaita 1.9 — GNOME 50

Status Component Description
✅ Sidebar (v2) Rewrite of the existing Sidebar component following AdwSidebar: named sections, per-row suffix widgets, context menus, and tooltip
✅ ViewSwitcherSidebar Sidebar-based view switcher — mirrors AdwViewSwitcherSidebar (replaces GtkStackSidebar)
✅ BreakpointBin Applies layout changes when the component (not the viewport) crosses a size threshold — CSS container queries equivalent of AdwBreakpointBin

Cross-cutting — accessibility

Status Item Description
✅ High-contrast support Add @media (prefers-contrast: more) overrides to design tokens and all components — formalised in libadwaita 1.8

Cross-cutting — internationalization

Status Item Description
✅ Provider-level Intl formatting GnomeProvider supplies locale, dir, numberFormat, and dateTimeFormat; useNumberFormatter and useDateTimeFormatter apply those defaults across @gnome-ui/react, @gnome-ui/layout, and @gnome-ui/charts

Tier 8 — Style-class Utilities & Composition Helpers

Lightweight components and props derived from the libadwaita 1.9 style-class reference. These fill gaps between what we already ship and what the full Adwaita toolkit offers.

Layout primitives

Status Component Description
✅ Toolbar .toolbar box: flat-buttons, 6 px margins/gap — wraps AdwHeaderBar, GtkActionBar, and generic tool rows
✅ Spacer .spacer separator: invisible flex: 1 filler for Toolbar/HeaderBar to push trailing items to the end — mirrors GtkSeparator.spacer
✅ LinkedGroup .linked box: children rendered as a single connected unit with no gap and merged borders — canonical GNOME pattern for button groups and segmented inputs
✅ Frame .frame container: simple bordered surface (border + border-radius) without background — mirrors GtkFrame default styling

BoxedList / ActionRow variants

Status Component Description
✅ BoxedList variant="separate" Add variant="separate" to BoxedList: renders each child as its own rounded card — mirrors .boxed-list-separate
✅ ButtonRow Full-width activatable row styled as a button inside a BoxedList — mirrors AdwButtonRow; supports suggested and destructive variants
✅ ActionRow variant="property" Add variant="property" to ActionRow: flips weight so the subtitle is the primary text (read-only property display) — mirrors .property style class
✅ ExpanderRow Collapsible ActionRow that reveals nested rows on activation — mirrors AdwExpanderRow

Button additions

Status Component Description
✅ SplitButton Primary action button with an attached dropdown arrow — mirrors AdwSplitButton; supports suggested and destructive variants
✅ IconButton Accessible icon-only action button composed from Button, Icon, and optional Tooltip; supports variants, sizes, disabled state, refs, and HTML button attributes — issue #102
✅ Button raised variant Add variant="raised" to Button: explicit raised look for buttons inside flat/toolbar contexts — mirrors .raised style class
✅ Button osd modifier Add osd prop to Button: dark semi-transparent style for controls overlaid on media/images — mirrors .osd style class
✅ CopyButton Icon button that copies a value to the clipboard via navigator.clipboard, swapping to a checkmark and "Copied!" tooltip/live-region announcement as confirmation; composed from IconButton — no @gnome-ui/platform dependency required

View Switcher additions

Status Component Description
✅ InlineViewSwitcher Compact inline view switcher built on ToggleGroup internals; flat and round style variants — mirrors AdwInlineViewSwitcher (GNOME 48)

StatusPage addition

Status Component Description
✅ StatusPage compact prop Add compact prop to the existing StatusPage: reduces spacing and icon size for sidebars/popovers — mirrors .compact style class

Tab / Search inline variant

Status Component Description
✅ TabBar inline prop Add inline prop to TabBar: removes the header-bar background so the bar blends into any surface — mirrors .inline style class
✅ SearchBar inline prop Add inline prop to SearchBar: same neutral-background treatment as above — mirrors .inline style class
✅ SearchBar autocomplete suggestions prop + Popover-based typeahead list anchored to the input; keyboard navigation (↑↓ Enter); role="listbox" + aria-activedescendant; replaces deprecated GtkEntryCompletion following the GTK4 AdwSearchBar + GtkPopover + GtkListView pattern — issue #24

Tier 9 — Stories & Composition Examples

Full-page composition stories that demonstrate real-world use of the component library.

Status Story Description
✅ Layout/Dashboard Full-page app layout: Toolbar with logo + inline SearchBar + action buttons + avatar Popover, collapsible sidebar with SidebarItem + Badge, content area with Card, BoxedList, ExpanderRow, InlineViewSwitcher, StatusPage, and footer Toolbar
✅ Layout/FileManager GNOME Files (Nautilus)–style file browser: PathBar breadcrumb navigation in toolbar, collapsible Sidebar with Places + Network sections, InlineViewSwitcher for grid/list toggle, folder drill-down, and mobile overlay sidebar — issue #16
✅ Layout/Settings GNOME Settings–style preferences app: dual-headerbar pattern, 14-category sidebar with per-category icons, sub-page drill-down (Accessibility → Seeing), SwitchRow toggle rows, ActionRow for value navigation, PreferencesGroup sections

Tier 10 — Sidebar Enhancements (identified via Dashboard story)

Gaps discovered while building the Dashboard composition story.

Status Component Description
✅ Sidebar collapsed prop Built-in icon-only (mini/rail) mode: collapsed prop animates width (240 px → 56 px), hides labels/suffixes/section titles, and auto-shows tooltips on hover. SidebarCollapsedContext and useSidebarCollapsed hook exported for custom consumers. Dashboard story updated to use the real Sidebar component.

Tier 11 — Sidebar 1.9 Completeness

Gaps vs AdwSidebar in libadwaita 1.9 identified by reviewing the release notes.

Status Component Description
✅ Sidebar search/filter Built-in filter prop + SearchBar integration; shows a StatusPage placeholder when the filtered list is empty — mirrors AdwSidebar's GtkFilter support
✅ Sidebar adaptive mode mode prop ("sidebar" \| "page") that switches to a boxed-list layout on narrow viewports (≤ 400 sp), mirroring AdwSidebar mobile behaviour
✅ SidebarItem drop target onDrop / acceptTypes props to turn individual rows into drag-and-drop targets — mirrors AdwSidebar per-row drop target support

Tier 12 — Boxed List Row Variants

Specialised row types present in the libadwaita widget gallery but not yet implemented.

Status Component Description
✅ SwitchRow ActionRow with an embedded Switch as the end widget — mirrors AdwSwitchRow; the canonical GNOME settings toggle pattern
✅ ComboRow ActionRow with an inline dropdown selector — mirrors AdwComboRow; common for single-choice preferences
✅ EntryRow ActionRow where the subtitle area is a live text input — mirrors AdwEntryRow
✅ PasswordEntryRow EntryRow variant with masked input and a reveal toggle — mirrors AdwPasswordEntryRow
✅ SpinRow ActionRow with an embedded SpinButton — mirrors AdwSpinRow

Tier 13 — Preferences UI

Full preferences-window pattern used by most GNOME applications.

Status Component Description
✅ PreferencesGroup Titled section that wraps a BoxedList with an optional description — mirrors AdwPreferencesGroup
✅ PreferencesPage Scrollable page composed of PreferencesGroup sections — mirrors AdwPreferencesPage
✅ PreferencesDialog Multi-page settings dialog using PreferencesPage tabs — mirrors AdwPreferencesDialog (replaces AdwPreferencesWindow)

Tier 14 — Missing Navigation & Overlays

Navigation and overlay patterns present in the widget gallery but not yet shipped.

Status Component Description
✅ NavigationView Single-pane push/pop navigation stack — mirrors AdwNavigationView; the mobile-first counterpart to NavigationSplitView
✅ BottomSheet Slide-up panel that overlays content from the bottom edge — mirrors AdwBottomSheet (libadwaita 1.6+)
✅ Carousel Swipeable horizontal/vertical content carousel with CarouselIndicatorDots and CarouselIndicatorLines — mirrors AdwCarousel
✅ AlertDialog role="alertdialog" + responses/onResponse API added to Dialog — mirrors AdwAlertDialog
✅ AboutDialog variant="about" + app info props added to Dialog (details/credits/legal tabs) — mirrors AdwAboutDialog

Tier 15 — Layout & Utility Gaps

Small primitives and helpers present in the widget gallery that round out the system.

Status Component Description
✅ Bin Single-child container with no visual styling — mirrors AdwBin; useful as a base for custom components
✅ ToolbarView Layout container that attaches a HeaderBar/ActionBar at top or bottom and scrolls only the middle content — mirrors AdwToolbarView
✅ WindowTitle Two-line title + subtitle widget centred in a HeaderBar — mirrors AdwWindowTitle
✅ ShortcutLabel Read-only display of a keyboard shortcut (e.g. Ctrl+S) with proper key-cap styling — mirrors GtkShortcutLabel
✅ ButtonContent Icon + label layout helper for buttons with both an icon and text — mirrors AdwButtonContent

@gnome-ui/icons — Icon Library

Framework-agnostic Adwaita symbolic icon definitions. Each icon is a plain IconDefinition object (SVG path data) consumed by the <Icon> component in @gnome-ui/react.

Status Category Icons
✅ Navigation GoPrevious, GoNext, GoHome, GoUp, PanDown, PanUp, PanStart, PanEnd
✅ Actions Add, Remove, Delete, Edit, Copy, Paste, Cut, Undo, Redo, Save, DocumentOpen, Close, Search, Refresh, Share, Attachment
✅ UI OpenMenu, ViewMore, ViewSidebar, ViewReveal, ViewConceal, Settings
✅ Status Information, Warning, Error, Check
✅ People & Identity Person, Accessibility
✅ System & Hardware Applications, Notifications, InputMouse, InputKeyboard, InputTablet, ColorManagement, Printer, Lock
✅ Misc Star, StarOutline, Heart
✅ Media MediaPlay, MediaPause, MediaSkipForward, MediaSkipBackward

Tier 17 — @gnome-ui/layout Package

Dedicated npm package that ships the full-page application shell as a first-class primitive. Consumers can compose any GNOME application layout without pulling in the full @gnome-ui/react bundle.

Status Component Description
✅ Layout Full-page shell with four named zones: topBar (pinned header), sidebar (fixed-width navigation), children (scrollable content), and bottomBar (pinned footer). All zones are optional. Mirrors the composition pattern demonstrated in Layout/Dashboard. Ships as @gnome-ui/layout with its own CSS output, Vitest unit tests, and Storybook story (Layout/Layout).

Tier 16 — Platform Integration

New workspaces that bridge the React layer with the GNOME host process. See ARCHITECTURE.md for the full dependency graph and communication model.

@gnome-ui/platform — TypeScript API layer

Pure TypeScript package. Communicates with GNOME via the WebKitGTK window.webkit.messageHandlers bridge or XDG Desktop Portals. Falls back to no-op stubs in standard browsers / test environments.

Status Module Description
✅ settings getSetting/setSetting/onSettingChanged — WebKitGTK via the bridge (request/response correlation for the async Gio.Settings.get_value() round-trip, plus a changed-signal subscription filtered by key). No browser fallback — there is no web equivalent of an app-schema-scoped GSettings store.
✅ notifications sendNotification/withdrawNotification/onNotificationAction — WebKitGTK via the bridge (Gio.Notification, action buttons), with a real browser Notification fallback (permission flow, single click routed through the same action-event shape as a synthetic 'default' action).
✅ fileChooser openFile/saveFile/selectFolder — WebKitGTK via the bridge (GtkFileChooserDialog / XDG portal), request/response correlated. No browser fallback — the File System Access API only hands back opaque handles, never real filesystem paths.
✅ colorScheme getColorScheme/setColorScheme/onColorSchemeChanged — WebKitGTK via the bridge (maps to Adw.StyleManager; set only ever overrides this app's own rendering, never the desktop-wide preference), with a real matchMedia('(prefers-color-scheme: dark)') fallback for get/subscribe in browsers/PWAs. set has no browser fallback — there is no JS API to force the browser's own rendering into a scheme.
✅ window getWindowState/setMaximized/setFullscreen/minimizeWindow/closeWindow/onWindowStateChanged — WebKitGTK via the bridge (Gtk.Window's maximized/fullscreened/is-active). Browser/PWA fallback for fullscreen (real Fullscreen API), focused (real document.hasFocus()), and close (real window.close(), subject to the browser's own script-only restriction); maximized/minimize have no browser equivalent and either report false or reject.
✅ clipboard readText/writeText/readImage/writeImage/readFiles/writeFiles — WebKitGTK via the bridge (request/response correlation for the async Gdk.Clipboard reads; images cross the bridge as base64 data: URLs). Real navigator.clipboard fallback for text and images in browsers/PWAs (Blob⇄data: URL conversion keeps both paths returning the same shape). Files have no browser fallback — same constraint as fileChooser: browsers never expose real filesystem paths to page scripts.
✅ portals callPortal/onPortalSignal — the generic escape hatch behind the other portal-backed modules: an arbitrary { interface, method, args } D-Bus call (request/response correlated, bridging the real portal Request/Response two-step) plus signal subscription filtered by interface+signal. WebKitGTK only — portals are meaningless without a sandboxed host.

@gnome-ui/hooks — React hooks

React hooks that surface every @gnome-ui/platform module as idiomatic React state.

Status Hook Description
✅ useRuntime Full runtime snapshot: shell, engine, browser, OS
✅ usePlatform Convenience booleans for the current shell context (isGnomeWebView, isPWA…)
✅ useNativeEvent Subscribe to an event dispatched by the GJS host via gnome: prefix
✅ useBreakpoint Reactive isMobile, isTablet, isDesktop flags based on GNOME HIG breakpoints
✅ useSettings Read and write a GSettings key; re-renders on external changes, optimistic writes, exposes loading/error
✅ useNotification Returns send/dismiss, scoped to the component's lifetime — notifications sent through it are auto-withdrawn and onAction listeners auto-unsubscribed on unmount
✅ useColorScheme Returns the resolved "light"/"dark" scheme and a "light"/"dark"/"system" setter, reactive to external changes — talks to Adw.StyleManager via the bridge, distinct from @gnome-ui/react's GnomeProvider-based hook of the same name
✅ useFileChooser Returns open()/save()/selectFolder() triggers and the resolved path/paths as reactive state, plus loading/error
✅ useClipboard Returns { value, copy, paste } with reactive clipboard state, plus loading/error — text only; use @gnome-ui/platform's clipboard module directly for images/files
✅ useWindowState Returns { maximized, fullscreen, focused } and matching actions (setMaximized, setFullscreen, minimize, close), plus loading/error
✅ useHapticFeedback Trigger haptic feedback via feedbackd (native) or Vibration API (browser) — issue #81

GNOME 50 Compatibility

Items identified from GNOME 50 (libadwaita 1.9, March 2026) that require new work or updates.

Accessibility — prefers-reduced-motion

Status Item Description
✅ ExpanderRow Disable grid-template-rows transition and chevron rotate on reduced-motion
✅ PanelCard (@gnome-ui/layout) Disable grid-template-rows collapse transition on reduced-motion
✅ Layout (@gnome-ui/layout) Disable mobile sidebar slide-in transition on reduced-motion

CSS theming

Status Item Description
✅ Unified style.css GNOME 50 deprecates autoloaded style-dark.css / style-hc.css in favour of a single file with @media queries — tokens.css already follows this pattern

Adaptive layout guidance

Status Item Description
✅ Mobile-first audit Verified 400/550/860 sp thresholds and mobile-first defaults; added swipe-to-dismiss to OverlaySplitView; added NarrowViewport stories to NavigationSplitView, OverlaySplitView, and BreakpointBin — issue #15

New widgets

Status Component Description
✅ ColumnView Multi-column sortable data table — mirrors GtkColumnView / AdwColumnView (issue #14)

@gnome-ui/charts — Chart Components

Data visualisation components built on top of Recharts, fully styled with Adwaita design tokens.

Status Component Description
✅ LineChart Multi-series time-series line chart with optional grid and legend
✅ BarChart Grouped bar chart for categorical comparisons
✅ AreaChart Filled-area chart with stacking and gradient fill options
✅ PieChart Pie and donut chart for part-to-whole comparisons
✅ RadarChart Radar/spider chart for multi-attribute comparisons across subjects
✅ RadialBarChart Concentric arc bars for multiple circular progress metrics
✅ TreeMap Proportional rectangles for hierarchical data with optional grouping
✅ CloudChart Word/tag cloud with value-proportional font sizing
✅ SparkLineChart Minimal inline line sparkline for embedding in cards and tables
✅ SparkAreaChart Minimal inline area sparkline with optional gradient fill
✅ SparkBarChart Minimal inline bar sparkline for compact trend display
✅ ScatterChart Scatter/bubble chart for correlation between two numeric variables; ZAxis encodes a third dimension as bubble size
✅ FunnelChart Funnel visualization for conversion rates and sales pipelines
✅ ComposedChart Mixed chart combining bars, lines, and areas on shared axes
✅ GaugeChart Single-value speedometer/gauge chart for KPI dashboards, complementing StatCard
✅ Heatmap Generic matrix heatmap for correlation and density data (distinct from ContributionGraph's calendar layout)
✅ SankeyChart Flow diagram for multi-stage funnels and user journeys
✅ BulletChart Bullet graph: performance value vs. target with qualitative range bands, complements GaugeChart in dense KPI rows
✅ WaterfallChart Floating-bar chart for the cumulative effect of sequential increases/decreases — revenue bridges, budget breakdowns
✅ BoxPlot Box-and-whisker plot for distribution, quartiles, and outliers across one or more groups; accepts raw values or precomputed statistics
✅ SparkGaugeChart Compact inline gauge for embedding in cards and table cells
✅ SparkPieChart Compact inline pie/donut for part-to-whole composition, embeddable in cards and table cells
✅ SparkBulletChart Compact inline bullet graph for embedding in cards and table cells

Tier 18 — Data Display

Read-only data display widgets rendered in pure HTML/SVG — no Recharts dependency.

Status Component Description
✅ ContributionGraph Activity heatmap calendar: a 52-week grid of rounded SVG cells where colour intensity (Adwaita green palette) represents activity count; supports dark mode, keyboard navigation (role="grid"), and screen-reader labels — issue #21

Tier 19 — Dashboard Components (@gnome-ui/layout)

Purpose-built components for composing dashboard and monitoring views.

Status Component Description
✅ DashboardGrid Responsive CSS Grid container for arranging dashboard widgets; supports column count and per-item span — issue #82
✅ StatCard Key metric display with optional trend indicator (direction, percentage, period) and loading skeleton — issue #83
✅ ProgressCard Resource usage card with labelled progress bar; color thresholds at 75 % (warning) and 90 % (critical) — issue #84
✅ ActivityFeed Chronological event list with relative timestamps, icons, and truncation — issue #85
✅ QuickActions Grid of shortcut action buttons with keyboard navigation — issue #86
✅ SectionHeader Section title row with optional subtitle and trailing action slot — issue #88
✅ EmptyState Centered empty-state illustration: icon, title, description, optional CTA — issue #89
✅ StatusIndicator Service/connection status dot: online, offline, warning, error, loading — issue #87
✅ ErrorState Error state with preset types (generic, network, permission, not-found) and recovery action slot — issue #90

Tier 20 — Atomic & Molecular Gaps

Widgets from the GTK4/libadwaita catalog (or well-established HIG-adjacent patterns) not yet covered by the ~96 components already shipped in @gnome-ui/react. Scoped to atoms and simple molecules only — no new organism/page-level compositions. Verified against the current component tree to avoid duplicating existing functionality (e.g. ColorPicker already covers Adwaita accent-color swatches via GNOME_PALETTE, so it is intentionally excluded here).

Atoms

Status Component Description
✅ LevelBar Discrete level indicator with colour-coded low/high/full offset zones — mirrors GtkLevelBar; distinct from ProgressBar (determinate task progress) and SegmentedBar (proportional category breakdown)
✅ Expander Standalone disclosure triangle + collapsible content — mirrors GtkExpander; complements ExpanderRow, which only works nested inside a BoxedList row
✅ PasswordField Standalone masked TextField with a built-in reveal/conceal toggle, for login forms and dialogs outside a settings-row context — reuses the reveal-icon pattern from PasswordEntryRow without requiring ActionRow
✅ Divider Horizontal rule with an optional centred label (e.g. "OR") — common auth/login-form pattern; distinct from the plain Separator line
✅ RatingStars Star rating display and interactive input, keyboard-navigable — common HIG-adjacent pattern for review/quality widgets, pairs with Icon's Star/StarOutline
✅ FileTypeIcon Small icon (optionally with a thumbnail) resolved from a file's MIME type or extension — useful for file-manager-style listings, complementing the existing Layout/FileManager story
✅ Callout Inline, dismissible admonition box (info/warning/tip variants) for contextual help text within forms and cards — distinct from Banner (persistent, view-level) and Toast (temporary, notification-level)
✅ StepIndicator Numbered "Step X of Y" progress indicator for onboarding/wizard flows — complements the existing CarouselIndicatorDots/CarouselIndicatorLines with a labelled, linear alternative
✅ RangeSlider Dual-thumb slider for selecting a min/max range — Slider is single-thumb only; needed for range filters (price, date range, etc.)
✅ TextTruncate Single/multi-line text truncation with an automatic tooltip revealing the full content on overflow — mirrors GtkLabel's ellipsize property
✅ Kbd Standalone single key-cap (e.g. Enter, Esc) for inline instructional text ("press Enter to continue") — complements ShortcutLabel, which only renders full +-delimited combos and doesn't expose its key-cap rendering separately
✅ Highlight Inline <mark>-based text highlighting for matched search terms — pairs with SearchBar's suggestion list and any filterable list/table
✅ VisuallyHidden Reusable "sr-only" utility — visually hides content while keeping it in the accessibility tree; extracts the recipe currently duplicated inline inside CopyButton's live-region announcement
✅ Overlay Standalone backdrop/scrim layer with fade transition and click-to-dismiss — extracts the near-identical backdrop implementation currently duplicated across Modal, Dialog, and BottomSheet

Molecules

Status Component Description
✅ Calendar Month-grid date display with keyboard navigation — mirrors GtkCalendar; usable standalone or as the panel inside DatePicker
✅ DatePicker TextField trigger + Popover-anchored Calendar — mirrors the GtkCalendar + GtkPopover composition pattern used for date entry across GNOME apps. showTime adds TimePicker's hour/minute columns under the calendar, turning the emitted Date into a point in time: the day click then keeps the popover open and a Done button finishes the selection
✅ TimePicker Hour/minute selection built from paired SpinButtons in a Popover, with 12/24-hour format support. The columns live in an internal TimeFields, shared with DatePicker/DateRangePicker's showTime footer
✅ CalendarRange Start/end date-range selection sharing Calendar's grid engine (roving tabindex, month/year drill-down, min/max). Emits through onChange only once both ends have a value — a first click sets the anchor and paints a live preview band up to the hovered (or keyboard-focused) day, and the second click commits the range; picking backwards reorders the pair instead of failing. visibleMonths (default 1) renders N side-by-side month panels paged as one unit, so a range spanning months is picked without navigating. Adds minRange/maxRange day limits on top of min/max, aria-selected on every in-range cell plus start/in-range/end suffixes in the day labels, and Esc to cancel an in-progress range (after leaving the drill-down)
✅ DateRangePicker Popover-anchored CalendarRange behind a DatePicker-style trigger reading "1 Aug – 15 Aug 2026". Defaults to visibleMonths={2}, closes only on a complete range, and offers an optional preset sidebar (last 7 days, this month, last quarter) — the composition counterpart of DatePicker, mirroring GtkCalendar + GtkPopover. showTime gives each end its own hour/minute columns plus a Done button, keeping start ≤ end when both land on the same day
✅ FontPicker Button that opens a family/size/weight chooser — mirrors GtkFontDialogButton
✅ EmojiPicker Searchable emoji grid in a Popover — mirrors GtkEmojiChooser
✅ TagInput Type-to-add multi-value input rendering entries as removable Chips in a WrapBox — WrapBox/Chip currently only support static/pre-populated display, not interactive entry
✅ OtpInput Segmented PIN/verification-code input (one cell per digit, auto-advance, paste support) — common auth pattern, pairs naturally with PasswordEntryRow/PasswordField
✅ CopyField Read-only TextField/EntryRow with a built-in trailing CopyButton — for displaying copyable values (API keys, tokens, IDs) outside the CveIdentifier/CweIdentifier-style specialised components
✅ ChoiceCardGroup Card-based single-choice selector (large selectable cards instead of radio buttons) — mirrors the pattern used in GNOME Initial Setup / welcome flows
✅ FileDropZone Drag-and-drop file upload target with hover/active states, falling back to a GtkFileDialog-style click-to-browse trigger
✅ MultiSelectDropdown Checkbox-list variant of Dropdown for selecting multiple values from a single trigger — Dropdown/ComboRow are currently single-select only
✅ CodeBlock Static monospace code/config snippet display with optional line numbers and a CopyButton trailing action — distinct from TerminalView (live scrolling terminal output)
✅ WidgetManager Card that manages a controlled collection of widgets picked from a catalog, each rendering its own content via render(); edit-mode trigger opens a staged add/remove picker (Modal/BottomSheet/Drawer, configurable) applied only on confirm
✅ FieldGroup Generic form-field grouping with a shared label, help text, and error message, for arbitrary fields outside a BoxedList — PreferencesGroup is scoped specifically to wrapping settings rows
✅ Portal Reusable createPortal wrapper (mount target, SSR-safe no-op) — extracts the ad-hoc portal logic currently duplicated independently across 13 components (Dialog, Modal, Popover, Tooltip, BottomSheet, etc.)
✅ CoachMark Onboarding feature-discovery pattern: spotlights a target element and anchors a callout bubble (title, description, actions) beside it, with a CoachMarkTour orchestrator for sequential steps (Next/Back/Skip/Done + "X of N"). Pragmatic web-app pattern beyond GNOME HIG, built on Button/Portal and a viewport-aware flip positioner

Infrastructure

Status Item Description
✅ @gnome-ui/hooks tree-shaking sideEffects: false + vite-magic-tree-shaking per-hook entries; consumers importing a single hook no longer receive the full bundle — issue #76