# clean-design-system > A quiet, editorial design system for complex search applications. > 68 independently installable components in TypeScript, styled > with plain CSS custom properties — no runtime, no CSS framework, no build > plugin. Light and dark from one attribute. WCAG 2.2 AA contrast and keyboard > behaviour throughout, checked by axe over every story in CI. Docs and live examples: https://gitu.github.io/clean-design-system/ Source: https://github.com/gitu/clean-design-system ## Two ways to install **Copy the source in** (no account needed, and the files become yours): npx shadcn@latest add https://gitu.github.io/clean-design-system/r/.json The shadcn CLI installs from any public registry URL. Transitive dependencies resolve automatically — asking for `date-input` brings the tokens, `cx` and the seven components a date field is made of. Everything lands under one `components/ui/cds/` namespace and imports its neighbours relatively, so it works the same in Vite, Next and Remix. Requires a `components.json` at the project root; Tailwind is *not* used or required. **Or as a package** (GitHub Packages — needs a token with `read:packages`, even though the package is public): npm install @gitu/clean-design-system ## Rules that apply to everything - Wrap the tree in `` and import the stylesheet once. Nothing is styled without it. - Class names are prefixed `cds-`. The reset is wrapped in `:where()`, so it has zero specificity and cannot outrank your own CSS or Tailwind. - Colour comes from `--cds-color-*` custom properties; there are three theme blocks (light, `prefers-color-scheme: dark`, and `[data-cds-theme='dark']`) kept in sync by a build check. - Form controls wrapped in `` inherit their id, `aria-describedby`, invalid and required state automatically — do not wire those by hand. - Every component takes `className` and merges it. - Charts are the only thing with runtime dependencies (three `@visx/*` packages). - Self-hosted fonts are optional; the tokens fall back to Georgia and the system sans. ## Components Full props for every component. The short index is at https://gitu.github.io/clean-design-system/llms.txt ### Root #### ThemeProvider The root wrapper. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/theme-provider.json Exports: ThemeProvider, ThemeToggle, useTheme Types: ResolvedTheme, ThemeContextValue, ThemeProviderProps, ThemeSetting, ThemeToggleProps Depends on: icon, icon-button Props: - children: ReactNode (required) - theme: ThemeSetting — Controlled setting. Omit to let the provider own it. - defaultTheme: ThemeSetting - onThemeChange: (theme: ThemeSetting) => void - applyTo: 'element' | 'document' — Where the theme attribute lands. `element` scopes it to this subtree — right for embedding. `document` puts it on `` so portalled things (the command palette, drawers) inherit it too. Defaults to `document`. - storageKey: string | false — Where the choice is remembered. Pass `false` to keep it in memory only. Read in an effect rather than during render, because `localStorage` does not exist on a server and reading it during render would make the first client render disagree with the server's. - className: string — Extra classes on the root element. - asChild: boolean — Render children without the wrapping element. Requires `applyTo="document"`. ### Primitives #### Avatar A person, as a picture or as their initials. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/avatar.json Exports: Avatar Types: AvatarProps Props: - name: string (required) — The person. Used for the initials, and as the image's alt text. - src: string - size: 'xs' | 'sm' | 'md' | 'lg' - tinted: boolean — Tint the initials ground by hashing the name, so a list of people is scannable. Off by default — a wall of colour is not this system's manner. - decorative: boolean — Decorative when the name is already written next to it. #### Badge A read-only status marker: pipeline state, document type, embargo flag. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/badge.json Exports: Badge Types: BadgeProps, BadgeTone Props: - tone: BadgeTone — Semantic colour. `neutral` is the default and by far the most common. - variant: 'soft' | 'outline' | 'solid' — `soft` is a tinted ground, `outline` a hairline rule, `solid` a fill. - size: 'sm' | 'md' - dot: boolean — A small leading dot — useful for live/status readouts. - children: ReactNode #### Button The system's one button. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/button.json Exports: Button Types: ButtonProps, ButtonSize, ButtonVariant Depends on: spinner Props: - variant: ButtonVariant — `primary` is a solid ink fill — the one obvious action on a view. `accent` is the accent fill, reserved for the search/submit action itself. `secondary` is ruled, `ghost` is bare, `danger` is ruled in red. - size: ButtonSize - fullWidth: boolean — Stretch to the width of the container. - loading: boolean — Swap the leading icon for a spinner and block interaction. - iconStart: ReactNode — Icon shown before the label. - iconEnd: ReactNode — Icon shown after the label — chevrons, external-link marks. #### Callout An inline status block: what just happened, and what to do about it. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/callout.json Exports: Callout Types: CalloutProps, CalloutTone Depends on: icon, icon-button Props: - tone: CalloutTone — Which status this is. `info` is the default and by far the most common. - title: ReactNode — First line, set as a label. State what happened. - children: ReactNode — The explanation, and what to do about it. - actions: ReactNode — Buttons under the text — "Try again", "Enter it by hand". - icon: ReactNode — Replace the tone's own glyph. Pass `null` to drop it. - live: boolean — Announce this to assistive technology when it appears. Off by default, and that default matters: a callout that is simply part of the page — "imported from Homegate on 2 August" — is read in document order like any other text, and wrapping it in a live region would make a screen reader interrupt itself on every page load. Turn it on for one that appears *in response to something*, which is when there is news to announce. `danger` and `warning` announce assertively; the quieter tones do not interrupt. - onDismiss: () => void — Show a dismiss control. - dismissLabel: string #### Divider A hairline rule — the main structural device in this system. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/divider.json Exports: Divider Types: DividerProps Props: - orientation: 'horizontal' | 'vertical' - tone: 'subtle' | 'default' | 'strong' | 'accent' — `accent` is the heavy masthead rule — at most once per view. - label: ReactNode — Optional label set into the rule as a kicker. Horizontal only. - align: 'start' | 'center' — Where a label sits along the rule. - spacing: 'none' | 'sm' | 'md' | 'lg' — Vertical space above and below. #### Icon One glyph from the set, drawn rather than loaded. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/icon.json Exports: Icon, iconNames Types: IconName, IconProps Props: - name: IconName (required) — Which glyph to draw. - size: number | string — Edge length in pixels. Defaults to 16 — the system's control-icon size. - label: string — Accessible label. Omit it (the default) and the icon is hidden from assistive tech, which is correct whenever adjacent text already says what it means. Set it only for an icon carrying meaning on its own. #### IconButton A square Button carrying an icon and nothing else. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/icon-button.json Exports: IconButton Types: IconButtonProps Depends on: button, spinner Props: - icon: ReactNode (required) — The glyph. Usually an ``. - label: string (required) — Required. There is no visible text, so this is the only name the button has — it becomes both `aria-label` and the native tooltip. - variant: ButtonVariant - size: 'sm' | 'md' | 'lg' - loading: boolean - bare: boolean — Render at exactly the icon's size, with no padding box. #### Kbd Keyboard shortcut hint. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/kbd.json Exports: Kbd Types: KbdProps Props: - keys: string | string[] (required) — Keys to render. A string is split on `+` so `"Cmd+K"` becomes two caps. Pass an array for full control. - size: 'sm' | 'md' #### Progress A determinate or indeterminate progress bar. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/progress.json Exports: Progress Types: ProgressProps Props: - value: number — Omit for an indeterminate bar — work is happening, duration unknown. - max: number - label: string (required) — Accessible name. Required: a bare bar tells a screen reader nothing. - showLabel: boolean — Shown above the bar, with the value on the right. - valueLabel: ReactNode — Overrides the right-hand readout. Defaults to a percentage. - size: 'sm' | 'md' - tone: 'accent' | 'success' | 'warning' | 'danger' #### Skeleton Loading placeholder. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/skeleton.json Exports: Skeleton Types: SkeletonProps Props: - variant: 'text' | 'block' | 'circle' — `text` draws ruled lines, `block` a filled rectangle, `circle` an avatar. - lines: number — Number of lines when `variant="text"`. The last line is short, as prose is. - width: number | string - height: number | string #### Slider One value along a range. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/slider.json Exports: Slider Types: SliderProps Depends on: field Props: - value: number - defaultValue: number - min: number - max: number - step: number - valueLabel: ReactNode — Shown at the end of the track. Give it the value in the reader's terms — "72", "4 rooms", "CHF 1.2m" — not a bare number where a unit is meant. - minLabel: ReactNode — Text under the two ends of the track, e.g. "Poor" and "Excellent". - maxLabel: ReactNode - size: 'sm' | 'md' - invalid: boolean #### Spinner An indeterminate wait. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/spinner.json Exports: Spinner Types: SpinnerProps Props: - size: 'sm' | 'md' | 'lg' — Diameter. `sm` 12px, `md` 16px, `lg` 24px. - label: string | null — Announced to assistive tech. Pass `null` when a surrounding element already carries `aria-busy` — otherwise screen readers hear the state twice. #### Tag A value the user put into the interface — an applied filter, a chosen keyword. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/tag.json Exports: Tag Types: TagProps Depends on: icon Props: - facet: ReactNode — Optional dimension label rendered before the value, e.g. `Section`. - children: ReactNode (required) — The value itself. - onRemove: (event: MouseEvent) => void — Show a dismiss control. The handler receives the click event. - removeLabel: string — Accessible name for the dismiss control. - tone: 'neutral' | 'accent' - size: 'sm' | 'md' ### Forms #### Calendar A month grid for picking a day or a span. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/calendar.json Exports: Calendar Types: CalendarProps, CalendarRange Depends on: icon, icon-button Props: - mode: 'single' | 'range' — `single` picks a day, `range` picks two. Range selection takes two clicks; the second one before the first swaps them rather than rejecting the click. - value: IsoDate | null — Selected day, in `single` mode. `YYYY-MM-DD`. - onChange: (value: IsoDate | null) => void - range: CalendarRange — Selected span, in `range` mode. - onRangeChange: (range: CalendarRange) => void - month: IsoDate — The month on screen. Uncontrolled if omitted. - onMonthChange: (month: IsoDate) => void - min: IsoDate - max: IsoDate - disabledDates: IsoDate[] — Days that cannot be picked, on top of `min`/`max`. - months: 1 | 2 — Show two months side by side — the usual choice for picking a range. - weekStartsOn: 0 | 1 — 0 is Sunday, 1 Monday. - locale: string - markedDates: IsoDate[] — A dot under a day — deadlines, entries, anything countable. - label: string — Accessible name. - renderDay: (iso: IsoDate) => ReactNode — Rendered under each day number. Keep it to a word or a figure. #### Checkbox A real under a drawn box. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/checkbox.json Exports: Checkbox Types: CheckboxProps Depends on: field, icon Props: - label: ReactNode — Text beside the box. Omit it only when `aria-label` is set. - description: ReactNode — Secondary line under the label. - indeterminate: boolean — Renders the dash state for a partially-selected group. Purely visual until paired with the matching `checked` value, as the DOM property requires. - size: 'sm' | 'md' - invalid: boolean #### DateInput A date you can type or point at. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/date-input.json Exports: DateInput Types: DateInputProps Depends on: button, calendar, field, icon, input Props: - value: IsoDate | null (required) - onChange: (value: IsoDate | null) => void (required) - min: IsoDate - max: IsoDate - disabledDates: IsoDate[] — Days that cannot be chosen — an embargo blackout, a closed period. - markedDates: IsoDate[] — Days worth marking without disabling them. - today: IsoDate — The day relative input (`today`, `+3d`, `friday`) counts from. Pass one to make the component deterministic in a test or a screenshot. - dayFirst: boolean — How to read `8/7`. Defaults to day-first. - locale: string - weekStartsOn: 0 | 1 - size: 'sm' | 'md' | 'lg' - placeholder: string — Defaults to the locale's own shape — `dd/mm/yyyy`, `mm/dd/yyyy`, and so on. - label: string — Accessible name, when there is no surrounding ``. - hideCalendar: boolean — Drop the calendar button and accept typing only. - invalid: boolean - disabled: boolean - required: boolean - id: string - aria-describedby: string - className: string #### DateRangePicker A button that opens a two-month calendar and reports a span. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/date-range-picker.json Exports: DateRangePicker Types: DateRangePickerProps, DateRangePreset Depends on: button, calendar, icon Props: - value: CalendarRange (required) - onChange: (range: CalendarRange) => void (required) - presets: DateRangePreset[] — Shortcuts down the side — "Last 30 days" and friends. - min: IsoDate - max: IsoDate - months: 1 | 2 - weekStartsOn: 0 | 1 - locale: string - label: string — Field label, announced with the trigger. - placeholder: string - size: 'sm' | 'md' - disabled: boolean - className: string #### DateTimeInput A date and a time, as two fields that behave like one value. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/date-time-input.json Exports: DateTimeInput Types: DateTimeInputProps, LocalDateTime Depends on: calendar, date-input, field, time-input Props: - value: LocalDateTime | null (required) — `YYYY-MM-DDTHH:mm`, or null. Deliberately **not** a `Date` and deliberately **not** UTC. An embargo lifting at 06:00 lifts at 06:00 in the newsroom; storing that as an instant means picking a zone at the moment of entry and being wrong the next time the clocks change. The zone belongs to the system that publishes, and `zone` below is a label saying which one, not a conversion. - onChange: (value: LocalDateTime | null) => void (required) - defaultTime: TimeString — Used when a time is entered before a date. Defaults to `00:00`. - min: IsoDate - max: IsoDate - disabledDates: IsoDate[] - today: IsoDate — Anchors relative input like `today` and `+3d`. - step: number - hour12: boolean - zone: string — Shown after the time — `CET`, `Europe/Zurich`, `newsroom time`. - locale: string - weekStartsOn: 0 | 1 - size: 'sm' | 'md' | 'lg' - label: string — Names the pair, and both halves of it — "Embargo lifts" becomes "Embargo lifts — date" and "Embargo lifts — time". Taken from a surrounding `` when there is one. - invalid: boolean - disabled: boolean - required: boolean - className: string #### Field Label, hint, error and the ARIA wiring between them. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/field.json Exports: Field, FieldContext, useFieldControl Types: FieldContextValue, FieldProps Props: - label: ReactNode — Visible label. Omit it only when `aria-label` is set on the control. - hint: ReactNode — Supporting text under the label — syntax notes, units, examples. - error: ReactNode — Error text. Its presence puts the control into the invalid state. - required: boolean — Marks the label and sets `required` on the control. - disabled: boolean — Greys out the label and disables the control. - orientation: 'vertical' | 'horizontal' — Place the label beside the control instead of above it. - action: ReactNode — Right-aligned slot on the label row — "Reset", a counter, a help link. - children: ReactNode (required) #### FileDrop A drop target for files. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/file-drop.json Exports: FileDrop Types: FileDropProps Depends on: field, icon Props: - label: ReactNode — The instruction, and it should name both routes — dropping and choosing. Half of the people who use this will never drag anything. - hint: ReactNode — What is accepted, in words. The `accept` attribute is not readable. - onFiles: (files: FileList) => void — Called with whatever arrived, by either route. - invalid: boolean - size: 'sm' | 'md' — `sm` for a drop target beside a list that already has files in it. #### Input A single-line text field, with room either side of the text. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/input.json Exports: Input Types: InputProps Depends on: field Props: - size: 'sm' | 'md' | 'lg' - iconStart: ReactNode — Node pinned inside the leading edge — usually an ``. - iconEnd: ReactNode — Node pinned inside the trailing edge — a unit, a clear button, a count. - prefix: ReactNode — Static text fused to the leading edge, e.g. `https://`. - suffix: ReactNode — Static text fused to the trailing edge, e.g. `kg`. - invalid: boolean — Force the error styling. A surrounding `` sets this for you. - mono: boolean — Use the monospace face — right for IDs, hashes and query syntax. #### MarkdownEditor A text field for prose, with the markup it accepts made visible. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/markdown-editor.json Exports: MarkdownEditor Types: MarkdownEditorProps Depends on: field, icon, icon-button, markdown Props: - value: string (required) - onChange: (value: string) => void (required) - commands: MarkdownCommandId[] — Which toolbar buttons to show, in order. Trim it for a comment box; the default is the set a body field needs. - placeholder: string - rows: number — Visible rows in the write pane. The preview matches its height. - preview: 'tabs' | 'split' | 'off' — `tabs` puts write and preview behind a switch — right on a phone and in a narrow column. `split` shows both at once. `off` drops the preview. - defaultPane: 'write' | 'preview' — Which pane starts in front. Only meaningful with `preview="tabs"`. - maxLength: number — Soft ceiling. Going over is shown, not prevented — truncating someone's sentence mid-word to enforce a limit loses their work to save a validation message. - showCount: boolean — Show the word and character count under the editor. - hint: ReactNode — Extra note in the footer — a syntax reminder, usually. - invalid: boolean - disabled: boolean - readOnly: boolean - required: boolean - id: string - aria-describedby: string - label: string — Accessible name, when there is no surrounding ``. - className: string #### Radio One option in a mutually exclusive set. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/radio.json Exports: Radio, RadioGroup Types: RadioGroupProps, RadioProps Props: - label: ReactNode - description: ReactNode - size: 'sm' | 'md' - value: string (required) #### SegmentedControl A small set of mutually exclusive choices shown all at once — sort order, result density, time window. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/segmented-control.json Exports: SegmentedControl Types: SegmentedControlProps, SegmentedOption Props: - options: SegmentedOption[] (required) - value: T - defaultValue: T - onChange: (value: T) => void - size: 'sm' | 'md' - fullWidth: boolean — Stretch segments to fill the container evenly. - label: string — Accessible name for the set. - className: string #### Select A native ` fits well. - className: string #### SortControl Sort field plus direction. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/sort-control.json Exports: SortControl Types: SortControlProps, SortDirection, SortOption Depends on: icon, select Props: - options: SortOption[] (required) - value: string (required) - direction: SortDirection - onChange: (value: string, direction: SortDirection) => void (required) - label: string | null — Leading label. Pass `null` to omit it. - size: 'sm' | 'md' - disabled: boolean - className: string ### Charts #### AreaChart Quantity over time, as filled bands. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/area-chart.json Exports: AreaChart Types: AreaChartProps Depends on: chart Props: - x: (datum: Datum, index: number) => Date | number (required) — Position along x. A `Date` gives a time scale, a `number` a linear one. - formatX: (value: Date | number) => string - curve: 'linear' | 'monotone' | 'step' - xTicks: number - stacked: boolean — Stacking is the point of an area chart — the filled bands read as parts of a whole. Unstack only for two series that genuinely overlap. - stackOffset: 'none' | 'expand' — `expand` normalises each column to 100% — share over time rather than volume. - fillOpacity: number — Fill strength, 0–1. The default is a wash, not a block. #### BarChart Categorical comparison, grouped or stacked, in either orientation. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/bar-chart.json Exports: BarChart Types: BarChartProps npm dependencies: @visx/scale Depends on: chart, chart-tooltip Props: - x: (datum: Datum, index: number) => string (required) — The category. Bars use a band scale, so this is a string. - layout: 'vertical' | 'horizontal' — `horizontal` for long category labels — which is most of them in a search application, where categories are queries, sources and facet values. - stacked: boolean - barPadding: number — Gap between bands, 0–1. - maxBarWidth: number — Stops a single bar becoming a slab on a wide chart. - valueLabels: boolean — Draw the value at the end of each bar. #### ChartFrame The titled region a chart sits in — Panel, but with the slots a chart actually needs: a legend, a drill-down trail, a source note and a data table. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/chart-frame.json Exports: ChartFrame Types: ChartFrameProps Depends on: icon Props: - title: ReactNode - description: ReactNode - breadcrumbs: ReactNode — Drill-down trail. Pass the system's `Breadcrumbs`. - actions: ReactNode — Right-aligned slot in the header — a scope switch, a download. - legend: ReactNode - legendPlacement: 'top' | 'bottom' - footnote: ReactNode — Source line, set as a kicker under a hairline. - table: ReactNode — A visible data table behind a disclosure. Pass a `DataTable`. A slot rather than an import: every chart already renders a hidden table for screen readers, and building a visible one in would mean anyone importing `LineChart` also ships `DataTable`, `Checkbox` and `Skeleton`. - tableLabel: string - variant: 'plain' | 'ruled' - children: ReactNode #### ChartGroup Links several charts so they share a hover, a filter, a hidden-series set and a zoom window. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/chart-group.json Exports: ChartGroup, ChartGroupContext, ChartHoverContext Types: ChartGroupProps, ChartGroupValue, ChartHoverValue Depends on: chart Props: - hoverKey: ChartKey | null — The datum under the pointer in any member chart. - defaultHoverKey: ChartKey | null - onHoverChange: (key: ChartKey | null) => void - selected: ChartKey[] — Datum keys the whole group is filtered to. Empty means no filter. - defaultSelected: ChartKey[] - onSelectionChange: (keys: ChartKey[]) => void - hiddenSeries: string[] - defaultHiddenSeries: string[] - onHiddenSeriesChange: (keys: string[]) => void - zoom: ChartDomain | null - defaultZoom: ChartDomain | null - onZoomChange: (domain: ChartDomain | null) => void - sync: ('hover' | 'selection' | 'series' | 'zoom')[] — Which channels are shared. Defaults to all four. - selectionMode: 'toggle' | 'replace' | 'none' — How a datum click changes `selected`. - columns: 1 | 2 | 3 — Lays members out on a grid. Use `Stack` instead if you want a column. - gap: 4 | 6 | 8 - children: ReactNode #### ChartLegend The key, and the control for hiding series. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/chart-legend.json Exports: ChartLegend Types: ChartLegendItem, ChartLegendProps Props: - items: ChartLegendItem[] (required) - hiddenKeys: string[] — Series keys currently switched off. Named `hiddenKeys` rather than `hidden` so it does not shadow the HTML attribute of that name, which a caller may still want to use on the legend itself. - onHiddenChange: (keys: string[]) => void - activeKey: string | null — Emphasise one entry — wire to the chart's hovered series. - onActiveKeyChange: (key: string | null) => void - orientation: 'horizontal' | 'vertical' - swatch: 'line' | 'square' | 'dot' - size: 'sm' | 'md' - label: string #### ChartTooltip The readout beside the crosshair. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/chart-tooltip.json Exports: ChartTooltip Types: ChartTooltipProps, ChartTooltipRow Props: - title: ReactNode - rows: ChartTooltipRow[] - footer: ReactNode - x: number — Fraction across the plot, 0–1. Past 0.5 the surface flips to the left. - y: number — Fraction down the plot, 0–1. Ignored when `placement` is `top`. - placement: 'follow' | 'top' — `top` pins it to the plot's top edge — calmer for a multi-series read. - open: boolean #### LineChart A time series. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/line-chart.json Exports: LineChart Types: LineChartProps Depends on: chart Props: - x: (datum: Datum, index: number) => Date | number (required) — Position along x. A `Date` gives a time scale, a `number` a linear one. - formatX: (value: Date | number) => string - curve: 'linear' | 'monotone' | 'step' - points: 'none' | 'hover' | 'always' — `hover` draws a dot only at the point being read, which is the editorial default — a line with twenty dots on it is a line you read twice. - xTicks: number #### Sparkline A word-sized chart, for a table cell or the corner of a stat tile. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/sparkline.json Exports: Sparkline Types: SparklineProps npm dependencies: @visx/scale, @visx/shape Depends on: chart Props: - data: Datum[] (required) — One entry per step along the line. Order is the order drawn. - value: (datum: Datum, index: number) => number | null (required) — Reads the plotted number out of a datum. Return `null` for a genuine gap — a gap breaks the line, where zero draws a point at zero. They are not the same thing and a sparkline is small enough that the difference matters. - kind: 'line' | 'area' | 'bar' — `line` is the default and the quietest. `area` fills beneath it for a trend that carries weight. `bar` suits counts that are genuinely discrete. - label: string (required) — Accessible name, e.g. "Queries, last 30 days". Required — see the note below. - summary: string — Read out after the name, e.g. "up 12 per cent". A sparkline has no axes and no tooltip, so this sentence is the only thing a screen reader can convey. - color: string — Any CSS colour, or a token. Defaults to the surrounding text colour. - width: number — Fixed width. Ignored when `fluid` is set. - height: number - fluid: boolean — Fill the container's width instead of taking a fixed one. Measured rather than stretched: an SVG scaled with `preserveAspectRatio` would thicken the stroke and distort the shape, which is the one thing a sparkline has to get right. `width` becomes the fallback used for the first render and on a server. - endpoint: boolean — Mark the final point with a dot — useful when the line ends mid-scale. ### Prose #### Markdown A deliberately small Markdown subset, rendered as React elements. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/markdown.json Exports: Markdown Types: MarkdownProps Props: - children: string (required) - renderCode: (code: string, language?: string) => ReactNode — Rendered for a fenced code block. Pass one to add highlighting, or a `ChatFile`. Without it, code blocks render as plain preformatted text. - className: string ### Data #### DataTable A dense, ruled table. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/data-table.json Exports: DataTable Types: DataTableProps, SortDir, TableColumn Depends on: checkbox, icon, skeleton Props: - columns: Column[] (required) - rows: Row[] (required) - rowKey: (row: Row, index: number) => string (required) — Stable identity per row — required for selection and React keys. - sort: { key: string; direction: SortDir } — Current sort. Sorting itself is the caller's job. - onSortChange: (key: string, direction: SortDir) => void - selected: string[] — Selected row keys. Passing this turns on the selection column. - onSelectionChange: (keys: string[]) => void - onRowClick: (row: Row, index: number) => void - activeKey: string — Row keys to draw as active — usually the row open in a detail pane. - density: 'comfortable' | 'compact' — `compact` fits roughly a third more rows on screen. - stickyHeader: boolean — Header stays put while the body scrolls. - loading: boolean - loadingRows: number - empty: ReactNode — Shown in place of the body when there are no rows. - label: string — Accessible name for the table. - className: string #### EmptyState A zero-result state. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/empty-state.json Exports: EmptyState Types: EmptyStateProps Props: - title: ReactNode (required) — One line, set in the serif. State what happened, not what went wrong. - description: ReactNode — What to do about it. Keep it to a sentence or two. - icon: ReactNode — Glyph above the title. Optional — often the page reads better without. - actions: ReactNode — Primary and secondary actions. - suggestions: ReactNode[] — Concrete next steps, e.g. spelling, fewer filters, a broader date range. - size: 'sm' | 'md' ### Layout #### AppShell The page frame for a search application: masthead, filter column, results, optional detail pane. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/app-shell.json Exports: AppShell Types: AppShellProps Props: - header: ReactNode — Masthead. Sticks to the top and carries the accent rule underneath. - sidebar: ReactNode — Filter column. Sticky on wide screens; move it into a `Drawer` on narrow. - aside: ReactNode — Optional second column on the trailing edge — detail view, preview pane. - footer: ReactNode - children: ReactNode - sidebarWidth: string — Width of the filter column. Any CSS length. - asideWidth: string - sidebarHidden: boolean — Hide the sidebar — pair with a `Drawer` at narrow widths. - asideCollapse: 'hide' | 'stack' — What happens to the aside below 1100px. `hide` is the default because the usual aside is a preview pane, and a preview stacked under a hundred results helps nobody. Use `stack` when the aside carries something that exists nowhere else — a document's metadata, a summary — so a narrow screen does not simply lose it. - maxWidth: string | false — Cap and centre the content column. #### Breadcrumbs Path back up through a collection, an archive, or a folder tree. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/breadcrumbs.json Exports: Breadcrumbs Types: BreadcrumbsProps, Crumb Props: - items: Crumb[] (required) - separator: ReactNode — Character between crumbs. - maxItems: number — Collapse the middle behind an ellipsis when there are more crumbs than this, always keeping the first and the last two. - label: string #### ConfirmPopover A confirmation anchored to the thing being confirmed. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/confirm-popover.json Exports: ConfirmPopover Types: ConfirmPopoverProps Depends on: button, popover, stack Props: - trigger: (props: PopoverTriggerProps) => ReactNode (required) — The control being confirmed — a delete button, usually. - title: ReactNode (required) — The question. One line, and name the consequence rather than the verb. - description: ReactNode — A second line, for the part that is not obvious from the question. - confirmLabel: string - cancelLabel: string - tone: 'default' | 'danger' — `danger` rules the confirm button in red. - align: 'start' | 'end' - onConfirm: () => void (required) - open: boolean - defaultOpen: boolean - onOpenChange: (open: boolean) => void - className: string #### Dialog A centred modal, for a decision or a short form. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/dialog.json Exports: Dialog Types: DialogProps npm dependencies: react-dom Depends on: icon, icon-button Props: - open: boolean (required) - onClose: () => void (required) - title: ReactNode - description: ReactNode — A line under the title. For a `danger` dialog, say what is irreversible. - footer: ReactNode - size: 'sm' | 'md' | 'lg' - tone: 'default' | 'danger' — `danger` rules the header in red. Use it only where the action cannot be undone — if everything is alarming, nothing is. - hideClose: boolean - dismissible: boolean — Off for a dialog the reader must answer rather than dismiss. - children: ReactNode - className: string #### Disclosure A summary row that opens. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/disclosure.json Exports: Disclosure Types: DisclosureProps Depends on: icon Props: - summary: ReactNode (required) — The always-visible row. Keep it to one line — it is a heading, not a lede. - meta: ReactNode — Right-aligned slot on the summary row — a count, a date, a status. - children: ReactNode (required) - open: boolean - defaultOpen: boolean - onOpenChange: (open: boolean) => void - headingLevel: 2 | 3 | 4 | 5 | 6 — Heading level for the summary. A disclosure in a list of them is a section of the page and should say so; one inside a `Panel` that already has a heading should not. Omit it and the summary is a plain row. - border: 'bottom' | 'none' — Where the hairline sits. `none` for a disclosure inside a ruled container. - disabled: boolean #### Drawer A slide-over panel: filters on narrow screens, a result's detail view beside the list, a bulk-edit form. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/drawer.json Exports: Drawer Types: DrawerProps npm dependencies: react-dom Depends on: icon, icon-button Props: - open: boolean (required) - onClose: () => void (required) - title: ReactNode — Heading, set as a kicker over the content. - description: ReactNode - side: 'start' | 'end' | 'bottom' — Which edge it enters from. `start`/`end` follow writing direction. - size: 'sm' | 'md' | 'lg' - footer: ReactNode — Pinned action row at the bottom. - hideClose: boolean — Hide the close button — only sensible when the footer offers a way out. - dismissible: boolean — Let clicks on the backdrop close it. - children: ReactNode - className: string #### Menu A dropdown of actions, anchored to whatever opens it. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/menu.json Exports: Menu Types: MenuItem, MenuProps Props: - trigger: (props: { onClick: () => void 'aria-haspopup': 'menu' 'aria-expanded': boolean 'aria-co... (required) — The control that opens the menu. Gets the aria wiring automatically. - items: MenuItem[] (required) - align: 'start' | 'end' — Which edge of the trigger the menu lines up with. - label: string — Accessible name for the menu itself. - className: string #### NavList The navigation for an application with more than one page. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/nav-list.json Exports: NavList Types: NavItem, NavListProps Props: - items: NavItem[] (required) - value: string — The current destination. - onChange: (id: string) => void - orientation: 'vertical' | 'horizontal' — `vertical` is a sidebar, `horizontal` a header bar. Horizontal drops the group headings — a bar is not the place for section titles. - size: 'sm' | 'md' - label: string — Accessible name. Required when a page has more than one nav. #### Panel A titled region. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/panel.json Exports: Panel Types: PanelProps Props: - title: ReactNode — Heading, set as a kicker over a rule. - description: ReactNode — Secondary line under the title. - actions: ReactNode — Right-aligned slot in the header. - footer: ReactNode — Pinned to the bottom, above a rule. - variant: 'plain' | 'ruled' | 'sunken' — `plain` has no ground at all — the default, and the most editorial. `ruled` draws a hairline box. `sunken` fills with the recessed surface. - padding: 'none' | 'sm' | 'md' | 'lg' - children: ReactNode #### Popover Arbitrary content anchored to a control — a short form, a set of choices, a confirmation. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/popover.json Exports: Popover Types: PopoverProps, PopoverTriggerProps Props: - onClick: () => void (required) - aria-haspopup: 'dialog' (required) - aria-expanded: boolean (required) - aria-controls: string (required) - ref: Ref (required) #### Stack The layout primitive for glue between components. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/stack.json Exports: Stack Types: SpaceToken, StackProps Props: - direction: 'row' | 'column' — Layout direction. `row` is the horizontal form, often called an inline. - gap: SpaceToken — Gap, in space-scale steps — `4` means `var(--cds-space-4)`. - align: 'start' | 'center' | 'end' | 'stretch' | 'baseline' - justify: 'start' | 'center' | 'end' | 'between' | 'around' - wrap: boolean - dividers: boolean — Hairline rules between children — the system's default separator. - as: ElementType — Element to render. Use a landmark tag where one fits. - children: ReactNode #### Tabs Result scopes, document sections, saved views. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/tabs.json Exports: Tabs Types: TabItem, TabsProps Props: - items: TabItem[] (required) - value: string - defaultValue: string - onChange: (value: string) => void - variant: 'underline' | 'enclosed' — `underline` is the editorial default; `enclosed` reads as a filed folder. - size: 'sm' | 'md' - fullWidth: boolean — Stretch the rule the full width of the container. - label: string - children: ReactNode — Panel content. Render the active tab's content yourself. - className: string #### Toast Announces what just happened, without stealing focus. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/toast.json Exports: ToastProvider, useToast Types: ToastMessage, ToastProviderProps npm dependencies: react-dom Depends on: icon, icon-button Props: - children: ReactNode (required) - max: number — Oldest messages drop off beyond this. - duration: number | null — How long a message without an action stays. `null` keeps every message until dismissed, which is what this component used to do unconditionally. #### Toolbar A row of controls above or below a content region. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/toolbar.json Exports: Toolbar Types: ToolbarProps Props: - children: ReactNode — Left group — usually the primary controls. - end: ReactNode — Right group, pushed to the far edge. - border: 'none' | 'top' | 'bottom' | 'both' — Where the hairline sits. - size: 'sm' | 'md' - sticky: boolean — Stick to the top of the scroll container. - label: string ### Assistant #### Chat The assistant family: transcript, messages, tool calls, artefacts, files, diffs, images and question sets. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/chat.json Exports: ChatArtifact, ChatComposer, ChatDiff, ChatFile, ChatImage, ChatMarkdown, ChatMessage, ChatQuestion, ChatThread, ChatToolCall Types: ChatAnswer, ChatArtifactProps, ChatComposerProps, ChatDiffProps, ChatFileProps, ChatImageProps, ChatMarkdownProps, ChatMessageProps, ChatQuestionProps, ChatQuestionSpec, ChatThreadProps, ChatToolCallProps Depends on: avatar, button, dialog, icon, input, markdown, spinner, tabs ### Internal #### Chart Internal chart machinery — scales, axes, the SVG surface, the keyboard cursor. Installed as a dependency of the charts; not used directly. npx shadcn@latest add https://gitu.github.io/clean-design-system/r/chart.json Exports: CartesianChart, ChartAxis, ChartGrid, ChartSrTable, ChartSurface Types: AxisTick, CartesianChartProps npm dependencies: @visx/curve, @visx/scale, @visx/shape Depends on: chart-group, chart-tooltip Props: - x: (datum: Datum, index: number) => Date | number (required) - formatX: (value: Date | number) => string - curve: 'linear' | 'monotone' | 'step' - points: 'none' | 'hover' | 'always' - xTicks: number - mode: 'line' | 'area' — `area` fills beneath the line. - stacked: boolean - stackOffset: 'none' | 'expand' - fillOpacity: number