Get started
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
ComitorLocaleProvidersays 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.
The primitives: shadcn/ui in the new-york style, retuned so every colour resolves through a role token. Nothing here knows what a task or an invoice is.
ButtonInputDialogSelectTabsTooltip
The patterns every Comitor app repeats, assembled once from the primitives — and where most of the package’s value lives. Includes the eighteen components ported from Dice UI in 2.1.0.
DataTableKanbanStepperDatePickerTimePickerStatusPill
The application frame: a sidebar and header shared by every product, the workspace switcher and app launcher, and five display axes each user sets for themselves.
AppShellAppHeaderWorkspaceSwitcherAppLauncherThemeProvider
Whole screens — a CRM pipeline, a task board, an inbox, a checkout, an onboarding wizard — built from the three tiers and nothing else, with the code beside each one.
Web AppTasksCRMHRChatCommerce
What everything above stands on: the colour contract and its two palettes, type, spacing, icons, the brand marks, and which code may run on the server.
ColorsTypographySpacingIconographyServer and client
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.
// 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.