Overview
Folio is the Paysio design library: the dashboard’s own components and the rules behind them, packaged so an app built with it reads as part of the dashboard rather than a page framed by it. A folio is a leaf of a ledger. Every screen your app draws is a page in the merchant’s book, and this is what makes it read as one.
Every page in this section renders the real component — the same code the dashboard runs — next to how to use it. Buttons, forms, tabs and menus, popups, the list view, feedback, the header bar, and the three things that are not controls at all: the marketing backgrounds, the card and bank brand marks, and the icon set.
Get it
The React starter ships every component as source in src/components, styled by Folio. It is what paysio apps init writes for a new app; paysio apps scaffold writes the same into an existing app folder.
paysio apps init my-app # new app: creates it and writes the starter
paysio apps scaffold # existing folder: writes the starter (--force to overwrite)Not on React? Link the stylesheet and use the classes directly. The bridge pushes the merchant’s live theme onto the same variables, so dark mode and white-label tints follow with no work of your own.
<link rel="stylesheet" href="https://cdn.paysio.com/folio-1.15.css">
<script src="https://cdn.paysio.com/paysio-bridge-1.12.js"></script>
<body class="folio-app">Versioned by URL: folio-1.css rolls forward within v1, folio-1.15.css pins this design. A pinned file is never changed once published; a changed design gets the next number.
Where your app lives
Three facts about the frame shape every screen, and they are the ones new apps get wrong first.
- You sit under a header you do not own. The dashboard draws a 44px bar above your frame: your app’s logo and name on the left as a crumb back to Apps, and an About action on the right. So a page never repeats the app name as its title and never draws a header of its own. You get slots in that bar — crumbs, actions, a menu, search, an immersive layout — through
paysio.titleBar(). Open with the section the merchant came for. - You fill the island. Your frame fills the dashboard’s content island edge to edge, on the island’s own surface (
--card). No outer border, no page background, no card around the whole page: the dashboard already framed you. No padding of your own either: a list runs edge to edge (the bar and rows carry their 12px), and a section brings its own 24px sides. Fill the island top to bottom, and start. - The merchant is already signed in, on the sandbox or live side they chose. No login, no landing, no onboarding carousel. The first screen is the work, and a mode change refetches in place rather than reloading.
The design language
| Rule | What it means |
|---|---|
| Rhythm | A 4px grid: 4, 8, 12, 16, 20, 24. Sections sit 24px apart with 12px inside. No title row: the dashboard’s bar above your app is the page’s header (crumbs, view tabs, actions), so a page starts with its content. No cards: content sits directly on the island; a list is a bar and rows, a section is a 13px title and what follows, and sections are separated by space, never boxed. |
| Type | Inter. 13px medium is the working density (rows, chrome, section titles); 14px is copy and controls; 16px semibold with -0.04em tracking is titles and actions; 11px is badges. Headings always tighten. |
| Shape | Pills for buttons, 12px for fields and standalone surfaces, 8px for rows inside a 12px surface, 24px for popups. Buttons inside a popup square to 12px. There is no outlined button: secondary is a filled chip. |
| Edges and depth | One hairline (--border) does the work; never stack a border on a tinted fill. Three shadows only: resting card, floating menu, modal popup. Everything else is flat. |
| Lists | Records are rows, not tables: a bar of preset pills and filter chips, then transparent 44px rows with a hover band and no column header. Empty states are a narrow left-aligned column, never a dashed box. |
| Motion and loading | 150ms for state, press feedback by scale, 200ms on the iOS curve for popups while the page recedes 3%. Loading is a shimmer sweeping a static bar: never a block pulsing, never a spinner. |
| Popups | Ask the dashboard to render them (paysio.popup.open) so the scrim and the recede cover the whole page. Confirmations stack two full-width actions, cancel first, with one-word labels. |
| Words | Sentence case. Say what happened, not what the system did. Errors say what to do next. No exclamation marks, no “successfully”. |