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 initYou'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 buttonAdd more than one at once:
npx @kinetixui/cli add button card dialogadd 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 --alllist#
Not sure what's available? List every component in the registry:
npx @kinetixui/cli listinspect#
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 buttoninspect 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 registryPrints 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 doctortheme#
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 acmeSetting 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.cssResolves 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.swiftResolves 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.ktThe 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.dartThe 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/appEach 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.mjsalready do the equivalent. - Unsupported components — already covered by
doctor's "files that don't match the registry" check.
Options#
| Flag | Applies to | Does |
|---|---|---|
-y, --yes | init | skip prompts, use defaults |
-a, --all | add | install every component in the registry |
-o, --overwrite | add | replace files that already exist |
-r, --registry <url> | init, add, list, inspect, parity | point at a different registry (defaults to https://kinetixui.com/r, or registry in kinetixui.json) |
-f, --force | theme create | overwrite an existing theme file |
--no-fail-on-contrast | theme build | exit 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.