Skip to content

[NO-ISSUE] feat(webkit)!: adoption toolkit (in-package) + prop-vocabulary guardrails + canonical prop renames#717

Draft
HerbertJulio wants to merge 69 commits into
devfrom
feat/webkit-adoption-kit
Draft

[NO-ISSUE] feat(webkit)!: adoption toolkit (in-package) + prop-vocabulary guardrails + canonical prop renames#717
HerbertJulio wants to merge 69 commits into
devfrom
feat/webkit-adoption-kit

Conversation

@HerbertJulio

@HerbertJulio HerbertJulio commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Three things, all inside the existing @aziontech/webkit package (no new packages published):

  1. Adoption toolkit — so any project consumes webkit correctly by construction, not just by docs.
  2. Prop-vocabulary guardrails — one canonical prop/event API across every component, machine-enforced on create and edit.
  3. Canonical prop renames (⚠️ breaking) — bring existing components onto that vocabulary, and drop redundant events.

Branch is merged up to dev (aligned to avoid conflicts).


1. Adoption toolkit — folded into @aziontech/webkit (no new packages)

Everything ships inside the existing package as subpaths + bins, over one source of truth:

  • catalog.json — generated from package.json#exports + .specs (version-locked, regenerated at release). Every tool reads this one file, so nothing drifts from the installed version.
  • FORCE (blocks in pre-commit/CI)@aziontech/webkit/eslint-plugin (8 rules: valid-import-path, no-deep-internal-import, no-barrel-import, no-whole-icon-set-import, no-hardcoded-color, prefer-tree-shakeable-root, no-deprecated-component, prefer-webkit-component, no-style-override) + @aziontech/webkit/stylelint-config.
  • GUIDE (assists the AI/dev up front) — MCP server (npx -p @aziontech/webkit webkit-mcp, 10 tools) + a Claude Code bundle, wired by npx @aziontech/webkit init / verified by doctor. MCP deps are optionalDependencies.

2. Prop-vocabulary guardrails (.claude/)

A single dictionary — .claude/hooks/_lib/prop-vocabulary.mjs (documented in .claude/rules/prop-vocabulary.md, surfaced to the MCP via catalog.vocabulary) — enforced by validate-spec-compliance on every Write/Edit:

  • Prop names: banned aliases → canonical (variant/appearance/intent/headerVariant/mediaKind/cardStylekind, statusseverity, dismissable/closeabledismissible, heading/headertitle, isToggledmodelValue); no is/has prefix; positive booleans (hiddenvisible, …); size = small|medium|large (order enforced, no xs/sm/md/lg/xl).
  • Events: kebab-case; no redundant echo<x>-change is blocked when update:<x> exists (use v-model / @update:<x>; a distinct commit event is a bare change).
  • Default drift: the spec's Default column must match withDefaults.
  • no-style-override (consumer eslint): flags class/:class/style/:style on a webkit component tag — with an opt-in style_seam escape hatch and options.allow (slot content / composition is never flagged).
  • Parser coverage extended to named Props interfaces and ?: () => slots.

3. Canonical prop renames (⚠️ breaking — major bump)

Component(s) Old → New
CardPricing cardStylekind
ItemMedia mediaKindkind
Table / TableHeader headerVariantheaderKind / variantkind
Switch isToggledmodelValue, typekind, isFocusedfocused
Dialog / Drawer / Popover closeable/dismissabledismissible (light-dismiss; closable reserved for the X-button — Message/Toast/TabViewItem keep it)
OnboardingForm / PickList heading/headertitle
Badge / Tag / HelperText / Label valuelabel (slot-fallback text)
StatusIndicator statusseverity (positivesuccess)
TableSortButton hiddenvisible (inverted)

Redundant echo events removed (use v-model / @update:*): value-change (CodeBlock/TabView/NavigationMenu), page-change (Paginator), change (SegmentedButton). Kept: InputNumber change (commit-on-blur) and Calendar month-change (navigation) — genuinely distinct.

