shadcn-style components for gsx — copy-in, type-checked, server-rendered.
go install github.com/gsxhq/gsxui/cmd/gsxui@latest
In your project (a Go module):
gsxui init # tokens css, js runtime, class merger, gsx.toml wiring
gsxui add dialog # vendors dialog + its deps (button), regenerates
gsxui list # what's available
You own the vendored code. gsxui add never touches a modified file unless
you pass --overwrite.
After upgrading the gsxui binary, re-run gsxui add <name> --overwrite to
refresh vendored components — this discards local edits to those files.
Status: pre-release. 52 components + icon, covering most of shadcn/ui's set. The showcase site and theme editor are live at ui.gsxhq.dev.
- Components live flat in
ui/— onepackage ui, each component its own<name>.gsxsource (JSX-style, named parameters, fallthrough attrs) plus a behavior<name>.jswhen interactive.iconis the one exception, staying its ownui/iconpackage (generated Lucide data). Every component is imported the same way:import "github.com/gsxhq/gsxui/ui", used asui.Button,ui.CardHeader,ui.DialogContent, etc. — seedocs/jsx-parity.md's packaging note for why. make testregenerates and tests everything;make checkadds JS syntax and gofmt checks.- Divergences from the shadcn/ui reference:
docs/jsx-parity.md.
Form controls: button, button-group, checkbox, combobox, field, input, input-group, input-otp, label, native-select, radio, select, slider, switch, textarea, toggle, toggle-group
Display: alert, avatar, badge, card, empty, item, kbd, progress, separator, skeleton, spinner, table
Overlay: alert-dialog, context-menu, dialog, drawer, dropdown, hover-card, menubar, popover, sheet, sonner, tooltip
Navigation: accordion, breadcrumb, command, navigation-menu, pagination, sidebar, tabs
Layout: aspect-ratio, carousel, collapsible, resizable, scroll-area
Primitives: icon (Lucide, generated — a dependency of native-select, accordion, and dropdown's own future items, not usually added directly)
Some native-first components (checkbox, radio, switch, native-select,
accordion) trade a slice of shadcn's Radix-driven behavior for a real
<input>/<select>/<details> element — zero client JS, browser-native
:checked/:disabled/exclusivity semantics. dropdown and tooltip trade
Radix's Portal for the native popover API. Every divergence, with its
rationale, is ledgered in docs/jsx-parity.md.
gsxui add now vendors components flat into ui/<name>.gsx, one
package ui (a v1.0-era CLI instead vendored one package per component,
e.g. ui/button/button.gsx as package button). The two layouts don't
coexist — an old per-component-directory vendor tree is obsolete, not
migrated in place: delete your project's vendored ui/ directory and
re-run gsxui add <name>... against a current gsxui binary to get the
flat layout back.
The showcase site (site/) is a gsx-built, server-rendered Go app that
imports ui/ directly — it's the proof the components work, plus the
theme editor. Two commands from a fresh clone:
npm install
then, for the dev loop (Vite HMR proxied in front of a live-reloading Go server):
make site-dev
or, to build the production bundle and run it (Vite assets embedded into the binary, served without a dev proxy):
make site
Either way, open the printed URL (make site-dev prints Vite's dev URL;
make site serves directly on $GO_PORT, falling back to $PORT, then
7777 — the same default the dev loop's Vite proxy assumes — if neither is
set).
site/Dockerfile is a three-stage build (Vite production build → go build against the committed .x.go + built dist/ → distroless static
run stage) that binds $PORT (Cloud Run's convention; the image sets
PORT=8080 explicitly).
Build it from the repo root as context:
docker build -f site/Dockerfile -t gsxui-site .
docker run -p 8080:8080 gsxui-site
cloudbuild.yaml (repo root) builds, pushes to Artifact Registry, and
deploys to Cloud Run in one Cloud Build config — pattern-matched from
gsx's playground deploy.
Deferred out of v1 scope, tracked here rather than in the parity ledger's per-component GAP notes (see those for the detailed rationale):
- Tooltip delay-groups —
TooltipProvider's shareddelayDuration/ skip-delay-group coordination across multiple tooltips on a page. v1 hard-codes a fixed per-trigger open delay, no cross-tooltip grouping. - CSS anchor positioning migration — dropdown and tooltip currently
position via a hand-rolled
getBoundingClientRect()+position: fixedcalculation in JS. Once CSS anchor positioning (anchor()/position-anchor) reaches Baseline across browsers, both can drop that JS for native, collision-aware placement. - Checkbox checkmark theming (currentColor mask) — the check glyph is a
data-URI with hard-coded
stroke="white"; data-URIs are static text and can't reference CSS variables, so the mark doesn't follow--primary-foregroundand is wrong/low-contrast for themes where that color isn't near-white. Swap to acurrentColorCSS-mask (mask-image/-webkit-mask-imagepainted viabackground-color: currentColor) in the Plan 4 theming work. gsxui theme(local) — the theme editor ships remote (on the site,/theme) for v1; a localgsxui themecommand was deferred, open question: embed a built CSS artifact in the CLI binary (stays in sync with the site's editor, but bloats/staleness-risks the binary) vs. have it reuse the calling project's own Tailwind build (accurate to that project's tokens, but requires shelling out to its build tooling). Needs a decision before implementation.- Icon search — the icon gallery page (
site/examples/icon) ships v1 as a static grid of ~40 popular icons plus a "1,748 total" note; a searchable/filterable index over the full Lucide set is not built. - Copy-button success feedback — the site's copy-to-clipboard button
(
data-site-copy, wired inweb/site.js) copies but gives no success/failure affordance (e.g. a checkmark swap or toast); noted during Plan 4 Task 2 review as a minor deferred to here.