Design System
Every Ordo surface — Ordo Operations, Ordo Portal, Ordo Partner, Ordo Control, the mobile
apps, this site and the technical presentation — runs on one design system. Its source is the
tfs-frontend/packages/ui-kit package; the token tables on this page are read at build
time from that package’s tokens.css, and the component examples are drawn with the same
custom properties. The page cannot fall behind the code: when a token changes, the table changes.
The platform’s technical codename is TFS; URLs (tfs-*.karyabilisim.com), package names
(@tfs/*), repository names and environment variables carry it. Everywhere a person reads,
the product is Ordo.
Principles
Section titled “Principles”- Hierarchy by type and space, not by colour or boxes. One accent per view.
- Flat surfaces, hairline rules. A 1px divider before a card; never a card inside a card.
- Dense but calm. 8px grid; section gaps 32/48px; page gutters 24px desktop, 16px phone.
- No gradients, no glows, no glassmorphism. The sticky header’s plain translucent surface is the one exception.
- Icons are 1.5px stroke, currentColor, 16/20px. No emoji; no icon-in-coloured-circle grids.
- Tables are the primary surface in operations. Sticky header, right-aligned tabular figures, mono identifiers, row hover on
--tfs-surface-hover, no zebra. - Forms reserve one helper line. Label 13px/500; control 32px; masks on money, phone, e-mail, TCKN, IBAN and date. Error text is
--tfs-danger-text; a red border alone is not enough. - The page-header pattern is the same on every page. Eyebrow → h1 (24px/600) → one-line description → actions on the right; breadcrumbs above when depth > 1.
- Empty, loading and error states are designed. Skeletons mirror the final layout.
- Nothing overflows at 390px. Tables scroll inside their container; the page never scrolls horizontally.
- Simulations and live previews are static-size.
scrollHeightand the container’sgetBoundingClientRect()are identical before and after a run. - Dark theme is first-class. Every token has a dark counterpart; every screen touched is checked in both themes.
- Contrast-paired tokens. Every solid surface has a
*-contrast, every soft surface a*-text(WCAG AA). Body text never sits on a coloured surface. - Regulatory notes live in
InfoPopoverandReferenceShelf. Inline notes only where approved (financial position).
Tokens
Section titled “Tokens”Colour — neutral (cool ink)
Section titled “Colour — neutral (cool ink)”Page ground gray-100, surfaces white (dark theme gray-950 / gray-900), borders gray-200.
- gray-50
- #f7f8fa
- gray-100
- #f0f2f5
- gray-200
- #e1e4ea
- gray-300
- #c9ced7
- gray-400
- #9aa2af
- gray-500
- #6d7585
- gray-600
- #525a69
- gray-700
- #3c4350
- gray-800
- #282d37
- gray-900
- #181c24
- gray-950
- #0e1116
Colour — primary (marine ink)
Section titled “Colour — primary (marine ink)”The primary solid is primary-800 in light and primary-500 in dark; links and the active item use --tfs-primary-text.
- primary-50
- #f1f5fa
- primary-100
- #dfe8f2
- primary-200
- #c0d2e6
- primary-300
- #94b1d2
- primary-400
- #6189b8
- primary-500
- #3d679c
- primary-600
- #2c5082
- primary-700
- #24416a
- primary-800
- #1c3454
- primary-900
- #152741
- primary-950
- #0c1829
Colour — semantic
Section titled “Colour — semantic”The left half of every swatch is the light value, the right half the dark value.
- bg
- #f0f2f5
- #0e1116
- surface
- #ffffff
- #181c24
- surface-raised
- #ffffff
- #282d37
- surface-sunken
- #f7f8fa
- #0b0e13
- surface-hover
- #f0f2f5
- #282d37
- surface-tint
- color-mix(in srgb, #1c3454 6%, #ffffff)
- color-mix(in srgb, #3d679c 12%, #181c24)
- border
- #e1e4ea
- #282d37
- border-strong
- #c9ced7
- #3c4350
- text
- #181c24
- #f0f2f5
- text-muted
- #525a69
- #9aa2af
- text-subtle
- #6d7585
- #9aa2af
- primary
- #1c3454
- #3d679c
- primary-hover
- #152741
- #6189b8
- primary-soft
- #f1f5fa
- rgb(97 137 184 / 0.16)
- primary-contrast
- #ffffff
- primary-text
- #24416a
- #94b1d2
- focus-ring
- #3d679c
- #94b1d2
- focus-ring-soft
- rgb(61 103 156 / 0.28)
- rgb(148 177 210 / 0.32)
- overlay
- rgb(14 17 22 / 0.55)
- rgb(8 10 14 / 0.72)
- tooltip-bg
- #181c24
- #f0f2f5
- tooltip-text
- #f7f8fa
- #181c24
Colour — accent (brass)
Section titled “Colour — accent (brass)”The one warm note in the system: the mark, one featured figure, the hairline under a section title, the “drawn” share of a cycle. Never a button fill, never a status.
- accent
- #a97f2c
- #c9a24f
- accent-contrast
- #231a06
- accent-soft
- #f7f0df
- rgb(201 162 79 / 0.16)
- accent-text
- #7a5a1a
- #e2c27a
Colour — status
Section titled “Colour — status”One notch below full saturation. Pairing rule: solid + -contrast, soft + -text. Amber never carries white text.
- danger
- #c0392f
- danger-hover
- #a22f27
- danger-soft
- #fbeeec
- rgb(192 57 47 / 0.16)
- danger-contrast
- #ffffff
- danger-text
- #a12d25
- #f2a39c
- success
- #1f7a4d
- success-soft
- #e9f5ee
- rgb(31 122 77 / 0.16)
- success-contrast
- #ffffff
- success-text
- #1b6b43
- #6fcf97
- warning
- #b8791f
- warning-soft
- #fbf3e3
- rgb(184 121 31 / 0.16)
- warning-contrast
- #1f1400
- warning-text
- #8a5a12
- #e8b45a
- info
- #2a6db3
- info-soft
- #eaf2fb
- rgb(42 109 179 / 0.16)
- info-contrast
- #ffffff
- info-text
- #245f9c
- #7fb3ea
Colour — chart palette
Section titled “Colour — chart palette”The order is fixed; consumers index into it through chartPalette. The first two are the house pair
(marine, brass); the cash-flow pair uses the same two for money in and out, so the status green and red stay free for state.
- chart-1
- #2c5082
- #7fa6d6
- chart-2
- #a97f2c
- #d6b36a
- chart-3
- #1f8a7a
- #5cc3b2
- chart-4
- #6b7fb3
- #a3b1dc
- chart-5
- #c0392f
- #ef8d84
- chart-6
- #7d4e8c
- #c493d2
- chart-7
- #3f8f4f
- #85c98f
- chart-8
- #b5536d
- #e295aa
- chart-flow-in
- #2c5082
- #6189b8
- chart-flow-out
- #a97f2c
- #c9a24f
- chart-grid
- #e1e4ea
- #282d37
- chart-axis
- #6d7585
- #9aa2af
- chart-cursor
- rgb(24 28 36 / 0.06)
- rgb(154 162 175 / 0.1)
Typography
Section titled “Typography”IBM Plex Sans for text and headings, IBM Plex Mono for identifiers and figures that must align.
On the site, in the documentation and in the presentation, display headings of 28px and above are
set in Newsreader (serif, variable); body stays Plex. Eyebrow labels are 11–12px, uppercase,
0.06em tracking, --tfs-text-subtle. Figures use tabular-nums.
| Token | Value | Sample | Use |
|---|---|---|---|
--tfs-text-muted | var(--tfs-gray-600) · var(--tfs-gray-600) | Pool balance ₺48,200,000.00 | |
--tfs-text-subtle | var(--tfs-gray-500) · var(--tfs-gray-500) | Pool balance ₺48,200,000.00 | |
--tfs-text-xs | 0.75rem · 12px | Pool balance ₺48,200,000.00 | Meta, badges, table header, helper line |
--tfs-text-sm | 0.8125rem · 13px | Pool balance ₺48,200,000.00 | Form label, table cell, button |
--tfs-text-md | 0.875rem · 14px | Pool balance ₺48,200,000.00 | Body text (apps) |
--tfs-text-lg | 1rem · 16px | Pool balance ₺48,200,000.00 | Dialog title |
--tfs-text-xl | 1.25rem · 20px | Pool balance ₺48,200,000.00 | Page title (h1, 600) |
--tfs-text-2xl | 1.5rem · 24px | Pool balance ₺48,200,000.00 | KPI value (tabular) |
--tfs-text-3xl | 1.875rem · 30px | Pool balance ₺48,200,000.00 | Dashboard title |
--tfs-text-4xl | 2.25rem · 36px | Pool balance ₺48,200,000.00 | Marketing/docs only — Newsreader |
--tfs-font-sans | "IBM Plex Sans Variable", "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif | ||
--tfs-font-mono | "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace | ||
--tfs-tracking-tight | -0.01em | ||
--tfs-tracking-tighter | -0.02em | ||
--tfs-tracking-wide | 0.06em | ||
--tfs-leading-tight | 1.2 | ||
--tfs-leading-normal | 1.5 | ||
Deliberately sharp. Rounded-everything reads as consumer software; a ledger needs to look exact, not friendly. Pill shapes only on the status dot and the avatar.
| Token | Value | Sample |
|---|---|---|
--tfs-radius-sm | 0.125rem · 2px | |
--tfs-radius-md | 0.25rem · 4px | |
--tfs-radius-lg | 0.375rem · 6px | |
--tfs-radius-xl | 0.5rem · 8px | |
--tfs-radius-2xl | 0.75rem · 12px | |
--tfs-radius-full | 9999px · 9999px |
Spacing and density
Section titled “Spacing and density”| Token | Value | Sample |
|---|---|---|
--tfs-space-1 | 0.25rem · 4px | |
--tfs-space-2 | 0.5rem · 8px | |
--tfs-space-3 | 0.75rem · 12px | |
--tfs-space-4 | 1rem · 16px | |
--tfs-space-5 | 1.25rem · 20px | |
--tfs-space-6 | 1.5rem · 24px | |
--tfs-space-8 | 2rem · 32px |
Density tightens under [data-density="compact"]; the preference persists.
| Token | comfortable | compact |
|---|---|---|
--tfs-row-py | 0.375rem | 0.1875rem |
--tfs-cell-px | 0.75rem | 0.5rem |
Shadows
Section titled “Shadows”xs is a hairline lift, sm a card, md a popover, lg a dialog. On brand surfaces, sm at most.
| Token | Value | Sample |
|---|---|---|
--tfs-shadow-xs | 0 1px 1px 0 rgb(24 28 36 / 0.04)Dark theme: 0 1px 1px 0 rgb(0 0 0 / 0.3) | |
--tfs-shadow-sm | 0 1px 2px 0 rgb(24 28 36 / 0.06)Dark theme: 0 1px 2px 0 rgb(0 0 0 / 0.4) | |
--tfs-shadow-md | 0 4px 12px -2px rgb(24 28 36 / 0.12)Dark theme: 0 4px 12px -2px rgb(0 0 0 / 0.5) | |
--tfs-shadow-lg | 0 12px 32px -8px rgb(24 28 36 / 0.24)Dark theme: 0 12px 32px -8px rgb(0 0 0 / 0.65) | |
--tfs-shadow-pinned | 4px 0 8px -4px rgb(24 28 36 / 0.12)Dark theme: 4px 0 8px -4px rgb(0 0 0 / 0.5) |
Motion
Section titled “Motion”Three durations, three curves: standard for state changes, emphasized for things entering the screen, exit for leaving. No bounce, no scale on hover, no parallax. Everything is 0.01ms under reduced motion.
| Token | Value |
|---|---|
--tfs-duration-fast | 120ms |
--tfs-duration-base | 200ms |
--tfs-duration-slow | 320ms |
--tfs-ease-standard | cubic-bezier(0.4, 0, 0.2, 1) |
--tfs-ease-emphasized | cubic-bezier(0.2, 0, 0, 1) |
--tfs-ease-exit | cubic-bezier(0.4, 0, 1, 1) |
--tfs-transition-fast | var(--tfs-duration-fast) var(--tfs-ease-standard) |
Component inventory
Section titled “Component inventory”Every example is a static drawing of the component under packages/ui-kit/src/components,
painted with the same tokens; it repaints on theme change. The status badge is dot + label and
never picks its own tone — it uses the mapping in the platform status registry
(active → success, pending-approval → warning, refused → danger).
Buttoncomponents/button.tsx
Form fieldcomponents/form-field.tsx · input.tsx · masked-input.tsx
Stored as an integer in minor units
IBAN failed the mod-97 check
Status badgecomponents/status-badge.tsx · badge.tsx
Stat cardcomponents/stat-card.tsx
Data tablecomponents/table.tsx
| Contract | Customer | Product | Amount | Saved | Status |
|---|---|---|---|---|---|
| SZL-2026-0418 | E. Kaya | KONUT-STD | ₺1,250,000.00 | 36.7% | Pending approval |
| SZL-2026-0102 | M. Demir | KONUT-STD | ₺1,500,000.00 | 41.0% | Active |
| SZL-2026-0423 | A. Yılmaz | ARAC-STD | ₺2,400,000.00 | 12.5% | Refused |
| SZL-2025-0310 | S. Çelik | KONUT-STD | ₺900,000.00 | 58.3% | Entitled |
Page headercomponents/page-header.tsx · breadcrumbs.tsx
SZL-2026-0418 · Standard Housing
Established by demo-admin · 22 Sep 2026 · rule set v3
Tabscomponents/tabs.tsx
Empty and error statescomponents/empty-state.tsx · error-state.tsx
Toastcomponents/toast.tsx
Info popover and tooltipcomponents/info-popover.tsx · tooltip.tsx · regulatory-note.tsx
Confirm dialogcomponents/confirm-dialog.tsx · dialog.tsx
Drawercomponents/drawer.tsx
Skeletoncomponents/skeleton.tsx
Progress and threshold gaugecomponents/progress.tsx · threshold-gauge.tsx
Steppercomponents/stepper.tsx · timeline.tsx
- 01Application
- 02Identity
- 03Contract
- 04Signature
Avatarcomponents/avatar.tsx
App shellcomponents/app-shell.tsx · command-palette.tsx
Layout patterns
Section titled “Layout patterns”Page pattern: breadcrumbs → section eyebrow → h1 → description → actions → KPI row → tableops-web · treasury
Allocable capacity
Period 2026-10 · rule set v3 · data as of 22 Sep 2026 09:40
| Rank | Contract | Saved | Need | Result |
|---|---|---|---|---|
| 1 | SZL-0181 | 60% | ₺260,000.00 | Granted |
| 2 | SZL-0101 | 52% | ₺720,000.00 | Granted |
| 3 | SZL-0148 | 41% | ₺708,000.00 | Capacity short |
Form pattern: two columns, 32px controls, every field reserves one helper line, actions bottom rightcomponents/form-field.tsx · FieldRow · FormActions
The organisation fee is read from the rule set: 7%
The table region scrolls inside its container and its header is sticky within it; the page never
scrolls horizontally. The KPI row is a StatCard grid: value text-2xl tabular, delta and hint text-xs.
White-label contract
Section titled “White-label contract”Brand differentiation lives only in tenant configuration; there is no code or asset fork, the same
build serves every tenant. Configuration goes through PUT /api/v1/tenant/branding
(see the Branding API):
| What a tenant changes | How |
|---|---|
| Logo | logo.type: "svg" inline mark (same-document url(#id) gradient references allowed; external URLs and script-bearing SVGs refused) or logo.type: "initials" |
| Primary triple | primary / primarySoft / accent — the --tfs-primary, --tfs-primary-soft, --tfs-primary-text and --tfs-accent derivatives are computed from it |
| Locale | defaultLocale, availableLocales |
| Support channels | supportEmail, supportPhone |
| What a tenant cannot change | Why |
|---|---|
| The neutral gray scale, status colours, radii, typography, spacing, motion | The platform’s own voice; independent of the brand triple |
| Component behaviour, density, accessibility pairings | Contrast pairs are solved at the token layer; a brand cannot break them |
| The status → tone mapping | A status colour is one platform-wide |
The default demo brand is Demo Tasarruf Finansman (Örnek Kurulum), short “Demo TF”, initial “D”. A demo name that resembles a licensed institution is never used.
Do and don’t
Section titled “Do and don’t”White or gray-50 ground, serif heading, one sentence.
No gradient band, no glow. Hierarchy comes from type and space.
Dot + label; the tone mapping comes from the status registry.
No free-coloured pill badges; a badge colour is never guessed.
- Pool balance₺48.200.000,00
- MADDE 30/15 %
Hairline rows; term on the left, description on the right.
No icon-in-coloured-circle grids. Icons are 1.5px stroke, 16/20px, currentColor.
Flat surface, 1px border, shadow-sm at most.
No card inside a card, no shadow on everything, no rounded-2xl.
One accent per view: a brass hairline or one featured figure.
Brass is never a button fill and never a status colour.
| SZL-2026-0418 | ₺1.250.000,00 |
| SZL-2026-0102 | ₺900.000,00 |
Table figures right-aligned, tabular-nums, identifiers in mono.
| SZL-2026-0418 | ₺1.250.000 |
| SZL-2026-0102 | ₺900.000 |
Figures are never left-aligned in proportional type; minor units are never dropped.
Mark and wordmark
Section titled “Mark and wordmark”The mark is a ring in marine ink with one brass quarter: the pool, and the share drawn from it.
On dark surfaces the ring may turn white (currentColor); the brass quarter always stays brass.
The wordmark is “Ordo” in the display face (Newsreader) at weight 500–600, −0.02em tracking.
No tagline inside the lockup other than the descriptor line: TR “Tasarruf Finansmanı Platformu”,
EN “Savings Finance Platform”, DE “Plattform für Sparfinanzierung”.
Surface names: Ordo Operations (ops-web), Ordo Portal (customer-web), Ordo Partner (partner-web), Ordo Control (control plane), Ordo (website).
Status visualisation rules
Section titled “Status visualisation rules”- Monetary values are always formatted from minor units (see Money and minor units); minor units are never dropped.
- Status badges never pick arbitrary colours; they use the tone mapping in the platform status registry.
- Empty and error states are never silent: every list component defines empty copy and a retry action.
- Every user-facing string ships as an en/tr/de triple; a missing translation fails the build via tests.
ui-kitdoes not depend oni18n: every string a component shows is injected by the caller.