Skip to content

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.

  1. Hierarchy by type and space, not by colour or boxes. One accent per view.
  2. Flat surfaces, hairline rules. A 1px divider before a card; never a card inside a card.
  3. Dense but calm. 8px grid; section gaps 32/48px; page gutters 24px desktop, 16px phone.
  4. No gradients, no glows, no glassmorphism. The sticky header’s plain translucent surface is the one exception.
  5. Icons are 1.5px stroke, currentColor, 16/20px. No emoji; no icon-in-coloured-circle grids.
  6. Tables are the primary surface in operations. Sticky header, right-aligned tabular figures, mono identifiers, row hover on --tfs-surface-hover, no zebra.
  7. 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.
  8. 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.
  9. Empty, loading and error states are designed. Skeletons mirror the final layout.
  10. Nothing overflows at 390px. Tables scroll inside their container; the page never scrolls horizontally.
  11. Simulations and live previews are static-size. scrollHeight and the container’s getBoundingClientRect() are identical before and after a run.
  12. Dark theme is first-class. Every token has a dark counterpart; every screen touched is checked in both themes.
  13. Contrast-paired tokens. Every solid surface has a *-contrast, every soft surface a *-text (WCAG AA). Body text never sits on a coloured surface.
  14. Regulatory notes live in InfoPopover and ReferenceShelf. Inline notes only where approved (financial position).

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

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

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

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

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

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)

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.

TokenValueSampleUse
--tfs-text-mutedvar(--tfs-gray-600) · var(--tfs-gray-600)Pool balance ₺48,200,000.00
--tfs-text-subtlevar(--tfs-gray-500) · var(--tfs-gray-500)Pool balance ₺48,200,000.00
--tfs-text-xs0.75rem · 12pxPool balance ₺48,200,000.00Meta, badges, table header, helper line
--tfs-text-sm0.8125rem · 13pxPool balance ₺48,200,000.00Form label, table cell, button
--tfs-text-md0.875rem · 14pxPool balance ₺48,200,000.00Body text (apps)
--tfs-text-lg1rem · 16pxPool balance ₺48,200,000.00Dialog title
--tfs-text-xl1.25rem · 20pxPool balance ₺48,200,000.00Page title (h1, 600)
--tfs-text-2xl1.5rem · 24pxPool balance ₺48,200,000.00KPI value (tabular)
--tfs-text-3xl1.875rem · 30pxPool balance ₺48,200,000.00Dashboard title
--tfs-text-4xl2.25rem · 36pxPool balance ₺48,200,000.00Marketing/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-wide0.06em
--tfs-leading-tight1.2
--tfs-leading-normal1.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.

TokenValueSample
--tfs-radius-sm0.125rem · 2px
--tfs-radius-md0.25rem · 4px
--tfs-radius-lg0.375rem · 6px
--tfs-radius-xl0.5rem · 8px
--tfs-radius-2xl0.75rem · 12px
--tfs-radius-full9999px · 9999px
TokenValueSample
--tfs-space-10.25rem · 4px
--tfs-space-20.5rem · 8px
--tfs-space-30.75rem · 12px
--tfs-space-41rem · 16px
--tfs-space-51.25rem · 20px
--tfs-space-61.5rem · 24px
--tfs-space-82rem · 32px

Density tightens under [data-density="compact"]; the preference persists.

Tokencomfortablecompact
--tfs-row-py0.375rem0.1875rem
--tfs-cell-px0.75rem0.5rem

xs is a hairline lift, sm a card, md a popover, lg a dialog. On brand surfaces, sm at most.

TokenValueSample
--tfs-shadow-xs0 1px 1px 0 rgb(24 28 36 / 0.04)
Dark theme: 0 1px 1px 0 rgb(0 0 0 / 0.3)
--tfs-shadow-sm0 1px 2px 0 rgb(24 28 36 / 0.06)
Dark theme: 0 1px 2px 0 rgb(0 0 0 / 0.4)
--tfs-shadow-md0 4px 12px -2px rgb(24 28 36 / 0.12)
Dark theme: 0 4px 12px -2px rgb(0 0 0 / 0.5)
--tfs-shadow-lg0 12px 32px -8px rgb(24 28 36 / 0.24)
Dark theme: 0 12px 32px -8px rgb(0 0 0 / 0.65)
--tfs-shadow-pinned4px 0 8px -4px rgb(24 28 36 / 0.12)
Dark theme: 4px 0 8px -4px rgb(0 0 0 / 0.5)

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.

TokenValue
--tfs-duration-fast120ms
--tfs-duration-base200ms
--tfs-duration-slow320ms
--tfs-ease-standardcubic-bezier(0.4, 0, 0.2, 1)
--tfs-ease-emphasizedcubic-bezier(0.2, 0, 0, 1)
--tfs-ease-exitcubic-bezier(0.4, 0, 1, 1)
--tfs-transition-fastvar(--tfs-duration-fast) var(--tfs-ease-standard)

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

PrimarySecondaryGhostDangerDisabledSmallLarge

Form fieldcomponents/form-field.tsx · input.tsx · masked-input.tsx

Financing amount*

Stored as an integer in minor units

Term (months)

 

Product

 

IBAN

IBAN failed the mod-97 check

 

Status badgecomponents/status-badge.tsx · badge.tsx

DraftPending approvalActiveSuspendedRefusedInfoEntitled

Stat cardcomponents/stat-card.tsx

Pool balance₺48,200,000.00+2.4%vs previous period
Open tasks14+3today
Amount in arrears₺1,250,000.00−0.8%this month

Data tablecomponents/table.tsx

