Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f98d8d5
feat: Add spike-react-native package with GdButton integration and co…
alex-povolotskiy-exa Jul 10, 2026
41ca5c9
feat(ui): Optimize A2UI rendering with caching and React.memo integra…
alex-povolotskiy-exa Jul 13, 2026
b96b4a0
feat: CTORNDSD-581: Replace hardcoded style values with theme-driven …
alex-povolotskiy-exa Jul 13, 2026
74c1ea6
feat: CTORNDSD-581: Introduce theme-driven style resolvers for wrappe…
alex-povolotskiy-exa Jul 13, 2026
7e47cec
chore: Remove deprecated token resolver tests and reproduction artifa…
alex-povolotskiy-exa Jul 14, 2026
0d7fdec
feat: Add isolation and performance test harnesses for CTORNDSD-581
alex-povolotskiy-exa Jul 14, 2026
8fff226
chore: CTORNDSD-581: add usage documentation and examples to web-comp…
Zlodej43sm Jul 14, 2026
526b56e
Merge branch 'develop' into feature/CTORNDSD-580
Zlodej43sm Jul 22, 2026
96e8e4c
Potential fix for pull request finding
Zlodej43sm Jul 22, 2026
984830d
chore: remove CTOR ticket references, clean up comments and metadata
Zlodej43sm Jul 22, 2026
d22f3e9
Potential fix for pull request finding
Zlodej43sm Jul 22, 2026
89ca91e
Potential fix for pull request finding
Zlodej43sm Jul 22, 2026
ca03415
Potential fix for pull request finding
Zlodej43sm Jul 22, 2026
2e34e72
Potential fix for pull request finding
Zlodej43sm Jul 22, 2026
b86c5dd
chore: exclude `spike-react-native` from lint scripts, update import …
Zlodej43sm Jul 22, 2026
826a499
Merge branch 'feature/CTORNDSD-580' into feature/CTORNDSD-581
Zlodej43sm Jul 22, 2026
4450a19
chore: remove performance strategy document, simplify `spike-react-na…
Zlodej43sm Jul 22, 2026
3d4b6fd
chore: add JSX typings for gd-form elements, update dependencies, and…
Zlodej43sm Jul 22, 2026
3b2b0ed
chore: remove CTOR ticket references and streamline metadata
Zlodej43sm Jul 22, 2026
2cbfc03
chore: update `.prettierignore` to exclude additional SSR harness files
Zlodej43sm Jul 22, 2026
5dd5656
Merge branch 'develop' into feature/CTORNDSD-581
Zlodej43sm Aug 5, 2026
2aba814
Feature/ctorndsd 646 (#20)
Zlodej43sm Aug 13, 2026
d72618f
[SPIKE]feat:CTORNDSD-590: react native support (#16)
Zlodej43sm Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ __screenshots__
.claude/tools/node_modules
/libs/ui/scripts/output/*
/libs/ui/.size-limit.json
/libs/web-components/bundle-size-report.json

# Generated by `npm run check:web-components-ssr` — regenerated on every run, so tracking them
# makes the working tree dirty for anyone who runs the check. The command that produces them is
# step 6 of the walkthrough in the root README.
/libs/web-components/harness/ssr-dsd-static.html
/libs/web-components/harness/ssr-dsd-hydrated.html

# Vitest artifacts
.vitest-artifacts
Expand All @@ -84,3 +91,10 @@ plans/
.mcp.json
AGENTS.md
/stats.html
/libs/web-components/screenshots/

# CTORNDSD-646b platform fixtures — build output and generated types only; the fixture
# sources themselves are tracked. See docs/webcomponents-migration/08-react-and-nextjs.md.
/fixtures/*/.next/
/fixtures/*/next-env.d.ts
.vitest-attachments
5 changes: 5 additions & 0 deletions .nxignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CTORNDSD-646b: throwaway platform fixtures live here. Next.js generates
# `fixtures/next-ssr-check/.next/dev/package.json` with no `name` field, which makes Nx fail to
# process the project graph for the ENTIRE repo ("The projects in the following directories have no
# name provided"). Every `nx` command — lint, build, test — breaks until this is excluded.
fixtures/**
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ dist/
libs/ui/src/stories
libs/ui/scripts/
libs/ui/stats.html
libs/web-components/harness/ssr-dsd-static.html
libs/web-components/harness/ssr-dsd-hydrated.html
package.json
package-lock.json
.github/
Expand Down
201 changes: 201 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ Nx monorepo containing the GridKit design system packages.
| `gd-form-configurator` | ![npm](https://img.shields.io/npm/v/gd-form-configurator) | JSON-Schema form engine (AJV + Zustand) |
| `gd-form-configurator-react` | ![npm](https://img.shields.io/npm/v/gd-form-configurator-react) | React bindings for gd-form-configurator |

Not published, and under active investigation:

| Package | Description |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `gd-design-core` (`libs/design-core`) | Framework-agnostic state stores + token resolution. No React, no Lit, no `gd-design-library` dependency |
| `web-components` (`libs/web-components`) | Lit custom-element port of 5 GridKit atoms. `private: true` — see [Web Components spike](#web-components-spike-ctorndsd-646) |

## Quick start

Install the primary package:
Expand Down Expand Up @@ -82,3 +89,197 @@ npm run build:form-configurator && npm run publish:form-configurator
```bash
npm run crc ComponentName
```

## Web Components spike (CTORNDSD-646)

An investigation into porting GridKit from React to Lit custom elements. **Every demo below runs from
the repo root — you never need to `cd` into a package or fixture.**

### One-time setup

```bash
npm install # if you haven't already
npm run demo:setup # builds dist/ + installs the two fixtures (~2-3 min)
```

`demo:setup` builds `gd-design-library`, `gd-design-core`, and `web-components`, then installs
`fixtures/react19-check` and `fixtures/next-ssr-check`. Some demos need those build artifacts; the
demo index tells you which.

### Test it step by step

Twelve steps, in order, all from the repo root. Each one lists the command, what you should see, and
which finding it proves. **Steps 1–6 are non-interactive** — run them and read the terminal. **Steps
7–11 open a browser.** Step 12 runs the whole automated set at once.

If you only have five minutes, run **step 12**.

#### 1. Type-check

```bash
npm run type-check:web-components
echo $? # 0
```

**Success is silent** — `tsc` prints nothing and exits `0`. Any output at all means a failure. This
checks two projects: the shipped library and the harness. The harness one was broken for a while and is
now covered.

#### 2. Lint

```bash
npx nx lint web-components
npx nx lint design-core
```

Expect `Successfully ran target lint` from both, with no rule violations listed above it. `nx` prints a
problem count only when there are problems.

#### 3. Framework-agnostic core tests

```bash
npm run test:design-core
```

Expect **73 passed (6 files)**. Includes the `resetTo` action added because form reset was silently
keeping a checkbox checked — see `FINDINGS.md` §17.1.

#### 4. Component tests in a real browser

```bash
npm run test:web-components
```

Expect **35 passed (4 files)**, running in real Chromium. This is the suite that found the `gd-input`
accessibility bug on its first run (§18.2). It covers the Input cursor guard, the checkbox
`attribute: false` constraint, form participation, the shared-stylesheet cache, and axe.

> Real Chromium is a constraint, not a preference — jsdom does not reliably implement Constructable
> StyleSheets, the `popover` attribute, or Declarative Shadow DOM.

#### 5. Bundle size and the regression gate

```bash
npm run check:web-components-size
```

Prints a per-atom Lit-vs-React table, then expect `✓ no bundle-size regressions`. Ballpark: 5 atoms at
**~11 kB** gzip (**~18 kB** including the `lit` runtime) against **~106 kB** for React+Emotion —
§3, §18.5.

Exact totals drift by a few dozen bytes between builds because Rollup redistributes shared-helper bytes
between chunks whenever any one chunk changes. That is why the gate's tolerance is 10% rather than 0 —
see [`10-performance-report.md`](./docs/webcomponents-migration/10-performance-report.md).

To prove the gate actually fails, edit a number down in
`libs/web-components/bundle-size-baseline.json` and re-run: it should exit `1`.

#### 6. Server rendering with zero client JavaScript

```bash
npm run check:web-components-ssr
```

Generates `libs/web-components/harness/ssr-dsd-static.html` (**0** `<script>` tags) and
`ssr-dsd-hydrated.html` (1). Expect `DSD present for gd-button: true` and the same for
`gd-typography`. Both pages are opened in step 10. Proves §2.

This step is worth running on its own after any change to a component's `render()`: it is the only check
that exercises the components in **Node**, where there is no shadow root and no Constructable
StyleSheets. It caught a real regression that the browser tests could not — §18.7.

#### 7. Start the harness and Storybook

```bash
npm run demo:harness # prints the demo map, serves harness pages on :5173
npm run storybook # SECOND terminal — the React baseline on :6006
```

`demo:harness` prints every page with its URL, what it proves, and any missing prerequisite. Run
`npm run demo:index` on its own any time for the same map.

#### 8. Style isolation — the core justification

Open **`/harness/shell-isolation-check.html`**. Read the JSON block on the page:

```json
{
"Global reset broke the plain Emotion button (control) — expect true": true,
"Global reset leaked INTO native gd-button Shadow DOM — expect false": false,
"Global reset leaked INTO gd-button-shell Shadow DOM — the question this test answers": false,
"gd-button-shell's own real Button styling actually rendered — separate question": false
}
```

The **control** matters: the first `true` proves the CTORNDSD-286 collision is real and reproduced. The
last `false` is the Lit-wraps-React shell failing — it blocks the bad styles _and_ its own good ones.
§1, §15.

#### 9. Forms, CSS Parts, and the remaining findings

Open **`/harness/form-participation-check.html`**. You should see the button ringed in magenta dashes
and the input filled cyan — that is light-DOM CSS reaching **through** the shadow boundary via
`::part()`. The control rule proves a plain descendant selector cannot. Then submit the empty form: the
browser blocks it with a native validation bubble. Results also on `window.__formCheck`. §17.1, §17.2.

Open **`/harness/remaining-findings-repro.html`** for three more: Input cursor stability under late
external writes, `document.querySelector('h1')` returning nothing for a shadow-rendered heading, and
Select's native `popover` dismissal. §4, §5, §6.

#### 10. Visual fidelity, SSR, and speed

| Open | What to look for |
| -------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `/harness/fidelity-check.html` | All 5 atoms with the real theme. Compare side by side against Storybook on :6006 |
| `/harness/ssr-dsd-static.html` | Fully styled with **zero** JavaScript. View source — no `<script>` tags |
| `/harness/ssr-dsd-hydrated.html` | Same markup, now hydrated without discarding the server-rendered node |
| `/harness/perf-check.html` | **Auto-runs, ~30s.** React vs native Lit vs shell. Raw per-trial numbers on `window.__PERF_RESULTS__` |

Speed figures are machine-specific — the _percentages_ in
[`10-performance-report.md`](./docs/webcomponents-migration/10-performance-report.md) are the portable
result, not the absolute milliseconds.

#### 11. The two framework fixtures

```bash
npm run demo:react19 # :5273
```

Read `window.__react19Check`. Expect object props to reach the property natively (`theme` by
reference, no stringified attribute) but `onGdChange` to fire **zero** times — silently, with no
warning. That silence is why the React wrapper layer should be generated. §17.3.

```bash
npm run demo:next # :5373
```

Then, in another terminal:

```bash
curl -s http://localhost:5373/ | grep -c shadowrootmode # expect 0
curl -s http://localhost:5373/ | grep -c '<h2' # expect 0
```

Both zeros are the finding: **Next.js emits no Declarative Shadow DOM**, so with JavaScript off the
page has unstyled text and no headings. The page also shows a server-side import probe failing with
`createContext is not a function` — the token barrel is not RSC-safe. §17.4.

#### 12. Everything automated, in one command

```bash
npm run verify:web-components
```

Runs steps 1–6 in sequence: type-check, lint, both test suites, the SSR check, and the size gate. Exit
code `0` means the whole non-interactive set passed. This is the CI gate.

### Read the results

| Where | What |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [`docs/webcomponents-migration/`](./docs/webcomponents-migration/) | The decision document set — 15 files. **Start with its `README.md`** |
| [`libs/web-components/FINDINGS.md`](./libs/web-components/FINDINGS.md) | The chronological engineering log; every measurement quoted in the docs |
| [`libs/web-components/README.md`](./libs/web-components/README.md) | Component API reference — props, events, slots, CSS parts, form participation |

Every claim in the docs is labelled **measured**, **reasoned**, or **not attempted**, so it is always
clear which conclusions rest on evidence.
Loading