Skip to content
Kinetixui

CLI

The kinetixui CLI drops component source straight into your project — no account, no build step to consume it, just files you own.

init#

Set up kinetixui.json (your aliases) and pull in the token contract:

npx @kinetixui/cli init

You'll be asked where your global CSS file lives and which import aliases to use for components and utils. Pass -y to accept the defaults and skip the prompts.

add#

npx @kinetixui/cli add button

Add more than one at once:

npx @kinetixui/cli add button card dialog

add resolves registry dependencies automatically — asking for button also pulls in tokens the first time, installs the npm packages it needs (@radix-ui/react-slot, class-variance-authority, …) with whichever package manager your project already uses (pnpm / yarn / bun / npm, detected from the lockfile), and writes the component into your configured ui alias directory. Existing files are left alone unless you pass --overwrite.

Want everything at once instead of naming each one?

npx @kinetixui/cli add --all

list#

Not sure what's available? List every component in the registry:

npx @kinetixui/cli list

inspect#

Look at one component before (or after) adding it — description, npm dependencies, other registry items it needs, the files it writes, and whether it's already installed in the current project:

npx @kinetixui/cli inspect button

inspect also prints the component's first release and status, its variant axes and each axis's option names (Primary/Secondary/Outline/… for variant, sm/md/lg/… for size) for components built on a variant matrix like Button, Badge and Sheet, and its parts — the component and every sub-component it exports — with the props each declares itself (? marks optional). A part with no props listed only takes the props of the element or primitive it wraps. It's read from a generated specs/components/<name>.json, so it can't drift from the real prop surface; every component has one. See Component specs.

parity#

See which native platforms carry a component — or every component at once:

npx @kinetixui/cli parity button card kanban-board
npx @kinetixui/cli parity          # every component in the registry

Prints a matrix (✓/— per platform, plus a status tag for anything beta/deprecated) straight from the registry index, so it stays in step with components.manifest.json without a second copy of that data anywhere.

doctor#

Sanity-check a project that's already run init: is kinetixui.json valid, do its aliases resolve to real directories, does the Tailwind CSS target exist, is the registry reachable, and does everything sitting in your ui directory still match a name the registry knows about. Each check prints pass / warn / fail; doctor exits non-zero if anything failed outright (not just warned), so it's safe to run in CI:

npx @kinetixui/cli doctor

theme#

Scaffold and compile a local token override — CSS only. theme create writes kinetixui-themes/<name>.csv, every overridable semantic token listed and commented out; uncomment and edit the ones you want to change. theme build compiles that into kinetixui-themes/<name>.css (a :root override block — paste it after @import "@kinetixui/tokens/css";) and prints a WCAG AA contrast report for every base/foreground pair you set:

npx @kinetixui/cli theme create acme
# … edit kinetixui-themes/acme.csv …
npx @kinetixui/cli theme build acme

Setting a base color (e.g. primary) without its -foreground pair auto-derives the foreground by contrast (best of white or black) — you don't have to specify both. theme build exits non-zero if any pair fails AA (4.5:1), so it's safe to run in CI; pass --no-fail-on-contrast to build anyway and just see the report.

This is the same math as the Create workspace, and for a file of token,hex rows the two produce the same :root block — theme is the scriptable, version-controllable route for a project that wants its override checked into source rather than pasted by hand.

They are no longer equivalent in full. Create also generates a .dark block and the radius and elevation variables from its visual controls, and it has no CSV input format to express a radius or a surface treatment in. The shared subset is exactly this: semantic colours, given as literal hex values, for the light theme. Anything Create generates beyond that has to be copied from the workspace.

Presets#

kinetixui preset decode KX1_…

Prints what a Create preset contains — theme colour, neutral, radius, surface, chart palette and any manual overrides. It accepts a bare KX1_ code or a full /create?preset=… URL, and --json prints the same thing for piping.

kinetixui preset css KX1_…
kinetixui preset css KX1_… --output src/theme.css

Resolves a preset into the web CSS override block — the :root and .dark declarations, including the radius and elevation variables that theme build cannot express. This is the same theme engine and the same exporter the workspace runs behind the workspace's Web CSS target, so for a given preset the two produce byte-identical output; the CLI's tests assert that rather than assume it. A preset that changes nothing prints nothing, so > theme.css writes an empty file rather than a comment.

Web CSS only. This command produces a stylesheet and nothing else, which is why it is named preset css rather than preset apply — the name says what comes out.

kinetixui preset swiftui KX1_…
kinetixui preset swiftui KX1_… --name AcmeTheme --output Sources/AcmeTheme.swift

Resolves the same preset into a SwiftUI theme file: a public enum holding a light and a dark KinetixColors, built from the same resolved theme preset css renders as CSS. Apply it with

KinetixTheme(light: AcmeTheme.light, dark: AcmeTheme.dark) { … }

--name picks the Swift type (default CreateTheme). It must be a plain Swift identifier — letters, digits and underscores, not starting with a digit, and not a Swift keyword. Anything else is refused with the reason rather than quietly rewritten.

The whole design, and SwiftUI only. This writes KinetixColors, KinetixRadii and KinetixElevations — colours, corner radii and the shadow ladder — so a design's radius and surface treatment now arrive in SwiftUI rather than applying on the web alone. Radii and elevation are one value each rather than a light/dark pair, because the runtime takes one and both appearances resolve to the same ladder.

One thing does not survive: CSS shadows carry spread and SwiftUI's .shadow has no equivalent, so it is dropped and the larger steps render slightly wider than on the web. The generated file says so in its own header. A ladder the design did not touch is written as KinetixRadii.default rather than a copy of today's numbers, so it keeps following the library — the same rule the colours follow. A field the design did not change is written as a reference to the shipped token, so it keeps following the library.