ContractCustomerProductAmountSavedStatus
SZL-2026-0418E. KayaKONUT-STD₺1,250,000.0036.7%Pending approval
SZL-2026-0102M. DemirKONUT-STD₺1,500,000.0041.0%Active
SZL-2026-0423A. YılmazARAC-STD₺2,400,000.0012.5%Refused
SZL-2025-0310S. ÇelikKONUT-STD₺900,000.0058.3%Entitled

Page headercomponents/page-header.tsx · breadcrumbs.tsx

  1. Contracts
  2. SZL-2026-0418
Contract

SZL-2026-0418 · Standard Housing

Established by demo-admin · 22 Sep 2026 · rule set v3

ExportApprove

Tabscomponents/tabs.tsx

SummaryPayment planEventsDocuments

Empty and error statescomponents/empty-state.tsx · error-state.tsx

No suspended payments yetUnmatched transfers are listed here and land in the task inbox.Record a payment
The list could not be loadedReport traceId 8f4a2c1e-9b3d to the support team.Retry

Toastcomponents/toast.tsx

Contract approved
SZL-2026-0418 · demo-approver · 09:41
Approval refused
Same user cannot approve — MSG-S-21

Info popover and tooltipcomponents/info-popover.tsx · tooltip.tsx · regulatory-note.tsx

MADDE 21/240% of the contract amount must be saved and 40% of the term elapsed before allocation.
Four-eyes: the approver may not be the makeri
MADDE 30/1 · The reserve fund is 5% of the pool; capacity is derived from pool balance × coverage ratio.

Confirm dialogcomponents/confirm-dialog.tsx · dialog.tsx

Run the draw
This period’s draw is single-use. The participant list will be locked and the seed recorded.
CancelRun

Drawercomponents/drawer.tsx

Delivery file TSL-2026-0077Valuation ✓ · Insurance ✓ · Collateral — pending
ApproveCancel

Skeletoncomponents/skeleton.tsx

Progress and threshold gaugecomponents/progress.tsx · threshold-gauge.tsx

Saved 36.7% · threshold 40%

Steppercomponents/stepper.tsx · timeline.tsx

  1. 01Application
  2. 02Identity
  3. 03Contract
  4. 04Signature

Avatarcomponents/avatar.tsx

DAEKD

App shellcomponents/app-shell.tsx · command-palette.tsx

⌘Kdemo-admin · ops-admin
Contract
SZL-2026-0418 · Standard Housing
Pool balance₺48,200,000.00
Open tasks14

Page pattern: breadcrumbs → section eyebrow → h1 → description → actions → KPI row → tableops-web · treasury

  1. Treasury
  2. Capacity
Treasury

Allocable capacity

Period 2026-10 · rule set v3 · data as of 22 Sep 2026 09:40

ExportEvaluate period
Pool balance₺48,200,000.00
Reserve fund · 5%₺2,410,000.00
Coverage ratio80%
Allocable₺38,560,000.00
RankContractSavedNeedResult
1SZL-018160%₺260,000.00Granted
2SZL-010152%₺720,000.00Granted
3SZL-014841%₺708,000.00Capacity short

Form pattern: two columns, 32px controls, every field reserves one helper line, actions bottom rightcomponents/form-field.tsx · FieldRow · FormActions

Customer*

 

Product*

 

Amount*

The organisation fee is read from the rule set: 7%

Term (months)

 

CancelSave

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.

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 · Ground
Ordo
Pool balance
Verified on every event.

White or gray-50 ground, serif heading, one sentence.

Don’t · Ground
Pool balance
Verified on every event.

No gradient band, no glow. Hierarchy comes from type and space.

Do · Status
ActivePending approval

Dot + label; the tone mapping comes from the status registry.

Don’t · Status
ActivePending approval

No free-coloured pill badges; a badge colour is never guessed.

Do · List
  • Pool balance₺48.200.000,00
  • MADDE 30/15 %

Hairline rows; term on the left, description on the right.

Don’t · List
●▲■

No icon-in-coloured-circle grids. Icons are 1.5px stroke, 16/20px, currentColor.

Do · Surface
Pool balance
₺48.200.000,00

Flat surface, 1px border, shadow-sm at most.

Don’t · Surface
Pool balance
₺48.200.000,00

No card inside a card, no shadow on everything, no rounded-2xl.

Do · Accent
Pool balance
OK—

One accent per view: a brass hairline or one featured figure.

Don’t · Accent
OKActive

Brass is never a button fill and never a status colour.

Do · Figures
SZL-2026-0418₺1.250.000,00
SZL-2026-0102₺900.000,00

Table figures right-aligned, tabular-nums, identifiers in mono.

Don’t · Figures
SZL-2026-0418₺1.250.000
SZL-2026-0102₺900.000

Figures are never left-aligned in proportional type; minor units are never dropped.

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”.

OrdoSavings Finance PlatformLight ground — marine ring, brass quarter
OrdoSavings Finance PlatformDark ground — white ring, the quarter stays brass
OrdoSavings Finance PlatformMarine ground — white ring
Clear space: the ring’s stroke width on every side (x = 11% of the mark height)
Minimum size: 16px (favicon), 24px in UI, 6mm in print
OrdoSavings Finance PlatformLockup: mark + “Ordo” (Newsreader 600, −0.02em) + the descriptor line
✕ Never inside a gradient square
✕ Never recoloured
✕ Never rotated
✕ Never below the minimum size
✕ Never with a status dot or badge attached

Surface names: Ordo Operations (ops-web), Ordo Portal (customer-web), Ordo Partner (partner-web), Ordo Control (control plane), Ordo (website).

  • 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-kit does not depend on i18n: every string a component shows is injected by the caller.