Introduction

The Comitor Design System is one npm package, @comitor/ui, and this site — the reference for how everything in it looks, behaves and is meant to be used. Comitor’s own products are built from it: Tasks, Chat, CRM, HR, the admin console and the marketing site.

It is React 19 and Tailwind v4, ESM only, typed strictly, and it speaks English by default — so an app that has nothing to do with Comitor can install it and use it as it is. This page explains how it is organised and the few principles every part of it follows; the installation guide takes you from an empty app to a first rendered component.

New in 2.1 and 3.0

documenting 3.17.3
  • 3.0.0 — English by default. Every component now renders English unless a ComitorLocaleProvider says otherwise; Vietnamese is a built-in table you choose once at the root. Set the language
  • 2.1.0 — eighteen components from Dice UI, rebuilt on the package’s own primitives and tokens, with the upstream bugs fixed on the way in:

Everything else that changed, with the migration for each breaking item, is in the changelog.

What is in it

Three tiers, each built only from the ones below it, plus the rules they all stand on and whole screens that show them working together. The sections of this site follow the same split.

One package, nine doors

The package is split by what each part needs installed, not by topic. Everything a plain React app needs sits behind the main entry, which has no optional peer at all; each part that depends on something heavier — Next, recharts, a table engine, a form library, an upload client, an editor engine — has an entry of its own, so you only install what you import. The installation guide has the full table, and the two names that really do mean different things at different doors.

imports
// Tier 1 + Tier 2, the language provider, cn and the tokens — no optional peer
import { Button, DataTable, Kanban, ComitorLocaleProvider } from '@comitor/ui'

// Tier 3, the app frame — needs next, next-themes
import { AppShell, ThemeProvider } from '@comitor/ui/shell'

// Behind their own doors, one optional peer each
import { ComitorBarChart } from '@comitor/ui/chart'          // recharts
import { DataGrid } from '@comitor/ui/data-grid'             // @tanstack/react-table
import { Form, FormField } from '@comitor/ui/form'           // react-hook-form
import { ImageUploadField } from '@comitor/ui/uploader'      // @uppy/core, @uppy/xhr-upload
import { MarkdownEditor } from '@comitor/ui/editor'          // @tiptap/*, lowlight
import { ChatThread } from '@comitor/ui/chat'                // the editor's peers, nothing more

// Plain values for places with no Tailwind: canvas, PDF, email
import { semantic } from '@comitor/ui/tokens'

Principles

Six decisions that every component follows. Each one exists because its absence was once a bug that made no noise — the pages linked below tell those stories.

One source of truth

The package is the implementation; this site imports the published package and vendors nothing. If a demo renders, the package really does that — and a missing token is fixed in the package and released, never patched in an app.

Installation →

Colour is a contract

Components never name a raw colour. A fill, the text on that fill, and the ink used on the page background are three different tokens — which is how the same component stays correct in light, dark and the high-contrast palette.

Colors →

Accessible by construction

Contrast is measured, not eyeballed; every control works from a keyboard; every name a screen reader speaks can be translated. Where the package knowingly falls short, the page for that component says so, with the workaround.

Display axes →

English first, Vietnamese built in

Since 3.0.0 every string defaults to English, and Vietnamese is one provider away. Language and region are kept apart: the reader’s language never decides a workspace’s currency or country.

Set the language →

Server-friendly

“use client” sits only in the files that need it, and every constant lives in a module a Server Component can read. What cannot cross the boundary fails loudly rather than arriving as undefined.

Server and client →

A stable API

The API has been frozen since 1.0.0 and follows SemVer: a breaking change is a major version, and every breaking item ships with the migration an app has to perform.

Changelog →

How to read this site

Component pages share one shape: what the component is for, live demos, the props, what it cannot do yet, and what it does for keyboard and screen-reader users — in that order.

Preview, then Code
Every demo is the real component with real state — type, drag and select, and it responds. The Code tab next to it is the same demo as source you can paste.
Props, from the types
Props tables are written from the package’s published type declarations, and defaults are checked against its source. Plain text on purpose: what you read is what you type.
Why, not only what
The package’s source records why each decision was made and what was measured. Pages pass that on — a contrast ratio, the bug a default exists to prevent — because that is what lets you disagree with a default safely.
One version at a time
The site documents exactly the version named at the foot of the sidebar, read from the installed package at build time. Older behaviour and every migration live in the changelog.

Where to start