kinetixui preset compose KX1_…
kinetixui preset compose KX1_… --name AcmeTheme --output AcmeTheme.kt

The same design as a Jetpack Compose theme: a Kotlin object holding a light and a dark KinetixColors. Apply it with

KinetixTheme(
    light = AcmeTheme.light,
    dark = AcmeTheme.dark,
) {
    App()
}

--name picks the Kotlin object (default CreateTheme) and follows the same rule as --name for SwiftUI, with Kotlin's keyword list.

Colours only here too. Compose's radius and elevation are generated constants that components reference directly — there is no runtime theme to override — so a design's radius and surface treatment do not travel. Two preset roles do not either: input and ring can be overridden by hand in a preset, and KinetixColors has no field for them. The generated file names all of this in its own header, and distinguishes it from tertiary-foreground, which is an older gap in the Compose theme rather than anything a preset can reach.

Compose writes Color(0xffRRGGBB), which is the resolved colour exactly, so nothing is lost between the engine and the platform.

kinetixui preset flutter KX1_…
kinetixui preset flutter KX1_… --name AcmeTheme --output acme_theme.dart

The same design as a Flutter theme: a Dart class holding a light and a dark KinetixColors. Apply it to Kinetix widgets with

KinetixTheme.custom(
  light: AcmeTheme.light,
  dark: AcmeTheme.dark,
  child: App(),
)

and to Material or Cupertino widgets through the adapters:

MaterialApp(
  theme: KinetixMaterialTheme.fromColors(Brightness.light, AcmeTheme.light),
  darkTheme: KinetixMaterialTheme.fromColors(Brightness.dark, AcmeTheme.dark),
)

The most complete native target. Flutter's KinetixColors has all 35 semantic fields, including input, ring and tertiaryForeground — the three Compose lacks — so no colour role is left behind. Radius and elevation still do not travel: widgets read KinetixRadius constants directly and KinetixTheme carries no shadow model, so a design's radius and surface treatment apply on the web only.

The Material and Cupertino adapters map the subset their own theme APIs can represent, which is smaller than the Kinetix contract — wrap the app in KinetixTheme.custom as well for Kinetix* widgets to see the full palette.

There is no Android XML exporter. preset swiftui, preset compose and preset flutter are the native targets that exist.

kinetixui preset url KX1_…

Prints the canonical share URL. It prints rather than launching a browser: piping it to open, xdg-open or start is one character, and a command that spawns a browser is unusable over SSH and untestable in CI.

Every preset subcommand uses the same codec the website does, so a code means one thing in both places. None of them executes anything from a preset — a preset is enum names, hex colours and token names off a fixed list, and anything else is refused.

theme build is the CSS-only command, and that is a fact about that command rather than about the library. It compiles a CSV of literal colours, and doing the same for a native platform would mean porting meaningful parts of style-dictionary/hooks.mjs's token-transform logic to three more target languages — a real, separate undertaking, not attempted. A native theme comes from a preset instead: preset swiftui, preset compose and preset flutter, documented above, with the colour-only limits each one states.

lint#

Scans your project for hardcoded values that should probably be semantic tokens instead — a raw hex color in a Tailwind arbitrary-value utility (bg-[#1a2b3c]) or an inline style, and an arbitrary spacing value (p-[13px], gap-[10px], …) on a box-model utility. With no argument it scans the components/ui aliases from kinetixui.json; pass a path to scan anywhere else:

npx @kinetixui/cli lint
npx @kinetixui/cli lint src/app

Each hit prints as file:line with the offending line and a suggestion. Comments are skipped (a hex mentioned in a // or /* */ comment won't fire), and the checks are anchored to real Tailwind/style contexts, not any bare #xxxxxx-looking substring — an href="#section" anchor link won't trigger it. lint exits non-zero if it finds anything, so it's safe to run in CI; pass --no-fail to see the report without failing the run.

Not covered by this check — each needs infrastructure this is deliberately not building yet:

  • Unknown/deprecated tokens — would need a live, version-matched list of every current token name fetched over the network; fragile without real support for it.
  • Accessibility violations — needs a real engine (axe-core or similar), a separate undertaking on its own.
  • Cross-platform inconsistencies — doesn't apply to a single consumer's single-platform project; that check only makes sense inside this monorepo, where scripts/check-rtl.mjs/check-icon-mapping.mjs already do the equivalent.
  • Unsupported components — already covered by doctor's "files that don't match the registry" check.

Options#

FlagApplies toDoes
-y, --yesinitskip prompts, use defaults
-a, --alladdinstall every component in the registry
-o, --overwriteaddreplace files that already exist
-r, --registry <url>init, add, list, inspect, paritypoint at a different registry (defaults to https://kinetixui.com/r, or registry in kinetixui.json)
-f, --forcetheme createoverwrite an existing theme file
--no-fail-on-contrasttheme buildexit 0 even if a pair fails WCAG AA

doctor doesn't take --registry, but it no longer assumes the default either: it checks whichever registry the project resolves to, so if kinetixui.json sets registry, that is the origin it reports on. Checking a different origin from the one add will use is worse than not checking, because it reads as a pass for something that was never tested.

Pointing at your own registry#

add needs the registry origin to be reachable and has no offline mode, so the default origin is a dependency of every install. To set an alternative once for a project rather than on every command, put it in kinetixui.json:

{
  "registry": "https://your-mirror.example.com/r"
}

--registry still wins when given. When the registry cannot be reached the CLI names the origin and both ways to change it, rather than surfacing a bare network error.

See kinetixui.json for what the config file controls.