Skip to content
Orbix React · Bootstrap 5 v1.0.3

Using components

Orbix components are plain JSX with fd-* classes (BEM naming) and, for interactive ones, a data-fd-* attribute that the bundled behaviors pick up automatically when the page renders (usePageScript()): there is nothing else to initialize. The UI, Forms and Tables pages of the template show every component; open the page component under src/pages/ to copy its markup. Reusable blocks are typed React components in src/components/ (<ProductCard>, <MailRow>, <Password>, <Otp>, <Example>...).

In the snippets below, replace the translation keys with your own (see Languages).

Buttons

<button type="button" className="fd-btn fd-btn--primary"><span className="fd-btn__label">{t('common.actions.save')}</span></button>
<button type="button" className="fd-btn fd-btn--secondary"><span className="fd-btn__label">{t('common.actions.cancel')}</span></button>
<button type="button" className="fd-btn fd-btn--ghost fd-btn--sm"><span className="fd-btn__label">{t('common.actions.preview')}</span></button>
<button type="button" className="fd-btn fd-btn--danger"><span className="fd-btn__label">{t('common.actions.delete')}</span></button>
<button type="button" className="fd-btn fd-btn--ghost fd-btn--icon" aria-label={t('shell.settings')}>
  <svg className="fd-icon" aria-hidden="true"><use href="#lc-settings" /></svg>
</button>

Cards

<article className="fd-card" aria-labelledby="card-title">
  <header className="fd-card__header">
    <div className="fd-card__heading">
      <h2 className="fd-card__title" id="card-title">{t('reports.revenue.title')}</h2>
      <p className="fd-card__subtitle">{t('reports.revenue.period')}</p>
    </div>
  </header>
  <div className="fd-card__body"><p className="fd-text-muted">{t('reports.revenue.text')}</p></div>
  <footer className="fd-card__footer">
    <button type="button" className="fd-btn fd-btn--primary fd-btn--sm"><span className="fd-btn__label">{t('reports.revenue.open')}</span></button>
  </footer>
</article>

Stat (KPI) tiles

fmt() formats the value for the active language (number, currency, percent...):

<div className="fd-kpis">
  <section className="fd-stat" aria-labelledby="kpi-orders">
    <div className="fd-stat__header">
      <h2 className="fd-stat__label" id="kpi-orders">{t('nav.orders')}</h2>
      <span className="fd-stat__icon" aria-hidden="true"><svg className="fd-icon"><use href="#lc-shopping-bag" /></svg></span>
    </div>
    <p className="fd-stat__value">{fmt('currency', 48250, { digits: 0 })}</p>
  </section>
</div>

Alerts and badges

<div className="fd-alert fd-alert--success" role="status">
  <svg className="fd-icon fd-alert__icon" aria-hidden="true"><use href="#fd-i-success" /></svg>
  <div className="fd-alert__content">
    <p className="fd-alert__title">{t('billing.paid_title')}</p>
    <p>{t('billing.paid_text')}</p>
  </div>
</div>

<span className="fd-badge fd-badge--success">{t('common.status.paid')}</span>
<span className="fd-badge fd-badge--warning">{t('common.status.pending')}</span>
<span className="fd-badge fd-badge--danger">{t('common.status.overdue')}</span>

Form fields

<div className="fd-field">
  <label className="fd-field__label" htmlFor="email">{t('common.fields.email')}</label>
  <input className="fd-input" id="email" type="email" autoComplete="email" />
</div>
<div className="fd-field">
  <label className="fd-field__label" htmlFor="website">{t('common.fields.website')}</label>
  <div className="fd-input-wrap">
    <svg className="fd-icon fd-input-wrap__icon" aria-hidden="true"><use href="#lc-globe" /></svg>
    <input className="fd-input" id="website" type="url" />
  </div>
</div>

The demo forms are uncontrolled (defaultValue); in your application use controlled inputs or a form library as you prefer – the markup and classes stay the same. The password field with a visibility toggle and the one-time code input are components: <Password id="new-password" meter="true" /> and <Otp /> (src/components/forms/).

Tables

