Design system
Foundations
Color, status tones, type, sizes, glass, icons and motion: the values every Ostorya app uses.
Color#
Ostorya has one brand color, and it isn't fixed: the accent follows the merchant's wallpaper or their pick in Desktop & appearance. Use var(--o-accent) for primary buttons, switches and selected states, andvar(--o-accent-ink) for accent text, icons, borders and rings (on a dark wallpaper the accent can be near black; in dark mode --o-accent-ink lifts it so it stays readable). Never write the default sage #627a66 in your code.
No bright palette colors (a pure green, red or blue) and no third-party brand colors: they break the calm of the desktop and clash with the merchant's accent.
Status tones
Statuses use five soft tones, each with a light and a dark value. They are fixed: never the accent, so an order reads the same on every desktop. Grey means "not yet" or "over", never success; red only means failed or destructive.
<div class="o-card o-row-gap">
<span class="o-badge"><i data-lucide="clock"></i>To confirm</span>
<span class="o-badge o-good"><i data-lucide="circle-check"></i>Delivered</span>
<span class="o-badge o-info"><i data-lucide="truck"></i>Shipped</span>
<span class="o-badge o-warn"><i data-lucide="phone-missed"></i>No answer</span>
<span class="o-badge o-bad"><i data-lucide="undo"></i>Returned</span>
</div>| Tone | Class | Use for |
|---|---|---|
| Neutral (grey) | o-badge | Pending, nothing yet, over: To confirm, Draft, Archived, Expired, Paused. |
| Good (sage) | o-good | Done or live: Confirmed, Delivered, Paid, Active. |
| Info (slate) | o-info | On its way or later: Shipped, Out for delivery, Call back, Scheduled. |
| Warn (sand) | o-warn | Needs attention: No answer, Attempted, Needs cost. |
| Bad (clay) | o-bad | Failed: Refused, Returned, Lost, Cancelled. Solid clay for a delete button. |
Typography#
Inter, with Western digits in every language. Ostorya's sizes are small and precise: most of the desktop is 13 and 14px.
| Size | Weight | Use for |
|---|---|---|
| 22px | 600 | Page title inside a card, large numbers |
| 18px | 600 | Section title, empty state title |
| 20px | 600 | KPI value (tabular figures) |
| 14px | 400 | Body text, inputs, card titles (600) |
| 13.5px | 500–600 | Buttons, segmented controls, menu items |
| 13px | 500 | Labels, compact buttons, secondary text |
| 12–12.5px | 500 | Badges, help text, captions |
| 12px uppercase | 600, +0.04em | KPI labels only |
Sizes and spacing#
Spacing follows a 4px grid: 4, 6, 8, 12, 16, 20, 24. Cards are 16px apart; content is 20px from a card's edge.
| Element | Size |
|---|---|
| Button, field, select | 40px high (36px compact, 32px icon button in a row) |
| Segmented control | 36px buttons in a 4px padded track |
| Switch | 40 × 24px |
| Checkbox, radio | 18px |
| List row | 48px, one line, 36px thumbnail |
| Card | radius 22px, padding 20px |
| Field, inset panel | radius 12px / 16px |
| Menu, popover | radius 14px, items 36px |
| Buttons, badges, tabs | fully rounded (pill) |
| Icons | 16px in buttons and rows, 12px in badges, 26px in empty states |
Glass#
Glass is Ostorya's signature: a light top-to-bottom gradient, a 24px blur with saturation, a hairline border and a soft long shadow. The kit's o-card is exactly the Store's card.
Glass card
The merchant's wallpaper shows through, blurred.
With an inset
<div class="o-grid o-grid-2">
<div class="o-card">
<h3 class="o-h3">Glass card</h3>
<p class="o-muted" style="margin:0">The merchant's wallpaper shows through, blurred.</p>
</div>
<div class="o-card">
<h3 class="o-h3">With an inset</h3>
<div class="o-inset">Grouped content uses a soft inset, not another card.</div>
</div>
</div>- Keep
htmlandbodytransparent (o-appdoes it). Never paint a full-page background. - Don't put a card inside a card: group with
o-insetinstead. - Never put
filteroropacitybelow 1 on an element withbackdrop-filter: the blur disappears. - Reduce glass (a desktop setting, or the OS's reduced transparency): the kit makes cards solid when
reduce-glassis onhtml. Test your app with it on. - Menus, popovers and modals are nearly opaque (95%), so text stays readable over anything.
Icons#
Ostorya uses Lucide, stroke 2, 16px next to text. Every button, tab, filter, menu item and nav link gets one. In React use lucide-react; in plain HTML write <i data-lucide="plus"></i> and calllucide.createIcons(). The examples in this guide use that form.
Motion#
- Short and physical: springs for things that move (a switch knob, the selected segment), 150ms fades for color.
- Cards fade up 10px when a page opens; nothing loops except loading skeletons.
- Respect
prefers-reduced-motion: the kit stops its own animations.
Window and dock#
- Your app fills a desktop window. Don't add a title bar or close button: use
bridge.setTitle(). - The dock floats over the bottom of every window: leave
var(--o-dock-room)(92px) free at the bottom of scrolling pages, and never place fixed buttons there. - Under 768px your app is full screen above the dock bar. Design for 360px wide.