Deferred (intentional): ToastItem typeseverity (entangled with the public toast({type}) store API — one planned break later; canonical set recorded as neutral|success|info|warning|danger); BreadcrumbItem keeps current (maps to native aria-current="page").


How to test

Node ≥ 22. Run pnpm install --lockfile-only first — the dev merge left pnpm-lock.yaml needing regeneration (a dep-set reconcile), which the frozen-lockfile CI check depends on.

  • Toolkit: pnpm --filter @aziontech/webkit test (node:test — eslint rules, MCP, CLI, stylelint).
  • Guardrails: editing a component with a banned prop/alias, a negative boolean, a <x>-change echo, or default drift is blocked by validate-spec-compliance.
  • Consumer: no-style-override flags class/style on a webkit tag; npx @aziontech/webkit init wires a scratch project.

Follow-ups (not blocking this PR)

  • pnpm install --lockfile-only for the merged lockfile (needs the toolchain).
  • Pre-existing spec↔code animation drift on dialog / table (from the dev merge) — reconcile separately.
  • Pre-existing spec gaps surfaced by the broadened parser (field-switch required, menu-item props).

Comment thread packages/webkit/scripts/build-catalog.mjs Fixed
…alog, lint, mcp, cli)

Catalog generator: parse slot Scope/Notes columns correctly; detect compounds by spec
structure so dropdown/paginator classify as compounds with tree-shakeable -root and
sub-components get a parent; refine kindFromTarget. Add ./dropdown-root and
./paginator-root exports. Ignore the generated catalog.json in prettier so the drift
gate is stable.

eslint-plugin: resolve either published package name (@aziontech/webkit or .dev), derive
the import prefix from the catalog, and warn (not silently disable) when no catalog
resolves; scope no-hardcoded-color so short hex in non-style strings (anchors/ids) is not
flagged; cover export-from and dynamic import() in no-barrel; correct the icon-font rule
guidance. Add .releaserc, README, engines and test devDeps.

webkit-mcp: stop misclassifying sibling @aziontech/webkit-* packages; emit runnable empty
literals for required array/object props; read server version from package.json; report
treeShakeable honestly. Add README and engines.

webkit-cli: document and ship the correct command (webkit-cli init); refuse to overwrite a
malformed package.json/.mcp.json; write eslint.config.mjs; husky v9 hook + prepare script;
wire stylelint .vue/.scss custom syntax and detect an existing stylelint config; reject
unknown flags. Add README and engines.

Adds unit tests for every new behavior (44 total).
…est/drift job

One publish workflow per new package (eslint-plugin-webkit, stylelint-config-webkit,
webkit-mcp, webkit-cli), cloned from package-webkit.yml. Add a governance 'toolkit' job
that runs the four unit-test suites and the catalog drift check, and include catalog.json
in webkit's semantic-release git assets.
…ules

no-deep-internal-import and valid-import-path (only when a single unambiguous
candidate exists) now declare meta.fixable and apply the catalog-verified replacement on
`eslint --fix`. no-barrel-import verifies each guessed per-subpath against the catalog and
only offers the split suggestion when all resolve, so it never proposes an invalid import.
…+ version reporting

`webkit-cli doctor` reads the project and reports OK/WARN/FAIL for: catalog resolvable,
eslint/stylelint config, .mcp.json webkit server, husky prepare script + pre-commit hook,
theme import, and resolved dependency versions (warns on `latest` pins). Read-only; exits
non-zero on any FAIL so it works as a CI setup gate. Exposes the shared init predicates
(read/firstExisting/readJsonStrict + config-candidate constants) for reuse.
… MCP tool

