Feedback
Every component, with its variants, sizes and states. Change the appearance here and every sample follows.
Alert
A message that belongs to the screen and stays until the situation changes.
Tones
<div class="or-alert or-alert--warning" role="status">
<svg class="or-i or-i-18" aria-hidden="true"><use href="#or-i-triangle-alert"></use></svg>
<div>Three products are out of stock.</div>
</div>
<div className="or-alert or-alert--warning" role="status">
<Icon name="triangle-alert" size={18} />
<div>Three products are out of stock.</div>
</div>
<div class="or-alert or-alert--warning" role="status">
<Icon name="triangle-alert" :size="18" />
<div>Three products are out of stock.</div>
</div>
<div className="or-alert or-alert--warning" role="status">
<Icon name="triangle-alert" size={18} />
<div>Three products are out of stock.</div>
</div>
<div class="or-alert or-alert--warning" role="status">
<Icon name="triangle-alert" :size="18" />
<div>Three products are out of stock.</div>
</div>Toast
The result of something the reader just did. It leaves on its own.
Tones
<script>
window.Orion.toast('Saved', 'success')
</script>
const push = useToasts((state) => state.push)
push(t('form.saved'), 'success')
const toasts = useToasts()
toasts.push(t('form.saved'), 'success')
// Server actions answer in place: the island shows the result inline
// rather than in a toast, so it is still there after a redirect.
<p className="or-alert or-alert--success" role="status">{t('form.saved')}</p>
const toasts = useToasts()
toasts.push(t('form.saved'), 'success')Skeleton
The shape of what is loading, so the page can be read before the numbers land.
Variants
<span class="or-skel or-skel--value"></span>
<span class="or-skel or-skel--text" style="inline-size: 40%"></span>
<Skeleton variant="value" />
<Skeleton width="40%" />
<Skeleton variant="value" />
<Skeleton width="40%" />
<Skeleton variant="value" />
<Skeleton width="40%" />
<Skeleton variant="value" />
<Skeleton width="40%" />Empty state
Says why there is nothing here and offers the way out.
Used in the demo at /orders
Variants
Nothing matches those filters
Try a different search, or clear the filters to see everything again.
<div class="or-empty">
<span class="or-empty__mark">
<svg class="or-i or-i-24" aria-hidden="true"><use href="#or-i-search"></use></svg>
</span>
<h3>Nothing matches those filters</h3>
<p class="or-muted">Try a different search.</p>
</div>
<EmptyState icon="search" title={t('table.noResults')} body={t('table.noResultsBody')}>
<button type="button" className="or-btn" onClick={table.clearFilters}>{t('table.clearFilters')}</button>
</EmptyState>
<EmptyState icon="search" :title="t('table.noResults')" :body="t('table.noResultsBody')">
<button type="button" class="or-btn" @click="table.clearFilters">{{ t('table.clearFilters') }}</button>
</EmptyState>
<EmptyState icon="search" title={t('table.noResults')} body={t('table.noResultsBody')}>
<button type="button" className="or-btn" onClick={table.clearFilters}>{t('table.clearFilters')}</button>
</EmptyState>
<EmptyState icon="search" :title="t('table.noResults')" :body="t('table.noResultsBody')">
<button type="button" class="or-btn" @click="table.clearFilters">{{ t('table.clearFilters') }}</button>
</EmptyState>Form bar
What is unsaved and the two things to do about it, stuck to the bottom of the form.
Used in the demo at /settings
States
<div class="or-formbar">
<p class="or-formbar__state" role="status" data-or-formstate></p>
<span class="or-push"></span>
<button type="button" class="or-btn or-btn--ghost" data-or-discard disabled>Discard changes</button>
<button type="submit" class="or-btn or-btn--primary" data-or-save disabled>Save changes</button>
</div>
<FormBar
dirty={form.dirty}
saving={save.isPending}
errors={form.errorCount}
onSave={() => form.submit(() => save.mutate())}
onDiscard={form.discard}
/>
<FormBar
:dirty="form.dirty.value"
:saving="save.isPending.value"
:errors="form.errorCount.value"
@save="form.submit(() => save.mutate())"
@discard="form.discard()"
/>
<FormBar
dirty={form.dirty}
saving={save.isPending}
errors={form.errorCount}
onSave={() => form.submit(() => save.mutate())}
onDiscard={form.discard}
/>
<FormBar
:dirty="form.dirty.value"
:saving="save.isPending.value"
:errors="form.errorCount.value"
@save="form.submit(() => save.mutate())"
@discard="form.discard()"
/>Confirm dialog
Stands between the reader and losing their work. Escape is the safe answer.
Variants
Unsaved changes
Leaving this screen now loses them.
<div class="or-modal">
<div class="or-dialog" role="alertdialog" aria-modal="true" aria-labelledby="leave-title">
<h3 id="leave-title">Unsaved changes</h3>
<p>Leaving this screen now loses them.</p>
<div class="or-dialog__actions">
<button type="button" class="or-btn">Stay here</button>
<button type="button" class="or-btn or-btn--danger">Leave anyway</button>
</div>
</div>
</div>
<ConfirmDialog
open={guard.pending !== null}
title={t('form.unsaved')}
body={t('form.unsavedBody')}
confirmLabel={t('form.leave')}
onConfirm={guard.leave}
onCancel={guard.stay}
/>
<ConfirmDialog
:open="guard.pending.value !== null"
:title="t('form.unsaved')"
:body="t('form.unsavedBody')"
:confirm-label="t('form.leave')"
@confirm="guard.leave"
@cancel="guard.stay"
/>
<ConfirmDialog
open={guard.pending !== null}
title={t('form.unsaved')}
body={t('form.unsavedBody')}
confirmLabel={t('form.leave')}
onConfirm={guard.leave}
onCancel={guard.stay}
/>
<FormConfirmDialog
:open="guard.pending.value !== null"
:title="t('form.unsaved')"
:body="t('form.unsavedBody')"
:confirm-label="t('form.leave')"
@confirm="guard.leave"
@cancel="guard.stay"
/>