<div className="fd-table-wrap" tabIndex={0} role="region" aria-label={t('nav.invoices')}>
  <table className="fd-table">
    <thead><tr><th scope="col">{t('common.fields.invoice')}</th><th scope="col">{t('common.fields.customer')}</th><th scope="col">{t('common.fields.status')}</th></tr></thead>
    <tbody>
      {invoices.map((invoice) => (
        <tr key={invoice.id}><td>{invoice.number}</td><td>{invoice.customer}</td><td><span className="fd-badge fd-badge--success">{t('common.status.paid')}</span></td></tr>
      ))}
    </tbody>
  </table>
</div>

The Tables → Data table page shows the data grid with sorting, search, column filters, selection, export and pagination (src/scripts/tables.ts). In your own pages use the useDataGrid hook:

const ref = useDataGrid<HTMLDivElement, Invoice>({ rows: invoices, rowId: (r) => r.id, columns: [/* key, label, sortable, render */] });
return <div className="fd-datatable" ref={ref}>{/* toolbar, table and footer markup as on the Data table page */}</div>;

Modals

<button type="button" className="fd-btn fd-btn--primary" data-fd-modal-open="modal-invite" aria-haspopup="dialog">
  <span className="fd-btn__label">{t('pages.team.invite')}</span>
</button>

<dialog className="fd-modal fd-modal--sm" id="modal-invite" aria-labelledby="modal-invite-title" data-fd-modal="">
  <div className="fd-modal__header">
    <div className="fd-modal__heading"><h2 className="fd-modal__title" id="modal-invite-title">{t('pages.team.invite')}</h2></div>
  </div>
  <div className="fd-modal__body"><p>{t('pages.team.invite_text')}</p></div>
  <div className="fd-modal__footer">
    <button type="button" className="fd-btn fd-btn--secondary" data-fd-modal-close=""><span className="fd-btn__label">{t('common.close')}</span></button>
  </div>
</dialog>

From code: openModal(dialog) and closeModal(dialog) from @foundry/ui; confirmations with await confirmDialog({ title, body, confirmLabel, cancelLabel, tone: 'danger' }).

Tabs

<div className="fd-tabs" data-fd-tabs="">
  <div className="fd-tabs__list" role="tablist" aria-label={t('nav.settings')}>
    <button type="button" className="fd-tabs__tab" role="tab" id="tab-general" aria-controls="panel-general" aria-selected="true">{t('settings.general')}</button>
    <button type="button" className="fd-tabs__tab" role="tab" id="tab-billing" aria-controls="panel-billing" aria-selected="false" tabIndex={-1}>{t('nav.billing')}</button>
  </div>
  <div className="fd-tabs__panel" role="tabpanel" id="panel-general" aria-labelledby="tab-general" tabIndex={0}>…</div>
  <div className="fd-tabs__panel" role="tabpanel" id="panel-billing" aria-labelledby="tab-billing" tabIndex={0} hidden>…</div>
</div>
<div className="fd-dropdown" data-fd-dropdown="">
  <button type="button" className="fd-btn fd-btn--secondary" aria-haspopup="menu" aria-expanded="false" aria-controls="menu-actions">
    <span className="fd-btn__label">{t('common.actions.actions')}</span>
  </button>
  <div className="fd-menu" role="menu" id="menu-actions" hidden>
    <button type="button" className="fd-menu__item" role="menuitem" tabIndex={-1}><span className="fd-menu__label-text">{t('common.actions.edit')}</span></button>
    <hr className="fd-menu__separator" />
    <button type="button" className="fd-menu__item fd-menu__item--danger" role="menuitem" tabIndex={-1}><span className="fd-menu__label-text">{t('common.actions.delete')}</span></button>
  </div>
</div>

The menu dispatches an fd:select event with the chosen item: listen to it in an effect, or use regular onClick handlers on the items.

Toasts

import { toast } from '@foundry/ui';

<button type="button" className="fd-btn fd-btn--secondary" onClick={() => toast({ title: t('account.saved'), variant: 'success', closeLabel: t('common.dismiss') })}>
  <span className="fd-btn__label">{t('common.actions.save')}</span>
</button>

Grid

Responsive 12-column grid with logical gaps:

<div className="fd-row">
  <div className="fd-col-12 fd-col-md-6 fd-col-xl-4">…</div>
  <div className="fd-col-12 fd-col-md-6 fd-col-xl-8">…</div>
</div>

Accessibility

Components ship with the right roles and keyboard support (menus, tabs, modals, comboboxes, date pickers). Keep the aria-* attributes from the examples, give every icon-only button an aria-label, and use aria-hidden="true" on decorative icons.