build-catalog now emits a positive `tokens` inventory (CSS custom properties unioned from
the theme's generated globals.css across channels, grouped by segment, plus the semantic
typography classes) alongside the deny `tokenRules`. The MCP surfaces it via a new
list_tokens tool (group index, or a group's tokens by category) — closing the GUIDE loop
"don't hardcode #hex → use THIS token". Deterministic + fail-safe; drift-check stays stable.
…workspaces

Records the four new workspace packages and their deps (@modelcontextprotocol/sdk, zod,
postcss-html, postcss-scss) so `pnpm install --frozen-lockfile` (CI, incl. the toolkit job)
passes.
…h/webkit (no new packages)

The eslint plugin, stylelint config, MCP server and CLI now ship INSIDE the existing
@aziontech/webkit package instead of as four new npm packages:

- src moved to packages/webkit/src/{eslint-plugin,stylelint-config,mcp,cli} + cli-templates/
- exports: ./eslint-plugin, ./stylelint-config; bins: webkit (init/doctor), webkit-mcp
- MCP deps (@modelcontextprotocol/sdk, zod) are optionalDependencies; eslint/stylelint/
  vue-eslint-parser are optional peers so UI consumers stay lean
- init writes @aziontech/webkit/eslint-plugin + /stylelint-config and runs the MCP via
  'npx -p @aziontech/webkit webkit-mcp'; 'npx @aziontech/webkit init' now works
- deleted the 4 package manifests/.releaserc + publish workflows; the toolkit node:test
  suites moved to packages/webkit/test/** (test:toolkit); governance runs them + catalog:check

No new package published; nothing lost. 52 toolkit tests green; init + ESLint e2e verified.
build-catalog extracts the spec's Purpose + When-to-use / When-NOT-to-use / Related /
Best-practices sections into each catalog entry (empty until backfilled). MCP get_component
returns them, a new get_best_practices tool exposes the guidance subset, and suggest_component
scores against Purpose/useWhen so sibling picks (Dialog vs Drawer, Badge vs Tag) resolve by intent.
Comment on lines +135 to +136
const text = String(section)
.replace(/<!--[\s\S]*?-->/g, '')
Comment on lines +148 to +149
return String(section)
.replace(/<!--[\s\S]*?-->/g, '')
…oken tokens.md links

enforce-component-create.mjs called skillReferencedInTranscript (undefined; the function is
pipelineReferencedInTranscript) → it threw and failed open, so the 'run the spec pipeline first'
gate never blocked. Also repoint the dangling ./tokens.md links in no-invention/migration/
dependencies to the real catalog doc (.claude/docs/DESIGN.md § Animations).
… rules

validate-spec-compliance: block forbidden variant-prop names (variant/appearance/intent → kind),
is/has boolean prefixes, and camelCase (non-kebab) event names.
validate-tokens: block JS class presets (const *Classes = {}/[]/computed), component-local <style>
blocks, component-local @Keyframes, arbitrary animate-[…], and hardcoded duration/ease/delay-[…].
Both fire only on NEW matches (baseline diff) so existing components are grandfathered; verified
no false positives on button/input-text/badge and a clean sample.
… + consumer rules

Animations: build-catalog now emits tokens.animations (semantic utilities ∪ primitive
--animate-*); validate-spec-compliance blocks any animate-* not in it, pointing to
/add-animation, which scaffolds the utility+keyframes in semantic/animations.js and records a
Theme gap (never a component-local @Keyframes, never DESIGN.md).
Consumer rules: no-deprecated-component (blocks importing a component the catalog marks
deprecated → replacedBy) and prefer-webkit-component (steers off foreign UI libs like PrimeVue).
Catalog also carries deprecated/replacedBy from spec frontmatter. 57 toolkit tests green.
Template gains optional When-to-use / When-NOT-to-use / Related / Best-practices sections
(build-catalog already extracts them; not required by the validator, so existing specs keep
passing). Backfilled with real content the sibling groups where selection is hardest:
badge/tag/chip, dialog/drawer, input-text/textarea/input-number. Checksums recomputed so the
edited specs stay tamper-consistent; catalog regenerated (MCP get_best_practices/suggest now
answer 'badge vs tag', 'dialog vs drawer').
…-vocab-style

# Conflicts:
#	.specs/chip.md
#	.specs/textarea.md
#	packages/webkit/package.json
#	pnpm-lock.yaml
@robsongajunior robsongajunior added the WIP Work in Progress label Jul 8, 2026
…heck

Bind "what we suggest to the AI" and "what the pipeline blocks" to one definition:

- .claude/hooks/_lib/standards.mjs — single registry pairing every .claude/rules
  standard with its enforcement (write-time hook / lint / CI ratchet) or an explicit
  reviewGated flag
- authoring-checks.mjs exposes STANDARD_BY_CHECK linking each mechanized check to its rule
- test/standards/invariant.test.mjs (runs in the CI toolkit job) fails when a rule has no
  gate, a gate has no rule, a standard is silently unenforced, or the write-time hook and
  the CI ratchet stop sharing the same engine
Lista item a item cada rule, hook, lint, skill e agent, com o que valida, exemplos
tabelados ❌/✅ (criação e implementação) e a garantia fonte-única + meta-check.
Kept as a personal reference (not a shipped repo artifact) per request.
Nothing out of standard is a mere warning: the recommended and performance presets
promote every rule to `error`, so a consumer using the design system off-pattern fails
its lint instead of getting a skippable warning.

BREAKING CHANGE: consumers on the `recommended`/`performance` presets now get errors
(not warnings) for no-hardcoded-color, prefer-tree-shakeable-root, no-whole-icon-set-import,
prefer-webkit-component, prefer-define-model, and no-style-override.
Model review as a first-class blocking surface: a standard is enforced automatically
(hook/lint/CI) and/or by mandatory review (the PR cannot merge without the 2 required
approvals). Remove `reviewGated` — nothing is advisory. The invariant test now requires
every standard to have at least one enforcement surface.
Add `scope: 'general' | 'webkit'` to every standard — a clear separation between the
construction patterns that ship to consuming projects (general) and the rules specific to
authoring the design system itself (webkit). Export GENERAL_STANDARDS / WEBKIT_STANDARDS.
The invariant test now requires every standard to declare a valid scope.
One page for consumers to see every pattern: Part A (building a component by hand — the
general patterns, wrong vs. correct) and Part B (using a webkit component — the consumer
rules), with a rendered Mermaid flow of how enforcement blocks the merge.
…und, accurate requirements

Replace the stale README (WebkitPlugin/list-data-table no longer exist) with a getting
started modeled on best-in-class DS docs: quick start with AI (init + MCP), install,
theme CSS wiring, first component, Storybook playground, the all-error lint preset, and
terminal exploration (doctor, webkit-mcp). Requirements corrected to Vue 3.5+ (useId).
…lder structure

Add the Foundations token table (color/typography/spacing/shape/shadow/motion, use vs
never — semantic spacing scale per DESIGN.md), the one-component-one-folder structure,
the fixed script-setup order, and a getting-started pointer. Fix the MCP identifier to
npx webkit-mcp.
.claude/README.md — the entry-point map of the governance system (folder layout, where
each concern lives, the single-source flow). .claude/rules/README.md — index of the 23
rules with scope (general/webkit) and enforcement, as the human-readable summary of
standards.mjs. The invariant test ignores README.md when enumerating rule docs.
…xamples

The examples taught the primitive scale (--spacing-2/-3/-4/-1) that DESIGN.md forbids in
webkit components. Rewritten to the semantic tokens (--spacing-xs/-sm/-md/-xxs) that the
components actually use — so no example anywhere shows a token the hooks would block.
Replace the contributor-onboarding page with a consumer getting started modeled on
best-in-class DS docs: quick start with AI (init + MCP), install, theme CSS, first
component, the all-error lint preset, and terminal exploration — with a short
contributor section at the end. Mirrors packages/webkit/README.md.
… the init bundle

The consumer bundle now carries the blocking guidance, not only usage tips:

- rules/webkit-style-override.md — never restyle a webkit component with class/style
  (the no-style-override lint blocks it as error); compose in slots, use props, or a
  styleSeam component
- rules/webkit-construction-standards.md — the scope:general standards distilled for
  components the consumer builds by hand (defineModel, typed props/emits/slots,
  composables, data-* styling, root element, states, a11y, testid, deprecation)
- CLAUDE.md fragment gains both directives; plan + tests updated
- templates migrated off the primitive spacing scale (--spacing-4 → --spacing-md)
… examples

Prettier's html formatter lowercased <Button>/<Dialog> to native elements, inverting the
example's meaning (a native button MAY take a class). Guard the fences with
prettier-ignore so the webkit tags stay PascalCase.
One definition, three enforcement surfaces. The authoring-checks engine moves into the
package (src/eslint-plugin/authoring-checks.js) and is now consumed by:
- the design system's write-time hook (via a _lib re-export shim)
- the design system's CI ratchet
- NEW webkit/authoring-standards ESLint rule (error in every preset) — blocks the
  consumer at commit (husky) and in their CI: manual modelValue+update:modelValue,
  runtime defineProps({})/defineEmits([]), <slot> without defineSlots, composables
  returning reactive() or authored as .js, bare @deprecated

The init bundle now carries the full scope:general standards as consumer rules
(webkit-{v-model,props,prop-vocabulary,emits,slots,composables,styling,
component-structure,root-element,component-states,accessibility,testid,deprecation}.md),
with webkit-construction-standards.md as the index. Standards registry records the new
lint surface; the invariant test asserts all three surfaces share the engine.

BREAKING CHANGE: the recommended/strict presets now include webkit/authoring-standards
as error; consumer code with those patterns fails lint.
STYLEGUIDE.md is the programmer's base reference and the exact same rule set the AI
pipeline receives: 22 sections covering all 23 standards (foundations/tokens, naming,
file organization, props, prop vocabulary, v-model, emits, slots, composables, styling,
root element, states, accessibility, testid, deprecation, imports/tree-shaking + bundle
budget, compound API, dependencies, spec-first authoring, storybook, releases/git), each
with the rule, its concrete decisions, wrong/correct examples, and the gate that blocks
it — closing with the enforcement map (one engine, three surfaces). Linked from README
and GUIDELINES (which stays as the one-page digest).
Renders packages/webkit/docs/STYLEGUIDE.md verbatim via a raw import (single source,
zero drift), placed right after Get Started in the sidebar; Get Started now points to it.
… "Style Guide" title

- MDX docs pages (Get Started, Style Guide) now switch with the light/dark toolbar:
  withThemeByClassName only runs as a story decorator, so preview.js mirrors the theme
  global onto <html> via a GLOBALS_UPDATED channel listener
- class-keyed docs styles in preview.css theme the sbdocs surface, text, tables, inline
  code, and blockquotes for both azion-light and azion-dark
- docs hyperlinks were colorSecondary #585C6D (unreadable on the dark canvas) — now the
  Azion orange in dark and a darker orange on light
- sidebar/tab title renamed Styleguide → "Style Guide"
… bypass the hook

validate-tokens' checks (hex/palette/raw typography, class presets, <style>, local
@Keyframes, arbitrary motion) previously only ran as a write-time hook inside the AI
pipeline — a human pushing from their editor bypassed them, leaving the standard
review-only in CI. Extract the 17 checks into a shared engine
(src/eslint-plugin/token-checks.js, hook imports via a _lib shim) and run them repo-wide
in the check-authoring ratchet with the frozen-baseline mechanism (74 token entries
grandfathered; any NEW violation fails the PR). Standards registry records the new CI
surface for styling and root-element.
…y gap closed

Extract the validate-spec-compliance checks (no-invention, prop/event vocabulary, naming,
defaults drift, testid, animations-vs-spec/catalog) into a shared engine
(_lib/spec-compliance-checks.mjs); the PostToolUse hook becomes a thin wrapper and the CI
ratchet now runs the same checks repo-wide with the frozen baseline (32 spec entries
grandfathered). With this, every standard's mechanizable slice has a CI-facing gate: an
editor push that never ran the hooks can no longer merge off-spec, off-vocabulary,
off-token, or off-standard code. Registry updated (6 standards gain the ci surface).
…dard has a CI-facing gate

Extract the "Show code" checks into _lib/story-source-checks.mjs (hook becomes a thin
wrapper) and run them repo-wide over apps/storybook/src in the check-authoring ratchet
(existing story debt frozen in the baseline). Registry updated: storybook-source and
migration gain ci:check-authoring; dependencies records its real CI gate (forbidden libs
are uninstalled — any import fails vue-tsc/build). With this, 23/23 standards block in
CI or consumer lint — an editor push can no longer bypass any write-time hook.
…talog never ran

The toolkit job's three steps filtered @aziontech/webkit.dev, but the package was renamed
to @aziontech/webkit when the toolkit was folded in — pnpm --filter with no match exits 0,
so catalog drift, the toolkit/invariant tests, and the authoring ratchet were a green
no-op in CI. Point the filters at the real package name so the gates actually gate.
…s 'changes'

The changes filter only watched packages/webkit/** and .specs/** — a PR touching only
.claude/hooks/** (the shared engines + standards registry), .claude/rules/**, or
apps/storybook/** (the ratchet's story scan) skipped the toolkit job entirely. Widen the
filter, and include the 'changes' job itself in the governance-check results map so the
gate no longer passes when change detection fails.
…ce_all

Audit findings, both reproduced live: (1) validate-tokens scanned only the edit
fragments, so a forbidden token split across two edits ('bg-red-' + '-500') passed the
hook; it now reconstructs the resulting file like its siblings. (2) All three
reconstructing hooks modeled Edit as a single first-occurrence replace and ignored
replace_all, so removing every occurrence of a must-have (e.g. defineSlots) in one
replace_all edit evaded the negative checks.
Audit findings: (1) the story scan's walk() missed .stories.tsx/jsx/mjs that STORY_RE
accepts; (2) plain-Set baseline let a SECOND violation of an already-baselined id in the
same file evade — the diff is now a multiset and scanTokens emits one entry per match;
(3) deleting/renaming a spec evaporated every baselined entry as 'fixed' — the ratchet
now requires a spec (missing-spec violation) via collectSpecViolations({ requireSpec });
(4) the spec resolver only matched components/<cat>/<name>/<file>.vue, so nested
sub-component folders, flat components (avatar/tag/overline), and compositions whose
root is <name>-root.vue (tab-view, navigation-menu) received ZERO spec checks on both
surfaces — all three layouts now resolve, and <name>-root.vue counts as the root.
Baseline regenerated: 391 entries (the newly covered surfaces frozen as known debt).
…name

Audit findings: (1) the init-generated eslint.config.mjs could not parse the TypeScript
SFCs the standards mandate — it now wires @typescript-eslint/parser (added to the dev
deps); (2) a pre-existing non-husky "prepare" script silently left the hooks inert —
init now advises how to chain it instead; (3) the CLI help still described the old
recommended preset; (4) prefer-define-model's message pointed at a DS-internal rule path
consumers don't have; (5) webkit-mcp's runtime deps lived in optionalDependencies (an
omit=optional install broke the server) — moved to dependencies, error message fixed;
(6) apps/storybook still depended on the ghost @aziontech/webkit.dev and aliased onto it
— renamed to @aziontech/webkit, alias removed, lockfile regenerated (it could not even
re-resolve while the ghost existed).

BREAKING CHANGE: @modelcontextprotocol/sdk and zod are now regular dependencies of
@aziontech/webkit.
…, and orphan lints

The invariant only asserted engine-sharing for authoring-checks — the token, spec, and
story engines could silently fork (and 'ci' enforcers were free-form strings, the exact
failure mode that let the toolkit job no-op on a ghost package name). It now asserts all
four engines are shared hook↔ratchet, validates every ci enforcer against a known-gate
whitelist, and fails when a shipped eslint rule is claimed by no standard. Registry:
styling claims no-hardcoded-color/no-style-override, imports claims the tree-shaking and
icon-set lints.
Audit drift pass: bundle-budget.md and accessibility.md claimed gates that do not run
yet (size-limit job, axe) — both now state pending status honestly; the STYLEGUIDE
"Enforced by" lines and rules/README "Blocks via" column gain the CI ratchet surfaces
that landed after they were written; .claude/README counts 11 lint rules and describes
the four shared engines.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge WIP Work in Progress

Development

Successfully merging this pull request may close these issues.

3 participants