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 view under src/views/ 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>Dropdown menus
<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.