Tanqory design system
Design system
Every Tanqory website is built from one set of tokens and components: tanqory.com, the help center, the legal center and these developer docs. A page never picks a colour, a size or a font of its own; it asks for a role, and the role answers for light and dark, for every width from a phone to a wide screen, and for each of the 26 writing systems our 197 locales use.
Start with the foundations to learn the rules, use the components to build, and take the tokens as JSON or CSS when you need the raw values.
On this page
Seven rules every page keeps
These rules are checked in code, not left to taste. A page that breaks one fails its build.
Black, white and one grey for text
Text is black or white at full strength, with one secondary level at 60 percent for captions and meta. There is no third readable grey, so contrast never drifts.
Flat surfaces, outlines only where you type
Cards and tiles sit on a neutral grey ramp without a border; depth comes from the surface step and three shadow heights. Only a field or a select carries a hairline, so it reads as a place to type.
Three kinds of action
A solid pill for the main action, a tinted pill for the second, and a text link with an arrow for the rest. Nothing else looks clickable.
Light, dark and inverse
Light is the base and the system setting decides by default. A dark band on a light page is inverse, so a theme switch repaints it with the rest.
Motion for state, not decoration
No entrance fades or scroll reveals. Movement shows a change of state or a product at work, and stops when the reader asks for reduced motion.
A face for every script
Geist sets Latin, Cyrillic and Greek. Every other script gets its own face, downloaded only when a page shows it, and only the ranges it needs.
Right to left without overrides
Spacing and placement use logical properties (start and end, not left and right), so Arabic, Hebrew and Urdu pages mirror with no extra rules.
Foundations
The rules behind every token: what each value is for, when to use it and what to use instead.
- ColourPick a colour by the job it does. 80 roles, each with a light and a dark value.
- TypographyOne sans family, a type scale that grows with the screen, and medium headings in sentence case.
- SpacingA 2 to 128 px scale on a 4 px rhythm, plus section, stack and gutter steps that grow with the viewport.
- Shape and depthEight corner radii from 4 px to round, and three shadow heights for parts that rise.
- MotionThree easings and four durations, and the rules for when anything moves at all.
- ThemesLight, dark and inverse scopes, islands that stay put, and why page CSS never keys on dark.
- LayoutContent, wide and prose widths, four breakpoints, control heights and the stacking order.
- Languages and scripts197 locales across 26 scripts: the face each one gets, direction, and where the brand face appears.
Components
Server rendered React parts that read only tokens. Each page below shows the part live, its states, the tokens it uses and its keyboard and screen reader behaviour.
- ButtonPrimary, secondary and link actions, three sizes, an icon and a busy state.
- Badge and chipShort labels, and pressed chips for filters.
- FieldFloating labels on 16 input types, hints and errors tied to the input.
- Select and checkboxNative controls styled with tokens, so the keyboard and screen readers work as built.
- SwitchAn on and off setting that says its state in words, not colour alone.
- TabsArrow keys move between tabs; the panel follows the selected one.
- NoticeNeutral, info, positive, caution and critical messages with an icon.
- AccordionQuestions that open in place, readable without script.
- DialogA modal that traps focus, closes on Escape and returns focus where it came from.
- LoadingSpinner, progress, skeleton and shimmer, with a message that says what is happening.
- NavigationBreadcrumbs, pagination, side navigation and menus.
- Icon124 outline icons at one stroke, sized by the text around them.
Tokens
Every value lives once, in tokens.json. The build writes it out as CSS custom properties and a typed TypeScript module, and a check fails the build if the generated files fall behind.
- Colour roles
- 80
- Spacing steps
- 54
- Corner radii
- 8
- Type sizes
- 16
- Easings and durations
- 13
- Shadow heights
- 3
- Sizes
- 11
- Stacking levels
- 7
- Breakpoints
- 4
.order-card { padding: var(--space-6); border-radius: var(--radius-lg); background: var(--color-panel); color: var(--color-fg); transition: background var(--dur-fast) var(--ease-out);}.order-card:hover { background: var(--color-surface);}.order-card .meta { font-size: var(--text-sm); color: var(--color-fg-2);}Built for every script
The same page sets its words in the face made for each writing system, with its own direction. These lines are drawn live in the faces our locales use.
- locales
- 197
- scripts
- 26
- right to left
- 3
- Your order is on its way
- คำสั่งซื้อของคุณกำลังจัดส่ง
- طلبك في الطريق إليك
- आपका ऑर्डर रास्ते में है
- ההזמנה שלך בדרך
- Ваш заказ уже в пути
Checked on every change
Five checks run in CI on every pull request. A page cannot ship around them.
- Contrast
check-contrast.mjsEvery text and surface pair meets WCAG 2.2 AA in light and in dark, including code panels. - No hard-coded values
check-hardcoded.mjsA literal colour or spacing value in any app or package fails, so every part changes with its theme. - Generated files current
build-tokens.mjs --checkThe CSS and TypeScript written from tokens.json match it exactly. - Section schema
check-sections.mjsEvery page section is checked against its typed schema and a set of known good and bad pages. - A face for every script
check-fonts.mjsEach script in the locale list has a face, and the brand face lists only locales it can set.